diff options
| author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-29 13:19:27 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-29 13:19:27 -0500 |
| commit | 076d84bbdb396360d16aaa108c55aa1e24ad47a3 (patch) | |
| tree | 7ea509f9d6160fafa9ed6bdadeae649e204a8337 | |
| parent | d40e705903397445c6861a0a56c23e5b2e8f9b9a (diff) | |
| parent | 7be2a03e3174cee3a3cdcdf17db357470f51caff (diff) | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/mingo/linux-2.6-sched
* git://git.kernel.org/pub/scm/linux/kernel/git/mingo/linux-2.6-sched:
softlockup: fix task state setting
rcu: add support for dynamic ticks and preempt rcu
| -rw-r--r-- | include/linux/hardirq.h | 10 | ||||
| -rw-r--r-- | include/linux/rcuclassic.h | 3 | ||||
| -rw-r--r-- | include/linux/rcupreempt.h | 22 | ||||
| -rw-r--r-- | kernel/rcupreempt.c | 224 | ||||
| -rw-r--r-- | kernel/softirq.c | 1 | ||||
| -rw-r--r-- | kernel/softlockup.c | 13 | ||||
| -rw-r--r-- | kernel/time/tick-sched.c | 3 |
7 files changed, 266 insertions, 10 deletions
diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h index 2961ec788046..49829988bfa0 100644 --- a/include/linux/hardirq.h +++ b/include/linux/hardirq.h | |||
| @@ -109,6 +109,14 @@ static inline void account_system_vtime(struct task_struct *tsk) | |||
| 109 | } | 109 | } |
| 110 | #endif | 110 | #endif |
| 111 | 111 | ||
| 112 | #if defined(CONFIG_PREEMPT_RCU) && defined(CONFIG_NO_HZ) | ||
| 113 | extern void rcu_irq_enter(void); | ||
| 114 | extern void rcu_irq_exit(void); | ||
| 115 | #else | ||
| 116 | # define rcu_irq_enter() do { } while (0) | ||
| 117 | # define rcu_irq_exit() do { } while (0) | ||
| 118 | #endif /* CONFIG_PREEMPT_RCU */ | ||
| 119 | |||
| 112 | /* | 120 | /* |
| 113 | * It is safe to do non-atomic ops on ->hardirq_context, | 121 | * It is safe to do non-atomic ops on ->hardirq_context, |
| 114 | * because NMI handlers may not preempt and the ops are | 122 | * because NMI handlers may not preempt and the ops are |
| @@ -117,6 +125,7 @@ static inline void account_system_vtime(struct task_struct *tsk) | |||
| 117 | */ | 125 | */ |
| 118 | #define __irq_enter() \ | 126 | #define __irq_enter() \ |
| 119 | do { \ | 127 | do { \ |
| 128 | rcu_irq_enter(); \ | ||
| 120 | account_system_vtime(current); \ | 129 | account_system_vtime(current); \ |
| 121 | add_preempt_count(HARDIRQ_OFFSET); \ | 130 | add_preempt_count(HARDIRQ_OFFSET); \ |
| 122 | trace_hardirq_enter(); \ | 131 | trace_hardirq_enter(); \ |
| @@ -135,6 +144,7 @@ extern void irq_enter(void); | |||
| 135 | trace_hardirq_exit(); \ | 144 | trace_hardirq_exit(); \ |
| 136 | account_system_vtime(current); \ | 145 | account_system_vtime(current); \ |
| 137 | sub_preempt_count(HARDIRQ_OFFSET); \ | 146 | sub_preempt_count(HARDIRQ_OFFSET); \ |
| 147 | rcu_irq_exit(); \ | ||
| 138 | } while (0) | 148 | } while (0) |
| 139 | 149 | ||
| 140 | /* | 150 | /* |
diff --git a/include/linux/rcuclassic.h b/include/linux/rcuclassic.h index 4d6624260b4c..b3dccd68629e 100644 --- a/include/linux/rcuclassic.h +++ b/include/linux/rcuclassic.h | |||
| @@ -160,5 +160,8 @@ extern void rcu_restart_cpu(int cpu); | |||
| 160 | extern long rcu_batches_completed(void); | 160 | extern long rcu_batches_completed(void); |
| 161 | extern long rcu_batches_completed_bh(void); | 161 | extern long rcu_batches_completed_bh(void); |
| 162 | 162 | ||
| 163 | #define rcu_enter_nohz() do { } while (0) | ||
| 164 | #define rcu_exit_nohz() do { } while (0) | ||
| 165 | |||
| 163 | #endif /* __KERNEL__ */ | 166 | #endif /* __KERNEL__ */ |
| 164 | #endif /* __LINUX_RCUCLASSIC_H */ | 167 | #endif /* __LINUX_RCUCLASSIC_H */ |
diff --git a/include/linux/rcupreempt.h b/include/linux/rcupreempt.h index 60c2a033b19e..01152ed532c8 100644 --- a/include/linux/rcupreempt.h +++ b/include/linux/rcupreempt.h | |||
| @@ -82,5 +82,27 @@ extern struct rcupreempt_trace *rcupreempt_trace_cpu(int cpu); | |||
| 82 | 82 | ||
| 83 | struct softirq_action; | 83 | struct softirq_action; |
| 84 | 84 | ||
| 85 | #ifdef CONFIG_NO_HZ | ||
| 86 | DECLARE_PER_CPU(long, dynticks_progress_counter); | ||
| 87 | |||
| 88 | static inline void rcu_enter_nohz(void) | ||
| 89 | { | ||
| 90 | __get_cpu_var(dynticks_progress_counter)++; | ||
| 91 | WARN_ON(__get_cpu_var(dynticks_progress_counter) & 0x1); | ||
| 92 | mb(); | ||
| 93 | } | ||
| 94 | |||
| 95 | static inline void rcu_exit_nohz(void) | ||
| 96 | { | ||
| 97 | mb(); | ||
| 98 | __get_cpu_var(dynticks_progress_counter)++; | ||
| 99 | WARN_ON(!(__get_cpu_var(dynticks_progress_counter) & 0x1)); | ||
| 100 | } | ||
| 101 | |||
| 102 | #else /* CONFIG_NO_HZ */ | ||
| 103 | #define rcu_enter_nohz() do { } while (0) | ||
| 104 | #define rcu_exit_nohz() do { } while (0) | ||
| 105 | #endif /* CONFIG_NO_HZ */ | ||
| 106 | |||
| 85 | #endif /* __KERNEL__ */ | 107 | #endif /* __KERNEL__ */ |
| 86 | #endif /* __LINUX_RCUPREEMPT_H */ | 108 | #endif /* __LINUX_RCUPREEMPT_H */ |
diff --git a/kernel/rcupreempt.c b/kernel/rcupreempt.c index 987cfb7ade89..c7c52096df48 100644 --- a/kernel/rcupreempt.c +++ b/kernel/rcupreempt.c | |||
| @@ -23,6 +23,10 @@ | |||
| 23 | * to Suparna Bhattacharya for pushing me completely away | 23 | * to Suparna Bhattacharya for pushing me completely away |
| 24 | * from atomic instructions on the read side. | 24 | * from atomic instructions on the read side. |
| 25 | * | 25 | * |
| 26 | * - Added handling of Dynamic Ticks | ||
| 27 | * Copyright 2007 - Paul E. Mckenney <paulmck@us.ibm.com> | ||
| 28 | * - Steven Rostedt <srostedt@redhat.com> | ||
| 29 | * | ||
| 26 | * Papers: http://www.rdrop.com/users/paulmck/RCU | 30 | * Papers: http://www.rdrop.com/users/paulmck/RCU |
| 27 | * | 31 | * |
| 28 | * Design Document: http://lwn.net/Articles/253651/ | 32 | * Design Document: http://lwn.net/Articles/253651/ |
| @@ -409,6 +413,212 @@ static void __rcu_advance_callbacks(struct rcu_data *rdp) | |||
| 409 | } | 413 | } |
| 410 | } | 414 | } |
| 411 | 415 | ||
| 416 | #ifdef CONFIG_NO_HZ | ||
| 417 | |||
| 418 | DEFINE_PER_CPU(long, dynticks_progress_counter) = 1; | ||
| 419 | static DEFINE_PER_CPU(long, rcu_dyntick_snapshot); | ||
| 420 | static DEFINE_PER_CPU(int, rcu_update_flag); | ||
| 421 | |||
| 422 | /** | ||
| 423 | * rcu_irq_enter - Called from Hard irq handlers and NMI/SMI. | ||
| 424 | * | ||
| 425 | * If the CPU was idle with dynamic ticks active, this updates the | ||
| 426 | * dynticks_progress_counter to let the RCU handling know that the | ||
| 427 | * CPU is active. | ||
| 428 | */ | ||
| 429 | void rcu_irq_enter(void) | ||
| 430 | { | ||
| 431 | int cpu = smp_processor_id(); | ||
| 432 | |||
| 433 | if (per_cpu(rcu_update_flag, cpu)) | ||
| 434 | per_cpu(rcu_update_flag, cpu)++; | ||
| 435 | |||
| 436 | /* | ||
| 437 | * Only update if we are coming from a stopped ticks mode | ||
| 438 | * (dynticks_progress_counter is even). | ||
| 439 | */ | ||
| 440 | if (!in_interrupt() && | ||
| 441 | (per_cpu(dynticks_progress_counter, cpu) & 0x1) == 0) { | ||
| 442 | /* | ||
| 443 | * The following might seem like we could have a race | ||
| 444 | * with NMI/SMIs. But this really isn't a problem. | ||
| 445 | * Here we do a read/modify/write, and the race happens | ||
| 446 | * when an NMI/SMI comes in after the read and before | ||
| 447 | * the write. But NMI/SMIs will increment this counter | ||
| 448 | * twice before returning, so the zero bit will not | ||
| 449 | * be corrupted by the NMI/SMI which is the most important | ||
| 450 | * part. | ||
| 451 | * | ||
| 452 | * The only thing is that we would bring back the counter | ||
| 453 | * to a postion that it was in during the NMI/SMI. | ||
| 454 | * But the zero bit would be set, so the rest of the | ||
| 455 | * counter would again be ignored. | ||
| 456 | * | ||
| 457 | * On return from the IRQ, the counter may have the zero | ||
| 458 | * bit be 0 and the counter the same as the return from | ||
| 459 | * the NMI/SMI. If the state machine was so unlucky to | ||
| 460 | * see that, it still doesn't matter, since all | ||
| 461 | * RCU read-side critical sections on this CPU would | ||
| 462 | * have already completed. | ||
| 463 | */ | ||
| 464 | per_cpu(dynticks_progress_counter, cpu)++; | ||
| 465 | /* | ||
| 466 | * The following memory barrier ensures that any | ||
| 467 | * rcu_read_lock() primitives in the irq handler | ||
| 468 | * are seen by other CPUs to follow the above | ||
| 469 | * increment to dynticks_progress_counter. This is | ||
| 470 | * required in order for other CPUs to correctly | ||
| 471 | * determine when it is safe to advance the RCU | ||
| 472 | * grace-period state machine. | ||
| 473 | */ | ||
| 474 | smp_mb(); /* see above block comment. */ | ||
| 475 | /* | ||
| 476 | * Since we can't determine the dynamic tick mode from | ||
| 477 | * the dynticks_progress_counter after this routine, | ||
| 478 | * we use a second flag to acknowledge that we came | ||
| 479 | * from an idle state with ticks stopped. | ||
| 480 | */ | ||
| 481 | per_cpu(rcu_update_flag, cpu)++; | ||
| 482 | /* | ||
| 483 | * If we take an NMI/SMI now, they will also increment | ||
| 484 | * the rcu_update_flag, and will not update the | ||
| 485 | * dynticks_progress_counter on exit. That is for | ||
| 486 | * this IRQ to do. | ||
| 487 | */ | ||
| 488 | } | ||
| 489 | } | ||
| 490 | |||
| 491 | /** | ||
| 492 | * rcu_irq_exit - Called from exiting Hard irq context. | ||
| 493 | * | ||
| 494 | * If the CPU was idle with dynamic ticks active, update the | ||
| 495 | * dynticks_progress_counter to put let the RCU handling be | ||
| 496 | * aware that the CPU is going back to idle with no ticks. | ||
| 497 | */ | ||
