1
2
3
4
5
6
7
8
9#include <linux/module.h>
10#include <linux/kernel_stat.h>
11#include <linux/interrupt.h>
12#include <linux/init.h>
13#include <linux/mm.h>
14#include <linux/notifier.h>
15#include <linux/percpu.h>
16#include <linux/cpu.h>
17#include <linux/kthread.h>
18#include <linux/rcupdate.h>
19
20#include <asm/irq.h>
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39#ifndef __ARCH_IRQ_STAT
40irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;
41EXPORT_SYMBOL(irq_stat);
42#endif
43
44static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
45
46static DEFINE_PER_CPU(struct task_struct *, ksoftirqd);
47
48
49
50
51
52
53
54static inline void wakeup_softirqd(void)
55{
56
57 struct task_struct *tsk = __get_cpu_var(ksoftirqd);
58
59 if (tsk && tsk->state != TASK_RUNNING)
60 wake_up_process(tsk);
61}
62
63
64
65
66
67
68
69
70
71
72#define MAX_SOFTIRQ_RESTART 10
73
74asmlinkage void __do_softirq(void)
75{
76 struct softirq_action *h;
77 __u32 pending;
78 int max_restart = MAX_SOFTIRQ_RESTART;
79 int cpu;
80
81 pending = local_softirq_pending();
82
83 local_bh_disable();
84 cpu = smp_processor_id();
85restart:
86
87 local_softirq_pending() = 0;
88
89 local_irq_enable();
90
91 h = softirq_vec;
92
93 do {
94 if (pending & 1) {
95 h->action(h);
96 rcu_bh_qsctr_inc(cpu);
97 }
98 h++;
99 pending >>= 1;
100 } while (pending);
101
102 local_irq_disable();
103
104 pending = local_softirq_pending();
105 if (pending && --max_restart)
106 goto restart;
107
108 if (pending)
109 wakeup_softirqd();
110
111 __local_bh_enable();
112}
113
114#ifndef __ARCH_HAS_DO_SOFTIRQ
115
116asmlinkage void do_softirq(void)
117{
118 __u32 pending;
119 unsigned long flags;
120
121 if (in_interrupt())
122 return;
123
124 local_irq_save(flags);
125
126 pending = local_softirq_pending();
127
128 if (pending)
129 __do_softirq();
130
131 local_irq_restore(flags);
132}
133
134EXPORT_SYMBOL(do_softirq);
135
136#endif
137
138void local_bh_enable(void)
139{
140 __local_bh_enable();
141 WARN_ON(!softirq_count() && irqs_disabled());
142 if (unlikely(!in_interrupt() &&
143 local_softirq_pending()))
144 invoke_softirq();
145 preempt_check_resched();
146}
147EXPORT_SYMBOL(local_bh_enable);
148
149
150
151
152inline fastcall void raise_softirq_irqoff(unsigned int nr)
153{
154 __raise_softirq_irqoff(nr);
155
156
157
158
159
160
161
162
163
164
165 if (!in_interrupt())
166 wakeup_softirqd();
167}
168
169EXPORT_SYMBOL(raise_softirq_irqoff);
170
171void fastcall raise_softirq(unsigned int nr)
172{
173 unsigned long flags;
174
175 local_irq_save(flags);
176 raise_softirq_irqoff(nr);
177 local_irq_restore(flags);
178}
179
180void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
181{
182 softirq_vec[nr].data = data;
183 softirq_vec[nr].action = action;
184}
185
186EXPORT_SYMBOL(open_softirq);
187
188
189struct tasklet_head
190{
191 struct tasklet_struct *list;
192};
193
194
195
196static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec) = { NULL };
197static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec) = { NULL };
198
199void fastcall __tasklet_schedule(struct tasklet_struct *t)
200{
201 unsigned long flags;
202
203 local_irq_save(flags);
204 t->next = __get_cpu_var(tasklet_vec).list;
205 __get_cpu_var(tasklet_vec).list = t;
206 raise_softirq_irqoff(TASKLET_SOFTIRQ);
207 local_irq_restore(flags);
208}
209
210EXPORT_SYMBOL(__tasklet_schedule);
211
212void fastcall __tasklet_hi_schedule(struct tasklet_struct *t)
213{
214 unsigned long flags;
215
216 local_irq_save(flags);
217 t->next = __get_cpu_var(tasklet_hi_vec).list;
218 __get_cpu_var(tasklet_hi_vec).list = t;
219 raise_softirq_irqoff(HI_SOFTIRQ);
220 local_irq_restore(flags);
221}
222
223EXPORT_SYMBOL(__tasklet_hi_schedule);
224
225static void tasklet_action(struct softirq_action *a)
226{
227 struct tasklet_struct *list;
228
229 local_irq_disable();
230 list = __get_cpu_var(tasklet_vec).list;
231 __get_cpu_var(tasklet_vec).list = NULL;
232 local_irq_enable();
233
234 while (list) {
235 struct tasklet_struct *t = list;
236
237 list = list->next;
238
239 if (tasklet_trylock(t)) {
240 if (!atomic_read(&t->count)) {
241 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
242 BUG();
243 t->func(t->data);
244 tasklet_unlock(t);
245 continue;
246 }
247 tasklet_unlock(t);
248 }
249
250 local_irq_disable();
251 t->next = __get_cpu_var(tasklet_vec).list;
252 __get_cpu_var(tasklet_vec).list = t;
253 __raise_softirq_irqoff(TASKLET_SOFTIRQ);
254 local_irq_enable();
255 }
256}
257
258static void tasklet_hi_action(struct softirq_action *a)
259{
260 struct tasklet_struct *list;
261
262 local_irq_disable();
263 list = __get_cpu_var(tasklet_hi_vec).list;
264 __get_cpu_var(tasklet_hi_vec).list = NULL;
265 local_irq_enable();
266
267 while (list) {
268 struct tasklet_struct *t = list;
269
270 list = list->next;
271
272 if (tasklet_trylock(t)) {
273 if (!atomic_read(&t->count)) {
274 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
275 BUG();
276 t->func(t->data);
277 tasklet_unlock(t);
278 continue;
279 }
280 tasklet_unlock(t);
281 }
282
283 local_irq_disable();
284 t->next = __get_cpu_var(tasklet_hi_vec).list;
285 __get_cpu_var(tasklet_hi_vec).list = t;
286 __raise_softirq_irqoff(HI_SOFTIRQ);
287 local_irq_enable();
288 }
289}
290
291
292void tasklet_init(struct tasklet_struct *t,
293 void (*func)(unsigned long), unsigned long data)
294{
295 t->next = NULL;
296 t->state = 0;
297 atomic_set(&t->count, 0);
298 t->func = func;
299 t->data = data;
300}
301
302EXPORT_SYMBOL(tasklet_init);
303
304void tasklet_kill(struct tasklet_struct *t)
305{
306 if (in_interrupt())
307 printk("Attempt to kill tasklet from interrupt\n");
308
309 while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
310 do
311 yield();
312 while (test_bit(TASKLET_STATE_SCHED, &t->state));
313 }
314 tasklet_unlock_wait(t);
315 clear_bit(TASKLET_STATE_SCHED, &t->state);
316}
317
318EXPORT_SYMBOL(tasklet_kill);
319
320struct tasklet_head saved_tasklet;
321
322void dump_clear_tasklet(void)
323{
324 saved_tasklet.list = __get_cpu_var(tasklet_vec).list;
325 __get_cpu_var(tasklet_vec).list = NULL;
326}
327
328EXPORT_SYMBOL_GPL(dump_clear_tasklet);
329
330void dump_run_tasklet(void)
331{
332 struct tasklet_struct *list;
333
334 list = __get_cpu_var(tasklet_vec).list;
335 __get_cpu_var(tasklet_vec).list = NULL;
336
337 while (list) {
338 struct tasklet_struct *t = list;
339 list = list->next;
340
341 if (!atomic_read(&t->count) &&
342 (test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)))
343 t->func(t->data);
344
345 t->next = __get_cpu_var(tasklet_vec).list;
346 __get_cpu_var(tasklet_vec).list = t;
347 }
348}
349
350EXPORT_SYMBOL_GPL(dump_run_tasklet);
351
352void __init softirq_init(void)
353{
354 open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
355 open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
356}
357
358static int ksoftirqd(void * __bind_cpu)
359{
360 set_user_nice(current, 19);
361 current->flags |= PF_NOFREEZE;
362
363 set_current_state(TASK_INTERRUPTIBLE);
364
365 while (!kthread_should_stop()) {
366 if (!local_softirq_pending())
367 schedule();
368
369 __set_current_state(TASK_RUNNING);
370
371 while (local_softirq_pending()) {
372
373
374
375 preempt_disable();
376 if (cpu_is_offline((long)__bind_cpu))
377 goto wait_to_die;
378 do_softirq();
379 preempt_enable();
380 cond_resched();
381 }
382
383 set_current_state(TASK_INTERRUPTIBLE);
384 }
385 __set_current_state(TASK_RUNNING);
386 return 0;
387
388wait_to_die:
389 preempt_enable();
390
391 set_current_state(TASK_INTERRUPTIBLE);
392 while (!kthread_should_stop()) {
393 schedule();
394 set_current_state(TASK_INTERRUPTIBLE);
395 }
396 __set_current_state(TASK_RUNNING);
397 return 0;
398}
399
400#ifdef CONFIG_HOTPLUG_CPU
401
402
403
404
405
406
407
408
409
410void tasklet_kill_immediate(struct tasklet_struct *t, unsigned int cpu)
411{
412 struct tasklet_struct **i;
413
414 BUG_ON(cpu_online(cpu));
415 BUG_ON(test_bit(TASKLET_STATE_RUN, &t->state));
416
417 if (!test_bit(TASKLET_STATE_SCHED, &t->state))
418 return;
419
420
421 for (i = &per_cpu(tasklet_vec, cpu).list; *i; i = &(*i)->next) {
422 if (*i == t) {
423 *i = t->next;
424 return;
425 }
426 }
427 BUG();
428}
429
430static void takeover_tasklets(unsigned int cpu)
431{
432 struct tasklet_struct **i;
433
434
435 local_irq_disable();
436
437
438 for (i = &__get_cpu_var(tasklet_vec).list; *i; i = &(*i)->next);
439 *i = per_cpu(tasklet_vec, cpu).list;
440 per_cpu(tasklet_vec, cpu).list = NULL;
441 raise_softirq_irqoff(TASKLET_SOFTIRQ);
442
443 for (i = &__get_cpu_var(tasklet_hi_vec).list; *i; i = &(*i)->next);
444 *i = per_cpu(tasklet_hi_vec, cpu).list;
445 per_cpu(tasklet_hi_vec, cpu).list = NULL;
446 raise_softirq_irqoff(HI_SOFTIRQ);
447
448 local_irq_enable();
449}
450#endif
451
452static int __devinit cpu_callback(struct notifier_block *nfb,
453 unsigned long action,
454 void *hcpu)
455{
456 int hotcpu = (unsigned long)hcpu;
457 struct task_struct *p;
458
459 switch (action) {
460 case CPU_UP_PREPARE:
461 BUG_ON(per_cpu(tasklet_vec, hotcpu).list);
462 BUG_ON(per_cpu(tasklet_hi_vec, hotcpu).list);
463 p = kthread_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu);
464 if (IS_ERR(p)) {
465 printk("ksoftirqd for %i failed\n", hotcpu);
466 return NOTIFY_BAD;
467 }
468 kthread_bind(p, hotcpu);
469 per_cpu(ksoftirqd, hotcpu) = p;
470 break;
471 case CPU_ONLINE:
472 wake_up_process(per_cpu(ksoftirqd, hotcpu));
473 break;
474#ifdef CONFIG_HOTPLUG_CPU
475 case CPU_UP_CANCELED:
476
477 kthread_bind(per_cpu(ksoftirqd, hotcpu), smp_processor_id());
478 case CPU_DEAD:
479 p = per_cpu(ksoftirqd, hotcpu);
480 per_cpu(ksoftirqd, hotcpu) = NULL;
481 kthread_stop(p);
482 takeover_tasklets(hotcpu);
483 break;
484#endif
485 }
486 return NOTIFY_OK;
487}
488
489static struct notifier_block __devinitdata cpu_nfb = {
490 .notifier_call = cpu_callback
491};
492
493__init int spawn_ksoftirqd(void)
494{
495 void *cpu = (void *)(long)smp_processor_id();
496 cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);
497 cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);
498 register_cpu_notifier(&cpu_nfb);
499 return 0;
500}
501