Skip to content

Commit

Permalink
[PATCH] srcu-3: add SRCU operations to rcutorture
Browse files Browse the repository at this point in the history
Adds SRCU operations to rcutorture and updates rcutorture documentation.
Also increases the stress imposed by the rcutorture test.

[bunk@stusta.de: make needlessly global code static]
Signed-off-by: Paul E. McKenney <paulmck@us.ibm.com>
Cc: Paul E. McKenney <paulmck@us.ibm.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
  • Loading branch information
Paul E. McKenney authored and Linus Torvalds committed Oct 4, 2006
1 parent 621934e commit b2896d2
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 10 deletions.
15 changes: 15 additions & 0 deletions Documentation/RCU/torture.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,21 @@ o "Free-Block Circulation": Shows the number of torture structures
as it is only incremented if a torture structure's counter
somehow gets incremented farther than it should.

Different implementations of RCU can provide implementation-specific
additional information. For example, SRCU provides the following:

srcu-torture: rtc: f8cf46a8 ver: 355 tfle: 0 rta: 356 rtaf: 0 rtf: 346 rtmbe: 0
srcu-torture: Reader Pipe: 559738 939 0 0 0 0 0 0 0 0 0
srcu-torture: Reader Batch: 560434 243 0 0 0 0 0 0 0 0
srcu-torture: Free-Block Circulation: 355 354 353 352 351 350 349 348 347 346 0
srcu-torture: per-CPU(idx=1): 0(0,1) 1(0,1) 2(0,0) 3(0,1)

The first four lines are similar to those for RCU. The last line shows
the per-CPU counter state. The numbers in parentheses are the values
of the "old" and "current" counters for the corresponding CPU. The
"idx" value maps the "old" and "current" values to the underlying array,
and is useful for debugging.


USAGE

Expand Down
133 changes: 123 additions & 10 deletions kernel/rcutorture.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include <linux/delay.h>
#include <linux/byteorder/swabb.h>
#include <linux/stat.h>
#include <linux/srcu.h>

MODULE_LICENSE("GPL");

Expand All @@ -53,7 +54,7 @@ static int stat_interval; /* Interval between stats, in seconds. */
static int verbose; /* Print more debug info. */
static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */
static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/
static char *torture_type = "rcu"; /* What to torture. */
static char *torture_type = "rcu"; /* What to torture: rcu, srcu. */

module_param(nreaders, int, 0);
MODULE_PARM_DESC(nreaders, "Number of RCU reader threads");
Expand All @@ -66,7 +67,7 @@ MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs");
module_param(shuffle_interval, int, 0);
MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles");
module_param(torture_type, charp, 0);
MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh)");
MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)");

#define TORTURE_FLAG "-torture:"
#define PRINTK_STRING(s) \
Expand Down Expand Up @@ -104,11 +105,11 @@ static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_count) =
static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_batch) =
{ 0 };
static atomic_t rcu_torture_wcount[RCU_TORTURE_PIPE_LEN + 1];
atomic_t n_rcu_torture_alloc;
atomic_t n_rcu_torture_alloc_fail;
atomic_t n_rcu_torture_free;
atomic_t n_rcu_torture_mberror;
atomic_t n_rcu_torture_error;
static atomic_t n_rcu_torture_alloc;
static atomic_t n_rcu_torture_alloc_fail;
static atomic_t n_rcu_torture_free;
static atomic_t n_rcu_torture_mberror;
static atomic_t n_rcu_torture_error;

/*
* Allocate an element from the rcu_tortures pool.
Expand Down Expand Up @@ -180,6 +181,7 @@ struct rcu_torture_ops {
void (*init)(void);
void (*cleanup)(void);
int (*readlock)(void);
void (*readdelay)(struct rcu_random_state *rrsp);
void (*readunlock)(int idx);
int (*completed)(void);
void (*deferredfree)(struct rcu_torture *p);
Expand All @@ -198,6 +200,18 @@ static int rcu_torture_read_lock(void) __acquires(RCU)
return 0;
}

static void rcu_read_delay(struct rcu_random_state *rrsp)
{
long delay;
const long longdelay = 200;

/* We want there to be long-running readers, but not all the time. */

delay = rcu_random(rrsp) % (nrealreaders * 2 * longdelay);
if (!delay)
udelay(longdelay);
}

