rcutorture: Add trivial RCU implementation
I have been showing off a trivial RCU implementation for non-preemptive
environments for some time now:
#define rcu_read_lock()
#define rcu_read_unlock()
#define rcu_dereference(p) READ_ONCE(p)
#define rcu_assign_pointer(p, v) smp_store_release(&(p), (v))
void synchronize_rcu(void)
{
int cpu;
for_each_online_cpu(cpu)
sched_setaffinity(current->pid, cpumask_of(cpu));
}
Trivial or not, as the old saying goes, "if it ain't tested, it don't
work!". This commit therefore adds a "trivial" flavor to rcutorture
and a corresponding TRIVIAL test scenario. This variant does not handle
CPU hotplug, which is unconditionally enabled on x86 for post-v5.1-rc3
kernels, which is why the TRIVIAL.boot says "rcutorture.onoff_interval=0".
This commit actually does handle CONFIG_PREEMPT=y kernels, but only
because it turns back the Linux-kernel clock in order to provide these
alternative definitions (or the moral equivalent thereof):
#define rcu_read_lock() preempt_disable()
#define rcu_read_unlock() preempt_enable()
In CONFIG_PREEMPT=n kernels without debugging, these are equivalent to
empty macros give or take a compiler barrier. However, the have been
successfully tested with actual empty macros as well.
Signed-off-by: Paul E. McKenney <paulmck@linux.ibm.com>
[ paulmck: Fix symbol issue reported by kbuild test robot <lkp@intel.com>. ]
[ paulmck: Work around sched_setaffinity() issue noted by Andrea Parri. ]
[ paulmck: Add rcutorture.shuffle_interval=0 to TRIVIAL.boot to fix
interaction with shuffler task noted by Peter Zijlstra. ]
Tested-by: Andrea Parri <andrea.parri@amarulasolutions.com>
This commit is contained in:
@@ -446,6 +446,7 @@ void rcu_request_urgent_qs_task(struct task_struct *t);
|
|||||||
enum rcutorture_type {
|
enum rcutorture_type {
|
||||||
RCU_FLAVOR,
|
RCU_FLAVOR,
|
||||||
RCU_TASKS_FLAVOR,
|
RCU_TASKS_FLAVOR,
|
||||||
|
RCU_TRIVIAL_FLAVOR,
|
||||||
SRCU_FLAVOR,
|
SRCU_FLAVOR,
|
||||||
INVALID_RCU_FLAVOR
|
INVALID_RCU_FLAVOR
|
||||||
};
|
};
|
||||||
@@ -479,6 +480,10 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_RCU_TORTURE_TEST) || IS_MODULE(CONFIG_RCU_TORTURE_TEST)
|
||||||
|
long rcutorture_sched_setaffinity(pid_t pid, const struct cpumask *in_mask);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_TINY_SRCU
|
#ifdef CONFIG_TINY_SRCU
|
||||||
|
|
||||||
static inline void srcutorture_get_gp_data(enum rcutorture_type test_type,
|
static inline void srcutorture_get_gp_data(enum rcutorture_type test_type,
|
||||||
|
|||||||
@@ -672,6 +672,47 @@ static struct rcu_torture_ops tasks_ops = {
|
|||||||
.name = "tasks"
|
.name = "tasks"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definitions for trivial CONFIG_PREEMPT=n-only torture testing.
|
||||||
|
* This implementation does not necessarily work well with CPU hotplug.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void synchronize_rcu_trivial(void)
|
||||||
|
{
|
||||||
|
int cpu;
|
||||||
|
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
rcutorture_sched_setaffinity(current->pid, cpumask_of(cpu));
|
||||||
|
WARN_ON_ONCE(raw_smp_processor_id() != cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rcu_torture_read_lock_trivial(void) __acquires(RCU)
|
||||||
|
{
|
||||||
|
preempt_disable();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rcu_torture_read_unlock_trivial(int idx) __releases(RCU)
|
||||||
|
{
|
||||||
|
preempt_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct rcu_torture_ops trivial_ops = {
|
||||||
|
.ttype = RCU_TRIVIAL_FLAVOR,
|
||||||
|
.init = rcu_sync_torture_init,
|
||||||
|
.readlock = rcu_torture_read_lock_trivial,
|
||||||
|
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||||
|
.readunlock = rcu_torture_read_unlock_trivial,
|
||||||
|
.get_gp_seq = rcu_no_completed,
|
||||||
|
.sync = synchronize_rcu_trivial,
|
||||||
|
.exp_sync = synchronize_rcu_trivial,
|
||||||
|
.fqs = NULL,
|
||||||
|
.stats = NULL,
|
||||||
|
.irq_capable = 1,
|
||||||
|
.name = "trivial"
|
||||||
|
};
|
||||||
|
|
||||||
static unsigned long rcutorture_seq_diff(unsigned long new, unsigned long old)
|
static unsigned long rcutorture_seq_diff(unsigned long new, unsigned long old)
|
||||||
{
|
{
|
||||||
if (!cur_ops->gp_diff)
|
if (!cur_ops->gp_diff)
|
||||||
@@ -1789,6 +1830,8 @@ static void rcu_torture_fwd_prog_cr(void)
|
|||||||
|
|
||||||
if (READ_ONCE(rcu_fwd_emergency_stop))
|
if (READ_ONCE(rcu_fwd_emergency_stop))
|
||||||
return; /* Get out of the way quickly, no GP wait! */
|
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. */
|
/* Loop continuously posting RCU callbacks. */
|
||||||
WRITE_ONCE(rcu_fwd_cb_nodelay, true);
|
WRITE_ONCE(rcu_fwd_cb_nodelay, true);
|
||||||
@@ -2265,7 +2308,7 @@ rcu_torture_init(void)
|
|||||||
int firsterr = 0;
|
int firsterr = 0;
|
||||||
static struct rcu_torture_ops *torture_ops[] = {
|
static struct rcu_torture_ops *torture_ops[] = {
|
||||||
&rcu_ops, &rcu_busted_ops, &srcu_ops, &srcud_ops,
|
&rcu_ops, &rcu_busted_ops, &srcu_ops, &srcud_ops,
|
||||||
&busted_srcud_ops, &tasks_ops,
|
&busted_srcud_ops, &tasks_ops, &trivial_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!torture_init_begin(torture_type, verbose))
|
if (!torture_init_begin(torture_type, verbose))
|
||||||
|
|||||||
@@ -423,6 +423,19 @@ EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read);
|
|||||||
do { } while (0)
|
do { } while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_RCU_TORTURE_TEST) || IS_MODULE(CONFIG_RCU_TORTURE_TEST)
|
||||||
|
/* Get rcutorture access to sched_setaffinity(). */
|
||||||
|
long rcutorture_sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = sched_setaffinity(pid, in_mask);
|
||||||
|
WARN_ONCE(ret, "%s: sched_setaffinity() returned %d\n", __func__, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rcutorture_sched_setaffinity);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_RCU_STALL_COMMON
|
#ifdef CONFIG_RCU_STALL_COMMON
|
||||||
int rcu_cpu_stall_suppress __read_mostly; /* 1 = suppress stall warnings. */
|
int rcu_cpu_stall_suppress __read_mostly; /* 1 = suppress stall warnings. */
|
||||||
EXPORT_SYMBOL_GPL(rcu_cpu_stall_suppress);
|
EXPORT_SYMBOL_GPL(rcu_cpu_stall_suppress);
|
||||||
|
|||||||
14
tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL
Normal file
14
tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
CONFIG_SMP=y
|
||||||
|
CONFIG_NR_CPUS=8
|
||||||
|
CONFIG_PREEMPT_NONE=y
|
||||||
|
CONFIG_PREEMPT_VOLUNTARY=n
|
||||||
|
CONFIG_PREEMPT=n
|
||||||
|
CONFIG_HZ_PERIODIC=n
|
||||||
|
CONFIG_NO_HZ_IDLE=y
|
||||||
|
CONFIG_NO_HZ_FULL=n
|
||||||
|
CONFIG_HOTPLUG_CPU=n
|
||||||
|
CONFIG_SUSPEND=n
|
||||||
|
CONFIG_HIBERNATION=n
|
||||||
|
CONFIG_DEBUG_LOCK_ALLOC=n
|
||||||
|
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
|
||||||
|
CONFIG_RCU_EXPERT=y
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
rcutorture.torture_type=trivial
|
||||||
|
rcutorture.onoff_interval=0
|
||||||
|
rcutorture.shuffle_interval=0
|
||||||
Reference in New Issue
Block a user