Skip to content

Commit 8b355e3

Browse files
committed
rcu: Drive expedited grace periods from workqueue
The current implementation of expedited grace periods has the user task drive the grace period. This works, but has downsides: (1) The user task must awaken tasks piggybacking on this grace period, which can result in latencies rivaling that of the grace period itself, and (2) User tasks can receive signals, which interfere with RCU CPU stall warnings. This commit therefore uses workqueues to drive the grace periods, so that the user task need not do the awakening. A subsequent commit will remove the now-unnecessary code allowing for signals. Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
1 parent f7b8eb8 commit 8b355e3

File tree

3 files changed

+47
-9
lines changed

3 files changed

+47
-9
lines changed

kernel/rcu/tree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ struct rcu_data {
400400
#ifdef CONFIG_RCU_FAST_NO_HZ
401401
struct rcu_head oom_head;
402402
#endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */
403+
atomic_long_t exp_workdone0; /* # done by workqueue. */
403404
atomic_long_t exp_workdone1; /* # done by others #1. */
404405
atomic_long_t exp_workdone2; /* # done by others #2. */
405406
atomic_long_t exp_workdone3; /* # done by others #3. */

kernel/rcu/tree_exp.h

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,6 @@ static void rcu_exp_wait_wake(struct rcu_state *rsp, unsigned long s)
500500
* next GP, to proceed.
501501
*/
502502
mutex_lock(&rsp->exp_wake_mutex);
503-
mutex_unlock(&rsp->exp_mutex);
504503

505504
rcu_for_each_node_breadth_first(rsp, rnp) {
506505
if (ULONG_CMP_LT(READ_ONCE(rnp->exp_seq_rq), s)) {
@@ -516,13 +515,39 @@ static void rcu_exp_wait_wake(struct rcu_state *rsp, unsigned long s)
516515
mutex_unlock(&rsp->exp_wake_mutex);
517516
}
518517

518+
/* Let the workqueue handler know what it is supposed to do. */
519+
struct rcu_exp_work {
520+
smp_call_func_t rew_func;
521+
struct rcu_state *rew_rsp;
522+
unsigned long rew_s;
523+
struct work_struct rew_work;
524+
};
525+
526+
/*
527+
* Work-queue handler to drive an expedited grace period forward.
528+
*/
529+
static void wait_rcu_exp_gp(struct work_struct *wp)
530+
{
531+
struct rcu_exp_work *rewp;
532+
533+
/* Initialize the rcu_node tree in preparation for the wait. */
534+
rewp = container_of(wp, struct rcu_exp_work, rew_work);
535+
sync_rcu_exp_select_cpus(rewp->rew_rsp, rewp->rew_func);
536+
537+
/* Wait and clean up, including waking everyone. */
538+
rcu_exp_wait_wake(rewp->rew_rsp, rewp->rew_s);
539+
}
540+
519541
/*
520542
* Given an rcu_state pointer and a smp_call_function() handler, kick
521543
* off the specified flavor of expedited grace period.
522544
*/
523545
static void _synchronize_rcu_expedited(struct rcu_state *rsp,
524546
smp_call_func_t func)
525547
{
548+
struct rcu_data *rdp;
549+
struct rcu_exp_work rew;
550+
struct rcu_node *rnp;
526551
unsigned long s;
527552

528553
/* If expedited grace periods are prohibited, fall back to normal. */
@@ -536,11 +561,22 @@ static void _synchronize_rcu_expedited(struct rcu_state *rsp,
536561
if (exp_funnel_lock(rsp, s))
537562
return; /* Someone else did our work for us. */
538563

539-
/* Initialize the rcu_node tree in preparation for the wait. */
540-
sync_rcu_exp_select_cpus(rsp, func);
541-
542-
/* Wait and clean up, including waking everyone. */
543-
rcu_exp_wait_wake(rsp, s);
564+
/* Marshall arguments and schedule the expedited grace period. */
565+
rew.rew_func = func;
566+
rew.rew_rsp = rsp;
567+
rew.rew_s = s;
568+
INIT_WORK_ONSTACK(&rew.rew_work, wait_rcu_exp_gp);
569+
schedule_work(&rew.rew_work);
570+
571+
/* Wait for expedited grace period to complete. */
572+
rdp = per_cpu_ptr(rsp->rda, raw_smp_processor_id());
573+
rnp = rcu_get_root(rsp);
574+
wait_event(rnp->exp_wq[(s >> 1) & 0x3],
575+
sync_exp_work_done(rsp,
576+
&rdp->exp_workdone0, s));
577+
578+
/* Let the next expedited grace period start. */
579+
mutex_unlock(&rsp->exp_mutex);
544580
}
545581

546582
/**

kernel/rcu/tree_trace.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,16 +185,17 @@ static int show_rcuexp(struct seq_file *m, void *v)
185185
int cpu;
186186
struct rcu_state *rsp = (struct rcu_state *)m->private;
187187
struct rcu_data *rdp;
188-
unsigned long s1 = 0, s2 = 0, s3 = 0;
188+
unsigned long s0 = 0, s1 = 0, s2 = 0, s3 = 0;
189189

190190
for_each_possible_cpu(cpu) {
191191
rdp = per_cpu_ptr(rsp->rda, cpu);
192+
s0 += atomic_long_read(&rdp->exp_workdone0);
192193
s1 += atomic_long_read(&rdp->exp_workdone1);
193194
s2 += atomic_long_read(&rdp->exp_workdone2);
194195
s3 += atomic_long_read(&rdp->exp_workdone3);
195196
}
196-
seq_printf(m, "s=%lu wd1=%lu wd2=%lu wd3=%lu n=%lu enq=%d sc=%lu\n",
197-
rsp->expedited_sequence, s1, s2, s3,
197+
seq_printf(m, "s=%lu wd0=%lu wd1=%lu wd2=%lu wd3=%lu n=%lu enq=%d sc=%lu\n",
198+
rsp->expedited_sequence, s0, s1, s2, s3,
198199
atomic_long_read(&rsp->expedited_normal),
199200
atomic_read(&rsp->expedited_need_qs),
200201
rsp->expedited_sequence / 2);

0 commit comments

Comments
 (0)