mirror of https://gitee.com/openkylin/linux.git
genirq: Add return value to check_irq_resend()
In preparation for an interrupt injection interface which can be used safely by error injection mechanisms. e.g. PCI-E/ AER, add a return value to check_irq_resend() so errors can be propagated to the caller. Split out the software resend code so the ugly #ifdef in check_irq_resend() goes away and the whole thing becomes readable. Fix up the caller in debugfs. The caller in irq_startup() does not care about the return value as this is unconditionally invoked for all interrupts and the resend is best effort anyway. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Marc Zyngier <maz@kernel.org> Link: https://lkml.kernel.org/r/20200306130623.775200917@linutronix.de
This commit is contained in:
parent
008f1d60fe
commit
1f85b1f5e1
|
@ -218,8 +218,7 @@ static ssize_t irq_debug_write(struct file *file, const char __user *user_buf,
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
} else {
|
} else {
|
||||||
desc->istate |= IRQS_PENDING;
|
desc->istate |= IRQS_PENDING;
|
||||||
check_irq_resend(desc);
|
err = check_irq_resend(desc);
|
||||||
err = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||||
|
|
|
@ -108,7 +108,7 @@ irqreturn_t handle_irq_event_percpu(struct irq_desc *desc);
|
||||||
irqreturn_t handle_irq_event(struct irq_desc *desc);
|
irqreturn_t handle_irq_event(struct irq_desc *desc);
|
||||||
|
|
||||||
/* Resending of interrupts :*/
|
/* Resending of interrupts :*/
|
||||||
void check_irq_resend(struct irq_desc *desc);
|
int check_irq_resend(struct irq_desc *desc);
|
||||||
bool irq_wait_for_poll(struct irq_desc *desc);
|
bool irq_wait_for_poll(struct irq_desc *desc);
|
||||||
void __irq_wake_thread(struct irq_desc *desc, struct irqaction *action);
|
void __irq_wake_thread(struct irq_desc *desc, struct irqaction *action);
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,43 @@ static void resend_irqs(unsigned long arg)
|
||||||
/* Tasklet to handle resend: */
|
/* Tasklet to handle resend: */
|
||||||
static DECLARE_TASKLET(resend_tasklet, resend_irqs, 0);
|
static DECLARE_TASKLET(resend_tasklet, resend_irqs, 0);
|
||||||
|
|
||||||
|
static int irq_sw_resend(struct irq_desc *desc)
|
||||||
|
{
|
||||||
|
unsigned int irq = irq_desc_get_irq(desc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validate whether this interrupt can be safely injected from
|
||||||
|
* non interrupt context
|
||||||
|
*/
|
||||||
|
if (handle_enforce_irqctx(&desc->irq_data))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the interrupt is running in the thread context of the parent
|
||||||
|
* irq we need to be careful, because we cannot trigger it
|
||||||
|
* directly.
|
||||||
|
*/
|
||||||
|
if (irq_settings_is_nested_thread(desc)) {
|
||||||
|
/*
|
||||||
|
* If the parent_irq is valid, we retrigger the parent,
|
||||||
|
* otherwise we do nothing.
|
||||||
|
*/
|
||||||
|
if (!desc->parent_irq)
|
||||||
|
return -EINVAL;
|
||||||
|
irq = desc->parent_irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set it pending and activate the softirq: */
|
||||||
|
set_bit(irq, irqs_resend);
|
||||||
|
tasklet_schedule(&resend_tasklet);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
static int irq_sw_resend(struct irq_desc *desc)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -54,50 +91,28 @@ static DECLARE_TASKLET(resend_tasklet, resend_irqs, 0);
|
||||||
*
|
*
|
||||||
* Is called with interrupts disabled and desc->lock held.
|
* Is called with interrupts disabled and desc->lock held.
|
||||||
*/
|
*/
|
||||||
void check_irq_resend(struct irq_desc *desc)
|
int check_irq_resend(struct irq_desc *desc)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We do not resend level type interrupts. Level type
|
* We do not resend level type interrupts. Level type interrupts
|
||||||
* interrupts are resent by hardware when they are still
|
* are resent by hardware when they are still active. Clear the
|
||||||
* active. Clear the pending bit so suspend/resume does not
|
* pending bit so suspend/resume does not get confused.
|
||||||
* get confused.
|
|
||||||
*/
|
*/
|
||||||
if (irq_settings_is_level(desc)) {
|
if (irq_settings_is_level(desc)) {
|
||||||
desc->istate &= ~IRQS_PENDING;
|
desc->istate &= ~IRQS_PENDING;
|
||||||
return;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc->istate & IRQS_REPLAY)
|
if (desc->istate & IRQS_REPLAY)
|
||||||
return;
|
return -EBUSY;
|
||||||
|
|
||||||
if (desc->istate & IRQS_PENDING) {
|
if (desc->istate & IRQS_PENDING) {
|
||||||
desc->istate &= ~IRQS_PENDING;
|
desc->istate &= ~IRQS_PENDING;
|
||||||
desc->istate |= IRQS_REPLAY;
|
desc->istate |= IRQS_REPLAY;
|
||||||
|
|
||||||
if ((!desc->irq_data.chip->irq_retrigger ||
|
if (!desc->irq_data.chip->irq_retrigger ||
|
||||||
!desc->irq_data.chip->irq_retrigger(&desc->irq_data)) &&
|
!desc->irq_data.chip->irq_retrigger(&desc->irq_data))
|
||||||
!handle_enforce_irqctx(&desc->irq_data)) {
|
return irq_sw_resend(desc);
|
||||||
#ifdef CONFIG_HARDIRQS_SW_RESEND
|
|
||||||
unsigned int irq = irq_desc_get_irq(desc);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the interrupt is running in the thread
|
|
||||||
* context of the parent irq we need to be
|
|
||||||
* careful, because we cannot trigger it
|
|
||||||
* directly.
|
|
||||||
*/
|
|
||||||
if (irq_settings_is_nested_thread(desc)) {
|
|
||||||
/*
|
|
||||||
* If the parent_irq is valid, we
|
|
||||||
* retrigger the parent, otherwise we
|
|
||||||
* do nothing.
|
|
||||||
*/
|
|
||||||
if (!desc->parent_irq)
|
|
||||||
return;
|
|
||||||
irq = desc->parent_irq;
|
|
||||||
}
|
|
||||||
/* Set it pending and activate the softirq: */
|
|
||||||
set_bit(irq, irqs_resend);
|
|
||||||
tasklet_schedule(&resend_tasklet);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue