diff options
-rw-r--r-- | Documentation/locking/locktorture.txt | 16 | ||||
-rw-r--r-- | kernel/locking/locktorture.c | 176 |
2 files changed, 168 insertions, 24 deletions
diff --git a/Documentation/locking/locktorture.txt b/Documentation/locking/locktorture.txt index f2a905b27862..7a72621b924f 100644 --- a/Documentation/locking/locktorture.txt +++ b/Documentation/locking/locktorture.txt | |||
@@ -29,6 +29,11 @@ nwriters_stress Number of kernel threads that will stress exclusive lock | |||
29 | ownership (writers). The default value is twice the number | 29 | ownership (writers). The default value is twice the number |
30 | of online CPUs. | 30 | of online CPUs. |
31 | 31 | ||
32 | nreaders_stress Number of kernel threads that will stress shared lock | ||
33 | ownership (readers). The default is the same amount of writer | ||
34 | locks. If the user did not specify nwriters_stress, then | ||
35 | both readers and writers be the amount of online CPUs. | ||
36 | |||
32 | torture_type Type of lock to torture. By default, only spinlocks will | 37 | torture_type Type of lock to torture. By default, only spinlocks will |
33 | be tortured. This module can torture the following locks, | 38 | be tortured. This module can torture the following locks, |
34 | with string values as follows: | 39 | with string values as follows: |
@@ -97,15 +102,18 @@ STATISTICS | |||
97 | Statistics are printed in the following format: | 102 | Statistics are printed in the following format: |
98 | 103 | ||
99 | spin_lock-torture: Writes: Total: 93746064 Max/Min: 0/0 Fail: 0 | 104 | spin_lock-torture: Writes: Total: 93746064 Max/Min: 0/0 Fail: 0 |
100 | (A) (B) (C) (D) | 105 | (A) (B) (C) (D) (E) |
101 | 106 | ||
102 | (A): Lock type that is being tortured -- torture_type parameter. | 107 | (A): Lock type that is being tortured -- torture_type parameter. |
103 | 108 | ||
104 | (B): Number of times the lock was acquired. | 109 | (B): Number of writer lock acquisitions. If dealing with a read/write primitive |
110 | a second "Reads" statistics line is printed. | ||
111 | |||
112 | (C): Number of times the lock was acquired. | ||
105 | 113 | ||
106 | (C): Min and max number of times threads failed to acquire the lock. | 114 | (D): Min and max number of times threads failed to acquire the lock. |
107 | 115 | ||
108 | (D): true/false values if there were errors acquiring the lock. This should | 116 | (E): true/false values if there were errors acquiring the lock. This should |
109 | -only- be positive if there is a bug in the locking primitive's | 117 | -only- be positive if there is a bug in the locking primitive's |
110 | implementation. Otherwise a lock should never fail (i.e., spin_lock()). | 118 | implementation. Otherwise a lock should never fail (i.e., spin_lock()). |
111 | Of course, the same applies for (C), above. A dummy example of this is | 119 | Of course, the same applies for (C), above. A dummy example of this is |
diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c index 988267cc92c1..c1073d79e440 100644 --- a/kernel/locking/locktorture.c +++ b/kernel/locking/locktorture.c | |||
@@ -52,6 +52,8 @@ MODULE_AUTHOR("Paul E. McKenney <paulmck@us.ibm.com>"); | |||
52 | 52 | ||
53 | torture_param(int, nwriters_stress, -1, | 53 | torture_param(int, nwriters_stress, -1, |
54 | "Number of write-locking stress-test threads"); | 54 | "Number of write-locking stress-test threads"); |
55 | torture_param(int, nreaders_stress, -1, | ||
56 | "Number of read-locking stress-test threads"); | ||
55 | torture_param(int, onoff_holdoff, 0, "Time after boot before CPU hotplugs (s)"); | 57 | torture_param(int, onoff_holdoff, 0, "Time after boot before CPU hotplugs (s)"); |
56 | torture_param(int, onoff_interval, 0, | 58 | torture_param(int, onoff_interval, 0, |
57 | "Time between CPU hotplugs (s), 0=disable"); | 59 | "Time between CPU hotplugs (s), 0=disable"); |
@@ -74,15 +76,19 @@ static atomic_t n_lock_torture_errors; | |||
74 | 76 | ||
75 | static struct task_struct *stats_task; | 77 | static struct task_struct *stats_task; |
76 | static struct task_struct **writer_tasks; | 78 | static struct task_struct **writer_tasks; |
79 | static struct task_struct **reader_tasks; | ||
77 | 80 | ||
78 | static int nrealwriters_stress; | 81 | static int nrealwriters_stress; |
79 | static bool lock_is_write_held; | 82 | static bool lock_is_write_held; |
83 | static int nrealreaders_stress; | ||
84 | static bool lock_is_read_held; | ||
80 | 85 | ||
81 | struct lock_stress_stats { | 86 | struct lock_stress_stats { |
82 | long n_lock_fail; | 87 | long n_lock_fail; |
83 | long n_lock_acquired; | 88 | long n_lock_acquired; |
84 | }; | 89 | }; |
85 | static struct lock_stress_stats *lwsa; /* writer statistics */ | 90 | static struct lock_stress_stats *lwsa; /* writer statistics */ |
91 | static struct lock_stress_stats *lrsa; /* reader statistics */ | ||
86 | 92 | ||
87 | #if defined(MODULE) | 93 | #if defined(MODULE) |
88 | #define LOCKTORTURE_RUNNABLE_INIT 1 | 94 | #define LOCKTORTURE_RUNNABLE_INIT 1 |
@@ -104,6 +110,9 @@ struct lock_torture_ops { | |||
104 | int (*writelock)(void); | 110 | int (*writelock)(void); |
105 | void (*write_delay)(struct torture_random_state *trsp); | 111 | void (*write_delay)(struct torture_random_state *trsp); |
106 | void (*writeunlock)(void); | 112 | void (*writeunlock)(void); |
113 | int (*readlock)(void); | ||
114 | void (*read_delay)(struct torture_random_state *trsp); | ||
115 | void (*readunlock)(void); | ||
107 | unsigned long flags; | 116 | unsigned long flags; |
108 | const char *name; | 117 | const char *name; |
109 | }; | 118 | }; |
@@ -142,6 +151,9 @@ static struct lock_torture_ops lock_busted_ops = { | |||
142 | .writelock = torture_lock_busted_write_lock, | 151 | .writelock = torture_lock_busted_write_lock, |
143 | .write_delay = torture_lock_busted_write_delay, | 152 | .write_delay = torture_lock_busted_write_delay, |
144 | .writeunlock = torture_lock_busted_write_unlock, | 153 | .writeunlock = torture_lock_busted_write_unlock, |
154 | .readlock = NULL, | ||
155 | .read_delay = NULL, | ||
156 | .readunlock = NULL, | ||
145 | .name = "lock_busted" | 157 | .name = "lock_busted" |
146 | }; | 158 | }; |
147 | 159 | ||
@@ -182,6 +194,9 @@ static struct lock_torture_ops spin_lock_ops = { | |||
182 | .writelock = torture_spin_lock_write_lock, | 194 | .writelock = torture_spin_lock_write_lock, |
183 | .write_delay = torture_spin_lock_write_delay, | 195 | .write_delay = torture_spin_lock_write_delay, |
184 | .writeunlock = torture_spin_lock_write_unlock, | 196 | .writeunlock = torture_spin_lock_write_unlock, |
197 | .readlock = NULL, | ||
198 | .read_delay = NULL, | ||
199 | .readunlock = NULL, | ||
185 | .name = "spin_lock" | 200 | .name = "spin_lock" |
186 | }; | 201 | }; |
187 | 202 | ||
@@ -205,6 +220,9 @@ static struct lock_torture_ops spin_lock_irq_ops = { | |||
205 | .writelock = torture_spin_lock_write_lock_irq, | 220 | .writelock = torture_spin_lock_write_lock_irq, |
206 | .write_delay = torture_spin_lock_write_delay, | 221 | .write_delay = torture_spin_lock_write_delay, |
207 | .writeunlock = torture_lock_spin_write_unlock_irq, | 222 | .writeunlock = torture_lock_spin_write_unlock_irq, |
223 | .readlock = NULL, | ||
224 | .read_delay = NULL, | ||
225 | .readunlock = NULL, | ||
208 | .name = "spin_lock_irq" | 226 | .name = "spin_lock_irq" |
209 | }; | 227 | }; |
210 | 228 | ||
@@ -241,6 +259,9 @@ static struct lock_torture_ops mutex_lock_ops = { | |||
241 | .writelock = torture_mutex_lock, | 259 | .writelock = torture_mutex_lock, |
242 | .write_delay = torture_mutex_delay, | 260 | .write_delay = torture_mutex_delay, |
243 | .writeunlock = torture_mutex_unlock, | 261 | .writeunlock = torture_mutex_unlock, |
262 | .readlock = NULL, | ||
263 | .read_delay = NULL, | ||
264 | .readunlock = NULL, | ||
244 | .name = "mutex_lock" | 265 | .name = "mutex_lock" |
245 | }; | 266 | }; |
246 | 267 | ||
@@ -274,28 +295,57 @@ static int lock_torture_writer(void *arg) | |||
274 | } | 295 | } |
275 | 296 | ||
276 | /* | 297 | /* |
298 | * Lock torture reader kthread. Repeatedly acquires and releases | ||
299 | * the reader lock. | ||
300 | */ | ||
301 | static int lock_torture_reader(void *arg) | ||
302 | { | ||
303 | struct lock_stress_stats *lrsp = arg; | ||
304 | static DEFINE_TORTURE_RANDOM(rand); | ||
305 | |||
306 | VERBOSE_TOROUT_STRING("lock_torture_reader task started"); | ||
307 | set_user_nice(current, MAX_NICE); | ||
308 | |||
309 | do { | ||
310 | if ((torture_random(&rand) & 0xfffff) == 0) | ||
311 | schedule_timeout_uninterruptible(1); | ||
312 | cur_ops->readlock(); | ||
313 | lock_is_read_held = 1; | ||
314 | lrsp->n_lock_acquired++; | ||
315 | cur_ops->read_delay(&rand); | ||
316 | lock_is_read_held = 0; | ||
317 | cur_ops->readunlock(); | ||
318 | stutter_wait("lock_torture_reader"); | ||
319 | } while (!torture_must_stop()); | ||
320 | torture_kthread_stopping("lock_torture_reader"); | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | /* | ||
277 | * Create an lock-torture-statistics message in the specified buffer. | 325 | * Create an lock-torture-statistics message in the specified buffer. |
278 | */ | 326 | */ |
279 | static void lock_torture_printk(char *page) | 327 | static void __torture_print_stats(char *page, |
328 | struct lock_stress_stats *statp, bool write) | ||
280 | { | 329 | { |
281 | bool fail = 0; | 330 | bool fail = 0; |
282 | int i; | 331 | int i, n_stress; |
283 | long max = 0; | 332 | long max = 0; |
284 | long min = lwsa[0].n_lock_acquired; | 333 | long min = statp[0].n_lock_acquired; |
285 | long long sum = 0; | 334 | long long sum = 0; |
286 | 335 | ||
287 | for (i = 0; i < nrealwriters_stress; i++) { | 336 | n_stress = write ? nrealwriters_stress : nrealreaders_stress; |
288 | if (lwsa[i].n_lock_fail) | 337 | for (i = 0; i < n_stress; i++) { |
338 | if (statp[i].n_lock_fail) | ||
289 | fail = true; | 339 | fail = true; |
290 | sum += lwsa[i].n_lock_acquired; | 340 | sum += statp[i].n_lock_acquired; |
291 | if (max < lwsa[i].n_lock_fail) | 341 | if (max < statp[i].n_lock_fail) |
292 | max = lwsa[i].n_lock_fail; | 342 | max = statp[i].n_lock_fail; |
293 | if (min > lwsa[i].n_lock_fail) | 343 | if (min > statp[i].n_lock_fail) |
294 | min = lwsa[i].n_lock_fail; | 344 | min = statp[i].n_lock_fail; |
295 | } | 345 | } |
296 | page += sprintf(page, "%s%s ", torture_type, TORTURE_FLAG); | ||
297 | page += sprintf(page, | 346 | page += sprintf(page, |
298 | "Writes: Total: %lld Max/Min: %ld/%ld %s Fail: %d %s\n", | 347 | "%s: Total: %lld Max/Min: %ld/%ld %s Fail: %d %s\n", |
348 | write ? "Writes" : "Reads ", | ||
299 | sum, max, min, max / 2 > min ? "???" : "", | 349 | sum, max, min, max / 2 > min ? "???" : "", |
300 | fail, fail ? "!!!" : ""); | 350 | fail, fail ? "!!!" : ""); |
301 | if (fail) | 351 | if (fail) |
@@ -315,15 +365,32 @@ static void lock_torture_stats_print(void) | |||
315 | int size = nrealwriters_stress * 200 + 8192; | 365 | int size = nrealwriters_stress * 200 + 8192; |
316 | char *buf; | 366 | char *buf; |
317 | 367 | ||
368 | if (cur_ops->readlock) | ||
369 | size += nrealreaders_stress * 200 + 8192; | ||
370 | |||
318 | buf = kmalloc(size, GFP_KERNEL); | 371 | buf = kmalloc(size, GFP_KERNEL); |
319 | if (!buf) { | 372 | if (!buf) { |
320 | pr_err("lock_torture_stats_print: Out of memory, need: %d", | 373 | pr_err("lock_torture_stats_print: Out of memory, need: %d", |
321 | size); | 374 | size); |
322 | return; | 375 | return; |
323 | } | 376 | } |
324 | lock_torture_printk(buf); | 377 | |
378 | __torture_print_stats(buf, lwsa, true); | ||
325 | pr_alert("%s", buf); | 379 | pr_alert("%s", buf); |
326 | kfree(buf); | 380 | kfree(buf); |
381 | |||
382 | if (cur_ops->readlock) { | ||
383 | buf = kmalloc(size, GFP_KERNEL); | ||
384 | if (!buf) { | ||
385 | pr_err("lock_torture_stats_print: Out of memory, need: %d", | ||
386 | size); | ||
387 | return; | ||
388 | } | ||
389 | |||
390 | __torture_print_stats(buf, lrsa, false); | ||
391 | pr_alert("%s", buf); | ||
392 | kfree(buf); | ||
393 | } | ||
327 | } | 394 | } |
328 | 395 | ||
329 | /* | 396 | /* |
@@ -350,10 +417,10 @@ lock_torture_print_module_parms(struct lock_torture_ops *cur_ops, | |||
350 | const char *tag) | 417 | const char *tag) |
351 | { | 418 | { |
352 | pr_alert("%s" TORTURE_FLAG | 419 | pr_alert("%s" TORTURE_FLAG |
353 | "--- %s%s: nwriters_stress=%d stat_interval=%d verbose=%d shuffle_interval=%d stutter=%d shutdown_secs=%d onoff_interval=%d onoff_holdoff=%d\n", | 420 | "--- %s%s: nwriters_stress=%d nreaders_stress=%d stat_interval=%d verbose=%d shuffle_interval=%d stutter=%d shutdown_secs=%d onoff_interval=%d onoff_holdoff=%d\n", |
354 | torture_type, tag, debug_lock ? " [debug]": "", | 421 | torture_type, tag, debug_lock ? " [debug]": "", |
355 | nrealwriters_stress, stat_interval, verbose, | 422 | nrealwriters_stress, nrealreaders_stress, stat_interval, |
356 | shuffle_interval, stutter, shutdown_secs, | 423 | verbose, shuffle_interval, stutter, shutdown_secs, |
357 | onoff_interval, onoff_holdoff); | 424 | onoff_interval, onoff_holdoff); |
358 | } | 425 | } |
359 | 426 | ||
@@ -372,6 +439,14 @@ static void lock_torture_cleanup(void) | |||
372 | writer_tasks = NULL; | 439 | writer_tasks = NULL; |
373 | } | 440 | } |
374 | 441 | ||
442 | if (reader_tasks) { | ||
443 | for (i = 0; i < nrealreaders_stress; i++) | ||
444 | torture_stop_kthread(lock_torture_reader, | ||
445 | reader_tasks[i]); | ||
446 | kfree(reader_tasks); | ||
447 | reader_tasks = NULL; | ||
448 | } | ||
449 | |||
375 | torture_stop_kthread(lock_torture_stats, stats_task); | 450 | torture_stop_kthread(lock_torture_stats, stats_task); |
376 | lock_torture_stats_print(); /* -After- the stats thread is stopped! */ | 451 | lock_torture_stats_print(); /* -After- the stats thread is stopped! */ |
377 | 452 | ||
@@ -389,7 +464,7 @@ static void lock_torture_cleanup(void) | |||
389 | 464 | ||
390 | static int __init lock_torture_init(void) | 465 | static int __init lock_torture_init(void) |
391 | { | 466 | { |
392 | int i; | 467 | int i, j; |
393 | int firsterr = 0; | 468 | int firsterr = 0; |
394 | static struct lock_torture_ops *torture_ops[] = { | 469 | static struct lock_torture_ops *torture_ops[] = { |
395 | &lock_busted_ops, &spin_lock_ops, &spin_lock_irq_ops, &mutex_lock_ops, | 470 | &lock_busted_ops, &spin_lock_ops, &spin_lock_irq_ops, &mutex_lock_ops, |
@@ -430,7 +505,6 @@ static int __init lock_torture_init(void) | |||
430 | if (strncmp(torture_type, "spin", 4) == 0) | 505 | if (strncmp(torture_type, "spin", 4) == 0) |
431 | debug_lock = true; | 506 | debug_lock = true; |
432 | #endif | 507 | #endif |
433 | lock_torture_print_module_parms(cur_ops, "Start of test"); | ||
434 | 508 | ||
435 | /* Initialize the statistics so that each run gets its own numbers. */ | 509 | /* Initialize the statistics so that each run gets its own numbers. */ |
436 | 510 | ||
@@ -446,8 +520,37 @@ static int __init lock_torture_init(void) | |||
446 | lwsa[i].n_lock_acquired = 0; | 520 | lwsa[i].n_lock_acquired = 0; |
447 | } | 521 | } |
448 | 522 | ||
449 | /* Start up the kthreads. */ | 523 | if (cur_ops->readlock) { |
524 | if (nreaders_stress >= 0) | ||
525 | nrealreaders_stress = nreaders_stress; | ||
526 | else { | ||
527 | /* | ||
528 | * By default distribute evenly the number of | ||
529 | * readers and writers. We still run the same number | ||
530 | * of threads as the writer-only locks default. | ||
531 | */ | ||
532 | if (nwriters_stress < 0) /* user doesn't care */ | ||
533 | nrealwriters_stress = num_online_cpus(); | ||
534 | nrealreaders_stress = nrealwriters_stress; | ||
535 | } | ||
536 | |||
537 | lock_is_read_held = 0; | ||
538 | lrsa = kmalloc(sizeof(*lrsa) * nrealreaders_stress, GFP_KERNEL); | ||
539 | if (lrsa == NULL) { | ||
540 | VERBOSE_TOROUT_STRING("lrsa: Out of memory"); | ||
541 | firsterr = -ENOMEM; | ||
542 | kfree(lwsa); | ||
543 | goto unwind; | ||
544 | } | ||
450 | 545 | ||
546 | for (i = 0; i < nrealreaders_stress; i++) { | ||
547 | lrsa[i].n_lock_fail = 0; | ||
548 | lrsa[i].n_lock_acquired = 0; | ||
549 | } | ||
550 | } | ||
551 | lock_torture_print_module_parms(cur_ops, "Start of test"); | ||
552 | |||
553 | /* Prepare torture context. */ | ||
451 | if (onoff_interval > 0) { | 554 | if (onoff_interval > 0) { |
452 | firsterr = torture_onoff_init(onoff_holdoff * HZ, | 555 | firsterr = torture_onoff_init(onoff_holdoff * HZ, |
453 | onoff_interval * HZ); | 556 | onoff_interval * HZ); |
@@ -478,11 +581,44 @@ static int __init lock_torture_init(void) | |||
478 | firsterr = -ENOMEM; | 581 | firsterr = -ENOMEM; |
479 | goto unwind; | 582 | goto unwind; |
480 | } | 583 | } |
481 | for (i = 0; i < nrealwriters_stress; i++) { | 584 | |
585 | if (cur_ops->readlock) { | ||
586 | reader_tasks = kzalloc(nrealreaders_stress * sizeof(reader_tasks[0]), | ||
587 | GFP_KERNEL); | ||
588 | if (reader_tasks == NULL) { | ||
589 | VERBOSE_TOROUT_ERRSTRING("reader_tasks: Out of memory"); | ||
590 | firsterr = -ENOMEM; | ||
591 | goto unwind; | ||
592 | } | ||
593 | } | ||
594 | |||
595 | /* | ||
596 | * Create the kthreads and start torturing (oh, those poor little locks). | ||
597 | * | ||
598 | * TODO: Note that we interleave writers with readers, giving writers a | ||
599 | * slight advantage, by creating its kthread first. This can be modified | ||
600 | * for very specific needs, or even let the user choose the policy, if | ||
601 | * ever wanted. | ||
602 | */ | ||
603 | for (i = 0, j = 0; i < nrealwriters_stress || | ||
604 | j < nrealreaders_stress; i++, j++) { | ||
605 | if (i >= nrealwriters_stress) | ||
606 | goto create_reader; | ||
607 | |||
608 | /* Create writer. */ | ||
482 | firsterr = torture_create_kthread(lock_torture_writer, &lwsa[i], | 609 | firsterr = torture_create_kthread(lock_torture_writer, &lwsa[i], |
483 | writer_tasks[i]); | 610 | writer_tasks[i]); |
484 | if (firsterr) | 611 | if (firsterr) |
485 | goto unwind; | 612 | goto unwind; |
613 | |||
614 | create_reader: | ||
615 | if (cur_ops->readlock == NULL || (j >= nrealreaders_stress)) | ||
616 | continue; | ||
617 | /* Create reader. */ | ||
618 | firsterr = torture_create_kthread(lock_torture_reader, &lrsa[j], | ||
619 | reader_tasks[j]); | ||
620 | if (firsterr) | ||
621 | goto unwind; | ||
486 | } | 622 | } |
487 | if (stat_interval > 0) { | 623 | if (stat_interval > 0) { |
488 | firsterr = torture_create_kthread(lock_torture_stats, NULL, | 624 | firsterr = torture_create_kthread(lock_torture_stats, NULL, |