diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-28 15:56:32 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-28 15:56:32 -0400 |
| commit | 1ba4b8cb94e59b17fd0142a509eb583695c36db6 (patch) | |
| tree | e42d1967025670401758d32964a5fa048f59f10a /kernel | |
| parent | c4a227d89f758e582fd167bb15245f2704de99ef (diff) | |
| parent | cc3ce5176d83cd8ae1134f86e208ea758d6cb78e (diff) | |
Merge branch 'core-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'core-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
rcu: Start RCU kthreads in TASK_INTERRUPTIBLE state
rcu: Remove waitqueue usage for cpu, node, and boost kthreads
rcu: Avoid acquiring rcu_node locks in timer functions
atomic: Add atomic_or()
Documentation: Add statistics about nested locks
rcu: Decrease memory-barrier usage based on semi-formal proof
rcu: Make rcu_enter_nohz() pay attention to nesting
rcu: Don't do reschedule unless in irq
rcu: Remove old memory barriers from rcu_process_callbacks()
rcu: Add memory barriers
rcu: Fix unpaired rcu_irq_enter() from locking selftests
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/rcutree.c | 164 | ||||
| -rw-r--r-- | kernel/rcutree.h | 30 | ||||
| -rw-r--r-- | kernel/rcutree_plugin.h | 24 | ||||
| -rw-r--r-- | kernel/rcutree_trace.c | 12 |
4 files changed, 94 insertions, 136 deletions
diff --git a/kernel/rcutree.c b/kernel/rcutree.c index f07d2f03181a..77a7671dd147 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c | |||
| @@ -36,7 +36,7 @@ | |||
| 36 | #include <linux/interrupt.h> | 36 | #include <linux/interrupt.h> |
| 37 | #include <linux/sched.h> | 37 | #include <linux/sched.h> |
| 38 | #include <linux/nmi.h> | 38 | #include <linux/nmi.h> |
| 39 | #include <asm/atomic.h> | 39 | #include <linux/atomic.h> |
| 40 | #include <linux/bitops.h> | 40 | #include <linux/bitops.h> |
| 41 | #include <linux/module.h> | 41 | #include <linux/module.h> |
| 42 | #include <linux/completion.h> | 42 | #include <linux/completion.h> |
| @@ -95,7 +95,6 @@ static DEFINE_PER_CPU(struct task_struct *, rcu_cpu_kthread_task); | |||
| 95 | DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_status); | 95 | DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_status); |
| 96 | DEFINE_PER_CPU(int, rcu_cpu_kthread_cpu); | 96 | DEFINE_PER_CPU(int, rcu_cpu_kthread_cpu); |
| 97 | DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_loops); | 97 | DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_loops); |
| 98 | static DEFINE_PER_CPU(wait_queue_head_t, rcu_cpu_wq); | ||
| 99 | DEFINE_PER_CPU(char, rcu_cpu_has_work); | 98 | DEFINE_PER_CPU(char, rcu_cpu_has_work); |
| 100 | static char rcu_kthreads_spawnable; | 99 | static char rcu_kthreads_spawnable; |
| 101 | 100 | ||
| @@ -163,7 +162,7 @@ EXPORT_SYMBOL_GPL(rcu_note_context_switch); | |||
| 163 | #ifdef CONFIG_NO_HZ | 162 | #ifdef CONFIG_NO_HZ |
| 164 | DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { | 163 | DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { |
| 165 | .dynticks_nesting = 1, | 164 | .dynticks_nesting = 1, |
| 166 | .dynticks = 1, | 165 | .dynticks = ATOMIC_INIT(1), |
| 167 | }; | 166 | }; |
| 168 | #endif /* #ifdef CONFIG_NO_HZ */ | 167 | #endif /* #ifdef CONFIG_NO_HZ */ |
| 169 | 168 | ||
| @@ -322,13 +321,25 @@ void rcu_enter_nohz(void) | |||
| 322 | unsigned long flags; | 321 | unsigned long flags; |
| 323 | struct rcu_dynticks *rdtp; | 322 | struct rcu_dynticks *rdtp; |
| 324 | 323 | ||
| 325 | smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */ | ||
| 326 | local_irq_save(flags); | 324 | local_irq_save(flags); |
| 327 | rdtp = &__get_cpu_var(rcu_dynticks); | 325 | rdtp = &__get_cpu_var(rcu_dynticks); |
| 328 | rdtp->dynticks++; | 326 | if (--rdtp->dynticks_nesting) { |
| 329 | rdtp->dynticks_nesting--; | 327 | local_irq_restore(flags); |
| 330 | WARN_ON_ONCE(rdtp->dynticks & 0x1); | 328 | return; |
| 329 | } | ||
| 330 | /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ | ||
| 331 | smp_mb__before_atomic_inc(); /* See above. */ | ||
| 332 | atomic_inc(&rdtp->dynticks); | ||
| 333 | smp_mb__after_atomic_inc(); /* Force ordering with next sojourn. */ | ||
| 334 | WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1); | ||
| 331 | local_irq_restore(flags); | 335 | local_irq_restore(flags); |
| 336 | |||
| 337 | /* If the interrupt queued a callback, get out of dyntick mode. */ | ||
| 338 | if (in_irq() && | ||
| 339 | (__get_cpu_var(rcu_sched_data).nxtlist || | ||
| 340 | __get_cpu_var(rcu_bh_data).nxtlist || | ||
| 341 | rcu_preempt_needs_cpu(smp_processor_id()))) | ||
| 342 | set_need_resched(); | ||
| 332 | } | 343 | } |
| 333 | 344 | ||
| 334 | /* | 345 | /* |
| @@ -344,11 +355,16 @@ void rcu_exit_nohz(void) | |||
| 344 | 355 | ||
| 345 | local_irq_save(flags); | 356 | local_irq_save(flags); |
| 346 | rdtp = &__get_cpu_var(rcu_dynticks); | 357 | rdtp = &__get_cpu_var(rcu_dynticks); |
| 347 | rdtp->dynticks++; | 358 | if (rdtp->dynticks_nesting++) { |
| 348 | rdtp->dynticks_nesting++; | 359 | local_irq_restore(flags); |
| 349 | WARN_ON_ONCE(!(rdtp->dynticks & 0x1)); | 360 | return; |
| 361 | } | ||
| 362 | smp_mb__before_atomic_inc(); /* Force ordering w/previous sojourn. */ | ||
| 363 | atomic_inc(&rdtp->dynticks); | ||
| 364 | /* CPUs seeing atomic_inc() must see later RCU read-side crit sects */ | ||
| 365 | smp_mb__after_atomic_inc(); /* See above. */ | ||
| 366 | WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1)); | ||
| 350 | local_irq_restore(flags); | 367 | local_irq_restore(flags); |
| 351 | smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */ | ||
| 352 | } | 368 | } |
| 353 | 369 | ||
| 354 | /** | 370 | /** |
| @@ -362,11 +378,15 @@ void rcu_nmi_enter(void) | |||
| 362 | { | 378 | { |
| 363 | struct rcu_dynticks *rdtp = &__get_cpu_var(rcu_dynticks); | 379 | struct rcu_dynticks *rdtp = &__get_cpu_var(rcu_dynticks); |
| 364 | 380 | ||
| 365 | if (rdtp->dynticks & 0x1) | 381 | if (rdtp->dynticks_nmi_nesting == 0 && |
| 382 | (atomic_read(&rdtp->dynticks) & 0x1)) | ||
| 366 | return; | 383 | return; |
| 367 | rdtp->dynticks_nmi++; | 384 | rdtp->dynticks_nmi_nesting++; |
| 368 | WARN_ON_ONCE(!(rdtp->dynticks_nmi & 0x1)); | 385 | smp_mb__before_atomic_inc(); /* Force delay from prior write. */ |
| 369 | smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */ | 386 | atomic_inc(&rdtp->dynticks); |
| 387 | /* CPUs seeing atomic_inc() must see later RCU read-side crit sects */ | ||
| 388 | smp_mb__after_atomic_inc(); /* See above. */ | ||
| 389 | WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1)); | ||
| 370 | } | 390 | } |
| 371 | 391 | ||
| 372 | /** | 392 | /** |
| @@ -380,11 +400,14 @@ void rcu_nmi_exit(void) | |||
| 380 | { | 400 | { |
| 381 | struct rcu_dynticks *rdtp = &__get_cpu_var(rcu_dynticks); | 401 | struct rcu_dynticks *rdtp = &__get_cpu_var(rcu_dynticks); |
| 382 | 402 | ||
| 383 | if (rdtp->dynticks & 0x1) | 403 | if (rdtp->dynticks_nmi_nesting == 0 || |
| 404 | --rdtp->dynticks_nmi_nesting != 0) | ||
| 384 | return; | 405 | return; |
| 385 | smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */ | 406 | /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ |
| 386 | rdtp->dynticks_nmi++; | 407 | smp_mb__before_atomic_inc(); /* See above. */ |
| 387 | WARN_ON_ONCE(rdtp->dynticks_nmi & 0x1); | 408 | atomic_inc(&rdtp->dynticks); |
| 409 | smp_mb__after_atomic_inc(); /* Force delay to next write. */ | ||
| 410 | WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1); | ||
| 388 | } | 411 | } |
| 389 | 412 | ||
| 390 | /** | 413 | /** |
| @@ -395,13 +418,7 @@ void rcu_nmi_exit(void) | |||
| 395 | */ | 418 | */ |
| 396 | void rcu_irq_enter(void) | 419 | void rcu_irq_enter(void) |
| 397 | { | 420 | { |
| 398 | struct rcu_dynticks *rdtp = &__get_cpu_var(rcu_dynticks); | 421 | rcu_exit_nohz(); |
| 399 | |||
| 400 | if (rdtp->dynticks_nesting++) | ||
| 401 | return; | ||
| 402 | rdtp->dynticks++; | ||
| 403 | WARN_ON_ONCE(!(rdtp->dynticks & 0x1)); | ||
| 404 | smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */ | ||
| 405 | } | 422 | } |
| 406 | 423 | ||
| 407 | /** | 424 | /** |
| @@ -413,18 +430,7 @@ void rcu_irq_enter(void) | |||
| 413 | */ | 430 | */ |
| 414 | void rcu_irq_exit(void) | 431 | void rcu_irq_exit(void) |
| 415 | { | 432 | { |
| 416 | struct rcu_dynticks *rdtp = &__get_cpu_var(rcu_dynticks); | 433 | rcu_enter_nohz(); |
| 417 | |||
| 418 | if (--rdtp->dynticks_nesting) | ||
| 419 | return; | ||
| 420 | smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */ | ||
| 421 | rdtp->dynticks++; | ||
| 422 | WARN_ON_ONCE(rdtp->dynticks & 0x1); | ||
| 423 | |||
| 424 | /* If the interrupt queued a callback, get out of dyntick mode. */ | ||
| 425 | if (__this_cpu_read(rcu_sched_data.nxtlist) || | ||
| 426 | __this_cpu_read(rcu_bh_data.nxtlist)) | ||
| 427 | set_need_resched(); | ||
| 428 | } | 434 | } |
| 429 | 435 | ||
| 430 | #ifdef CONFIG_SMP | 436 | #ifdef CONFIG_SMP |
| @@ -436,19 +442,8 @@ void rcu_irq_exit(void) | |||
| 436 | */ | 442 | */ |
| 437 | static int dyntick_save_progress_counter(struct rcu_data *rdp) | 443 | static int dyntick_save_progress_counter(struct rcu_data *rdp) |
| 438 | { | 444 | { |
| 439 | int ret; | 445 | rdp->dynticks_snap = atomic_add_return(0, &rdp->dynticks->dynticks); |
| 440 | int snap; | 446 | return 0; |
| 441 | int snap_nmi; | ||
| 442 | |||
| 443 | snap = rdp->dynticks->dynticks; | ||
| 444 | snap_nmi = rdp->dynticks->dynticks_nmi; | ||
| 445 | smp_mb(); /* Order sampling of snap with end of grace period. */ | ||
| 446 | rdp->dynticks_snap = snap; | ||
| 447 | rdp->dynticks_nmi_snap = snap_nmi; | ||
| 448 | ret = ((snap & 0x1) == 0) && ((snap_nmi & 0x1) == 0); | ||
| 449 | if (ret) | ||
| 450 | rdp->dynticks_fqs++; | ||
| 451 | return ret; | ||
| 452 | } | 447 | } |
| 453 | 448 | ||
| 454 | /* | 449 | /* |
| @@ -459,16 +454,11 @@ static int dyntick_save_progress_counter(struct rcu_data *rdp) | |||
| 459 | */< | ||
