Commit cab303be authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Rafael J. Wysocki

genirq: Add sanity checks for PM options on shared interrupt lines

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: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 8df2e02c
...@@ -36,6 +36,11 @@ struct irq_desc; ...@@ -36,6 +36,11 @@ struct irq_desc;
* @threads_oneshot: bitfield to handle shared oneshot threads * @threads_oneshot: bitfield to handle shared oneshot threads
* @threads_active: number of irqaction threads currently running * @threads_active: number of irqaction threads currently running
* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers * @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 * @dir: /proc/irq/ procfs entry
* @name: flow handler name for /proc/interrupts output * @name: flow handler name for /proc/interrupts output
*/ */
...@@ -68,6 +73,11 @@ struct irq_desc { ...@@ -68,6 +73,11 @@ struct irq_desc {
unsigned long threads_oneshot; unsigned long threads_oneshot;
atomic_t threads_active; atomic_t threads_active;
wait_queue_head_t wait_for_threads; 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 #ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir; struct proc_dir_entry *dir;
#endif #endif
......
...@@ -194,3 +194,13 @@ static inline void kstat_incr_irqs_this_cpu(unsigned int irq, struct irq_desc *d ...@@ -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(*desc->kstat_irqs);
__this_cpu_inc(kstat.irqs_sum); __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
...@@ -1200,6 +1200,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1200,6 +1200,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
new->irq = irq; new->irq = irq;
*old_ptr = new; *old_ptr = new;
irq_pm_install_action(desc, new);
/* Reset broken irq detection when installing new handler */ /* Reset broken irq detection when installing new handler */
desc->irq_count = 0; desc->irq_count = 0;
desc->irqs_unhandled = 0; desc->irqs_unhandled = 0;
...@@ -1318,6 +1320,8 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id) ...@@ -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: */ /* Found it - now remove it from the list of entries: */
*action_ptr = action->next; *action_ptr = action->next;
irq_pm_remove_action(desc, action);
/* If this was the last handler, shut down the IRQ line: */ /* If this was the last handler, shut down the IRQ line: */
if (!desc->action) { if (!desc->action) {
irq_shutdown(desc); irq_shutdown(desc);
......
...@@ -13,6 +13,42 @@ ...@@ -13,6 +13,42 @@
#include "internals.h" #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) static void suspend_device_irq(struct irq_desc *desc, int irq)
{ {
if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND)) if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment