aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@us.ibm.com>2006-10-04 05:17:03 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-10-04 10:55:30 -0400
commitb2896d2e75c87ea6a842c088db730b03c91db737 (patch)
tree69411d92f0ba99d3888c6fe8c817708e9a47eb67
parent621934ee7ed5b073c7fd638b347e632c53572761 (diff)
[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: 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>
-rw-r--r--Documentation/RCU/torture.txt15
-rw-r--r--kernel/rcutorture.c133
2 files changed, 138 insertions, 10 deletions
diff --git a/Documentation/RCU/torture.txt b/Documentation/RCU/torture.txt
index a4948591607d..2174badd31e0 100644
--- a/Documentation/RCU/torture.txt
+++ b/Documentation/RCU/torture.txt
@@ -118,6 +118,21 @@ o "Free-Block Circulation": Shows the number of torture structures
118 as it is only incremented if a torture structure's counter 118 as it is only incremented if a torture structure's counter
119 somehow gets incremented farther than it should. 119 somehow gets incremented farther than it should.
120 120
121Different implementations of RCU can provide implementation-specific
122additional information. For example, SRCU provides the following:
123
124 srcu-torture: rtc: f8cf46a8 ver: 355 tfle: 0 rta: 356 rtaf: 0 rtf: 346 rtmbe: 0
125 srcu-torture: Reader Pipe: 559738 939 0 0 0 0 0 0 0 0 0
126 srcu-torture: Reader Batch: 560434 243 0 0 0 0 0 0 0 0
127 srcu-torture: Free-Block Circulation: 355 354 353 352 351 350 349 348 347 346 0
128 srcu-torture: per-CPU(idx=1): 0(0,1) 1(0,1) 2(0,0) 3(0,1)
129
130The first four lines are similar to those for RCU. The last line shows
131the per-CPU counter state. The numbers in parentheses are the values
132of the "old" and "current" counters for the corresponding CPU. The
133"idx" value maps the "old" and "current" values to the underlying array,
134and is useful for debugging.
135
121 136
122USAGE 137USAGE
123 138
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c
index 23446e91cded..e34d22bf2293 100644
--- a/kernel/rcutorture.c
+++ b/kernel/rcutorture.c
@@ -44,6 +44,7 @@
44#include <linux/delay.h> 44#include <linux/delay.h>
45#include <linux/byteorder/swabb.h> 45#include <linux/byteorder/swabb.h>
46#include <linux/stat.h> 46#include <linux/stat.h>
47#include <linux/srcu.h>
47 48
48MODULE_LICENSE("GPL"); 49MODULE_LICENSE("GPL");
49 50
@@ -53,7 +54,7 @@ static int stat_interval; /* Interval between stats, in seconds. */
53static int verbose; /* Print more debug info. */ 54static int verbose; /* Print more debug info. */
54static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ 55static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */
55static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/ 56static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/
56static char *torture_type = "rcu"; /* What to torture. */ 57static char *torture_type = "rcu"; /* What to torture: rcu, srcu. */
57 58
58module_param(nreaders, int, 0); 59module_param(nreaders, int, 0);
59MODULE_PARM_DESC(nreaders, "Number of RCU reader threads"); 60MODULE_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");
66module_param(shuffle_interval, int, 0); 67module_param(shuffle_interval, int, 0);
67MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); 68MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles");
68module_param(torture_type, charp, 0); 69module_param(torture_type, charp, 0);
69MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh)"); 70MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)");
70 71
71#define TORTURE_FLAG "-torture:" 72#define TORTURE_FLAG "-torture:"
72#define PRINTK_STRING(s) \ 73#define PRINTK_STRING(s) \
@@ -104,11 +105,11 @@ static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_count) =
104static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_batch) = 105static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_batch) =
105 { 0 }; 106 { 0 };
106static atomic_t rcu_torture_wcount[RCU_TORTURE_PIPE_LEN + 1]; 107static atomic_t rcu_torture_wcount[RCU_TORTURE_PIPE_LEN + 1];
107atomic_t n_rcu_torture_alloc; 108static atomic_t n_rcu_torture_alloc;
108atomic_t n_rcu_torture_alloc_fail; 109static atomic_t n_rcu_torture_alloc_fail;
109atomic_t n_rcu_torture_free; 110static atomic_t n_rcu_torture_free;
110atomic_t n_rcu_torture_mberror; 111static atomic_t n_rcu_torture_mberror;
111atomic_t n_rcu_torture_error; 112static atomic_t n_rcu_torture_error;
112 113
113/* 114/*
114 * Allocate an element from the rcu_tortures pool. 115 * Allocate an element from the rcu_tortures pool.
@@ -180,6 +181,7 @@ struct rcu_torture_ops {
180 void (*init)(void); 181 void (*init)(void);
181 void (*cleanup)(void); 182 void (*cleanup)(void);
182 int (*readlock)(void); 183 int (*readlock)(void);
184 void (*readdelay)(struct rcu_random_state *rrsp);
183 void (*readunlock)(int idx); 185 void (*readunlock)(int idx);
184 int (*completed)(void); 186 int (*completed)(void);
185 void (*deferredfree)(struct rcu_torture *p); 187 void (*deferredfree)(struct rcu_torture *p);
@@ -198,6 +200,18 @@ static int rcu_torture_read_lock(void) __acquires(RCU)
198 return 0; 200 return 0;
199} 201}
200 202
203static void rcu_read_delay(struct rcu_random_state *rrsp)
204{
205 long delay;
206 const long longdelay = 200;
207
208 /* We want there to be long-running readers, but not all the time. */
209
210 delay = rcu_random(rrsp) % (nrealreaders * 2 * longdelay);
211 if (!delay)
212 udelay(longdelay);
213}
214
201static void rcu_torture_read_unlock(int idx) __releases(RCU) 215static void rcu_torture_read_unlock(int idx) __releases(RCU)
202{ 216{
203 rcu_read_unlock(); 217 rcu_read_unlock();
@@ -239,6 +253,7 @@ static struct rcu_torture_ops rcu_ops = {
239 .init = NULL, 253 .init = NULL,
240 .cleanup = NULL, 254 .cleanup = NULL,
241 .readlock = rcu_torture_read_lock, 255 .readlock = rcu_torture_read_lock,
256 .readdelay = rcu_read_delay,
242 .readunlock = rcu_torture_read_unlock, 257 .readunlock = rcu_torture_read_unlock,
243 .completed = rcu_torture_completed, 258 .completed = rcu_torture_completed,
244 .deferredfree = rcu_torture_deferred_free, 259 .deferredfree = rcu_torture_deferred_free,
@@ -275,6 +290,7 @@ static struct rcu_torture_ops rcu_bh_ops = {
275 .init = NULL, 290 .init = NULL,
276 .cleanup = NULL, 291 .cleanup = NULL,
277 .readlock = rcu_bh_torture_read_lock, 292 .readlock = rcu_bh_torture_read_lock,
293 .readdelay = rcu_read_delay, /* just reuse rcu's version. */
278 .readunlock = rcu_bh_torture_read_unlock, 294 .readunlock = rcu_bh_torture_read_unlock,
279 .completed = rcu_bh_torture_completed, 295 .completed = rcu_bh_torture_completed,
280 .deferredfree = rcu_bh_torture_deferred_free, 296 .deferredfree = rcu_bh_torture_deferred_free,
@@ -282,8 +298,105 @@ static struct rcu_torture_ops rcu_bh_ops = {
282 .name = "rcu_bh" 298 .name = "rcu_bh"
283}; 299};
284 300
301/*
302 * Definitions for srcu torture testing.
303 */
304
305static struct srcu_struct srcu_ctl;
306static struct list_head srcu_removed;
307
308static void srcu_torture_init(void)
309{
310 init_srcu_struct(&srcu_ctl);
311 INIT_LIST_HEAD(&srcu_removed);
312}
313
314static void srcu_torture_cleanup(void)
315{
316 synchronize_srcu(&srcu_ctl);
317 cleanup_srcu_struct(&srcu_ctl);
318}
319
320static int srcu_torture_read_lock(void)
321{
322 return srcu_read_lock(&srcu_ctl);
323}
324
325static void srcu_read_delay(struct rcu_random_state *rrsp)
326{
327 long delay;
328 const long uspertick = 1000000 / HZ;
329 const long longdelay = 10;
330
331 /* We want there to be long-running readers, but not all the time. */
332
333 delay = rcu_random(rrsp) % (nrealreaders * 2 * longdelay * uspertick);
334 if (!delay)
335 schedule_timeout_interruptible(longdelay);
336}
337
338static void srcu_torture_read_unlock(int idx)
339{
340 srcu_read_unlock(&srcu_ctl, idx);
341}
342
343static int srcu_torture_completed(void)
344{
345 return srcu_batches_completed(&srcu_ctl);
346}
347
348static void srcu_torture_deferred_free(struct rcu_torture *p)
349{
350 int i;
351 struct rcu_torture *rp;
352 struct rcu_torture *rp1;
353
354 synchronize_srcu(&srcu_ctl);
355 list_add(&p->rtort_free, &srcu_removed);
356 list_for_each_entry_safe(rp, rp1, &srcu_removed, rtort_free) {
357 i = rp->rtort_pipe_count;
358 if (i > RCU_TORTURE_PIPE_LEN)
359 i = RCU_TORTURE_PIPE_LEN;
360 atomic_inc(&rcu_torture_wcount[i]);
361 if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) {
362 rp->rtort_mbtest = 0;
363 list_del(&rp->rtort_free);
364 rcu_torture_free(rp);
365 }
366 }
367}
368
369static int srcu_torture_stats(char *page)
370{
371 int cnt = 0;
372 int cpu;
373 int idx = srcu_ctl.completed & 0x1;
374
375 cnt += sprintf(&page[cnt], "%s%s per-CPU(idx=%d):",
376 torture_type, TORTURE_FLAG, idx);
377 for_each_possible_cpu(cpu) {
378 cnt += sprintf(&page[cnt], " %d(%d,%d)", cpu,
379 per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[!idx],
380 per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[idx]);
381 }
382 cnt += sprintf(&page[cnt], "\n");
383 return cnt;
384}
385
386static struct rcu_torture_ops srcu_ops = {
387 .init = srcu_torture_init,
388 .cleanup = srcu_torture_cleanup,
389 .readlock = srcu_torture_read_lock,
390 .readdelay = srcu_read_delay,
391 .readunlock = srcu_torture_read_unlock,
392 .completed = srcu_torture_completed,
393 .deferredfree = srcu_torture_deferred_free,
394 .stats = srcu_torture_stats,
395 .name = "srcu"
396};
397
285static struct rcu_torture_ops *torture_ops[] = 398static struct rcu_torture_ops *torture_ops[] =
286 { &rcu_ops, &rcu_bh_ops, NULL }; 399 { &rcu_ops, &rcu_bh_ops, &srcu_ops, NULL };
287 400
288/* 401/*
289 * RCU torture writer kthread. Repeatedly substitutes a new structure 402 * RCU torture writer kthread. Repeatedly substitutes a new structure
@@ -359,7 +472,7 @@ rcu_torture_reader(void *arg)
359 } 472 }
360 if (p->rtort_mbtest == 0) 473 if (p->rtort_mbtest == 0)
361 atomic_inc(&n_rcu_torture_mberror); 474 atomic_inc(&n_rcu_torture_mberror);
362 udelay(rcu_random(&rand) & 0x7f); 475 cur_ops->readdelay(&rand);
363 preempt_disable(); 476 preempt_disable();
364 pipe_count = p->rtort_pipe_count; 477 pipe_count = p->rtort_pipe_count;
365 if (pipe_count > RCU_TORTURE_PIPE_LEN) { 478 if (pipe_count > RCU_TORTURE_PIPE_LEN) {
@@ -483,7 +596,7 @@ static int rcu_idle_cpu; /* Force all torture tasks off this CPU */
483/* Shuffle tasks such that we allow @rcu_idle_cpu to become idle. A special case 596/* Shuffle tasks such that we allow @rcu_idle_cpu to become idle. A special case
484 * is when @rcu_idle_cpu = -1, when we allow the tasks to run on all CPUs. 597 * is when @rcu_idle_cpu = -1, when we allow the tasks to run on all CPUs.
485 */ 598 */
486void rcu_torture_shuffle_tasks(void) 599static void rcu_torture_shuffle_tasks(void)
487{ 600{
488 cpumask_t tmp_mask = CPU_MASK_ALL; 601 cpumask_t tmp_mask = CPU_MASK_ALL;
489 int i; 602 int i;