* [PATCH 1/9] rcutorture: Make srcu_lockdep.sh check kernel Kconfig
2025-03-10 18:38 [PATCH 0/9] RCU torture-test changes for v6.16 Paul E. McKenney
@ 2025-03-10 18:38 ` Paul E. McKenney
2025-03-10 18:38 ` [PATCH 2/9] rcutorture: Make srcu_lockdep.sh check reader-conflict handling Paul E. McKenney
` (8 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Paul E. McKenney @ 2025-03-10 18:38 UTC (permalink / raw)
To: rcu; +Cc: linux-kernel, kernel-team, rostedt, Paul E. McKenney
The srcu_lockdep.sh currently blindly trusts the rcutorture SRCU-P
scenario to build its kernel with lockdep enabled. Of course, this
dependency might not be obvious to someone rebalancing SRCU scenarios.
This commit therefore adds code to srcu_lockdep.sh that verifies that
the .config file has lockdep enabled.
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
---
.../testing/selftests/rcutorture/bin/srcu_lockdep.sh | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/rcutorture/bin/srcu_lockdep.sh b/tools/testing/selftests/rcutorture/bin/srcu_lockdep.sh
index 2db12c5cad9c6..b94f6d3445c6c 100755
--- a/tools/testing/selftests/rcutorture/bin/srcu_lockdep.sh
+++ b/tools/testing/selftests/rcutorture/bin/srcu_lockdep.sh
@@ -39,8 +39,9 @@ do
shift
done
-err=
nerrs=0
+
+# Test lockdep's handling of deadlocks.
for d in 0 1
do
for t in 0 1 2
@@ -52,6 +53,12 @@ do
tools/testing/selftests/rcutorture/bin/kvm.sh --allcpus --duration 5s --configs "SRCU-P" --kconfig "CONFIG_FORCE_NEED_SRCU_NMI_SAFE=y" --bootargs "rcutorture.test_srcu_lockdep=$val rcutorture.reader_flavor=0x2" --trust-make --datestamp "$ds/$val" > "$T/kvm.sh.out" 2>&1
ret=$?
mv "$T/kvm.sh.out" "$RCUTORTURE/res/$ds/$val"
+ if ! grep -q '^CONFIG_PROVE_LOCKING=y' .config
+ then
+ echo "rcu_torture_init_srcu_lockdep:Error: CONFIG_PROVE_LOCKING disabled in rcutorture SRCU-P scenario"
+ nerrs=$((nerrs+1))
+ err=1
+ fi
if test "$d" -ne 0 && test "$ret" -eq 0
then
err=1
@@ -71,6 +78,8 @@ do
done
done
done
+
+# Set up exit code.
if test "$nerrs" -ne 0
then
exit 1
--
2.40.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 2/9] rcutorture: Make srcu_lockdep.sh check reader-conflict handling
2025-03-10 18:38 [PATCH 0/9] RCU torture-test changes for v6.16 Paul E. McKenney
2025-03-10 18:38 ` [PATCH 1/9] rcutorture: Make srcu_lockdep.sh check kernel Kconfig Paul E. McKenney
@ 2025-03-10 18:38 ` Paul E. McKenney
2025-03-10 18:38 ` [PATCH 3/9] rcutorture: Split out beginning and end from rcu_torture_one_read() Paul E. McKenney
` (7 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Paul E. McKenney @ 2025-03-10 18:38 UTC (permalink / raw)
To: rcu; +Cc: linux-kernel, kernel-team, rostedt, Paul E. McKenney
Mixing different flavors of RCU readers is forbidden, for example, you
should not use srcu_read_lock() and srcu_read_lock_nmisafe() on the same
srcu_struct structure. There are checks for this, but these checks are
not tested on a regular basis. This commit therefore adds such tests
to srcu_lockdep.sh.
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
---
.../selftests/rcutorture/bin/srcu_lockdep.sh | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/tools/testing/selftests/rcutorture/bin/srcu_lockdep.sh b/tools/testing/selftests/rcutorture/bin/srcu_lockdep.sh
index b94f6d3445c6c..208be7d09a612 100755
--- a/tools/testing/selftests/rcutorture/bin/srcu_lockdep.sh
+++ b/tools/testing/selftests/rcutorture/bin/srcu_lockdep.sh
@@ -79,6 +79,37 @@ do
done
done
+# Test lockdep-enabled testing of mixed SRCU readers.
+for val in 0x1 0xf
+do
+ err=
+ tools/testing/selftests/rcutorture/bin/kvm.sh --allcpus --duration 5s --configs "SRCU-P" --kconfig "CONFIG_FORCE_NEED_SRCU_NMI_SAFE=y" --bootargs "rcutorture.reader_flavor=$val" --trust-make --datestamp "$ds/$val" > "$T/kvm.sh.out" 2>&1
+ ret=$?
+ mv "$T/kvm.sh.out" "$RCUTORTURE/res/$ds/$val"
+ if ! grep -q '^CONFIG_PROVE_LOCKING=y' .config
+ then
+ echo "rcu_torture_init_srcu_lockdep:Error: CONFIG_PROVE_LOCKING disabled in rcutorture SRCU-P scenario"
+ nerrs=$((nerrs+1))
+ err=1
+ fi
+ if test "$val" -eq 0xf && test "$ret" -eq 0
+ then
+ err=1
+ echo -n Unexpected success for > "$RCUTORTURE/res/$ds/$val/kvm.sh.err"
+ fi
+ if test "$val" -eq 0x1 && test "$ret" -ne 0
+ then
+ err=1
+ echo -n Unexpected failure for > "$RCUTORTURE/res/$ds/$val/kvm.sh.err"
+ fi
+ if test -n "$err"
+ then
+ grep "rcu_torture_init_srcu_lockdep: test_srcu_lockdep = " "$RCUTORTURE/res/$ds/$val/SRCU-P/console.log" | sed -e 's/^.*rcu_torture_init_srcu_lockdep://' >> "$RCUTORTURE/res/$ds/$val/kvm.sh.err"
+ cat "$RCUTORTURE/res/$ds/$val/kvm.sh.err"
+ nerrs=$((nerrs+1))
+ fi
+done
+
# Set up exit code.
if test "$nerrs" -ne 0
then
--
2.40.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 3/9] rcutorture: Split out beginning and end from rcu_torture_one_read()
2025-03-10 18:38 [PATCH 0/9] RCU torture-test changes for v6.16 Paul E. McKenney
2025-03-10 18:38 ` [PATCH 1/9] rcutorture: Make srcu_lockdep.sh check kernel Kconfig Paul E. McKenney
2025-03-10 18:38 ` [PATCH 2/9] rcutorture: Make srcu_lockdep.sh check reader-conflict handling Paul E. McKenney
@ 2025-03-10 18:38 ` Paul E. McKenney
2025-03-10 18:38 ` [PATCH 4/9] rcutorture: Make torture.sh --do-rt use CONFIG_PREEMPT_RT Paul E. McKenney
` (6 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Paul E. McKenney @ 2025-03-10 18:38 UTC (permalink / raw)
To: rcu; +Cc: linux-kernel, kernel-team, rostedt, Paul E. McKenney
The rcu_torture_one_read() function is designed for RCU readers that are
confined to a task, such that a single thread of control extends from the
beginning of a given RCU read-side critical section to its end. This does
not suffice for things like srcu_down_read() and srcu_up_read(), where
the critical section might start at task level and end in a timer handler.
This commit therefore creates separate init_rcu_torture_one_read_state(),
rcu_torture_one_read_start(), and rcu_torture_one_read_end() functions,
along with a rcu_torture_one_read_state structure to coordinate their
actions. These will be used to create tests for srcu_down_read()
and friends.
One caution: The caller to rcu_torture_one_read_start() must enter the
initial read-side critical section prior to the call. This enables use
of non-standard primitives such as srcu_down_read() while still using
the same validation code.
---
kernel/rcu/rcutorture.c | 124 ++++++++++++++++++++++++++--------------
1 file changed, 81 insertions(+), 43 deletions(-)
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index 65095664f5c5b..b0e96df636226 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -2164,53 +2164,70 @@ rcutorture_loop_extend(int *readstate, bool insoftirq, struct torture_random_sta
return &rtrsp[j];
}
-/*
- * 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.
- */
-static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
-{
- bool checkpolling = !(torture_random(trsp) & 0xfff);
+struct rcu_torture_one_read_state {
+ bool checkpolling;
unsigned long cookie;
struct rcu_gp_oldstate cookie_full;
- int i;
unsigned long started;
- unsigned long completed;
- int newstate;
struct rcu_torture *p;
- int pipe_count;
- bool preempted = false;
- int readstate = 0;
- struct rt_read_seg rtseg[RCUTORTURE_RDR_MAX_SEGS] = { { 0 } };
- struct rt_read_seg *rtrsp = &rtseg[0];
- struct rt_read_seg *rtrsp1;
+ int readstate;
+ struct rt_read_seg rtseg[RCUTORTURE_RDR_MAX_SEGS];
+ struct rt_read_seg *rtrsp;
unsigned long long ts;
+};
- WARN_ON_ONCE(!rcu_is_watching());
- newstate = rcutorture_extend_mask(readstate, trsp);
- rcutorture_one_extend(&readstate, newstate, myid < 0, trsp, rtrsp++);
- if (checkpolling) {
+static void init_rcu_torture_one_read_state(struct rcu_torture_one_read_state *rtorsp,
+ struct torture_random_state *trsp)
+{
+ memset(rtorsp, 0, sizeof(*rtorsp));
+ rtorsp->checkpolling = !(torture_random(trsp) & 0xfff);
+ rtorsp->rtrsp = &rtorsp->rtseg[0];
+}
+
+/*
+ * 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.
+ */
+static bool 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)
- cookie = cur_ops->get_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(&cookie_full);
+ cur_ops->get_gp_state_full(&rtorsp->cookie_full);
}
- started = cur_ops->get_gp_seq();
- ts = rcu_trace_clock_local();
- p = rcu_dereference_check(rcu_torture_current,
+ 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());
- if (p == NULL) {
+ if (rtorsp->p == NULL) {
/* Wait for rcu_torture_writer to get underway */
- rcutorture_one_extend(&readstate, 0, myid < 0, trsp, rtrsp);
+ rcutorture_one_extend(&rtorsp->readstate, 0, myid < 0, trsp, rtorsp->rtrsp);
return false;
}
- if (p->rtort_mbtest == 0)
+ if (rtorsp->p->rtort_mbtest == 0)
atomic_inc(&n_rcu_torture_mberror);
- rcu_torture_reader_do_mbchk(myid, p, trsp);
- rtrsp = rcutorture_loop_extend(&readstate, myid < 0, trsp, rtrsp);
+ rcu_torture_reader_do_mbchk(myid, rtorsp->p, trsp);
+ return true;
+}
+
+/*
+ * Complete the last segment of a series of overlapping read-side
+ * critical sections and check for errors.
+ */
+static void rcu_torture_one_read_end(struct rcu_torture_one_read_state *rtorsp,
+ struct torture_random_state *trsp, long myid)
+{
+ int i;
+ unsigned long completed;
+ int pipe_count;
+ bool preempted = false;
+ struct rt_read_seg *rtrsp1;
+
preempt_disable();
- pipe_count = READ_ONCE(p->rtort_pipe_count);
+ 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.
@@ -2218,28 +2235,28 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
}
completed = cur_ops->get_gp_seq();
if (pipe_count > 1) {
- do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu,
- ts, started, completed);
+ 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, started);
+ 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 (checkpolling) {
+ if (rtorsp->checkpolling) {
if (cur_ops->get_gp_state && cur_ops->poll_gp_state)
- WARN_ONCE(cur_ops->poll_gp_state(cookie),
+ 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,
- cookie, cur_ops->get_gp_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(&cookie_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(),
@@ -2248,21 +2265,42 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
}
if (cur_ops->reader_blocked)
preempted = cur_ops->reader_blocked();
- rcutorture_one_extend(&readstate, 0, myid < 0, trsp, rtrsp);
- WARN_ON_ONCE(readstate);
+ rcutorture_one_extend(&rtorsp->readstate, 0, myid < 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(p->rtort_pipe_count) > 1);
+ 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 = &rtseg[0]; rtrsp1 < rtrsp; rtrsp1++)
+ 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.
+ */
+static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
+{
+ int newstate;
+ struct rcu_torture_one_read_state rtors;
+
+ WARN_ON_ONCE(!rcu_is_watching());
+ init_rcu_torture_one_read_state(&rtors, trsp);
+ newstate = rcutorture_extend_mask(rtors.readstate, trsp);
+ rcutorture_one_extend(&rtors.readstate, newstate, myid < 0, trsp, rtors.rtrsp++);
+ if (!rcu_torture_one_read_start(&rtors, trsp, myid)) {
+ rcutorture_one_extend(&rtors.readstate, 0, myid < 0, trsp, rtors.rtrsp);
+ return false;
+ }
+ rtors.rtrsp = rcutorture_loop_extend(&rtors.readstate, myid < 0, trsp, rtors.rtrsp);
+ rcu_torture_one_read_end(&rtors, trsp, myid);
return true;
}
--
2.40.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 4/9] rcutorture: Make torture.sh --do-rt use CONFIG_PREEMPT_RT
2025-03-10 18:38 [PATCH 0/9] RCU torture-test changes for v6.16 Paul E. McKenney
` (2 preceding siblings ...)
2025-03-10 18:38 ` [PATCH 3/9] rcutorture: Split out beginning and end from rcu_torture_one_read() Paul E. McKenney
@ 2025-03-10 18:38 ` Paul E. McKenney
2025-03-10 18:38 ` [PATCH 5/9] rcutorture: Add tests for SRCU up/down reader primitives Paul E. McKenney
` (5 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Paul E. McKenney @ 2025-03-10 18:38 UTC (permalink / raw)
To: rcu
Cc: linux-kernel, kernel-team, rostedt, Paul E. McKenney,
Sebastian Andrzej Siewior
The torture.sh --do-rt command-line parameter is intended to mimic -rt
kernels. Now that CONFIG_PREEMPT_RT is upstream, this commit makes this
mimicking more precise.
Note that testing of RCU priority boosting is disabled in favor
of forward-progress testing of RCU callbacks. If it turns out to be
possible to make kernels built with CONFIG_PREEMPT_RT=y to tolerate
testing of both, both will be enabled.
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
tools/testing/selftests/rcutorture/bin/torture.sh | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/rcutorture/bin/torture.sh b/tools/testing/selftests/rcutorture/bin/torture.sh
index 0447c4a00cc4d..9cabe505585ea 100755
--- a/tools/testing/selftests/rcutorture/bin/torture.sh
+++ b/tools/testing/selftests/rcutorture/bin/torture.sh
@@ -448,13 +448,17 @@ fi
if test "$do_rt" = "yes"
then
+ # In both runs, disable testing of RCU priority boosting because
+ # -rt doesn't like its interaction with testing of callback
+ # flooding.
+
# With all post-boot grace periods forced to normal.
- torture_bootargs="rcupdate.rcu_cpu_stall_suppress_at_boot=1 torture.disable_onoff_at_boot rcupdate.rcu_task_stall_timeout=30000 rcupdate.rcu_normal=1"
- torture_set "rcurttorture" tools/testing/selftests/rcutorture/bin/kvm.sh --allcpus --duration "$duration_rcutorture" --configs "TREE03" --trust-make
+ torture_bootargs="rcupdate.rcu_cpu_stall_suppress_at_boot=1 torture.disable_onoff_at_boot rcupdate.rcu_task_stall_timeout=30000 rcutorture.test_boost=0 rcupdate.rcu_normal=1"
+ torture_set "rcurttorture" tools/testing/selftests/rcutorture/bin/kvm.sh --allcpus --duration "$duration_rcutorture" --configs "TREE03" --kconfig "CONFIG_PREEMPT_LAZY=n CONFIG_PREEMPT_RT=y CONFIG_EXPERT=y" --trust-make
# With all post-boot grace periods forced to expedited.
- torture_bootargs="rcupdate.rcu_cpu_stall_suppress_at_boot=1 torture.disable_onoff_at_boot rcupdate.rcu_task_stall_timeout=30000 rcupdate.rcu_expedited=1"
- torture_set "rcurttorture-exp" tools/testing/selftests/rcutorture/bin/kvm.sh --allcpus --duration "$duration_rcutorture" --configs "TREE03" --trust-make
+ torture_bootargs="rcupdate.rcu_cpu_stall_suppress_at_boot=1 torture.disable_onoff_at_boot rcupdate.rcu_task_stall_timeout=30000 rcutorture.test_boost=0 rcupdate.rcu_expedited=1"
+ torture_set "rcurttorture-exp" tools/testing/selftests/rcutorture/bin/kvm.sh --allcpus --duration "$duration_rcutorture" --configs "TREE03" --kconfig "CONFIG_PREEMPT_LAZY=n CONFIG_PREEMPT_RT=y CONFIG_EXPERT=y" --trust-make
fi
if test "$do_srcu_lockdep" = "yes"
--
2.40.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 5/9] rcutorture: Add tests for SRCU up/down reader primitives
2025-03-10 18:38 [PATCH 0/9] RCU torture-test changes for v6.16 Paul E. McKenney
` (3 preceding siblings ...)
2025-03-10 18:38 ` [PATCH 4/9] rcutorture: Make torture.sh --do-rt use CONFIG_PREEMPT_RT Paul E. McKenney
@ 2025-03-10 18:38 ` Paul E. McKenney
2025-03-27 3:26 ` Z qiang
2025-03-10 18:38 ` [PATCH 6/9] rcutorture: Pull rcu_torture_updown() loop body into new function Paul E. McKenney
` (4 subsequent siblings)
9 siblings, 1 reply; 17+ messages in thread
From: Paul E. McKenney @ 2025-03-10 18:38 UTC (permalink / raw)
To: rcu; +Cc: linux-kernel, kernel-team, rostedt, Paul E. McKenney
This commit adds a new rcutorture.n_up_down kernel boot parameter
that specifies the number of outstanding SRCU up/down readers, which
begin in kthread context and end in an hrtimer handler. There is a new
kthread ("rcu_torture_updown") that scans an per-reader array looking
for elements whose readers have ended. This kthread sleeps between one
and two milliseconds between consecutive scans.
[ paulmck: Apply kernel test robot feedback. ]
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
---
kernel/rcu/rcutorture.c | 227 ++++++++++++++++++++++++++++++++++++----
1 file changed, 208 insertions(+), 19 deletions(-)
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index b0e96df636226..6afcd33e724ba 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -55,22 +55,24 @@ 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_NBITS 7 /* Number of bits defined above. */
-#define RCUTORTURE_MAX_EXTEND \
+// 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)
+ 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)
@@ -110,6 +112,7 @@ 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");
@@ -152,6 +155,7 @@ static int nrealfakewriters;
static struct task_struct *writer_task;
static struct task_struct **fakewriter_tasks;
static struct task_struct **reader_tasks;
+static struct task_struct *updown_task;
static struct task_struct **nocb_tasks;
static struct task_struct *stats_task;
static struct task_struct *fqs_task;
@@ -374,6 +378,8 @@ struct rcu_torture_ops {
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);
unsigned long (*get_gp_seq)(void);
unsigned long (*gp_diff)(unsigned long new, unsigned long old);
void (*deferred_free)(struct rcu_torture *p);
@@ -421,6 +427,7 @@ struct rcu_torture_ops {
int no_pi_lock;
int debug_objects;
int start_poll_irqsoff;
+ int have_up_down;
const char *name;
};
@@ -754,6 +761,50 @@ static int torture_srcu_read_lock_held(void)
return srcu_read_lock_held(srcu_ctlp);
}
+static bool srcu_torture_have_up_down(void)
+{
+ int rf = reader_flavor;
+
+ if (!rf)
+ rf = SRCU_READ_FLAVOR_NORMAL;
+ return !!(cur_ops->have_up_down & rf);
+}
+
+static int srcu_torture_down_read(void)
+{
+ int idx;
+ struct srcu_ctr __percpu *scp;
+
+ WARN_ON_ONCE(reader_flavor & ~SRCU_READ_FLAVOR_ALL);
+ WARN_ON_ONCE(reader_flavor & (reader_flavor - 1));
+
+ if ((reader_flavor & SRCU_READ_FLAVOR_NORMAL) || !(reader_flavor & SRCU_READ_FLAVOR_ALL)) {
+ idx = srcu_down_read(srcu_ctlp);
+ WARN_ON_ONCE(idx & ~0x1);
+ return idx;
+ }
+ if (reader_flavor & SRCU_READ_FLAVOR_FAST) {
+ scp = srcu_down_read_fast(srcu_ctlp);
+ idx = __srcu_ptr_to_ctr(srcu_ctlp, scp);
+ WARN_ON_ONCE(idx & ~0x1);
+ return idx << 3;
+ }
+ WARN_ON_ONCE(1);
+ return 0;
+}
+
+static void srcu_torture_up_read(int idx)
+{
+ WARN_ON_ONCE((reader_flavor && (idx & ~reader_flavor)) || (!reader_flavor && (idx & ~0x1)));
+ if (reader_flavor & SRCU_READ_FLAVOR_FAST)
+ srcu_up_read_fast(srcu_ctlp, __srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x8) >> 3));
+ else if ((reader_flavor & SRCU_READ_FLAVOR_NORMAL) ||
+ !(reader_flavor & SRCU_READ_FLAVOR_ALL))
+ srcu_up_read(srcu_ctlp, idx & 0x1);
+ else
+ WARN_ON_ONCE(1);
+}
+
static unsigned long srcu_torture_completed(void)
{
return srcu_batches_completed(srcu_ctlp);
@@ -811,6 +862,8 @@ static struct rcu_torture_ops srcu_ops = {
.readlock = srcu_torture_read_lock,
.read_delay = srcu_read_delay,
.readunlock = srcu_torture_read_unlock,
+ .down_read = srcu_torture_down_read,
+ .up_read = srcu_torture_up_read,
.readlock_held = torture_srcu_read_lock_held,
.get_gp_seq = srcu_torture_completed,
.gp_diff = rcu_seq_diff,
@@ -831,6 +884,8 @@ static struct rcu_torture_ops srcu_ops = {
.irq_capable = 1,
.no_pi_lock = IS_ENABLED(CONFIG_TINY_SRCU),
.debug_objects = 1,
+ .have_up_down = IS_ENABLED(CONFIG_TINY_SRCU)
+ ? 0 : SRCU_READ_FLAVOR_NORMAL | SRCU_READ_FLAVOR_FAST,
.name = "srcu"
};
@@ -856,6 +911,8 @@ static struct rcu_torture_ops srcud_ops = {
.read_delay = srcu_read_delay,
.readunlock = srcu_torture_read_unlock,
.readlock_held = torture_srcu_read_lock_held,
+ .down_read = srcu_torture_down_read,
+ .up_read = srcu_torture_up_read,
.get_gp_seq = srcu_torture_completed,
.gp_diff = rcu_seq_diff,
.deferred_free = srcu_torture_deferred_free,
@@ -875,6 +932,8 @@ static struct rcu_torture_ops srcud_ops = {
.irq_capable = 1,
.no_pi_lock = IS_ENABLED(CONFIG_TINY_SRCU),
.debug_objects = 1,
+ .have_up_down = IS_ENABLED(CONFIG_TINY_SRCU)
+ ? 0 : SRCU_READ_FLAVOR_NORMAL | SRCU_READ_FLAVOR_FAST,
.name = "srcud"
};
@@ -1985,7 +2044,7 @@ static void rcutorture_one_extend(int *readstate, int newstate, bool insoftirq,
first = idxold1 == 0;
WARN_ON_ONCE(idxold2 < 0);
- WARN_ON_ONCE(idxold2 & ~RCUTORTURE_RDR_ALLBITS);
+ WARN_ON_ONCE(idxold2 & ~(RCUTORTURE_RDR_ALLBITS | RCUTORTURE_RDR_UPDOWN));
rcutorture_one_extend_check("before change", idxold1, statesnew, statesold, insoftirq);
rtrsp->rt_readstate = newstate;
@@ -2061,6 +2120,11 @@ static void rcutorture_one_extend(int *readstate, int newstate, bool insoftirq,
if (lockit)
raw_spin_unlock_irqrestore(¤t->pi_lock, flags);
}
+ if (statesold & RCUTORTURE_RDR_UPDOWN) {
+ cur_ops->up_read((idxold1 & RCUTORTURE_RDR_MASK_1) >> RCUTORTURE_RDR_SHIFT_1);
+ WARN_ON_ONCE(idxnew1 != -1);
+ idxold1 = 0;
+ }
/* Delay if neither beginning nor end and there was a change. */
if ((statesnew || statesold) && *readstate && newstate)
@@ -2201,7 +2265,8 @@ static bool rcu_torture_one_read_start(struct rcu_torture_one_read_state *rtorsp
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());
+ !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, myid < 0, trsp, rtorsp->rtrsp);
@@ -2370,6 +2435,123 @@ rcu_torture_reader(void *arg)
return 0;
}
+struct rcu_torture_one_read_state_updown {
+ struct hrtimer rtorsu_hrt;
+ bool rtorsu_inuse;
+ struct torture_random_state rtorsu_trs;
+ struct rcu_torture_one_read_state rtorsu_rtors;
+};
+
+static struct rcu_torture_one_read_state_updown *updownreaders;
+static DEFINE_TORTURE_RANDOM(rcu_torture_updown_rand);
+static int rcu_torture_updown(void *arg);
+
+static enum hrtimer_restart rcu_torture_updown_hrt(struct hrtimer *hrtp)
+{
+ struct rcu_torture_one_read_state_updown *rtorsup;
+
+ rtorsup = container_of(hrtp, struct rcu_torture_one_read_state_updown, rtorsu_hrt);
+ rcu_torture_one_read_end(&rtorsup->rtorsu_rtors, &rtorsup->rtorsu_trs, -1);
+ smp_store_release(&rtorsup->rtorsu_inuse, false);
+ return HRTIMER_NORESTART;
+}
+
+static int rcu_torture_updown_init(void)
+{
+ int i;
+ struct torture_random_state *rand = &rcu_torture_updown_rand;
+ int ret;
+
+ if (n_up_down < 0)
+ return 0;
+ if (!srcu_torture_have_up_down()) {
+ VERBOSE_TOROUT_STRING("rcu_torture_updown_init: Disabling up/down reader tests due to lack of primitives");
+ return 0;
+ }
+ updownreaders = kcalloc(n_up_down, sizeof(*updownreaders), GFP_KERNEL);
+ if (!updownreaders) {
+ VERBOSE_TOROUT_STRING("rcu_torture_updown_init: Out of memory, disabling up/down reader tests");
+ return -ENOMEM;
+ }
+ for (i = 0; i < n_up_down; i++) {
+ init_rcu_torture_one_read_state(&updownreaders[i].rtorsu_rtors, rand);
+ hrtimer_init(&updownreaders[i].rtorsu_hrt, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL | HRTIMER_MODE_SOFT);
+ updownreaders[i].rtorsu_hrt.function = rcu_torture_updown_hrt;
+ torture_random_init(&updownreaders[i].rtorsu_trs);
+ init_rcu_torture_one_read_state(&updownreaders[i].rtorsu_rtors,
+ &updownreaders[i].rtorsu_trs);
+ }
+ ret = torture_create_kthread(rcu_torture_updown, rand, updown_task);
+ if (ret) {
+ kfree(updownreaders);
+ updownreaders = NULL;
+ }
+ return ret;
+}
+
+static void rcu_torture_updown_cleanup(void)
+{
+ struct rcu_torture_one_read_state_updown *rtorsup;
+
+ for (rtorsup = updownreaders; rtorsup < &updownreaders[n_up_down]; rtorsup++) {
+ if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
+ continue;
+ (void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
+ WARN_ON_ONCE(rtorsup->rtorsu_inuse);
+
+ }
+ kfree(updownreaders);
+ updownreaders = NULL;
+}
+
+/*
+ * RCU torture up/down reader kthread, starting RCU readers in kthread
+ * context and ending them in hrtimer handlers. Otherwise similar to
+ * rcu_torture_reader().
+ */
+static int
+rcu_torture_updown(void *arg)
+{
+ int idx;
+ int rawidx;
+ struct rcu_torture_one_read_state_updown *rtorsup;
+ ktime_t t;
+
+ VERBOSE_TOROUT_STRING("rcu_torture_updown task started");
+ do {
+ for (rtorsup = updownreaders; rtorsup < &updownreaders[n_up_down]; rtorsup++) {
+ if (torture_must_stop())
+ break;
+ if (smp_load_acquire(&rtorsup->rtorsu_inuse))
+ continue;
+ init_rcu_torture_one_read_state(&rtorsup->rtorsu_rtors,
+ &rtorsup->rtorsu_trs);
+ rawidx = cur_ops->down_read();
+ idx = (rawidx << RCUTORTURE_RDR_SHIFT_1) & RCUTORTURE_RDR_MASK_1;
+ rtorsup->rtorsu_rtors.readstate = idx | RCUTORTURE_RDR_UPDOWN;
+ rtorsup->rtorsu_rtors.rtrsp++;
+ if (!rcu_torture_one_read_start(&rtorsup->rtorsu_rtors,
+ &rtorsup->rtorsu_trs, -1)) {
+ cur_ops->up_read(rawidx);
+ schedule_timeout_idle(HZ);
+ continue;
+ }
+ 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_SOFT);
+ }
+ 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.
@@ -2620,7 +2802,7 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
"reader_flavor=%x "
"nocbs_nthreads=%d nocbs_toggle=%d "
"test_nmis=%d "
- "preempt_duration=%d preempt_interval=%d\n",
+ "preempt_duration=%d preempt_interval=%d n_up_down=%d\n",
torture_type, tag, nrealreaders, nrealfakewriters,
stat_interval, verbose, test_no_idle_hz, shuffle_interval,
stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter,
@@ -2634,7 +2816,7 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
reader_flavor,
nocbs_nthreads, nocbs_toggle,
test_nmis,
- preempt_duration, preempt_interval);
+ preempt_duration, preempt_interval, n_up_down);
}
static int rcutorture_booster_cleanup(unsigned int cpu)
@@ -3686,6 +3868,10 @@ rcu_torture_cleanup(void)
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,
@@ -4216,6 +4402,9 @@ rcu_torture_init(void)
if (torture_init_error(firsterr))
goto unwind;
}
+ firsterr = rcu_torture_updown_init();
+ if (torture_init_error(firsterr))
+ goto unwind;
nrealnocbers = nocbs_nthreads;
if (WARN_ON(nrealnocbers < 0))
nrealnocbers = 1;
--
2.40.1
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 5/9] rcutorture: Add tests for SRCU up/down reader primitives
2025-03-10 18:38 ` [PATCH 5/9] rcutorture: Add tests for SRCU up/down reader primitives Paul E. McKenney
@ 2025-03-27 3:26 ` Z qiang
2025-03-27 14:30 ` Paul E. McKenney
0 siblings, 1 reply; 17+ messages in thread
From: Z qiang @ 2025-03-27 3:26 UTC (permalink / raw)
To: Paul E. McKenney; +Cc: rcu, linux-kernel, kernel-team, rostedt
>
> This commit adds a new rcutorture.n_up_down kernel boot parameter
> that specifies the number of outstanding SRCU up/down readers, which
> begin in kthread context and end in an hrtimer handler. There is a new
> kthread ("rcu_torture_updown") that scans an per-reader array looking
> for elements whose readers have ended. This kthread sleeps between one
> and two milliseconds between consecutive scans.
>
> [ paulmck: Apply kernel test robot feedback. ]
>
> Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
> ---
> kernel/rcu/rcutorture.c | 227 ++++++++++++++++++++++++++++++++++++----
> 1 file changed, 208 insertions(+), 19 deletions(-)
>
> diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
> index b0e96df636226..6afcd33e724ba 100644
> --- a/kernel/rcu/rcutorture.c
> +++ b/kernel/rcu/rcutorture.c
> @@ -55,22 +55,24 @@ 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_NBITS 7 /* Number of bits defined above. */
> -#define RCUTORTURE_MAX_EXTEND \
> +// 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)
> + 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)
> @@ -110,6 +112,7 @@ 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");
> @@ -152,6 +155,7 @@ static int nrealfakewriters;
> static struct task_struct *writer_task;
> static struct task_struct **fakewriter_tasks;
> static struct task_struct **reader_tasks;
> +static struct task_struct *updown_task;
> static struct task_struct **nocb_tasks;
> static struct task_struct *stats_task;
> static struct task_struct *fqs_task;
> @@ -374,6 +378,8 @@ struct rcu_torture_ops {
> 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);
> unsigned long (*get_gp_seq)(void);
> unsigned long (*gp_diff)(unsigned long new, unsigned long old);
> void (*deferred_free)(struct rcu_torture *p);
> @@ -421,6 +427,7 @@ struct rcu_torture_ops {
> int no_pi_lock;
> int debug_objects;
> int start_poll_irqsoff;
> + int have_up_down;
> const char *name;
> };
>
> @@ -754,6 +761,50 @@ static int torture_srcu_read_lock_held(void)
> return srcu_read_lock_held(srcu_ctlp);
> }
>
> +static bool srcu_torture_have_up_down(void)
> +{
> + int rf = reader_flavor;
> +
> + if (!rf)
> + rf = SRCU_READ_FLAVOR_NORMAL;
> + return !!(cur_ops->have_up_down & rf);
> +}
> +
> +static int srcu_torture_down_read(void)
> +{
> + int idx;
> + struct srcu_ctr __percpu *scp;
> +
> + WARN_ON_ONCE(reader_flavor & ~SRCU_READ_FLAVOR_ALL);
> + WARN_ON_ONCE(reader_flavor & (reader_flavor - 1));
> +
> + if ((reader_flavor & SRCU_READ_FLAVOR_NORMAL) || !(reader_flavor & SRCU_READ_FLAVOR_ALL)) {
> + idx = srcu_down_read(srcu_ctlp);
> + WARN_ON_ONCE(idx & ~0x1);
> + return idx;
> + }
> + if (reader_flavor & SRCU_READ_FLAVOR_FAST) {
> + scp = srcu_down_read_fast(srcu_ctlp);
> + idx = __srcu_ptr_to_ctr(srcu_ctlp, scp);
> + WARN_ON_ONCE(idx & ~0x1);
> + return idx << 3;
> + }
> + WARN_ON_ONCE(1);
> + return 0;
> +}
> +
> +static void srcu_torture_up_read(int idx)
> +{
> + WARN_ON_ONCE((reader_flavor && (idx & ~reader_flavor)) || (!reader_flavor && (idx & ~0x1)));
> + if (reader_flavor & SRCU_READ_FLAVOR_FAST)
> + srcu_up_read_fast(srcu_ctlp, __srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x8) >> 3));
> + else if ((reader_flavor & SRCU_READ_FLAVOR_NORMAL) ||
> + !(reader_flavor & SRCU_READ_FLAVOR_ALL))
> + srcu_up_read(srcu_ctlp, idx & 0x1);
> + else
> + WARN_ON_ONCE(1);
> +}
> +
> static unsigned long srcu_torture_completed(void)
> {
> return srcu_batches_completed(srcu_ctlp);
> @@ -811,6 +862,8 @@ static struct rcu_torture_ops srcu_ops = {
> .readlock = srcu_torture_read_lock,
> .read_delay = srcu_read_delay,
> .readunlock = srcu_torture_read_unlock,
> + .down_read = srcu_torture_down_read,
> + .up_read = srcu_torture_up_read,
> .readlock_held = torture_srcu_read_lock_held,
> .get_gp_seq = srcu_torture_completed,
> .gp_diff = rcu_seq_diff,
> @@ -831,6 +884,8 @@ static struct rcu_torture_ops srcu_ops = {
> .irq_capable = 1,
> .no_pi_lock = IS_ENABLED(CONFIG_TINY_SRCU),
> .debug_objects = 1,
> + .have_up_down = IS_ENABLED(CONFIG_TINY_SRCU)
> + ? 0 : SRCU_READ_FLAVOR_NORMAL | SRCU_READ_FLAVOR_FAST,
> .name = "srcu"
> };
>
> @@ -856,6 +911,8 @@ static struct rcu_torture_ops srcud_ops = {
> .read_delay = srcu_read_delay,
> .readunlock = srcu_torture_read_unlock,
> .readlock_held = torture_srcu_read_lock_held,
> + .down_read = srcu_torture_down_read,
> + .up_read = srcu_torture_up_read,
> .get_gp_seq = srcu_torture_completed,
> .gp_diff = rcu_seq_diff,
> .deferred_free = srcu_torture_deferred_free,
> @@ -875,6 +932,8 @@ static struct rcu_torture_ops srcud_ops = {
> .irq_capable = 1,
> .no_pi_lock = IS_ENABLED(CONFIG_TINY_SRCU),
> .debug_objects = 1,
> + .have_up_down = IS_ENABLED(CONFIG_TINY_SRCU)
> + ? 0 : SRCU_READ_FLAVOR_NORMAL | SRCU_READ_FLAVOR_FAST,
> .name = "srcud"
> };
>
> @@ -1985,7 +2044,7 @@ static void rcutorture_one_extend(int *readstate, int newstate, bool insoftirq,
>
> first = idxold1 == 0;
> WARN_ON_ONCE(idxold2 < 0);
> - WARN_ON_ONCE(idxold2 & ~RCUTORTURE_RDR_ALLBITS);
> + WARN_ON_ONCE(idxold2 & ~(RCUTORTURE_RDR_ALLBITS | RCUTORTURE_RDR_UPDOWN));
> rcutorture_one_extend_check("before change", idxold1, statesnew, statesold, insoftirq);
> rtrsp->rt_readstate = newstate;
>
> @@ -2061,6 +2120,11 @@ static void rcutorture_one_extend(int *readstate, int newstate, bool insoftirq,
> if (lockit)
> raw_spin_unlock_irqrestore(¤t->pi_lock, flags);
> }
> + if (statesold & RCUTORTURE_RDR_UPDOWN) {
> + cur_ops->up_read((idxold1 & RCUTORTURE_RDR_MASK_1) >> RCUTORTURE_RDR_SHIFT_1);
> + WARN_ON_ONCE(idxnew1 != -1);
> + idxold1 = 0;
> + }
>
> /* Delay if neither beginning nor end and there was a change. */
> if ((statesnew || statesold) && *readstate && newstate)
> @@ -2201,7 +2265,8 @@ static bool rcu_torture_one_read_start(struct rcu_torture_one_read_state *rtorsp
> 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());
> + !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, myid < 0, trsp, rtorsp->rtrsp);
> @@ -2370,6 +2435,123 @@ rcu_torture_reader(void *arg)
> return 0;
> }
>
> +struct rcu_torture_one_read_state_updown {
> + struct hrtimer rtorsu_hrt;
> + bool rtorsu_inuse;
> + struct torture_random_state rtorsu_trs;
> + struct rcu_torture_one_read_state rtorsu_rtors;
> +};
> +
> +static struct rcu_torture_one_read_state_updown *updownreaders;
> +static DEFINE_TORTURE_RANDOM(rcu_torture_updown_rand);
> +static int rcu_torture_updown(void *arg);
> +
> +static enum hrtimer_restart rcu_torture_updown_hrt(struct hrtimer *hrtp)
> +{
> + struct rcu_torture_one_read_state_updown *rtorsup;
> +
> + rtorsup = container_of(hrtp, struct rcu_torture_one_read_state_updown, rtorsu_hrt);
> + rcu_torture_one_read_end(&rtorsup->rtorsu_rtors, &rtorsup->rtorsu_trs, -1);
> + smp_store_release(&rtorsup->rtorsu_inuse, false);
> + return HRTIMER_NORESTART;
> +}
> +
> +static int rcu_torture_updown_init(void)
> +{
> + int i;
> + struct torture_random_state *rand = &rcu_torture_updown_rand;
> + int ret;
> +
> + if (n_up_down < 0)
> + return 0;
> + if (!srcu_torture_have_up_down()) {
> + VERBOSE_TOROUT_STRING("rcu_torture_updown_init: Disabling up/down reader tests due to lack of primitives");
> + return 0;
> + }
> + updownreaders = kcalloc(n_up_down, sizeof(*updownreaders), GFP_KERNEL);
> + if (!updownreaders) {
> + VERBOSE_TOROUT_STRING("rcu_torture_updown_init: Out of memory, disabling up/down reader tests");
> + return -ENOMEM;
> + }
> + for (i = 0; i < n_up_down; i++) {
> + init_rcu_torture_one_read_state(&updownreaders[i].rtorsu_rtors, rand);
> + hrtimer_init(&updownreaders[i].rtorsu_hrt, CLOCK_MONOTONIC,
> + HRTIMER_MODE_REL | HRTIMER_MODE_SOFT);
> + updownreaders[i].rtorsu_hrt.function = rcu_torture_updown_hrt;
> + torture_random_init(&updownreaders[i].rtorsu_trs);
> + init_rcu_torture_one_read_state(&updownreaders[i].rtorsu_rtors,
> + &updownreaders[i].rtorsu_trs);
> + }
> + ret = torture_create_kthread(rcu_torture_updown, rand, updown_task);
> + if (ret) {
> + kfree(updownreaders);
> + updownreaders = NULL;
> + }
> + return ret;
> +}
> +
> +static void rcu_torture_updown_cleanup(void)
> +{
> + struct rcu_torture_one_read_state_updown *rtorsup;
> +
> + for (rtorsup = updownreaders; rtorsup < &updownreaders[n_up_down]; rtorsup++) {
> + if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
> + continue;
> + (void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
> + WARN_ON_ONCE(rtorsup->rtorsu_inuse);
Hello, Paul
When I rmmod rcutorture, the following warning is triggered:
[ 809.227012] WARNING: CPU: 7 PID: 662 at
kernel/rcu/rcutorture.c:2506 rcu_torture_updown+0x3ff/0x620
[rcutorture]
[ 809.227038] Modules linked in: rcutorture(-) torture [last
unloaded: rcutorture]
[ 809.227052] CPU: 7 UID: 0 PID: 662 Comm: rcu_torture_upd Not
tainted 6.14.0-rc1-yoctodev-standard+ #103
f927b67579e64efac707898e59c492a894becb07
[ 809.227057] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009),
BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
[ 809.227061] RIP: 0010:rcu_torture_updown+0x3ff/0x620 [rcutorture]
[ 809.227112] Call Trace:
[ 809.227114] <TASK>
[ 809.227118] ? show_regs+0x65/0x70
[ 809.227127] ? __warn+0xd5/0x310
[ 809.227137] ? rcu_torture_updown+0x3ff/0x620 [rcutorture
1eb1c0a0090f471c5e98b34f024c9030cef829ed]
[ 809.227155] ? report_bug+0x23e/0x490
[ 809.227172] ? handle_bug+0x5b/0xa0
[ 809.227180] ? exc_invalid_op+0x1c/0x50
[ 809.227188] ? asm_exc_invalid_op+0x1f/0x30
[ 809.227210] ? hrtimer_try_to_cancel+0x160/0x490
[ 809.227216] ? _raw_spin_unlock_irqrestore+0x4a/0x80
[ 809.227225] ? rcu_torture_updown+0x3ff/0x620 [rcutorture
1eb1c0a0090f471c5e98b34f024c9030cef829ed]
[ 809.227255] ? rcu_torture_updown+0x340/0x620 [rcutorture
1eb1c0a0090f471c5e98b34f024c9030cef829ed]
[ 809.227320] ? __pfx_rcu_torture_updown+0x10/0x10 [rcutorture
1eb1c0a0090f471c5e98b34f024c9030cef829ed]
[ 809.227337] kthread+0x3d9/0x810
[ 809.227349] ? __pfx_kthread+0x10/0x10
[ 809.227357] ? rt_spin_unlock+0x4c/0x90
[ 809.227362] ? rt_spin_unlock+0x4c/0x90
[ 809.227367] ? calculate_sigpending+0x88/0xa0
[ 809.227372] ? __pfx_kthread+0x10/0x10
[ 809.227380] ret_from_fork+0x40/0x70
[ 809.227383] ? __pfx_kthread+0x10/0x10
[ 809.227390] ret_from_fork_asm+0x1a/0x30
[ 809.227420] </TASK>
If rtorsu_hrt timer is still in timer_queue, invoke hrtimer_cancel() will
remove it from timerqueue and directly return, so the rcu_torture_updown_hrt()
will not be executed and the rtorsup->rtorsu_inuse cannot be set false.
How about modifying it as follows:
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index 04d7a2173b95..ecf3d3797f7e 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -2502,8 +2502,7 @@ static void rcu_torture_updown_cleanup(void)
for (rtorsup = updownreaders; rtorsup <
&updownreaders[n_up_down]; rtorsup++) {
if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
continue;
- (void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
- if (WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
+ if (hrtimer_cancel(&rtorsup->rtorsu_hrt) ||
WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
rcu_torture_one_read_end(&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);
rtorsup->rtorsu_nups++;
Thanks
Zqiang
> +
> + }
> + kfree(updownreaders);
> + updownreaders = NULL;
> +}
> +
> +/*
> + * RCU torture up/down reader kthread, starting RCU readers in kthread
> + * context and ending them in hrtimer handlers. Otherwise similar to
> + * rcu_torture_reader().
> + */
> +static int
> +rcu_torture_updown(void *arg)
> +{
> + int idx;
> + int rawidx;
> + struct rcu_torture_one_read_state_updown *rtorsup;
> + ktime_t t;
> +
> + VERBOSE_TOROUT_STRING("rcu_torture_updown task started");
> + do {
> + for (rtorsup = updownreaders; rtorsup < &updownreaders[n_up_down]; rtorsup++) {
> + if (torture_must_stop())
> + break;
> + if (smp_load_acquire(&rtorsup->rtorsu_inuse))
> + continue;
> + init_rcu_torture_one_read_state(&rtorsup->rtorsu_rtors,
> + &rtorsup->rtorsu_trs);
> + rawidx = cur_ops->down_read();
> + idx = (rawidx << RCUTORTURE_RDR_SHIFT_1) & RCUTORTURE_RDR_MASK_1;
> + rtorsup->rtorsu_rtors.readstate = idx | RCUTORTURE_RDR_UPDOWN;
> + rtorsup->rtorsu_rtors.rtrsp++;
> + if (!rcu_torture_one_read_start(&rtorsup->rtorsu_rtors,
> + &rtorsup->rtorsu_trs, -1)) {
> + cur_ops->up_read(rawidx);
> + schedule_timeout_idle(HZ);
> + continue;
> + }
> + 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_SOFT);
> + }
> + 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.
> @@ -2620,7 +2802,7 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
> "reader_flavor=%x "
> "nocbs_nthreads=%d nocbs_toggle=%d "
> "test_nmis=%d "
> - "preempt_duration=%d preempt_interval=%d\n",
> + "preempt_duration=%d preempt_interval=%d n_up_down=%d\n",
> torture_type, tag, nrealreaders, nrealfakewriters,
> stat_interval, verbose, test_no_idle_hz, shuffle_interval,
> stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter,
> @@ -2634,7 +2816,7 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
> reader_flavor,
> nocbs_nthreads, nocbs_toggle,
> test_nmis,
> - preempt_duration, preempt_interval);
> + preempt_duration, preempt_interval, n_up_down);
> }
>
> static int rcutorture_booster_cleanup(unsigned int cpu)
> @@ -3686,6 +3868,10 @@ rcu_torture_cleanup(void)
> 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,
> @@ -4216,6 +4402,9 @@ rcu_torture_init(void)
> if (torture_init_error(firsterr))
> goto unwind;
> }
> + firsterr = rcu_torture_updown_init();
> + if (torture_init_error(firsterr))
> + goto unwind;
> nrealnocbers = nocbs_nthreads;
> if (WARN_ON(nrealnocbers < 0))
> nrealnocbers = 1;
> --
> 2.40.1
>
>
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 5/9] rcutorture: Add tests for SRCU up/down reader primitives
2025-03-27 3:26 ` Z qiang
@ 2025-03-27 14:30 ` Paul E. McKenney
2025-03-27 16:22 ` Joel Fernandes
0 siblings, 1 reply; 17+ messages in thread
From: Paul E. McKenney @ 2025-03-27 14:30 UTC (permalink / raw)
To: Z qiang; +Cc: rcu, linux-kernel, kernel-team, rostedt
On Thu, Mar 27, 2025 at 11:26:01AM +0800, Z qiang wrote:
> >
> > This commit adds a new rcutorture.n_up_down kernel boot parameter
> > that specifies the number of outstanding SRCU up/down readers, which
> > begin in kthread context and end in an hrtimer handler. There is a new
> > kthread ("rcu_torture_updown") that scans an per-reader array looking
> > for elements whose readers have ended. This kthread sleeps between one
> > and two milliseconds between consecutive scans.
> >
> > [ paulmck: Apply kernel test robot feedback. ]
> >
> > Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
> > ---
> > kernel/rcu/rcutorture.c | 227 ++++++++++++++++++++++++++++++++++++----
> > 1 file changed, 208 insertions(+), 19 deletions(-)
> >
> > diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
> > index b0e96df636226..6afcd33e724ba 100644
> > --- a/kernel/rcu/rcutorture.c
> > +++ b/kernel/rcu/rcutorture.c
> > @@ -55,22 +55,24 @@ 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_NBITS 7 /* Number of bits defined above. */
> > -#define RCUTORTURE_MAX_EXTEND \
> > +// 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)
> > + 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)
> > @@ -110,6 +112,7 @@ 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");
> > @@ -152,6 +155,7 @@ static int nrealfakewriters;
> > static struct task_struct *writer_task;
> > static struct task_struct **fakewriter_tasks;
> > static struct task_struct **reader_tasks;
> > +static struct task_struct *updown_task;
> > static struct task_struct **nocb_tasks;
> > static struct task_struct *stats_task;
> > static struct task_struct *fqs_task;
> > @@ -374,6 +378,8 @@ struct rcu_torture_ops {
> > 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);
> > unsigned long (*get_gp_seq)(void);
> > unsigned long (*gp_diff)(unsigned long new, unsigned long old);
> > void (*deferred_free)(struct rcu_torture *p);
> > @@ -421,6 +427,7 @@ struct rcu_torture_ops {
> > int no_pi_lock;
> > int debug_objects;
> > int start_poll_irqsoff;
> > + int have_up_down;
> > const char *name;
> > };
> >
> > @@ -754,6 +761,50 @@ static int torture_srcu_read_lock_held(void)
> > return srcu_read_lock_held(srcu_ctlp);
> > }
> >
> > +static bool srcu_torture_have_up_down(void)
> > +{
> > + int rf = reader_flavor;
> > +
> > + if (!rf)
> > + rf = SRCU_READ_FLAVOR_NORMAL;
> > + return !!(cur_ops->have_up_down & rf);
> > +}
> > +
> > +static int srcu_torture_down_read(void)
> > +{
> > + int idx;
> > + struct srcu_ctr __percpu *scp;
> > +
> > + WARN_ON_ONCE(reader_flavor & ~SRCU_READ_FLAVOR_ALL);
> > + WARN_ON_ONCE(reader_flavor & (reader_flavor - 1));
> > +
> > + if ((reader_flavor & SRCU_READ_FLAVOR_NORMAL) || !(reader_flavor & SRCU_READ_FLAVOR_ALL)) {
> > + idx = srcu_down_read(srcu_ctlp);
> > + WARN_ON_ONCE(idx & ~0x1);
> > + return idx;
> > + }
> > + if (reader_flavor & SRCU_READ_FLAVOR_FAST) {
> > + scp = srcu_down_read_fast(srcu_ctlp);
> > + idx = __srcu_ptr_to_ctr(srcu_ctlp, scp);
> > + WARN_ON_ONCE(idx & ~0x1);
> > + return idx << 3;
> > + }
> > + WARN_ON_ONCE(1);
> > + return 0;
> > +}
> > +
> > +static void srcu_torture_up_read(int idx)
> > +{
> > + WARN_ON_ONCE((reader_flavor && (idx & ~reader_flavor)) || (!reader_flavor && (idx & ~0x1)));
> > + if (reader_flavor & SRCU_READ_FLAVOR_FAST)
> > + srcu_up_read_fast(srcu_ctlp, __srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x8) >> 3));
> > + else if ((reader_flavor & SRCU_READ_FLAVOR_NORMAL) ||
> > + !(reader_flavor & SRCU_READ_FLAVOR_ALL))
> > + srcu_up_read(srcu_ctlp, idx & 0x1);
> > + else
> > + WARN_ON_ONCE(1);
> > +}
> > +
> > static unsigned long srcu_torture_completed(void)
> > {
> > return srcu_batches_completed(srcu_ctlp);
> > @@ -811,6 +862,8 @@ static struct rcu_torture_ops srcu_ops = {
> > .readlock = srcu_torture_read_lock,
> > .read_delay = srcu_read_delay,
> > .readunlock = srcu_torture_read_unlock,
> > + .down_read = srcu_torture_down_read,
> > + .up_read = srcu_torture_up_read,
> > .readlock_held = torture_srcu_read_lock_held,
> > .get_gp_seq = srcu_torture_completed,
> > .gp_diff = rcu_seq_diff,
> > @@ -831,6 +884,8 @@ static struct rcu_torture_ops srcu_ops = {
> > .irq_capable = 1,
> > .no_pi_lock = IS_ENABLED(CONFIG_TINY_SRCU),
> > .debug_objects = 1,
> > + .have_up_down = IS_ENABLED(CONFIG_TINY_SRCU)
> > + ? 0 : SRCU_READ_FLAVOR_NORMAL | SRCU_READ_FLAVOR_FAST,
> > .name = "srcu"
> > };
> >
> > @@ -856,6 +911,8 @@ static struct rcu_torture_ops srcud_ops = {
> > .read_delay = srcu_read_delay,
> > .readunlock = srcu_torture_read_unlock,
> > .readlock_held = torture_srcu_read_lock_held,
> > + .down_read = srcu_torture_down_read,
> > + .up_read = srcu_torture_up_read,
> > .get_gp_seq = srcu_torture_completed,
> > .gp_diff = rcu_seq_diff,
> > .deferred_free = srcu_torture_deferred_free,
> > @@ -875,6 +932,8 @@ static struct rcu_torture_ops srcud_ops = {
> > .irq_capable = 1,
> > .no_pi_lock = IS_ENABLED(CONFIG_TINY_SRCU),
> > .debug_objects = 1,
> > + .have_up_down = IS_ENABLED(CONFIG_TINY_SRCU)
> > + ? 0 : SRCU_READ_FLAVOR_NORMAL | SRCU_READ_FLAVOR_FAST,
> > .name = "srcud"
> > };
> >
> > @@ -1985,7 +2044,7 @@ static void rcutorture_one_extend(int *readstate, int newstate, bool insoftirq,
> >
> > first = idxold1 == 0;
> > WARN_ON_ONCE(idxold2 < 0);
> > - WARN_ON_ONCE(idxold2 & ~RCUTORTURE_RDR_ALLBITS);
> > + WARN_ON_ONCE(idxold2 & ~(RCUTORTURE_RDR_ALLBITS | RCUTORTURE_RDR_UPDOWN));
> > rcutorture_one_extend_check("before change", idxold1, statesnew, statesold, insoftirq);
> > rtrsp->rt_readstate = newstate;
> >
> > @@ -2061,6 +2120,11 @@ static void rcutorture_one_extend(int *readstate, int newstate, bool insoftirq,
> > if (lockit)
> > raw_spin_unlock_irqrestore(¤t->pi_lock, flags);
> > }
> > + if (statesold & RCUTORTURE_RDR_UPDOWN) {
> > + cur_ops->up_read((idxold1 & RCUTORTURE_RDR_MASK_1) >> RCUTORTURE_RDR_SHIFT_1);
> > + WARN_ON_ONCE(idxnew1 != -1);
> > + idxold1 = 0;
> > + }
> >
> > /* Delay if neither beginning nor end and there was a change. */
> > if ((statesnew || statesold) && *readstate && newstate)
> > @@ -2201,7 +2265,8 @@ static bool rcu_torture_one_read_start(struct rcu_torture_one_read_state *rtorsp
> > 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());
> > + !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, myid < 0, trsp, rtorsp->rtrsp);
> > @@ -2370,6 +2435,123 @@ rcu_torture_reader(void *arg)
> > return 0;
> > }
> >
> > +struct rcu_torture_one_read_state_updown {
> > + struct hrtimer rtorsu_hrt;
> > + bool rtorsu_inuse;
> > + struct torture_random_state rtorsu_trs;
> > + struct rcu_torture_one_read_state rtorsu_rtors;
> > +};
> > +
> > +static struct rcu_torture_one_read_state_updown *updownreaders;
> > +static DEFINE_TORTURE_RANDOM(rcu_torture_updown_rand);
> > +static int rcu_torture_updown(void *arg);
> > +
> > +static enum hrtimer_restart rcu_torture_updown_hrt(struct hrtimer *hrtp)
> > +{
> > + struct rcu_torture_one_read_state_updown *rtorsup;
> > +
> > + rtorsup = container_of(hrtp, struct rcu_torture_one_read_state_updown, rtorsu_hrt);
> > + rcu_torture_one_read_end(&rtorsup->rtorsu_rtors, &rtorsup->rtorsu_trs, -1);
> > + smp_store_release(&rtorsup->rtorsu_inuse, false);
> > + return HRTIMER_NORESTART;
> > +}
> > +
> > +static int rcu_torture_updown_init(void)
> > +{
> > + int i;
> > + struct torture_random_state *rand = &rcu_torture_updown_rand;
> > + int ret;
> > +
> > + if (n_up_down < 0)
> > + return 0;
> > + if (!srcu_torture_have_up_down()) {
> > + VERBOSE_TOROUT_STRING("rcu_torture_updown_init: Disabling up/down reader tests due to lack of primitives");
> > + return 0;
> > + }
> > + updownreaders = kcalloc(n_up_down, sizeof(*updownreaders), GFP_KERNEL);
> > + if (!updownreaders) {
> > + VERBOSE_TOROUT_STRING("rcu_torture_updown_init: Out of memory, disabling up/down reader tests");
> > + return -ENOMEM;
> > + }
> > + for (i = 0; i < n_up_down; i++) {
> > + init_rcu_torture_one_read_state(&updownreaders[i].rtorsu_rtors, rand);
> > + hrtimer_init(&updownreaders[i].rtorsu_hrt, CLOCK_MONOTONIC,
> > + HRTIMER_MODE_REL | HRTIMER_MODE_SOFT);
> > + updownreaders[i].rtorsu_hrt.function = rcu_torture_updown_hrt;
> > + torture_random_init(&updownreaders[i].rtorsu_trs);
> > + init_rcu_torture_one_read_state(&updownreaders[i].rtorsu_rtors,
> > + &updownreaders[i].rtorsu_trs);
> > + }
> > + ret = torture_create_kthread(rcu_torture_updown, rand, updown_task);
> > + if (ret) {
> > + kfree(updownreaders);
> > + updownreaders = NULL;
> > + }
> > + return ret;
> > +}
> > +
> > +static void rcu_torture_updown_cleanup(void)
> > +{
> > + struct rcu_torture_one_read_state_updown *rtorsup;
> > +
> > + for (rtorsup = updownreaders; rtorsup < &updownreaders[n_up_down]; rtorsup++) {
> > + if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
> > + continue;
> > + (void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
> > + WARN_ON_ONCE(rtorsup->rtorsu_inuse);
>
> Hello, Paul
>
> When I rmmod rcutorture, the following warning is triggered:
>
> [ 809.227012] WARNING: CPU: 7 PID: 662 at
> kernel/rcu/rcutorture.c:2506 rcu_torture_updown+0x3ff/0x620
> [rcutorture]
> [ 809.227038] Modules linked in: rcutorture(-) torture [last
> unloaded: rcutorture]
> [ 809.227052] CPU: 7 UID: 0 PID: 662 Comm: rcu_torture_upd Not
> tainted 6.14.0-rc1-yoctodev-standard+ #103
> f927b67579e64efac707898e59c492a894becb07
> [ 809.227057] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009),
> BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
> [ 809.227061] RIP: 0010:rcu_torture_updown+0x3ff/0x620 [rcutorture]
> [ 809.227112] Call Trace:
> [ 809.227114] <TASK>
> [ 809.227118] ? show_regs+0x65/0x70
> [ 809.227127] ? __warn+0xd5/0x310
> [ 809.227137] ? rcu_torture_updown+0x3ff/0x620 [rcutorture
> 1eb1c0a0090f471c5e98b34f024c9030cef829ed]
> [ 809.227155] ? report_bug+0x23e/0x490
> [ 809.227172] ? handle_bug+0x5b/0xa0
> [ 809.227180] ? exc_invalid_op+0x1c/0x50
> [ 809.227188] ? asm_exc_invalid_op+0x1f/0x30
> [ 809.227210] ? hrtimer_try_to_cancel+0x160/0x490
> [ 809.227216] ? _raw_spin_unlock_irqrestore+0x4a/0x80
> [ 809.227225] ? rcu_torture_updown+0x3ff/0x620 [rcutorture
> 1eb1c0a0090f471c5e98b34f024c9030cef829ed]
> [ 809.227255] ? rcu_torture_updown+0x340/0x620 [rcutorture
> 1eb1c0a0090f471c5e98b34f024c9030cef829ed]
> [ 809.227320] ? __pfx_rcu_torture_updown+0x10/0x10 [rcutorture
> 1eb1c0a0090f471c5e98b34f024c9030cef829ed]
> [ 809.227337] kthread+0x3d9/0x810
> [ 809.227349] ? __pfx_kthread+0x10/0x10
> [ 809.227357] ? rt_spin_unlock+0x4c/0x90
> [ 809.227362] ? rt_spin_unlock+0x4c/0x90
> [ 809.227367] ? calculate_sigpending+0x88/0xa0
> [ 809.227372] ? __pfx_kthread+0x10/0x10
> [ 809.227380] ret_from_fork+0x40/0x70
> [ 809.227383] ? __pfx_kthread+0x10/0x10
> [ 809.227390] ret_from_fork_asm+0x1a/0x30
> [ 809.227420] </TASK>
>
> If rtorsu_hrt timer is still in timer_queue, invoke hrtimer_cancel() will
> remove it from timerqueue and directly return, so the rcu_torture_updown_hrt()
> will not be executed and the rtorsup->rtorsu_inuse cannot be set false.
>
> How about modifying it as follows:
>
> diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
> index 04d7a2173b95..ecf3d3797f7e 100644
> --- a/kernel/rcu/rcutorture.c
> +++ b/kernel/rcu/rcutorture.c
> @@ -2502,8 +2502,7 @@ static void rcu_torture_updown_cleanup(void)
> for (rtorsup = updownreaders; rtorsup <
> &updownreaders[n_up_down]; rtorsup++) {
> if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
> continue;
> - (void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
> - if (WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
> + if (hrtimer_cancel(&rtorsup->rtorsu_hrt) ||
> WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
>
> rcu_torture_one_read_end(&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);
> rtorsup->rtorsu_nups++;
Good eyes, thank you! I have applied this fix with attribution.
Thanx, Paul
> Thanks
> Zqiang
>
> > +
> > + }
> > + kfree(updownreaders);
> > + updownreaders = NULL;
> > +}
> > +
> > +/*
> > + * RCU torture up/down reader kthread, starting RCU readers in kthread
> > + * context and ending them in hrtimer handlers. Otherwise similar to
> > + * rcu_torture_reader().
> > + */
> > +static int
> > +rcu_torture_updown(void *arg)
> > +{
> > + int idx;
> > + int rawidx;
> > + struct rcu_torture_one_read_state_updown *rtorsup;
> > + ktime_t t;
> > +
> > + VERBOSE_TOROUT_STRING("rcu_torture_updown task started");
> > + do {
> > + for (rtorsup = updownreaders; rtorsup < &updownreaders[n_up_down]; rtorsup++) {
> > + if (torture_must_stop())
> > + break;
> > + if (smp_load_acquire(&rtorsup->rtorsu_inuse))
> > + continue;
> > + init_rcu_torture_one_read_state(&rtorsup->rtorsu_rtors,
> > + &rtorsup->rtorsu_trs);
> > + rawidx = cur_ops->down_read();
> > + idx = (rawidx << RCUTORTURE_RDR_SHIFT_1) & RCUTORTURE_RDR_MASK_1;
> > + rtorsup->rtorsu_rtors.readstate = idx | RCUTORTURE_RDR_UPDOWN;
> > + rtorsup->rtorsu_rtors.rtrsp++;
> > + if (!rcu_torture_one_read_start(&rtorsup->rtorsu_rtors,
> > + &rtorsup->rtorsu_trs, -1)) {
> > + cur_ops->up_read(rawidx);
> > + schedule_timeout_idle(HZ);
> > + continue;
> > + }
> > + 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_SOFT);
> > + }
> > + 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.
> > @@ -2620,7 +2802,7 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
> > "reader_flavor=%x "
> > "nocbs_nthreads=%d nocbs_toggle=%d "
> > "test_nmis=%d "
> > - "preempt_duration=%d preempt_interval=%d\n",
> > + "preempt_duration=%d preempt_interval=%d n_up_down=%d\n",
> > torture_type, tag, nrealreaders, nrealfakewriters,
> > stat_interval, verbose, test_no_idle_hz, shuffle_interval,
> > stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter,
> > @@ -2634,7 +2816,7 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
> > reader_flavor,
> > nocbs_nthreads, nocbs_toggle,
> > test_nmis,
> > - preempt_duration, preempt_interval);
> > + preempt_duration, preempt_interval, n_up_down);
> > }
> >
> > static int rcutorture_booster_cleanup(unsigned int cpu)
> > @@ -3686,6 +3868,10 @@ rcu_torture_cleanup(void)
> > 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,
> > @@ -4216,6 +4402,9 @@ rcu_torture_init(void)
> > if (torture_init_error(firsterr))
> > goto unwind;
> > }
> > + firsterr = rcu_torture_updown_init();
> > + if (torture_init_error(firsterr))
> > + goto unwind;
> > nrealnocbers = nocbs_nthreads;
> > if (WARN_ON(nrealnocbers < 0))
> > nrealnocbers = 1;
> > --
> > 2.40.1
> >
> >
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 5/9] rcutorture: Add tests for SRCU up/down reader primitives
2025-03-27 14:30 ` Paul E. McKenney
@ 2025-03-27 16:22 ` Joel Fernandes
2025-03-27 16:48 ` Paul E. McKenney
0 siblings, 1 reply; 17+ messages in thread
From: Joel Fernandes @ 2025-03-27 16:22 UTC (permalink / raw)
To: paulmck, Z qiang; +Cc: rcu, linux-kernel, kernel-team, rostedt
Paul,
>> If rtorsu_hrt timer is still in timer_queue, invoke hrtimer_cancel() will
>> remove it from timerqueue and directly return, so the rcu_torture_updown_hrt()
>> will not be executed and the rtorsup->rtorsu_inuse cannot be set false.
>>
>> How about modifying it as follows:
>>
>> diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
>> index 04d7a2173b95..ecf3d3797f7e 100644
>> --- a/kernel/rcu/rcutorture.c
>> +++ b/kernel/rcu/rcutorture.c
>> @@ -2502,8 +2502,7 @@ static void rcu_torture_updown_cleanup(void)
>> for (rtorsup = updownreaders; rtorsup <
>> &updownreaders[n_up_down]; rtorsup++) {
>> if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
>> continue;
>> - (void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
>> - if (WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
>> + if (hrtimer_cancel(&rtorsup->rtorsu_hrt) ||
>> WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
>>
>> rcu_torture_one_read_end(&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);
>> rtorsup->rtorsu_nups++;
>
> Good eyes, thank you! I have applied this fix with attribution.
Could you re-send the series, or should I apply the fix the patch myself? Or
provide the new patch inline here.
Thanks!
- Joel
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 5/9] rcutorture: Add tests for SRCU up/down reader primitives
2025-03-27 16:22 ` Joel Fernandes
@ 2025-03-27 16:48 ` Paul E. McKenney
2025-03-27 17:08 ` Joel Fernandes
0 siblings, 1 reply; 17+ messages in thread
From: Paul E. McKenney @ 2025-03-27 16:48 UTC (permalink / raw)
To: Joel Fernandes; +Cc: Z qiang, rcu, linux-kernel, kernel-team, rostedt
On Thu, Mar 27, 2025 at 12:22:12PM -0400, Joel Fernandes wrote:
> Paul,
>
> >> If rtorsu_hrt timer is still in timer_queue, invoke hrtimer_cancel() will
> >> remove it from timerqueue and directly return, so the rcu_torture_updown_hrt()
> >> will not be executed and the rtorsup->rtorsu_inuse cannot be set false.
> >>
> >> How about modifying it as follows:
> >>
> >> diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
> >> index 04d7a2173b95..ecf3d3797f7e 100644
> >> --- a/kernel/rcu/rcutorture.c
> >> +++ b/kernel/rcu/rcutorture.c
> >> @@ -2502,8 +2502,7 @@ static void rcu_torture_updown_cleanup(void)
> >> for (rtorsup = updownreaders; rtorsup <
> >> &updownreaders[n_up_down]; rtorsup++) {
> >> if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
> >> continue;
> >> - (void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
> >> - if (WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
> >> + if (hrtimer_cancel(&rtorsup->rtorsu_hrt) ||
> >> WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
> >>
> >> rcu_torture_one_read_end(&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);
> >> rtorsup->rtorsu_nups++;
> >
> > Good eyes, thank you! I have applied this fix with attribution.
>
> Could you re-send the series, or should I apply the fix the patch myself? Or
> provide the new patch inline here.
Your choice, just let me know. If you have modified any of the other
patches in that series, it will probably be easier for you if I either
resend just that one patch or if you apply the changes. If you haven't
done any modifications, it might be easier for you if I re-sent the
series.
I have the delta patch below, which I have pushed out for kernel test
robot ministrations and which I expect to merge into the original
later today.
Thanx, Paul
------------------------------------------------------------------------
commit 55fcac5cb3fc96479d935db648c98503cb0a944b
Author: Paul E. McKenney <paulmck@kernel.org>
Date: Thu Mar 27 07:29:48 2025 -0700
squash! rcutorture: Add tests for SRCU up/down reader primitives
[ paulmck: Apply Z qiang feedback. ]
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index 04d7a2173b95d..ecf3d3797f7e1 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -2502,8 +2502,7 @@ static void rcu_torture_updown_cleanup(void)
for (rtorsup = updownreaders; rtorsup < &updownreaders[n_up_down]; rtorsup++) {
if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
continue;
- (void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
- if (WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
+ if (hrtimer_cancel(&rtorsup->rtorsu_hrt) || WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
rcu_torture_one_read_end(&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);
rtorsup->rtorsu_nups++;
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 5/9] rcutorture: Add tests for SRCU up/down reader primitives
2025-03-27 16:48 ` Paul E. McKenney
@ 2025-03-27 17:08 ` Joel Fernandes
2025-03-27 17:18 ` Paul E. McKenney
0 siblings, 1 reply; 17+ messages in thread
From: Joel Fernandes @ 2025-03-27 17:08 UTC (permalink / raw)
To: paulmck; +Cc: qiang Z, rcu, linux-kernel, kernel-team, rostedt
> On Mar 27, 2025, at 12:48 PM, Paul E. McKenney <paulmck@kernel.org> wrote:
>
> On Thu, Mar 27, 2025 at 12:22:12PM -0400, Joel Fernandes wrote:
>> Paul,
>>
>>>> If rtorsu_hrt timer is still in timer_queue, invoke hrtimer_cancel() will
>>>> remove it from timerqueue and directly return, so the rcu_torture_updown_hrt()
>>>> will not be executed and the rtorsup->rtorsu_inuse cannot be set false.
>>>>
>>>> How about modifying it as follows:
>>>>
>>>> diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
>>>> index 04d7a2173b95..ecf3d3797f7e 100644
>>>> --- a/kernel/rcu/rcutorture.c
>>>> +++ b/kernel/rcu/rcutorture.c
>>>> @@ -2502,8 +2502,7 @@ static void rcu_torture_updown_cleanup(void)
>>>> for (rtorsup = updownreaders; rtorsup <
>>>> &updownreaders[n_up_down]; rtorsup++) {
>>>> if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
>>>> continue;
>>>> - (void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
>>>> - if (WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
>>>> + if (hrtimer_cancel(&rtorsup->rtorsu_hrt) ||
>>>> WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
>>>>
>>>> rcu_torture_one_read_end(&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);
>>>> rtorsup->rtorsu_nups++;
>>>
>>> Good eyes, thank you! I have applied this fix with attribution.
>>
>> Could you re-send the series, or should I apply the fix the patch myself? Or
>> provide the new patch inline here.
>
> Your choice, just let me know. If you have modified any of the other
> patches in that series, it will probably be easier for you if I either
> resend just that one patch or if you apply the changes. If you haven't
> done any modifications, it might be easier for you if I re-sent the
> series.
Thanks, you could resend as I have not changed other patches.
- Joel
>
> I have the delta patch below, which I have pushed out for kernel test
> robot ministrations and which I expect to merge into the original
> later today.
>
> Thanx, Paul
>
> ------------------------------------------------------------------------
>
> commit 55fcac5cb3fc96479d935db648c98503cb0a944b
> Author: Paul E. McKenney <paulmck@kernel.org>
> Date: Thu Mar 27 07:29:48 2025 -0700
>
> squash! rcutorture: Add tests for SRCU up/down reader primitives
>
> [ paulmck: Apply Z qiang feedback. ]
>
> Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
>
> diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
> index 04d7a2173b95d..ecf3d3797f7e1 100644
> --- a/kernel/rcu/rcutorture.c
> +++ b/kernel/rcu/rcutorture.c
> @@ -2502,8 +2502,7 @@ static void rcu_torture_updown_cleanup(void)
> for (rtorsup = updownreaders; rtorsup < &updownreaders[n_up_down]; rtorsup++) {
> if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
> continue;
> - (void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
> - if (WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
> + if (hrtimer_cancel(&rtorsup->rtorsu_hrt) || WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
> rcu_torture_one_read_end(&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);
> rtorsup->rtorsu_nups++;
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 5/9] rcutorture: Add tests for SRCU up/down reader primitives
2025-03-27 17:08 ` Joel Fernandes
@ 2025-03-27 17:18 ` Paul E. McKenney
0 siblings, 0 replies; 17+ messages in thread
From: Paul E. McKenney @ 2025-03-27 17:18 UTC (permalink / raw)
To: Joel Fernandes; +Cc: qiang Z, rcu, linux-kernel, kernel-team, rostedt
On Thu, Mar 27, 2025 at 05:08:35PM +0000, Joel Fernandes wrote:
>
>
> > On Mar 27, 2025, at 12:48 PM, Paul E. McKenney <paulmck@kernel.org> wrote:
> >
> > On Thu, Mar 27, 2025 at 12:22:12PM -0400, Joel Fernandes wrote:
> >> Paul,
> >>
> >>>> If rtorsu_hrt timer is still in timer_queue, invoke hrtimer_cancel() will
> >>>> remove it from timerqueue and directly return, so the rcu_torture_updown_hrt()
> >>>> will not be executed and the rtorsup->rtorsu_inuse cannot be set false.
> >>>>
> >>>> How about modifying it as follows:
> >>>>
> >>>> diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
> >>>> index 04d7a2173b95..ecf3d3797f7e 100644
> >>>> --- a/kernel/rcu/rcutorture.c
> >>>> +++ b/kernel/rcu/rcutorture.c
> >>>> @@ -2502,8 +2502,7 @@ static void rcu_torture_updown_cleanup(void)
> >>>> for (rtorsup = updownreaders; rtorsup <
> >>>> &updownreaders[n_up_down]; rtorsup++) {
> >>>> if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
> >>>> continue;
> >>>> - (void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
> >>>> - if (WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
> >>>> + if (hrtimer_cancel(&rtorsup->rtorsu_hrt) ||
> >>>> WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
> >>>>
> >>>> rcu_torture_one_read_end(&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);
> >>>> rtorsup->rtorsu_nups++;
> >>>
> >>> Good eyes, thank you! I have applied this fix with attribution.
> >>
> >> Could you re-send the series, or should I apply the fix the patch myself? Or
> >> provide the new patch inline here.
> >
> > Your choice, just let me know. If you have modified any of the other
> > patches in that series, it will probably be easier for you if I either
> > resend just that one patch or if you apply the changes. If you haven't
> > done any modifications, it might be easier for you if I re-sent the
> > series.
>
> Thanks, you could resend as I have not changed other patches.
Will do! I expect to get that to you by end of this coming Monday
at the latest.
Thanx, Paul
> - Joel
>
> >
> > I have the delta patch below, which I have pushed out for kernel test
> > robot ministrations and which I expect to merge into the original
> > later today.
> >
> > Thanx, Paul
> >
> > ------------------------------------------------------------------------
> >
> > commit 55fcac5cb3fc96479d935db648c98503cb0a944b
> > Author: Paul E. McKenney <paulmck@kernel.org>
> > Date: Thu Mar 27 07:29:48 2025 -0700
> >
> > squash! rcutorture: Add tests for SRCU up/down reader primitives
> >
> > [ paulmck: Apply Z qiang feedback. ]
> >
> > Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
> >
> > diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
> > index 04d7a2173b95d..ecf3d3797f7e1 100644
> > --- a/kernel/rcu/rcutorture.c
> > +++ b/kernel/rcu/rcutorture.c
> > @@ -2502,8 +2502,7 @@ static void rcu_torture_updown_cleanup(void)
> > for (rtorsup = updownreaders; rtorsup < &updownreaders[n_up_down]; rtorsup++) {
> > if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
> > continue;
> > - (void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
> > - if (WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
> > + if (hrtimer_cancel(&rtorsup->rtorsu_hrt) || WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
> > rcu_torture_one_read_end(&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);
> > rtorsup->rtorsu_nups++;
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 6/9] rcutorture: Pull rcu_torture_updown() loop body into new function
2025-03-10 18:38 [PATCH 0/9] RCU torture-test changes for v6.16 Paul E. McKenney
` (4 preceding siblings ...)
2025-03-10 18:38 ` [PATCH 5/9] rcutorture: Add tests for SRCU up/down reader primitives Paul E. McKenney
@ 2025-03-10 18:38 ` Paul E. McKenney
2025-03-10 18:38 ` [PATCH 7/9] rcutorture: Comment invocations of tick_dep_set_task() Paul E. McKenney
` (3 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Paul E. McKenney @ 2025-03-10 18:38 UTC (permalink / raw)
To: rcu; +Cc: linux-kernel, kernel-team, rostedt, Paul E. McKenney
This is strictly a code-movement commit, pulling that part of
the rcu_torture_updown() function's loop body that processes
one rcu_torture_one_read_state_updown structure into a new
rcu_torture_updown_one() function. The checks for the end of the
torture test and the current structure being in use remain in the
rcu_torture_updown() function.
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
---
kernel/rcu/rcutorture.c | 46 ++++++++++++++++++++++-------------------
1 file changed, 25 insertions(+), 21 deletions(-)
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index 6afcd33e724ba..6d808c70349c4 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -2505,6 +2505,30 @@ static void rcu_torture_updown_cleanup(void)
updownreaders = NULL;
}
+// Do one reader for rcu_torture_updown().
+static void 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();
+ idx = (rawidx << RCUTORTURE_RDR_SHIFT_1) & RCUTORTURE_RDR_MASK_1;
+ rtorsup->rtorsu_rtors.readstate = idx | RCUTORTURE_RDR_UPDOWN;
+ rtorsup->rtorsu_rtors.rtrsp++;
+ if (!rcu_torture_one_read_start(&rtorsup->rtorsu_rtors, &rtorsup->rtorsu_trs, -1)) {
+ cur_ops->up_read(rawidx);
+ 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_SOFT);
+}
+
/*
* RCU torture up/down reader kthread, starting RCU readers in kthread
* context and ending them in hrtimer handlers. Otherwise similar to
@@ -2513,10 +2537,7 @@ static void rcu_torture_updown_cleanup(void)
static int
rcu_torture_updown(void *arg)
{
- int idx;
- int rawidx;
struct rcu_torture_one_read_state_updown *rtorsup;
- ktime_t t;
VERBOSE_TOROUT_STRING("rcu_torture_updown task started");
do {
@@ -2525,24 +2546,7 @@ rcu_torture_updown(void *arg)
break;
if (smp_load_acquire(&rtorsup->rtorsu_inuse))
continue;
- init_rcu_torture_one_read_state(&rtorsup->rtorsu_rtors,
- &rtorsup->rtorsu_trs);
- rawidx = cur_ops->down_read();
- idx = (rawidx << RCUTORTURE_RDR_SHIFT_1) & RCUTORTURE_RDR_MASK_1;
- rtorsup->rtorsu_rtors.readstate = idx | RCUTORTURE_RDR_UPDOWN;
- rtorsup->rtorsu_rtors.rtrsp++;
- if (!rcu_torture_one_read_start(&rtorsup->rtorsu_rtors,
- &rtorsup->rtorsu_trs, -1)) {
- cur_ops->up_read(rawidx);
- schedule_timeout_idle(HZ);
- continue;
- }
- 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_SOFT);
+ rcu_torture_updown_one(rtorsup);
}
torture_hrtimeout_ms(1, 1000, &rcu_torture_updown_rand);
stutter_wait("rcu_torture_updown");
--
2.40.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 7/9] rcutorture: Comment invocations of tick_dep_set_task()
2025-03-10 18:38 [PATCH 0/9] RCU torture-test changes for v6.16 Paul E. McKenney
` (5 preceding siblings ...)
2025-03-10 18:38 ` [PATCH 6/9] rcutorture: Pull rcu_torture_updown() loop body into new function Paul E. McKenney
@ 2025-03-10 18:38 ` Paul E. McKenney
2025-03-10 18:38 ` [PATCH 8/9] rcutorture: Complain if an ->up_read() is delayed more than 10 seconds Paul E. McKenney
` (2 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Paul E. McKenney @ 2025-03-10 18:38 UTC (permalink / raw)
To: rcu; +Cc: linux-kernel, kernel-team, rostedt, Paul E. McKenney
The rcu_torture_reader() and rcu_torture_fwd_prog_cr() functions
run CPU-bound for extended periods of time (tens or even
hundreds of milliseconds), so they invoke tick_dep_set_task() and
tick_dep_clear_task() to ensure that the scheduling-clock tick helps
move grace periods forward.
So why doesn't rcu_torture_fwd_prog_nr() also invoke tick_dep_set_task()
and tick_dep_clear_task()? Because the point of this function is to test
RCU's ability to (eventually) force grace periods forward even when the
tick has been disabled during long CPU-bound kernel execution.
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
---
kernel/rcu/rcutorture.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index 6d808c70349c4..3042a7950fe23 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -2410,7 +2410,7 @@ rcu_torture_reader(void *arg)
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);
+ 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))
@@ -3260,7 +3260,7 @@ static void rcu_torture_fwd_prog_cr(struct rcu_fwd *rfp)
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);
+ 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()) {
--
2.40.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 8/9] rcutorture: Complain if an ->up_read() is delayed more than 10 seconds
2025-03-10 18:38 [PATCH 0/9] RCU torture-test changes for v6.16 Paul E. McKenney
` (6 preceding siblings ...)
2025-03-10 18:38 ` [PATCH 7/9] rcutorture: Comment invocations of tick_dep_set_task() Paul E. McKenney
@ 2025-03-10 18:38 ` Paul E. McKenney
2025-03-10 18:38 ` [PATCH 9/9] rcutorture: Check for ->up_read() without matching ->down_read() Paul E. McKenney
2025-03-11 2:24 ` [PATCH 0/9] RCU torture-test changes for v6.16 Joel Fernandes
9 siblings, 0 replies; 17+ messages in thread
From: Paul E. McKenney @ 2025-03-10 18:38 UTC (permalink / raw)
To: rcu; +Cc: linux-kernel, kernel-team, rostedt, Paul E. McKenney
The down/up SRCU reader testing uses an hrtimer handler to exit the SRCU
read-side critical section. This might be delayed, and if delayed for
too long, it can prevent the rcutorture run from completing. This commit
therefore complains if the hrtimer handler is delayed for more than
ten seconds.
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
---
kernel/rcu/rcutorture.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index 3042a7950fe23..fcdd6271f435c 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -2438,6 +2438,7 @@ rcu_torture_reader(void *arg)
struct rcu_torture_one_read_state_updown {
struct hrtimer rtorsu_hrt;
bool rtorsu_inuse;
+ unsigned long rtorsu_j;
struct torture_random_state rtorsu_trs;
struct rcu_torture_one_read_state rtorsu_rtors;
};
@@ -2522,6 +2523,7 @@ static void rcu_torture_updown_one(struct rcu_torture_one_read_state_updown *rto
schedule_timeout_idle(HZ);
return;
}
+ rtorsup->rtorsu_j = jiffies;
smp_store_release(&rtorsup->rtorsu_inuse, true);
t = torture_random(&rtorsup->rtorsu_trs) & 0xfffff; // One per million.
if (t < 10 * 1000)
@@ -2544,8 +2546,10 @@ rcu_torture_updown(void *arg)
for (rtorsup = updownreaders; rtorsup < &updownreaders[n_up_down]; rtorsup++) {
if (torture_must_stop())
break;
- if (smp_load_acquire(&rtorsup->rtorsu_inuse))
+ if (smp_load_acquire(&rtorsup->rtorsu_inuse)) {
+ WARN_ON_ONCE(time_after(jiffies, rtorsup->rtorsu_j + 10 * HZ));
continue;
+ }
rcu_torture_updown_one(rtorsup);
}
torture_hrtimeout_ms(1, 1000, &rcu_torture_updown_rand);
--
2.40.1
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH 9/9] rcutorture: Check for ->up_read() without matching ->down_read()
2025-03-10 18:38 [PATCH 0/9] RCU torture-test changes for v6.16 Paul E. McKenney
` (7 preceding siblings ...)
2025-03-10 18:38 ` [PATCH 8/9] rcutorture: Complain if an ->up_read() is delayed more than 10 seconds Paul E. McKenney
@ 2025-03-10 18:38 ` Paul E. McKenney
2025-03-11 2:24 ` [PATCH 0/9] RCU torture-test changes for v6.16 Joel Fernandes
9 siblings, 0 replies; 17+ messages in thread
From: Paul E. McKenney @ 2025-03-10 18:38 UTC (permalink / raw)
To: rcu
Cc: linux-kernel, kernel-team, rostedt, Paul E. McKenney, kernel test robot
This commit creates counters in the rcu_torture_one_read_state_updown
structure that check for a call to ->up_read() that lacks a matching
call to ->down_read().
While in the area, add end-of-run cleanup code that prevents calls to
rcu_torture_updown_hrt() from happening after the test has moved on. Yes,
the srcu_barrier() at the end of the test will wait for them, but this
could result in confusing states, statistics, and diagnostic information.
So explicitly wait for them before we get to the end-of-test output.
[ paulmck: Apply kernel test robot feedback. ]
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
Tested-by: kernel test robot <oliver.sang@intel.com>
---
kernel/rcu/rcutorture.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index fcdd6271f435c..a0786b3c223be 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -2439,6 +2439,8 @@ struct rcu_torture_one_read_state_updown {
struct hrtimer rtorsu_hrt;
bool rtorsu_inuse;
unsigned long rtorsu_j;
+ unsigned long rtorsu_ndowns;
+ unsigned long rtorsu_nups;
struct torture_random_state rtorsu_trs;
struct rcu_torture_one_read_state rtorsu_rtors;
};
@@ -2453,6 +2455,8 @@ static enum hrtimer_restart rcu_torture_updown_hrt(struct hrtimer *hrtp)
rtorsup = container_of(hrtp, struct rcu_torture_one_read_state_updown, rtorsu_hrt);
rcu_torture_one_read_end(&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);
+ rtorsup->rtorsu_nups++;
smp_store_release(&rtorsup->rtorsu_inuse, false);
return HRTIMER_NORESTART;
}
@@ -2499,7 +2503,12 @@ static void rcu_torture_updown_cleanup(void)
if (!smp_load_acquire(&rtorsup->rtorsu_inuse))
continue;
(void)hrtimer_cancel(&rtorsup->rtorsu_hrt);
- WARN_ON_ONCE(rtorsup->rtorsu_inuse);
+ if (WARN_ON_ONCE(rtorsup->rtorsu_inuse)) {
+ rcu_torture_one_read_end(&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);
+ rtorsup->rtorsu_nups++;
+ smp_store_release(&rtorsup->rtorsu_inuse, false);
+ }
}
kfree(updownreaders);
@@ -2515,11 +2524,14 @@ static void rcu_torture_updown_one(struct rcu_torture_one_read_state_updown *rto
init_rcu_torture_one_read_state(&rtorsup->rtorsu_rtors, &rtorsup->rtorsu_trs);
rawidx = cur_ops->down_read();
+ rtorsup->rtorsu_ndowns++;
idx = (rawidx << RCUTORTURE_RDR_SHIFT_1) & RCUTORTURE_RDR_MASK_1;
rtorsup->rtorsu_rtors.readstate = idx | RCUTORTURE_RDR_UPDOWN;
rtorsup->rtorsu_rtors.rtrsp++;
if (!rcu_torture_one_read_start(&rtorsup->rtorsu_rtors, &rtorsup->rtorsu_trs, -1)) {
cur_ops->up_read(rawidx);
+ rtorsup->rtorsu_nups++;
+ WARN_ONCE(rtorsup->rtorsu_nups >= rtorsup->rtorsu_ndowns, "%s: Up without matching down #%zu.\n", __func__, rtorsup - updownreaders);
schedule_timeout_idle(HZ);
return;
}
--
2.40.1
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 0/9] RCU torture-test changes for v6.16
2025-03-10 18:38 [PATCH 0/9] RCU torture-test changes for v6.16 Paul E. McKenney
` (8 preceding siblings ...)
2025-03-10 18:38 ` [PATCH 9/9] rcutorture: Check for ->up_read() without matching ->down_read() Paul E. McKenney
@ 2025-03-11 2:24 ` Joel Fernandes
9 siblings, 0 replies; 17+ messages in thread
From: Joel Fernandes @ 2025-03-11 2:24 UTC (permalink / raw)
To: Paul E. McKenney; +Cc: rcu, linux-kernel, kernel-team, rostedt
On Mon, Mar 10, 2025 at 11:38:02AM -0700, Paul E. McKenney wrote:
> Hello!
>
> The following series improves testing of SRCU-fast and SRCU up/down:
>
> 1. Make srcu_lockdep.sh check kernel Kconfig.
>
> 2. Make srcu_lockdep.sh check reader-conflict handling.
>
> 3. Split out beginning and end from rcu_torture_one_read().
>
> 4. Make torture.sh --do-rt use CONFIG_PREEMPT_RT.
>
> 5. Add tests for SRCU up/down reader primitives.
>
> 6. Pull rcu_torture_updown() loop body into new function.
>
> 7. Comment invocations of tick_dep_set_task().
>
> 8. Complain if an ->up_read() is delayed more than 10 seconds.
>
> 9. Check for ->up_read() without matching ->down_read().
Will queue for 6.16 on my side, for further testing and review.
thanks,
- Joel
^ permalink raw reply [flat|nested] 17+ messages in thread