aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/softirq.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/softirq.c')
-rw-r--r--kernel/softirq.c152
1 files changed, 144 insertions, 8 deletions
diff --git a/kernel/softirq.c b/kernel/softirq.c
index c506f266a6b9..7110daeb9a90 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -6,6 +6,8 @@
6 * Distribute under GPLv2. 6 * Distribute under GPLv2.
7 * 7 *
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 *
10 * Remote softirq infrastructure is by Jens Axboe.
9 */ 11 */
10 12
11#include <linux/module.h> 13#include <linux/module.h>
@@ -46,7 +48,7 @@ irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;
46EXPORT_SYMBOL(irq_stat); 48EXPORT_SYMBOL(irq_stat);
47#endif 49#endif
48 50
49static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp; 51static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
50 52
51static DEFINE_PER_CPU(struct task_struct *, ksoftirqd); 53static DEFINE_PER_CPU(struct task_struct *, ksoftirqd);
52 54
@@ -205,7 +207,18 @@ restart:
205 207
206 do { 208 do {
207 if (pending & 1) { 209 if (pending & 1) {
210 int prev_count = preempt_count();
211
208 h->action(h); 212 h->action(h);
213
214 if (unlikely(prev_count != preempt_count())) {
215 printk(KERN_ERR "huh, entered softirq %td %p"
216 "with preempt_count %08x,"
217 " exited with %08x?\n", h - softirq_vec,
218 h->action, prev_count, preempt_count());
219 preempt_count() = prev_count;
220 }
221
209 rcu_bh_qsctr_inc(cpu); 222 rcu_bh_qsctr_inc(cpu);
210 } 223 }
211 h++; 224 h++;
@@ -254,16 +267,12 @@ asmlinkage void do_softirq(void)
254 */ 267 */
255void irq_enter(void) 268void irq_enter(void)
256{ 269{
257#ifdef CONFIG_NO_HZ
258 int cpu = smp_processor_id(); 270 int cpu = smp_processor_id();
271
259 if (idle_cpu(cpu) && !in_interrupt()) 272 if (idle_cpu(cpu) && !in_interrupt())
260 tick_nohz_stop_idle(cpu); 273 tick_check_idle(cpu);
261#endif 274
262 __irq_enter(); 275 __irq_enter();
263#ifdef CONFIG_NO_HZ
264 if (idle_cpu(cpu))
265 tick_nohz_update_jiffies();
266#endif
267} 276}
268 277
269#ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED 278#ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED
@@ -463,17 +472,144 @@ void tasklet_kill(struct tasklet_struct *t)
463 472
464EXPORT_SYMBOL(tasklet_kill); 473EXPORT_SYMBOL(tasklet_kill);
465 474
475DEFINE_PER_CPU(struct list_head [NR_SOFTIRQS], softirq_work_list);
476EXPORT_PER_CPU_SYMBOL(softirq_work_list);
477
478static void __local_trigger(struct call_single_data *cp, int softirq)
479{
480 struct list_head *head = &__get_cpu_var(softirq_work_list[softirq]);
481
482 list_add_tail(&cp->list, head);
483
484 /* Trigger the softirq only if the list was previously empty. */
485 if (head->next == &cp->list)
486 raise_softirq_irqoff(softirq);
487}
488
489#ifdef CONFIG_USE_GENERIC_SMP_HELPERS
490static void remote_softirq_receive(void *data)
491{
492 struct call_single_data *cp = data;
493 unsigned long flags;
494 int softirq;
495
496 softirq = cp->priv;
497
498 local_irq_save(flags);
499 __local_trigger(cp, softirq);
500 local_irq_restore(flags);
501}
502
503static int __try_remote_softirq(struct call_single_data *cp, int cpu, int softirq)
504{
505 if (cpu_online(cpu)) {
506 cp->func = remote_softirq_receive;
507 cp->info = cp;
508 cp->flags = 0;
509 cp->priv = softirq;
510
511 __smp_call_function_single(cpu, cp);
512 return 0;
513 }
514 return 1;
515}
516#else /* CONFIG_USE_GENERIC_SMP_HELPERS */
517static int __try_remote_softirq(struct call_single_data *cp, int cpu, int softirq)
518{
519 return 1;
520}
521#endif
522
523/**
524 * __send_remote_softirq - try to schedule softirq work on a remote cpu
525 * @cp: private SMP call function data area
526 * @cpu: the remote cpu
527 * @this_cpu: the currently executing cpu
528 * @softirq: the softirq for the work
529 *
530 * Attempt to schedule softirq work on a remote cpu. If this cannot be
531 * done, the work is instead queued up on the local cpu.
532 *
533 * Interrupts must be disabled.
534 */
535void __send_remote_softirq(struct call_single_data *cp, int cpu, int this_cpu, int softirq)
536{
537 if (cpu == this_cpu || __try_remote_softirq(cp, cpu, softirq))
538 __local_trigger(cp, softirq);
539}
540EXPORT_SYMBOL(__send_remote_softirq);
541
542/**
543 * send_remote_softirq - try to schedule softirq work on a remote cpu
544 * @cp: private SMP call function data area
545 * @cpu: the remote cpu
546 * @softirq: the softirq for the work
547 *
548 * Like __send_remote_softirq except that disabling interrupts and
549 * computing the current cpu is done for the caller.
550 */
551void send_remote_softirq(struct call_single_data *cp, int cpu, int softirq)
552{
553 unsigned long flags;
554 int this_cpu;
555
556 local_irq_save(flags);
557 this_cpu = smp_processor_id();
558 __send_remote_softirq(cp, cpu, this_cpu, softirq);
559 local_irq_restore(flags);
560}
561EXPORT_SYMBOL(send_remote_softirq);
562
563static int __cpuinit remote_softirq_cpu_notify(struct notifier_block *self,
564 unsigned long action, void *hcpu)
565{
566 /*
567 * If a CPU goes away, splice its entries to the current CPU
568 * and trigger a run of the softirq
569 */
570 if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) {
571 int cpu = (unsigned long) hcpu;
572 int i;
573
574 local_irq_disable();
575 for (i = 0; i < NR_SOFTIRQS; i++) {
576 struct list_head *head = &per_cpu(softirq_work_list[i], cpu);
577 struct list_head *local_head;
578
579 if (list_empty(head))
580 continue;
581
582 local_head = &__get_cpu_var(softirq_work_list[i]);
583 list_splice_init(head, local_head);
584 raise_softirq_irqoff(i);
585 }
586 local_irq_enable();
587 }
588
589 return NOTIFY_OK;
590}
591
592static struct notifier_block __cpuinitdata remote_softirq_cpu_notifier = {
593 .notifier_call = remote_softirq_cpu_notify,
594};
595
466void __init softirq_init(void) 596void __init softirq_init(void)
467{ 597{
468 int cpu; 598 int cpu;
469 599
470 for_each_possible_cpu(cpu) { 600 for_each_possible_cpu(cpu) {
601 int i;
602
471 per_cpu(tasklet_vec, cpu).tail = 603 per_cpu(tasklet_vec, cpu).tail =
472 &per_cpu(tasklet_vec, cpu).head; 604 &per_cpu(tasklet_vec, cpu).head;
473 per_cpu(tasklet_hi_vec, cpu).tail = 605 per_cpu(tasklet_hi_vec, cpu).tail =
474 &per_cpu(tasklet_hi_vec, cpu).head; 606 &per_cpu(tasklet_hi_vec, cpu).head;
607 for (i = 0; i < NR_SOFTIRQS; i++)
608 INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));
475 } 609 }
476 610
611 register_hotcpu_notifier(&remote_softirq_cpu_notifier);
612
477 open_softirq(TASKLET_SOFTIRQ, tasklet_action); 613 open_softirq(TASKLET_SOFTIRQ, tasklet_action);
478 open_softirq(HI_SOFTIRQ, tasklet_hi_action); 614 open_softirq(HI_SOFTIRQ, tasklet_hi_action);
479} 615}