diff options
author | Ian Campbell <ian.campbell@citrix.com> | 2011-10-03 10:37:00 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2011-10-17 05:42:49 -0400 |
commit | 9bab0b7fbaceec47d32db51cd9e59c82fb071f5a (patch) | |
tree | 19699e2c8463554c09fc44e3672c402687724d44 | |
parent | 32cffdde4a3ee6c2d9e0f0a94edecf1a9ce7586b (diff) |
genirq: Add IRQF_RESUME_EARLY and resume such IRQs earlier
This adds a mechanism to resume selected IRQs during syscore_resume
instead of dpm_resume_noirq.
Under Xen we need to resume IRQs associated with IPIs early enough
that the resched IPI is unmasked and we can therefore schedule
ourselves out of the stop_machine where the suspend/resume takes
place.
This issue was introduced by 676dc3cf5bc3 "xen: Use IRQF_FORCE_RESUME".
Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
Cc: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Jeremy Fitzhardinge <Jeremy.Fitzhardinge@citrix.com>
Cc: xen-devel <xen-devel@lists.xensource.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Link: http://lkml.kernel.org/r/1318713254.11016.52.camel@dagon.hellion.org.uk
Cc: stable@kernel.org (at least to 2.6.32.y)
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | drivers/xen/events.c | 2 | ||||
-rw-r--r-- | include/linux/interrupt.h | 3 | ||||
-rw-r--r-- | kernel/irq/pm.c | 48 |
3 files changed, 45 insertions, 8 deletions
diff --git a/drivers/xen/events.c b/drivers/xen/events.c index da70f5c32eb9..a758bc14d0f3 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c | |||
@@ -1021,7 +1021,7 @@ int bind_ipi_to_irqhandler(enum ipi_vector ipi, | |||
1021 | if (irq < 0) | 1021 | if (irq < 0) |
1022 | return irq; | 1022 | return irq; |
1023 | 1023 | ||
1024 | irqflags |= IRQF_NO_SUSPEND | IRQF_FORCE_RESUME; | 1024 | irqflags |= IRQF_NO_SUSPEND | IRQF_FORCE_RESUME | IRQF_EARLY_RESUME; |
1025 | retval = request_irq(irq, handler, irqflags, devname, dev_id); | 1025 | retval = request_irq(irq, handler, irqflags, devname, dev_id); |
1026 | if (retval != 0) { | 1026 | if (retval != 0) { |
1027 | unbind_from_irq(irq); | 1027 | unbind_from_irq(irq); |
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 664544ff77d5..a64b00e286f5 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h | |||
@@ -59,6 +59,8 @@ | |||
59 | * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend | 59 | * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend |
60 | * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set | 60 | * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set |
61 | * IRQF_NO_THREAD - Interrupt cannot be threaded | 61 | * IRQF_NO_THREAD - Interrupt cannot be threaded |
62 | * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device | ||
63 | * resume time. | ||
62 | */ | 64 | */ |
63 | #define IRQF_DISABLED 0x00000020 | 65 | #define IRQF_DISABLED 0x00000020 |
64 | #define IRQF_SAMPLE_RANDOM 0x00000040 | 66 | #define IRQF_SAMPLE_RANDOM 0x00000040 |
@@ -72,6 +74,7 @@ | |||
72 | #define IRQF_NO_SUSPEND 0x00004000 | 74 | #define IRQF_NO_SUSPEND 0x00004000 |
73 | #define IRQF_FORCE_RESUME 0x00008000 | 75 | #define IRQF_FORCE_RESUME 0x00008000 |
74 | #define IRQF_NO_THREAD 0x00010000 | 76 | #define IRQF_NO_THREAD 0x00010000 |
77 | #define IRQF_EARLY_RESUME 0x00020000 | ||
75 | 78 | ||
76 | #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) | 79 | #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) |
77 | 80 | ||
diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index f76fc00c9877..15e53b1766a6 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/irq.h> | 9 | #include <linux/irq.h> |
10 | #include <linux/module.h> | 10 | #include <linux/module.h> |
11 | #include <linux/interrupt.h> | 11 | #include <linux/interrupt.h> |
12 | #include <linux/syscore_ops.h> | ||
12 | 13 | ||
13 | #include "internals.h" | 14 | #include "internals.h" |
14 | 15 | ||
@@ -39,25 +40,58 @@ void suspend_device_irqs(void) | |||
39 | } | 40 | } |
40 | EXPORT_SYMBOL_GPL(suspend_device_irqs); | 41 | EXPORT_SYMBOL_GPL(suspend_device_irqs); |
41 | 42 | ||
42 | /** | 43 | static void resume_irqs(bool want_early) |
43 | * resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs() | ||
44 | * | ||
45 | * Enable all interrupt lines previously disabled by suspend_device_irqs() that | ||
46 | * have the IRQS_SUSPENDED flag set. | ||
47 | */ | ||
48 | void resume_device_irqs(void) | ||
49 | { | 44 | { |
50 | struct irq_desc *desc; | 45 | struct irq_desc *desc; |
51 | int irq; | 46 | int irq; |
52 | 47 | ||
53 | for_each_irq_desc(irq, desc) { | 48 | for_each_irq_desc(irq, desc) { |
54 | unsigned long flags; | 49 | unsigned long flags; |
50 | bool is_early = desc->action && | ||
51 | desc->action->flags & IRQF_EARLY_RESUME; | ||
52 | |||
53 | if (is_early != want_early) | ||
54 | continue; | ||
55 | 55 | ||
56 | raw_spin_lock_irqsave(&desc->lock, flags); | 56 | raw_spin_lock_irqsave(&desc->lock, flags); |
57 | __enable_irq(desc, irq, true); | 57 | __enable_irq(desc, irq, true); |
58 | raw_spin_unlock_irqrestore(&desc->lock, flags); | 58 | raw_spin_unlock_irqrestore(&desc->lock, flags); |
59 | } | 59 | } |
60 | } | 60 | } |
61 | |||
62 | /** | ||
63 | * irq_pm_syscore_ops - enable interrupt lines early | ||
64 | * | ||
65 | * Enable all interrupt lines with %IRQF_EARLY_RESUME set. | ||
66 | */ | ||
67 | static void irq_pm_syscore_resume(void) | ||
68 | { | ||
69 | resume_irqs(true); | ||
70 | } | ||
71 | |||
72 | static struct syscore_ops irq_pm_syscore_ops = { | ||
73 | .resume = irq_pm_syscore_resume, | ||
74 | }; | ||
75 | |||
76 | static int __init irq_pm_init_ops(void) | ||
77 | { | ||
78 | register_syscore_ops(&irq_pm_syscore_ops); | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | device_initcall(irq_pm_init_ops); | ||
83 | |||
84 | /** | ||
85 | * resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs() | ||
86 | * | ||
87 | * Enable all non-%IRQF_EARLY_RESUME interrupt lines previously | ||
88 | * disabled by suspend_device_irqs() that have the IRQS_SUSPENDED flag | ||
89 | * set as well as those with %IRQF_FORCE_RESUME. | ||
90 | */ | ||
91 | void resume_device_irqs(void) | ||
92 | { | ||
93 | resume_irqs(false); | ||
94 | } | ||
61 | EXPORT_SYMBOL_GPL(resume_device_irqs); | 95 | EXPORT_SYMBOL_GPL(resume_device_irqs); |
62 | 96 | ||
63 | /** | 97 | /** |