v2 RCU idle/no-CB changes for 3.9
Subsystems (3)
cover v2 RCU idle/no-CB changes for 3.9
Hello!
This series contains changes to RCU_FAST_NO_HZ idle entry/exit and also removes restrictions on no-CBs CPUs. This series contains some commits that are still a bit on experimental side, so you should avoid using these patches unless you would like to help debug them. ;-)
1. Remove restrictions on no-CBs CPUs. This patch is probably the
highest-risk of the group.
2. Allow some control of no-CBs CPUs at kernel-build time. The option
of most interest is probably the one that makes -all- CPUs be no-CBs CPUs.
3. Distinguish the no-CBs kthreads for the various RCU flavors.
Without this patch, CPU 0 would have up to three kthreads all named "rcuo0", which is less than optimal.
4. Export RCU_FAST_NO_HZ parameters to sysfs to allow run-time
adjustment.
5. Re-introduce callback acceleration during grace-period cleanup.
Now that the callbacks are associated with specific grace periods, such acceleration is idempotent, and it is now safe to accelerate more than needed. (In contrast, in the past, too-frequent callback acceleration resulted in infrequent RCU failures.)
6. Use the newly numbered callbacks to greatly reduce the CPU overhead
incurred at idle entry by RCU_FAST_NO_HZ. The fact that the callbacks are now numbered means that instead of repeatedly cranking the RCU state machine to try to get all callbacks invoked, we can instead rely on the numbering so that the CPU can take full advantage of any grace periods that elapse while it is asleep. CPUs with callbacks still have limited sleep times, especially if they have at least one non-lazy callback queued.
7-12. Allow CPUs to make known their need for future grace periods,
which is also used to reduce the need for frenetic RCU state-machine cranking upon RCU_FAST_NO_HZ entry to idle.
7. Move the release of the root rcu_node structure's ->lock
to then end of rcu_start_gp().
8. Repurpose no-CB's grace-period event tracing to that of
future grace periods, which share no-CB's grace-period mechanism.
9. Move the release of the root rcu_node structure's ->lock
to rcu_start_gp()'s callers.
10. Rename the rcu_node ->n_nocb_gp_requests field to
->need_future_gp.
11. Abstract rcu_start_future_gp() from rcu_nocb_wait_gp()
to that RCU_FAST_NO_HZ can use the no-CB CPUs mechanism for allowing a CPU to record its need for future grace periods.
12. Make rcu_accelerate_cbs() note the need for future
grace periods, thus avoiding delays in starting grace periods that currently happen due to the CPUs needing those grace periods being out of action when the previous grace period ends.
Changes since v1:
o Fixed a deadlock in #1 spotted by Xie ChanglongX.
o Updated #2 to bring the abbreviations in line with conventional
per-CPU kthread naming.
o Moved the first two patches into their own group.
Thanx, Paul
b/Documentation/kernel-parameters.txt | 7 b/include/linux/rcupdate.h | 1 b/include/trace/events/rcu.h | 71 ++ b/init/Kconfig | 52 +- b/kernel/rcutree.c | 277 ++++++++-- b/kernel/rcutree.h | 39 - b/kernel/rcutree_plugin.h | 865 ++++++++++++++-------------------- b/kernel/rcutree_trace.c | 2 8 files changed, 710 insertions(+), 604 deletions(-)
PATCH 1/12 rcu: Remove restrictions on no-CBs CPUs
From: "Paul E. McKenney" <[email protected]>
Currently, CPU 0 is constrained to not be a no-CBs CPU, and furthermore at least one no-CBs CPU must remain online at any given time. These restrictions are problematic in some situations, such as cases where all CPUs must run a real-time workload that needs to be insulated from OS jitter and latencies due to RCU callback invocation. This commit therefore provides no-CBs CPUs a way to start and to wait for grace periods independently of the normal RCU callback mechanisms. This approach allows any or all of the CPUs to be designated as no-CBs CPUs, and allows any proper subset of the CPUs (whether no-CBs CPUs or not) to be offlined.
This commit also provides event tracing, as well as a fix for a locking bug spotted by Xie ChanglongX <[email protected]>.
--- include/trace/events/rcu.h | 55 +++++++++ init/Kconfig | 4 +- kernel/rcutree.c | 18 ++-- kernel/rcutree.h | 20 ++-- kernel/rcutree_plugin.h | 276 +++++++++++++++++++++++++++----------------- 5 files changed, 250 insertions(+), 123 deletions(-)
include/trace/events/rcu.h +55 -0
@@ -72,6 +72,58 @@ TRACE_EVENT(rcu_grace_period, );/* +* Tracepoint for no-callbacks grace-period events. The caller should +* pull the data from the rcu_node structure, other than rcuname, which +* comes from the rcu_state structure, and event, which is one of the +* following: +* +* "Startleaf": Request a nocb grace period based on leaf-node data. +* "Startedleaf": Leaf-node start proved sufficient. +* "Startedleafroot": Leaf-node start proved sufficient after checking root. +* "Startedroot": Requested a nocb grace period based on root-node data. +* "StartWait": Start waiting for the requested grace period. +* "ResumeWait": Resume waiting after signal. +* "EndWait": Complete wait. +* "Cleanup": Clean up rcu_node structure after previous GP. +* "CleanupMore": Clean up, and another no-CB GP is needed. +*/ +TRACE_EVENT (rcu_nocb_grace_period, + + TP_PROTO(char *rcuname, unsigned long gpnum, unsigned long completed, + unsigned long c, u8 level, int grplo, int grphi, + char *gpevent), + +TP_ARGS (rcuname, gpnum, completed, c, level, grplo, grphi, gpevent), + +TP_STRUCT__entry ( + __field(char *, rcuname) + __field(unsigned long, gpnum) + __field(unsigned long, completed) + __field(unsigned long, c) + __field(u8, level) + __field(int, grplo) + __field(int, grphi) + __field(char *, gpevent) + ), + +TP_fast_assign ( + __entry->rcuname = rcuname; + __entry->gpnum = gpnum; + __entry->completed = completed; + __entry->c = c; + __entry->level = level; + __entry->grplo = grplo; + __entry->grphi = grphi; + __entry->gpevent = gpevent; + ), + +TP_printk ("%s %lu %lu %lu %u %d %d %s" , + __entry->rcuname, __entry->gpnum, __entry->completed, + __entry->c, __entry->level, __entry->grplo, __entry->grphi, + __entry->gpevent) +); + +/* * Tracepoint for grace-period-initialization events. These are * distinguished by the type of RCU, the new grace-period number, the * rcu_node structure level, the starting and ending CPU covered by the
@@ -601,6 +653,9 @@ TRACE_EVENT(rcu_barrier,#define trace_rcu_grace_period (rcuname, gpnum, gpevent) do { } while (0)#define trace_rcu_grace_period_init (rcuname, gpnum, level, grplo, grphi, \ qsmask) do { } while (0) +#define trace_rcu_nocb_grace_period (rcuname, gpnum, completed, c, \ + level, grplo, grphi, event) \ + do { } while (0)#define trace_rcu_preempt_task (rcuname, pid, gpnum) do { } while (0)#define trace_rcu_unlock_preempted_task (rcuname, gpnum, pid) do { } while (0) #define trace_rcu_quiescent_state_report(rcuname, gpnum, mask, qsmask, level, \
init/Kconfig +2 -2
@@ -665,7 +665,7 @@ config RCU_BOOST_DELAY Accept the default if unsure. config RCU_NOCB_CPU - bool "Offload RCU callback processing from boot-selected CPUs" + bool "Offload RCU callback processing from boot-selected CPUs (EXPERIMENTAL" depends on TREE_RCU || TREE_PREEMPT_RCU default n help
@@ -683,7 +683,7 @@ config RCU_NOCB_CPU callback, and (2) affinity or cgroups can be used to force the kthreads to run on whatever set of CPUs is desired. - Say Y here if you want reduced OS jitter on selected CPUs. + Say Y here if you want to help to debug reduced OS jitter. Say N here if you are unsure. endmenu # "RCU Subsystem"
kernel/rcutree.c +10 -8
@@ -310,6 +310,8 @@ cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp)if (rcu_gp_in_progress (rsp))return 0 ;/* No, a grace period is already in progress. */ +if (rcu_nocb_needs_gp (rsp)) +return 1 ;/* Yes, a no-CBs CPU needs one. */ if (!rdp->nxttail[RCU_NEXT_TAIL ])return 0 ;/* No, this is a no-CBs (or offline) CPU. */ if (*rdp->nxttail[RCU_NEXT_READY_TAIL ])
@@ -1035,10 +1037,11 @@ static void init_callback_list(struct rcu_data *rdp) {int i; +if (init_nocb_callback_list (rdp)) +return ; rdp->nxtlist =NULL ;for (i =0 ; i <RCU_NEXT_SIZE ; i++) rdp->nxttail[i] = &rdp->nxtlist; -init_nocb_callback_list (rdp); }/*
@@ -1361,6 +1364,7 @@ int rcu_gp_fqs(struct rcu_state *rsp, int fqs_state_in)static void rcu_gp_cleanup (struct rcu_state *rsp) {unsigned long gp_duration; +int nocb =0 ;struct rcu_data *rdp;struct rcu_node *rnp =rcu_get_root (rsp);
@@ -1391,11 +1395,13 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)rcu_for_each_node_breadth_first (rsp, rnp) {raw_spin_lock_irq (&rnp->lock); rnp->completed = rsp->gpnum; + nocb +=rcu_nocb_gp_cleanup (rsp, rnp);raw_spin_unlock_irq (&rnp->lock);cond_resched (); } rnp =rcu_get_root (rsp);raw_spin_lock_irq (&rnp->lock); +rcu_nocb_gp_set (rnp, nocb); rsp->completed = rsp->gpnum;/* Declare grace period done. */ trace_rcu_grace_period (rsp->name, rsp->completed,"end" );
@@ -2909,7 +2915,6 @@ static int __cpuinit rcu_cpu_notify(struct notifier_block *self,struct rcu_data *rdp =per_cpu_ptr (rcu_state->rda, cpu);struct rcu_node *rnp = rdp->mynode;struct rcu_state *rsp; -int ret =NOTIFY_OK ;trace_rcu_utilization ("Start CPU hotplug" );switch (action) {
@@ -2923,10 +2928,7 @@ static int __cpuinit rcu_cpu_notify(struct notifier_block *self,rcu_boost_kthread_setaffinity (rnp,-1 );break ;case CPU_DOWN_PREPARE : -if (nocb_cpu_expendable (cpu)) -rcu_boost_kthread_setaffinity (rnp, cpu); -else - ret =NOTIFY_BAD ; +rcu_boost_kthread_setaffinity (rnp, cpu);break ;case CPU_DYING :case CPU_DYING_FROZEN :
@@ -2950,7 +2952,7 @@ static int __cpuinit rcu_cpu_notify(struct notifier_block *self,break ; }trace_rcu_utilization ("End CPU hotplug" ); -return ret; +return NOTIFY_OK ; }/*
@@ -3085,6 +3087,7 @@ static void __init rcu_init_one(struct rcu_state *rsp, } rnp->level = i;INIT_LIST_HEAD (&rnp->blkd_tasks); +rcu_init_one_nocb (rnp); } }
@@ -3170,7 +3173,6 @@ void __init rcu_init(void)rcu_init_one (&rcu_sched_state, &rcu_sched_data);rcu_init_one (&rcu_bh_state, &rcu_bh_data);__rcu_init_preempt (); -rcu_init_nocb ();open_softirq (RCU_SOFTIRQ , rcu_process_callbacks);/*
kernel/rcutree.h +11 -9
@@ -196,6 +196,12 @@ struct rcu_node {/* Refused to boost: not sure why, though. */ /* This can happen due to race conditions. */ #endif /* #ifdef CONFIG_RCU_BOOST */ +#ifdef CONFIG_RCU_NOCB_CPU +wait_queue_head_t nocb_gp_wq[2 ]; +/* Place for rcu_nocb_kthread() to wait GP. */ +int n_nocb_gp_requests[2 ]; +/* Counts of upcoming no-CB GP requests. */ +#endif /* #ifdef CONFIG_RCU_NOCB_CPU */ raw_spinlock_t fqslock____cacheline_internodealigned_in_smp ; }____cacheline_internodealigned_in_smp ;
@@ -375,12 +381,6 @@ struct rcu_state {struct rcu_data __percpu *rda;/* pointer of percu rcu_data. */ void (*call)(struct rcu_head *head,/* call_rcu() flavor. */ void (*func)(struct rcu_head *head)); -#ifdef CONFIG_RCU_NOCB_CPU -void (*call_remote)(struct rcu_head *head, -void (*func)(struct rcu_head *head)); -/* call_rcu() flavor, but for */ -/* placing on remote CPU. */ -#endif /* #ifdef CONFIG_RCU_NOCB_CPU */ /* The following fields are guarded by the root rcu_node's lock. */
@@ -529,16 +529,18 @@ static void print_cpu_stall_info(struct rcu_state *rsp, int cpu);static void print_cpu_stall_info_end (void );static void zero_cpu_stall_ticks (struct rcu_data *rdp);static void increment_cpu_stall_ticks (void ); +static int rcu_nocb_needs_gp (struct rcu_state *rsp); +static void rcu_nocb_gp_set (struct rcu_node *rnp,int nrq); +static int rcu_nocb_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp); +static void rcu_init_one_nocb (struct rcu_node *rnp);static bool is_nocb_cpu (int cpu);static bool __call_rcu_nocb (struct rcu_data *rdp,struct rcu_head *rhp,bool lazy);static bool rcu_nocb_adopt_orphan_cbs (struct rcu_state *rsp,struct rcu_data *rdp); -static bool nocb_cpu_expendable (int cpu);static void rcu_boot_init_nocb_percpu_data (struct rcu_data *rdp);static void rcu_spawn_nocb_kthreads (struct rcu_state *rsp); -static void init_nocb_callback_list (struct rcu_data *rdp); -static void __init rcu_init_nocb (void ); +static bool init_nocb_callback_list (struct rcu_data *rdp);#endif /* #ifndef RCU_TREE_NONCORE */
kernel/rcutree_plugin.h +172 -104
344 lines not on this page · raw message
Signature · 1 line
1.7.8
PATCH 2/12 rcu: Provide compile-time control for no-CBs CPUs
From: "Paul E. McKenney" <[email protected]>
Currently, the only way to specify no-CBs CPUs is via the rcu_nocbs kernel command-line parameter. This is inconvenient in some cases, particularly for randconfig testing, so this commit adds a new RCU_NOCB_CPU_DEFAULT kernel configuration parameter. Setting this new parameter to zero (the default) retains the old behavior, setting it to one offloads callback processing from CPU 0 (along with any other CPUs specified by the rcu_nocbs boot-time parameter), and setting it to two offloads callback processing from all CPUs.
--- init/Kconfig | 35 +++++++++++++++++++++++++++++++++++ kernel/rcutree_plugin.h | 14 ++++++++++++++ 2 files changed, 49 insertions(+), 0 deletions(-)
init/Kconfig +35 -0
@@ -686,6 +686,41 @@ config RCU_NOCB_CPU Say Y here if you want to help to debug reduced OS jitter. Say N here if you are unsure. +choice + prompt "Build-forced no-CBs CPUs" + default RCU_NOCB_CPU_NONE + +config RCU_NOCB_CPU_NONE + bool "No build_forced no-CBs CPUs" + depends on RCU_NOCB_CPU + help + This option does not force any of the CPUs to be no-CBs CPUs. + Only CPUs designated by the rcu_nocbs= boot parameter will be + no-CBs CPUs. + +config RCU_NOCB_CPU_ZERO + bool "CPU 0 is a build_forced no-CBs CPU" + depends on RCU_NOCB_CPU + help + This option forces CPU 0 to be a no-CBs CPU. Additional CPUs + may be designated as no-CBs CPUs using the rcu_nocbs= boot + parameter will be no-CBs CPUs. + + Select this if CPU 0 needs to be a no-CBs CPU for real-time + or energy-efficiency reasons. + +config RCU_NOCB_CPU_ALL + bool "All CPUs are build_forced no-CBs CPUs" + depends on RCU_NOCB_CPU + help + This option forces all CPUs to be no-CBs CPUs. The rcu_nocbs= + boot parameter will be ignored. + + Select this if all CPUs need to be no-CBs CPUs for real-time + or energy-efficiency reasons. + +endchoice + endmenu # "RCU Subsystem" config IKCONFIG
kernel/rcutree_plugin.h +14 -0
@@ -85,6 +85,20 @@ static void __init rcu_bootup_announce_oddness(void)if (nr_cpu_ids !=NR_CPUS )printk (KERN_INFO "\tRCU restricting CPUs from NR_CPUS=%d to nr_cpu_ids=%d.\n" ,NR_CPUS , nr_cpu_ids);#ifdef CONFIG_RCU_NOCB_CPU +#ifndef CONFIG_RCU_NOCB_CPU_NONE +if (!have_rcu_nocb_mask) { +alloc_bootmem_cpumask_var (&rcu_nocb_mask); + have_rcu_nocb_mask = true; + } +#ifdef CONFIG_RCU_NOCB_CPU_ZERO +pr_info ("\tExperimental no-CBs CPU 0\n" ); +cpumask_set_cpu (0 , rcu_nocb_mask); +#endif /* #ifdef CONFIG_RCU_NOCB_CPU_ZERO */ +#ifdef CONFIG_RCU_NOCB_CPU_ALL +pr_info ("\tExperimental no-CBs for all CPUs\n" ); +cpumask_setall (rcu_nocb_mask); +#endif /* #ifdef CONFIG_RCU_NOCB_CPU_ALL */ +#endif /* #ifndef CONFIG_RCU_NOCB_CPU_NONE */ if (have_rcu_nocb_mask) {cpulist_scnprintf (nocb_buf,sizeof (nocb_buf), rcu_nocb_mask);pr_info ("\tExperimental no-CBs CPUs: %s.\n" , nocb_buf);
Signature · 1 line
1.7.8
PATCH 3/12 rcu: Distinguish "rcuo" kthreads by RCU flavor
In reply to Paul E. McKenneyFrom: "Paul E. McKenney" <[email protected]>
Currently, the per-no-CBs-CPU kthreads are named "rcuo" followed by the CPU number, for example, "rcuo". This is problematic given that there are either two or three RCU flavors, each of which gets a per-CPU kthread with exactly the same name. This commit therefore introduces a one-letter abbreviation for each RCU flavor, namely 'b' for RCU-bh, 'p' for RCU-preempt, and 's' for RCU-sched. This abbreviation use used to distinguish the "rcuo" kthreads, for example, for CPU 0 we would have "rcuo0b", "rcuo0p", and "rcuo0s".
--- Documentation/kernel-parameters.txt | 7 +++++-- init/Kconfig | 13 +++++++------ kernel/rcutree.c | 7 ++++--- kernel/rcutree.h | 1 + kernel/rcutree_plugin.h | 5 +++-- 5 files changed, 20 insertions(+), 13 deletions(-)
Documentation/kernel-parameters.txt +5 -2
@@ -2432,9 +2432,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted. In kernels built with CONFIG_RCU_NOCB_CPU=y, set the specified list of CPUs to be no-callback CPUs. Invocation of these CPUs' RCU callbacks will - be offloaded to "rcuoN" kthreads created for - that purpose. This reduces OS jitter on the + be offloaded to "rcuox/N" kthreads created for + that purpose, where "x" is "b" for RCU-bh, "p" + for RCU-preempt, and "s" for RCU-sched, and "N" + is the CPU number. This reduces OS jitter on the offloaded CPUs, which can be useful for HPC and + real-time workloads. It can also improve energy efficiency for asymmetric multiprocessors.
init/Kconfig +7 -6
@@ -676,12 +676,13 @@ config RCU_NOCB_CPU This option offloads callback invocation from the set of CPUs specified at boot time by the rcu_nocbs parameter. - For each such CPU, a kthread ("rcuoN") will be created to - invoke callbacks, where the "N" is the CPU being offloaded. - Nothing prevents this kthread from running on the specified - CPUs, but (1) the kthreads may be preempted between each - callback, and (2) affinity or cgroups can be used to force - the kthreads to run on whatever set of CPUs is desired. + For each such CPU, a kthread ("rcuox/N") will be created to + invoke callbacks, where the "N" is the CPU being offloaded, + and where the "x" is "b" for RCU-bh, "p" for RCU-preempt, and + "s" for RCU-sched. Nothing prevents this kthread from running + on the specified CPUs, but (1) the kthreads may be preempted + between each callback, and (2) affinity or cgroups can be used + to force the kthreads to run on whatever set of CPUs is desired. Say Y here if you want to help to debug reduced OS jitter. Say N here if you are unsure.
kernel/rcutree.c +4 -3
@@ -64,7 +64,7 @@static struct lock_class_key rcu_node_class[RCU_NUM_LVLS ];static struct lock_class_key rcu_fqs_class[RCU_NUM_LVLS ]; -#define RCU_STATE_INITIALIZER (sname, cr) { \ +#define RCU_STATE_INITIALIZER (sname, sabbr, cr) { \ .level = { &sname##_state.node[0] }, \ .call = cr, \ .fqs_state = RCU_GP_IDLE, \
@@ -76,13 +76,14 @@ static struct lock_class_key rcu_fqs_class[RCU_NUM_LVLS]; .barrier_mutex = __MUTEX_INITIALIZER(sname##_state.barrier_mutex), \ .onoff_mutex = __MUTEX_INITIALIZER(sname##_state.onoff_mutex), \ .name = #sname, \ + .abbr = sabbr, \ }struct rcu_state rcu_sched_state = -RCU_STATE_INITIALIZER (rcu_sched, call_rcu_sched); +RCU_STATE_INITIALIZER (rcu_sched,'s' , call_rcu_sched); DEFINE_PER_CPU(struct rcu_data, rcu_sched_data); -struct rcu_state rcu_bh_state =RCU_STATE_INITIALIZER (rcu_bh, call_rcu_bh); +struct rcu_state rcu_bh_state =RCU_STATE_INITIALIZER (rcu_bh,'b' , call_rcu_bh); DEFINE_PER_CPU(struct rcu_data, rcu_bh_data);static struct rcu_state *rcu_state;
kernel/rcutree.h +1 -0
@@ -443,6 +443,7 @@ struct rcu_state {unsigned long gp_max;/* Maximum GP duration in */ /* jiffies. */ char *name;/* Name of structure. */ +char abbr;/* Abbreviated name. */ struct list_head flavors;/* List of RCU flavors. */ };
kernel/rcutree_plugin.h +3 -2
@@ -111,7 +111,7 @@ static void __init rcu_bootup_announce_oddness(void)#ifdef CONFIG_TREE_PREEMPT_RCU struct rcu_state rcu_preempt_state = -RCU_STATE_INITIALIZER (rcu_preempt, call_rcu); +RCU_STATE_INITIALIZER (rcu_preempt,'p' , call_rcu); DEFINE_PER_CPU(struct rcu_data, rcu_preempt_data);static struct rcu_state *rcu_state = &rcu_preempt_state;
@@ -2517,7 +2517,8 @@ static void __init rcu_spawn_nocb_kthreads(struct rcu_state *rsp)return ;for_each_cpu (cpu, rcu_nocb_mask) { rdp =per_cpu_ptr (rsp->rda, cpu); - t =kthread_run (rcu_nocb_kthread, rdp,"rcuo%d" , cpu); + t =kthread_run (rcu_nocb_kthread, rdp, +"rcuo%c/%d" , rsp->abbr, cpu);BUG_ON (IS_ERR (t));ACCESS_ONCE (rdp->nocb_kthread) = t; }
Signature · 1 line
1.7.8
PATCH 4/12 rcu: Export RCU_FAST_NO_HZ parameters to sysfs
In reply to Paul E. McKenneyFrom: "Paul E. McKenney" <[email protected]>
RCU_FAST_NO_HZ operation is controlled by four compile-time C-preprocessor macros, but some use cases benefit greatly from runtime adjustment, particularly when tuning devices. This commit therefore creates the corresponding sysfs entries.
--- kernel/rcutree_plugin.h | 31 ++++++++++++++++++++----------- 1 files changed, 20 insertions(+), 11 deletions(-)
kernel/rcutree_plugin.h +20 -11
@@ -1617,6 +1617,15 @@ static void rcu_idle_count_callbacks_posted(void)#define RCU_IDLE_GP_DELAY 4/* Roughly one grace period. */ #define RCU_IDLE_LAZY_GP_DELAY (6 * HZ)/* Roughly six seconds. */ +static int rcu_idle_flushes =RCU_IDLE_FLUSHES ; +module_param (rcu_idle_flushes, int,0644 ); +static int rcu_idle_opt_flushes =RCU_IDLE_OPT_FLUSHES ; +module_param (rcu_idle_opt_flushes, int,0644 ); +static int rcu_idle_gp_delay =RCU_IDLE_GP_DELAY ; +module_param (rcu_idle_gp_delay, int,0644 ); +static int rcu_idle_lazy_gp_delay =RCU_IDLE_LAZY_GP_DELAY ; +module_param (rcu_idle_lazy_gp_delay, int,0644 ); +extern int tick_nohz_enabled;/*
@@ -1696,10 +1705,10 @@ int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies) }/* Set up for the possibility that RCU will post a timer. */ if (rcu_cpu_has_nonlazy_callbacks (cpu)) { - *delta_jiffies =round_up (RCU_IDLE_GP_DELAY + jiffies, -RCU_IDLE_GP_DELAY ) - jiffies; + *delta_jiffies =round_up (rcu_idle_gp_delay + jiffies, + rcu_idle_gp_delay) - jiffies; }else { - *delta_jiffies = jiffies +RCU_IDLE_LAZY_GP_DELAY ; + *delta_jiffies = jiffies + rcu_idle_lazy_gp_delay; *delta_jiffies =round_jiffies (*delta_jiffies) - jiffies; }return 0 ;
@@ -1805,11 +1814,11 @@ static void rcu_prepare_for_idle(int cpu)if (rcu_cpu_has_nonlazy_callbacks (cpu)) {trace_rcu_prep_idle ("User dyntick with callbacks" ); rdtp->idle_gp_timer_expires = -round_up (jiffies +RCU_IDLE_GP_DELAY , -RCU_IDLE_GP_DELAY ); +round_up (jiffies + rcu_idle_gp_delay, + rcu_idle_gp_delay); }else if (rcu_cpu_has_callbacks (cpu)) { rdtp->idle_gp_timer_expires = -round_jiffies (jiffies +RCU_IDLE_LAZY_GP_DELAY ); +round_jiffies (jiffies + rcu_idle_lazy_gp_delay);trace_rcu_prep_idle ("User dyntick with lazy callbacks" ); }else {return ;
@@ -1861,8 +1870,8 @@ static void rcu_prepare_for_idle(int cpu)/* Check and update the ->dyntick_drain sequencing. */ if (rdtp->dyntick_drain <=0 ) {/* First time through, initialize the counter. */ - rdtp->dyntick_drain =RCU_IDLE_FLUSHES ; - }else if (rdtp->dyntick_drain <=RCU_IDLE_OPT_FLUSHES && + rdtp->dyntick_drain = rcu_idle_flushes; + }else if (rdtp->dyntick_drain <= rcu_idle_opt_flushes && !rcu_pending (cpu) && !local_softirq_pending ()) {/* Can we go dyntick-idle despite still having callbacks? */
@@ -1871,11 +1880,11 @@ static void rcu_prepare_for_idle(int cpu)if (rcu_cpu_has_nonlazy_callbacks (cpu)) {trace_rcu_prep_idle ("Dyntick with callbacks" ); rdtp->idle_gp_timer_expires = -round_up (jiffies +RCU_IDLE_GP_DELAY , -RCU_IDLE_GP_DELAY ); +round_up (jiffies + rcu_idle_gp_delay, + rcu_idle_gp_delay); }else { rdtp->idle_gp_timer_expires = -round_jiffies (jiffies +RCU_IDLE_LAZY_GP_DELAY ); +round_jiffies (jiffies + rcu_idle_lazy_gp_delay);trace_rcu_prep_idle ("Dyntick with lazy callbacks" ); } tp = &rdtp->idle_gp_timer;
Signature · 1 line
1.7.8
PATCH 5/12 rcu: Accelerate RCU callbacks at grace-period end
In reply to Paul E. McKenneyFrom: "Paul E. McKenney" <[email protected]>
Now that callback acceleration is idempotent, it is safe to accelerate callbacks during grace-period cleanup on any CPUs that the kthread happens to be running on. This commit therefore propagates the completion of the grace period to the per-CPU data structures, and also adds an rcu_advance_cbs() just before the cpu_needs_another_gp() check in order to reduce false-positive grace periods.
--- kernel/rcutree.c | 21 +++++++++++++-------- 1 files changed, 13 insertions(+), 8 deletions(-)
kernel/rcutree.c +13 -8
@@ -1396,6 +1396,9 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)rcu_for_each_node_breadth_first (rsp, rnp) {raw_spin_lock_irq (&rnp->lock); rnp->completed = rsp->gpnum; + rdp =this_cpu_ptr (rsp->rda); +if (rnp == rdp->mynode) +__rcu_process_gp_end (rsp, rnp, rdp); nocb +=rcu_nocb_gp_cleanup (rsp, rnp);raw_spin_unlock_irq (&rnp->lock);cond_resched ();
@@ -1408,6 +1411,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)trace_rcu_grace_period (rsp->name, rsp->completed,"end" ); rsp->fqs_state =RCU_GP_IDLE ; rdp =this_cpu_ptr (rsp->rda); +rcu_advance_cbs (rsp, rnp, rdp);/* Reduce false positives below. */ if (cpu_needs_another_gp (rsp, rdp)) rsp->gp_flags =1 ;raw_spin_unlock_irq (&rnp->lock);
@@ -1497,6 +1501,15 @@ rcu_start_gp(struct rcu_state *rsp, unsigned long flags)struct rcu_data *rdp =this_cpu_ptr (rsp->rda);struct rcu_node *rnp =rcu_get_root (rsp); +/* +* If there is no grace period in progress right now, any +* callbacks we have up to this point will be satisfied by the +* next grace period. Also, advancing the callbacks reduces the +* probability of false positives from cpu_needs_another_gp() +* resulting in pointless grace periods. So, advance callbacks! +*/ +rcu_advance_cbs (rsp, rnp, rdp); +if (!rsp->gp_kthread || !cpu_needs_another_gp (rsp, rdp)) {/*
@@ -1509,14 +1522,6 @@ rcu_start_gp(struct rcu_state *rsp, unsigned long flags)return ; } -/* -* Because there is no grace period in progress right now, -* any callbacks we have up to this point will be satisfied -* by the next grace period. So this is a good place to -* assign a grace period number to recently posted callbacks. -*/ -rcu_accelerate_cbs (rsp, rnp, rdp); - rsp->gp_flags =RCU_GP_FLAG_INIT ;raw_spin_unlock (&rnp->lock);/* Interrupts remain disabled. */
Signature · 1 line
1.7.8
PATCH 6/12 rcu: Make RCU_FAST_NO_HZ take advantage of numbered callbacks
In reply to Paul E. McKenneyFrom: "Paul E. McKenney" <[email protected]>
Because RCU callbacks are now associated with the number of the grace period that they must wait for, CPUs can now take advance callbacks corresponding to grace periods that ended while a given CPU was in dyntick-idle mode. This eliminates the need to try forcing the RCU state machine while entering idle, thus reducing the CPU intensiveness of RCU_FAST_NO_HZ, which should increase its energy efficiency.
--- include/linux/rcupdate.h | 1 + kernel/rcutree.c | 28 +++-- kernel/rcutree.h | 12 +-- kernel/rcutree_plugin.h | 350 +++++++++++++++------------------------------- kernel/rcutree_trace.c | 2 - 5 files changed, 131 insertions(+), 262 deletions(-)
include/linux/rcupdate.h +1 -0
@@ -80,6 +80,7 @@ extern void do_trace_rcu_torture_read(char *rcutorturename,#define UINT_CMP_LT (a, b) (UINT_MAX / 2 < (a) - (b))#define ULONG_CMP_GE (a, b) (ULONG_MAX / 2 >= (a) - (b))#define ULONG_CMP_LT (a, b) (ULONG_MAX / 2 < (a) - (b)) +#define ulong2long (a) (*(long *)(&(a)))/* Exported common interfaces */
kernel/rcutree.c +17 -11
@@ -2640,19 +2640,27 @@ static int rcu_pending(int cpu) }/* -* Check to see if any future RCU-related work will need to be done -* by the current CPU, even if none need be done immediately, returning -* 1 if so. +* Return true if the specified CPU has any callback. If all_lazy is +* non-NULL, store an indication of whether all callbacks are lazy. +* (If there are no callbacks, all of them are deemed to be lazy.) */ -static int rcu_cpu_has_callbacks (int cpu) +static int rcu_cpu_has_callbacks (int cpu,bool *all_lazy) { +bool al = true; +bool hc = false; +struct rcu_data *rdp;struct rcu_state *rsp; -/* RCU callbacks either ready or pending? */ -for_each_rcu_flavor (rsp) -if (per_cpu_ptr (rsp->rda, cpu)->nxtlist) -return 1 ; -return 0 ; +for_each_rcu_flavor (rsp) { + rdp =per_cpu_ptr (rsp->rda, cpu); +if (rdp->qlen != rdp->qlen_lazy) + al = false; +if (rdp->nxtlist) + hc = true; + } +if (all_lazy) + *all_lazy = al; +return hc; }/*
@@ -2871,7 +2879,6 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp, int preemptible) rdp->dynticks->dynticks_nesting =DYNTICK_TASK_EXIT_IDLE ;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. */ /* Add CPU to rcu_node bitmasks. */
@@ -2945,7 +2952,6 @@ static int __cpuinit rcu_cpu_notify(struct notifier_block *self,*/ for_each_rcu_flavor (rsp)rcu_cleanup_dying_cpu (rsp); -rcu_cleanup_after_idle (cpu);break ;case CPU_DEAD :case CPU_DEAD_FROZEN :
kernel/rcutree.h +3 -9
@@ -88,18 +88,13 @@ struct rcu_dynticks {int dynticks_nmi_nesting;/* Track NMI nesting level. */ atomic_t dynticks;/* Even value for idle, else odd. */ #ifdef CONFIG_RCU_FAST_NO_HZ -int dyntick_drain;/* Prepare-for-idle state variable. */ -unsigned long dyntick_holdoff; -/* No retries for the jiffy of failure. */ -struct timer_list idle_gp_timer; -/* Wake up CPU sleeping with callbacks. */ -unsigned long idle_gp_timer_expires; -/* When to wake up CPU (for repost). */ -bool idle_first_pass;/* First pass of attempt to go idle? */ +bool all_lazy;/* Are all CPU's CBs lazy? */ unsigned long nonlazy_posted;/* # times non-lazy CBs posted to CPU. */ unsigned long nonlazy_posted_snap;/* idle-period nonlazy_posted snapshot. */ +unsigned long last_accelerate; +/* Last jiffy CBs were accelerated. */ int tick_nohz_enabled_snap;/* Previously seen value from sysfs. */ #endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */ };
@@ -521,7 +516,6 @@ static int __cpuinit rcu_spawn_one_boost_kthread(struct rcu_state *rsp, struct rcu_node *rnp);#endif /* #ifdef CONFIG_RCU_BOOST */ 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);static void rcu_idle_count_callbacks_posted (void );
kernel/rcutree_plugin.h +110 -240
439 lines not on this page · raw message
kernel/rcutree_trace.c +0 -2
@@ -46,8 +46,6 @@#define RCU_TREE_NONCORE #include "rcutree.h" -#define ulong2long (a) (*(long *)(&(a))) -static int r_open (struct inode *inode,struct file *file,const struct seq_operations *op) {
Signature · 1 line
1.7.8
PATCH 7/12 rcu: Rearrange locking in rcu_start_gp()
In reply to Paul E. McKenneyFrom: "Paul E. McKenney" <[email protected]>
If CPUs are to give prior notice of needed grace periods, it will be necessary to invoke rcu_start_gp() without dropping the root rcu_node structure's ->lock. This commit takes a first step in this direction by moving the release of this lock to the end of rcu_start_gp().
--- kernel/rcutree.c | 6 ++---- 1 files changed, 2 insertions(+), 4 deletions(-)
kernel/rcutree.c +2 -4
@@ -1521,16 +1521,14 @@ rcu_start_gp(struct rcu_state *rsp, unsigned long flags)raw_spin_unlock_irqrestore (&rnp->lock, flags);return ; } - rsp->gp_flags =RCU_GP_FLAG_INIT ; -raw_spin_unlock (&rnp->lock);/* Interrupts remain disabled. */ /* Ensure that CPU is aware of completion of last grace period. */ -rcu_process_gp_end (rsp, rdp); -local_irq_restore (flags); +__rcu_process_gp_end (rsp, rdp->mynode, rdp);/* Wake up rcu_gp_kthread() to start the grace period. */ wake_up (&rsp->gp_wq); +raw_spin_unlock_irqrestore (&rnp->lock, flags); }/*
Signature · 1 line
1.7.8
PATCH 8/12 rcu: Repurpose no-CBs event tracing to future-GP events
In reply to Paul E. McKenneyFrom: "Paul E. McKenney" <[email protected]>
Dyntick-idle CPUs need to be able to pre-announce their need for grace periods. This can be done using something similar to the mechanism used by no-CB CPUs to announce their need for grace periods. This commit moves in this direction by renaming the no-CBs grace-period event tracing to suit the new future-grace-period needs.
--- include/trace/events/rcu.h | 16 +++++----- kernel/rcutree_plugin.h | 62 ++++++++++++++++++++++--------------------- 2 files changed, 40 insertions(+), 38 deletions(-)
include/trace/events/rcu.h +8 -8
@@ -72,10 +72,10 @@ TRACE_EVENT(rcu_grace_period, );/* -* Tracepoint for no-callbacks grace-period events. The caller should -* pull the data from the rcu_node structure, other than rcuname, which -* comes from the rcu_state structure, and event, which is one of the -* following: +* Tracepoint for future grace-period events, including those for no-callbacks +* CPUs. The caller should pull the data from the rcu_node structure, +* other than rcuname, which comes from the rcu_state structure, and event, +* which is one of the following: * * "Startleaf": Request a nocb grace period based on leaf-node data. * "Startedleaf": Leaf-node start proved sufficient.
@@ -87,7 +87,7 @@ TRACE_EVENT(rcu_grace_period,* "Cleanup": Clean up rcu_node structure after previous GP. * "CleanupMore": Clean up, and another no-CB GP is needed. */ -TRACE_EVENT(rcu_nocb_grace_period, +TRACE_EVENT(rcu_future_grace_period, TP_PROTO(char *rcuname, unsigned long gpnum, unsigned long completed, unsigned long c, u8 level, int grplo, int grphi,
@@ -653,9 +653,9 @@ TRACE_EVENT(rcu_barrier,#define trace_rcu_grace_period (rcuname, gpnum, gpevent) do { } while (0)#define trace_rcu_grace_period_init (rcuname, gpnum, level, grplo, grphi, \ qsmask) do { } while (0) -#define trace_rcu_nocb_grace_period (rcuname, gpnum, completed, c, \ - level, grplo, grphi, event) \ - do { } while (0) +#define trace_rcu_future_grace_period (rcuname, gpnum, completed, c, \ + level, grplo, grphi, event) \ + do { } while (0)#define trace_rcu_preempt_task (rcuname, pid, gpnum) do { } while (0)#define trace_rcu_unlock_preempted_task (rcuname, gpnum, pid) do { } while (0) #define trace_rcu_quiescent_state_report(rcuname, gpnum, mask, qsmask, level, \
kernel/rcutree_plugin.h +32 -30
105 lines not on this page · raw message
Signature · 1 line
1.7.8
PATCH 9/12 rcu: Push lock release to rcu_start_gp()'s callers
In reply to Paul E. McKenneyFrom: "Paul E. McKenney" <[email protected]>
If CPUs are to give prior notice of needed grace periods, it will be necessary to invoke rcu_start_gp() without dropping the root rcu_node structure's ->lock. This commit takes a second step in this direction by moving the release of this lock to rcu_start_gp()'s callers.
--- kernel/rcutree.c | 24 ++++++++++-------------- kernel/rcutree_plugin.h | 5 ++--- 2 files changed, 12 insertions(+), 17 deletions(-)
kernel/rcutree.c +10 -14
@@ -1487,16 +1487,14 @@ static int __noreturn rcu_gp_kthread(void *arg)/* * Start a new RCU grace period if warranted, re-initializing the hierarchy * in preparation for detecting the next grace period. The caller must hold -* the root node's ->lock, which is released before return. Hard irqs must -* be disabled. +* the root node's ->lock and hard irqs must be disabled. * * Note that it is legal for a dying CPU (which is marked as offline) to * invoke this function. This can happen when the dying CPU reports its * quiescent state. */ static void -rcu_start_gp (struct rcu_state *rsp,unsigned long flags) - __releases(rcu_get_root(rsp)->lock) +rcu_start_gp (struct rcu_state *rsp) {struct rcu_data *rdp =this_cpu_ptr (rsp->rda);struct rcu_node *rnp =rcu_get_root (rsp);
@@ -1510,15 +1508,13 @@ rcu_start_gp(struct rcu_state *rsp, unsigned long flags)*/ rcu_advance_cbs (rsp, rnp, rdp); -if (!rsp->gp_kthread || - !cpu_needs_another_gp (rsp, rdp)) { +if (!rsp->gp_kthread || !cpu_needs_another_gp (rsp, rdp)) {/* * Either we have not yet spawned the grace-period * task, this CPU does not need another grace period, * or a grace period is already in progress. * Either way, don't start a new grace period. */ -raw_spin_unlock_irqrestore (&rnp->lock, flags);return ; } rsp->gp_flags =RCU_GP_FLAG_INIT ;
@@ -1528,15 +1524,14 @@ rcu_start_gp(struct rcu_state *rsp, unsigned long flags)/* Wake up rcu_gp_kthread() to start the grace period. */ wake_up (&rsp->gp_wq); -raw_spin_unlock_irqrestore (&rnp->lock, flags); }/* * Report a full set of quiescent states to the specified rcu_state * data structure. This involves cleaning up after the prior grace * period and letting rcu_start_gp() start up the next grace period -* if one is needed. Note that the caller must hold rnp->lock, as -* required by rcu_start_gp(), which will release it. +* if one is needed. Note that the caller must hold rnp->lock, which +* is released before return. */ static void rcu_report_qs_rsp(struct rcu_state *rsp, unsigned long flags) __releases(rcu_get_root(rsp)->lock)
@@ -2134,7 +2129,8 @@ __rcu_process_callbacks(struct rcu_state *rsp)local_irq_save (flags);if (cpu_needs_another_gp (rsp, rdp)) {raw_spin_lock (&rcu_get_root (rsp)->lock);/* irqs disabled. */ -rcu_start_gp (rsp, flags);/* releases above lock */ +rcu_start_gp (rsp); +raw_spin_unlock_irqrestore (&rcu_get_root (rsp)->lock, flags); }else {local_irq_restore (flags); }
@@ -2214,11 +2210,11 @@ static void __call_rcu_core(struct rcu_state *rsp, struct rcu_data *rdp,/* Start a new grace period if one not already started. */ if (!rcu_gp_in_progress (rsp)) { -unsigned long nestflag;struct rcu_node *rnp_root =rcu_get_root (rsp); -raw_spin_lock_irqsave (&rnp_root->lock, nestflag); -rcu_start_gp (rsp, nestflag);/* rlses rnp_root->lock */ +raw_spin_lock (&rnp_root->lock); +rcu_start_gp (rsp); +raw_spin_unlock (&rnp_root->lock); }else {/* Give the grace period a kick. */ rdp->blimit =LONG_MAX ;
kernel/rcutree_plugin.h +2 -3
@@ -2220,7 +2220,6 @@ static void rcu_nocb_wait_gp(struct rcu_data *rdp)unsigned long c;bool d;unsigned long flags; -unsigned long flags1;struct rcu_node *rnp = rdp->mynode;struct rcu_node *rnp_root =rcu_get_root (rdp->rsp);
@@ -2282,8 +2281,8 @@ static void rcu_nocb_wait_gp(struct rcu_data *rdp) c, rnp->level, rnp->grplo, rnp->grphi, "Startedroot"); -local_save_flags (flags1); -rcu_start_gp (rdp->rsp, flags1);/* Rlses ->lock. */ +rcu_start_gp (rdp->rsp); +raw_spin_unlock (&rnp->lock); }/* Clean up locking and irq state. */
Signature · 1 line
1.7.8
PATCH 10/12 rcu: Rename n_nocb_gp_requests to need_future_gp
In reply to Paul E. McKenneyFrom: "Paul E. McKenney" <[email protected]>
CPUs going idle need to be able to indicate their need for future grace periods. A mechanism for doing this already exists for no-callbacks CPUs, so the idea is to re-use that mechanism. This commit therefore moves the ->n_nocb_gp_requests field of the rcu_node structure out from under the CONFIG_RCU_NOCB_CPU #ifdef and renames it to ->need_future_gp.
--- kernel/rcutree.h | 4 ++-- kernel/rcutree_plugin.h | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-)
kernel/rcutree.h +2 -2
@@ -194,9 +194,9 @@ struct rcu_node {#ifdef CONFIG_RCU_NOCB_CPU wait_queue_head_t nocb_gp_wq[2 ];/* Place for rcu_nocb_kthread() to wait GP. */ -int n_nocb_gp_requests[2 ]; -/* Counts of upcoming no-CB GP requests. */ #endif /* #ifdef CONFIG_RCU_NOCB_CPU */ +int need_future_gp[2 ]; +/* Counts of upcoming no-CB GP requests. */ raw_spinlock_t fqslock____cacheline_internodealigned_in_smp ; }____cacheline_internodealigned_in_smp ;
kernel/rcutree_plugin.h +9 -9
@@ -2064,7 +2064,7 @@ static int rcu_nocb_needs_gp(struct rcu_state *rsp) {struct rcu_node *rnp =rcu_get_root (rsp); -return rnp->n_nocb_gp_requests[(ACCESS_ONCE (rnp->completed) +1 ) &0x1 ]; +return rnp->need_future_gp[(ACCESS_ONCE (rnp->completed) +1 ) &0x1 ]; }/*
@@ -2078,8 +2078,8 @@ static int rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp)int needmore;wake_up_all (&rnp->nocb_gp_wq[c &0x1 ]); - rnp->n_nocb_gp_requests[c &0x1 ] =0 ; - needmore = rnp->n_nocb_gp_requests[(c +1 ) &0x1 ]; + rnp->need_future_gp[c &0x1 ] =0 ; + needmore = rnp->need_future_gp[(c +1 ) &0x1 ];trace_rcu_future_grace_period (rsp->name, rnp->gpnum, rnp->completed, c, rnp->level, rnp->grplo, rnp->grphi, needmore ?"CleanupMore" :"Cleanup" );
@@ -2087,7 +2087,7 @@ static int rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) }/* -* Set the root rcu_node structure's ->n_nocb_gp_requests field +* Set the root rcu_node structure's ->need_future_gp field * based on the sum of those of all rcu_node structures. This does * double-count the root rcu_node structure's requests, but this * is necessary to handle the possibility of a rcu_nocb_kthread()
@@ -2096,7 +2096,7 @@ static int rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp)*/ static void rcu_nocb_gp_set (struct rcu_node *rnp,int nrq) { - rnp->n_nocb_gp_requests[(rnp->completed +1 ) &0x1 ] += nrq; + rnp->need_future_gp[(rnp->completed +1 ) &0x1 ] += nrq; }static void rcu_init_one_nocb (struct rcu_node *rnp)
@@ -2227,7 +2227,7 @@ static void rcu_nocb_wait_gp(struct rcu_data *rdp) c = rnp->completed +2 ;/* Count our request for a grace period. */ - rnp->n_nocb_gp_requests[c &0x1 ]++; + rnp->need_future_gp[c &0x1 ]++;trace_rcu_future_grace_period (rdp->rsp->name, rnp->gpnum, rnp->completed, c, rnp->level, rnp->grplo, rnp->grphi,"Startleaf" );
@@ -2271,10 +2271,10 @@ static void rcu_nocb_wait_gp(struct rcu_data *rdp)* Adjust counters accordingly and start the * needed grace period. */ - rnp->n_nocb_gp_requests[c &0x1 ]--; + rnp->need_future_gp[c &0x1 ]--; c = rnp_root->completed +1 ; - rnp->n_nocb_gp_requests[c &0x1 ]++; - rnp_root->n_nocb_gp_requests[c &0x1 ]++; + rnp->need_future_gp[c &0x1 ]++; + rnp_root->need_future_gp[c &0x1 ]++; trace_rcu_future_grace_period(rdp->rsp->name, rnp->gpnum, rnp->completed,
Signature · 1 line
1.7.8
PATCH 11/12 rcu: Abstract rcu_start_future_gp() from rcu_nocb_wait_gp()
In reply to Paul E. McKenneyFrom: "Paul E. McKenney" <[email protected]>
CPUs going idle will need to record the need for a future grace period, but won't actually need to block waiting on it. This commit therefore splits rcu_start_future_gp(), which does the recording, from rcu_nocb_wait_gp(), which now invokes rcu_start_future_gp() to do the recording, after which rcu_nocb_wait_gp() does the waiting.
--- kernel/rcutree.c | 123 +++++++++++++++++++++++++++++++++++++++++++++-- kernel/rcutree.h | 2 +- kernel/rcutree_plugin.h | 104 ++++------------------------------------ 3 files changed, 130 insertions(+), 99 deletions(-)
kernel/rcutree.c +119 -4
@@ -224,6 +224,7 @@ static ulong jiffies_till_next_fqs = RCU_JIFFIES_TILL_FORCE_QS;module_param (jiffies_till_first_fqs, ulong,0644 );module_param (jiffies_till_next_fqs, ulong,0644 ); +static void rcu_start_gp (struct rcu_state *rsp);static void force_qs_rnp (struct rcu_state *rsp,int (*f)(struct rcu_data *));static void force_quiescent_state (struct rcu_state *rsp);static int rcu_pending (int cpu);
@@ -1075,6 +1076,120 @@ static unsigned long rcu_cbs_completed(struct rcu_state *rsp, }/* +* Trace-event helper function for rcu_start_future_gp() and +* rcu_nocb_wait_gp(). +*/ +static void trace_rcu_future_gp (struct rcu_node *rnp,struct rcu_data *rdp, +unsigned long c,char *s) +{ +trace_rcu_future_grace_period (rdp->rsp->name, rnp->gpnum, + rnp->completed, c, rnp->level, + rnp->grplo, rnp->grphi, s); +} + +/* +* Start some future grace period, as needed to handle newly arrived +* callbacks. The required future grace periods are recorded in each +* rcu_node structure's ->need_future_gp field. +* +* The caller must hold the specified rcu_node structure's ->lock. +*/ +static unsigned long __maybe_unused +rcu_start_future_gp (struct rcu_node *rnp,struct rcu_data *rdp) +{ +unsigned long c; +int i; +struct rcu_node *rnp_root =rcu_get_root (rdp->rsp); + +/* +* Pick up grace-period number for new callbacks. If this +* grace period is already marked as needed, return to the caller. +*/ + c =rcu_cbs_completed (rdp->rsp, rnp); +trace_rcu_future_gp (rnp, rdp, c,"Startleaf" ); +if (rnp->need_future_gp[c &0x1 ]) { +trace_rcu_future_gp (rnp, rdp, c,"Prestartleaf" ); +return c; + } + +/* +* If either this rcu_node structure or the root rcu_node structure +* believe that a grace period is in progress, then we must wait +* for the one following, which is in "c". Because our request +* will be noticed at the end of the current grace period, we don't +* need to explicitly start one. +*/ +if (rnp->gpnum != rnp->completed || +ACCESS_ONCE (rnp->gpnum) !=ACCESS_ONCE (rnp->completed)) { + rnp->need_future_gp[c &0x1 ]++; +trace_rcu_future_gp (rnp, rdp, c,"Startedleaf" ); +return c; + } + +/* +* There might be no grace period in progress. If we don't already +* hold it, acquire the root rcu_node structure's lock in order to +* start one (if needed). +*/ +if (rnp != rnp_root) +raw_spin_lock (&rnp_root->lock); + +/* +* Get a new grace-period number. If there really is no grace +* period in progress, it will be smaller than the one we obtained +* earlier. Adjust callbacks as needed. Note that even no-CBs +* CPUs have a ->nxtcompleted[] array, so no no-CBs checks needed. +*/ + c =rcu_cbs_completed (rdp->rsp, rnp_root); +for (i =RCU_DONE_TAIL ; i <RCU_NEXT_TAIL ; i++) +if (ULONG_CMP_LT (c, rdp->nxtcompleted[i])) + rdp->nxtcompleted[i] = c; + +/* +* If the needed for the required grace period is already +* recorded, trace and leave. +*/ +if (rnp_root->need_future_gp[c &0x1 ]) { +trace_rcu_future_gp (rnp, rdp, c,"Prestartedroot" ); + goto unlock_out; + } + +/* Record the need for the future grace period. */ + rnp_root->need_future_gp[c &0x1 ]++; + +/* If a grace period is not already in progress, start one. */ +if (rnp_root->gpnum != rnp_root->completed) { +trace_rcu_future_gp (rnp, rdp, c,"Startedleafroot" ); + }else { +trace_rcu_future_gp (rnp, rdp, c,"Startedroot" ); +rcu_start_gp (rdp->rsp); + } +unlock_out: +if (rnp != rnp_root) +raw_spin_unlock (&rnp_root->lock); +return c; +} + +/* +* Clean up any old requests for the just-ended grace period. Also return +* whether any additional grace periods have been requested. Also invoke +* rcu_nocb_gp_cleanup() in order to wake up any no-callbacks kthreads +* waiting for this grace period to complete. +*/ +static int rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) +{ +int c = rnp->completed; +int needmore; +struct rcu_data *rdp =this_cpu_ptr (rsp->rda); + +rcu_nocb_gp_cleanup (rsp, rnp); + rnp->need_future_gp[c &0x1 ] =0 ; + needmore = rnp->need_future_gp[(c +1 ) &0x1 ]; +trace_rcu_future_gp (rnp, rdp, c, needmore ?"CleanupMore" :"Cleanup" ); +return needmore; +} + +/* * If there is room, assign a ->completed number to any callbacks on * this CPU that have not already been assigned. Also accelerate any * callbacks that were previously assigned a ->completed number that has
@@ -1312,9 +1427,9 @@ static int rcu_gp_init(struct rcu_state *rsp) rdp =this_cpu_ptr (rsp->rda);rcu_preempt_check_blocked_tasks (rnp); rnp->qsmask = rnp->qsmaskinit; - rnp->gpnum = rsp->gpnum; +ACCESS_ONCE (rnp->gpnum) = rsp->gpnum;WARN_ON_ONCE (rnp->completed != rsp->completed); - rnp->completed = rsp->completed; +ACCESS_ONCE (rnp->completed) = rsp->completed;if (rnp == rdp->mynode)rcu_start_gp_per_cpu (rsp, rnp, rdp);rcu_preempt_boost_start_gp (rnp);
@@ -1395,11 +1510,11 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)*/ rcu_for_each_node_breadth_first (rsp, rnp) {raw_spin_lock_irq (&rnp->lock); - rnp->completed = rsp->gpnum; +ACCESS_ONCE (rnp->completed) = rsp->gpnum; rdp =this_cpu_ptr (rsp->rda);if (rnp == rdp->mynode)__rcu_process_gp_end (rsp, rnp, rdp); - nocb +=rcu_nocb_gp_cleanup (rsp, rnp); + nocb +=rcu_future_gp_cleanup (rsp, rnp);raw_spin_unlock_irq (&rnp->lock);cond_resched (); }
kernel/rcutree.h +1 -1
@@ -526,7 +526,7 @@ static void zero_cpu_stall_ticks(struct rcu_data *rdp);static void increment_cpu_stall_ticks (void );static int rcu_nocb_needs_gp (struct rcu_state *rsp);static void rcu_nocb_gp_set (struct rcu_node *rnp,int nrq); -static int rcu_nocb_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp); +static void rcu_nocb_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp);static void rcu_init_one_nocb (struct rcu_node *rnp);static bool is_nocb_cpu (int cpu); static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp,
kernel/rcutree_plugin.h +10 -94
139 lines not on this page · raw message
Signature · 1 line
1.7.8
PATCH 12/12 rcu: Make rcu_accelerate_cbs() note need for future grace periods
In reply to Paul E. McKenneyFrom: "Paul E. McKenney" <[email protected]>
Now that rcu_start_future_gp() has been abstracted from rcu_nocb_wait_gp(), rcu_accelerate_cbs() can invoke rcu_start_future_gp() so as to register the need for any future grace periods needed by a CPU about to enter dyntick-idle mode. This commit makes this change. Note that some refactoring of rcu_start_gp() is carried out to avoid recursion and subsequent self-deadlocks.
--- kernel/rcutree.c | 50 ++++++++++++++++++++++++++++++++------------------ 1 files changed, 32 insertions(+), 18 deletions(-)
kernel/rcutree.c +32 -18
@@ -224,7 +224,8 @@ static ulong jiffies_till_next_fqs = RCU_JIFFIES_TILL_FORCE_QS;module_param (jiffies_till_first_fqs, ulong,0644 );module_param (jiffies_till_next_fqs, ulong,0644 ); -static void rcu_start_gp (struct rcu_state *rsp); +static void rcu_start_gp_advanced (struct rcu_state *rsp,struct rcu_node *rnp, +struct rcu_data *rdp);static void force_qs_rnp (struct rcu_state *rsp,int (*f)(struct rcu_data *));static void force_quiescent_state (struct rcu_state *rsp);static int rcu_pending (int cpu);
@@ -1162,7 +1163,7 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp)trace_rcu_future_gp (rnp, rdp, c,"Startedleafroot" ); }else {trace_rcu_future_gp (rnp, rdp, c,"Startedroot" ); -rcu_start_gp (rdp->rsp); +rcu_start_gp_advanced (rdp->rsp, rnp_root, rdp); } unlock_out:if (rnp != rnp_root)
@@ -1248,6 +1249,8 @@ static void rcu_accelerate_cbs(struct rcu_state *rsp, struct rcu_node *rnp, rdp->nxttail[i] = rdp->nxttail[RCU_NEXT_TAIL ]; rdp->nxtcompleted[i] = c; } +/* Record any needed additional grace periods. */ +rcu_start_future_gp (rnp, rdp);/* Trace depending on how much we were able to accelerate. */ if (!*rdp->nxttail[RCU_WAIT_TAIL ])
@@ -1609,20 +1612,9 @@ static int __noreturn rcu_gp_kthread(void *arg)* quiescent state. */ static void -rcu_start_gp (struct rcu_state *rsp) +rcu_start_gp_advanced (struct rcu_state *rsp,struct rcu_node *rnp, +struct rcu_data *rdp) { -struct rcu_data *rdp =this_cpu_ptr (rsp->rda); -struct rcu_node *rnp =rcu_get_root (rsp); - -/* -* If there is no grace period in progress right now, any -* callbacks we have up to this point will be satisfied by the -* next grace period. Also, advancing the callbacks reduces the -* probability of false positives from cpu_needs_another_gp() -* resulting in pointless grace periods. So, advance callbacks! -*/ -rcu_advance_cbs (rsp, rnp, rdp); -if (!rsp->gp_kthread || !cpu_needs_another_gp (rsp, rdp)) {/* * Either we have not yet spawned the grace-period
@@ -1634,14 +1626,36 @@ rcu_start_gp(struct rcu_state *rsp) } rsp->gp_flags =RCU_GP_FLAG_INIT ; -/* Ensure that CPU is aware of completion of last grace period. */ -__rcu_process_gp_end (rsp, rdp->mynode, rdp); -/* Wake up rcu_gp_kthread() to start the grace period. */ wake_up (&rsp->gp_wq); }/* +* Similar to rcu_start_gp_advanced(), but also advance the calling CPU's +* callbacks. Note that rcu_start_gp_advanced() cannot do this because it +* is invoked indirectly from rcu_advance_cbs(), which would result in +* endless recursion -- or would do so if it wasn't for the self-deadlock +* that is encountered beforehand. +*/ +static void +rcu_start_gp (struct rcu_state *rsp) +{ +struct rcu_data *rdp =this_cpu_ptr (rsp->rda); +struct rcu_node *rnp =rcu_get_root (rsp); + +/* +* If there is no grace period in progress right now, any +* callbacks we have up to this point will be satisfied by the +* next grace period. Also, advancing the callbacks reduces the +* probability of false positives from cpu_needs_another_gp() +* resulting in pointless grace periods. So, advance callbacks +* then start the grace period! +*/ +rcu_advance_cbs (rsp, rnp, rdp); +rcu_start_gp_advanced (rsp, rnp, rdp); +} + +/* * Report a full set of quiescent states to the specified rcu_state * data structure. This involves cleaning up after the prior grace * period and letting rcu_start_gp() start up the next grace period
Signature · 1 line
1.7.8