Skip to content

Commit

Permalink
rcutorture: Add forward-progress checking for writer
Browse files Browse the repository at this point in the history
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: Paul E. McKenney <[email protected]>
  • Loading branch information
paulmck committed May 13, 2014
1 parent c9eaa44 commit ad0dc7f
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 0 deletions.
19 changes: 19 additions & 0 deletions include/linux/rcupdate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
{
}
Expand Down
37 changes: 37 additions & 0 deletions kernel/rcu/rcutorture.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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++) {
Expand Down Expand Up @@ -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;
}

/*
Expand Down
33 changes: 33 additions & 0 deletions kernel/rcu/tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ad0dc7f

Please sign in to comment.