static void rcu_torture_read_unlock(int idx) __releases(RCU)
{
rcu_read_unlock();
Expand Down Expand Up @@ -239,6 +253,7 @@ static struct rcu_torture_ops rcu_ops = {
.init = NULL,
.cleanup = NULL,
.readlock = rcu_torture_read_lock,
.readdelay = rcu_read_delay,
.readunlock = rcu_torture_read_unlock,
.completed = rcu_torture_completed,
.deferredfree = rcu_torture_deferred_free,
Expand Down Expand Up @@ -275,15 +290,113 @@ static struct rcu_torture_ops rcu_bh_ops = {
.init = NULL,
.cleanup = NULL,
.readlock = rcu_bh_torture_read_lock,
.readdelay = rcu_read_delay, /* just reuse rcu's version. */
.readunlock = rcu_bh_torture_read_unlock,
.completed = rcu_bh_torture_completed,
.deferredfree = rcu_bh_torture_deferred_free,
.stats = NULL,
.name = "rcu_bh"
};

/*
* Definitions for srcu torture testing.
*/

static struct srcu_struct srcu_ctl;
static struct list_head srcu_removed;

static void srcu_torture_init(void)
{
init_srcu_struct(&srcu_ctl);
INIT_LIST_HEAD(&srcu_removed);
}

static void srcu_torture_cleanup(void)
{
synchronize_srcu(&srcu_ctl);
cleanup_srcu_struct(&srcu_ctl);
}

static int srcu_torture_read_lock(void)
{
return srcu_read_lock(&srcu_ctl);
}

static void srcu_read_delay(struct rcu_random_state *rrsp)
{
long delay;
const long uspertick = 1000000 / HZ;
const long longdelay = 10;

/* We want there to be long-running readers, but not all the time. */

delay = rcu_random(rrsp) % (nrealreaders * 2 * longdelay * uspertick);
if (!delay)
schedule_timeout_interruptible(longdelay);
}

static void srcu_torture_read_unlock(int idx)
{
srcu_read_unlock(&srcu_ctl, idx);
}

static int srcu_torture_completed(void)
{
return srcu_batches_completed(&srcu_ctl);
}

static void srcu_torture_deferred_free(struct rcu_torture *p)
{
int i;
struct rcu_torture *rp;
struct rcu_torture *rp1;

synchronize_srcu(&srcu_ctl);
list_add(&p->rtort_free, &srcu_removed);
list_for_each_entry_safe(rp, rp1, &srcu_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);
}
}
}

static int srcu_torture_stats(char *page)
{
int cnt = 0;
int cpu;
int idx = srcu_ctl.completed & 0x1;

cnt += sprintf(&page[cnt], "%s%s per-CPU(idx=%d):",
torture_type, TORTURE_FLAG, idx);
for_each_possible_cpu(cpu) {
cnt += sprintf(&page[cnt], " %d(%d,%d)", cpu,
per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[!idx],
per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[idx]);
}
cnt += sprintf(&page[cnt], "\n");
return cnt;
}

static struct rcu_torture_ops srcu_ops = {
.init = srcu_torture_init,
.cleanup = srcu_torture_cleanup,
.readlock = srcu_torture_read_lock,
.readdelay = srcu_read_delay,
.readunlock = srcu_torture_read_unlock,
.completed = srcu_torture_completed,
.deferredfree = srcu_torture_deferred_free,
.stats = srcu_torture_stats,
.name = "srcu"
};

static struct rcu_torture_ops *torture_ops[] =
{ &rcu_ops, &rcu_bh_ops, NULL };
{ &rcu_ops, &rcu_bh_ops, &srcu_ops, NULL };

/*
* RCU torture writer kthread. Repeatedly substitutes a new structure
Expand Down Expand Up @@ -359,7 +472,7 @@ rcu_torture_reader(void *arg)
}
if (p->rtort_mbtest == 0)
atomic_inc(&n_rcu_torture_mberror);
udelay(rcu_random(&rand) & 0x7f);
cur_ops->readdelay(&rand);
preempt_disable();
pipe_count = p->rtort_pipe_count;
if (pipe_count > RCU_TORTURE_PIPE_LEN) {
Expand Down Expand Up @@ -483,7 +596,7 @@ static int rcu_idle_cpu; /* Force all torture tasks off this CPU */
/* Shuffle tasks such that we allow @rcu_idle_cpu to become idle. A special case
* is when @rcu_idle_cpu = -1, when we allow the tasks to run on all CPUs.
*/
void rcu_torture_shuffle_tasks(void)
static void rcu_torture_shuffle_tasks(void)
{
cpumask_t tmp_mask = CPU_MASK_ALL;
int i;
Expand Down

0 comments on commit b2896d2

Please sign in to comment.