Commit b2896d2e authored by Paul E. McKenney's avatar Paul E. McKenney Committed by Linus Torvalds

[PATCH] srcu-3: add SRCU operations to rcutorture

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: default avatarPaul E. McKenney <paulmck@us.ibm.com>
Cc: Paul E. McKenney <paulmck@us.ibm.com>
Signed-off-by: default avatarAdrian Bunk <bunk@stusta.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 621934ee
...@@ -118,6 +118,21 @@ o "Free-Block Circulation": Shows the number of torture structures ...@@ -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 as it is only incremented if a torture structure's counter
somehow gets incremented farther than it should. 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 USAGE
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/byteorder/swabb.h> #include <linux/byteorder/swabb.h>
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/srcu.h>
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -53,7 +54,7 @@ static int stat_interval; /* Interval between stats, in seconds. */ ...@@ -53,7 +54,7 @@ static int stat_interval; /* Interval between stats, in seconds. */
static int verbose; /* Print more debug info. */ static int verbose; /* Print more debug info. */
static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ 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 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_param(nreaders, int, 0);
MODULE_PARM_DESC(nreaders, "Number of RCU reader threads"); MODULE_PARM_DESC(nreaders, "Number of RCU reader threads");
...@@ -66,7 +67,7 @@ MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs"); ...@@ -66,7 +67,7 @@ MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs");
module_param(shuffle_interval, int, 0); module_param(shuffle_interval, int, 0);
MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles");
module_param(torture_type, charp, 0); 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 TORTURE_FLAG "-torture:"
#define PRINTK_STRING(s) \ #define PRINTK_STRING(s) \
...@@ -104,11 +105,11 @@ static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_count) = ...@@ -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) = static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_batch) =
{ 0 }; { 0 };
static atomic_t rcu_torture_wcount[RCU_TORTURE_PIPE_LEN + 1]; static atomic_t rcu_torture_wcount[RCU_TORTURE_PIPE_LEN + 1];
atomic_t n_rcu_torture_alloc; static atomic_t n_rcu_torture_alloc;
atomic_t n_rcu_torture_alloc_fail; static atomic_t n_rcu_torture_alloc_fail;
atomic_t n_rcu_torture_free; static atomic_t n_rcu_torture_free;
atomic_t n_rcu_torture_mberror; static atomic_t n_rcu_torture_mberror;
atomic_t n_rcu_torture_error; static atomic_t n_rcu_torture_error;
/* /*
* Allocate an element from the rcu_tortures pool. * Allocate an element from the rcu_tortures pool.
...@@ -180,6 +181,7 @@ struct rcu_torture_ops { ...@@ -180,6 +181,7 @@ struct rcu_torture_ops {
void (*init)(void); void (*init)(void);
void (*cleanup)(void); void (*cleanup)(void);
int (*readlock)(void); int (*readlock)(void);
void (*readdelay)(struct rcu_random_state *rrsp);
void (*readunlock)(int idx); void (*readunlock)(int idx);
int (*completed)(void); int (*completed)(void);
void (*deferredfree)(struct rcu_torture *p); void (*deferredfree)(struct rcu_torture *p);
...@@ -198,6 +200,18 @@ static int rcu_torture_read_lock(void) __acquires(RCU) ...@@ -198,6 +200,18 @@ static int rcu_torture_read_lock(void) __acquires(RCU)
return 0; 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) static void rcu_torture_read_unlock(int idx) __releases(RCU)
{ {
rcu_read_unlock(); rcu_read_unlock();
...@@ -239,6 +253,7 @@ static struct rcu_torture_ops rcu_ops = { ...@@ -239,6 +253,7 @@ static struct rcu_torture_ops rcu_ops = {
.init = NULL, .init = NULL,
.cleanup = NULL, .cleanup = NULL,
.readlock = rcu_torture_read_lock, .readlock = rcu_torture_read_lock,
.readdelay = rcu_read_delay,
.readunlock = rcu_torture_read_unlock, .readunlock = rcu_torture_read_unlock,
.completed = rcu_torture_completed, .completed = rcu_torture_completed,
.deferredfree = rcu_torture_deferred_free, .deferredfree = rcu_torture_deferred_free,
...@@ -275,6 +290,7 @@ static struct rcu_torture_ops rcu_bh_ops = { ...@@ -275,6 +290,7 @@ static struct rcu_torture_ops rcu_bh_ops = {
.init = NULL, .init = NULL,
.cleanup = NULL, .cleanup = NULL,
.readlock = rcu_bh_torture_read_lock, .readlock = rcu_bh_torture_read_lock,
.readdelay = rcu_read_delay, /* just reuse rcu's version. */
.readunlock = rcu_bh_torture_read_unlock, .readunlock = rcu_bh_torture_read_unlock,
.completed = rcu_bh_torture_completed, .completed = rcu_bh_torture_completed,
.deferredfree = rcu_bh_torture_deferred_free, .deferredfree = rcu_bh_torture_deferred_free,
...@@ -282,8 +298,105 @@ static struct rcu_torture_ops rcu_bh_ops = { ...@@ -282,8 +298,105 @@ static struct rcu_torture_ops rcu_bh_ops = {
.name = "rcu_bh" .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[] = 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 * RCU torture writer kthread. Repeatedly substitutes a new structure
...@@ -359,7 +472,7 @@ rcu_torture_reader(void *arg) ...@@ -359,7 +472,7 @@ rcu_torture_reader(void *arg)
} }
if (p->rtort_mbtest == 0) if (p->rtort_mbtest == 0)
atomic_inc(&n_rcu_torture_mberror); atomic_inc(&n_rcu_torture_mberror);
udelay(rcu_random(&rand) & 0x7f); cur_ops->readdelay(&rand);
preempt_disable(); preempt_disable();
pipe_count = p->rtort_pipe_count; pipe_count = p->rtort_pipe_count;
if (pipe_count > RCU_TORTURE_PIPE_LEN) { if (pipe_count > RCU_TORTURE_PIPE_LEN) {
...@@ -483,7 +596,7 @@ static int rcu_idle_cpu; /* Force all torture tasks off this CPU */ ...@@ -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 /* 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. * 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; cpumask_t tmp_mask = CPU_MASK_ALL;
int i; int i;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment