rcutorture: Add trivial RCU implementation
authorPaul E. McKenney <paulmck@linux.ibm.com>
Fri, 19 Apr 2019 14:38:27 +0000 (07:38 -0700)
committerPaul E. McKenney <paulmck@linux.ibm.com>
Tue, 28 May 2019 16:06:09 +0000 (09:06 -0700)
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>
kernel/rcu/rcu.h
kernel/rcu/rcutorture.c
kernel/rcu/update.c
tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL [new file with mode: 0644]
tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL.boot [new file with mode: 0644]

index 390aab20115e8133e45d97e73b163ccb59121fe3..5290b01de534f2d8bc7d10f90939fb790b09e8c4 100644 (file)
@@ -446,6 +446,7 @@ void rcu_request_urgent_qs_task(struct task_struct *t);
 enum rcutorture_type {
        RCU_FLAVOR,
        RCU_TASKS_FLAVOR,
+       RCU_TRIVIAL_FLAVOR,
        SRCU_FLAVOR,
        INVALID_RCU_FLAVOR
 };
@@ -479,6 +480,10 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
 #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
 
 static inline void srcutorture_get_gp_data(enum rcutorture_type test_type,
index a3f5488a319a21e72d0f6318c4ad60a1e3708983..6b803fb2f7ca8d65cf595eca843c93fec30bdf2a 100644 (file)
@@ -672,6 +672,47 @@ static struct rcu_torture_ops tasks_ops = {
        .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)
 {
        if (!cur_ops->gp_diff)
@@ -1789,6 +1830,8 @@ static void rcu_torture_fwd_prog_cr(void)
 
        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. */
        WRITE_ONCE(rcu_fwd_cb_nodelay, true);
@@ -2265,7 +2308,7 @@ rcu_torture_init(void)
        int firsterr = 0;
        static struct rcu_torture_ops *torture_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))
index c3bf44ba42e5420117601047fc7d1bdd7d511bad..61df2bf08563201e54b0ad667be1cab049c50aea 100644 (file)
@@ -423,6 +423,19 @@ EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read);
        do { } while (0)
 #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
 int rcu_cpu_stall_suppress __read_mostly; /* 1 = suppress stall warnings. */
 EXPORT_SYMBOL_GPL(rcu_cpu_stall_suppress);
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL b/tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL
new file mode 100644 (file)
index 0000000..4d8eb5b
--- /dev/null
@@ -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
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL.boot b/tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL.boot
new file mode 100644 (file)
index 0000000..7017f5f
--- /dev/null
@@ -0,0 +1,3 @@
+rcutorture.torture_type=trivial
+rcutorture.onoff_interval=0
+rcutorture.shuffle_interval=0