Commit d94f12e8 authored by Paul E. McKenney's avatar Paul E. McKenney Committed by Boqun Feng
Browse files

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: default avatarPaul E. McKenney <paulmck@kernel.org>
Acked-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarBoqun Feng <boqun.feng@gmail.com>
parent 0471db44
Loading
Loading
Loading
Loading
+151 −0
Original line number Diff line number Diff line
@@ -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 {