genirq: Add sanity checks for PM options on shared interrupt lines
authorThomas Gleixner <tglx@linutronix.de>
Thu, 28 Aug 2014 09:44:31 +0000 (11:44 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 1 Sep 2014 11:48:05 +0000 (13:48 +0200)
Account the IRQF_NO_SUSPEND and IRQF_RESUME_EARLY actions on shared
interrupt lines and yell loudly if there is a mismatch.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
include/linux/irqdesc.h
kernel/irq/internals.h
kernel/irq/manage.c
kernel/irq/pm.c

index 472c021a2d4f09923cd3913647be44c016ff3003..cb1a31e448aed11831dc9ba1fc135e8703caba89 100644 (file)
@@ -36,6 +36,11 @@ struct irq_desc;
  * @threads_oneshot:   bitfield to handle shared oneshot threads
  * @threads_active:    number of irqaction threads currently running
  * @wait_for_threads:  wait queue for sync_irq to wait for threaded handlers
+ * @nr_actions:                number of installed actions on this descriptor
+ * @no_suspend_depth:  number of irqactions on a irq descriptor with
+ *                     IRQF_NO_SUSPEND set
+ * @force_resume_depth:        number of irqactions on a irq descriptor with
+ *                     IRQF_FORCE_RESUME set
  * @dir:               /proc/irq/ procfs entry
  * @name:              flow handler name for /proc/interrupts output
  */
@@ -68,6 +73,11 @@ struct irq_desc {
        unsigned long           threads_oneshot;
        atomic_t                threads_active;
        wait_queue_head_t       wait_for_threads;
+#ifdef CONFIG_PM_SLEEP
+       unsigned int            nr_actions;
+       unsigned int            no_suspend_depth;
+       unsigned int            force_resume_depth;
+#endif
 #ifdef CONFIG_PROC_FS
        struct proc_dir_entry   *dir;
 #endif
index af2821178900a6313cdab63b7895f2d2f28c813e..c402502a5111f6c1abc403060ceb4257a793aca0 100644 (file)
@@ -194,3 +194,13 @@ static inline void kstat_incr_irqs_this_cpu(unsigned int irq, struct irq_desc *d
        __this_cpu_inc(*desc->kstat_irqs);
        __this_cpu_inc(kstat.irqs_sum);
 }
+
+#ifdef CONFIG_PM_SLEEP
+void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action);
+void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action);
+#else
+static inline void
+irq_pm_install_action(struct irq_desc *desc, struct irqaction *action) { }
+static inline void
+irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action) { }
+#endif
index fa564e8db996b5e32b969ed9f6c3ef9ca95c2394..0a9104b4608b8dc374769a7435b594f27a0b97ae 100644 (file)
@@ -1200,6 +1200,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
        new->irq = irq;
        *old_ptr = new;
 
+       irq_pm_install_action(desc, new);
+
        /* Reset broken irq detection when installing new handler */
        desc->irq_count = 0;
        desc->irqs_unhandled = 0;
@@ -1318,6 +1320,8 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
        /* Found it - now remove it from the list of entries: */
        *action_ptr = action->next;
 
+       irq_pm_remove_action(desc, action);
+
        /* If this was the last handler, shut down the IRQ line: */
        if (!desc->action) {
                irq_shutdown(desc);
index b84141dcee5ed038d9e1939908b532e8d2f6e8b0..1b1b67a73218d58c5538e5d6129173fdf4f86751 100644 (file)
 
 #include "internals.h"
 
+/*
+ * Called from __setup_irq() with desc->lock held after @action has
+ * been installed in the action chain.
+ */
+void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action)
+{
+       desc->nr_actions++;
+
+       if (action->flags & IRQF_FORCE_RESUME)
+               desc->force_resume_depth++;
+
+       WARN_ON_ONCE(desc->force_resume_depth &&
+                    desc->force_resume_depth != desc->nr_actions);
+
+       if (action->flags & IRQF_NO_SUSPEND)
+               desc->no_suspend_depth++;
+
+       WARN_ON_ONCE(desc->no_suspend_depth &&
+                    desc->no_suspend_depth != desc->nr_actions);
+}
+
+/*
+ * Called from __free_irq() with desc->lock held after @action has
+ * been removed from the action chain.
+ */
+void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action)
+{
+       desc->nr_actions--;
+
+       if (action->flags & IRQF_FORCE_RESUME)
+               desc->force_resume_depth--;
+
+       if (action->flags & IRQF_NO_SUSPEND)
+               desc->no_suspend_depth--;
+}
+
 static void suspend_device_irq(struct irq_desc *desc, int irq)
 {
        if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND))