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

rcutorture: Add forward-progress checking for writer



The rcutorture output currently does not distinguish between stalls in
the RCU implementation and stalls in the rcu_torture_writer() kthreads.
This commit therefore adds some diagnostics to help distinguish between
these two conditions, at least for the non-SRCU implementations.  (SRCU
does not provide evidence of update-side forward progress by design.)

Signed-off-by: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
parent c9eaa447
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -51,7 +51,17 @@ extern int rcu_expedited; /* for sysctl */
extern int rcutorture_runnable; /* for sysctl */
#endif /* #ifdef CONFIG_RCU_TORTURE_TEST */

enum rcutorture_type {
	RCU_FLAVOR,
	RCU_BH_FLAVOR,
	RCU_SCHED_FLAVOR,
	SRCU_FLAVOR,
	INVALID_RCU_FLAVOR
};

#if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU)
void rcutorture_get_gp_data(enum rcutorture_type test_type, int *flags,
			    unsigned long *gpnum, unsigned long *completed);
void rcutorture_record_test_transition(void);
void rcutorture_record_progress(unsigned long vernum);
void do_trace_rcu_torture_read(const char *rcutorturename,
@@ -60,6 +70,15 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
			       unsigned long c_old,
			       unsigned long c);
#else
static inline void rcutorture_get_gp_data(enum rcutorture_type test_type,
					  int *flags,
					  unsigned long *gpnum,
					  unsigned long *completed)
{
	*flags = 0;
	*gpnum = 0;
	*completed = 0;
}
static inline void rcutorture_record_test_transition(void)
{
}
+37 −0
Original line number Diff line number Diff line
@@ -138,6 +138,15 @@ static long n_barrier_attempts;
static long n_barrier_successes;
static struct list_head rcu_torture_removed;

static int rcu_torture_writer_state;
#define RTWS_FIXED_DELAY	0
#define RTWS_DELAY		1
#define RTWS_REPLACE		2
#define RTWS_DEF_FREE		3
#define RTWS_EXP_SYNC		4
#define RTWS_STUTTER		5
#define RTWS_STOPPING		6

#if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE)
#define RCUTORTURE_RUNNABLE_INIT 1
#else
@@ -214,6 +223,7 @@ rcu_torture_free(struct rcu_torture *p)
 */

struct rcu_torture_ops {
	int ttype;
	void (*init)(void);
	int (*readlock)(void);
	void (*read_delay)(struct torture_random_state *rrsp);
@@ -312,6 +322,7 @@ static void rcu_sync_torture_init(void)
}

static struct rcu_torture_ops rcu_ops = {
	.ttype		= RCU_FLAVOR,
	.init		= rcu_sync_torture_init,
	.readlock	= rcu_torture_read_lock,
	.read_delay	= rcu_read_delay,
@@ -355,6 +366,7 @@ static void rcu_bh_torture_deferred_free(struct rcu_torture *p)
}

static struct rcu_torture_ops rcu_bh_ops = {
	.ttype		= RCU_BH_FLAVOR,
	.init		= rcu_sync_torture_init,
	.readlock	= rcu_bh_torture_read_lock,
	.read_delay	= rcu_read_delay,  /* just reuse rcu's version. */
@@ -397,6 +409,7 @@ call_rcu_busted(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
}

static struct rcu_torture_ops rcu_busted_ops = {
	.ttype		= INVALID_RCU_FLAVOR,
	.init		= rcu_sync_torture_init,
	.readlock	= rcu_torture_read_lock,
	.read_delay	= rcu_read_delay,  /* just reuse rcu's version. */
@@ -492,6 +505,7 @@ static void srcu_torture_synchronize_expedited(void)
}

static struct rcu_torture_ops srcu_ops = {
	.ttype		= SRCU_FLAVOR,
	.init		= rcu_sync_torture_init,
	.readlock	= srcu_torture_read_lock,
	.read_delay	= srcu_read_delay,
@@ -527,6 +541,7 @@ static void rcu_sched_torture_deferred_free(struct rcu_torture *p)
}

static struct rcu_torture_ops sched_ops = {
	.ttype		= RCU_SCHED_FLAVOR,
	.init		= rcu_sync_torture_init,
	.readlock	= sched_torture_read_lock,
	.read_delay	= rcu_read_delay,  /* just reuse rcu's version. */
@@ -699,12 +714,15 @@ rcu_torture_writer(void *arg)
	set_user_nice(current, MAX_NICE);

