diff options
| -rw-r--r-- | kernel/irq_work.c | 51 |
1 files changed, 45 insertions, 6 deletions
diff --git a/kernel/irq_work.c b/kernel/irq_work.c index b3c113a14727..4ed17490f629 100644 --- a/kernel/irq_work.c +++ b/kernel/irq_work.c | |||
| @@ -12,6 +12,8 @@ | |||
| 12 | #include <linux/percpu.h> | 12 | #include <linux/percpu.h> |
| 13 | #include <linux/hardirq.h> | 13 | #include <linux/hardirq.h> |
| 14 | #include <linux/irqflags.h> | 14 | #include <linux/irqflags.h> |
| 15 | #include <linux/cpu.h> | ||
| 16 | #include <linux/notifier.h> | ||
| 15 | #include <asm/processor.h> | 17 | #include <asm/processor.h> |
| 16 | 18 | ||
| 17 | /* | 19 | /* |
| @@ -110,11 +112,7 @@ bool irq_work_needs_cpu(void) | |||
| 110 | return true; | 112 | return true; |
| 111 | } | 113 | } |
| 112 | 114 | ||
| 113 | /* | 115 | static void __irq_work_run(void) |
| 114 | * Run the irq_work entries on this cpu. Requires to be ran from hardirq | ||
| 115 | * context with local IRQs disabled. | ||
| 116 | */ | ||
| 117 | void irq_work_run(void) | ||
| 118 | { | 116 | { |
| 119 | struct irq_work *work; | 117 | struct irq_work *work; |
| 120 | struct llist_head *this_list; | 118 | struct llist_head *this_list; |
| @@ -124,7 +122,6 @@ void irq_work_run(void) | |||
| 124 | if (llist_empty(this_list)) | 122 | if (llist_empty(this_list)) |
| 125 | return; | 123 | return; |
| 126 | 124 | ||
| 127 | BUG_ON(!in_irq()); | ||
| 128 | BUG_ON(!irqs_disabled()); | 125 | BUG_ON(!irqs_disabled()); |
| 129 | 126 | ||
| 130 | llnode = llist_del_all(this_list); | 127 | llnode = llist_del_all(this_list); |
| @@ -149,6 +146,16 @@ void irq_work_run(void) | |||
| 149 | (void)cmpxchg(&work->flags, IRQ_WORK_BUSY, 0); | 146 | (void)cmpxchg(&work->flags, IRQ_WORK_BUSY, 0); |
| 150 | } | 147 | } |
| 151 | } | 148 | } |
| 149 | |||
| 150 | /* | ||
| 151 | * Run the irq_work entries on this cpu. Requires to be ran from hardirq | ||
| 152 | * context with local IRQs disabled. | ||
| 153 | */ | ||
| 154 | void irq_work_run(void) | ||
| 155 | { | ||
| 156 | BUG_ON(!in_irq()); | ||
| 157 | __irq_work_run(); | ||
| 158 | } | ||
| 152 | EXPORT_SYMBOL_GPL(irq_work_run); | 159 | EXPORT_SYMBOL_GPL(irq_work_run); |
| 153 | 160 | ||
| 154 | /* | 161 | /* |
| @@ -163,3 +170,35 @@ void irq_work_sync(struct irq_work *work) | |||
| 163 | cpu_relax(); | 170 | cpu_relax(); |
| 164 | } | 171 | } |
| 165 | EXPORT_SYMBOL_GPL(irq_work_sync); | 172 | EXPORT_SYMBOL_GPL(irq_work_sync); |
| 173 | |||
| 174 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 175 | static int irq_work_cpu_notify(struct notifier_block *self, | ||
| 176 | unsigned long action, void *hcpu) | ||
| 177 | { | ||
| 178 | long cpu = (long)hcpu; | ||
| 179 | |||
| 180 | switch (action) { | ||
| 181 | case CPU_DYING: | ||
| 182 | /* Called from stop_machine */ | ||
| 183 | if (WARN_ON_ONCE(cpu != smp_processor_id())) | ||
| 184 | break; | ||
| 185 | __irq_work_run(); | ||
| 186 | break; | ||
| 187 | default: | ||
| 188 | break; | ||
| 189 | } | ||
| 190 | return NOTIFY_OK; | ||
| 191 | } | ||
| 192 | |||
| 193 | static struct notifier_block cpu_notify; | ||
| 194 | |||
| 195 | static __init int irq_work_init_cpu_notifier(void) | ||
| 196 | { | ||
| 197 | cpu_notify.notifier_call = irq_work_cpu_notify; | ||
| 198 | cpu_notify.priority = 0; | ||
| 199 | register_cpu_notifier(&cpu_notify); | ||
| 200 | return 0; | ||
| 201 | } | ||
| 202 | device_initcall(irq_work_init_cpu_notifier); | ||
| 203 | |||
| 204 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
