genirq: Add return value to check_irq_resend()
authorThomas Gleixner <tglx@linutronix.de>
Fri, 6 Mar 2020 13:03:45 +0000 (14:03 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Sun, 8 Mar 2020 10:06:41 +0000 (11:06 +0100)
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
kernel/irq/debugfs.c
kernel/irq/internals.h
kernel/irq/resend.c

index d44c8fd..0c60779 100644 (file)
@@ -218,8 +218,7 @@ static ssize_t irq_debug_write(struct file *file, const char __user *user_buf,
                        err = -EINVAL;
                } else {
                        desc->istate |= IRQS_PENDING;
-                       check_irq_resend(desc);
-                       err = 0;
+                       err = check_irq_resend(desc);
                }
 
                raw_spin_unlock_irqrestore(&desc->lock, flags);
index 5be382f..8980859 100644 (file)
@@ -108,7 +108,7 @@ irqreturn_t handle_irq_event_percpu(struct irq_desc *desc);
 irqreturn_t handle_irq_event(struct irq_desc *desc);
 
 /* 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);
 void __irq_wake_thread(struct irq_desc *desc, struct irqaction *action);
 
index 5064b13..a21fc4e 100644 (file)
@@ -47,6 +47,43 @@ static void resend_irqs(unsigned long arg)
 /* Tasklet to handle resend: */
 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
 
 /*
@@ -54,50 +91,28 @@ static DECLARE_TASKLET(resend_tasklet, resend_irqs, 0);
  *
  * 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
-        * interrupts are resent by hardware when they are still
-        * active. Clear the pending bit so suspend/resume does not
-        * get confused.
+        * We do not resend level type interrupts. Level type interrupts
+        * are resent by hardware when they are still active. Clear the
+        * pending bit so suspend/resume does not get confused.
         */
        if (irq_settings_is_level(desc)) {
                desc->istate &= ~IRQS_PENDING;
-               return;
+               return -EINVAL;
        }
+
        if (desc->istate & IRQS_REPLAY)
-               return;
+               return -EBUSY;
+
        if (desc->istate & IRQS_PENDING) {
                desc->istate &= ~IRQS_PENDING;
                desc->istate |= IRQS_REPLAY;
 
-               if ((!desc->irq_data.chip->irq_retrigger ||
-                   !desc->irq_data.chip->irq_retrigger(&desc->irq_data)) &&
-                   !handle_enforce_irqctx(&desc->irq_data)) {
-#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
-               }
+               if (!desc->irq_data.chip->irq_retrigger ||
+                   !desc->irq_data.chip->irq_retrigger(&desc->irq_data))
+                   return irq_sw_resend(desc);
        }
+       return 0;
 }