1
2
3
4
5
6
7
8
9
10
11
12#include <linux/config.h>
13#include <linux/mm.h>
14#include <linux/kernel_stat.h>
15#include <linux/interrupt.h>
16#include <linux/smp_lock.h>
17#include <linux/init.h>
18#include <linux/tqueue.h>
19#include <linux/percpu.h>
20#include <linux/notifier.h>
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41irq_cpustat_t irq_stat[NR_CPUS];
42
43static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
44
45
46
47
48
49
50
51static inline void wakeup_softirqd(unsigned cpu)
52{
53 struct task_struct * tsk = ksoftirqd_task(cpu);
54
55 if (tsk && tsk->state != TASK_RUNNING)
56 wake_up_process(tsk);
57}
58
59asmlinkage void do_softirq()
60{
61 __u32 pending;
62 unsigned long flags;
63 __u32 mask;
64 int cpu;
65
66 if (in_interrupt())
67 return;
68
69 local_irq_save(flags);
70 cpu = smp_processor_id();
71
72 pending = softirq_pending(cpu);
73
74 if (pending) {
75 struct softirq_action *h;
76
77 mask = ~pending;
78 local_bh_disable();
79restart:
80
81 softirq_pending(cpu) = 0;
82
83 local_irq_enable();
84
85 h = softirq_vec;
86
87 do {
88 if (pending & 1)
89 h->action(h);
90 h++;
91 pending >>= 1;
92 } while (pending);
93
94 local_irq_disable();
95
96 pending = softirq_pending(cpu);
97 if (pending & mask) {
98 mask &= ~pending;
99 goto restart;
100 }
101 __local_bh_enable();
102
103 if (pending)
104 wakeup_softirqd(cpu);
105 }
106
107 local_irq_restore(flags);
108}
109
110
111
112
113inline void cpu_raise_softirq(unsigned int cpu, unsigned int nr)
114{
115 __cpu_raise_softirq(cpu, nr);
116
117
118
119
120
121
122
123
124
125
126 if (!in_interrupt())
127 wakeup_softirqd(cpu);
128}
129
130void raise_softirq(unsigned int nr)
131{
132 unsigned long flags;
133
134 local_irq_save(flags);
135 cpu_raise_softirq(smp_processor_id(), nr);
136 local_irq_restore(flags);
137}
138
139void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
140{
141 softirq_vec[nr].data = data;
142 softirq_vec[nr].action = action;
143}
144
145
146
147struct tasklet_head
148{
149 struct tasklet_struct *list;
150};
151
152
153
154static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec) = { NULL };
155static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec) = { NULL };
156
157void __tasklet_schedule(struct tasklet_struct *t)
158{
159 unsigned long flags;
160
161 local_irq_save(flags);
162 t->next = __get_cpu_var(tasklet_vec).list;
163 __get_cpu_var(tasklet_vec).list = t;
164 cpu_raise_softirq(smp_processor_id(), TASKLET_SOFTIRQ);
165 local_irq_restore(flags);
166}
167
168void __tasklet_hi_schedule(struct tasklet_struct *t)
169{
170 unsigned long flags;
171
172 local_irq_save(flags);
173 t->next = __get_cpu_var(tasklet_hi_vec).list;
174 __get_cpu_var(tasklet_hi_vec).list = t;
175 cpu_raise_softirq(smp_processor_id(), HI_SOFTIRQ);
176 local_irq_restore(flags);
177}
178
179static void tasklet_action(struct softirq_action *a)
180{
181 struct tasklet_struct *list;
182
183 local_irq_disable();
184 list = __get_cpu_var(tasklet_vec).list;
185 __get_cpu_var(tasklet_vec).list = NULL;
186 local_irq_enable();
187
188 while (list) {
189 struct tasklet_struct *t = list;
190
191 list = list->next;
192
193 if (tasklet_trylock(t)) {
194 if (!atomic_read(&t->count)) {
195 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
196 BUG();
197 t->func(t->data);
198 tasklet_unlock(t);
199 continue;
200 }
201 tasklet_unlock(t);
202 }
203
204 local_irq_disable();
205 t->next = __get_cpu_var(tasklet_vec).list;
206 __get_cpu_var(tasklet_vec).list = t;
207 __cpu_raise_softirq(smp_processor_id(), TASKLET_SOFTIRQ);
208 local_irq_enable();
209 }
210}
211
212static void tasklet_hi_action(struct softirq_action *a)
213{
214 struct tasklet_struct *list;
215
216 local_irq_disable();
217 list = __get_cpu_var(tasklet_hi_vec).list;
218 __get_cpu_var(tasklet_hi_vec).list = NULL;
219 local_irq_enable();
220
221 while (list) {
222 struct tasklet_struct *t = list;
223
224 list = list->next;
225
226 if (tasklet_trylock(t)) {
227 if (!atomic_read(&t->count)) {
228 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
229 BUG();
230 t->func(t->data);
231 tasklet_unlock(t);
232 continue;
233 }
234 tasklet_unlock(t);
235 }
236
237 local_irq_disable();
238 t->next = __get_cpu_var(tasklet_hi_vec).list;
239 __get_cpu_var(tasklet_hi_vec).list = t;
240 __cpu_raise_softirq(smp_processor_id(), HI_SOFTIRQ);
241 local_irq_enable();
242 }
243}
244
245
246void tasklet_init(struct tasklet_struct *t,
247 void (*func)(unsigned long), unsigned long data)
248{
249 t->next = NULL;
250 t->state = 0;
251 atomic_set(&t->count, 0);
252 t->func = func;
253 t->data = data;
254}
255
256void tasklet_kill(struct tasklet_struct *t)
257{
258 if (in_interrupt())
259 printk("Attempt to kill tasklet from interrupt\n");
260
261 while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
262 do
263 yield();
264 while (test_bit(TASKLET_STATE_SCHED, &t->state));
265 }
266 tasklet_unlock_wait(t);
267 clear_bit(TASKLET_STATE_SCHED, &t->state);
268}
269
270
271
272
273
274static void (*bh_base[32])(void);
275struct tasklet_struct bh_task_vec[32];
276
277
278
279
280
281
282
283
284
285
286spinlock_t global_bh_lock = SPIN_LOCK_UNLOCKED;
287
288static void bh_action(unsigned long nr)
289{
290 if (!spin_trylock(&global_bh_lock))
291 goto resched;
292
293 if (bh_base[nr])
294 bh_base[nr]();
295
296 hardirq_endlock();
297 spin_unlock(&global_bh_lock);
298 return;
299
300 spin_unlock(&global_bh_lock);
301resched:
302 mark_bh(nr);
303}
304
305void init_bh(int nr, void (*routine)(void))
306{
307 bh_base[nr] = routine;
308 mb();
309}
310
311void remove_bh(int nr)
312{
313 tasklet_kill(bh_task_vec+nr);
314 bh_base[nr] = NULL;
315}
316
317void __init softirq_init()
318{
319 int i;
320
321 for (i=0; i<32; i++)
322 tasklet_init(bh_task_vec+i, bh_action, i);
323
324 open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
325 open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
326}
327
328void __run_task_queue(task_queue *list)
329{
330 struct list_head head, *next;
331 unsigned long flags;
332
333 spin_lock_irqsave(&tqueue_lock, flags);
334 list_add(&head, list);
335 list_del_init(list);
336 spin_unlock_irqrestore(&tqueue_lock, flags);
337
338 next = head.next;
339 while (next != &head) {
340 void (*f) (void *);
341 struct tq_struct *p;
342 void *data;
343
344 p = list_entry(next, struct tq_struct, list);
345 next = next->next;
346 f = p->routine;
347 data = p->data;
348 wmb();
349 p->sync = 0;
350 if (f)
351 f(data);
352 }
353}
354
355static int ksoftirqd(void * __bind_cpu)
356{
357 int cpu = (int) (long) __bind_cpu;
358
359 daemonize();
360 set_user_nice(current, 19);
361 current->flags |= PF_IOTHREAD;
362 sigfillset(¤t->blocked);
363
364
365 set_cpus_allowed(current, 1UL << cpu);
366 if (smp_processor_id() != cpu)
367 BUG();
368
369 sprintf(current->comm, "ksoftirqd_CPU%d", cpu);
370
371 __set_current_state(TASK_INTERRUPTIBLE);
372 mb();
373
374 ksoftirqd_task(cpu) = current;
375
376 for (;;) {
377 if (!softirq_pending(cpu))
378 schedule();
379
380 __set_current_state(TASK_RUNNING);
381
382 while (softirq_pending(cpu)) {
383 do_softirq();
384 cond_resched();
385 }
386
387 __set_current_state(TASK_INTERRUPTIBLE);
388 }
389}
390
391static int __devinit cpu_callback(struct notifier_block *nfb,
392 unsigned long action,
393 void *hcpu)
394{
395 int hotcpu = (unsigned long)hcpu;
396
397 if (action == CPU_ONLINE) {
398 if (kernel_thread(ksoftirqd, hcpu, CLONE_KERNEL) < 0) {
399 printk("ksoftirqd for %i failed\n", hotcpu);
400 return NOTIFY_BAD;
401 }
402
403 while (!ksoftirqd_task(hotcpu))
404 yield();
405 return NOTIFY_OK;
406 }
407 return NOTIFY_BAD;
408}
409
410static struct notifier_block cpu_nfb = { &cpu_callback, NULL, 0 };
411
412__init int spawn_ksoftirqd(void)
413{
414 cpu_callback(&cpu_nfb, CPU_ONLINE, (void *)(long)smp_processor_id());
415 register_cpu_notifier(&cpu_nfb);
416 return 0;
417}
418