Preview of yet more RCU changes for 3.3
Subsystems (15)
cover Preview of yet more RCU changes for 3.3
Hello!
This patchset adds to the pair that were previously posted earlier this month (see https://lkml.org/lkml/2011/11/2/363
and https://lkml.org/lkml/2011/11/15/302). This third set adds more rcutorture capabilities to ease automated testing under KVM (randomized CPU hotplug operations), updates Frederic's user-mode nohz RCU infrastructure, adds tracing for RCU_FAST_NO_HZ, makes a number of improvements to RCU_FAST_NO_HZ suggested by the resulting traces, speeds up TREE_RCU's dyntick-idle identification, and provides documentation updates. The patches are as follows:
1. Add randomized CPU-hotplug capability to rcutorture.
2. Decouple turning off the scheduler tick and informing RCU
of CPU idleness (courtesy of Frederic Weisbecker).
3,4,12. Documentation updates, with the addition of load/store atomicity
guarantees (#3) being of the most widespread interest.
5. Add event tracing for RCU_FAST_NO_HZ.
6,7,8,9,10,11,13.
RCU_FAST_NO_HZ improvements suggested by event-tracing output.
For a testing-only version of this patchset from git, please see the following subject-to-rebase branch:
git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git rcu/dev
Thanx, Paul
------------------------------------------------------------------------
b/Documentation/RCU/torture.txt | 8 + b/Documentation/atomic_ops.txt | 87 +++++++++++++ b/arch/arm/kernel/process.c | 6 b/arch/avr32/kernel/process.c | 6 b/arch/blackfin/kernel/process.c | 6 b/arch/microblaze/kernel/process.c | 6 b/arch/mips/kernel/process.c | 6 b/arch/openrisc/kernel/idle.c | 6 b/arch/powerpc/kernel/idle.c | 15 +- b/arch/powerpc/platforms/iseries/setup.c | 12 + b/arch/s390/kernel/process.c | 6 b/arch/sh/kernel/idle.c | 6 b/arch/sparc/kernel/process_64.c | 6 b/arch/tile/kernel/process.c | 6 b/arch/um/kernel/process.c | 6 b/arch/unicore32/kernel/process.c | 6 b/arch/x86/kernel/process_32.c | 6 b/include/linux/rcupdate.h | 15 ++ b/include/linux/srcu.h | 5 b/include/linux/tick.h | 47 ------- b/include/trace/events/rcu.h | 12 + b/kernel/rcutorture.c | 117 ++++++++++++++++- b/kernel/rcutree.c | 2 b/kernel/rcutree.h | 3 b/kernel/rcutree_plugin.h | 18 ++ b/kernel/time/tick-sched.c | 15 +- include/trace/events/rcu.h | 41 +++++- kernel/rcutree.c | 10 + kernel/rcutree.h | 2 kernel/rcutree_plugin.h | 205 ++++++++++++++++--------------- 30 files changed, 488 insertions(+), 204 deletions(-)
RFC PATCH 1/13 rcu: Add rcutorture CPU-hotplug capability
From: Paul E. McKenney <[email protected]>
Running CPU-hotplug operations concurrently with rcutorture has historically been a good way to find bugs in both RCU and CPU hotplug. This commit therefore adds an rcutorture module parameter called "onoff_interval" that causes a randomly selected CPU-hotplug operation to be executed at the specified interval, in seconds. The default value of "onoff_interval" is zero, which disables rcutorture-instigated CPU-hotplug operations.
--- Documentation/RCU/torture.txt | 8 +++ kernel/rcutorture.c | 117 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 120 insertions(+), 5 deletions(-)
Documentation/RCU/torture.txt +8 -0
@@ -61,6 +61,14 @@ nreaders This is the number of RCU reading threads supported. To properly exercise RCU implementations with preemptible read-side critical sections. +onoff_interval + The number of seconds between each attempt to execute a + randomly selected CPU-hotplug operation. Defaults to + zero, which disables CPU hotplugging. In HOTPLUG_CPU=n + kernels, rcutorture will silently refuse to do any + CPU-hotplug operations regardless of what value is + specified for onoff_interval. + shuffle_interval The number of seconds to keep the test threads affinitied to a particular subset of the CPUs, defaults to 3 seconds.
kernel/rcutorture.c +112 -5
187 lines not on this page · raw message
Signature · 1 line
1.7.3.2
RFC PATCH 2/13 nohz: Remove tick_nohz_idle_enter_norcu() / tick_nohz_idle_exit_norcu()
In reply to Paul E. McKenneyFrom: Frederic Weisbecker <[email protected]>
Those two APIs were provided to optimize the calls of tick_nohz_idle_enter() and rcu_idle_enter() into a single irq disabled section. This way no interrupt happening in-between would needlessly process any RCU job.
Now we are talking about an optimization for which benefits have yet to be measured. Let's start simple and completely decouple idle rcu and dyntick idle logics to simplify.
--- arch/arm/kernel/process.c | 6 +++- arch/avr32/kernel/process.c | 6 +++- arch/blackfin/kernel/process.c | 6 +++- arch/microblaze/kernel/process.c | 6 +++- arch/mips/kernel/process.c | 6 +++- arch/openrisc/kernel/idle.c | 6 +++- arch/powerpc/kernel/idle.c | 15 +++++----- arch/powerpc/platforms/iseries/setup.c | 12 +++++--- arch/s390/kernel/process.c | 6 +++- arch/sh/kernel/idle.c | 6 +++- arch/sparc/kernel/process_64.c | 6 +++- arch/tile/kernel/process.c | 6 +++- arch/um/kernel/process.c | 6 +++- arch/unicore32/kernel/process.c | 6 +++- arch/x86/kernel/process_32.c | 6 +++- include/linux/tick.h | 47 +------------------------------- kernel/time/tick-sched.c | 15 +++++----- 17 files changed, 76 insertions(+), 91 deletions(-)
arch/arm/kernel/process.c +4 -2
@@ -183,7 +183,8 @@ void cpu_idle(void)/* endless idle loop with no priority at all */ while (1 ) { -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();leds_event (led_idle_start);while (!need_resched ()) {#ifdef CONFIG_HOTPLUG_CPU
@@ -210,7 +211,8 @@ void cpu_idle(void) } }leds_event (led_idle_end); -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();schedule ();preempt_disable ();
arch/avr32/kernel/process.c +4 -2
@@ -34,10 +34,12 @@ void cpu_idle(void) {/* endless idle loop with no priority at all */ while (1 ) { -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();while (!need_resched ())cpu_idle_sleep (); -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();schedule ();preempt_disable ();
arch/blackfin/kernel/process.c +4 -2
@@ -88,10 +88,12 @@ void cpu_idle(void)#endif if (!idle) idle = default_idle; -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();while (!need_resched ())idle (); -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();schedule ();preempt_disable ();
arch/microblaze/kernel/process.c +4 -2
@@ -103,10 +103,12 @@ void cpu_idle(void)if (!idle) idle = default_idle; -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();while (!need_resched ())idle (); -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();schedule ();
arch/mips/kernel/process.c +4 -2
@@ -56,7 +56,8 @@ void __noreturn cpu_idle(void)/* endless idle loop with no priority at all */ while (1 ) { -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();while (!need_resched () &&cpu_online (cpu)) {#ifdef CONFIG_MIPS_MT_SMTC extern void smtc_idle_loop_hook (void );
@@ -77,7 +78,8 @@ void __noreturn cpu_idle(void) system_state == SYSTEM_BOOTING))play_dead ();#endif -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();schedule ();preempt_disable ();
arch/openrisc/kernel/idle.c +4 -2
@@ -51,7 +51,8 @@ void cpu_idle(void)/* endless idle loop with no priority at all */ while (1 ) { -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();while (!need_resched ()) {check_pgt_cache ();
@@ -69,7 +70,8 @@ void cpu_idle(void)set_thread_flag (TIF_POLLING_NRFLAG ); } -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();schedule ();preempt_disable ();
arch/powerpc/kernel/idle.c +7 -8
@@ -62,10 +62,10 @@ void cpu_idle(void)set_thread_flag (TIF_POLLING_NRFLAG );while (1 ) { -if (idle_uses_rcu) -tick_nohz_idle_enter (); -else -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +if (!idle_uses_rcu) +rcu_idle_enter (); +while (!need_resched () && !cpu_should_die ()) {ppc64_runlatch_off ();
@@ -102,10 +102,9 @@ void cpu_idle(void)HMT_medium ();ppc64_runlatch_on (); -if (idle_uses_rcu) -tick_nohz_idle_exit (); -else -tick_nohz_idle_exit_norcu (); +if (!idle_uses_rcu) +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();if (cpu_should_die ())cpu_die ();
arch/powerpc/platforms/iseries/setup.c +8 -4
@@ -563,7 +563,8 @@ static void yield_shared_processor(void)static void iseries_shared_idle (void ) {while (1 ) { -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();while (!need_resched () && !hvlpevent_is_pending ()) {local_irq_disable ();ppc64_runlatch_off ();
@@ -577,7 +578,8 @@ static void iseries_shared_idle(void) }ppc64_runlatch_on (); -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();if (hvlpevent_is_pending ())process_iSeries_events ();
@@ -593,7 +595,8 @@ static void iseries_dedicated_idle(void)set_thread_flag (TIF_POLLING_NRFLAG );while (1 ) { -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();if (!need_resched ()) {while (!need_resched ()) {ppc64_runlatch_off ();
@@ -610,7 +613,8 @@ static void iseries_dedicated_idle(void) }ppc64_runlatch_on (); -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();schedule ();preempt_disable ();
arch/s390/kernel/process.c +4 -2
@@ -91,10 +91,12 @@ static void default_idle(void)void cpu_idle (void ) {for (;;) { -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();while (!need_resched ())default_idle (); -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();schedule ();preempt_disable ();
arch/sh/kernel/idle.c +4 -2
@@ -89,7 +89,8 @@ void cpu_idle(void)/* endless idle loop with no priority at all */ while (1 ) { -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();while (!need_resched ()) {check_pgt_cache ();
@@ -111,7 +112,8 @@ void cpu_idle(void)start_critical_timings (); } -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();schedule ();preempt_disable ();
arch/sparc/kernel/process_64.c +4 -2
@@ -95,12 +95,14 @@ void cpu_idle(void)set_thread_flag (TIF_POLLING_NRFLAG );while (1 ) { -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();while (!need_resched () && !cpu_is_offline (cpu))sparc64_yield (cpu); -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();
arch/tile/kernel/process.c +4 -2
@@ -85,7 +85,8 @@ void cpu_idle(void)/* endless idle loop with no priority at all */ while (1 ) { -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();while (!need_resched ()) {if (cpu_is_offline (cpu))BUG ();/* no HOTPLUG_CPU */
@@ -105,7 +106,8 @@ void cpu_idle(void)local_irq_enable ();current_thread_info ()->status |=TS_POLLING ; } -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();schedule ();preempt_disable ();
arch/um/kernel/process.c +4 -2
@@ -246,10 +246,12 @@ void default_idle(void)if (need_resched ())schedule (); -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter (); nsecs =disable_timer ();idle_sleep (nsecs); -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit (); } }
arch/unicore32/kernel/process.c +4 -2
@@ -55,7 +55,8 @@ void cpu_idle(void) {/* endless idle loop with no priority at all */ while (1 ) { -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();while (!need_resched ()) {local_irq_disable ();stop_critical_timings ();
@@ -63,7 +64,8 @@ void cpu_idle(void)local_irq_enable ();start_critical_timings (); } -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();schedule ();preempt_disable ();
arch/x86/kernel/process_32.c +4 -2
@@ -99,7 +99,8 @@ void cpu_idle(void)/* endless idle loop with no priority at all */ while (1 ) { -tick_nohz_idle_enter_norcu (); +tick_nohz_idle_enter (); +rcu_idle_enter ();while (!need_resched ()) {check_pgt_cache ();
@@ -116,7 +117,8 @@ void cpu_idle(void)pm_idle ();start_critical_timings (); } -tick_nohz_idle_exit_norcu (); +rcu_idle_exit (); +tick_nohz_idle_exit ();preempt_enable_no_resched ();schedule ();preempt_disable ();
include/linux/tick.h +1 -46
60 lines not on this page · raw message
kernel/time/tick-sched.c +8 -7
@@ -454,21 +454,20 @@ out:* When the next event is more than a tick into the future, stop the idle tick * Called when we start the idle loop. * -* If no use of RCU is made in the idle loop between -* tick_nohz_idle_enter() and tick_nohz_idle_exit() calls, then -* tick_nohz_idle_enter_norcu() should be called instead and the arch -* doesn't need to call rcu_idle_enter() and rcu_idle_exit() explicitly. -* -* Otherwise the arch is responsible of calling: +* The arch is responsible of calling: * * - rcu_idle_enter() after its last use of RCU before the CPU is put * to sleep. * - rcu_idle_exit() before the first use of RCU after the CPU is woken up. */ -void __tick_nohz_idle_enter (void ) +void tick_nohz_idle_enter (void ) {struct tick_sched *ts; +WARN_ON_ONCE (irqs_disabled ()); + +local_irq_disable (); + ts = &__get_cpu_var (tick_cpu_sched);/* * set ts->inidle unconditionally. even if the system did not
@@ -477,6 +476,8 @@ void __tick_nohz_idle_enter(void)*/ ts->inidle =1 ;tick_nohz_stop_sched_tick (ts); + +local_irq_enable (); }/**
Signature · 1 line
1.7.3.2
RFC PATCH 4/13 rcu: Update trace_rcu_dyntick() header comment
In reply to Paul E. McKenneyFrom: Paul E. McKenney <[email protected]>
This commit updates the trace_rcu_dyntick() header comment to reflect events added by commit 4b4f421.
--- include/trace/events/rcu.h | 12 ++++++++++-- 1 files changed, 10 insertions(+), 2 deletions(-)
include/trace/events/rcu.h +10 -2
@@ -241,8 +241,16 @@ TRACE_EVENT(rcu_fqs,/* * Tracepoint for dyntick-idle entry/exit events. These take a string -* as argument: "Start" for entering dyntick-idle mode and "End" for -* leaving it. +* as argument: "Start" for entering dyntick-idle mode, "End" for +* leaving it, "--=" for events moving towards idle, and "++=" for events +* moving away from idle. "Error on entry: not idle task" and "Error on +* exit: not idle task" indicate that a non-idle task is erroneously +* toying with the idle loop. +* +* These events also take a pair of numbers, which indicate the nesting +* depth before and after the event of interest. Note that task-related +* events use the upper bits of each number, while interrupt-related +* events use the lower bits. */ TRACE_EVENT(rcu_dyntick,
Signature · 1 line
1.7.3.2
RFC PATCH 5/13 rcu: Add tracing for RCU_FAST_NO_HZ
In reply to Paul E. McKenneyFrom: Paul E. McKenney <[email protected]>
This commit adds trace_rcu_prep_idle(), which is invoked from rcu_prepare_for_idle() and rcu_wake_cpu() to trace attempts on the part of RCU to force CPUs into dyntick-idle mode.
--- include/trace/events/rcu.h | 37 +++++++++++++++++++++++++++++++++++++ kernel/rcutree_plugin.h | 18 +++++++++++++++--- 2 files changed, 52 insertions(+), 3 deletions(-)
include/trace/events/rcu.h +37 -0
@@ -275,6 +275,42 @@ TRACE_EVENT(rcu_dyntick, );/* +* Tracepoint for RCU preparation for idle, the goal being to get RCU +* processing done so that the current CPU can shut off its scheduling +* clock and enter dyntick-idle mode. One way to accomplish this is +* to drain all RCU callbacks from this CPU, and the other is to have +* done everything RCU requires for the current grace period. In this +* latter case, the CPU will be awakened at the end of the current grace +* period in order to process the remainder of its callbacks. +* +* These tracepoints take a string as argument: +* +* "No callbacks": Nothing to do, no callbacks on this CPU. +* "In holdoff": Nothing to do, holding off after unsuccessful attempt. +* "Dyntick with callbacks": Callbacks remain, but RCU doesn't need CPU. +* "Begin holdoff": Attempt failed, don't retry until next jiffy. +* "More callbacks": Still more callbacks, try again to clear them out. +* "Callbacks drained": All callbacks processed, off to dyntick idle! +* "CPU awakened at GP end": +*/ +TRACE_EVENT (rcu_prep_idle, + +TP_PROTO (char *reason), + +TP_ARGS (reason), + +TP_STRUCT__entry ( + __field(char *, reason) + ), + +TP_fast_assign ( + __entry->reason = reason; + ), + +TP_printk ("%s" , __entry->reason) +); + +/* * Tracepoint for the registration of a single RCU callback function. * The first argument is the type of RCU, the second argument is * a pointer to the RCU callback itself, and the third element is the
@@ -482,6 +518,7 @@ TRACE_EVENT(rcu_torture_read,#define trace_rcu_quiescent_state_report (rcuname, gpnum, mask, qsmask, level, grplo, grphi, gp_tasks) do { } while (0)#define trace_rcu_fqs (rcuname, gpnum, cpu, qsevent) do { } while (0)#define trace_rcu_dyntick (polarity, oldnesting, newnesting) do { } while (0) +#define trace_rcu_prep_idle (reason) do { } while (0)#define trace_rcu_callback (rcuname, rhp, qlen) do { } while (0)#define trace_rcu_kfree_callback (rcuname, rhp, offset, qlen) do { } while (0)#define trace_rcu_batch_start (rcuname, qlen, blimit) do { } while (0)
kernel/rcutree_plugin.h +15 -3
@@ -2031,10 +2031,13 @@ static void rcu_prepare_for_idle(int cpu)/* If no callbacks or in the holdoff period, enter dyntick-idle. */ if (!rcu_cpu_has_callbacks (cpu)) {per_cpu (rcu_dyntick_holdoff, cpu) = jiffies -1 ; +trace_rcu_prep_idle ("No callbacks" );return ; } -if (per_cpu (rcu_dyntick_holdoff, cpu) == jiffies) +if (per_cpu (rcu_dyntick_holdoff, cpu) == jiffies) { +trace_rcu_prep_idle ("In holdoff" );return ; + }/* Check and update the rcu_dyntick_drain sequencing. */ if (per_cpu (rcu_dyntick_drain, cpu) <=0 ) {
@@ -2044,9 +2047,11 @@ static void rcu_prepare_for_idle(int cpu)/* We have hit the limit, so time to give up. */ per_cpu (rcu_dyntick_holdoff, cpu) = jiffies;if (!rcu_pending (cpu)) { +trace_rcu_prep_idle ("Dyntick with callbacks" );per_cpu (rcu_awake_at_gp_end, cpu) =1 ;return ;/* Nothing to do immediately. */ } +trace_rcu_prep_idle ("Begin holdoff" );invoke_rcu_core ();/* Force the CPU out of dyntick-idle. */ return ; }
@@ -2073,9 +2078,15 @@ static void rcu_prepare_for_idle(int cpu) c = c ||per_cpu (rcu_bh_data, cpu).nxtlist; } -/* If RCU callbacks are still pending, RCU still needs this CPU. */ -if (c) +/* +* If RCU callbacks are still pending, RCU still needs this CPU. +* So try forcing the callbacks through the grace period. +*/ +if (c) { +trace_rcu_prep_idle ("More callbacks" );invoke_rcu_core (); + }else +trace_rcu_prep_idle ("Callbacks drained" ); }/*
@@ -2085,6 +2096,7 @@ static void rcu_prepare_for_idle(int cpu)*/ static void rcu_wake_cpu (void *unused) { +trace_rcu_prep_idle ("CPU awakened at GP end" );invoke_rcu_core (); }
Signature · 1 line
1.7.3.2
RFC PATCH 6/13 rcu: Go dyntick-idle more quickly if CPU has serviced current grace period
In reply to Paul E. McKenneyFrom: Paul E. McKenney <[email protected]>
The earlier version would attempt to push callbacks through five times before going into dyntick-idle mode if callbacks remained, but the CPU had done all that it needed to do for the current RCU grace periods. This is wasteful: In most cases, once the CPU has done all that it needs to for the current RCU grace periods, it will make no further progress on the callbacks no matter how many times it loops through the RCU core processing and the idle-entry code.
This commit therefore goes to dyntick-idle mode whenever the current CPU has done all it can for the current grace period.
--- kernel/rcutree_plugin.h | 24 ++++++++++++++++++------ 1 files changed, 18 insertions(+), 6 deletions(-)
kernel/rcutree_plugin.h +18 -6
@@ -2028,12 +2028,29 @@ static void rcu_prepare_for_idle(int cpu) {int c =0 ; -/* If no callbacks or in the holdoff period, enter dyntick-idle. */ +/* +* If there are no callbacks on this CPU or if RCU has no further +* need for this CPU at the moment, enter dyntick-idle mode. +* Also reset state so as to not prejudice later attempts. +*/ if (!rcu_cpu_has_callbacks (cpu)) {per_cpu (rcu_dyntick_holdoff, cpu) = jiffies -1 ; +per_cpu (rcu_dyntick_drain, cpu) =0 ;trace_rcu_prep_idle ("No callbacks" );return ; } +if (!rcu_pending (cpu)) { +trace_rcu_prep_idle ("Dyntick with callbacks" ); +per_cpu (rcu_dyntick_holdoff, cpu) = jiffies -1 ; +per_cpu (rcu_dyntick_drain, cpu) =0 ; +per_cpu (rcu_awake_at_gp_end, cpu) =1 ; +return ;/* Nothing to do immediately. */ + } + +/* +* If in holdoff mode, just return. We will presumably have +* refrained from disabling the scheduling-clock tick. +*/ if (per_cpu (rcu_dyntick_holdoff, cpu) == jiffies) {trace_rcu_prep_idle ("In holdoff" );return ;
@@ -2046,11 +2063,6 @@ static void rcu_prepare_for_idle(int cpu) }else if (--per_cpu (rcu_dyntick_drain, cpu) <=0 ) {/* We have hit the limit, so time to give up. */ per_cpu (rcu_dyntick_holdoff, cpu) = jiffies; -if (!rcu_pending (cpu)) { -trace_rcu_prep_idle ("Dyntick with callbacks" ); -per_cpu (rcu_awake_at_gp_end, cpu) =1 ; -return ;/* Nothing to do immediately. */ - }trace_rcu_prep_idle ("Begin holdoff" );invoke_rcu_core ();/* Force the CPU out of dyntick-idle. */ return ;
Signature · 1 line
1.7.3.2
RFC PATCH 7/13 rcu: Avoid needlessly IPIing CPUs at GP end
In reply to Paul E. McKenneyFrom: Paul E. McKenney <[email protected]>
If a CPU enters dyntick-idle mode with callbacks pending, it will need an IPI at the end of the grace period. However, if it exits dyntick-idle mode before the grace period ends, it will be needlessly IPIed at the end of the grace period.
Therefore, this commit clears the per-CPU rcu_awake_at_gp_end flag when a CPU determines that it does not need it. This in turn requires disabling interrupts across much of rcu_prepare_for_idle() in order to avoid having nested interrupts clearing this state out from under us.
--- kernel/rcutree_plugin.h | 16 ++++++++++++++-- 1 files changed, 14 insertions(+), 2 deletions(-)
kernel/rcutree_plugin.h +14 -2
@@ -2027,6 +2027,9 @@ int rcu_needs_cpu(int cpu)static void rcu_prepare_for_idle (int cpu) {int c =0 ; +unsigned long flags; + +local_irq_save (flags);/* * If there are no callbacks on this CPU or if RCU has no further
@@ -2036,14 +2039,17 @@ static void rcu_prepare_for_idle(int cpu)if (!rcu_cpu_has_callbacks (cpu)) {per_cpu (rcu_dyntick_holdoff, cpu) = jiffies -1 ;per_cpu (rcu_dyntick_drain, cpu) =0 ; +per_cpu (rcu_awake_at_gp_end, cpu) =0 ; +local_irq_restore (flags);trace_rcu_prep_idle ("No callbacks" );return ; }if (!rcu_pending (cpu)) { -trace_rcu_prep_idle ("Dyntick with callbacks" );per_cpu (rcu_dyntick_holdoff, cpu) = jiffies -1 ;per_cpu (rcu_dyntick_drain, cpu) =0 ;per_cpu (rcu_awake_at_gp_end, cpu) =1 ; +local_irq_restore (flags); +trace_rcu_prep_idle ("Dyntick with callbacks" );return ;/* Nothing to do immediately. */ }
@@ -2052,6 +2058,7 @@ static void rcu_prepare_for_idle(int cpu)* refrained from disabling the scheduling-clock tick. */ if (per_cpu (rcu_dyntick_holdoff, cpu) == jiffies) { +local_irq_restore (flags);trace_rcu_prep_idle ("In holdoff" );return ; }
@@ -2060,9 +2067,11 @@ static void rcu_prepare_for_idle(int cpu)if (per_cpu (rcu_dyntick_drain, cpu) <=0 ) {/* First time through, initialize the counter. */ per_cpu (rcu_dyntick_drain, cpu) =RCU_NEEDS_CPU_FLUSHES ; +per_cpu (rcu_awake_at_gp_end, cpu) =0 ; }else if (--per_cpu (rcu_dyntick_drain, cpu) <=0 ) {/* We have hit the limit, so time to give up. */ per_cpu (rcu_dyntick_holdoff, cpu) = jiffies; +local_irq_restore (flags);trace_rcu_prep_idle ("Begin holdoff" );invoke_rcu_core ();/* Force the CPU out of dyntick-idle. */ return ;
@@ -2095,10 +2104,13 @@ static void rcu_prepare_for_idle(int cpu)* So try forcing the callbacks through the grace period. */ if (c) { +local_irq_restore (flags);trace_rcu_prep_idle ("More callbacks" );invoke_rcu_core (); - }else + }else { +local_irq_restore (flags);trace_rcu_prep_idle ("Callbacks drained" ); + } }/*
Signature · 1 line
1.7.3.2
RFC PATCH 8/13 rcu: Eliminate RCU_FAST_NO_HZ grace-period hang
In reply to Paul E. McKenneyFrom: Paul E. McKenney <[email protected]>
With the new implementation of RCU_FAST_NO_HZ, it was possible to hang RCU grace periods as follows:
o CPU 0 attempts to go idle, cycles several times through the
rcu_prepare_for_idle() loop, then goes dyntick-idle when RCU needs nothing more from it, while still having at least on RCU callback pending.
o CPU 1 goes idle with no callbacks.
Both CPUs can then stay in dyntick-idle mode indefinitely, preventing the RCU grace period from ever completing, possibly hanging the system.
This commit therefore prevents CPUs that have RCU callbacks from entering dyntick-idle mode. This approach also eliminates the need for the end-of-grace-period IPIs used previously.
--- include/trace/events/rcu.h | 1 - kernel/rcutree.c | 2 - kernel/rcutree.h | 3 -- kernel/rcutree_plugin.h | 78 +------------------------------------------ 4 files changed, 2 insertions(+), 82 deletions(-)
include/trace/events/rcu.h +0 -1
@@ -287,7 +287,6 @@ TRACE_EVENT(rcu_dyntick,* * "No callbacks": Nothing to do, no callbacks on this CPU. * "In holdoff": Nothing to do, holding off after unsuccessful attempt. -* "Dyntick with callbacks": Callbacks remain, but RCU doesn't need CPU. * "Begin holdoff": Attempt failed, don't retry until next jiffy. * "More callbacks": Still more callbacks, try again to clear them out. * "Callbacks drained": All callbacks processed, off to dyntick idle!
kernel/rcutree.c +0 -2
@@ -1086,7 +1086,6 @@ static void rcu_report_qs_rsp(struct rcu_state *rsp, unsigned long flags)* callbacks are waiting on the grace period that just now * completed. */ -rcu_schedule_wake_gp_end ();if (*rdp->nxttail[RCU_WAIT_TAIL ] ==NULL ) {raw_spin_unlock (&rnp->lock);/* irqs remain disabled. */
@@ -1672,7 +1671,6 @@ static void rcu_process_callbacks(struct softirq_action *unused) &__get_cpu_var(rcu_sched_data));__rcu_process_callbacks (&rcu_bh_state, &__get_cpu_var (rcu_bh_data));rcu_preempt_process_callbacks (); -rcu_wake_cpus_for_gp_end ();trace_rcu_utilization ("End RCU core" ); }
kernel/rcutree.h +0 -3
@@ -88,7 +88,6 @@ struct rcu_dynticks {/* Process level is worth LLONG_MAX/2. */ int dynticks_nmi_nesting;/* Track NMI nesting level. */ atomic_t dynticks;/* Even value for idle, else odd. */ -int wake_gp_end;/* A GP ended, need to wake up CPUs. */ };/* RCU's kthread states for tracing. */
@@ -469,7 +468,5 @@ static void rcu_yield(void (*f)(unsigned long), unsigned long arg);static void rcu_cpu_kthread_setrt (int cpu,int to_rt);static void __cpuinit rcu_prepare_kthreads (int cpu);static void rcu_prepare_for_idle (int cpu); -static void rcu_wake_cpus_for_gp_end (void ); -static void rcu_schedule_wake_gp_end (void );#endif /* #ifndef RCU_TREE_NONCORE */
kernel/rcutree_plugin.h +2 -76
113 lines not on this page · raw message
Signature · 1 line
1.7.3.2
RFC PATCH 9/13 rcu: Reduce latency of rcu_prepare_for_idle()
In reply to Paul E. McKenneyFrom: Paul E. McKenney <[email protected]>
Re-enable interrupts across calls to quiescent-state functions and also across force_quiescent_state() to reduce latency.
--- kernel/rcutree_plugin.h | 12 +++++++----- 1 files changed, 7 insertions(+), 5 deletions(-)
kernel/rcutree_plugin.h +7 -5
@@ -2009,7 +2009,6 @@ int rcu_needs_cpu(int cpu)*/ static void rcu_prepare_for_idle (int cpu) { -int c =0 ;unsigned long flags;local_irq_save (flags);
@@ -2055,27 +2054,30 @@ static void rcu_prepare_for_idle(int cpu)*/ #ifdef CONFIG_TREE_PREEMPT_RCU if (per_cpu (rcu_preempt_data, cpu).nxtlist) { +local_irq_restore (flags);rcu_preempt_qs (cpu);force_quiescent_state (&rcu_preempt_state,0 ); - c = c ||per_cpu (rcu_preempt_data, cpu).nxtlist; +local_irq_save (flags); }#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ if (per_cpu (rcu_sched_data, cpu).nxtlist) { +local_irq_restore (flags);rcu_sched_qs (cpu);force_quiescent_state (&rcu_sched_state,0 ); - c = c ||per_cpu (rcu_sched_data, cpu).nxtlist; +local_irq_save (flags); }if (per_cpu (rcu_bh_data, cpu).nxtlist) { +local_irq_restore (flags);rcu_bh_qs (cpu);force_quiescent_state (&rcu_bh_state,0 ); - c = c ||per_cpu (rcu_bh_data, cpu).nxtlist; +local_irq_save (flags); }/* * If RCU callbacks are still pending, RCU still needs this CPU. * So try forcing the callbacks through the grace period. */ -if (c) { +if (rcu_cpu_has_callbacks (cpu)) {local_irq_restore (flags);trace_rcu_prep_idle ("More callbacks" );invoke_rcu_core ();
Signature · 1 line
1.7.3.2
RFC PATCH 10/13 rcu: Remove dynticks false positives and RCU failures
In reply to Paul E. McKenneyFrom: Paul E. McKenney <[email protected]>
Assertions in rcu_init_percpu_data() unknowingly relied on outgoing CPUs being turned off before reaching the idle loop. Unfortunately, when running under kvm/qemu on x86, CPUs really can get to idle before begin shut off. These CPUs are then born in dyntick-idle mode from an RCU perspective, which results in splats in rcu_init_percpu_data() and in RCU wrongly ignoring those CPUs despite them being active. This in turn can cause RCU to end grace periods prematurely, potentially freeing up memory that the newly onlined CPUs were still using. This is most decidedly not what we need to see in an RCU implementation.
This commit therefore replaces the assertions in rcu_init_percpu_data() with code that forces RCU's dyntick-idle view of newly onlined CPUs to match reality.
--- kernel/rcutree.c | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-)
kernel/rcutree.c +3 -2
@@ -2054,8 +2054,9 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp, int preemptible) rdp->qlen_last_fqs_check =0 ; rdp->n_force_qs_snap = rsp->n_force_qs; rdp->blimit = blimit; -WARN_ON_ONCE (rdp->dynticks->dynticks_nesting !=DYNTICK_TASK_NESTING ); -WARN_ON_ONCE ((atomic_read (&rdp->dynticks->dynticks) &0x1 ) !=1 ); + rdp->dynticks->dynticks_nesting =DYNTICK_TASK_NESTING ; +atomic_set (&rdp->dynticks->dynticks, + (atomic_read (&rdp->dynticks->dynticks) & ~0x1 ) +1 );raw_spin_unlock (&rnp->lock);/* irqs remain disabled. */ /*
Signature · 1 line
1.7.3.2
RFC PATCH 11/13 rcu: Identify dyntick-idle CPUs on first force_quiescent_state() pass
In reply to Paul E. McKenneyFrom: Paul E. McKenney <[email protected]>
Fixes and workarounds for a number of issues (for example, that in df4012edc) make it safe to once again detect dyntick-idle CPUs on the first pass of force_quiescent_state(), so this commit makes that change.
--- kernel/rcutree.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
kernel/rcutree.c +1 -1
@@ -607,7 +607,7 @@ int rcu_is_cpu_rrupt_from_idle(void)static int dyntick_save_progress_counter (struct rcu_data *rdp) { rdp->dynticks_snap =atomic_add_return (0 , &rdp->dynticks->dynticks); -return 0 ; +return (rdp->dynticks_snap &0x1 ) ==0 ; }/*
Signature · 1 line
1.7.3.2
RFC PATCH 12/13 rcu: Document same-context read-side constraints
In reply to Paul E. McKenneyThe intent is that a given RCU read-side critical section be confined to a single context. For example, it is illegal to invoke rcu_read_lock() in an exception handler and then invoke rcu_read_unlock() from the context of the task that received the exception.
--- include/linux/rcupdate.h | 15 +++++++++++++++ include/linux/srcu.h | 5 +++++ 2 files changed, 20 insertions(+), 0 deletions(-)
include/linux/rcupdate.h +15 -0
@@ -265,6 +265,11 @@ extern int debug_lockdep_rcu_enabled(void);* * Checks debug_lockdep_rcu_enabled() to prevent false positives during boot * and while lockdep is disabled. +* +* Note that rcu_read_lock() and the matching rcu_read_unlock() must +* occur in the same context, for example, it is illegal to invoke +* rcu_read_unlock() in process context if the matching rcu_read_lock() +* was invoked from within an irq handler. */ static inline int rcu_read_lock_held (void ) {
@@ -689,6 +694,11 @@ static inline void rcu_read_unlock(void)* critical sections in interrupt context can use just rcu_read_lock(), * though this should at least be commented to avoid confusing people * reading the code. +* +* Note that rcu_read_lock_bh() and the matching rcu_read_unlock_bh() +* must occur in the same context, for example, it is illegal to invoke +* rcu_read_unlock_bh() from one task if the matching rcu_read_lock_bh() +* was invoked from some other task. */ static inline void rcu_read_lock_bh (void ) {
@@ -716,6 +726,11 @@ static inline void rcu_read_unlock_bh(void)* are being done using call_rcu_sched() or synchronize_rcu_sched(). * Read-side critical sections can also be introduced by anything that * disables preemption, including local_irq_disable() and friends. +* +* Note that rcu_read_lock_sched() and the matching rcu_read_unlock_sched() +* must occur in the same context, for example, it is illegal to invoke +* rcu_read_unlock_sched() from process context if the matching +* rcu_read_lock_sched() was invoked from an NMI handler. */ static inline void rcu_read_lock_sched (void ) {
include/linux/srcu.h +5 -0
@@ -158,6 +158,11 @@ static inline int srcu_read_lock_held(struct srcu_struct *sp)* one way to indirectly wait on an SRCU grace period is to acquire * a mutex that is held elsewhere while calling synchronize_srcu() or * synchronize_srcu_expedited(). +* +* Note that srcu_read_lock() and the matching srcu_read_unlock() must +* occur in the same context, for example, it is illegal to invoke +* srcu_read_unlock() in an irq handler if the matching srcu_read_lock() +* was invoked in process context. */ static inline int srcu_read_lock (struct srcu_struct *sp)__acquires (sp) {
Signature · 1 line
1.7.3.2
RFC PATCH 13/13 rcu: Permit dyntick-idle with callbacks pending
In reply to Paul E. McKenneyFrom: Paul E. McKenney <[email protected]>
The current implementation of RCU_FAST_NO_HZ prevents CPUs from entering dyntick-idle state if they have RCU callbacks pending. Unfortunately, this has the side-effect of often preventing them from entering this state, especially if at least one other CPU is not in dyntick-idle state. However, the resulting per-tick wakeup is wasteful in many cases: if the CPU has already fully responded to the current RCU grace period, there will be nothing for it to do until this grace period ends, which will frequently take several jiffies.
This commit therefore permits a CPU that has done everything that the current grace period has asked of it (rcu_pending() == 0) even if it still as RCU callbacks pending. However, such a CPU posts a timer to wake it up several jiffies later (6 jiffies, based on experience with grace-period lengths). This wakeup is required to handle situations that can result in all CPUs being in dyntick-idle mode, thus failing to ever complete the current grace period. If a CPU wakes up before the timer goes off, then it cancels that timer, thus avoiding spurious wakeups.
--- include/trace/events/rcu.h | 3 +- kernel/rcutree.c | 3 ++ kernel/rcutree.h | 2 + kernel/rcutree_plugin.h | 75 +++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 78 insertions(+), 5 deletions(-)
include/trace/events/rcu.h +2 -1
@@ -288,9 +288,10 @@ TRACE_EVENT(rcu_dyntick,* "No callbacks": Nothing to do, no callbacks on this CPU. * "In holdoff": Nothing to do, holding off after unsuccessful attempt. * "Begin holdoff": Attempt failed, don't retry until next jiffy. +* "Dyntick with callbacks": Entering dyntick-idle despite callbacks. * "More callbacks": Still more callbacks, try again to clear them out. * "Callbacks drained": All callbacks processed, off to dyntick idle! -* "CPU awakened at GP end": +* "Timer": Timer fired to cause CPU to continue processing callbacks. */ TRACE_EVENT(rcu_prep_idle,
kernel/rcutree.c +3 -0
@@ -448,6 +448,7 @@ static void rcu_idle_exit_common(struct rcu_dynticks *rdtp, long long oldval)/* CPUs seeing atomic_inc() must see later RCU read-side crit sects */ smp_mb__after_atomic_inc ();/* See above. */ WARN_ON_ONCE (!(atomic_read (&rdtp->dynticks) &0x1 )); +rcu_cleanup_after_idle (smp_processor_id ());trace_rcu_dyntick ("End" , oldval, rdtp->dynticks_nesting);if (!is_idle_task (current)) {struct task_struct *idle =idle_task (smp_processor_id ());
@@ -2057,6 +2058,7 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp, int preemptible) rdp->dynticks->dynticks_nesting =DYNTICK_TASK_NESTING ;atomic_set (&rdp->dynticks->dynticks, (atomic_read (&rdp->dynticks->dynticks) & ~0x1 ) +1 ); +rcu_prepare_for_idle_init (cpu);raw_spin_unlock (&rnp->lock);/* irqs remain disabled. */ /*
@@ -2138,6 +2140,7 @@ static int __cpuinit rcu_cpu_notify(struct notifier_block *self,rcu_send_cbs_to_online (&rcu_bh_state);rcu_send_cbs_to_online (&rcu_sched_state);rcu_preempt_send_cbs_to_online (); +rcu_cleanup_after_idle (cpu);break ;case CPU_DEAD :case CPU_DEAD_FROZEN :
kernel/rcutree.h +2 -0
@@ -467,6 +467,8 @@ static void rcu_yield(void (*f)(unsigned long), unsigned long arg);#endif /* #ifdef CONFIG_RCU_BOOST */ static void rcu_cpu_kthread_setrt (int cpu,int to_rt);static void __cpuinit rcu_prepare_kthreads (int cpu); +static void rcu_prepare_for_idle_init (int cpu); +static void rcu_cleanup_after_idle (int cpu);static void rcu_prepare_for_idle (int cpu);#endif /* #ifndef RCU_TREE_NONCORE */
kernel/rcutree_plugin.h +71 -4
107 lines not on this page · raw message
Signature · 1 line
1.7.3.2