Skip to content

Commit aafe12f

Browse files
author
Joel Fernandes
committed
rcutorture: Perform more frequent testing of ->gpwrap
Currently, the ->gpwrap is not tested (at all per my testing) due to the requirement of a large delta between a CPU's rdp->gp_seq and its node's rnp->gpseq. This results in no testing of ->gpwrap being set. This patch by default adds 5 minutes of testing with ->gpwrap forced by lowering the delta between rdp->gp_seq and rnp->gp_seq to just 8 GPs. All of this is configurable, including the active time for the setting and a full testing cycle. By default, the first 25 minutes of a test will have the _default_ behavior there is right now (ULONG_MAX / 4) delta. Then for 5 minutes, we switch to a smaller delta causing 1-2 wraps in 5 minutes. I believe this is reasonable since we at least add a little bit of testing for usecases where ->gpwrap is set. [ Apply fix for Dan Carpenter's bug report on init path cleanup. ] [ Apply kernel doc warning fix from Akira Yokosawa. ] Tested-by: Paul E. McKenney <[email protected]> Signed-off-by: Joel Fernandes <[email protected]>
1 parent 7e01c4c commit aafe12f

File tree

5 files changed

+138
-4
lines changed

5 files changed

+138
-4
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5657,6 +5657,31 @@
56575657
are zero, rcutorture acts as if is interpreted
56585658
they are all non-zero.
56595659

5660+
rcutorture.gpwrap_lag= [KNL]
5661+
Enable grace-period wrap lag testing. Setting
5662+
to false prevents the gpwrap lag test from
5663+
running. Default is true.
5664+
5665+
rcutorture.gpwrap_lag_gps= [KNL]
5666+
Set the value for grace-period wrap lag during
5667+
active lag testing periods. This controls how many
5668+
grace periods differences we tolerate between
5669+
rdp and rnp's gp_seq before setting overflow flag.
5670+
The default is always set to 8.
5671+
5672+
rcutorture.gpwrap_lag_cycle_mins= [KNL]
5673+
Set the total cycle duration for gpwrap lag
5674+
testing in minutes. This is the total time for
5675+
one complete cycle of active and inactive
5676+
testing periods. Default is 30 minutes.
5677+
5678+
rcutorture.gpwrap_lag_active_mins= [KNL]
5679+
Set the duration for which gpwrap lag is active
5680+
within each cycle, in minutes. During this time,
5681+
the grace-period wrap lag will be set to the
5682+
value specified by gpwrap_lag_gps. Default is
5683+
5 minutes.
5684+
56605685
rcutorture.irqreader= [KNL]
56615686
Run RCU readers from irq handlers, or, more
56625687
accurately, from a timer handler. Not all RCU

kernel/rcu/rcu.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,8 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
572572
unsigned long c_old,
573573
unsigned long c);
574574
void rcu_gp_set_torture_wait(int duration);
575+
void rcu_set_gpwrap_lag(unsigned long lag);
576+
int rcu_get_gpwrap_count(int cpu);
575577
#else
576578
static inline void rcutorture_get_gp_data(int *flags, unsigned long *gp_seq)
577579
{
@@ -589,6 +591,8 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
589591
do { } while (0)
590592
#endif
591593
static inline void rcu_gp_set_torture_wait(int duration) { }
594+
static inline void rcu_set_gpwrap_lag(unsigned long lag) { }
595+
static inline int rcu_get_gpwrap_count(int cpu) { return 0; }
592596
#endif
593597
unsigned long long rcutorture_gather_gp_seqs(void);
594598
void rcutorture_format_gp_seqs(unsigned long long seqs, char *cp, size_t len);

kernel/rcu/rcutorture.c

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ torture_param(int, nreaders, -1, "Number of RCU reader threads");
115115
torture_param(int, object_debug, 0, "Enable debug-object double call_rcu() testing");
116116
torture_param(int, onoff_holdoff, 0, "Time after boot before CPU hotplugs (s)");
117117
torture_param(int, onoff_interval, 0, "Time between CPU hotplugs (jiffies), 0=disable");
118+
torture_param(bool, gpwrap_lag, true, "Enable grace-period wrap lag testing");
119+
torture_param(int, gpwrap_lag_gps, 8, "Value to set for set_gpwrap_lag during an active testing period.");
120+
torture_param(int, gpwrap_lag_cycle_mins, 30, "Total cycle duration for gpwrap lag testing (in minutes)");
121+
torture_param(int, gpwrap_lag_active_mins, 5, "Duration for which gpwrap lag is active within each cycle (in minutes)");
118122
torture_param(int, nocbs_nthreads, 0, "Number of NOCB toggle threads, 0 to disable");
119123
torture_param(int, nocbs_toggle, 1000, "Time between toggling nocb state (ms)");
120124
torture_param(int, preempt_duration, 0, "Preemption duration (ms), zero to disable");
@@ -413,6 +417,8 @@ struct rcu_torture_ops {
413417
bool (*reader_blocked)(void);
414418
unsigned long long (*gather_gp_seqs)(void);
415419
void (*format_gp_seqs)(unsigned long long seqs, char *cp, size_t len);
420+
void (*set_gpwrap_lag)(unsigned long lag);
421+
int (*get_gpwrap_count)(int cpu);
416422
long cbflood_max;
417423
int irq_capable;
418424
int can_boost;
@@ -619,6 +625,8 @@ static struct rcu_torture_ops rcu_ops = {
619625
: NULL,
620626
.gather_gp_seqs = rcutorture_gather_gp_seqs,
621627
.format_gp_seqs = rcutorture_format_gp_seqs,
628+
.set_gpwrap_lag = rcu_set_gpwrap_lag,
629+
.get_gpwrap_count = rcu_get_gpwrap_count,
622630
.irq_capable = 1,
623631
.can_boost = IS_ENABLED(CONFIG_RCU_BOOST),
624632
.extendables = RCUTORTURE_MAX_EXTEND,
@@ -2432,6 +2440,7 @@ rcu_torture_stats_print(void)
24322440
int i;
24332441
long pipesummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 };
24342442
long batchsummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 };
2443+
long n_gpwraps = 0;
24352444
struct rcu_torture *rtcp;
24362445
static unsigned long rtcv_snap = ULONG_MAX;
24372446
static bool splatted;
@@ -2442,6 +2451,8 @@ rcu_torture_stats_print(void)
24422451
pipesummary[i] += READ_ONCE(per_cpu(rcu_torture_count, cpu)[i]);
24432452
batchsummary[i] += READ_ONCE(per_cpu(rcu_torture_batch, cpu)[i]);
24442453
}
2454+
if (cur_ops->get_gpwrap_count)
2455+
n_gpwraps += cur_ops->get_gpwrap_count(cpu);
24452456
}
24462457
for (i = RCU_TORTURE_PIPE_LEN; i >= 0; i--) {
24472458
if (pipesummary[i] != 0)
@@ -2473,8 +2484,9 @@ rcu_torture_stats_print(void)
24732484
data_race(n_barrier_attempts),
24742485
data_race(n_rcu_torture_barrier_error));
24752486
pr_cont("read-exits: %ld ", data_race(n_read_exits)); // Statistic.
2476-
pr_cont("nocb-toggles: %ld:%ld\n",
2487+
pr_cont("nocb-toggles: %ld:%ld ",
24772488
atomic_long_read(&n_nocb_offload), atomic_long_read(&n_nocb_deoffload));
2489+
pr_cont("gpwraps: %ld\n", n_gpwraps);
24782490

24792491
pr_alert("%s%s ", torture_type, TORTURE_FLAG);
24802492
if (atomic_read(&n_rcu_torture_mberror) ||
@@ -3645,6 +3657,57 @@ static int rcu_torture_preempt(void *unused)
36453657

36463658
static enum cpuhp_state rcutor_hp;
36473659

3660+
static struct hrtimer gpwrap_lag_timer;
3661+
static bool gpwrap_lag_active;
3662+
3663+
/* Timer handler for toggling RCU grace-period sequence overflow test lag value */
3664+
static enum hrtimer_restart rcu_gpwrap_lag_timer(struct hrtimer *timer)
3665+
{
3666+
ktime_t next_delay;
3667+
3668+
if (gpwrap_lag_active) {
3669+
pr_alert("rcu-torture: Disabling gpwrap lag (value=0)\n");
3670+
cur_ops->set_gpwrap_lag(0);
3671+
gpwrap_lag_active = false;
3672+
next_delay = ktime_set((gpwrap_lag_cycle_mins - gpwrap_lag_active_mins) * 60, 0);
3673+
} else {
3674+
pr_alert("rcu-torture: Enabling gpwrap lag (value=%d)\n", gpwrap_lag_gps);
3675+
cur_ops->set_gpwrap_lag(gpwrap_lag_gps);
3676+
gpwrap_lag_active = true;
3677+
next_delay = ktime_set(gpwrap_lag_active_mins * 60, 0);
3678+
}
3679+
3680+
if (torture_must_stop_irq())
3681+
return HRTIMER_NORESTART;
3682+
3683+
hrtimer_forward_now(timer, next_delay);
3684+
return HRTIMER_RESTART;
3685+
}
3686+
3687+
static int rcu_gpwrap_lag_init(void)
3688+
{
3689+
if (!gpwrap_lag)
3690+
return 0;
3691+
3692+
if (gpwrap_lag_cycle_mins <= 0 || gpwrap_lag_active_mins <= 0) {
3693+
pr_alert("rcu-torture: lag timing parameters must be positive\n");
3694+
return -EINVAL;
3695+
}
3696+
3697+
hrtimer_setup(&gpwrap_lag_timer, rcu_gpwrap_lag_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
3698+
gpwrap_lag_active = false;
3699+
hrtimer_start(&gpwrap_lag_timer,
3700+
ktime_set((gpwrap_lag_cycle_mins - gpwrap_lag_active_mins) * 60, 0), HRTIMER_MODE_REL);
3701+
3702+
return 0;
3703+
}
3704+
3705+
static void rcu_gpwrap_lag_cleanup(void)
3706+
{
3707+
hrtimer_cancel(&gpwrap_lag_timer);
3708+
cur_ops->set_gpwrap_lag(0);
3709+
gpwrap_lag_active = false;
3710+
}
36483711
static void
36493712
rcu_torture_cleanup(void)
36503713
{
@@ -3814,6 +3877,9 @@ rcu_torture_cleanup(void)
38143877
torture_cleanup_end();
38153878
if (cur_ops->gp_slow_unregister)
38163879
cur_ops->gp_slow_unregister(NULL);
3880+
3881+
if (gpwrap_lag && cur_ops->set_gpwrap_lag)
3882+
rcu_gpwrap_lag_cleanup();
38173883
}
38183884

38193885
static void rcu_torture_leak_cb(struct rcu_head *rhp)
@@ -4310,9 +4376,17 @@ rcu_torture_init(void)
43104376
}
43114377
if (object_debug)
43124378
rcu_test_debug_objects();
4313-
torture_init_end();
4379+
43144380
if (cur_ops->gp_slow_register && !WARN_ON_ONCE(!cur_ops->gp_slow_unregister))
43154381
cur_ops->gp_slow_register(&rcu_fwd_cb_nodelay);
4382+
4383+
if (gpwrap_lag && cur_ops->set_gpwrap_lag) {
4384+
firsterr = rcu_gpwrap_lag_init();
4385+
if (torture_init_error(firsterr))
4386+
goto unwind;
4387+
}
4388+
4389+
torture_init_end();
43164390
return 0;
43174391

43184392
unwind:

kernel/rcu/tree.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ static void rcu_sr_normal_gp_cleanup_work(struct work_struct *);
8080
static DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_data, rcu_data) = {
8181
.gpwrap = true,
8282
};
83+
84+
int rcu_get_gpwrap_count(int cpu)
85+
{
86+
struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
87+
88+
return READ_ONCE(rdp->gpwrap_count);
89+
}
90+
EXPORT_SYMBOL_GPL(rcu_get_gpwrap_count);
91+
8392
static struct rcu_state rcu_state = {
8493
.level = { &rcu_state.node[0] },
8594
.gp_state = RCU_GP_IDLE,
@@ -757,6 +766,25 @@ void rcu_request_urgent_qs_task(struct task_struct *t)
757766
smp_store_release(per_cpu_ptr(&rcu_data.rcu_urgent_qs, cpu), true);
758767
}
759768

769+
static unsigned long seq_gpwrap_lag = ULONG_MAX / 4;
770+
771+
/**
772+
* rcu_set_gpwrap_lag - Set RCU GP sequence overflow lag value.
773+
* @lag_gps: Set overflow lag to this many grace period worth of counters
774+
* which is used by rcutorture to quickly force a gpwrap situation.
775+
* @lag_gps = 0 means we reset it back to the boot-time value.
776+
*/
777+
void rcu_set_gpwrap_lag(unsigned long lag_gps)
778+
{
779+
unsigned long lag_seq_count;
780+
781+
lag_seq_count = (lag_gps == 0)
782+
? ULONG_MAX / 4
783+
: lag_gps << RCU_SEQ_CTR_SHIFT;
784+
WRITE_ONCE(seq_gpwrap_lag, lag_seq_count);
785+
}
786+
EXPORT_SYMBOL_GPL(rcu_set_gpwrap_lag);
787+
760788
/*
761789
* When trying to report a quiescent state on behalf of some other CPU,
762790
* it is our responsibility to check for and handle potential overflow
@@ -767,9 +795,11 @@ void rcu_request_urgent_qs_task(struct task_struct *t)
767795
static void rcu_gpnum_ovf(struct rcu_node *rnp, struct rcu_data *rdp)
768796
{
769797
raw_lockdep_assert_held_rcu_node(rnp);
770-
if (ULONG_CMP_LT(rcu_seq_current(&rdp->gp_seq) + ULONG_MAX / 4,
771-
rnp->gp_seq))
798+
if (ULONG_CMP_LT(rcu_seq_current(&rdp->gp_seq) + seq_gpwrap_lag,
799+
rnp->gp_seq)) {
772800
WRITE_ONCE(rdp->gpwrap, true);
801+
WRITE_ONCE(rdp->gpwrap_count, READ_ONCE(rdp->gpwrap_count) + 1);
802+
}
773803
if (ULONG_CMP_LT(rdp->rcu_iw_gp_seq + ULONG_MAX / 4, rnp->gp_seq))
774804
rdp->rcu_iw_gp_seq = rnp->gp_seq + ULONG_MAX / 4;
775805
}

kernel/rcu/tree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ struct rcu_data {
183183
bool core_needs_qs; /* Core waits for quiescent state. */
184184
bool beenonline; /* CPU online at least once. */
185185
bool gpwrap; /* Possible ->gp_seq wrap. */
186+
unsigned int gpwrap_count; /* Count of GP sequence wrap. */
186187
bool cpu_started; /* RCU watching this onlining CPU. */
187188
struct rcu_node *mynode; /* This CPU's leaf of hierarchy */
188189
unsigned long grpmask; /* Mask to apply to leaf qsmask. */

0 commit comments

Comments
 (0)