Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a48f3fad authored by Paul E. McKenney's avatar Paul E. McKenney
Browse files

rcutorture: Add tests for get_state_synchronize_rcu()



This commit adds rcutorture testing for get_state_synchronize_rcu()
and cond_synchronize_rcu().

Signed-off-by: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Reviewed-by: default avatarJosh Triplett <josh@joshtriplett.org>
parent e8d07a4e
Loading
Loading
Loading
Loading
+95 −35
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ torture_param(int, fqs_duration, 0,
	      "Duration of fqs bursts (us), 0 to disable");
torture_param(int, fqs_holdoff, 0, "Holdoff time within fqs bursts (us)");
torture_param(int, fqs_stutter, 3, "Wait time between fqs bursts (s)");
torture_param(bool, gp_cond, false, "Use conditional/async GP wait primitives");
torture_param(bool, gp_exp, false, "Use expedited GP wait primitives");
torture_param(bool, gp_normal, false,
	     "Use normal (non-expedited) GP wait primitives");
@@ -144,8 +145,10 @@ static int rcu_torture_writer_state;
#define RTWS_REPLACE		2
#define RTWS_DEF_FREE		3
#define RTWS_EXP_SYNC		4
#define RTWS_STUTTER		5
#define RTWS_STOPPING		6
#define RTWS_COND_GET		5
#define RTWS_COND_SYNC		6
#define RTWS_STUTTER		7
#define RTWS_STOPPING		8

#if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE)
#define RCUTORTURE_RUNNABLE_INIT 1
@@ -232,6 +235,8 @@ struct rcu_torture_ops {
	void (*deferred_free)(struct rcu_torture *p);
	void (*sync)(void);
	void (*exp_sync)(void);
	unsigned long (*get_state)(void);
	void (*cond_sync)(unsigned long oldstate);
	void (*call)(struct rcu_head *head, void (*func)(struct rcu_head *rcu));
	void (*cb_barrier)(void);
	void (*fqs)(void);
@@ -283,10 +288,48 @@ static int rcu_torture_completed(void)
	return rcu_batches_completed();
}

/*
 * Update callback in the pipe.  This should be invoked after a grace period.
 */
static bool
rcu_torture_pipe_update_one(struct rcu_torture *rp)
{
	int i;

	i = rp->rtort_pipe_count;
	if (i > RCU_TORTURE_PIPE_LEN)
		i = RCU_TORTURE_PIPE_LEN;
	atomic_inc(&rcu_torture_wcount[i]);
	if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) {
		rp->rtort_mbtest = 0;
		return true;
	}
	return false;
}

/*
 * Update all callbacks in the pipe.  Suitable for synchronous grace-period
 * primitives.
 */
static void
rcu_torture_pipe_update(struct rcu_torture *old_rp)
{
	struct rcu_torture *rp;
	struct rcu_torture *rp1;

	if (old_rp)
		list_add(&old_rp->rtort_free, &rcu_torture_removed);
	list_for_each_entry_safe(rp, rp1, &rcu_torture_removed, rtort_free) {
		if (rcu_torture_pipe_update_one(rp)) {
			list_del(&rp->rtort_free);
			rcu_torture_free(rp);
		}
	}
}

static void
rcu_torture_cb(struct rcu_head *p)
{
	int i;
	struct rcu_torture *rp = container_of(p, struct rcu_torture, rtort_rcu);

	if (torture_must_stop_irq()) {
@@ -294,17 +337,11 @@ rcu_torture_cb(struct rcu_head *p)
		/* The next initialization will pick up the pieces. */
		return;
	}
	i = rp->rtort_pipe_count;
	if (i > RCU_TORTURE_PIPE_LEN)
		i = RCU_TORTURE_PIPE_LEN;
	atomic_inc(&rcu_torture_wcount[i]);
	if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) {
		rp->rtort_mbtest = 0;
	if (rcu_torture_pipe_update_one(rp))
		rcu_torture_free(rp);
	} else {
	else
		cur_ops->deferred_free(rp);
}
}

