v2 Add callback-free CPUs
Subsystems (2)
cover v2 Add callback-free CPUs
Hello!
RCU callback execution can add significant OS jitter and also can degrade scheduling latency. This commit therefore adds the ability for selected CPUs ("rcu_nocbs=" boot parameter) to have their callbacks offloaded to kthreads, inspired by Joe Korty's and Jim Houston's JRCU. If the "rcu_nocb_poll" boot parameter is also specified, these kthreads will do polling, removing the need for the offloaded CPUs to do wakeups. At least one CPU must be doing normal callback processing: currently CPU 0 cannot be selected as a no-CBs CPU. In addition, attempts to offline the last normal-CBs CPU will fail.
Changes since v1 (https://lkml.org/lkml/2012/9/5/572):
1. Contains fixes for a few problems located by Fengguang Wu's
kbuild test robot.
2. Counters are now atomic_long_t rather than atomic_t, as
suggested by Peter Zijlstra.
3. The rcu_nocbs= kernel boot parameter is now documented.
4. Fixed a bug introduced by commit c96ea7cf (Avoid spurious
RCU CPU stall warnings) that resulted in boot-time NULL-pointer dereferences (reported by Paul Gortmaker).
4. Account for normal and offloaded callbacks separately, so that
offloading is represented in debugfs output.
The patches in this series are as follows:
1. Offload RCU callbacks based on boot-time kernel parameter.
2. Account for normal and offloaded callbacks separately, so that
offloading is represented in debugfs output.
Thanx, Paul
b/Documentation/kernel-parameters.txt | 5 b/include/trace/events/rcu.h | 1 b/init/Kconfig | 19 + b/kernel/rcutree.c | 63 ++++- b/kernel/rcutree.h | 48 ++++ b/kernel/rcutree_plugin.h | 399 +++++++++++++++++++++++++++++++++- b/kernel/rcutree_trace.c | 12 - 7 files changed, 528 insertions(+), 19 deletions(-)
PATCH 2/2 rcu: Separate accounting of callbacks from callback-free CPUs
In reply to Paul E. McKenneyFrom: "Paul E. McKenney" <[email protected]>
Currently, callback invocations from callback-free CPUs are accounted to the CPU that registered the callback, but using the same field that is used for normal callbacks. This makes it impossible to determine from debugfs output whether callbacks are in fact being diverted. This commit therefore adds a separate ->n_nocbs_invoked field in the rcu_data structure in which diverted callback invocations are counted. RCU's debugfs tracing still displays normal callback invocations using ci=, but displayed diverted callbacks with nci=.
--- kernel/rcutree.h | 1 + kernel/rcutree_plugin.h | 2 +- kernel/rcutree_trace.c | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-)
kernel/rcutree.h +1 -0
@@ -287,6 +287,7 @@ struct rcu_data {long qlen_last_fqs_check;/* qlen at last check for QS forcing */ unsigned long n_cbs_invoked;/* count of RCU cbs invoked. */ +unsigned long n_nocbs_invoked;/* count of no-CBs RCU cbs invoked. */ unsigned long n_cbs_orphaned;/* RCU cbs orphaned by dying CPU */ unsigned long n_cbs_adopted;/* RCU cbs adopted from dying CPU */ unsigned long n_force_qs_snap;
kernel/rcutree_plugin.h +1 -1
@@ -2406,7 +2406,7 @@ static int rcu_nocb_kthread(void *arg)trace_rcu_batch_end (rdp->rsp->name, c, !!list,0 ,0 ,1 );ACCESS_ONCE (rdp->nocb_p_count) -= c;ACCESS_ONCE (rdp->nocb_p_count_lazy) -= cl; - rdp->n_cbs_invoked += c; + rdp->n_nocbs_invoked += c; }return 0 ; }
kernel/rcutree_trace.c +3 -2
@@ -146,8 +146,9 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) per_cpu(rcu_cpu_kthread_loops, rdp->cpu) & 0xffff);#endif /* #ifdef CONFIG_RCU_BOOST */ seq_printf (m," b=%ld" , rdp->blimit); -seq_printf (m," ci=%lu co=%lu ca=%lu\n" , - rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); +seq_printf (m," ci=%lu nci=%lu co=%lu ca=%lu\n" , + rdp->n_cbs_invoked, rdp->n_nocbs_invoked, + rdp->n_cbs_orphaned, rdp->n_cbs_adopted); }static int show_rcudata (struct seq_file *m,void *v)
Signature · 1 line
1.7.8
PATCH 1/2 rcu: Add callback-free CPUs
In reply to Paul E. McKenneyFrom: "Paul E. McKenney" <[email protected]>
RCU callback execution can add significant OS jitter and also can degrade scheduling latency. This commit therefore adds the ability for selected CPUs ("rcu_nocbs=" boot parameter) to have their callbacks offloaded to kthreads. If the "rcu_nocb_poll" boot parameter is also specified, these kthreads will do polling, removing the need for the offloaded CPUs to do wakeups. At least one CPU must be doing normal callback processing:
currently CPU 0 cannot be selected as a no-CBs CPU. In addition, attempts to offline the last normal-CBs CPU will fail.
This feature was inspired by Jim Houston's and Joe Korty's JRCU, and this commit includes fixes to problems located by Fengguang Wu's kbuild test robot.
[ paulmck: Added gfp.h include file as suggested by Fengguang Wu. ]
--- Documentation/kernel-parameters.txt | 5 + include/trace/events/rcu.h | 1 + init/Kconfig | 19 ++ kernel/rcutree.c | 63 +++++- kernel/rcutree.h | 47 ++++ kernel/rcutree_plugin.h | 397 ++++++++++++++++++++++++++++++++++- kernel/rcutree_trace.c | 7 +- 7 files changed, 523 insertions(+), 16 deletions(-)
Documentation/kernel-parameters.txt +5 -0
@@ -2394,6 +2394,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ramdisk_size= [RAM] Sizes of RAM disks in kilobytes See Documentation/blockdev/ramdisk.txt. + rcu_nocbs= [KNL,BOOT] + Set the specified list of CPUs to be no-callback + CPUs. Invocation of these CPUs' RCU callbacks will + be offloaded to kthreads created for that purpose. + rcutree.blimit= [KNL,BOOT] Set maximum number of finished RCU callbacks to process in one batch.
include/trace/events/rcu.h +1 -0
@@ -549,6 +549,7 @@ TRACE_EVENT(rcu_torture_read,* "EarlyExit": rcu_barrier_callback() piggybacked, thus early exit. * "Inc1": rcu_barrier_callback() piggyback check counter incremented. * "Offline": rcu_barrier_callback() found offline CPU +* "OnlineNoCB": rcu_barrier_callback() found online no-CBs CPU. * "OnlineQ": rcu_barrier_callback() found online CPU with callbacks. * "OnlineNQ": rcu_barrier_callback() found online CPU, no callbacks. * "IRQ": An rcu_barrier_callback() callback posted on remote CPU.
init/Kconfig +19 -0
@@ -654,6 +654,25 @@ config RCU_BOOST_DELAY Accept the default if unsure. +config RCU_NOCB_CPU + bool "Offload RCU callback processing from boot-selected CPUs" + depends on TREE_RCU || TREE_PREEMPT_RCU + default n + help + Use this option to reduce OS jitter for aggressive HPC or + real-time workloads. + + 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. + Nothing prevents this kthread from running on of of 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 off + of those CPUs if desired. + + Say Y here if you want reduced OS jitter on selected CPUs. + Say N here if you are unsure. + endmenu # "RCU Subsystem" config IKCONFIG
kernel/rcutree.c +51 -12
163 lines not on this page · raw message
kernel/rcutree.h +47 -0
70 lines not on this page · raw message
kernel/rcutree_plugin.h +394 -3
436 lines not on this page · raw message
kernel/rcutree_trace.c +6 -1
@@ -111,6 +111,8 @@ static char convert_kthread_status(unsigned int kthread_status)static void print_one_rcu_data (struct seq_file *m,struct rcu_data *rdp) { +long ql, qll; +if (!rdp->beenonline)return ; seq_printf(m, "%3d%cc=%lu g=%lu pq=%d qp=%d",
@@ -124,8 +126,11 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) rdp->dynticks->dynticks_nmi_nesting, rdp->dynticks_fqs);seq_printf (m," of=%lu" , rdp->offline_fqs); +rcu_nocb_q_lengths (rdp, &ql, &qll); + qll += rdp->qlen_lazy; + ql += rdp->qlen; seq_printf(m, " ql=%ld/%ld qs=%c%c%c%c", - rdp->qlen_lazy, rdp->qlen, + qll, ql, ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != rdp->nxttail[RCU_NEXT_TAIL]], ".R"[rdp->nxttail[RCU_WAIT_TAIL] !=
Signature · 1 line
1.7.8
Quoted 8 lines · Paul E. McKenney
This commit therefore adds the ability
for selected CPUs ("rcu_nocbs=" boot parameter) to have their callbacks
offloaded to kthreads, inspired by Joe Korty's and Jim Houston's JRCU. If the "rcu_nocb_poll" boot parameter is also specified, these kthreads
will do polling, removing the need for the offloaded CPUs to do wakeups.
How much pain to make that dynamically configurable?
Quoted 11 lines · Peter Zijlstra
Quoted 8 lines · Paul E. McKenney
This commit therefore adds the ability
for selected CPUs ("rcu_nocbs=" boot parameter) to have their callbacks
offloaded to kthreads, inspired by Joe Korty's and Jim Houston's JRCU. If the "rcu_nocb_poll" boot parameter is also specified, these kthreads
will do polling, removing the need for the offloaded CPUs to do wakeups.How much pain to make that dynamically configurable?
A fair amount of pain, though I do hope to make it runtime configurable at some point. Known problems include races between changes in the nocb configuration, rcu_barrier(), grace-period initialization, and CPU hotplug. Should be solvable, but want to get the other bugs out of the way first.
Thanx, Paul
Re: [PATCH tip/core/rcu 1/2] rcu: Add callback-free CPUs
In reply to Paul E. McKenney2012/10/31 Paul E. McKenney <[email protected]>:
Quoted 70 lines
kernel/rcutree_plugin.h +70
-0+/* +* Per-rcu_data kthread, but only for no-CBs CPUs. Each kthread invokes +* callbacks queued by the corresponding no-CBs CPU. +*/ +static int rcu_nocb_kthread (void *arg) +{ +int c, cl; +struct rcu_head *list; +struct rcu_head *next; +struct rcu_head **tail; +struct rcu_data *rdp = arg; + +/* Each pass through this loop invokes one batch of callbacks */ +for (;;) { +/* If not polling, wait for next batch of callbacks. */ +if (!rcu_nocb_poll) +wait_event (rdp->nocb_wq, rdp->nocb_head); + list =ACCESS_ONCE (rdp->nocb_head); +if (!list) { +schedule_timeout_interruptible (1 ); +continue ; + } + +/* +* Extract queued callbacks, update counts, and wait +* for a grace period to elapse. +*/ +ACCESS_ONCE (rdp->nocb_head) =NULL ; + tail =xchg (&rdp->nocb_tail, &rdp->nocb_head); + c =atomic_long_xchg (&rdp->nocb_q_count,0 ); + cl =atomic_long_xchg (&rdp->nocb_q_count_lazy,0 ); +ACCESS_ONCE (rdp->nocb_p_count) += c; +ACCESS_ONCE (rdp->nocb_p_count_lazy) += cl; +wait_rcu_gp (rdp->rsp->call_remote); + +/* Each pass through the following loop invokes a callback. */ +trace_rcu_batch_start (rdp->rsp->name, cl, c,-1 ); + c = cl =0 ; +while (list) { + next = list->next; +/* Wait for enqueuing to complete, if needed. */ +while (next ==NULL && &list->next != tail) { +schedule_timeout_interruptible (1 ); + next = list->next; + } +debug_rcu_head_unqueue (list); +local_bh_disable (); +if (__rcu_reclaim (rdp->rsp->name, list)) + cl++; + c++; +local_bh_enable (); + list = next; + } +trace_rcu_batch_end (rdp->rsp->name, c, !!list,0 ,0 ,1 ); +ACCESS_ONCE (rdp->nocb_p_count) -= c; +ACCESS_ONCE (rdp->nocb_p_count_lazy) -= cl; + rdp->n_cbs_invoked += c; + } +return 0 ; +} + +/* Initialize per-rcu_data variables for no-CBs CPUs. */ +static void __init rcu_boot_init_nocb_percpu_data (struct rcu_data *rdp) +{ + rdp->nocb_tail = &rdp->nocb_head; +init_waitqueue_head (&rdp->nocb_wq); +} + +/* Create a kthread for each RCU flavor for each no-CBs CPU. */ +static void __init rcu_spawn_nocb_kthreads (struct rcu_state *rsp)
kernel/rcutree_plugin.h static void increment_cpu_stall_ticks(void) · line 2432
Quoted 10 lines
kernel/rcutree_plugin.h +10
-0+{ +int cpu; +struct rcu_data *rdp; +struct task_struct *t; + +if (rcu_nocb_mask ==NULL ) +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);
Sorry, I think I left my brain in the middle of the diff. But there is something I'm misunderstanding I think. Here you're creating an rcu_nocb_kthread per nocb cpu. Looking at the code of rcu_nocb_kthread(), it seems to execute the callbacks with __rcu_reclaim().
So, in the end, no callbacks CPU execute their callbacks. Isn't it the opposite than what is expected? (again, just referring to my misunderstanding).
Thanks.
Quoted 4 lines
kernel/rcutree_plugin.h +4
-0+BUG_ON (IS_ERR (t)); +ACCESS_ONCE (rdp->nocb_kthread) = t; + } +}
Re: [PATCH tip/core/rcu 1/2] rcu: Add callback-free CPUs
Quoted 79 lines · Frederic Weisbecker
2012/10/31 Paul E. McKenney <[email protected]>:
Quoted 78 lines
kernel/rcutree_plugin.h +78
-0+/* +* Per-rcu_data kthread, but only for no-CBs CPUs. Each kthread invokes +* callbacks queued by the corresponding no-CBs CPU. +*/ +static int rcu_nocb_kthread (void *arg) +{ +int c, cl; +struct rcu_head *list; +struct rcu_head *next; +struct rcu_head **tail; +struct rcu_data *rdp = arg; + +/* Each pass through this loop invokes one batch of callbacks */ +for (;;) { +/* If not polling, wait for next batch of callbacks. */ +if (!rcu_nocb_poll) +wait_event (rdp->nocb_wq, rdp->nocb_head); + list =ACCESS_ONCE (rdp->nocb_head); +if (!list) { +schedule_timeout_interruptible (1 ); +continue ; + } + +/* +* Extract queued callbacks, update counts, and wait +* for a grace period to elapse. +*/ +ACCESS_ONCE (rdp->nocb_head) =NULL ; + tail =xchg (&rdp->nocb_tail, &rdp->nocb_head); + c =atomic_long_xchg (&rdp->nocb_q_count,0 ); + cl =atomic_long_xchg (&rdp->nocb_q_count_lazy,0 ); +ACCESS_ONCE (rdp->nocb_p_count) += c; +ACCESS_ONCE (rdp->nocb_p_count_lazy) += cl; +wait_rcu_gp (rdp->rsp->call_remote); + +/* Each pass through the following loop invokes a callback. */ +trace_rcu_batch_start (rdp->rsp->name, cl, c,-1 ); + c = cl =0 ; +while (list) { + next = list->next; +/* Wait for enqueuing to complete, if needed. */ +while (next ==NULL && &list->next != tail) { +schedule_timeout_interruptible (1 ); + next = list->next; + } +debug_rcu_head_unqueue (list); +local_bh_disable (); +if (__rcu_reclaim (rdp->rsp->name, list)) + cl++; + c++; +local_bh_enable (); + list = next; + } +trace_rcu_batch_end (rdp->rsp->name, c, !!list,0 ,0 ,1 ); +ACCESS_ONCE (rdp->nocb_p_count) -= c; +ACCESS_ONCE (rdp->nocb_p_count_lazy) -= cl; + rdp->n_cbs_invoked += c; + } +return 0 ; +} + +/* Initialize per-rcu_data variables for no-CBs CPUs. */ +static void __init rcu_boot_init_nocb_percpu_data (struct rcu_data *rdp) +{ + rdp->nocb_tail = &rdp->nocb_head; +init_waitqueue_head (&rdp->nocb_wq); +} + +/* Create a kthread for each RCU flavor for each no-CBs CPU. */ +static void __init rcu_spawn_nocb_kthreads (struct rcu_state *rsp) +{ +int cpu; +struct rcu_data *rdp; +struct task_struct *t; + +if (rcu_nocb_mask ==NULL ) +return ; +for_each_cpu (cpu, rcu_nocb_mask) {
Quoted 8 lines
kernel/rcutree_plugin.h static void increment_cpu_stall_ticks(void) · line 2432
Quoted 2 lines
kernel/rcutree_plugin.h +2
-0+ rdp =per_cpu_ptr (rsp->rda, cpu); + t =kthread_run (rcu_nocb_kthread, rdp,"rcuo%d" , cpu);Sorry, I think I left my brain in the middle of the diff. But there is something I'm misunderstanding I think. Here you're creating an
rcu_nocb_kthreadper nocb cpu. Looking at the code ofrcu_nocb_kthread(), it seems to execute the callbacks with__rcu_reclaim().
True, executing within the context of the kthread, which might be executing on any CPU.
Quoted 3 lines
So, in the end, no callbacks CPU execute their callbacks. Isn't it the opposite than what is expected? (again, just referring to my misunderstanding).
The no-callbacks CPU would execute its own callbacks only if the corresponding kthread happened to be executing on that CPU. If you wanted a given CPU to be completely free of callbacks, you would instead constrain the corresponding kthread to run elsewhere.
Thanx, Paul
Quoted 7 lines
Thanks.
Quoted 4 lines
kernel/rcutree_plugin.h +4
-0+BUG_ON (IS_ERR (t)); +ACCESS_ONCE (rdp->nocb_kthread) = t; + } +}
Re: [PATCH tip/core/rcu 1/2] rcu: Add callback-free CPUs
2012/11/2 Paul E. McKenney <[email protected]>:
Quoted 5 lines
The no-callbacks CPU would execute its own callbacks only if the corresponding kthread happened to be executing on that CPU. If you wanted a given CPU to be completely free of callbacks, you would instead constrain the corresponding kthread to run elsewhere.
Ah ok, makes sense.
Thanks.