diff options
-rw-r--r-- | include/linux/torture.h | 7 | ||||
-rw-r--r-- | kernel/rcu/rcutorture.c | 69 | ||||
-rw-r--r-- | kernel/torture.c | 87 |
3 files changed, 104 insertions, 59 deletions
diff --git a/include/linux/torture.h b/include/linux/torture.h index 0259db38bfb0..203f127d9ddf 100644 --- a/include/linux/torture.h +++ b/include/linux/torture.h | |||
@@ -75,8 +75,13 @@ int torture_shuffle_init(long shuffint); | |||
75 | /* Shutdown task absorption, for when the tasks cannot safely be killed. */ | 75 | /* Shutdown task absorption, for when the tasks cannot safely be killed. */ |
76 | void torture_shutdown_absorb(const char *title); | 76 | void torture_shutdown_absorb(const char *title); |
77 | 77 | ||
78 | /* Task stuttering, which forces load/no-load transitions. */ | ||
79 | void stutter_wait(const char *title); | ||
80 | int torture_stutter_init(int s); | ||
81 | void torture_stutter_cleanup(void); | ||
82 | |||
78 | /* Initialization and cleanup. */ | 83 | /* Initialization and cleanup. */ |
79 | void torture_init_begin(char *ttype, bool v); | 84 | void torture_init_begin(char *ttype, bool v, int *runnable); |
80 | void torture_init_end(void); | 85 | void torture_init_end(void); |
81 | bool torture_cleanup(void); | 86 | bool torture_cleanup(void); |
82 | bool torture_must_stop(void); | 87 | bool torture_must_stop(void); |
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 9357c88cc8cc..4329ad14f8dc 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c | |||
@@ -103,7 +103,6 @@ static struct task_struct *writer_task; | |||
103 | static struct task_struct **fakewriter_tasks; | 103 | static struct task_struct **fakewriter_tasks; |
104 | static struct task_struct **reader_tasks; | 104 | static struct task_struct **reader_tasks; |
105 | static struct task_struct *stats_task; | 105 | static struct task_struct *stats_task; |
106 | static struct task_struct *stutter_task; | ||
107 | static struct task_struct *fqs_task; | 106 | static struct task_struct *fqs_task; |
108 | static struct task_struct *boost_tasks[NR_CPUS]; | 107 | static struct task_struct *boost_tasks[NR_CPUS]; |
109 | static struct task_struct *shutdown_task; | 108 | static struct task_struct *shutdown_task; |
@@ -145,8 +144,6 @@ static long n_barrier_attempts; | |||
145 | static long n_barrier_successes; | 144 | static long n_barrier_successes; |
146 | static struct list_head rcu_torture_removed; | 145 | static struct list_head rcu_torture_removed; |
147 | 146 | ||
148 | static int stutter_pause_test; | ||
149 | |||
150 | #if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE) | 147 | #if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE) |
151 | #define RCUTORTURE_RUNNABLE_INIT 1 | 148 | #define RCUTORTURE_RUNNABLE_INIT 1 |
152 | #else | 149 | #else |
@@ -222,18 +219,6 @@ rcu_torture_free(struct rcu_torture *p) | |||
222 | spin_unlock_bh(&rcu_torture_lock); | 219 | spin_unlock_bh(&rcu_torture_lock); |
223 | } | 220 | } |
224 | 221 | ||
225 | static void | ||
226 | rcu_stutter_wait(const char *title) | ||
227 | { | ||
228 | while (stutter_pause_test || !rcutorture_runnable) { | ||
229 | if (rcutorture_runnable) | ||
230 | schedule_timeout_interruptible(1); | ||
231 | else | ||
232 | schedule_timeout_interruptible(round_jiffies_relative(HZ)); | ||
233 | torture_shutdown_absorb(title); | ||
234 | } | ||
235 | } | ||
236 | |||
237 | /* | 222 | /* |
238 | * Operations vector for selecting different types of tests. | 223 | * Operations vector for selecting different types of tests. |
239 | */ | 224 | */ |
@@ -571,7 +556,7 @@ static int rcu_torture_boost(void *arg) | |||
571 | oldstarttime = boost_starttime; | 556 | oldstarttime = boost_starttime; |
572 | while (ULONG_CMP_LT(jiffies, oldstarttime)) { | 557 | while (ULONG_CMP_LT(jiffies, oldstarttime)) { |
573 | schedule_timeout_interruptible(oldstarttime - jiffies); | 558 | schedule_timeout_interruptible(oldstarttime - jiffies); |
574 | rcu_stutter_wait("rcu_torture_boost"); | 559 | stutter_wait("rcu_torture_boost"); |
575 | if (torture_must_stop()) | 560 | if (torture_must_stop()) |
576 | goto checkwait; | 561 | goto checkwait; |
577 | } | 562 | } |
@@ -593,7 +578,7 @@ static int rcu_torture_boost(void *arg) | |||
593 | call_rcu_time = jiffies; | 578 | call_rcu_time = jiffies; |
594 | } | 579 | } |
595 | cond_resched(); | 580 | cond_resched(); |
596 | rcu_stutter_wait("rcu_torture_boost"); | 581 | stutter_wait("rcu_torture_boost"); |
597 | if (torture_must_stop()) | 582 | if (torture_must_stop()) |
598 | goto checkwait; | 583 | goto checkwait; |
599 | } | 584 | } |
@@ -618,7 +603,7 @@ static int rcu_torture_boost(void *arg) | |||
618 | } | 603 | } |
619 | 604 | ||
620 | /* Go do the stutter. */ | 605 | /* Go do the stutter. */ |
621 | checkwait: rcu_stutter_wait("rcu_torture_boost"); | 606 | checkwait: stutter_wait("rcu_torture_boost"); |
622 | } while (!torture_must_stop()); | 607 | } while (!torture_must_stop()); |
623 | 608 | ||
624 | /* Clean up and exit. */ | 609 | /* Clean up and exit. */ |
@@ -656,7 +641,7 @@ rcu_torture_fqs(void *arg) | |||
656 | udelay(fqs_holdoff); | 641 | udelay(fqs_holdoff); |
657 | fqs_burst_remaining -= fqs_holdoff; | 642 | fqs_burst_remaining -= fqs_holdoff; |
658 | } | 643 | } |
659 | rcu_stutter_wait("rcu_torture_fqs"); | 644 | stutter_wait("rcu_torture_fqs"); |
660 | } while (!torture_must_stop()); | 645 | } while (!torture_must_stop()); |
661 | VERBOSE_TOROUT_STRING("rcu_torture_fqs task stopping"); | 646 | VERBOSE_TOROUT_STRING("rcu_torture_fqs task stopping"); |
662 | torture_shutdown_absorb("rcu_torture_fqs"); | 647 | torture_shutdown_absorb("rcu_torture_fqs"); |
@@ -728,7 +713,7 @@ rcu_torture_writer(void *arg) | |||
728 | } | 713 | } |
729 | } | 714 | } |
730 | rcutorture_record_progress(++rcu_torture_current_version); | 715 | rcutorture_record_progress(++rcu_torture_current_version); |
731 | rcu_stutter_wait("rcu_torture_writer"); | 716 | stutter_wait("rcu_torture_writer"); |
732 | } while (!torture_must_stop()); | 717 | } while (!torture_must_stop()); |
733 | VERBOSE_TOROUT_STRING("rcu_torture_writer task stopping"); | 718 | VERBOSE_TOROUT_STRING("rcu_torture_writer task stopping"); |
734 | torture_shutdown_absorb("rcu_torture_writer"); | 719 | torture_shutdown_absorb("rcu_torture_writer"); |
@@ -765,7 +750,7 @@ rcu_torture_fakewriter(void *arg) | |||
765 | } else { | 750 | } else { |
766 | cur_ops->exp_sync(); | 751 | cur_ops->exp_sync(); |
767 | } | 752 | } |
768 | rcu_stutter_wait("rcu_torture_fakewriter"); | 753 | stutter_wait("rcu_torture_fakewriter"); |
769 | } while (!torture_must_stop()); | 754 | } while (!torture_must_stop()); |
770 | 755 | ||
771 | VERBOSE_TOROUT_STRING("rcu_torture_fakewriter task stopping"); | 756 | VERBOSE_TOROUT_STRING("rcu_torture_fakewriter task stopping"); |
@@ -910,7 +895,7 @@ rcu_torture_reader(void *arg) | |||
910 | preempt_enable(); | 895 | preempt_enable(); |
911 | cur_ops->readunlock(idx); | 896 | cur_ops->readunlock(idx); |
912 | schedule(); | 897 | schedule(); |
913 | rcu_stutter_wait("rcu_torture_reader"); | 898 | stutter_wait("rcu_torture_reader"); |
914 | } while (!torture_must_stop()); | 899 | } while (!torture_must_stop()); |
915 | VERBOSE_TOROUT_STRING("rcu_torture_reader task stopping"); | 900 | VERBOSE_TOROUT_STRING("rcu_torture_reader task stopping"); |
916 | torture_shutdown_absorb("rcu_torture_reader"); | 901 | torture_shutdown_absorb("rcu_torture_reader"); |
@@ -1034,25 +1019,6 @@ rcu_torture_stats(void *arg) | |||
1034 | return 0; | 1019 | return 0; |
1035 | } | 1020 | } |
1036 | 1021 | ||
1037 | /* Cause the rcutorture test to "stutter", starting and stopping all | ||
1038 | * threads periodically. | ||
1039 | */ | ||
1040 | static int | ||
1041 | rcu_torture_stutter(void *arg) | ||
1042 | { | ||
1043 | VERBOSE_TOROUT_STRING("rcu_torture_stutter task started"); | ||
1044 | do { | ||
1045 | schedule_timeout_interruptible(stutter * HZ); | ||
1046 | stutter_pause_test = 1; | ||
1047 | if (!kthread_should_stop()) | ||
1048 | schedule_timeout_interruptible(stutter * HZ); | ||
1049 | stutter_pause_test = 0; | ||
1050 | torture_shutdown_absorb("rcu_torture_stutter"); | ||
1051 | } while (!kthread_should_stop()); | ||
1052 | VERBOSE_TOROUT_STRING("rcu_torture_stutter task stopping"); | ||
1053 | return 0; | ||
1054 | } | ||
1055 | |||
1056 | static inline void | 1022 | static inline void |
1057 | rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag) | 1023 | rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag) |
1058 | { | 1024 | { |
@@ -1403,11 +1369,7 @@ rcu_torture_cleanup(void) | |||
1403 | 1369 | ||
1404 | rcu_torture_barrier_cleanup(); | 1370 | rcu_torture_barrier_cleanup(); |
1405 | rcu_torture_stall_cleanup(); | 1371 | rcu_torture_stall_cleanup(); |
1406 | if (stutter_task) { | 1372 | torture_stutter_cleanup(); |
1407 | VERBOSE_TOROUT_STRING("Stopping rcu_torture_stutter task"); | ||
1408 | kthread_stop(stutter_task); | ||
1409 | } | ||
1410 | stutter_task = NULL; | ||
1411 | 1373 | ||
1412 | if (writer_task) { | 1374 | if (writer_task) { |
1413 | VERBOSE_TOROUT_STRING("Stopping rcu_torture_writer task"); | 1375 | VERBOSE_TOROUT_STRING("Stopping rcu_torture_writer task"); |
@@ -1548,7 +1510,7 @@ rcu_torture_init(void) | |||
1548 | &rcu_ops, &rcu_bh_ops, &srcu_ops, &sched_ops, | 1510 | &rcu_ops, &rcu_bh_ops, &srcu_ops, &sched_ops, |
1549 | }; | 1511 | }; |
1550 | 1512 | ||
1551 | torture_init_begin(torture_type, verbose); | 1513 | torture_init_begin(torture_type, verbose, &rcutorture_runnable); |
1552 | 1514 | ||
1553 | /* Process args and tell the world that the torturer is on the job. */ | 1515 | /* Process args and tell the world that the torturer is on the job. */ |
1554 | for (i = 0; i < ARRAY_SIZE(torture_ops); i++) { | 1516 | for (i = 0; i < ARRAY_SIZE(torture_ops); i++) { |
@@ -1682,21 +1644,14 @@ rcu_torture_init(void) | |||
1682 | if (stutter < 0) | 1644 | if (stutter < 0) |
1683 | stutter = 0; | 1645 | stutter = 0; |
1684 | if (stutter) { | 1646 | if (stutter) { |
1685 | /* Create the stutter thread */ | 1647 | firsterr = torture_stutter_init(stutter * HZ); |
1686 | stutter_task = kthread_run(rcu_torture_stutter, NULL, | 1648 | if (firsterr) |
1687 | "rcu_torture_stutter"); | ||
1688 | if (IS_ERR(stutter_task)) { | ||
1689 | firsterr = PTR_ERR(stutter_task); | ||
1690 | VERBOSE_TOROUT_ERRSTRING("Failed to create stutter"); | ||
1691 | stutter_task = NULL; | ||
1692 | goto unwind; | 1649 | goto unwind; |
1693 | } | ||
1694 | torture_shuffle_task_register(stutter_task); | ||
1695 | } | 1650 | } |
1696 | if (fqs_duration < 0) | 1651 | if (fqs_duration < 0) |
1697 | fqs_duration = 0; | 1652 | fqs_duration = 0; |
1698 | if (fqs_duration) { | 1653 | if (fqs_duration) { |
1699 | /* Create the stutter thread */ | 1654 | /* Create the fqs thread */ |
1700 | fqs_task = kthread_run(rcu_torture_fqs, NULL, | 1655 | fqs_task = kthread_run(rcu_torture_fqs, NULL, |
1701 | "rcu_torture_fqs"); | 1656 | "rcu_torture_fqs"); |
1702 | if (IS_ERR(fqs_task)) { | 1657 | if (IS_ERR(fqs_task)) { |
diff --git a/kernel/torture.c b/kernel/torture.c index d51de3029a5c..b30c2ee78580 100644 --- a/kernel/torture.c +++ b/kernel/torture.c | |||
@@ -58,6 +58,7 @@ static bool verbose; | |||
58 | #define FULLSTOP_RMMOD 2 /* Normal rmmod of torture. */ | 58 | #define FULLSTOP_RMMOD 2 /* Normal rmmod of torture. */ |
59 | static int fullstop = FULLSTOP_RMMOD; | 59 | static int fullstop = FULLSTOP_RMMOD; |
60 | static DEFINE_MUTEX(fullstop_mutex); | 60 | static DEFINE_MUTEX(fullstop_mutex); |
61 | static int *torture_runnable; | ||
61 | 62 | ||
62 | #ifdef CONFIG_HOTPLUG_CPU | 63 | #ifdef CONFIG_HOTPLUG_CPU |
63 | 64 | ||
@@ -453,16 +454,100 @@ static struct notifier_block torture_shutdown_nb = { | |||
453 | }; | 454 | }; |
454 | 455 | ||
455 | /* | 456 | /* |
457 | * Variables for stuttering, which means to periodically pause and | ||
458 | * restart testing in order to catch bugs that appear when load is | ||
459 | * suddenly applied to or removed from the system. | ||
460 | */ | ||
461 | static struct task_struct *stutter_task; | ||
462 | static int stutter_pause_test; | ||
463 | static int stutter; | ||
464 | |||
465 | /* | ||
466 | * Block until the stutter interval ends. This must be called periodically | ||
467 | * by all running kthreads that need to be subject to stuttering. | ||
468 | */ | ||
469 | void stutter_wait(const char *title) | ||
470 | { | ||
471 | while (ACCESS_ONCE(stutter_pause_test) || | ||
472 | (torture_runnable && !ACCESS_ONCE(*torture_runnable))) { | ||
473 | if (stutter_pause_test) | ||
474 | schedule_timeout_interruptible(1); | ||
475 | else | ||
476 | schedule_timeout_interruptible(round_jiffies_relative(HZ)); | ||
477 | torture_shutdown_absorb(title); | ||
478 | } | ||
479 | } | ||
480 | EXPORT_SYMBOL_GPL(stutter_wait); | ||
481 | |||
482 | /* | ||
483 | * Cause the torture test to "stutter", starting and stopping all | ||
484 | * threads periodically. | ||
485 | */ | ||
486 | static int torture_stutter(void *arg) | ||
487 | { | ||
488 | VERBOSE_TOROUT_STRING("torture_stutter task started"); | ||
489 | do { | ||
490 | if (!torture_must_stop()) { | ||
491 | schedule_timeout_interruptible(stutter); | ||
492 | ACCESS_ONCE(stutter_pause_test) = 1; | ||
493 | } | ||
494 | if (!torture_must_stop()) | ||
495 | schedule_timeout_interruptible(stutter); | ||
496 | ACCESS_ONCE(stutter_pause_test) = 0; | ||
497 | torture_shutdown_absorb("torture_stutter"); | ||
498 | } while (!torture_must_stop()); | ||
499 | VERBOSE_TOROUT_STRING("torture_stutter task stopping"); | ||
500 | return 0; | ||
501 | } | ||
502 | |||
503 | /* | ||
504 | * Initialize and kick off the torture_stutter kthread. | ||
505 | */ | ||
506 | int torture_stutter_init(int s) | ||
507 | { | ||
508 | int ret; | ||
509 | |||
510 | stutter = s; | ||
511 | stutter_task = kthread_run(torture_stutter, NULL, "torture_stutter"); | ||
512 | if (IS_ERR(stutter_task)) { | ||
513 | ret = PTR_ERR(stutter_task); | ||
514 | VERBOSE_TOROUT_ERRSTRING("Failed to create stutter"); | ||
515 | stutter_task = NULL; | ||
516 | return ret; | ||
517 | } | ||
518 | torture_shuffle_task_register(stutter_task); | ||
519 | return 0; | ||
520 | } | ||
521 | EXPORT_SYMBOL_GPL(torture_stutter_init); | ||
522 | |||
523 | /* | ||
524 | * Cleanup after the torture_stutter kthread. | ||
525 | */ | ||
526 | void torture_stutter_cleanup(void) | ||
527 | { | ||
528 | if (!stutter_task) | ||
529 | return; | ||
530 | VERBOSE_TOROUT_STRING("Stopping torture_stutter task"); | ||
531 | kthread_stop(stutter_task); | ||
532 | stutter_task = NULL; | ||
533 | } | ||
534 | EXPORT_SYMBOL_GPL(torture_stutter_cleanup); | ||
535 | |||
536 | /* | ||
456 | * Initialize torture module. Please note that this is -not- invoked via | 537 | * Initialize torture module. Please note that this is -not- invoked via |
457 | * the usual module_init() mechanism, but rather by an explicit call from | 538 | * the usual module_init() mechanism, but rather by an explicit call from |
458 | * the client torture module. This call must be paired with a later | 539 | * the client torture module. This call must be paired with a later |
459 | * torture_init_end(). | 540 | * torture_init_end(). |
541 | * | ||
542 | * The runnable parameter points to a flag that controls whether or not | ||
543 | * the test is currently runnable. If there is no such flag, pass in NULL. | ||
460 | */ | 544 | */ |
461 | void __init torture_init_begin(char *ttype, bool v) | 545 | void __init torture_init_begin(char *ttype, bool v, int *runnable) |
462 | { | 546 | { |
463 | mutex_lock(&fullstop_mutex); | 547 | mutex_lock(&fullstop_mutex); |
464 | torture_type = ttype; | 548 | torture_type = ttype; |
465 | verbose = v; | 549 | verbose = v; |
550 | torture_runnable = runnable; | ||
466 | fullstop = FULLSTOP_DONTSTOP; | 551 | fullstop = FULLSTOP_DONTSTOP; |
467 | 552 | ||
468 | } | 553 | } |