aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/softirq.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/softirq.c')
-rw-r--r--kernel/softirq.c164
1 files changed, 83 insertions, 81 deletions
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 11025ccc06dd..490fcbb1dc5b 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -8,6 +8,8 @@
8 * Rewritten. Old one was good in 2.2, but in 2.3 it was immoral. --ANK (990903) 8 * Rewritten. Old one was good in 2.2, but in 2.3 it was immoral. --ANK (990903)
9 */ 9 */
10 10
11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12
11#include <linux/export.h> 13#include <linux/export.h>
12#include <linux/kernel_stat.h> 14#include <linux/kernel_stat.h>
13#include <linux/interrupt.h> 15#include <linux/interrupt.h>
@@ -54,7 +56,7 @@ static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp
54 56
55DEFINE_PER_CPU(struct task_struct *, ksoftirqd); 57DEFINE_PER_CPU(struct task_struct *, ksoftirqd);
56 58
57char *softirq_to_name[NR_SOFTIRQS] = { 59const char * const softirq_to_name[NR_SOFTIRQS] = {
58 "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL", 60 "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL",
59 "TASKLET", "SCHED", "HRTIMER", "RCU" 61 "TASKLET", "SCHED", "HRTIMER", "RCU"
60}; 62};
@@ -89,7 +91,7 @@ static void wakeup_softirqd(void)
89 * where hardirqs are disabled legitimately: 91 * where hardirqs are disabled legitimately:
90 */ 92 */
91#ifdef CONFIG_TRACE_IRQFLAGS 93#ifdef CONFIG_TRACE_IRQFLAGS
92static void __local_bh_disable(unsigned long ip, unsigned int cnt) 94void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
93{ 95{
94 unsigned long flags; 96 unsigned long flags;
95 97
@@ -107,33 +109,21 @@ static void __local_bh_disable(unsigned long ip, unsigned int cnt)
107 /* 109 /*
108 * Were softirqs turned off above: 110 * Were softirqs turned off above:
109 */ 111 */
110 if (softirq_count() == cnt) 112 if (softirq_count() == (cnt & SOFTIRQ_MASK))
111 trace_softirqs_off(ip); 113 trace_softirqs_off(ip);
112 raw_local_irq_restore(flags); 114 raw_local_irq_restore(flags);
113 115
114 if (preempt_count() == cnt) 116 if (preempt_count() == cnt)
115 trace_preempt_off(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1)); 117 trace_preempt_off(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1));
116} 118}
117#else /* !CONFIG_TRACE_IRQFLAGS */ 119EXPORT_SYMBOL(__local_bh_disable_ip);
118static inline void __local_bh_disable(unsigned long ip, unsigned int cnt)
119{
120 preempt_count_add(cnt);
121 barrier();
122}
123#endif /* CONFIG_TRACE_IRQFLAGS */ 120#endif /* CONFIG_TRACE_IRQFLAGS */
124 121
125void local_bh_disable(void)
126{
127 __local_bh_disable(_RET_IP_, SOFTIRQ_DISABLE_OFFSET);
128}
129
130EXPORT_SYMBOL(local_bh_disable);
131
132static void __local_bh_enable(unsigned int cnt) 122static void __local_bh_enable(unsigned int cnt)
133{ 123{
134 WARN_ON_ONCE(!irqs_disabled()); 124 WARN_ON_ONCE(!irqs_disabled());
135 125
136 if (softirq_count() == cnt) 126 if (softirq_count() == (cnt & SOFTIRQ_MASK))
137 trace_softirqs_on(_RET_IP_); 127 trace_softirqs_on(_RET_IP_);
138 preempt_count_sub(cnt); 128 preempt_count_sub(cnt);
139} 129}
@@ -148,10 +138,9 @@ void _local_bh_enable(void)
148 WARN_ON_ONCE(in_irq()); 138 WARN_ON_ONCE(in_irq());
149 __local_bh_enable(SOFTIRQ_DISABLE_OFFSET); 139 __local_bh_enable(SOFTIRQ_DISABLE_OFFSET);
150} 140}
151
152EXPORT_SYMBOL(_local_bh_enable); 141EXPORT_SYMBOL(_local_bh_enable);
153 142
154static inline void _local_bh_enable_ip(unsigned long ip) 143void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
155{ 144{
156 WARN_ON_ONCE(in_irq() || irqs_disabled()); 145 WARN_ON_ONCE(in_irq() || irqs_disabled());
157#ifdef CONFIG_TRACE_IRQFLAGS 146#ifdef CONFIG_TRACE_IRQFLAGS
@@ -165,8 +154,8 @@ static inline void _local_bh_enable_ip(unsigned long ip)
165 /* 154 /*
166 * Keep preemption disabled until we are done with 155 * Keep preemption disabled until we are done with
167 * softirq processing: 156 * softirq processing:
168 */ 157 */
169 preempt_count_sub(SOFTIRQ_DISABLE_OFFSET - 1); 158 preempt_count_sub(cnt - 1);
170 159
171 if (unlikely(!in_interrupt() && local_softirq_pending())) { 160 if (unlikely(!in_interrupt() && local_softirq_pending())) {
172 /* 161 /*
@@ -182,18 +171,7 @@ static inline void _local_bh_enable_ip(unsigned long ip)
182#endif 171#endif
183 preempt_check_resched(); 172 preempt_check_resched();
184} 173}
185 174EXPORT_SYMBOL(__local_bh_enable_ip);
186void local_bh_enable(void)
187{
188 _local_bh_enable_ip(_RET_IP_);
189}
190EXPORT_SYMBOL(local_bh_enable);
191
192void local_bh_enable_ip(unsigned long ip)
193{
194 _local_bh_enable_ip(ip);
195}
196EXPORT_SYMBOL(local_bh_enable_ip);
197 175
198/* 176/*
199 * We restart softirq processing for at most MAX_SOFTIRQ_RESTART times, 177 * We restart softirq processing for at most MAX_SOFTIRQ_RESTART times,
@@ -211,14 +189,49 @@ EXPORT_SYMBOL(local_bh_enable_ip);
211#define MAX_SOFTIRQ_TIME msecs_to_jiffies(2) 189#define MAX_SOFTIRQ_TIME msecs_to_jiffies(2)
212#define MAX_SOFTIRQ_RESTART 10 190#define MAX_SOFTIRQ_RESTART 10
213 191
192#ifdef CONFIG_TRACE_IRQFLAGS
193/*
194 * When we run softirqs from irq_exit() and thus on the hardirq stack we need
195 * to keep the lockdep irq context tracking as tight as possible in order to
196 * not miss-qualify lock contexts and miss possible deadlocks.
197 */
198
199static inline bool lockdep_softirq_start(void)
200{
201 bool in_hardirq = false;
202
203 if (trace_hardirq_context(current)) {
204 in_hardirq = true;
205 trace_hardirq_exit();
206 }
207
208 lockdep_softirq_enter();
209
210 return in_hardirq;
211}
212
213static inline void lockdep_softirq_end(bool in_hardirq)
214{
215 lockdep_softirq_exit();
216
217 if (in_hardirq)
218 trace_hardirq_enter();
219}
220#else
221static inline bool lockdep_softirq_start(void) { return false; }
222static inline void lockdep_softirq_end(bool in_hardirq) { }
223#endif
224
214asmlinkage void __do_softirq(void) 225asmlinkage void __do_softirq(void)
215{ 226{
216 struct softirq_action *h;
217 __u32 pending;
218 unsigned long end = jiffies + MAX_SOFTIRQ_TIME; 227 unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
219 int cpu;
220 unsigned long old_flags = current->flags; 228 unsigned long old_flags = current->flags;
221 int max_restart = MAX_SOFTIRQ_RESTART; 229 int max_restart = MAX_SOFTIRQ_RESTART;
230 struct softirq_action *h;
231 bool in_hardirq;
232 __u32 pending;
233 int softirq_bit;
234 int cpu;
222 235
223 /* 236 /*
224 * Mask out PF_MEMALLOC s current task context is borrowed for the 237 * Mask out PF_MEMALLOC s current task context is borrowed for the
@@ -230,8 +243,8 @@ asmlinkage void __do_softirq(void)
230 pending = local_softirq_pending(); 243 pending = local_softirq_pending();
231 account_irq_enter_time(current); 244 account_irq_enter_time(current);
232 245
233 __local_bh_disable(_RET_IP_, SOFTIRQ_OFFSET); 246 __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
234 lockdep_softirq_enter(); 247 in_hardirq = lockdep_softirq_start();
235 248
236 cpu = smp_processor_id(); 249 cpu = smp_processor_id();
237restart: 250restart:
@@ -242,30 +255,30 @@ restart:
242 255
243 h = softirq_vec; 256 h = softirq_vec;
244 257
245 do { 258 while ((softirq_bit = ffs(pending))) {
246 if (pending & 1) { 259 unsigned int vec_nr;
247 unsigned int vec_nr = h - softirq_vec; 260 int prev_count;
248 int prev_count = preempt_count(); 261
249 262 h += softirq_bit - 1;
250 kstat_incr_softirqs_this_cpu(vec_nr); 263
251 264 vec_nr = h - softirq_vec;
252 trace_softirq_entry(vec_nr); 265 prev_count = preempt_count();
253 h->action(h); 266
254 trace_softirq_exit(vec_nr); 267 kstat_incr_softirqs_this_cpu(vec_nr);
255 if (unlikely(prev_count != preempt_count())) {
256 printk(KERN_ERR "huh, entered softirq %u %s %p"
257 "with preempt_count %08x,"
258 " exited with %08x?\n", vec_nr,
259 softirq_to_name[vec_nr], h->action,
260 prev_count, preempt_count());
261 preempt_count_set(prev_count);
262 }
263 268
264 rcu_bh_qs(cpu); 269 trace_softirq_entry(vec_nr);
270 h->action(h);
271 trace_softirq_exit(vec_nr);
272 if (unlikely(prev_count != preempt_count())) {
273 pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",
274 vec_nr, softirq_to_name[vec_nr], h->action,
275 prev_count, preempt_count());
276 preempt_count_set(prev_count);
265 } 277 }
278 rcu_bh_qs(cpu);
266 h++; 279 h++;
267 pending >>= 1; 280 pending >>= softirq_bit;
268 } while (pending); 281 }
269 282
270 local_irq_disable(); 283 local_irq_disable();
271 284
@@ -278,16 +291,13 @@ restart:
278 wakeup_softirqd(); 291 wakeup_softirqd();
279 } 292 }
280 293
281 lockdep_softirq_exit(); 294 lockdep_softirq_end(in_hardirq);
282
283 account_irq_exit_time(current); 295 account_irq_exit_time(current);
284 __local_bh_enable(SOFTIRQ_OFFSET); 296 __local_bh_enable(SOFTIRQ_OFFSET);
285 WARN_ON_ONCE(in_interrupt()); 297 WARN_ON_ONCE(in_interrupt());
286 tsk_restore_flags(current, old_flags, PF_MEMALLOC); 298 tsk_restore_flags(current, old_flags, PF_MEMALLOC);
287} 299}
288 300
289
290
291asmlinkage void do_softirq(void) 301asmlinkage void do_softirq(void)
292{ 302{
293 __u32 pending; 303 __u32 pending;
@@ -311,8 +321,6 @@ asmlinkage void do_softirq(void)
311 */ 321 */
312void irq_enter(void) 322void irq_enter(void)
313{ 323{
314 int cpu = smp_processor_id();
315
316 rcu_irq_enter(); 324 rcu_irq_enter();
317 if (is_idle_task(current) && !in_interrupt()) { 325 if (is_idle_task(current) && !in_interrupt()) {
318 /* 326 /*
@@ -320,7 +328,7 @@ void irq_enter(void)
320 * here, as softirq will be serviced on return from interrupt. 328 * here, as softirq will be serviced on return from interrupt.
321 */ 329 */
322 local_bh_disable(); 330 local_bh_disable();
323 tick_check_idle(cpu); 331 tick_irq_enter();
324 _local_bh_enable(); 332 _local_bh_enable();
325 } 333 }
326 334
@@ -375,13 +383,13 @@ void irq_exit(void)
375#endif 383#endif
376 384
377 account_irq_exit_time(current); 385 account_irq_exit_time(current);
378 trace_hardirq_exit();
379 preempt_count_sub(HARDIRQ_OFFSET); 386 preempt_count_sub(HARDIRQ_OFFSET);
380 if (!in_interrupt() && local_softirq_pending()) 387 if (!in_interrupt() && local_softirq_pending())
381 invoke_softirq(); 388 invoke_softirq();
382 389
383 tick_irq_exit(); 390 tick_irq_exit();
384 rcu_irq_exit(); 391 rcu_irq_exit();
392 trace_hardirq_exit(); /* must be last! */
385} 393}
386 394
387/* 395/*
@@ -427,8 +435,7 @@ void open_softirq(int nr, void (*action)(struct softirq_action *))
427/* 435/*
428 * Tasklets 436 * Tasklets
429 */ 437 */
430struct tasklet_head 438struct tasklet_head {
431{
432 struct tasklet_struct *head; 439 struct tasklet_struct *head;
433 struct tasklet_struct **tail; 440 struct tasklet_struct **tail;
434}; 441};
@@ -447,7 +454,6 @@ void __tasklet_schedule(struct tasklet_struct *t)
447 raise_softirq_irqoff(TASKLET_SOFTIRQ); 454 raise_softirq_irqoff(TASKLET_SOFTIRQ);
448 local_irq_restore(flags); 455 local_irq_restore(flags);
449} 456}
450
451EXPORT_SYMBOL(__tasklet_schedule); 457EXPORT_SYMBOL(__tasklet_schedule);
452 458
453void __tasklet_hi_schedule(struct tasklet_struct *t) 459void __tasklet_hi_schedule(struct tasklet_struct *t)
@@ -461,7 +467,6 @@ void __tasklet_hi_schedule(struct tasklet_struct *t)
461 raise_softirq_irqoff(HI_SOFTIRQ); 467 raise_softirq_irqoff(HI_SOFTIRQ);
462 local_irq_restore(flags); 468 local_irq_restore(flags);
463} 469}
464
465EXPORT_SYMBOL(__tasklet_hi_schedule); 470EXPORT_SYMBOL(__tasklet_hi_schedule);
466 471
467void __tasklet_hi_schedule_first(struct tasklet_struct *t) 472void __tasklet_hi_schedule_first(struct tasklet_struct *t)
@@ -472,7 +477,6 @@ void __tasklet_hi_schedule_first(struct tasklet_struct *t)
472 __this_cpu_write(tasklet_hi_vec.head, t); 477 __this_cpu_write(tasklet_hi_vec.head, t);
473 __raise_softirq_irqoff(HI_SOFTIRQ); 478 __raise_softirq_irqoff(HI_SOFTIRQ);
474} 479}
475
476EXPORT_SYMBOL(__tasklet_hi_schedule_first); 480EXPORT_SYMBOL(__tasklet_hi_schedule_first);
477 481
478static void tasklet_action(struct softirq_action *a) 482static void tasklet_action(struct softirq_action *a)
@@ -492,7 +496,8 @@ static void tasklet_action(struct softirq_action *a)
492 496
493 if (tasklet_trylock(t)) { 497 if (tasklet_trylock(t)) {
494 if (!atomic_read(&t->count)) { 498 if (!atomic_read(&t->count)) {
495 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) 499 if (!test_and_clear_bit(TASKLET_STATE_SCHED,
500 &t->state))
496 BUG(); 501 BUG();
497 t->func(t->data); 502 t->func(t->data);
498 tasklet_unlock(t); 503 tasklet_unlock(t);
@@ -527,7 +532,8 @@ static void tasklet_hi_action(struct softirq_action *a)
527 532
528 if (tasklet_trylock(t)) { 533 if (tasklet_trylock(t)) {
529 if (!atomic_read(&t->count)) { 534 if (!atomic_read(&t->count)) {
530 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) 535 if (!test_and_clear_bit(TASKLET_STATE_SCHED,
536 &t->state))
531 BUG(); 537 BUG();
532 t->func(t->data); 538 t->func(t->data);
533 tasklet_unlock(t); 539 tasklet_unlock(t);
@@ -545,7 +551,6 @@ static void tasklet_hi_action(struct softirq_action *a)
545 } 551 }
546} 552}
547 553
548
549void tasklet_init(struct tasklet_struct *t, 554void tasklet_init(struct tasklet_struct *t,
550 void (*func)(unsigned long), unsigned long data) 555 void (*func)(unsigned long), unsigned long data)
551{ 556{
@@ -555,13 +560,12 @@ void tasklet_init(struct tasklet_struct *t,
555 t->func = func; 560 t->func = func;
556 t->data = data; 561 t->data = data;
557} 562}
558
559EXPORT_SYMBOL(tasklet_init); 563EXPORT_SYMBOL(tasklet_init);
560 564
561void tasklet_kill(struct tasklet_struct *t) 565void tasklet_kill(struct tasklet_struct *t)
562{ 566{
563 if (in_interrupt()) 567 if (in_interrupt())
564 printk("Attempt to kill tasklet from interrupt\n"); 568 pr_notice("Attempt to kill tasklet from interrupt\n");
565 569
566 while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) { 570 while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
567 do { 571 do {
@@ -571,7 +575,6 @@ void tasklet_kill(struct tasklet_struct *t)
571 tasklet_unlock_wait(t); 575 tasklet_unlock_wait(t);
572 clear_bit(TASKLET_STATE_SCHED, &t->state); 576 clear_bit(TASKLET_STATE_SCHED, &t->state);
573} 577}
574
575EXPORT_SYMBOL(tasklet_kill); 578EXPORT_SYMBOL(tasklet_kill);
576 579
577/* 580/*
@@ -721,9 +724,8 @@ static void takeover_tasklets(unsigned int cpu)
721} 724}
722#endif /* CONFIG_HOTPLUG_CPU */ 725#endif /* CONFIG_HOTPLUG_CPU */
723 726
724static int cpu_callback(struct notifier_block *nfb, 727static int cpu_callback(struct notifier_block *nfb, unsigned long action,
725 unsigned long action, 728 void *hcpu)
726 void *hcpu)
727{ 729{
728 switch (action) { 730 switch (action) {
729#ifdef CONFIG_HOTPLUG_CPU 731#ifdef CONFIG_HOTPLUG_CPU