rcutorture: Add a textbook-style trivial preemptible RCU

This commit adds a trivial textbook implementation of preemptible RCU
to rcutorture ("torture_type=trivial-preempt"), similar in spirit to the
existing "torture_type=trivial" textbook implementation of non-preemptible
RCU.  Neither trivial RCU implementation has any value for production use,
and are intended only to keep Paul honest in his introductory writings
and presentations.

[ paulmck: Apply kernel test robot feedback. ]

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
This commit is contained in:
Paul E. McKenney
2025-11-15 19:07:41 -08:00
committed by Joel Fernandes
parent 7aaa8047ea
commit c6f4e552e1
7 changed files with 112 additions and 1 deletions

View File

@@ -949,6 +949,10 @@ struct task_struct {
struct srcu_ctr __percpu *trc_reader_scp;
#endif /* #ifdef CONFIG_TASKS_TRACE_RCU */
#ifdef CONFIG_TRIVIAL_PREEMPT_RCU
int rcu_trivial_preempt_nesting;
#endif /* #ifdef CONFIG_TRIVIAL_PREEMPT_RCU */
struct sched_info sched_info;
struct list_head tasks;

View File

@@ -228,4 +228,15 @@ config RCU_DYNTICKS_TORTURE
This has no value for production and is only for testing.
config TRIVIAL_PREEMPT_RCU
bool "Textbook trivial preemptible RCU in rcutorture"
depends on RCU_EXPERT && RCU_TORTURE_TEST
default n
help
This option enables a textbook preemptible RCU that is
implemented in rcutorture. Its sole purpose is to validate
code used in books, papers, and presentations.
This has no value for production and is only for testing.
endmenu # "RCU Debugging"

View File

@@ -691,4 +691,8 @@ int rcu_stall_notifier_call_chain(unsigned long val, void *v);
static inline int rcu_stall_notifier_call_chain(unsigned long val, void *v) { return NOTIFY_DONE; }
#endif // #else // #if defined(CONFIG_RCU_STALL_COMMON) && defined(CONFIG_RCU_CPU_STALL_NOTIFIER)
#ifdef CONFIG_TRIVIAL_PREEMPT_RCU
void synchronize_rcu_trivial_preempt(void);
#endif // #ifdef CONFIG_TRIVIAL_PREEMPT_RCU
#endif /* __LINUX_RCU_H */

View File

@@ -1061,6 +1061,61 @@ static struct rcu_torture_ops trivial_ops = {
.name = "trivial"
};
#ifdef CONFIG_TRIVIAL_PREEMPT_RCU
/*
* Definitions for trivial CONFIG_PREEMPT=y torture testing. This
* implementation does not work well with large numbers of tasks or with
* long-term preemption. Either or both get you RCU CPU stall warnings.
*/
static void rcu_sync_torture_init_trivial_preempt(void)
{
rcu_sync_torture_init();
if (WARN_ONCE(onoff_interval || shuffle_interval, "%s: Non-zero onoff_interval (%d) or shuffle_interval (%d) breaks trivial RCU, resetting to zero", __func__, onoff_interval, shuffle_interval)) {
onoff_interval = 0;
shuffle_interval = 0;
}
}
static int rcu_torture_read_lock_trivial_preempt(void)
{
struct task_struct *t = current;
WRITE_ONCE(t->rcu_trivial_preempt_nesting, t->rcu_trivial_preempt_nesting + 1);
smp_mb();
return 0;
}
static void rcu_torture_read_unlock_trivial_preempt(int idx)
{
struct task_struct *t = current;
smp_store_release(&t->rcu_trivial_preempt_nesting, t->rcu_trivial_preempt_nesting - 1);
}
static struct rcu_torture_ops trivial_preempt_ops = {
.ttype = RCU_TRIVIAL_FLAVOR,
.init = rcu_sync_torture_init_trivial_preempt,
.readlock = rcu_torture_read_lock_trivial_preempt,
.read_delay = rcu_read_delay, // just reuse rcu's version.
.readunlock = rcu_torture_read_unlock_trivial_preempt,
.readlock_held = torture_readlock_not_held,
.get_gp_seq = rcu_no_completed,
.sync = synchronize_rcu_trivial_preempt,
.exp_sync = synchronize_rcu_trivial_preempt,
.irq_capable = 0, // In theory it should be, but let's keep it trivial.
.name = "trivial-preempt"
};
#define TRIVIAL_PREEMPT_OPS &trivial_preempt_ops,
#else // #ifdef CONFIG_TRIVIAL_PREEMPT_RCU
#define TRIVIAL_PREEMPT_OPS
#endif // #else // #ifdef CONFIG_TRIVIAL_PREEMPT_RCU
#ifdef CONFIG_TASKS_RCU
/*
@@ -4449,7 +4504,7 @@ rcu_torture_init(void)
static struct rcu_torture_ops *torture_ops[] = {
&rcu_ops, &rcu_busted_ops, &srcu_ops, &srcud_ops, &busted_srcud_ops,
TASKS_OPS TASKS_RUDE_OPS TASKS_TRACING_OPS
&trivial_ops,
&trivial_ops, TRIVIAL_PREEMPT_OPS
};
if (!torture_init_begin(torture_type, verbose))

View File

@@ -538,6 +538,28 @@ long torture_sched_setaffinity(pid_t pid, const struct cpumask *in_mask, bool do
EXPORT_SYMBOL_GPL(torture_sched_setaffinity);
#endif
#if IS_ENABLED(CONFIG_TRIVIAL_PREEMPT_RCU)
// Trivial and stupid grace-period wait. Defined here so that lockdep
// kernels can find tasklist_lock.
void synchronize_rcu_trivial_preempt(void)
{
struct task_struct *g;
struct task_struct *t;
smp_mb(); // Order prior accesses before grace-period start.
rcu_read_lock(); // Protect task list.
for_each_process_thread(g, t) {
if (t == current)
continue; // Don't deadlock on ourselves!
// Order later rcu_read_lock() on other tasks after QS.
while (smp_load_acquire(&t->rcu_trivial_preempt_nesting))
continue;
}
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(synchronize_rcu_trivial_preempt);
#endif // #if IS_ENABLED(CONFIG_TRIVIAL_PREEMPT_RCU)
int rcu_cpu_stall_notifiers __read_mostly; // !0 = provide stall notifiers (rarely useful)
EXPORT_SYMBOL_GPL(rcu_cpu_stall_notifiers);

View File

@@ -0,0 +1,12 @@
CONFIG_SMP=y
CONFIG_NR_CPUS=8
CONFIG_PREEMPT_NONE=n
CONFIG_PREEMPT_VOLUNTARY=n
CONFIG_PREEMPT=y
CONFIG_HZ_PERIODIC=n
CONFIG_NO_HZ_IDLE=y
CONFIG_NO_HZ_FULL=n
CONFIG_DEBUG_LOCK_ALLOC=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
CONFIG_RCU_EXPERT=y
CONFIG_TRIVIAL_PREEMPT_RCU=y

View File

@@ -0,0 +1,3 @@
rcutorture.torture_type=trivial-preempt
rcutorture.onoff_interval=0
rcutorture.shuffle_interval=0