Commit 43b39caf authored by Zqiang's avatar Zqiang Committed by Paul E. McKenney
Browse files

rcutorture: Make rcutorture support srcu double call test

This commit allows rcutorture to test double-call_srcu() when the
CONFIG_DEBUG_OBJECTS_RCU_HEAD Kconfig option is enabled.  The non-raw
sdp structure's ->spinlock will be acquired in call_srcu(), hence this
commit also removes the current IRQ and preemption disabling so as to
avoid lockdep complaints.

Link: https://lore.kernel.org/all/20240407112714.24460-1-qiang.zhang1211@gmail.com/



Signed-off-by: default avatarZqiang <qiang.zhang1211@gmail.com>
Signed-off-by: default avatarPaul E. McKenney <paulmck@kernel.org>
parent 1613e604
Loading
Loading
Loading
Loading
+26 −20
Original line number Diff line number Diff line
@@ -390,6 +390,7 @@ struct rcu_torture_ops {
	int extendables;
	int slow_gps;
	int no_pi_lock;
	int debug_objects;
	const char *name;
};

@@ -577,6 +578,7 @@ static struct rcu_torture_ops rcu_ops = {
	.irq_capable		= 1,
	.can_boost		= IS_ENABLED(CONFIG_RCU_BOOST),
	.extendables		= RCUTORTURE_MAX_EXTEND,
	.debug_objects		= 1,
	.name			= "rcu"
};

@@ -747,6 +749,7 @@ static struct rcu_torture_ops srcu_ops = {
	.cbflood_max	= 50000,
	.irq_capable	= 1,
	.no_pi_lock	= IS_ENABLED(CONFIG_TINY_SRCU),
	.debug_objects	= 1,
	.name		= "srcu"
};

@@ -786,6 +789,7 @@ static struct rcu_torture_ops srcud_ops = {
	.cbflood_max	= 50000,
	.irq_capable	= 1,
	.no_pi_lock	= IS_ENABLED(CONFIG_TINY_SRCU),
	.debug_objects	= 1,
	.name		= "srcud"
};

@@ -3455,7 +3459,6 @@ rcu_torture_cleanup(void)
		cur_ops->gp_slow_unregister(NULL);
}

#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD
static void rcu_torture_leak_cb(struct rcu_head *rhp)
{
}
@@ -3473,7 +3476,6 @@ static void rcu_torture_err_cb(struct rcu_head *rhp)
	 */
	pr_alert("%s: duplicated callback was invoked.\n", KBUILD_MODNAME);
}
#endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */

/*
 * Verify that double-free causes debug-objects to complain, but only
@@ -3482,39 +3484,43 @@ static void rcu_torture_err_cb(struct rcu_head *rhp)
 */
static void rcu_test_debug_objects(void)
{
#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD
	struct rcu_head rh1;
	struct rcu_head rh2;
	int idx;

	if (!IS_ENABLED(CONFIG_DEBUG_OBJECTS_RCU_HEAD)) {
		pr_alert("%s: !CONFIG_DEBUG_OBJECTS_RCU_HEAD, not testing duplicate call_%s()\n",
					KBUILD_MODNAME, cur_ops->name);
		return;
	}

	if (WARN_ON_ONCE(cur_ops->debug_objects &&
			(!cur_ops->call || !cur_ops->cb_barrier)))
		return;

	struct rcu_head *rhp = kmalloc(sizeof(*rhp), GFP_KERNEL);

	init_rcu_head_on_stack(&rh1);
	init_rcu_head_on_stack(&rh2);
	pr_alert("%s: WARN: Duplicate call_rcu() test starting.\n", KBUILD_MODNAME);
	pr_alert("%s: WARN: Duplicate call_%s() test starting.\n", KBUILD_MODNAME, cur_ops->name);

	/* Try to queue the rh2 pair of callbacks for the same grace period. */
	preempt_disable(); /* Prevent preemption from interrupting test. */
	rcu_read_lock(); /* Make it impossible to finish a grace period. */
	call_rcu_hurry(&rh1, rcu_torture_leak_cb); /* Start grace period. */
	local_irq_disable(); /* Make it harder to start a new grace period. */
	call_rcu_hurry(&rh2, rcu_torture_leak_cb);
	call_rcu_hurry(&rh2, rcu_torture_err_cb); /* Duplicate callback. */
	idx = cur_ops->readlock(); /* Make it impossible to finish a grace period. */
	cur_ops->call(&rh1, rcu_torture_leak_cb); /* Start grace period. */
	cur_ops->call(&rh2, rcu_torture_leak_cb);
	cur_ops->call(&rh2, rcu_torture_err_cb); /* Duplicate callback. */
	if (rhp) {
		call_rcu_hurry(rhp, rcu_torture_leak_cb);
		call_rcu_hurry(rhp, rcu_torture_err_cb); /* Another duplicate callback. */
		cur_ops->call(rhp, rcu_torture_leak_cb);
		cur_ops->call(rhp, rcu_torture_err_cb); /* Another duplicate callback. */
	}
	local_irq_enable();
	rcu_read_unlock();
	preempt_enable();
	cur_ops->readunlock(idx);

	/* Wait for them all to get done so we can safely return. */
	rcu_barrier();
	pr_alert("%s: WARN: Duplicate call_rcu() test complete.\n", KBUILD_MODNAME);
	cur_ops->cb_barrier();
	pr_alert("%s: WARN: Duplicate call_%s() test complete.\n", KBUILD_MODNAME, cur_ops->name);
	destroy_rcu_head_on_stack(&rh1);
	destroy_rcu_head_on_stack(&rh2);
	kfree(rhp);
#else /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */
	pr_alert("%s: !CONFIG_DEBUG_OBJECTS_RCU_HEAD, not testing duplicate call_rcu()\n", KBUILD_MODNAME);
#endif /* #else #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */
}

static void rcutorture_sync(void)