static int rcu_no_completed(void)
{
@@ -331,6 +368,8 @@ static struct rcu_torture_ops rcu_ops = {
	.deferred_free	= rcu_torture_deferred_free,
	.sync		= synchronize_rcu,
	.exp_sync	= synchronize_rcu_expedited,
	.get_state	= get_state_synchronize_rcu,
	.cond_sync	= cond_synchronize_rcu,
	.call		= call_rcu,
	.cb_barrier	= rcu_barrier,
	.fqs		= rcu_force_quiescent_state,
@@ -705,16 +744,39 @@ rcu_torture_fqs(void *arg)
static int
rcu_torture_writer(void *arg)
{
	bool exp;
	unsigned long gp_snap;
	bool gp_cond1 = gp_cond, gp_exp1 = gp_exp, gp_normal1 = gp_normal;
	int i;
	struct rcu_torture *rp;
	struct rcu_torture *rp1;
	struct rcu_torture *old_rp;
	static DEFINE_TORTURE_RANDOM(rand);
	int synctype[] = { RTWS_DEF_FREE, RTWS_EXP_SYNC, RTWS_COND_GET };
	int nsynctypes = 0;

	VERBOSE_TOROUT_STRING("rcu_torture_writer task started");
	set_user_nice(current, MAX_NICE);

	/* Initialize synctype[] array.  If none set, take default. */
	if (!gp_cond1 && !gp_exp1 && !gp_normal1)
		gp_cond1 = gp_exp1 = gp_normal1 = true;
	if (gp_cond1 && cur_ops->get_state && cur_ops->cond_sync)
		synctype[nsynctypes++] = RTWS_COND_GET;
	else if (gp_cond && (!cur_ops->get_state || !cur_ops->cond_sync))
		pr_alert("rcu_torture_writer: gp_cond without primitives.\n");
	if (gp_exp1 && cur_ops->exp_sync)
		synctype[nsynctypes++] = RTWS_EXP_SYNC;
	else if (gp_exp && !cur_ops->exp_sync)
		pr_alert("rcu_torture_writer: gp_exp without primitives.\n");
	if (gp_normal1 && cur_ops->deferred_free)
		synctype[nsynctypes++] = RTWS_DEF_FREE;
	else if (gp_normal && !cur_ops->deferred_free)
		pr_alert("rcu_torture_writer: gp_normal without primitives.\n");
	if (WARN_ONCE(nsynctypes == 0,
		      "rcu_torture_writer: No update-side primitives.\n")) {
		rcu_torture_writer_state = RTWS_STOPPING;
		torture_kthread_stopping("rcu_torture_writer");
	}

	do {
		rcu_torture_writer_state = RTWS_FIXED_DELAY;
		schedule_timeout_uninterruptible(1);
@@ -736,32 +798,30 @@ rcu_torture_writer(void *arg)
				i = RCU_TORTURE_PIPE_LEN;
			atomic_inc(&rcu_torture_wcount[i]);
			old_rp->rtort_pipe_count++;
			if (gp_normal == gp_exp)
				exp = !!(torture_random(&rand) & 0x80);
			else
				exp = gp_exp;
			if (!exp) {
			switch (synctype[torture_random(&rand) % nsynctypes]) {
			case RTWS_DEF_FREE:
				rcu_torture_writer_state = RTWS_DEF_FREE;
				cur_ops->deferred_free(old_rp);
			} else {
				break;
			case RTWS_EXP_SYNC:
				rcu_torture_writer_state = RTWS_EXP_SYNC;
				cur_ops->exp_sync();
				list_add(&old_rp->rtort_free,
					 &rcu_torture_removed);
				list_for_each_entry_safe(rp, rp1,
							 &rcu_torture_removed,
							 rtort_free) {
					i = rp->rtort_pipe_count;
					if (i > RCU_TORTURE_PIPE_LEN)
						i = RCU_TORTURE_PIPE_LEN;
					atomic_inc(&rcu_torture_wcount[i]);
					if (++rp->rtort_pipe_count >=
					    RCU_TORTURE_PIPE_LEN) {
						rp->rtort_mbtest = 0;
						list_del(&rp->rtort_free);
						rcu_torture_free(rp);
					}
				 }
				rcu_torture_pipe_update(old_rp);
				break;
			case RTWS_COND_GET:
				rcu_torture_writer_state = RTWS_COND_GET;
				gp_snap = cur_ops->get_state();
				i = torture_random(&rand) % 16;
				if (i != 0)
					schedule_timeout_interruptible(i);
				udelay(torture_random(&rand) % 1000);
				rcu_torture_writer_state = RTWS_COND_SYNC;
				cur_ops->cond_sync(gp_snap);
				rcu_torture_pipe_update(old_rp);
				break;
			default:
				WARN_ON_ONCE(1);
				break;
			}
		}
		rcutorture_record_progress(++rcu_torture_current_version);