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