rcutorture: Add SRCU deadlock scenarios

In order to test the new SRCU-lockdep functionality, this commit adds
an rcutorture.test_srcu_lockdep module parameter that, when non-zero,
selects an SRCU deadlock scenario to execute.  This parameter is a
five-digit number formatted as DNNL, where "D" is 1 to force a deadlock
and 0 to avoid doing so; "NN" is the test number, 0 for SRCU-based, 1
for SRCU/mutex-based, and 2 for SRCU/rwsem-based; and "L" is the number
of steps in the deadlock cycle.

Note that rcutorture.test_srcu_lockdep=1 will also force a hard hang.

If a non-zero value of rcutorture.test_srcu_lockdep does not select a
deadlock scenario, a console message is printed and testing continues.

[ paulmck: Apply kernel test robot feedback, add rwsem support. ]
[ paulmck: Apply Dan Carpenter feedback. ]

Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
This commit is contained in:
Paul E. McKenney 2023-01-13 21:34:27 -08:00 committed by Boqun Feng
parent 0471db447c
commit d94f12e8a4
1 changed files with 151 additions and 0 deletions

View File

@ -120,6 +120,7 @@ torture_param(int, test_boost, 1, "Test RCU prio boost: 0=no, 1=maybe, 2=yes.");
torture_param(int, test_boost_duration, 4, "Duration of each boost test, seconds.");
torture_param(int, test_boost_interval, 7, "Interval between boost tests, seconds.");
torture_param(bool, test_no_idle_hz, true, "Test support for tickless idle CPUs");
torture_param(int, test_srcu_lockdep, 0, "Test specified SRCU deadlock scenario.");
torture_param(int, verbose, 1, "Enable verbose debugging printk()s");
static char *torture_type = "rcu";
@ -3463,6 +3464,154 @@ static void rcutorture_sync(void)
cur_ops->sync();
}
static DEFINE_MUTEX(mut0);
static DEFINE_MUTEX(mut1);
static DEFINE_MUTEX(mut2);
static DEFINE_MUTEX(mut3);
static DEFINE_MUTEX(mut4);
static DEFINE_MUTEX(mut5);
static DEFINE_MUTEX(mut6);
static DEFINE_MUTEX(mut7);
static DEFINE_MUTEX(mut8);
static DEFINE_MUTEX(mut9);
static DECLARE_RWSEM(rwsem0);
static DECLARE_RWSEM(rwsem1);
static DECLARE_RWSEM(rwsem2);
static DECLARE_RWSEM(rwsem3);
static DECLARE_RWSEM(rwsem4);
static DECLARE_RWSEM(rwsem5);
static DECLARE_RWSEM(rwsem6);
static DECLARE_RWSEM(rwsem7);
static DECLARE_RWSEM(rwsem8);
static DECLARE_RWSEM(rwsem9);
DEFINE_STATIC_SRCU(srcu0);
DEFINE_STATIC_SRCU(srcu1);
DEFINE_STATIC_SRCU(srcu2);
DEFINE_STATIC_SRCU(srcu3);
DEFINE_STATIC_SRCU(srcu4);
DEFINE_STATIC_SRCU(srcu5);
DEFINE_STATIC_SRCU(srcu6);
DEFINE_STATIC_SRCU(srcu7);
DEFINE_STATIC_SRCU(srcu8);
DEFINE_STATIC_SRCU(srcu9);
static int srcu_lockdep_next(const char *f, const char *fl, const char *fs, const char *fu, int i,
int cyclelen, int deadlock)
{
int j = i + 1;
if (j >= cyclelen)
j = deadlock ? 0 : -1;
if (j >= 0)
pr_info("%s: %s(%d), %s(%d), %s(%d)\n", f, fl, i, fs, j, fu, i);
else
pr_info("%s: %s(%d), %s(%d)\n", f, fl, i, fu, i);
return j;
}
// Test lockdep on SRCU-based deadlock scenarios.
static void rcu_torture_init_srcu_lockdep(void)
{
int cyclelen;
int deadlock;
bool err = false;
int i;
int j;
int idx;
struct mutex *muts[] = { &mut0, &mut1, &mut2, &mut3, &mut4,
&mut5, &mut6, &mut7, &mut8, &mut9 };
struct rw_semaphore *rwsems[] = { &rwsem0, &rwsem1, &rwsem2, &rwsem3, &rwsem4,
&rwsem5, &rwsem6, &rwsem7, &rwsem8, &rwsem9 };
struct srcu_struct *srcus[] = { &srcu0, &srcu1, &srcu2, &srcu3, &srcu4,
&srcu5, &srcu6, &srcu7, &srcu8, &srcu9 };
int testtype;
if (!test_srcu_lockdep)
return;
deadlock = test_srcu_lockdep / 1000;
testtype = (test_srcu_lockdep / 10) % 100;
cyclelen = test_srcu_lockdep % 10;
WARN_ON_ONCE(ARRAY_SIZE(muts) != ARRAY_SIZE(srcus));
if (WARN_ONCE(deadlock != !!deadlock,
"%s: test_srcu_lockdep=%d and deadlock digit %d must be zero or one.\n",
__func__, test_srcu_lockdep, deadlock))
err = true;
if (WARN_ONCE(cyclelen <= 0,
"%s: test_srcu_lockdep=%d and cycle-length digit %d must be greater than zero.\n",
__func__, test_srcu_lockdep, cyclelen))
err = true;
if (err)
goto err_out;
if (testtype == 0) {
pr_info("%s: test_srcu_lockdep = %05d: SRCU %d-way %sdeadlock.\n",
__func__, test_srcu_lockdep, cyclelen, deadlock ? "" : "non-");
if (deadlock && cyclelen == 1)
pr_info("%s: Expect hang.\n", __func__);
for (i = 0; i < cyclelen; i++) {
j = srcu_lockdep_next(__func__, "srcu_read_lock", "synchronize_srcu",
"srcu_read_unlock", i, cyclelen, deadlock);
idx = srcu_read_lock(srcus[i]);
if (j >= 0)
synchronize_srcu(srcus[j]);
srcu_read_unlock(srcus[i], idx);
}
return;
}
if (testtype == 1) {
pr_info("%s: test_srcu_lockdep = %05d: SRCU/mutex %d-way %sdeadlock.\n",
__func__, test_srcu_lockdep, cyclelen, deadlock ? "" : "non-");
for (i = 0; i < cyclelen; i++) {
pr_info("%s: srcu_read_lock(%d), mutex_lock(%d), mutex_unlock(%d), srcu_read_unlock(%d)\n",
__func__, i, i, i, i);
idx = srcu_read_lock(srcus[i]);
mutex_lock(muts[i]);
mutex_unlock(muts[i]);
srcu_read_unlock(srcus[i], idx);
j = srcu_lockdep_next(__func__, "mutex_lock", "synchronize_srcu",
"mutex_unlock", i, cyclelen, deadlock);
mutex_lock(muts[i]);
if (j >= 0)
synchronize_srcu(srcus[j]);
mutex_unlock(muts[i]);
}
return;
}
if (testtype == 2) {
pr_info("%s: test_srcu_lockdep = %05d: SRCU/rwsem %d-way %sdeadlock.\n",
__func__, test_srcu_lockdep, cyclelen, deadlock ? "" : "non-");
for (i = 0; i < cyclelen; i++) {
pr_info("%s: srcu_read_lock(%d), down_read(%d), up_read(%d), srcu_read_unlock(%d)\n",
__func__, i, i, i, i);
idx = srcu_read_lock(srcus[i]);
down_read(rwsems[i]);
up_read(rwsems[i]);
srcu_read_unlock(srcus[i], idx);
j = srcu_lockdep_next(__func__, "down_write", "synchronize_srcu",
"up_write", i, cyclelen, deadlock);
down_write(rwsems[i]);
if (j >= 0)
synchronize_srcu(srcus[j]);
up_write(rwsems[i]);
}
return;
}
err_out:
pr_info("%s: test_srcu_lockdep = %05d does nothing.\n", __func__, test_srcu_lockdep);
pr_info("%s: test_srcu_lockdep = DNNL.\n", __func__);
pr_info("%s: D: Deadlock if nonzero.\n", __func__);
pr_info("%s: NN: Test number, 0=SRCU, 1=SRCU/mutex, 2=SRCU/rwsem.\n", __func__);
pr_info("%s: L: Cycle length.\n", __func__);
}
static int __init
rcu_torture_init(void)
{
@ -3504,6 +3653,8 @@ rcu_torture_init(void)
if (cur_ops->init)
cur_ops->init();
rcu_torture_init_srcu_lockdep();
if (nreaders >= 0) {
nrealreaders = nreaders;
} else {