	do {
		rcu_torture_writer_state = RTWS_FIXED_DELAY;
		schedule_timeout_uninterruptible(1);
		rp = rcu_torture_alloc();
		if (rp == NULL)
			continue;
		rp->rtort_pipe_count = 0;
		rcu_torture_writer_state = RTWS_DELAY;
		udelay(torture_random(&rand) & 0x3ff);
		rcu_torture_writer_state = RTWS_REPLACE;
		old_rp = rcu_dereference_check(rcu_torture_current,
					       current == writer_task);
		rp->rtort_mbtest = 1;
@@ -721,8 +739,10 @@ rcu_torture_writer(void *arg)
			else
				exp = gp_exp;
			if (!exp) {
				rcu_torture_writer_state = RTWS_DEF_FREE;
				cur_ops->deferred_free(old_rp);
			} else {
				rcu_torture_writer_state = RTWS_EXP_SYNC;
				cur_ops->exp_sync();
				list_add(&old_rp->rtort_free,
					 &rcu_torture_removed);
@@ -743,8 +763,10 @@ rcu_torture_writer(void *arg)
			}
		}
		rcutorture_record_progress(++rcu_torture_current_version);
		rcu_torture_writer_state = RTWS_STUTTER;
		stutter_wait("rcu_torture_writer");
	} while (!torture_must_stop());
	rcu_torture_writer_state = RTWS_STOPPING;
	torture_kthread_stopping("rcu_torture_writer");
	return 0;
}
@@ -937,6 +959,7 @@ rcu_torture_printk(char *page)
	int i;
	long pipesummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 };
	long batchsummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 };
	static unsigned long rtcv_snap = ULONG_MAX;

	for_each_possible_cpu(cpu) {
		for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) {
@@ -997,6 +1020,20 @@ rcu_torture_printk(char *page)
	page += sprintf(page, "\n");
	if (cur_ops->stats)
		cur_ops->stats(page);
	if (rtcv_snap == rcu_torture_current_version &&
	    rcu_torture_current != NULL) {
		int __maybe_unused flags;
		unsigned long __maybe_unused gpnum;
		unsigned long __maybe_unused completed;

		rcutorture_get_gp_data(cur_ops->ttype,
				       &flags, &gpnum, &completed);
		page += sprintf(page,
				"??? Writer stall state %d g%lu c%lu f%#x\n",
				rcu_torture_writer_state,
				gpnum, completed, flags);
	}
	rtcv_snap = rcu_torture_current_version;
}

/*
+33 −0
Original line number Diff line number Diff line
@@ -293,6 +293,39 @@ void rcutorture_record_test_transition(void)
}
EXPORT_SYMBOL_GPL(rcutorture_record_test_transition);

/*
 * Send along grace-period-related data for rcutorture diagnostics.
 */
void rcutorture_get_gp_data(enum rcutorture_type test_type, int *flags,
			    unsigned long *gpnum, unsigned long *completed)
{
	struct rcu_state *rsp = NULL;

	switch (test_type) {
	case RCU_FLAVOR:
		rsp = rcu_state;
		break;
	case RCU_BH_FLAVOR:
		rsp = &rcu_bh_state;
		break;
	case RCU_SCHED_FLAVOR:
		rsp = &rcu_sched_state;
		break;
	default:
		break;
	}
	if (rsp != NULL) {
		*flags = ACCESS_ONCE(rsp->gp_flags);
		*gpnum = ACCESS_ONCE(rsp->gpnum);
		*completed = ACCESS_ONCE(rsp->completed);
		return;
	}
	*flags = 0;
	*gpnum = 0;
	*completed = 0;
}
EXPORT_SYMBOL_GPL(rcutorture_get_gp_data);

/*
 * Record the number of writer passes through the current rcutorture test.
 * This is also used to correlate debugfs tracing stats with the rcutorture