aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@us.ibm.com>2006-06-27 05:54:03 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-27 20:32:40 -0400
commit72e9bb549280b354311af30640c9433474f3a32c (patch)
tree5f35cb787f758f29f7853b9e1d96a9e5ab02eaed
parent29766f1eb3d8d6cfaf1d6623fb4c3f7e5a822fe4 (diff)
[PATCH] rcutorture: add ops vector and Classic RCU ops
Add an ops vector to rcutorture, and add the ops for Classic RCU. Update the rcutorture documentation to reflect slight change to the dmesg formats. Signed-off-by: Paul E. McKenney <paulmck@us.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--Documentation/RCU/torture.txt22
-rw-r--r--kernel/rcutorture.c163
2 files changed, 133 insertions, 52 deletions
diff --git a/Documentation/RCU/torture.txt b/Documentation/RCU/torture.txt
index d896685a9100..a4948591607d 100644
--- a/Documentation/RCU/torture.txt
+++ b/Documentation/RCU/torture.txt
@@ -7,7 +7,7 @@ The CONFIG_RCU_TORTURE_TEST config option is available for all RCU
7implementations. It creates an rcutorture kernel module that can 7implementations. It creates an rcutorture kernel module that can
8be loaded to run a torture test. The test periodically outputs 8be loaded to run a torture test. The test periodically outputs
9status messages via printk(), which can be examined via the dmesg 9status messages via printk(), which can be examined via the dmesg
10command (perhaps grepping for "rcutorture"). The test is started 10command (perhaps grepping for "torture"). The test is started
11when the module is loaded, and stops when the module is unloaded. 11when the module is loaded, and stops when the module is unloaded.
12 12
13However, actually setting this config option to "y" results in the system 13However, actually setting this config option to "y" results in the system
@@ -44,6 +44,10 @@ test_no_idle_hz Whether or not to test the ability of RCU to operate in
44 a kernel that disables the scheduling-clock interrupt to 44 a kernel that disables the scheduling-clock interrupt to
45 idle CPUs. Boolean parameter, "1" to test, "0" otherwise. 45 idle CPUs. Boolean parameter, "1" to test, "0" otherwise.
46 46
47torture_type The type of RCU to test: "rcu" for the rcu_read_lock()
48 API, "rcu_bh" for the rcu_read_lock_bh() API, and "srcu"
49 for the "srcu_read_lock()" API.
50
47verbose Enable debug printk()s. Default is disabled. 51verbose Enable debug printk()s. Default is disabled.
48 52
49 53
@@ -51,14 +55,14 @@ OUTPUT
51 55
52The statistics output is as follows: 56The statistics output is as follows:
53 57
54 rcutorture: --- Start of test: nreaders=16 stat_interval=0 verbose=0 58 rcu-torture: --- Start of test: nreaders=16 stat_interval=0 verbose=0
55 rcutorture: rtc: 0000000000000000 ver: 1916 tfle: 0 rta: 1916 rtaf: 0 rtf: 1915 59 rcu-torture: rtc: 0000000000000000 ver: 1916 tfle: 0 rta: 1916 rtaf: 0 rtf: 1915
56 rcutorture: Reader Pipe: 1466408 9747 0 0 0 0 0 0 0 0 0 60 rcu-torture: Reader Pipe: 1466408 9747 0 0 0 0 0 0 0 0 0
57 rcutorture: Reader Batch: 1464477 11678 0 0 0 0 0 0 0 0 61 rcu-torture: Reader Batch: 1464477 11678 0 0 0 0 0 0 0 0
58 rcutorture: Free-Block Circulation: 1915 1915 1915 1915 1915 1915 1915 1915 1915 1915 0 62 rcu-torture: Free-Block Circulation: 1915 1915 1915 1915 1915 1915 1915 1915 1915 1915 0
59 rcutorture: --- End of test 63 rcu-torture: --- End of test
60 64
61The command "dmesg | grep rcutorture:" will extract this information on 65The command "dmesg | grep torture:" will extract this information on
62most systems. On more esoteric configurations, it may be necessary to 66most systems. On more esoteric configurations, it may be necessary to
63use other commands to access the output of the printk()s used by 67use other commands to access the output of the printk()s used by
64the RCU torture test. The printk()s use KERN_ALERT, so they should 68the RCU torture test. The printk()s use KERN_ALERT, so they should
@@ -124,7 +128,7 @@ The following script may be used to torture RCU:
124 modprobe rcutorture 128 modprobe rcutorture
125 sleep 100 129 sleep 100
126 rmmod rcutorture 130 rmmod rcutorture
127 dmesg | grep rcutorture: 131 dmesg | grep torture:
128 132
129The output can be manually inspected for the error flag of "!!!". 133The output can be manually inspected for the error flag of "!!!".
130One could of course create a more elaborate script that automatically 134One could of course create a more elaborate script that automatically
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c
index 2a65bd8a3d10..c96b5edd6ed1 100644
--- a/kernel/rcutorture.c
+++ b/kernel/rcutorture.c
@@ -53,6 +53,7 @@ static int stat_interval; /* Interval between stats, in seconds. */
53static int verbose; /* Print more debug info. */ 53static int verbose; /* Print more debug info. */
54static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ 54static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */
55static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/ 55static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/
56static char *torture_type = "rcu"; /* What to torture. */
56 57
57module_param(nreaders, int, 0); 58module_param(nreaders, int, 0);
58MODULE_PARM_DESC(nreaders, "Number of RCU reader threads"); 59MODULE_PARM_DESC(nreaders, "Number of RCU reader threads");
@@ -64,13 +65,16 @@ module_param(test_no_idle_hz, bool, 0);
64MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs"); 65MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs");
65module_param(shuffle_interval, int, 0); 66module_param(shuffle_interval, int, 0);
66MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); 67MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles");
67#define TORTURE_FLAG "rcutorture: " 68module_param(torture_type, charp, 0);
69MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu)");
70
71#define TORTURE_FLAG "-torture:"
68#define PRINTK_STRING(s) \ 72#define PRINTK_STRING(s) \
69 do { printk(KERN_ALERT TORTURE_FLAG s "\n"); } while (0) 73 do { printk(KERN_ALERT "%s" TORTURE_FLAG s "\n", torture_type); } while (0)
70#define VERBOSE_PRINTK_STRING(s) \ 74#define VERBOSE_PRINTK_STRING(s) \
71 do { if (verbose) printk(KERN_ALERT TORTURE_FLAG s "\n"); } while (0) 75 do { if (verbose) printk(KERN_ALERT "%s" TORTURE_FLAG s "\n", torture_type); } while (0)
72#define VERBOSE_PRINTK_ERRSTRING(s) \ 76#define VERBOSE_PRINTK_ERRSTRING(s) \
73 do { if (verbose) printk(KERN_ALERT TORTURE_FLAG "!!! " s "\n"); } while (0) 77 do { if (verbose) printk(KERN_ALERT "%s" TORTURE_FLAG "!!! " s "\n", torture_type); } while (0)
74 78
75static char printk_buf[4096]; 79static char printk_buf[4096];
76 80
@@ -139,28 +143,6 @@ rcu_torture_free(struct rcu_torture *p)
139 spin_unlock_bh(&rcu_torture_lock); 143 spin_unlock_bh(&rcu_torture_lock);
140} 144}
141 145
142static void
143rcu_torture_cb(struct rcu_head *p)
144{
145 int i;
146 struct rcu_torture *rp = container_of(p, struct rcu_torture, rtort_rcu);
147
148 if (fullstop) {
149 /* Test is ending, just drop callbacks on the floor. */
150 /* The next initialization will pick up the pieces. */
151 return;
152 }
153 i = rp->rtort_pipe_count;
154 if (i > RCU_TORTURE_PIPE_LEN)
155 i = RCU_TORTURE_PIPE_LEN;
156 atomic_inc(&rcu_torture_wcount[i]);
157 if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) {
158 rp->rtort_mbtest = 0;
159 rcu_torture_free(rp);
160 } else
161 call_rcu(p, rcu_torture_cb);
162}
163
164struct rcu_random_state { 146struct rcu_random_state {
165 unsigned long rrs_state; 147 unsigned long rrs_state;
166 unsigned long rrs_count; 148 unsigned long rrs_count;
@@ -191,6 +173,83 @@ rcu_random(struct rcu_random_state *rrsp)
191} 173}
192 174
193/* 175/*
176 * Operations vector for selecting different types of tests.
177 */
178
179struct rcu_torture_ops {
180 void (*init)(void);
181 void (*cleanup)(void);
182 int (*readlock)(void);
183 void (*readunlock)(int idx);
184 int (*completed)(void);
185 void (*deferredfree)(struct rcu_torture *p);
186 int (*stats)(char *page);
187 char *name;
188};
189static struct rcu_torture_ops *cur_ops = NULL;
190
191/*
192 * Definitions for rcu torture testing.
193 */
194
195static int rcu_torture_read_lock(void)
196{
197 rcu_read_lock();
198 return 0;
199}
200
201static void rcu_torture_read_unlock(int idx)
202{
203 rcu_read_unlock();
204}
205
206static int rcu_torture_completed(void)
207{
208 return rcu_batches_completed();
209}
210
211static void
212rcu_torture_cb(struct rcu_head *p)
213{
214 int i;
215 struct rcu_torture *rp = container_of(p, struct rcu_torture, rtort_rcu);
216
217 if (fullstop) {
218 /* Test is ending, just drop callbacks on the floor. */
219 /* The next initialization will pick up the pieces. */
220 return;
221 }
222 i = rp->rtort_pipe_count;
223 if (i > RCU_TORTURE_PIPE_LEN)
224 i = RCU_TORTURE_PIPE_LEN;
225 atomic_inc(&rcu_torture_wcount[i]);
226 if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) {
227 rp->rtort_mbtest = 0;
228 rcu_torture_free(rp);
229 } else
230 cur_ops->deferredfree(rp);
231}
232
233static void rcu_torture_deferred_free(struct rcu_torture *p)
234{
235 call_rcu(&p->rtort_rcu, rcu_torture_cb);
236}
237
238static struct rcu_torture_ops rcu_ops = {
239 .init = NULL,
240 .cleanup = NULL,
241 .readlock = rcu_torture_read_lock,
242 .readunlock = rcu_torture_read_unlock,
243 .completed = rcu_torture_completed,
244 .deferredfree = rcu_torture_deferred_free,
245 .stats = NULL,
246 .name = "rcu"
247};
248
249static struct rcu_torture_ops *torture_ops[] =
250 { &rcu_ops, NULL };
251
252/*
194 * RCU torture writer kthread. Repeatedly substitutes a new structure 253 * RCU torture writer kthread. Repeatedly substitutes a new structure
195 * for that pointed to by rcu_torture_current, freeing the old structure 254 * for that pointed to by rcu_torture_current, freeing the old structure
196 * after a series of grace periods (the "pipeline"). 255 * after a series of grace periods (the "pipeline").
@@ -209,8 +268,6 @@ rcu_torture_writer(void *arg)
209 268
210 do { 269 do {
211 schedule_timeout_uninterruptible(1); 270 schedule_timeout_uninterruptible(1);
212 if (rcu_batches_completed() == oldbatch)
213 continue;
214 if ((rp = rcu_torture_alloc()) == NULL) 271 if ((rp = rcu_torture_alloc()) == NULL)
215 continue; 272 continue;
216 rp->rtort_pipe_count = 0; 273 rp->rtort_pipe_count = 0;
@@ -225,10 +282,10 @@ rcu_torture_writer(void *arg)
225 i = RCU_TORTURE_PIPE_LEN; 282 i = RCU_TORTURE_PIPE_LEN;
226 atomic_inc(&rcu_torture_wcount[i]); 283 atomic_inc(&rcu_torture_wcount[i]);
227 old_rp->rtort_pipe_count++; 284 old_rp->rtort_pipe_count++;
228 call_rcu(&old_rp->rtort_rcu, rcu_torture_cb); 285 cur_ops->deferredfree(old_rp);
229 } 286 }
230 rcu_torture_current_version++; 287 rcu_torture_current_version++;
231 oldbatch = rcu_batches_completed(); 288 oldbatch = cur_ops->completed();
232 } while (!kthread_should_stop() && !fullstop); 289 } while (!kthread_should_stop() && !fullstop);
233 VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping"); 290 VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping");
234 while (!kthread_should_stop()) 291 while (!kthread_should_stop())
@@ -246,6 +303,7 @@ static int
246rcu_torture_reader(void *arg) 303rcu_torture_reader(void *arg)
247{ 304{
248 int completed; 305 int completed;
306 int idx;
249 DEFINE_RCU_RANDOM(rand); 307 DEFINE_RCU_RANDOM(rand);
250 struct rcu_torture *p; 308 struct rcu_torture *p;
251 int pipe_count; 309 int pipe_count;
@@ -254,12 +312,12 @@ rcu_torture_reader(void *arg)
254 set_user_nice(current, 19); 312 set_user_nice(current, 19);
255 313
256 do { 314 do {
257 rcu_read_lock(); 315 idx = cur_ops->readlock();
258 completed = rcu_batches_completed(); 316 completed = cur_ops->completed();
259 p = rcu_dereference(rcu_torture_current); 317 p = rcu_dereference(rcu_torture_current);
260 if (p == NULL) { 318 if (p == NULL) {
261 /* Wait for rcu_torture_writer to get underway */ 319 /* Wait for rcu_torture_writer to get underway */
262 rcu_read_unlock(); 320 cur_ops->readunlock(idx);
263 schedule_timeout_interruptible(HZ); 321 schedule_timeout_interruptible(HZ);
264 continue; 322 continue;
265 } 323 }
@@ -273,14 +331,14 @@ rcu_torture_reader(void *arg)
273 pipe_count = RCU_TORTURE_PIPE_LEN; 331 pipe_count = RCU_TORTURE_PIPE_LEN;
274 } 332 }
275 ++__get_cpu_var(rcu_torture_count)[pipe_count]; 333 ++__get_cpu_var(rcu_torture_count)[pipe_count];
276 completed = rcu_batches_completed() - completed; 334 completed = cur_ops->completed() - completed;
277 if (completed > RCU_TORTURE_PIPE_LEN) { 335 if (completed > RCU_TORTURE_PIPE_LEN) {
278 /* Should not happen, but... */ 336 /* Should not happen, but... */
279 completed = RCU_TORTURE_PIPE_LEN; 337 completed = RCU_TORTURE_PIPE_LEN;
280 } 338 }
281 ++__get_cpu_var(rcu_torture_batch)[completed]; 339 ++__get_cpu_var(rcu_torture_batch)[completed];
282 preempt_enable(); 340 preempt_enable();
283 rcu_read_unlock(); 341 cur_ops->readunlock(idx);
284 schedule(); 342 schedule();
285 } while (!kthread_should_stop() && !fullstop); 343 } while (!kthread_should_stop() && !fullstop);
286 VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping"); 344 VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping");
@@ -311,7 +369,7 @@ rcu_torture_printk(char *page)
311 if (pipesummary[i] != 0) 369 if (pipesummary[i] != 0)
312 break; 370 break;
313 } 371 }
314 cnt += sprintf(&page[cnt], "rcutorture: "); 372 cnt += sprintf(&page[cnt], "%s%s ", torture_type, TORTURE_FLAG);
315 cnt += sprintf(&page[cnt], 373 cnt += sprintf(&page[cnt],
316 "rtc: %p ver: %ld tfle: %d rta: %d rtaf: %d rtf: %d " 374 "rtc: %p ver: %ld tfle: %d rta: %d rtaf: %d rtf: %d "
317 "rtmbe: %d", 375 "rtmbe: %d",
@@ -324,7 +382,7 @@ rcu_torture_printk(char *page)
324 atomic_read(&n_rcu_torture_mberror)); 382 atomic_read(&n_rcu_torture_mberror));
325 if (atomic_read(&n_rcu_torture_mberror) != 0) 383 if (atomic_read(&n_rcu_torture_mberror) != 0)
326 cnt += sprintf(&page[cnt], " !!!"); 384 cnt += sprintf(&page[cnt], " !!!");
327 cnt += sprintf(&page[cnt], "\nrcutorture: "); 385 cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG);
328 if (i > 1) { 386 if (i > 1) {
329 cnt += sprintf(&page[cnt], "!!! "); 387 cnt += sprintf(&page[cnt], "!!! ");
330 atomic_inc(&n_rcu_torture_error); 388 atomic_inc(&n_rcu_torture_error);
@@ -332,17 +390,19 @@ rcu_torture_printk(char *page)
332 cnt += sprintf(&page[cnt], "Reader Pipe: "); 390 cnt += sprintf(&page[cnt], "Reader Pipe: ");
333 for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) 391 for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++)
334 cnt += sprintf(&page[cnt], " %ld", pipesummary[i]); 392 cnt += sprintf(&page[cnt], " %ld", pipesummary[i]);
335 cnt += sprintf(&page[cnt], "\nrcutorture: "); 393 cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG);
336 cnt += sprintf(&page[cnt], "Reader Batch: "); 394 cnt += sprintf(&page[cnt], "Reader Batch: ");
337 for (i = 0; i < RCU_TORTURE_PIPE_LEN; i++) 395 for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++)
338 cnt += sprintf(&page[cnt], " %ld", batchsummary[i]); 396 cnt += sprintf(&page[cnt], " %ld", batchsummary[i]);
339 cnt += sprintf(&page[cnt], "\nrcutorture: "); 397 cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG);
340 cnt += sprintf(&page[cnt], "Free-Block Circulation: "); 398 cnt += sprintf(&page[cnt], "Free-Block Circulation: ");
341 for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) { 399 for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) {
342 cnt += sprintf(&page[cnt], " %d", 400 cnt += sprintf(&page[cnt], " %d",
343 atomic_read(&rcu_torture_wcount[i])); 401 atomic_read(&rcu_torture_wcount[i]));
344 } 402 }
345 cnt += sprintf(&page[cnt], "\n"); 403 cnt += sprintf(&page[cnt], "\n");
404 if (cur_ops->stats != NULL)
405 cnt += cur_ops->stats(&page[cnt]);
346 return cnt; 406 return cnt;
347} 407}
348 408
@@ -444,11 +504,11 @@ rcu_torture_shuffle(void *arg)
444static inline void 504static inline void
445rcu_torture_print_module_parms(char *tag) 505rcu_torture_print_module_parms(char *tag)
446{ 506{
447 printk(KERN_ALERT TORTURE_FLAG "--- %s: nreaders=%d " 507 printk(KERN_ALERT "%s" TORTURE_FLAG "--- %s: nreaders=%d "
448 "stat_interval=%d verbose=%d test_no_idle_hz=%d " 508 "stat_interval=%d verbose=%d test_no_idle_hz=%d "
449 "shuffle_interval = %d\n", 509 "shuffle_interval = %d\n",
450 tag, nrealreaders, stat_interval, verbose, test_no_idle_hz, 510 torture_type, tag, nrealreaders, stat_interval, verbose,
451 shuffle_interval); 511 test_no_idle_hz, shuffle_interval);
452} 512}
453 513
454static void 514static void
@@ -493,6 +553,9 @@ rcu_torture_cleanup(void)
493 rcu_barrier(); 553 rcu_barrier();
494 554
495 rcu_torture_stats_print(); /* -After- the stats thread is stopped! */ 555 rcu_torture_stats_print(); /* -After- the stats thread is stopped! */
556
557 if (cur_ops->cleanup != NULL)
558 cur_ops->cleanup();
496 if (atomic_read(&n_rcu_torture_error)) 559 if (atomic_read(&n_rcu_torture_error))
497 rcu_torture_print_module_parms("End of test: FAILURE"); 560 rcu_torture_print_module_parms("End of test: FAILURE");
498 else 561 else
@@ -508,6 +571,20 @@ rcu_torture_init(void)
508 571
509 /* Process args and tell the world that the torturer is on the job. */ 572 /* Process args and tell the world that the torturer is on the job. */
510 573
574 for (i = 0; cur_ops = torture_ops[i], cur_ops != NULL; i++) {
575 cur_ops = torture_ops[i];
576 if (strcmp(torture_type, cur_ops->name) == 0) {
577 break;
578 }
579 }
580 if (cur_ops == NULL) {
581 printk(KERN_ALERT "rcutorture: invalid torture type: \"%s\"\n",
582 torture_type);
583 return (-EINVAL);
584 }
585 if (cur_ops->init != NULL)
586 cur_ops->init(); /* no "goto unwind" prior to this point!!! */
587
511 if (nreaders >= 0) 588 if (nreaders >= 0)
512 nrealreaders = nreaders; 589 nrealreaders = nreaders;
513 else 590 else