diff options
Diffstat (limited to 'kernel/softirq.c')
-rw-r--r-- | kernel/softirq.c | 139 |
1 files changed, 132 insertions, 7 deletions
diff --git a/kernel/softirq.c b/kernel/softirq.c index 37d67aa2d56f..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> |
@@ -265,16 +267,12 @@ asmlinkage void do_softirq(void) | |||
265 | */ | 267 | */ |
266 | void irq_enter(void) | 268 | void irq_enter(void) |
267 | { | 269 | { |
268 | #ifdef CONFIG_NO_HZ | ||
269 | int cpu = smp_processor_id(); | 270 | int cpu = smp_processor_id(); |
271 | |||
270 | if (idle_cpu(cpu) && !in_interrupt()) | 272 | if (idle_cpu(cpu) && !in_interrupt()) |
271 | tick_nohz_stop_idle(cpu); | 273 | tick_check_idle(cpu); |
272 | #endif | 274 | |
273 | __irq_enter(); | 275 | __irq_enter(); |
274 | #ifdef CONFIG_NO_HZ | ||
275 | if (idle_cpu(cpu)) | ||
276 | tick_nohz_update_jiffies(); | ||
277 | #endif | ||
278 | } | 276 | } |
279 | 277 | ||
280 | #ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED | 278 | #ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED |
@@ -474,17 +472,144 @@ void tasklet_kill(struct tasklet_struct *t) | |||
474 | 472 | ||
475 | EXPORT_SYMBOL(tasklet_kill); | 473 | EXPORT_SYMBOL(tasklet_kill); |
476 | 474 | ||
475 | DEFINE_PER_CPU(struct list_head [NR_SOFTIRQS], softirq_work_list); | ||
476 | EXPORT_PER_CPU_SYMBOL(softirq_work_list); | ||
477 | |||
478 | static 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 | ||
490 | static 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 | |||
503 | static 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 */ | ||
517 | static 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 | */ | ||
535 | void __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 | } | ||
540 | EXPORT_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 | */ | ||
551 | void 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 | } | ||
561 | EXPORT_SYMBOL(send_remote_softirq); | ||
562 | |||
563 | static 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 | |||
592 | static struct notifier_block __cpuinitdata remote_softirq_cpu_notifier = { | ||
593 | .notifier_call = remote_softirq_cpu_notify, | ||
594 | }; | ||
595 | |||
477 | void __init softirq_init(void) | 596 | void __init softirq_init(void) |
478 | { | 597 | { |
479 | int cpu; | 598 | int cpu; |
480 | 599 | ||
481 | for_each_possible_cpu(cpu) { | 600 | for_each_possible_cpu(cpu) { |
601 | int i; | ||
602 | |||
482 | per_cpu(tasklet_vec, cpu).tail = | 603 | per_cpu(tasklet_vec, cpu).tail = |
483 | &per_cpu(tasklet_vec, cpu).head; | 604 | &per_cpu(tasklet_vec, cpu).head; |
484 | per_cpu(tasklet_hi_vec, cpu).tail = | 605 | per_cpu(tasklet_hi_vec, cpu).tail = |
485 | &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)); | ||
486 | } | 609 | } |
487 | 610 | ||
611 | register_hotcpu_notifier(&remote_softirq_cpu_notifier); | ||
612 | |||
488 | open_softirq(TASKLET_SOFTIRQ, tasklet_action); | 613 | open_softirq(TASKLET_SOFTIRQ, tasklet_action); |
489 | open_softirq(HI_SOFTIRQ, tasklet_hi_action); | 614 | open_softirq(HI_SOFTIRQ, tasklet_hi_action); |
490 | } | 615 | } |