rcutorture: Split out beginning and end from rcu_torture_one_read()
The rcu_torture_one_read() function is designed for RCU readers that are confined to a task, such that a single thread of control extends from the beginning of a given RCU read-side critical section to its end. This does not suffice for things like srcu_down_read() and srcu_up_read(), where the critical section might start at task level and end in a timer handler. This commit therefore creates separate init_rcu_torture_one_read_state(), rcu_torture_one_read_start(), and rcu_torture_one_read_end() functions, along with a rcu_torture_one_read_state structure to coordinate their actions. These will be used to create tests for srcu_down_read() and friends. One caution: The caller to rcu_torture_one_read_start() must enter the initial read-side critical section prior to the call. This enables use of non-standard primitives such as srcu_down_read() while still using the same validation code. Signed-off-by: Paul E. McKenney <paulmck@kernel.org> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
This commit is contained in:
committed by
Joel Fernandes
parent
75d8bf48a8
commit
e73e5b7c1a
+81
-43
@@ -2164,53 +2164,70 @@ rcutorture_loop_extend(int *readstate, bool insoftirq, struct torture_random_sta
|
||||
return &rtrsp[j];
|
||||
}
|
||||
|
||||
/*
|
||||
* Do one read-side critical section, returning false if there was
|
||||
* no data to read. Can be invoked both from process context and
|
||||
* from a timer handler.
|
||||
*/
|
||||
static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
|
||||
{
|
||||
bool checkpolling = !(torture_random(trsp) & 0xfff);
|
||||
struct rcu_torture_one_read_state {
|
||||
bool checkpolling;
|
||||
unsigned long cookie;
|
||||
struct rcu_gp_oldstate cookie_full;
|
||||
int i;
|
||||
unsigned long started;
|
||||
unsigned long completed;
|
||||
int newstate;
|
||||
struct rcu_torture *p;
|
||||
int pipe_count;
|
||||
bool preempted = false;
|
||||
int readstate = 0;
|
||||
struct rt_read_seg rtseg[RCUTORTURE_RDR_MAX_SEGS] = { { 0 } };
|
||||
struct rt_read_seg *rtrsp = &rtseg[0];
|
||||
struct rt_read_seg *rtrsp1;
|
||||
int readstate;
|
||||
struct rt_read_seg rtseg[RCUTORTURE_RDR_MAX_SEGS];
|
||||
struct rt_read_seg *rtrsp;
|
||||
unsigned long long ts;
|
||||
};
|
||||
|
||||
WARN_ON_ONCE(!rcu_is_watching());
|
||||
newstate = rcutorture_extend_mask(readstate, trsp);
|
||||
rcutorture_one_extend(&readstate, newstate, myid < 0, trsp, rtrsp++);
|
||||
if (checkpolling) {
|
||||
static void init_rcu_torture_one_read_state(struct rcu_torture_one_read_state *rtorsp,
|
||||
struct torture_random_state *trsp)
|
||||
{
|
||||
memset(rtorsp, 0, sizeof(*rtorsp));
|
||||
rtorsp->checkpolling = !(torture_random(trsp) & 0xfff);
|
||||
rtorsp->rtrsp = &rtorsp->rtseg[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the first segment of a series of overlapping read-side
|
||||
* critical sections. The caller must have actually initiated the
|
||||
* outermost read-side critical section.
|
||||
*/
|
||||
static bool rcu_torture_one_read_start(struct rcu_torture_one_read_state *rtorsp,
|
||||
struct torture_random_state *trsp, long myid)
|
||||
{
|
||||
if (rtorsp->checkpolling) {
|
||||
if (cur_ops->get_gp_state && cur_ops->poll_gp_state)
|
||||
cookie = cur_ops->get_gp_state();
|
||||
rtorsp->cookie = cur_ops->get_gp_state();
|
||||
if (cur_ops->get_gp_state_full && cur_ops->poll_gp_state_full)
|
||||
cur_ops->get_gp_state_full(&cookie_full);
|
||||
cur_ops->get_gp_state_full(&rtorsp->cookie_full);
|
||||
}
|
||||
started = cur_ops->get_gp_seq();
|
||||
ts = rcu_trace_clock_local();
|
||||
p = rcu_dereference_check(rcu_torture_current,
|
||||
rtorsp->started = cur_ops->get_gp_seq();
|
||||
rtorsp->ts = rcu_trace_clock_local();
|
||||
rtorsp->p = rcu_dereference_check(rcu_torture_current,
|
||||
!cur_ops->readlock_held || cur_ops->readlock_held());
|
||||
if (p == NULL) {
|
||||
if (rtorsp->p == NULL) {
|
||||
/* Wait for rcu_torture_writer to get underway */
|
||||
rcutorture_one_extend(&readstate, 0, myid < 0, trsp, rtrsp);
|
||||
rcutorture_one_extend(&rtorsp->readstate, 0, myid < 0, trsp, rtorsp->rtrsp);
|
||||
return false;
|
||||
}
|
||||
if (p->rtort_mbtest == 0)
|
||||
if (rtorsp->p->rtort_mbtest == 0)
|
||||
atomic_inc(&n_rcu_torture_mberror);
|
||||
rcu_torture_reader_do_mbchk(myid, p, trsp);
|
||||
rtrsp = rcutorture_loop_extend(&readstate, myid < 0, trsp, rtrsp);
|
||||
rcu_torture_reader_do_mbchk(myid, rtorsp->p, trsp);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Complete the last segment of a series of overlapping read-side
|
||||
* critical sections and check for errors.
|
||||
*/
|
||||
static void rcu_torture_one_read_end(struct rcu_torture_one_read_state *rtorsp,
|
||||
struct torture_random_state *trsp, long myid)
|
||||
{
|
||||
int i;
|
||||
unsigned long completed;
|
||||
int pipe_count;
|
||||
bool preempted = false;
|
||||
struct rt_read_seg *rtrsp1;
|
||||
|
||||
preempt_disable();
|
||||
pipe_count = READ_ONCE(p->rtort_pipe_count);
|
||||
pipe_count = READ_ONCE(rtorsp->p->rtort_pipe_count);
|
||||
if (pipe_count > RCU_TORTURE_PIPE_LEN) {
|
||||
// Should not happen in a correct RCU implementation,
|
||||
// happens quite often for torture_type=busted.
|
||||
@@ -2218,28 +2235,28 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
|
||||
}
|
||||
completed = cur_ops->get_gp_seq();
|
||||
if (pipe_count > 1) {
|
||||
do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu,
|
||||
ts, started, completed);
|
||||
do_trace_rcu_torture_read(cur_ops->name, &rtorsp->p->rtort_rcu,
|
||||
rtorsp->ts, rtorsp->started, completed);
|
||||
rcu_ftrace_dump(DUMP_ALL);
|
||||
}
|
||||
__this_cpu_inc(rcu_torture_count[pipe_count]);
|
||||
completed = rcutorture_seq_diff(completed, started);
|
||||
completed = rcutorture_seq_diff(completed, rtorsp->started);
|
||||
if (completed > RCU_TORTURE_PIPE_LEN) {
|
||||
/* Should not happen, but... */
|
||||
completed = RCU_TORTURE_PIPE_LEN;
|
||||
}
|
||||
__this_cpu_inc(rcu_torture_batch[completed]);
|
||||
preempt_enable();
|
||||
if (checkpolling) {
|
||||
if (rtorsp->checkpolling) {
|
||||
if (cur_ops->get_gp_state && cur_ops->poll_gp_state)
|
||||
WARN_ONCE(cur_ops->poll_gp_state(cookie),
|
||||
WARN_ONCE(cur_ops->poll_gp_state(rtorsp->cookie),
|
||||
"%s: Cookie check 2 failed %s(%d) %lu->%lu\n",
|
||||
__func__,
|
||||
rcu_torture_writer_state_getname(),
|
||||
rcu_torture_writer_state,
|
||||
cookie, cur_ops->get_gp_state());
|
||||
rtorsp->cookie, cur_ops->get_gp_state());
|
||||
if (cur_ops->get_gp_state_full && cur_ops->poll_gp_state_full)
|
||||
WARN_ONCE(cur_ops->poll_gp_state_full(&cookie_full),
|
||||
WARN_ONCE(cur_ops->poll_gp_state_full(&rtorsp->cookie_full),
|
||||
"%s: Cookie check 6 failed %s(%d) online %*pbl\n",
|
||||
__func__,
|
||||
rcu_torture_writer_state_getname(),
|
||||
@@ -2248,21 +2265,42 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
|
||||
}
|
||||
if (cur_ops->reader_blocked)
|
||||
preempted = cur_ops->reader_blocked();
|
||||
rcutorture_one_extend(&readstate, 0, myid < 0, trsp, rtrsp);
|
||||
WARN_ON_ONCE(readstate);
|
||||
rcutorture_one_extend(&rtorsp->readstate, 0, myid < 0, trsp, rtorsp->rtrsp);
|
||||
WARN_ON_ONCE(rtorsp->readstate);
|
||||
// This next splat is expected behavior if leakpointer, especially
|
||||
// for CONFIG_RCU_STRICT_GRACE_PERIOD=y kernels.
|
||||
WARN_ON_ONCE(leakpointer && READ_ONCE(p->rtort_pipe_count) > 1);
|
||||
WARN_ON_ONCE(leakpointer && READ_ONCE(rtorsp->p->rtort_pipe_count) > 1);
|
||||
|
||||
/* If error or close call, record the sequence of reader protections. */
|
||||
if ((pipe_count > 1 || completed > 1) && !xchg(&err_segs_recorded, 1)) {
|
||||
i = 0;
|
||||
for (rtrsp1 = &rtseg[0]; rtrsp1 < rtrsp; rtrsp1++)
|
||||
for (rtrsp1 = &rtorsp->rtseg[0]; rtrsp1 < rtorsp->rtrsp; rtrsp1++)
|
||||
err_segs[i++] = *rtrsp1;
|
||||
rt_read_nsegs = i;
|
||||
rt_read_preempted = preempted;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do one read-side critical section, returning false if there was
|
||||
* no data to read. Can be invoked both from process context and
|
||||
* from a timer handler.
|
||||
*/
|
||||
static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
|
||||
{
|
||||
int newstate;
|
||||
struct rcu_torture_one_read_state rtors;
|
||||
|
||||
WARN_ON_ONCE(!rcu_is_watching());
|
||||
init_rcu_torture_one_read_state(&rtors, trsp);
|
||||
newstate = rcutorture_extend_mask(rtors.readstate, trsp);
|
||||
rcutorture_one_extend(&rtors.readstate, newstate, myid < 0, trsp, rtors.rtrsp++);
|
||||
if (!rcu_torture_one_read_start(&rtors, trsp, myid)) {
|
||||
rcutorture_one_extend(&rtors.readstate, 0, myid < 0, trsp, rtors.rtrsp);
|
||||
return false;
|
||||
}
|
||||
rtors.rtrsp = rcutorture_loop_extend(&rtors.readstate, myid < 0, trsp, rtors.rtrsp);
|
||||
rcu_torture_one_read_end(&rtors, trsp, myid);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user