MODULE_DESCRIPTION("Read-Copy Update module-based torture test facility");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Paul E. McKenney <paulmck@linux.ibm.com> and Josh Triplett <josh@joshtriplett.org>");
// Bits for ->extendables field, extendables param, and related definitions. #define RCUTORTURE_RDR_SHIFT_1 8 // Put SRCU index in upper bits. #define RCUTORTURE_RDR_MASK_1 (0xff << RCUTORTURE_RDR_SHIFT_1) #define RCUTORTURE_RDR_SHIFT_2 16 // Put SRCU index in upper bits. #define RCUTORTURE_RDR_MASK_2 (0xff << RCUTORTURE_RDR_SHIFT_2) #define RCUTORTURE_RDR_BH 0x01 // Extend readers by disabling bh. #define RCUTORTURE_RDR_IRQ 0x02 // ... disabling interrupts. #define RCUTORTURE_RDR_PREEMPT 0x04 // ... disabling preemption. #define RCUTORTURE_RDR_RBH 0x08 // ... rcu_read_lock_bh(). #define RCUTORTURE_RDR_SCHED 0x10 // ... rcu_read_lock_sched(). #define RCUTORTURE_RDR_RCU_1 0x20 // ... entering another RCU reader. #define RCUTORTURE_RDR_RCU_2 0x40 // ... entering another RCU reader. #define RCUTORTURE_RDR_UPDOWN 0x80 // ... up-read from task, down-read from timer. // Note: Manual start, automatic end. #define RCUTORTURE_RDR_NBITS 8 // Number of bits defined above. #define RCUTORTURE_MAX_EXTEND \
(RCUTORTURE_RDR_BH | RCUTORTURE_RDR_IRQ | RCUTORTURE_RDR_PREEMPT | \
RCUTORTURE_RDR_RBH | RCUTORTURE_RDR_SCHED) // Intentionally omit RCUTORTURE_RDR_UPDOWN. #define RCUTORTURE_RDR_ALLBITS \
(RCUTORTURE_MAX_EXTEND | RCUTORTURE_RDR_RCU_1 | RCUTORTURE_RDR_RCU_2 | \
RCUTORTURE_RDR_MASK_1 | RCUTORTURE_RDR_MASK_2) #define RCUTORTURE_RDR_MAX_LOOPS 0x7 /* Maximum reader extensions. */ /* Must be power of two minus one. */ #define RCUTORTURE_RDR_MAX_SEGS (RCUTORTURE_RDR_MAX_LOOPS + 3)
torture_param(int, extendables, RCUTORTURE_MAX_EXTEND, "Extend readers by disabling bh (1), irqs (2), or preempt (4)");
torture_param(int, fqs_duration, 0, "Duration of fqs bursts (us), 0 to disable");
torture_param(int, fqs_holdoff, 0, "Holdoff time within fqs bursts (us)");
torture_param(int, fqs_stutter, 3, "Wait time between fqs bursts (s)");
torture_param(int, fwd_progress, 1, "Number of grace-period forward progress tasks (0 to disable)");
torture_param(int, fwd_progress_div, 4, "Fraction of CPU stall to wait");
torture_param(int, fwd_progress_holdoff, 60, "Time between forward-progress tests (s)");
torture_param(bool, fwd_progress_need_resched, 1, "Hide cond_resched() behind need_resched()");
torture_param(bool, gp_cond, false, "Use conditional/async GP wait primitives");
torture_param(bool, gp_cond_exp, false, "Use conditional/async expedited GP wait primitives");
torture_param(bool, gp_cond_full, false, "Use conditional/async full-state GP wait primitives");
torture_param(bool, gp_cond_exp_full, false, "Use conditional/async full-stateexpedited GP wait primitives");
torture_param(int, gp_cond_wi, 16 * USEC_PER_SEC / HZ, "Wait interval for normal conditional grace periods, us (default 16 jiffies)");
torture_param(int, gp_cond_wi_exp, 128, "Wait interval for expedited conditional grace periods, us (default 128 us)");
torture_param(bool, gp_exp, false, "Use expedited GP wait primitives");
torture_param(bool, gp_normal, false, "Use normal (non-expedited) GP wait primitives");
torture_param(bool, gp_poll, false, "Use polling GP wait primitives");
torture_param(bool, gp_poll_exp, false, "Use polling expedited GP wait primitives");
torture_param(bool, gp_poll_full, false, "Use polling full-state GP wait primitives");
torture_param(bool, gp_poll_exp_full, false, "Use polling full-state expedited GP wait primitives");
torture_param(int, gp_poll_wi, 16 * USEC_PER_SEC / HZ, "Wait interval for normal polled grace periods, us (default 16 jiffies)");
torture_param(int, gp_poll_wi_exp, 128, "Wait interval for expedited polled grace periods, us (default 128 us)");
torture_param(bool, gp_sync, false, "Use synchronous GP wait primitives");
torture_param(int, irqreader, 1, "Allow RCU readers from irq handlers");
torture_param(int, leakpointer, 0, "Leak pointer dereferences from readers");
torture_param(int, n_barrier_cbs, 0, "# of callbacks/kthreads for barrier testing");
torture_param(int, n_up_down, 32, "# of concurrent up/down hrtimer-based RCU readers");
torture_param(int, nfakewriters, 4, "Number of RCU fake writer threads");
torture_param(int, nreaders, -1, "Number of RCU reader threads");
torture_param(int, object_debug, 0, "Enable debug-object double call_rcu() testing");
torture_param(int, onoff_holdoff, 0, "Time after boot before CPU hotplugs (s)");
torture_param(int, onoff_interval, 0, "Time between CPU hotplugs (jiffies), 0=disable");
torture_param(bool, gpwrap_lag, true, "Enable grace-period wrap lag testing");
torture_param(int, gpwrap_lag_gps, 8, "Value to set for set_gpwrap_lag during an active testing period.");
torture_param(int, gpwrap_lag_cycle_mins, 30, "Total cycle duration for gpwrap lag testing (in minutes)");
torture_param(int, gpwrap_lag_active_mins, 5, "Duration for which gpwrap lag is active within each cycle (in minutes)");
torture_param(int, nocbs_nthreads, 0, "Number of NOCB toggle threads, 0 to disable");
torture_param(int, nocbs_toggle, 1000, "Time between toggling nocb state (ms)");
torture_param(int, preempt_duration, 0, "Preemption duration (ms), zero to disable");
torture_param(int, preempt_interval, MSEC_PER_SEC, "Interval between preemptions (ms)");
torture_param(int, read_exit_delay, 13, "Delay between read-then-exit episodes (s)");
torture_param(int, read_exit_burst, 16, "# of read-then-exit bursts per episode, zero to disable");
torture_param(int, reader_flavor, SRCU_READ_FLAVOR_NORMAL, "Reader flavors to use, one per bit.");
torture_param(int, shuffle_interval, 3, "Number of seconds between shuffles");
torture_param(int, shutdown_secs, 0, "Shutdown time (s), <= zero to disable.");
torture_param(int, stall_cpu, 0, "Stall duration (s), zero to disable.");
torture_param(int, stall_cpu_holdoff, 10, "Time to wait before starting stall (s).");
torture_param(bool, stall_no_softlockup, false, "Avoid softlockup warning during cpu stall.");
torture_param(int, stall_cpu_irqsoff, 0, "Disable interrupts while stalling.");
torture_param(int, stall_cpu_block, 0, "Sleep while stalling.");
torture_param(int, stall_cpu_repeat, 0, "Number of additional stalls after the first one.");
torture_param(int, stall_gp_kthread, 0, "Grace-period kthread stall duration (s).");
torture_param(int, stat_interval, 60, "Number of seconds between stats printk()s");
torture_param(int, stutter, 5, "Number of seconds to run/halt test");
torture_param(int, test_boost, 1, "Test RCU prio boost: 0=no, 1=maybe, 2=yes.");
torture_param(int, test_boost_duration, 4, "Duration of each boost test, seconds.");
torture_param(int, test_boost_holdoff, 0, "Holdoff time from rcutorture start, seconds.");
torture_param(int, test_boost_interval, 7, "Interval between boost tests, seconds.");
torture_param(int, test_nmis, 0, "End-test NMI tests, 0 to disable.");
torture_param(bool, test_no_idle_hz, true, "Test support for tickless idle CPUs");
torture_param(int, test_srcu_lockdep, 0, "Test specified SRCU deadlock scenario.");
torture_param(int, verbose, 1, "Enable verbose debugging printk()s");
staticchar *torture_type = "rcu";
module_param(torture_type, charp, 0444);
MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, srcu, ...)");
/* * Stop aggressive CPU-hog tests a bit before the end of the test in order * to avoid interfering with test shutdown.
*/ staticbool shutdown_time_arrived(void)
{ return shutdown_secs && time_after(jiffies, shutdown_jiffies - 30 * HZ);
}
/* * Free an element to the rcu_tortures pool.
*/ staticvoid
rcu_torture_free(struct rcu_torture *p)
{
atomic_inc(&n_rcu_torture_free);
spin_lock_bh(&rcu_torture_lock);
list_add_tail(&p->rtort_free, &rcu_torture_freelist);
spin_unlock_bh(&rcu_torture_lock);
}
/* * Operations vector for selecting different types of tests.
*/
struct rcu_torture_ops { int ttype; void (*init)(void); void (*cleanup)(void); int (*readlock)(void); void (*read_delay)(struct torture_random_state *rrsp, struct rt_read_seg *rtrsp); void (*readunlock)(int idx); int (*readlock_held)(void); // lockdep. int (*readlock_nesting)(void); // actual nesting, if available, -1 if not. int (*down_read)(void); void (*up_read)(int idx); unsignedlong (*get_gp_seq)(void); unsignedlong (*gp_diff)(unsignedlongnew, unsignedlong old); void (*deferred_free)(struct rcu_torture *p); void (*sync)(void); void (*exp_sync)(void); unsignedlong (*get_gp_state_exp)(void); unsignedlong (*start_gp_poll_exp)(void); void (*start_gp_poll_exp_full)(struct rcu_gp_oldstate *rgosp); bool (*poll_gp_state_exp)(unsignedlong oldstate); void (*cond_sync_exp)(unsignedlong oldstate); void (*cond_sync_exp_full)(struct rcu_gp_oldstate *rgosp); unsignedlong (*get_comp_state)(void); void (*get_comp_state_full)(struct rcu_gp_oldstate *rgosp); bool (*same_gp_state)(unsignedlong oldstate1, unsignedlong oldstate2); bool (*same_gp_state_full)(struct rcu_gp_oldstate *rgosp1, struct rcu_gp_oldstate *rgosp2); unsignedlong (*get_gp_state)(void); void (*get_gp_state_full)(struct rcu_gp_oldstate *rgosp); unsignedlong (*start_gp_poll)(void); void (*start_gp_poll_full)(struct rcu_gp_oldstate *rgosp); bool (*poll_gp_state)(unsignedlong oldstate); bool (*poll_gp_state_full)(struct rcu_gp_oldstate *rgosp); bool (*poll_need_2gp)(bool poll, bool poll_full); void (*cond_sync)(unsignedlong oldstate); void (*cond_sync_full)(struct rcu_gp_oldstate *rgosp); int poll_active; int poll_active_full;
call_rcu_func_t call; void (*cb_barrier)(void); void (*fqs)(void); void (*stats)(void); void (*gp_kthread_dbg)(void); bool (*check_boost_failed)(unsignedlong gp_state, int *cpup); int (*stall_dur)(void); void (*get_gp_data)(int *flags, unsignedlong *gp_seq); void (*gp_slow_register)(atomic_t *rgssp); void (*gp_slow_unregister)(atomic_t *rgssp); bool (*reader_blocked)(void); unsignedlonglong (*gather_gp_seqs)(void); void (*format_gp_seqs)(unsignedlonglong seqs, char *cp, size_t len); void (*set_gpwrap_lag)(unsignedlong lag); int (*get_gpwrap_count)(int cpu); long cbflood_max; int irq_capable; int can_boost; int extendables; int slow_gps; int no_pi_lock; int debug_objects; int start_poll_irqsoff; int have_up_down; constchar *name;
};
/* We want a short delay sometimes to make a reader delay the grace * period, and we want a long delay occasionally to trigger
* force_quiescent_state. */
staticint rcu_torture_readlock_nesting(void)
{ if (IS_ENABLED(CONFIG_PREEMPT_RCU)) return rcu_preempt_depth(); if (IS_ENABLED(CONFIG_PREEMPT_COUNT)) return (preempt_count() & PREEMPT_MASK); return -1;
}
/* * Update callback in the pipe. This should be invoked after a grace period.
*/ staticbool
rcu_torture_pipe_update_one(struct rcu_torture *rp)
{ int i; struct rcu_torture_reader_check *rtrcp = READ_ONCE(rp->rtort_chkp);
if (rtrcp) {
WRITE_ONCE(rp->rtort_chkp, NULL);
smp_store_release(&rtrcp->rtc_ready, 1); // Pair with smp_load_acquire().
}
i = rp->rtort_pipe_count; if (i > RCU_TORTURE_PIPE_LEN)
i = RCU_TORTURE_PIPE_LEN;
atomic_inc(&rcu_torture_wcount[i]);
WRITE_ONCE(rp->rtort_pipe_count, i + 1);
ASSERT_EXCLUSIVE_WRITER(rp->rtort_pipe_count); if (i + 1 >= RCU_TORTURE_PIPE_LEN) {
rp->rtort_mbtest = 0; returntrue;
} returnfalse;
}
/* * Update all callbacks in the pipe. Suitable for synchronous grace-period * primitives.
*/ staticvoid
rcu_torture_pipe_update(struct rcu_torture *old_rp)
{ struct rcu_torture *rp; struct rcu_torture *rp1;
if (old_rp)
list_add(&old_rp->rtort_free, &rcu_torture_removed);
list_for_each_entry_safe(rp, rp1, &rcu_torture_removed, rtort_free) { if (rcu_torture_pipe_update_one(rp)) {
list_del(&rp->rtort_free);
rcu_torture_free(rp);
}
}
}
if (torture_must_stop_irq()) { /* Test is ending, just drop callbacks on the floor. */ /* The next initialization will pick up the pieces. */ return;
} if (rcu_torture_pipe_update_one(rp))
rcu_torture_free(rp); else
cur_ops->deferred_free(rp);
}
/* * Don't even think about trying any of these in real life!!! * The names includes "busted", and they really means it! * The only purpose of these functions is to provide a buggy RCU * implementation to make sure that rcutorture correctly emits * buggy-RCU error messages.
*/ staticvoid rcu_busted_torture_deferred_free(struct rcu_torture *p)
{ /* This is a deliberate bug for testing purposes only! */
rcu_torture_cb(&p->rtort_rcu);
}
staticvoid synchronize_rcu_busted(void)
{ /* This is a deliberate bug for testing purposes only! */
}
staticvoid
call_rcu_busted(struct rcu_head *head, rcu_callback_t func)
{ /* This is a deliberate bug for testing purposes only! */
func(head);
}
/* * Definitions for trivial CONFIG_PREEMPT=n-only torture testing. * This implementation does not work well with CPU hotplug nor * with rcutorture's shuffling.
*/
staticvoid synchronize_rcu_trivial(void)
{ int cpu;
/* * RCU torture priority-boost testing. Runs one real-time thread per * CPU for moderate bursts, repeatedly starting grace periods and waiting * for them to complete. If a given grace period takes too long, we assume * that priority inversion has occurred.
*/
staticint old_rt_runtime = -1;
staticvoid rcu_torture_disable_rt_throttle(void)
{ /* * Disable RT throttling so that rcutorture's boost threads don't get * throttled. Only possible if rcutorture is built-in otherwise the * user should manually do this by setting the sched_rt_period_us and * sched_rt_runtime sysctls.
*/ if (!IS_BUILTIN(CONFIG_RCU_TORTURE_TEST) || old_rt_runtime != -1) return;
if (end - *start > mininterval) { // Recheck after checking time to avoid false positives.
smp_mb(); // Time check before grace-period check. if (cur_ops->poll_gp_state(gp_state)) returnfalse; // passed, though perhaps just barely if (cur_ops->check_boost_failed && !cur_ops->check_boost_failed(gp_state, &cpu)) { // At most one persisted message per boost test.
j = jiffies;
lp = READ_ONCE(last_persist); if (time_after(j, lp + mininterval) &&
cmpxchg(&last_persist, lp, j) == lp) { if (cpu < 0)
pr_info("Boost inversion persisted: QS from all CPUs\n"); else
pr_info("Boost inversion persisted: No QS from CPU %d\n", cpu);
} returnfalse; // passed on a technicality
}
VERBOSE_TOROUT_STRING("rcu_torture_boost boosting failed");
n_rcu_torture_boost_failure++; if (!xchg(&dbg_done, 1) && cur_ops->gp_kthread_dbg) {
pr_info("Boost inversion thread ->rt_priority %u gp_state %lu jiffies %lu\n",
current->rt_priority, gp_state, end - *start);
cur_ops->gp_kthread_dbg(); // Recheck after print to flag grace period ending during splat.
gp_done = cur_ops->poll_gp_state(gp_state);
pr_info("Boost inversion: GP %lu %s.\n", gp_state,
gp_done ? "ended already" : "still pending");
if (test_boost_holdoff <= 0 || time_after(jiffies, booststarttime)) {
VERBOSE_TOROUT_STRING("rcu_torture_boost started");
} else {
VERBOSE_TOROUT_STRING("rcu_torture_boost started holdoff period"); while (time_before(jiffies, booststarttime)) {
schedule_timeout_idle(HZ); if (kthread_should_stop()) goto cleanup;
}
VERBOSE_TOROUT_STRING("rcu_torture_boost finished holdoff period");
}
/* Set real-time priority. */
sched_set_fifo_low(current);
/* Each pass through the following loop does one boost-test cycle. */ do { bool failed = false; // Test failed already in this test interval bool gp_initiated = false;
if (kthread_should_stop()) goto checkwait;
/* Wait for the next test interval. */
oldstarttime = READ_ONCE(boost_starttime); while (time_before(jiffies, oldstarttime)) {
schedule_timeout_interruptible(oldstarttime - jiffies); if (stutter_wait("rcu_torture_boost"))
sched_set_fifo_low(current); if (torture_must_stop()) goto checkwait;
}
// Do one boost-test interval.
endtime = oldstarttime + test_boost_duration * HZ; while (time_before(jiffies, endtime)) { // Has current GP gone too long? if (gp_initiated && !failed && !cur_ops->poll_gp_state(gp_state))
failed = rcu_torture_boost_failed(gp_state, &gp_state_time); // If we don't have a grace period in flight, start one. if (!gp_initiated || cur_ops->poll_gp_state(gp_state)) {
gp_state = cur_ops->start_gp_poll();
gp_initiated = true;
gp_state_time = jiffies;
} if (stutter_wait("rcu_torture_boost")) {
sched_set_fifo_low(current); // If the grace period already ended, // we don't know when that happened, so // start over. if (cur_ops->poll_gp_state(gp_state))
gp_initiated = false;
} if (torture_must_stop()) goto checkwait;
}
// In case the grace period extended beyond the end of the loop. if (gp_initiated && !failed && !cur_ops->poll_gp_state(gp_state))
rcu_torture_boost_failed(gp_state, &gp_state_time);
/* * Set the start time of the next test interval. * Yes, this is vulnerable to long delays, but such * delays simply cause a false negative for the next * interval. Besides, we are running at RT priority, * so delays should be relatively rare.
*/ while (oldstarttime == READ_ONCE(boost_starttime) && !kthread_should_stop()) { if (mutex_trylock(&boost_mutex)) { if (oldstarttime == boost_starttime) {
WRITE_ONCE(boost_starttime,
jiffies + test_boost_interval * HZ);
n_rcu_torture_boosts++;
}
mutex_unlock(&boost_mutex); break;
}
schedule_timeout_uninterruptible(HZ / 20);
}
/* Go do the stutter. */
checkwait: if (stutter_wait("rcu_torture_boost"))
sched_set_fifo_low(current);
} while (!torture_must_stop());
cleanup: /* Clean up and exit. */ while (!kthread_should_stop()) {
torture_shutdown_absorb("rcu_torture_boost");
schedule_timeout_uninterruptible(HZ / 20);
}
torture_kthread_stopping("rcu_torture_boost"); return 0;
}
/* * RCU torture force-quiescent-state kthread. Repeatedly induces * bursts of calls to force_quiescent_state(), increasing the probability * of occurrence of some important types of race conditions.
*/ staticint
rcu_torture_fqs(void *arg)
{ unsignedlong fqs_resume_time; int fqs_burst_remaining; int oldnice = task_nice(current);
VERBOSE_TOROUT_STRING("rcu_torture_fqs task started"); do {
fqs_resume_time = jiffies + fqs_stutter * HZ; while (time_before(jiffies, fqs_resume_time) &&
!kthread_should_stop()) {
schedule_timeout_interruptible(HZ / 20);
}
fqs_burst_remaining = fqs_duration; while (fqs_burst_remaining > 0 &&
!kthread_should_stop()) {
cur_ops->fqs();
udelay(fqs_holdoff);
fqs_burst_remaining -= fqs_holdoff;
} if (stutter_wait("rcu_torture_fqs"))
sched_set_normal(current, oldnice);
} while (!torture_must_stop());
torture_kthread_stopping("rcu_torture_fqs"); return 0;
}
// Used by writers to randomly choose from the available grace-period primitives. staticint synctype[ARRAY_SIZE(rcu_torture_writer_state_names)] = { }; staticint nsynctypes;
/* * Do the specified rcu_torture_writer() synchronous grace period, * while also testing out the polled APIs. Note well that the single-CPU * grace-period optimizations must be accounted for.
*/ staticvoid do_rtws_sync(struct torture_random_state *trsp, void (*sync)(void))
{ unsignedlong cookie; struct rcu_gp_oldstate cookie_full; bool dopoll; bool dopoll_full; unsignedlong r = torture_random(trsp);
/* * RCU torture writer kthread. Repeatedly substitutes a new structure * for that pointed to by rcu_torture_current, freeing the old structure * after a series of grace periods (the "pipeline").
*/ staticint
rcu_torture_writer(void *arg)
{ bool boot_ended; bool can_expedite = !rcu_gp_is_expedited() && !rcu_gp_is_normal(); unsignedlong cookie; struct rcu_gp_oldstate cookie_full; int expediting = 0; unsignedlong gp_snap; unsignedlong gp_snap1; struct rcu_gp_oldstate gp_snap_full; struct rcu_gp_oldstate gp_snap1_full; int i; int idx; int oldnice = task_nice(current); struct rcu_gp_oldstate *rgo = NULL; int rgo_size = 0; struct rcu_torture *rp; struct rcu_torture *old_rp; static DEFINE_TORTURE_RANDOM(rand); unsignedlong stallsdone = jiffies; bool stutter_waited; unsignedlong *ulo = NULL; int ulo_size = 0;
// If a new stall test is added, this must be adjusted. if (stall_cpu_holdoff + stall_gp_kthread + stall_cpu)
stallsdone += (stall_cpu_holdoff + stall_gp_kthread + stall_cpu + 60) *
HZ * (stall_cpu_repeat + 1);
VERBOSE_TOROUT_STRING("rcu_torture_writer task started"); if (!can_expedite)
pr_alert("%s" TORTURE_FLAG " GP expediting controlled from boot/sysfs for %s.\n",
torture_type, cur_ops->name); if (WARN_ONCE(nsynctypes == 0, "%s: No update-side primitives.\n", __func__)) { /* * No updates primitives, so don't try updating. * The resulting test won't be testing much, hence the * above WARN_ONCE().
*/
rcu_torture_writer_state = RTWS_STOPPING;
torture_kthread_stopping("rcu_torture_writer"); return 0;
} if (cur_ops->poll_active > 0) {
ulo = kzalloc(cur_ops->poll_active * sizeof(ulo[0]), GFP_KERNEL); if (!WARN_ON(!ulo))
ulo_size = cur_ops->poll_active;
} if (cur_ops->poll_active_full > 0) {
rgo = kzalloc(cur_ops->poll_active_full * sizeof(rgo[0]), GFP_KERNEL); if (!WARN_ON(!rgo))
rgo_size = cur_ops->poll_active_full;
}
do {
rcu_torture_writer_state = RTWS_FIXED_DELAY;
torture_hrtimeout_us(500, 1000, &rand);
rp = rcu_torture_alloc(); if (rp == NULL) continue;
rp->rtort_pipe_count = 0;
ASSERT_EXCLUSIVE_WRITER(rp->rtort_pipe_count);
rcu_torture_writer_state = RTWS_DELAY;
udelay(torture_random(&rand) & 0x3ff);
rcu_torture_writer_state = RTWS_REPLACE;
old_rp = rcu_dereference_check(rcu_torture_current,
current == writer_task);
rp->rtort_mbtest = 1;
rcu_assign_pointer(rcu_torture_current, rp);
smp_wmb(); /* Mods to old_rp must follow rcu_assign_pointer() */ if (old_rp) {
i = old_rp->rtort_pipe_count; if (i > RCU_TORTURE_PIPE_LEN)
i = RCU_TORTURE_PIPE_LEN;
atomic_inc(&rcu_torture_wcount[i]);
WRITE_ONCE(old_rp->rtort_pipe_count,
old_rp->rtort_pipe_count + 1);
ASSERT_EXCLUSIVE_WRITER(old_rp->rtort_pipe_count);
// Make sure readers block polled grace periods. if (cur_ops->get_gp_state && cur_ops->poll_gp_state) {
idx = cur_ops->readlock();
cookie = cur_ops->get_gp_state();
WARN_ONCE(cur_ops->poll_gp_state(cookie), "%s: Cookie check 1 failed %s(%d) %lu->%lu\n",
__func__,
rcu_torture_writer_state_getname(),
rcu_torture_writer_state,
cookie, cur_ops->get_gp_state()); if (cur_ops->get_comp_state) {
cookie = cur_ops->get_comp_state();
WARN_ON_ONCE(!cur_ops->poll_gp_state(cookie));
}
cur_ops->readunlock(idx);
} if (cur_ops->get_gp_state_full && cur_ops->poll_gp_state_full) {
idx = cur_ops->readlock();
cur_ops->get_gp_state_full(&cookie_full);
WARN_ONCE(cur_ops->poll_gp_state_full(&cookie_full), "%s: Cookie check 5 failed %s(%d) online %*pbl\n",
__func__,
rcu_torture_writer_state_getname(),
rcu_torture_writer_state,
cpumask_pr_args(cpu_online_mask)); if (cur_ops->get_comp_state_full) {
cur_ops->get_comp_state_full(&cookie_full);
WARN_ON_ONCE(!cur_ops->poll_gp_state_full(&cookie_full));
}
cur_ops->readunlock(idx);
} switch (synctype[torture_random(&rand) % nsynctypes]) { case RTWS_DEF_FREE:
rcu_torture_writer_state = RTWS_DEF_FREE;
cur_ops->deferred_free(old_rp); break; case RTWS_EXP_SYNC:
rcu_torture_writer_state = RTWS_EXP_SYNC;
do_rtws_sync(&rand, cur_ops->exp_sync);
rcu_torture_pipe_update(old_rp); break; case RTWS_COND_GET:
rcu_torture_writer_state = RTWS_COND_GET;
gp_snap = cur_ops->get_gp_state();
torture_hrtimeout_us(torture_random(&rand) % gp_cond_wi,
1000, &rand);
rcu_torture_writer_state = RTWS_COND_SYNC;
cur_ops->cond_sync(gp_snap);
rcu_torture_pipe_update(old_rp); break; case RTWS_COND_GET_EXP:
rcu_torture_writer_state = RTWS_COND_GET_EXP;
gp_snap = cur_ops->get_gp_state_exp();
torture_hrtimeout_us(torture_random(&rand) % gp_cond_wi_exp,
1000, &rand);
rcu_torture_writer_state = RTWS_COND_SYNC_EXP;
cur_ops->cond_sync_exp(gp_snap);
rcu_torture_pipe_update(old_rp); break; case RTWS_COND_GET_FULL:
rcu_torture_writer_state = RTWS_COND_GET_FULL;
cur_ops->get_gp_state_full(&gp_snap_full);
torture_hrtimeout_us(torture_random(&rand) % gp_cond_wi,
1000, &rand);
rcu_torture_writer_state = RTWS_COND_SYNC_FULL;
cur_ops->cond_sync_full(&gp_snap_full);
rcu_torture_pipe_update(old_rp); break; case RTWS_COND_GET_EXP_FULL:
rcu_torture_writer_state = RTWS_COND_GET_EXP_FULL;
cur_ops->get_gp_state_full(&gp_snap_full);
torture_hrtimeout_us(torture_random(&rand) % gp_cond_wi_exp,
1000, &rand);
rcu_torture_writer_state = RTWS_COND_SYNC_EXP_FULL;
cur_ops->cond_sync_exp_full(&gp_snap_full);
rcu_torture_pipe_update(old_rp); break; case RTWS_POLL_GET:
rcu_torture_writer_state = RTWS_POLL_GET; for (i = 0; i < ulo_size; i++)
ulo[i] = cur_ops->get_comp_state();
gp_snap = cur_ops->start_gp_poll();
rcu_torture_writer_state = RTWS_POLL_WAIT; while (!cur_ops->poll_gp_state(gp_snap)) {
gp_snap1 = cur_ops->get_gp_state(); for (i = 0; i < ulo_size; i++) if (cur_ops->poll_gp_state(ulo[i]) ||
cur_ops->same_gp_state(ulo[i], gp_snap1)) {
ulo[i] = gp_snap1; break;
}
WARN_ON_ONCE(ulo_size > 0 && i >= ulo_size);
torture_hrtimeout_us(torture_random(&rand) % gp_poll_wi,
1000, &rand);
}
rcu_torture_pipe_update(old_rp); break; case RTWS_POLL_GET_FULL:
rcu_torture_writer_state = RTWS_POLL_GET_FULL; for (i = 0; i < rgo_size; i++)
cur_ops->get_comp_state_full(&rgo[i]);
cur_ops->start_gp_poll_full(&gp_snap_full);
rcu_torture_writer_state = RTWS_POLL_WAIT_FULL; while (!cur_ops->poll_gp_state_full(&gp_snap_full)) {
cur_ops->get_gp_state_full(&gp_snap1_full); for (i = 0; i < rgo_size; i++) if (cur_ops->poll_gp_state_full(&rgo[i]) ||
cur_ops->same_gp_state_full(&rgo[i],
&gp_snap1_full)) {
rgo[i] = gp_snap1_full; break;
}
WARN_ON_ONCE(rgo_size > 0 && i >= rgo_size);
torture_hrtimeout_us(torture_random(&rand) % gp_poll_wi,
1000, &rand);
}
rcu_torture_pipe_update(old_rp); break; case RTWS_POLL_GET_EXP:
rcu_torture_writer_state = RTWS_POLL_GET_EXP;
gp_snap = cur_ops->start_gp_poll_exp();
rcu_torture_writer_state = RTWS_POLL_WAIT_EXP; while (!cur_ops->poll_gp_state_exp(gp_snap))
torture_hrtimeout_us(torture_random(&rand) % gp_poll_wi_exp,
1000, &rand);
rcu_torture_pipe_update(old_rp); break; case RTWS_POLL_GET_EXP_FULL:
rcu_torture_writer_state = RTWS_POLL_GET_EXP_FULL;
cur_ops->start_gp_poll_exp_full(&gp_snap_full);
rcu_torture_writer_state = RTWS_POLL_WAIT_EXP_FULL; while (!cur_ops->poll_gp_state_full(&gp_snap_full))
torture_hrtimeout_us(torture_random(&rand) % gp_poll_wi_exp,
1000, &rand);
rcu_torture_pipe_update(old_rp); break; case RTWS_SYNC:
rcu_torture_writer_state = RTWS_SYNC;
do_rtws_sync(&rand, cur_ops->sync);
rcu_torture_pipe_update(old_rp); break; default:
WARN_ON_ONCE(1); break;
}
}
WRITE_ONCE(rcu_torture_current_version,
rcu_torture_current_version + 1); /* Cycle through nesting levels of rcu_expedite_gp() calls. */ if (can_expedite &&
!(torture_random(&rand) & 0xff & (!!expediting - 1))) {
WARN_ON_ONCE(expediting == 0 && rcu_gp_is_expedited()); if (expediting >= 0)
rcu_expedite_gp(); else
rcu_unexpedite_gp(); if (++expediting > 3)
expediting = -expediting;
} elseif (!can_expedite) { /* Disabled during boot, recheck. */
can_expedite = !rcu_gp_is_expedited() &&
!rcu_gp_is_normal();
}
rcu_torture_writer_state = RTWS_STUTTER;
boot_ended = rcu_inkernel_boot_has_ended();
stutter_waited = stutter_wait("rcu_torture_writer"); if (stutter_waited &&
!atomic_read(&rcu_fwd_cb_nodelay) &&
!cur_ops->slow_gps &&
!torture_must_stop() &&
boot_ended &&
time_after(jiffies, stallsdone)) for (i = 0; i < ARRAY_SIZE(rcu_tortures); i++) if (list_empty(&rcu_tortures[i].rtort_free) &&
rcu_access_pointer(rcu_torture_current) != &rcu_tortures[i]) {
tracing_off(); if (cur_ops->gp_kthread_dbg)
cur_ops->gp_kthread_dbg();
WARN(1, "%s: rtort_pipe_count: %d\n", __func__, rcu_tortures[i].rtort_pipe_count);
rcu_ftrace_dump(DUMP_ALL); break;
} if (stutter_waited)
sched_set_normal(current, oldnice);
} while (!torture_must_stop());
rcu_torture_current = NULL; // Let stats task know that we are done. /* Reset expediting back to unexpedited. */ if (expediting > 0)
expediting = -expediting; while (can_expedite && expediting++ < 0)
rcu_unexpedite_gp();
WARN_ON_ONCE(can_expedite && rcu_gp_is_expedited()); if (!can_expedite)
pr_alert("%s" TORTURE_FLAG " Dynamic grace-period expediting was disabled.\n",
torture_type);
kfree(ulo);
kfree(rgo);
rcu_torture_writer_state = RTWS_STOPPING;
torture_kthread_stopping("rcu_torture_writer"); return 0;
}
/* * RCU torture fake writer kthread. Repeatedly calls sync, with a random * delay between calls.
*/ staticint
rcu_torture_fakewriter(void *arg)
{ unsignedlong gp_snap; struct rcu_gp_oldstate gp_snap_full;
DEFINE_TORTURE_RANDOM(rand);
if (WARN_ONCE(nsynctypes == 0, "%s: No update-side primitives.\n", __func__)) { /* * No updates primitives, so don't try updating. * The resulting test won't be testing much, hence the * above WARN_ONCE().
*/
torture_kthread_stopping("rcu_torture_fakewriter"); return 0;
}
do {
torture_hrtimeout_jiffies(torture_random(&rand) % 10, &rand); if (cur_ops->cb_barrier != NULL &&
torture_random(&rand) % (nrealfakewriters * 8) == 0) {
cur_ops->cb_barrier();
} else { switch (synctype[torture_random(&rand) % nsynctypes]) { case RTWS_DEF_FREE: break; case RTWS_EXP_SYNC:
cur_ops->exp_sync(); break; case RTWS_COND_GET:
gp_snap = cur_ops->get_gp_state();
torture_hrtimeout_jiffies(torture_random(&rand) % 16, &rand);
cur_ops->cond_sync(gp_snap); break; case RTWS_COND_GET_EXP:
gp_snap = cur_ops->get_gp_state_exp();
torture_hrtimeout_jiffies(torture_random(&rand) % 16, &rand);
cur_ops->cond_sync_exp(gp_snap); break; case RTWS_COND_GET_FULL:
cur_ops->get_gp_state_full(&gp_snap_full);
torture_hrtimeout_jiffies(torture_random(&rand) % 16, &rand);
cur_ops->cond_sync_full(&gp_snap_full); break; case RTWS_COND_GET_EXP_FULL:
cur_ops->get_gp_state_full(&gp_snap_full);
torture_hrtimeout_jiffies(torture_random(&rand) % 16, &rand);
cur_ops->cond_sync_exp_full(&gp_snap_full); break; case RTWS_POLL_GET: if (cur_ops->start_poll_irqsoff)
local_irq_disable();
gp_snap = cur_ops->start_gp_poll(); if (cur_ops->start_poll_irqsoff)
local_irq_enable(); while (!cur_ops->poll_gp_state(gp_snap)) {
torture_hrtimeout_jiffies(torture_random(&rand) % 16,
&rand);
} break; case RTWS_POLL_GET_FULL: if (cur_ops->start_poll_irqsoff)
local_irq_disable();
cur_ops->start_gp_poll_full(&gp_snap_full); if (cur_ops->start_poll_irqsoff)
local_irq_enable(); while (!cur_ops->poll_gp_state_full(&gp_snap_full)) {
torture_hrtimeout_jiffies(torture_random(&rand) % 16,
&rand);
} break; case RTWS_POLL_GET_EXP:
gp_snap = cur_ops->start_gp_poll_exp(); while (!cur_ops->poll_gp_state_exp(gp_snap)) {
torture_hrtimeout_jiffies(torture_random(&rand) % 16,
&rand);
} break; case RTWS_POLL_GET_EXP_FULL:
cur_ops->start_gp_poll_exp_full(&gp_snap_full); while (!cur_ops->poll_gp_state_full(&gp_snap_full)) {
torture_hrtimeout_jiffies(torture_random(&rand) % 16,
&rand);
} break; case RTWS_SYNC:
cur_ops->sync(); break; default:
WARN_ON_ONCE(1); break;
}
}
stutter_wait("rcu_torture_fakewriter");
} while (!torture_must_stop());
// Set up and carry out testing of RCU's global memory ordering staticvoid rcu_torture_reader_do_mbchk(long myid, struct rcu_torture *rtp, struct torture_random_state *trsp)
{ unsignedlong loops; int noc = torture_num_online_cpus(); int rdrchked; int rdrchker; struct rcu_torture_reader_check *rtrcp; // Me. struct rcu_torture_reader_check *rtrcp_assigner; // Assigned us to do checking. struct rcu_torture_reader_check *rtrcp_chked; // Reader being checked. struct rcu_torture_reader_check *rtrcp_chker; // Reader doing checking when not me.
if (myid < 0) return; // Don't try this from timer handlers.
// Attempt to assign someone else some checking work.
rdrchked = torture_random(trsp) % nrealreaders;
rtrcp_chked = &rcu_torture_reader_mbchk[rdrchked];
rdrchker = torture_random(trsp) % nrealreaders;
rtrcp_chker = &rcu_torture_reader_mbchk[rdrchker]; if (rdrchked != myid && rdrchked != rdrchker && noc >= rdrchked && noc >= rdrchker &&
smp_load_acquire(&rtrcp->rtc_chkrdr) < 0 && // Pairs with smp_store_release below.
!READ_ONCE(rtp->rtort_chkp) &&
!smp_load_acquire(&rtrcp_chker->rtc_assigner)) { // Pairs with smp_store_release below.
rtrcp->rtc_chkloops = READ_ONCE(rtrcp_chked->rtc_myloops);
WARN_ON_ONCE(rtrcp->rtc_chkrdr >= 0);
rtrcp->rtc_chkrdr = rdrchked;
WARN_ON_ONCE(rtrcp->rtc_ready); // This gets set after the grace period ends. if (cmpxchg_relaxed(&rtrcp_chker->rtc_assigner, NULL, rtrcp) ||
cmpxchg_relaxed(&rtp->rtort_chkp, NULL, rtrcp))
(void)cmpxchg_relaxed(&rtrcp_chker->rtc_assigner, rtrcp, NULL); // Back out.
}
// If assigned some completed work, do it!
rtrcp_assigner = READ_ONCE(rtrcp->rtc_assigner); if (!rtrcp_assigner || !smp_load_acquire(&rtrcp_assigner->rtc_ready)) return; // No work or work not yet ready.
rdrchked = rtrcp_assigner->rtc_chkrdr; if (WARN_ON_ONCE(rdrchked < 0)) return;
rtrcp_chked = &rcu_torture_reader_mbchk[rdrchked];
loops = READ_ONCE(rtrcp_chked->rtc_myloops);
atomic_inc(&n_rcu_torture_mbchk_tries); if (ULONG_CMP_LT(loops, rtrcp_assigner->rtc_chkloops))
atomic_inc(&n_rcu_torture_mbchk_fail);
rtrcp_assigner->rtc_chkloops = loops + ULONG_MAX / 2;
rtrcp_assigner->rtc_ready = 0;
smp_store_release(&rtrcp->rtc_assigner, NULL); // Someone else can assign us work.
smp_store_release(&rtrcp_assigner->rtc_chkrdr, -1); // Assigner can again assign.
}
// Verify the specified RCUTORTURE_RDR* state. #define ROEC_ARGS "%s %s: Current %#x To add %#x To remove %#x preempt_count() %#x\n", __func__, s, curstate, new, old, preempt_count() staticvoid rcutorture_one_extend_check(char *s, int curstate, intnew, int old)
{ int mask;
if (!IS_ENABLED(CONFIG_RCU_TORTURE_TEST_CHK_RDR_STATE) || in_nmi()) return;
/* * Do one extension of an RCU read-side critical section using the * current reader state in readstate (set to zero for initial entry * to extended critical section), set the new state as specified by * newstate (set to zero for final exit from extended critical section), * and random-number-generator state in trsp. If this is neither the * beginning or end of the critical section and if there was actually a * change, do a ->read_delay().
*/ staticvoid rcutorture_one_extend(int *readstate, int newstate, struct torture_random_state *trsp, struct rt_read_seg *rtrsp)
{ bool first; unsignedlong flags; int idxnew1 = -1; int idxnew2 = -1; int idxold1 = *readstate; int idxold2 = idxold1; int statesnew = ~*readstate & newstate; int statesold = *readstate & ~newstate;
/* First, put new protection in place to avoid critical-section gap. */ if (statesnew & RCUTORTURE_RDR_BH)
local_bh_disable(); if (statesnew & RCUTORTURE_RDR_RBH)
rcu_read_lock_bh(); if (statesnew & RCUTORTURE_RDR_IRQ)
local_irq_disable(); if (statesnew & RCUTORTURE_RDR_PREEMPT)
preempt_disable(); if (statesnew & RCUTORTURE_RDR_SCHED)
rcu_read_lock_sched(); if (statesnew & RCUTORTURE_RDR_RCU_1)
idxnew1 = (cur_ops->readlock() << RCUTORTURE_RDR_SHIFT_1) & RCUTORTURE_RDR_MASK_1; if (statesnew & RCUTORTURE_RDR_RCU_2)
idxnew2 = (cur_ops->readlock() << RCUTORTURE_RDR_SHIFT_2) & RCUTORTURE_RDR_MASK_2;
// Complain unless both the old and the new protection is in place.
rcutorture_one_extend_check("during change", idxold1 | statesnew, statesnew, statesold);
// Sample CPU under both sets of protections to reduce confusion. if (IS_ENABLED(CONFIG_RCU_TORTURE_TEST_LOG_CPU)) { int cpu = raw_smp_processor_id();
rtrsp->rt_cpu = cpu; if (!first) {
rtrsp[-1].rt_end_cpu = cpu; if (cur_ops->reader_blocked)
rtrsp[-1].rt_preempted = cur_ops->reader_blocked();
}
} // Sample grace-period sequence number, as good a place as any. if (IS_ENABLED(CONFIG_RCU_TORTURE_TEST_LOG_GP) && cur_ops->gather_gp_seqs) {
rtrsp->rt_gp_seq = cur_ops->gather_gp_seqs();
rtrsp->rt_ts = ktime_get_mono_fast_ns(); if (!first)
rtrsp[-1].rt_gp_seq_end = rtrsp->rt_gp_seq;
}
/* * Next, remove old protection, in decreasing order of strength * to avoid unlock paths that aren't safe in the stronger * context. Namely: BH can not be enabled with disabled interrupts. * Additionally PREEMPT_RT requires that BH is enabled in preemptible * context.
*/ if (statesold & RCUTORTURE_RDR_IRQ)
local_irq_enable(); if (statesold & RCUTORTURE_RDR_PREEMPT)
preempt_enable(); if (statesold & RCUTORTURE_RDR_SCHED)
rcu_read_unlock_sched(); if (statesold & RCUTORTURE_RDR_BH)
local_bh_enable(); if (statesold & RCUTORTURE_RDR_RBH)
rcu_read_unlock_bh(); if (statesold & RCUTORTURE_RDR_RCU_2) {
cur_ops->readunlock((idxold2 & RCUTORTURE_RDR_MASK_2) >> RCUTORTURE_RDR_SHIFT_2);
WARN_ON_ONCE(idxnew2 != -1);
idxold2 = 0;
} if (statesold & RCUTORTURE_RDR_RCU_1) { bool lockit;
/* Delay if neither beginning nor end and there was a change. */ if ((statesnew || statesold) && *readstate && newstate)
cur_ops->read_delay(trsp, rtrsp);
/* Update the reader state. */ if (idxnew1 == -1)
idxnew1 = idxold1 & RCUTORTURE_RDR_MASK_1;
WARN_ON_ONCE(idxnew1 < 0); if (idxnew2 == -1)
idxnew2 = idxold2 & RCUTORTURE_RDR_MASK_2;
WARN_ON_ONCE(idxnew2 < 0);
*readstate = idxnew1 | idxnew2 | newstate;
WARN_ON_ONCE(*readstate < 0); if (WARN_ON_ONCE(*readstate & ~RCUTORTURE_RDR_ALLBITS))
pr_info("Unexpected readstate value of %#x\n", *readstate);
rcutorture_one_extend_check("after change", *readstate, statesnew, statesold);
}
/* Return the biggest extendables mask given current RCU and boot parameters. */ staticint rcutorture_extend_mask_max(void)
{ int mask;
/* * Ideally these sequences would be detected in debug builds * (regardless of RT), but until then don't stop testing * them on non-RT.
*/ if (IS_ENABLED(CONFIG_PREEMPT_RT)) { /* Can't modify BH in atomic context */ if (oldmask & preempts_irq)
mask &= ~bhs; if ((oldmask | mask) & preempts_irq)
mask |= oldmask & bhs;
}
return mask ?: RCUTORTURE_RDR_RCU_1;
}
/* * Do a randomly selected number of extensions of an existing RCU read-side * critical section.
*/ staticstruct rt_read_seg *
rcutorture_loop_extend(int *readstate, struct torture_random_state *trsp, struct rt_read_seg *rtrsp)
{ int i; int j; int mask = rcutorture_extend_mask_max();
WARN_ON_ONCE(!*readstate); /* -Existing- RCU read-side critsect! */ if (!((mask - 1) & mask)) return rtrsp; /* Current RCU reader not extendable. */ /* Bias towards larger numbers of loops. */
i = torture_random(trsp);
i = ((i | (i >> 3)) & RCUTORTURE_RDR_MAX_LOOPS) + 1; for (j = 0; j < i; j++) {
mask = rcutorture_extend_mask(*readstate, trsp);
WARN_ON_ONCE(mask & RCUTORTURE_RDR_UPDOWN);
rcutorture_one_extend(readstate, mask, trsp, &rtrsp[j]);
} return &rtrsp[j];
}
/* * Set up the first segment of a series of overlapping read-side * critical sections. The caller must have actually initiated the * outermost read-side critical section.
*/ staticbool rcu_torture_one_read_start(struct rcu_torture_one_read_state *rtorsp, struct torture_random_state *trsp, long myid)
{ if (rtorsp->checkpolling) { if (cur_ops->get_gp_state && cur_ops->poll_gp_state)
rtorsp->cookie = cur_ops->get_gp_state(); if (cur_ops->get_gp_state_full && cur_ops->poll_gp_state_full)
cur_ops->get_gp_state_full(&rtorsp->cookie_full);
}
rtorsp->started = cur_ops->get_gp_seq();
rtorsp->ts = rcu_trace_clock_local();
rtorsp->p = rcu_dereference_check(rcu_torture_current,
!cur_ops->readlock_held || cur_ops->readlock_held() ||
(rtorsp->readstate & RCUTORTURE_RDR_UPDOWN)); if (rtorsp->p == NULL) { /* Wait for rcu_torture_writer to get underway */
rcutorture_one_extend(&rtorsp->readstate, 0, trsp, rtorsp->rtrsp); returnfalse;
} if (rtorsp->p->rtort_mbtest == 0)
atomic_inc(&n_rcu_torture_mberror);
rcu_torture_reader_do_mbchk(myid, rtorsp->p, trsp); returntrue;
}
/* * Complete the last segment of a series of overlapping read-side * critical sections and check for errors.
*/ staticvoid rcu_torture_one_read_end(struct rcu_torture_one_read_state *rtorsp, struct torture_random_state *trsp)
{ int i; unsignedlong completed; int pipe_count; bool preempted = false; struct rt_read_seg *rtrsp1;
preempt_disable();
pipe_count = READ_ONCE(rtorsp->p->rtort_pipe_count); if (pipe_count > RCU_TORTURE_PIPE_LEN) { // Should not happen in a correct RCU implementation, // happens quite often for torture_type=busted.
pipe_count = RCU_TORTURE_PIPE_LEN;
}
completed = cur_ops->get_gp_seq(); if (pipe_count > 1) {
do_trace_rcu_torture_read(cur_ops->name, &rtorsp->p->rtort_rcu,
rtorsp->ts, rtorsp->started, completed);
rcu_ftrace_dump(DUMP_ALL);
}
__this_cpu_inc(rcu_torture_count[pipe_count]);
completed = rcutorture_seq_diff(completed, rtorsp->started); if (completed > RCU_TORTURE_PIPE_LEN) { /* Should not happen, but... */
completed = RCU_TORTURE_PIPE_LEN;
}
__this_cpu_inc(rcu_torture_batch[completed]);
preempt_enable(); if (rtorsp->checkpolling) { if (cur_ops->get_gp_state && cur_ops->poll_gp_state)
WARN_ONCE(cur_ops->poll_gp_state(rtorsp->cookie), "%s: Cookie check 2 failed %s(%d) %lu->%lu\n",
__func__,
rcu_torture_writer_state_getname(),
rcu_torture_writer_state,
rtorsp->cookie, cur_ops->get_gp_state()); if (cur_ops->get_gp_state_full && cur_ops->poll_gp_state_full)
WARN_ONCE(cur_ops->poll_gp_state_full(&rtorsp->cookie_full), "%s: Cookie check 6 failed %s(%d) online %*pbl\n",
__func__,
rcu_torture_writer_state_getname(),
rcu_torture_writer_state,
cpumask_pr_args(cpu_online_mask));
} if (cur_ops->reader_blocked)
preempted = cur_ops->reader_blocked();
rcutorture_one_extend(&rtorsp->readstate, 0, trsp, rtorsp->rtrsp);
WARN_ON_ONCE(rtorsp->readstate); // This next splat is expected behavior if leakpointer, especially // for CONFIG_RCU_STRICT_GRACE_PERIOD=y kernels.
WARN_ON_ONCE(leakpointer && READ_ONCE(rtorsp->p->rtort_pipe_count) > 1);
/* If error or close call, record the sequence of reader protections. */ if ((pipe_count > 1 || completed > 1) && !xchg(&err_segs_recorded, 1)) {
i = 0; for (rtrsp1 = &rtorsp->rtseg[0]; rtrsp1 < rtorsp->rtrsp; rtrsp1++)
err_segs[i++] = *rtrsp1;
rt_read_nsegs = i;
rt_read_preempted = preempted;
}
}
/* * Do one read-side critical section, returning false if there was * no data to read. Can be invoked both from process context and * from a timer handler.
*/ staticbool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
{ int newstate; struct rcu_torture_one_read_state rtors;
/* * RCU torture reader from timer handler. Dereferences rcu_torture_current, * incrementing the corresponding element of the pipeline array. The * counter in the element should never be greater than 1, otherwise, the * RCU implementation is broken.
*/ staticvoid rcu_torture_timer(struct timer_list *unused)
{
atomic_long_inc(&n_rcu_torture_timers);
(void)rcu_torture_one_read(this_cpu_ptr(&rcu_torture_timer_rand), -1);
/* Test call_rcu() invocation from interrupt handler. */ if (cur_ops->call) { struct rcu_head *rhp = kmalloc(sizeof(*rhp), GFP_NOWAIT);
if (rhp)
cur_ops->call(rhp, rcu_torture_timer_cb);
}
}
/* * RCU torture reader kthread. Repeatedly dereferences rcu_torture_current, * incrementing the corresponding element of the pipeline array. The * counter in the element should never be greater than 1, otherwise, the * RCU implementation is broken.
*/ staticint
rcu_torture_reader(void *arg)
{ unsignedlong lastsleep = jiffies; long myid = (long)arg; int mynumonline = myid;
DEFINE_TORTURE_RANDOM(rand); struct timer_list t;
VERBOSE_TOROUT_STRING("rcu_torture_reader task started");
set_user_nice(current, MAX_NICE); if (irqreader && cur_ops->irq_capable)
timer_setup_on_stack(&t, rcu_torture_timer, 0);
tick_dep_set_task(current, TICK_DEP_BIT_RCU); // CPU bound, so need tick. do { if (irqreader && cur_ops->irq_capable) { if (!timer_pending(&t))
mod_timer(&t, jiffies + 1);
} if (!rcu_torture_one_read(&rand, myid) && !torture_must_stop())
schedule_timeout_interruptible(HZ); if (time_after(jiffies, lastsleep) && !torture_must_stop()) {
torture_hrtimeout_us(500, 1000, &rand);
lastsleep = jiffies + 10;
} while (torture_num_online_cpus() < mynumonline && !torture_must_stop())
schedule_timeout_interruptible(HZ / 5);
stutter_wait("rcu_torture_reader");
} while (!torture_must_stop()); if (irqreader && cur_ops->irq_capable) {
timer_delete_sync(&t);
timer_destroy_on_stack(&t);
}
tick_dep_clear_task(current, TICK_DEP_BIT_RCU);
torture_kthread_stopping("rcu_torture_reader"); return 0;
}
for (rtorsup = updownreaders; rtorsup < &updownreaders[n_up_down]; rtorsup++) { if (!smp_load_acquire(&rtorsup->rtorsu_inuse)) continue; if (hrtimer_cancel(&rtorsup->rtorsu_hrt) || WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
rcu_torture_one_read_end(&rtorsup->rtorsu_rtors, &rtorsup->rtorsu_trs);
WARN_ONCE(rtorsup->rtorsu_nups >= rtorsup->rtorsu_ndowns, "%s: Up without matching down #%zu.\n", __func__, rtorsup - updownreaders);
WRITE_ONCE(rtorsup->rtorsu_nups, rtorsup->rtorsu_nups + 1);
smp_store_release(&rtorsup->rtorsu_inuse, false);
}
}
kfree(updownreaders);
updownreaders = NULL;
}
// Do one reader for rcu_torture_updown(). staticvoid rcu_torture_updown_one(struct rcu_torture_one_read_state_updown *rtorsup)
{ int idx; int rawidx;
ktime_t t;
init_rcu_torture_one_read_state(&rtorsup->rtorsu_rtors, &rtorsup->rtorsu_trs);
rawidx = cur_ops->down_read();
WRITE_ONCE(rtorsup->rtorsu_ndowns, rtorsup->rtorsu_ndowns + 1);
idx = (rawidx << RCUTORTURE_RDR_SHIFT_1) & RCUTORTURE_RDR_MASK_1;
rtorsup->rtorsu_rtors.readstate = idx | RCUTORTURE_RDR_UPDOWN;
rtorsup->rtorsu_rtors.rtrsp++;
rtorsup->rtorsu_cpu = raw_smp_processor_id(); if (!rcu_torture_one_read_start(&rtorsup->rtorsu_rtors, &rtorsup->rtorsu_trs, -1)) {
WARN_ONCE(rtorsup->rtorsu_nups >= rtorsup->rtorsu_ndowns, "%s: Up without matching down #%zu.\n", __func__, rtorsup - updownreaders);
WRITE_ONCE(rtorsup->rtorsu_nups, rtorsup->rtorsu_nups + 1);
schedule_timeout_idle(HZ); return;
}
smp_store_release(&rtorsup->rtorsu_inuse, true);
t = torture_random(&rtorsup->rtorsu_trs) & 0xfffff; // One per million. if (t < 10 * 1000)
t = 200 * 1000 * 1000;
hrtimer_start(&rtorsup->rtorsu_hrt, t, HRTIMER_MODE_REL | HRTIMER_MODE_HARD);
smp_mb(); // Sample jiffies after posting hrtimer.
rtorsup->rtorsu_j = jiffies; // Not used by hrtimer handler.
rtorsup->rtorsu_kt = t;
}
/* * RCU torture up/down reader kthread, starting RCU readers in kthread * context and ending them in hrtimer handlers. Otherwise similar to * rcu_torture_reader().
*/ staticint
rcu_torture_updown(void *arg)
{ unsignedlong j; struct rcu_torture_one_read_state_updown *rtorsup;
VERBOSE_TOROUT_STRING("rcu_torture_updown task started"); do { for (rtorsup = updownreaders; rtorsup < &updownreaders[n_up_down]; rtorsup++) { if (torture_must_stop()) break;
j = smp_load_acquire(&jiffies); // Time before ->rtorsu_inuse. if (smp_load_acquire(&rtorsup->rtorsu_inuse)) {
WARN_ONCE(time_after(j, rtorsup->rtorsu_j + 1 + HZ * 10), "hrtimer queued at jiffies %lu for %lld ns took %lu jiffies\n", rtorsup->rtorsu_j, rtorsup->rtorsu_kt, j - rtorsup->rtorsu_j); continue;
}
rcu_torture_updown_one(rtorsup);
}
torture_hrtimeout_ms(1, 1000, &rcu_torture_updown_rand);
stutter_wait("rcu_torture_updown");
} while (!torture_must_stop());
rcu_torture_updown_cleanup();
torture_kthread_stopping("rcu_torture_updown"); return 0;
}
/* * Randomly Toggle CPUs' callback-offload state. This uses hrtimers to * increase race probabilities and fuzzes the interval between toggling.
*/ staticint rcu_nocb_toggle(void *arg)
{ int cpu; int maxcpu = -1; int oldnice = task_nice(current); long r;
DEFINE_TORTURE_RANDOM(rand);
ktime_t toggle_delay; unsignedlong toggle_fuzz;
ktime_t toggle_interval = ms_to_ktime(nocbs_toggle);
VERBOSE_TOROUT_STRING("rcu_nocb_toggle task started"); while (!rcu_inkernel_boot_has_ended())
schedule_timeout_interruptible(HZ / 10);
for_each_possible_cpu(cpu)
maxcpu = cpu;
WARN_ON(maxcpu < 0); if (toggle_interval > ULONG_MAX)
toggle_fuzz = ULONG_MAX >> 3; else
toggle_fuzz = toggle_interval >> 3; if (toggle_fuzz <= 0)
toggle_fuzz = NSEC_PER_USEC; do {
r = torture_random(&rand);
cpu = (r >> 1) % (maxcpu + 1); if (r & 0x1) {
rcu_nocb_cpu_offload(cpu);
atomic_long_inc(&n_nocb_offload);
} else {
rcu_nocb_cpu_deoffload(cpu);
atomic_long_inc(&n_nocb_deoffload);
}
toggle_delay = torture_random(&rand) % toggle_fuzz + toggle_interval;
set_current_state(TASK_INTERRUPTIBLE);
schedule_hrtimeout(&toggle_delay, HRTIMER_MODE_REL); if (stutter_wait("rcu_nocb_toggle"))
sched_set_normal(current, oldnice);
} while (!torture_must_stop());
torture_kthread_stopping("rcu_nocb_toggle"); return 0;
}
/* * Print torture statistics. Caller must ensure that there is only * one call to this function at a given time!!! This is normally * accomplished by relying on the module system to only have one copy * of the module loaded, and then by giving the rcu_torture_stats * kthread full control (or the init/cleanup functions when rcu_torture_stats * thread is not running).
*/ staticvoid
rcu_torture_stats_print(void)
{ int cpu; int i; long pipesummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; long batchsummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; long n_gpwraps = 0; unsignedlong ndowns = 0; unsignedlong nunexpired = 0; unsignedlong nmigrates = 0; unsignedlong nups = 0; struct rcu_torture *rtcp; staticunsignedlong rtcv_snap = ULONG_MAX; staticbool splatted; struct task_struct *wtp;
for_each_possible_cpu(cpu) { for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) {
pipesummary[i] += READ_ONCE(per_cpu(rcu_torture_count, cpu)[i]);
batchsummary[i] += READ_ONCE(per_cpu(rcu_torture_batch, cpu)[i]);
} if (cur_ops->get_gpwrap_count)
n_gpwraps += cur_ops->get_gpwrap_count(cpu);
} if (updownreaders) { for (i = 0; i < n_up_down; i++) {
ndowns += READ_ONCE(updownreaders[i].rtorsu_ndowns);
nups += READ_ONCE(updownreaders[i].rtorsu_nups);
nunexpired += READ_ONCE(updownreaders[i].rtorsu_inuse);
nmigrates += READ_ONCE(updownreaders[i].rtorsu_nmigrates);
}
} for (i = RCU_TORTURE_PIPE_LEN; i >= 0; i--) { if (pipesummary[i] != 0) break;
} // The value of variable "i" is used later, so don't clobber it!
if (boost_tasks[cpu] == NULL) return 0;
mutex_lock(&boost_mutex);
t = boost_tasks[cpu];
boost_tasks[cpu] = NULL;
rcu_torture_enable_rt_throttle();
mutex_unlock(&boost_mutex);
/* This must be outside of the mutex, otherwise deadlock! */
torture_stop_kthread(rcu_torture_boost, t); return 0;
}
staticint rcutorture_booster_init(unsignedint cpu)
{ int retval;
if (boost_tasks[cpu] != NULL) return 0; /* Already created, nothing more to do. */
// Testing RCU priority boosting requires rcutorture do // some serious abuse. Counter this by running ksoftirqd // at higher priority. if (IS_BUILTIN(CONFIG_RCU_TORTURE_TEST)) { struct sched_param sp; struct task_struct *t;
t = per_cpu(ksoftirqd, cpu);
WARN_ON_ONCE(!t);
sp.sched_priority = 2;
sched_setscheduler_nocheck(t, SCHED_FIFO, &sp); #ifdef CONFIG_IRQ_FORCED_THREADING if (force_irqthreads()) {
t = per_cpu(ktimerd, cpu);
WARN_ON_ONCE(!t);
sp.sched_priority = 2;
sched_setscheduler_nocheck(t, SCHED_FIFO, &sp);
} #endif
}
/* Don't allow time recalculation while creating a new task. */
mutex_lock(&boost_mutex);
rcu_torture_disable_rt_throttle();
VERBOSE_TOROUT_STRING("Creating rcu_torture_boost task");
boost_tasks[cpu] = kthread_run_on_cpu(rcu_torture_boost, NULL,
cpu, "rcu_torture_boost_%u"); if (IS_ERR(boost_tasks[cpu])) {
retval = PTR_ERR(boost_tasks[cpu]);
VERBOSE_TOROUT_STRING("rcu_torture_boost task create failed");
n_rcu_torture_boost_ktrerror++;
boost_tasks[cpu] = NULL;
mutex_unlock(&boost_mutex); return retval;
}
mutex_unlock(&boost_mutex); return 0;
}
/* * CPU-stall kthread. It waits as specified by stall_cpu_holdoff, then * induces a CPU stall for the time specified by stall_cpu. If a new * stall test is added, stallsdone in rcu_torture_writer() must be adjusted.
*/ staticvoid rcu_torture_stall_one(int rep, int irqsoff)
{ int idx; unsignedlong stop_at;
if (stall_cpu_holdoff > 0) {
VERBOSE_TOROUT_STRING("rcu_torture_stall begin holdoff");
schedule_timeout_interruptible(stall_cpu_holdoff * HZ);
VERBOSE_TOROUT_STRING("rcu_torture_stall end holdoff");
} if (!kthread_should_stop() && stall_gp_kthread > 0) {
VERBOSE_TOROUT_STRING("rcu_torture_stall begin GP stall");
rcu_gp_set_torture_wait(stall_gp_kthread * HZ); for (idx = 0; idx < stall_gp_kthread + 2; idx++) { if (kthread_should_stop()) break;
schedule_timeout_uninterruptible(HZ);
}
} if (!kthread_should_stop() && stall_cpu > 0) {
VERBOSE_TOROUT_STRING("rcu_torture_stall begin CPU stall");
stop_at = ktime_get_seconds() + stall_cpu; /* RCU CPU stall is expected behavior in following code. */
idx = cur_ops->readlock(); if (irqsoff)
local_irq_disable(); elseif (!stall_cpu_block)
preempt_disable();
pr_alert("%s start stall episode %d on CPU %d.\n",
__func__, rep + 1, raw_smp_processor_id()); while (ULONG_CMP_LT((unsignedlong)ktime_get_seconds(), stop_at) &&
!kthread_should_stop()) if (stall_cpu_block) { #ifdef CONFIG_PREEMPTION
preempt_schedule(); #else
schedule_timeout_uninterruptible(HZ); #endif
} elseif (stall_no_softlockup) {
touch_softlockup_watchdog();
} if (irqsoff)
local_irq_enable(); elseif (!stall_cpu_block)
preempt_enable();
cur_ops->readunlock(idx);
}
}
/* * CPU-stall kthread. Invokes rcu_torture_stall_one() once, and then as many * additional times as specified by the stall_cpu_repeat module parameter. * Note that stall_cpu_irqsoff is ignored on the second and subsequent * stall.
*/ staticint rcu_torture_stall(void *args)
{ int i; int repeat = stall_cpu_repeat; int ret;
VERBOSE_TOROUT_STRING("rcu_torture_stall task started"); if (repeat < 0) {
repeat = 0;
WARN_ON_ONCE(IS_BUILTIN(CONFIG_RCU_TORTURE_TEST));
} if (rcu_cpu_stall_notifiers) {
ret = rcu_stall_chain_notifier_register(&rcu_torture_stall_block); if (ret)
pr_info("%s: rcu_stall_chain_notifier_register() returned %d, %sexpected.\n",
__func__, ret, !IS_ENABLED(CONFIG_RCU_STALL_COMMON) ? "un" : "");
} for (i = 0; i <= repeat; i++) { if (kthread_should_stop()) break;
rcu_torture_stall_one(i, i == 0 ? stall_cpu_irqsoff : 0);
}
pr_alert("%s end.\n", __func__); if (rcu_cpu_stall_notifiers && !ret) {
ret = rcu_stall_chain_notifier_unregister(&rcu_torture_stall_block); if (ret)
pr_info("%s: rcu_stall_chain_notifier_unregister() returned %d.\n", __func__, ret);
}
torture_shutdown_absorb("rcu_torture_stall"); while (!kthread_should_stop())
schedule_timeout_interruptible(10 * HZ); return 0;
}
// Give the scheduler a chance, even on nohz_full CPUs. staticvoid rcu_torture_fwd_prog_cond_resched(unsignedlong iter)
{ if (IS_ENABLED(CONFIG_PREEMPTION) && IS_ENABLED(CONFIG_NO_HZ_FULL)) { // Real call_rcu() floods hit userspace, so emulate that. if (need_resched() || (iter & 0xfff))
schedule(); return;
} // No userspace emulation: CB invocation throttles call_rcu()
cond_resched();
}
/* * Free all callbacks on the rcu_fwd_cb_head list, either because the * test is over or because we hit an OOM event.
*/ staticunsignedlong rcu_torture_fwd_prog_cbfree(struct rcu_fwd *rfp)
{ unsignedlong flags; unsignedlong freed = 0; struct rcu_fwd_cb *rfcp;
for (;;) {
spin_lock_irqsave(&rfp->rcu_fwd_lock, flags);
rfcp = rfp->rcu_fwd_cb_head; if (!rfcp) {
spin_unlock_irqrestore(&rfp->rcu_fwd_lock, flags); break;
}
rfp->rcu_fwd_cb_head = rfcp->rfc_next; if (!rfp->rcu_fwd_cb_head)
rfp->rcu_fwd_cb_tail = &rfp->rcu_fwd_cb_head;
spin_unlock_irqrestore(&rfp->rcu_fwd_lock, flags);
kfree(rfcp);
freed++;
rcu_torture_fwd_prog_cond_resched(freed); if (tick_nohz_full_enabled()) {
local_irq_save(flags);
rcu_momentary_eqs();
local_irq_restore(flags);
}
} return freed;
}
/* Carry out need_resched()/cond_resched() forward-progress testing. */ staticvoid rcu_torture_fwd_prog_nr(struct rcu_fwd *rfp, int *tested, int *tested_tries)
{ unsignedlong cver; unsignedlong dur; struct fwd_cb_state fcs; unsignedlong gps; int idx; int sd; int sd4; bool selfpropcb = false; unsignedlong stopat; static DEFINE_TORTURE_RANDOM(trs);
pr_alert("%s: Starting forward-progress test %d\n", __func__, rfp->rcu_fwd_id); if (!cur_ops->sync) return; // Cannot do need_resched() forward progress testing without ->sync. if (cur_ops->call && cur_ops->cb_barrier) {
init_rcu_head_on_stack(&fcs.rh);
selfpropcb = true;
}
if (selfpropcb) {
WARN_ON(READ_ONCE(fcs.stop) != 2);
destroy_rcu_head_on_stack(&fcs.rh);
}
schedule_timeout_uninterruptible(HZ / 10); /* Let kthreads recover. */
atomic_dec(&rcu_fwd_cb_nodelay);
}
/* Carry out call_rcu() forward-progress testing. */ staticvoid rcu_torture_fwd_prog_cr(struct rcu_fwd *rfp)
{ unsignedlong cver; unsignedlong flags; unsignedlong gps; int i; long n_launders; long n_launders_cb_snap; long n_launders_sa; long n_max_cbs; long n_max_gps; struct rcu_fwd_cb *rfcp; struct rcu_fwd_cb *rfcpn; unsignedlong stopat; unsignedlong stoppedat;
pr_alert("%s: Starting forward-progress test %d\n", __func__, rfp->rcu_fwd_id); if (READ_ONCE(rcu_fwd_emergency_stop)) return; /* Get out of the way quickly, no GP wait! */ if (!cur_ops->call) return; /* Can't do call_rcu() fwd prog without ->call. */
/* Loop continuously posting RCU callbacks. */
atomic_inc(&rcu_fwd_cb_nodelay);
cur_ops->sync(); /* Later readers see above write. */
WRITE_ONCE(rfp->rcu_fwd_startat, jiffies);
stopat = rfp->rcu_fwd_startat + MAX_FWD_CB_JIFFIES;
n_launders = 0;
rfp->n_launders_cb = 0; // Hoist initialization for multi-kthread
n_launders_sa = 0;
n_max_cbs = 0;
n_max_gps = 0; for (i = 0; i < ARRAY_SIZE(rfp->n_launders_hist); i++)
rfp->n_launders_hist[i].n_launders = 0;
cver = READ_ONCE(rcu_torture_current_version);
gps = cur_ops->get_gp_seq();
rfp->rcu_launder_gp_seq_start = gps;
tick_dep_set_task(current, TICK_DEP_BIT_RCU); // CPU bound, so need tick. while (time_before(jiffies, stopat) &&
!shutdown_time_arrived() &&
!READ_ONCE(rcu_fwd_emergency_stop) && !torture_must_stop()) {
rfcp = READ_ONCE(rfp->rcu_fwd_cb_head);
rfcpn = NULL; if (rfcp)
rfcpn = READ_ONCE(rfcp->rfc_next); if (rfcpn) { if (rfcp->rfc_gps >= MIN_FWD_CB_LAUNDERS &&
++n_max_gps >= MIN_FWD_CBS_LAUNDERED) break;
rfp->rcu_fwd_cb_head = rfcpn;
n_launders++;
n_launders_sa++;
} elseif (!cur_ops->cbflood_max || cur_ops->cbflood_max > n_max_cbs) {
rfcp = kmalloc(sizeof(*rfcp), GFP_KERNEL); if (WARN_ON_ONCE(!rfcp)) {
schedule_timeout_interruptible(1); continue;
}
n_max_cbs++;
n_launders_sa = 0;
rfcp->rfc_gps = 0;
rfcp->rfc_rfp = rfp;
} else {
rfcp = NULL;
} if (rfcp)
cur_ops->call(&rfcp->rh, rcu_torture_fwd_cb_cr);
rcu_torture_fwd_prog_cond_resched(n_launders + n_max_cbs); if (tick_nohz_full_enabled()) {
local_irq_save(flags);
rcu_momentary_eqs();
local_irq_restore(flags);
}
}
stoppedat = jiffies;
n_launders_cb_snap = READ_ONCE(rfp->n_launders_cb);
cver = READ_ONCE(rcu_torture_current_version) - cver;
gps = rcutorture_seq_diff(cur_ops->get_gp_seq(), gps);
pr_alert("%s: Waiting for CBs: %pS() %d\n", __func__, cur_ops->cb_barrier, rfp->rcu_fwd_id);
cur_ops->cb_barrier(); /* Wait for callbacks to be invoked. */
(void)rcu_torture_fwd_prog_cbfree(rfp);
/* * OOM notifier, but this only prints diagnostic information for the * current forward-progress test.
*/ staticint rcutorture_oom_notify(struct notifier_block *self, unsignedlong notused, void *nfreed)
{ int i; long ncbs; struct rcu_fwd *rfp;
mutex_lock(&rcu_fwd_mutex);
rfp = rcu_fwds; if (!rfp) {
mutex_unlock(&rcu_fwd_mutex); return NOTIFY_OK;
}
WARN(1, "%s invoked upon OOM during forward-progress testing.\n",
__func__); for (i = 0; i < fwd_progress; i++) {
rcu_torture_fwd_cb_hist(&rfp[i]);
rcu_fwd_progress_check(1 + (jiffies - READ_ONCE(rfp[i].rcu_fwd_startat)) / 2);
}
WRITE_ONCE(rcu_fwd_emergency_stop, true);
smp_mb(); /* Emergency stop before free and wait to avoid hangs. */
ncbs = 0; for (i = 0; i < fwd_progress; i++)
ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]);
pr_info("%s: Freed %lu RCU callbacks.\n", __func__, ncbs);
cur_ops->cb_barrier();
ncbs = 0; for (i = 0; i < fwd_progress; i++)
ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]);
pr_info("%s: Freed %lu RCU callbacks.\n", __func__, ncbs);
cur_ops->cb_barrier();
ncbs = 0; for (i = 0; i < fwd_progress; i++)
ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]);
pr_info("%s: Freed %lu RCU callbacks.\n", __func__, ncbs);
smp_mb(); /* Frees before return to avoid redoing OOM. */
(*(unsignedlong *)nfreed)++; /* Forward progress CBs freed! */
pr_info("%s returning after OOM processing.\n", __func__);
mutex_unlock(&rcu_fwd_mutex); return NOTIFY_OK;
}
/* Avoid slow periods, better to test when busy. */ if (stutter_wait("rcu_torture_fwd_prog"))
sched_set_normal(current, oldnice);
} while (!torture_must_stop()); /* Short runs might not contain a valid forward-progress attempt. */ if (!rfp->rcu_fwd_id) {
WARN_ON(!tested && tested_tries >= 5);
pr_alert("%s: tested %d tested_tries %d\n", __func__, tested, tested_tries);
}
torture_kthread_stopping("rcu_torture_fwd_prog"); return 0;
}
/* If forward-progress checking is requested and feasible, spawn the thread. */ staticint __init rcu_torture_fwd_prog_init(void)
{ int i; int ret = 0; struct rcu_fwd *rfp;
if (!fwd_progress) return 0; /* Not requested, so don't do it. */ if (fwd_progress >= nr_cpu_ids) {
VERBOSE_TOROUT_STRING("rcu_torture_fwd_prog_init: Limiting fwd_progress to # CPUs.\n");
fwd_progress = nr_cpu_ids;
} elseif (fwd_progress < 0) {
fwd_progress = nr_cpu_ids;
} if ((!cur_ops->sync && !cur_ops->call) ||
(!cur_ops->cbflood_max && (!cur_ops->stall_dur || cur_ops->stall_dur() <= 0)) ||
cur_ops == &rcu_busted_ops) {
VERBOSE_TOROUT_STRING("rcu_torture_fwd_prog_init: Disabled, unsupported by RCU flavor under test");
fwd_progress = 0; return 0;
} if (stall_cpu > 0 || (preempt_duration > 0 && IS_ENABLED(CONFIG_RCU_NOCB_CPU))) {
VERBOSE_TOROUT_STRING("rcu_torture_fwd_prog_init: Disabled, conflicts with CPU-stall and/or preemption testing");
fwd_progress = 0; if (IS_MODULE(CONFIG_RCU_TORTURE_TEST)) return -EINVAL; /* In module, can fail back to user. */
WARN_ON(1); /* Make sure rcutorture scripting notices conflict. */ return 0;
} if (fwd_progress_holdoff <= 0)
fwd_progress_holdoff = 1; if (fwd_progress_div <= 0)
fwd_progress_div = 4;
rfp = kcalloc(fwd_progress, sizeof(*rfp), GFP_KERNEL);
fwd_prog_tasks = kcalloc(fwd_progress, sizeof(*fwd_prog_tasks), GFP_KERNEL); if (!rfp || !fwd_prog_tasks) {
kfree(rfp);
kfree(fwd_prog_tasks);
fwd_prog_tasks = NULL;
fwd_progress = 0; return -ENOMEM;
} for (i = 0; i < fwd_progress; i++) {
spin_lock_init(&rfp[i].rcu_fwd_lock);
rfp[i].rcu_fwd_cb_tail = &rfp[i].rcu_fwd_cb_head;
rfp[i].rcu_fwd_id = i;
}
mutex_lock(&rcu_fwd_mutex);
rcu_fwds = rfp;
mutex_unlock(&rcu_fwd_mutex);
register_oom_notifier(&rcutorture_oom_nb); for (i = 0; i < fwd_progress; i++) {
ret = torture_create_kthread(rcu_torture_fwd_prog, &rcu_fwds[i], fwd_prog_tasks[i]); if (ret) {
fwd_progress = i; return ret;
}
} return 0;
}
staticvoid rcu_torture_fwd_prog_cleanup(void)
{ int i; struct rcu_fwd *rfp;
if (!rcu_fwds || !fwd_prog_tasks) return; for (i = 0; i < fwd_progress; i++)
torture_stop_kthread(rcu_torture_fwd_prog, fwd_prog_tasks[i]);
unregister_oom_notifier(&rcutorture_oom_nb);
mutex_lock(&rcu_fwd_mutex);
rfp = rcu_fwds;
rcu_fwds = NULL;
mutex_unlock(&rcu_fwd_mutex);
kfree(rfp);
kfree(fwd_prog_tasks);
fwd_prog_tasks = NULL;
}
/* Callback function for RCU barrier testing. */ staticvoid rcu_torture_barrier_cbf(struct rcu_head *rcu)
{
atomic_inc(&barrier_cbs_invoked);
}
/* IPI handler to get callback posted on desired CPU, if online. */ staticint rcu_torture_barrier1cb(void *rcu_void)
{ struct rcu_head *rhp = rcu_void;
/* kthread function to register callbacks used to test RCU barriers. */ staticint rcu_torture_barrier_cbs(void *arg)
{ long myid = (long)arg; bool lastphase = false; bool newphase; struct rcu_head rcu;
init_rcu_head_on_stack(&rcu);
VERBOSE_TOROUT_STRING("rcu_torture_barrier_cbs task started");
set_user_nice(current, MAX_NICE); do {
wait_event(barrier_cbs_wq[myid],
(newphase =
smp_load_acquire(&barrier_phase)) != lastphase ||
torture_must_stop());
lastphase = newphase; if (torture_must_stop()) break; /* * The above smp_load_acquire() ensures barrier_phase load * is ordered before the following ->call().
*/ if (smp_call_on_cpu(myid, rcu_torture_barrier1cb, &rcu, 1))
cur_ops->call(&rcu, rcu_torture_barrier_cbf);
if (atomic_dec_and_test(&barrier_cbs_count))
wake_up(&barrier_wq);
} while (!torture_must_stop()); if (cur_ops->cb_barrier != NULL)
cur_ops->cb_barrier();
destroy_rcu_head_on_stack(&rcu);
torture_kthread_stopping("rcu_torture_barrier_cbs"); return 0;
}
/* kthread function to drive and coordinate RCU barrier testing. */ staticint rcu_torture_barrier(void *arg)
{ int i;
VERBOSE_TOROUT_STRING("rcu_torture_barrier task starting"); do {
atomic_set(&barrier_cbs_invoked, 0);
atomic_set(&barrier_cbs_count, n_barrier_cbs); /* Ensure barrier_phase ordered after prior assignments. */
smp_store_release(&barrier_phase, !barrier_phase); for (i = 0; i < n_barrier_cbs; i++)
wake_up(&barrier_cbs_wq[i]);
wait_event(barrier_wq,
atomic_read(&barrier_cbs_count) == 0 ||
torture_must_stop()); if (torture_must_stop()) break;
n_barrier_attempts++;
cur_ops->cb_barrier(); /* Implies smp_mb() for wait_event(). */ if (atomic_read(&barrier_cbs_invoked) != n_barrier_cbs) {
n_rcu_torture_barrier_error++;
pr_err("barrier_cbs_invoked = %d, n_barrier_cbs = %d\n",
atomic_read(&barrier_cbs_invoked),
n_barrier_cbs);
WARN_ON(1); // Wait manually for the remaining callbacks
i = 0; do { if (WARN_ON(i++ > HZ))
i = INT_MIN;
schedule_timeout_interruptible(1);
cur_ops->cb_barrier();
} while (atomic_read(&barrier_cbs_invoked) !=
n_barrier_cbs &&
!torture_must_stop());
smp_mb(); // Can't trust ordering if broken. if (!torture_must_stop())
pr_err("Recovered: barrier_cbs_invoked = %d\n",
atomic_read(&barrier_cbs_invoked));
} else {
n_barrier_successes++;
}
schedule_timeout_interruptible(HZ / 10);
} while (!torture_must_stop());
torture_kthread_stopping("rcu_torture_barrier"); return 0;
}
/* Initialize RCU barrier testing. */ staticint rcu_torture_barrier_init(void)
{ int i; int ret;
if (n_barrier_cbs <= 0) return 0; if (cur_ops->call == NULL || cur_ops->cb_barrier == NULL) {
pr_alert("%s" TORTURE_FLAG " Call or barrier ops missing for %s,\n",
torture_type, cur_ops->name);
pr_alert("%s" TORTURE_FLAG " RCU barrier testing omitted from run.\n",
torture_type); return 0;
}
atomic_set(&barrier_cbs_count, 0);
atomic_set(&barrier_cbs_invoked, 0);
barrier_cbs_tasks =
kcalloc(n_barrier_cbs, sizeof(barrier_cbs_tasks[0]),
GFP_KERNEL);
barrier_cbs_wq =
kcalloc(n_barrier_cbs, sizeof(barrier_cbs_wq[0]), GFP_KERNEL); if (barrier_cbs_tasks == NULL || !barrier_cbs_wq) return -ENOMEM; for (i = 0; i < n_barrier_cbs; i++) {
init_waitqueue_head(&barrier_cbs_wq[i]);
ret = torture_create_kthread(rcu_torture_barrier_cbs,
(void *)(long)i,
barrier_cbs_tasks[i]); if (ret) return ret;
} return torture_create_kthread(rcu_torture_barrier, NULL, barrier_task);
}
/* Clean up after RCU barrier testing. */ staticvoid rcu_torture_barrier_cleanup(void)
{ int i;
torture_stop_kthread(rcu_torture_barrier, barrier_task); if (barrier_cbs_tasks != NULL) { for (i = 0; i < n_barrier_cbs; i++)
torture_stop_kthread(rcu_torture_barrier_cbs,
barrier_cbs_tasks[i]);
kfree(barrier_cbs_tasks);
barrier_cbs_tasks = NULL;
} if (barrier_cbs_wq != NULL) {
kfree(barrier_cbs_wq);
barrier_cbs_wq = NULL;
}
}
staticbool rcu_torture_can_boost(void)
{ staticint boost_warn_once; int prio;
if (!(test_boost == 1 && cur_ops->can_boost) && test_boost != 2) returnfalse; if (!cur_ops->start_gp_poll || !cur_ops->poll_gp_state) returnfalse;
prio = rcu_get_gp_kthreads_prio(); if (!prio) returnfalse;
if (prio < 2) { if (boost_warn_once == 1) returnfalse;
pr_alert("%s: WARN: RCU kthread priority too low to test boosting. Skipping RCU boost test. Try passing rcutree.kthread_prio > 1 on the kernel command line.\n", KBUILD_MODNAME);
boost_warn_once = 1; returnfalse;
}
// Child kthread which just does an rcutorture reader and exits. staticint rcu_torture_read_exit_child(void *trsp_in)
{ struct torture_random_state *trsp = trsp_in;
set_user_nice(current, MAX_NICE); // Minimize time between reading and exiting. while (!kthread_should_stop())
schedule_timeout_uninterruptible(HZ / 20);
(void)rcu_torture_one_read(trsp, -1); return 0;
}
// Parent kthread which creates and destroys read-exit child kthreads. staticint rcu_torture_read_exit(void *unused)
{ bool errexit = false; int i; struct task_struct *tsp;
DEFINE_TORTURE_RANDOM(trs);
// Allocate and initialize.
set_user_nice(current, MAX_NICE);
VERBOSE_TOROUT_STRING("rcu_torture_read_exit: Start of test");
// Each pass through this loop does one read-exit episode. do {
VERBOSE_TOROUT_STRING("rcu_torture_read_exit: Start of episode"); for (i = 0; i < read_exit_burst; i++) { if (READ_ONCE(read_exit_child_stop)) break;
stutter_wait("rcu_torture_read_exit"); // Spawn child.
tsp = kthread_run(rcu_torture_read_exit_child,
&trs, "%s", "rcu_torture_read_exit_child"); if (IS_ERR(tsp)) {
TOROUT_ERRSTRING("out of memory");
errexit = true; break;
}
cond_resched();
kthread_stop(tsp);
n_read_exits++;
}
VERBOSE_TOROUT_STRING("rcu_torture_read_exit: End of episode");
rcu_barrier(); // Wait for task_struct free, avoid OOM.
i = 0; for (; !errexit && !READ_ONCE(read_exit_child_stop) && i < read_exit_delay; i++)
schedule_timeout_uninterruptible(HZ);
} while (!errexit && !READ_ONCE(read_exit_child_stop));
// Clean up and exit.
smp_store_release(&read_exit_child_stopped, true); // After reaping.
smp_mb(); // Store before wakeup.
wake_up(&read_exit_wq); while (!torture_must_stop())
schedule_timeout_uninterruptible(HZ / 20);
torture_kthread_stopping("rcu_torture_read_exit"); return 0;
}
staticvoid rcu_torture_read_exit_cleanup(void)
{ if (!read_exit_task) return;
WRITE_ONCE(read_exit_child_stop, true);
smp_mb(); // Above write before wait.
wait_event(read_exit_wq, smp_load_acquire(&read_exit_child_stopped));
torture_stop_kthread(rcutorture_read_exit, read_exit_task);
}
staticvoid rcutorture_test_nmis(int n)
{ #if IS_BUILTIN(CONFIG_RCU_TORTURE_TEST) int cpu; int dumpcpu; int i;
for (i = 0; i < n; i++) {
preempt_disable();
cpu = smp_processor_id();
dumpcpu = cpu + 1; if (dumpcpu >= nr_cpu_ids)
dumpcpu = 0;
pr_alert("%s: CPU %d invoking dump_cpu_task(%d)\n", __func__, cpu, dumpcpu);
dump_cpu_task(dumpcpu);
preempt_enable();
schedule_timeout_uninterruptible(15 * HZ);
} #else// #if IS_BUILTIN(CONFIG_RCU_TORTURE_TEST)
WARN_ONCE(n, "Non-zero rcutorture.test_nmis=%d permitted only when rcutorture is built in.\n", test_nmis); #endif// #else // #if IS_BUILTIN(CONFIG_RCU_TORTURE_TEST)
}
// Randomly preempt online CPUs. staticint rcu_torture_preempt(void *unused)
{ int cpu = -1;
DEFINE_TORTURE_RANDOM(rand);
schedule_timeout_idle(stall_cpu_holdoff); do { // Wait for preempt_interval ms with up to 100us fuzz.
torture_hrtimeout_ms(preempt_interval, 100, &rand); // Select online CPU.
cpu = cpumask_next(cpu, cpu_online_mask); if (cpu >= nr_cpu_ids)
cpu = cpumask_next(-1, cpu_online_mask);
WARN_ON_ONCE(cpu >= nr_cpu_ids); // Move to that CPU, if can't do so, retry later. if (torture_sched_setaffinity(current->pid, cpumask_of(cpu), false)) continue; // Preempt at high-ish priority, then reset to normal.
sched_set_fifo(current);
torture_sched_setaffinity(current->pid, cpu_present_mask, true);
mdelay(preempt_duration);
sched_set_normal(current, 0);
stutter_wait("rcu_torture_preempt");
} while (!torture_must_stop());
torture_kthread_stopping("rcu_torture_preempt"); return 0;
}
if (nocb_tasks) { for (i = 0; i < nrealnocbers; i++)
torture_stop_kthread(rcu_nocb_toggle, nocb_tasks[i]);
kfree(nocb_tasks);
nocb_tasks = NULL;
}
if (updown_task) {
torture_stop_kthread(rcu_torture_updown, updown_task);
updown_task = NULL;
} if (reader_tasks) { for (i = 0; i < nrealreaders; i++)
torture_stop_kthread(rcu_torture_reader,
reader_tasks[i]);
kfree(reader_tasks);
reader_tasks = NULL;
}
kfree(rcu_torture_reader_mbchk);
rcu_torture_reader_mbchk = NULL;
if (fakewriter_tasks) { for (i = 0; i < nrealfakewriters; i++)
torture_stop_kthread(rcu_torture_fakewriter,
fakewriter_tasks[i]);
kfree(fakewriter_tasks);
fakewriter_tasks = NULL;
}
/* * Wait for all RCU callbacks to fire, then do torture-type-specific * cleanup operations.
*/ if (cur_ops->cb_barrier != NULL) {
pr_info("%s: Invoking %pS().\n", __func__, cur_ops->cb_barrier);
cur_ops->cb_barrier();
} if (cur_ops->cleanup != NULL)
cur_ops->cleanup();
rcu_torture_mem_dump_obj();
rcu_torture_stats_print(); /* -After- the stats thread is stopped! */
if (err_segs_recorded) {
pr_alert("Failure/close-call rcutorture reader segments:\n"); if (rt_read_nsegs == 0)
pr_alert("\t: No segments recorded!!!\n");
firsttime = 1; for (i = 0; i < rt_read_nsegs; i++) { if (IS_ENABLED(CONFIG_RCU_TORTURE_TEST_LOG_GP))
pr_alert("\t%lluus ", div64_u64(err_segs[i].rt_ts, 1000ULL)); else
pr_alert("\t");
pr_cont("%d: %#4x", i, err_segs[i].rt_readstate); if (err_segs[i].rt_delay_jiffies != 0) {
pr_cont("%s%ldjiffies", firsttime ? "" : "+",
err_segs[i].rt_delay_jiffies);
firsttime = 0;
} if (IS_ENABLED(CONFIG_RCU_TORTURE_TEST_LOG_CPU)) {
pr_cont(" CPU %2d", err_segs[i].rt_cpu); if (err_segs[i].rt_cpu != err_segs[i].rt_end_cpu)
pr_cont("->%-2d", err_segs[i].rt_end_cpu); else
pr_cont(" ...");
} if (IS_ENABLED(CONFIG_RCU_TORTURE_TEST_LOG_GP) &&
cur_ops->gather_gp_seqs && cur_ops->format_gp_seqs) { char buf1[20+1]; char buf2[20+1]; char sepchar = '-';
cur_ops->format_gp_seqs(err_segs[i].rt_gp_seq,
buf1, ARRAY_SIZE(buf1));
cur_ops->format_gp_seqs(err_segs[i].rt_gp_seq_end,
buf2, ARRAY_SIZE(buf2)); if (err_segs[i].rt_gp_seq == err_segs[i].rt_gp_seq_end) { if (buf2[0]) { for (j = 0; buf2[j]; j++)
buf2[j] = '.'; if (j)
buf2[j - 1] = ' ';
}
sepchar = ' ';
}
pr_cont(" %s%c%s", buf1, sepchar, buf2);
} if (err_segs[i].rt_delay_ms != 0) {
pr_cont(" %s%ldms", firsttime ? "" : "+",
err_segs[i].rt_delay_ms);
firsttime = 0;
} if (err_segs[i].rt_delay_us != 0) {
pr_cont(" %s%ldus", firsttime ? "" : "+",
err_segs[i].rt_delay_us);
firsttime = 0;
}
pr_cont("%s", err_segs[i].rt_preempted ? " preempted" : ""); if (err_segs[i].rt_readstate & RCUTORTURE_RDR_BH)
pr_cont(" BH"); if (err_segs[i].rt_readstate & RCUTORTURE_RDR_IRQ)
pr_cont(" IRQ"); if (err_segs[i].rt_readstate & RCUTORTURE_RDR_PREEMPT)
pr_cont(" PREEMPT"); if (err_segs[i].rt_readstate & RCUTORTURE_RDR_RBH)
pr_cont(" RBH"); if (err_segs[i].rt_readstate & RCUTORTURE_RDR_SCHED)
pr_cont(" SCHED"); if (err_segs[i].rt_readstate & RCUTORTURE_RDR_RCU_1)
pr_cont(" RCU_1"); if (err_segs[i].rt_readstate & RCUTORTURE_RDR_RCU_2)
pr_cont(" RCU_2");
pr_cont("\n");
} if (rt_read_preempted)
pr_alert("\tReader was preempted.\n");
} if (atomic_read(&n_rcu_torture_error) || n_rcu_torture_barrier_error)
rcu_torture_print_module_parms(cur_ops, "End of test: FAILURE"); elseif (torture_onoff_failures())
rcu_torture_print_module_parms(cur_ops, "End of test: RCU_HOTPLUG"); else
rcu_torture_print_module_parms(cur_ops, "End of test: SUCCESS");
torture_cleanup_end(); if (cur_ops->gp_slow_unregister)
cur_ops->gp_slow_unregister(NULL);
if (gpwrap_lag && cur_ops->set_gpwrap_lag)
rcu_gpwrap_lag_cleanup();
}
staticvoid rcu_torture_err_cb(struct rcu_head *rhp)
{ /* * This -might- happen due to race conditions, but is unlikely. * The scenario that leads to this happening is that the * first of the pair of duplicate callbacks is queued, * someone else starts a grace period that includes that * callback, then the second of the pair must wait for the * next grace period. Unlikely, but can happen. If it * does happen, the debug-objects subsystem won't have splatted.
*/
pr_alert("%s: duplicated callback was invoked.\n", KBUILD_MODNAME);
}
/* * Verify that double-free causes debug-objects to complain, but only * if CONFIG_DEBUG_OBJECTS_RCU_HEAD=y. Otherwise, say that the test * cannot be carried out.
*/ staticvoid rcu_test_debug_objects(void)
{ struct rcu_head rh1; struct rcu_head rh2; int idx;
if (!IS_ENABLED(CONFIG_DEBUG_OBJECTS_RCU_HEAD)) {
pr_alert("%s: !CONFIG_DEBUG_OBJECTS_RCU_HEAD, not testing duplicate call_%s()\n",
KBUILD_MODNAME, cur_ops->name); return;
}
if (WARN_ON_ONCE(cur_ops->debug_objects &&
(!cur_ops->call || !cur_ops->cb_barrier))) return;
init_rcu_head_on_stack(&rh1);
init_rcu_head_on_stack(&rh2);
pr_alert("%s: WARN: Duplicate call_%s() test starting.\n", KBUILD_MODNAME, cur_ops->name);
/* Try to queue the rh2 pair of callbacks for the same grace period. */
idx = cur_ops->readlock(); /* Make it impossible to finish a grace period. */
cur_ops->call(&rh1, rcu_torture_leak_cb); /* Start grace period. */
cur_ops->call(&rh2, rcu_torture_leak_cb);
cur_ops->call(&rh2, rcu_torture_err_cb); /* Duplicate callback. */ if (rhp) {
cur_ops->call(rhp, rcu_torture_leak_cb);
cur_ops->call(rhp, rcu_torture_err_cb); /* Another duplicate callback. */
}
cur_ops->readunlock(idx);
/* Wait for them all to get done so we can safely return. */
cur_ops->cb_barrier();
pr_alert("%s: WARN: Duplicate call_%s() test complete.\n", KBUILD_MODNAME, cur_ops->name);
destroy_rcu_head_on_stack(&rh1);
destroy_rcu_head_on_stack(&rh2);
kfree(rhp);
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.67Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.