diff options
Diffstat (limited to 'kernel/rcutorture.c')
| -rw-r--r-- | kernel/rcutorture.c | 94 |
1 files changed, 89 insertions, 5 deletions
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 9bb52177af02..258cdf0a91eb 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c | |||
| @@ -61,6 +61,9 @@ static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ | |||
| 61 | static int shuffle_interval = 3; /* Interval between shuffles (in sec)*/ | 61 | static int shuffle_interval = 3; /* Interval between shuffles (in sec)*/ |
| 62 | static int stutter = 5; /* Start/stop testing interval (in sec) */ | 62 | static int stutter = 5; /* Start/stop testing interval (in sec) */ |
| 63 | static int irqreader = 1; /* RCU readers from irq (timers). */ | 63 | static int irqreader = 1; /* RCU readers from irq (timers). */ |
| 64 | static int fqs_duration = 0; /* Duration of bursts (us), 0 to disable. */ | ||
| 65 | static int fqs_holdoff = 0; /* Hold time within burst (us). */ | ||
| 66 | static int fqs_stutter = 3; /* Wait time between bursts (s). */ | ||
| 64 | static char *torture_type = "rcu"; /* What RCU implementation to torture. */ | 67 | static char *torture_type = "rcu"; /* What RCU implementation to torture. */ |
| 65 | 68 | ||
| 66 | module_param(nreaders, int, 0444); | 69 | module_param(nreaders, int, 0444); |
| @@ -79,6 +82,12 @@ module_param(stutter, int, 0444); | |||
| 79 | MODULE_PARM_DESC(stutter, "Number of seconds to run/halt test"); | 82 | MODULE_PARM_DESC(stutter, "Number of seconds to run/halt test"); |
| 80 | module_param(irqreader, int, 0444); | 83 | module_param(irqreader, int, 0444); |
| 81 | MODULE_PARM_DESC(irqreader, "Allow RCU readers from irq handlers"); | 84 | MODULE_PARM_DESC(irqreader, "Allow RCU readers from irq handlers"); |
| 85 | module_param(fqs_duration, int, 0444); | ||
| 86 | MODULE_PARM_DESC(fqs_duration, "Duration of fqs bursts (us)"); | ||
| 87 | module_param(fqs_holdoff, int, 0444); | ||
| 88 | MODULE_PARM_DESC(fqs_holdoff, "Holdoff time within fqs bursts (us)"); | ||
| 89 | module_param(fqs_stutter, int, 0444); | ||
| 90 | MODULE_PARM_DESC(fqs_stutter, "Wait time between fqs bursts (s)"); | ||
| 82 | module_param(torture_type, charp, 0444); | 91 | module_param(torture_type, charp, 0444); |
| 83 | MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)"); | 92 | MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)"); |
| 84 | 93 | ||
| @@ -99,6 +108,7 @@ static struct task_struct **reader_tasks; | |||
| 99 | static struct task_struct *stats_task; | 108 | static struct task_struct *stats_task; |
| 100 | static struct task_struct *shuffler_task; | 109 | static struct task_struct *shuffler_task; |
| 101 | static struct task_struct *stutter_task; | 110 | static struct task_struct *stutter_task; |
| 111 | static struct task_struct *fqs_task; | ||
| 102 | 112 | ||
| 103 | #define RCU_TORTURE_PIPE_LEN 10 | 113 | #define RCU_TORTURE_PIPE_LEN 10 |
| 104 | 114 | ||
| @@ -263,6 +273,7 @@ struct rcu_torture_ops { | |||
| 263 | void (*deferred_free)(struct rcu_torture *p); | 273 | void (*deferred_free)(struct rcu_torture *p); |
| 264 | void (*sync)(void); | 274 | void (*sync)(void); |
| 265 | void (*cb_barrier)(void); | 275 | void (*cb_barrier)(void); |
| 276 | void (*fqs)(void); | ||
| 266 | int (*stats)(char *page); | 277 | int (*stats)(char *page); |
| 267 | int irq_capable; | 278 | int irq_capable; |
| 268 | char *name; | 279 | char *name; |
| @@ -347,6 +358,7 @@ static struct rcu_torture_ops rcu_ops = { | |||
| 347 | .deferred_free = rcu_torture_deferred_free, | 358 | .deferred_free = rcu_torture_deferred_free, |
| 348 | .sync = synchronize_rcu, | 359 | .sync = synchronize_rcu, |
| 349 | .cb_barrier = rcu_barrier, | 360 | .cb_barrier = rcu_barrier, |
| 361 | .fqs = rcu_force_quiescent_state, | ||
| 350 | .stats = NULL, | 362 | .stats = NULL, |
| 351 | .irq_capable = 1, | 363 | .irq_capable = 1, |
| 352 | .name = "rcu" | 364 | .name = "rcu" |
| @@ -388,6 +400,7 @@ static struct rcu_torture_ops rcu_sync_ops = { | |||
| 388 | .deferred_free = rcu_sync_torture_deferred_free, | 400 | .deferred_free = rcu_sync_torture_deferred_free, |
| 389 | .sync = synchronize_rcu, | 401 | .sync = synchronize_rcu, |
| 390 | .cb_barrier = NULL, | 402 | .cb_barrier = NULL, |
| 403 | .fqs = rcu_force_quiescent_state, | ||
| 391 | .stats = NULL, | 404 | .stats = NULL, |
| 392 | .irq_capable = 1, | 405 | .irq_capable = 1, |
| 393 | .name = "rcu_sync" | 406 | .name = "rcu_sync" |
| @@ -403,6 +416,7 @@ static struct rcu_torture_ops rcu_expedited_ops = { | |||
| 403 | .deferred_free = rcu_sync_torture_deferred_free, | 416 | .deferred_free = rcu_sync_torture_deferred_free, |
| 404 | .sync = synchronize_rcu_expedited, | 417 | .sync = synchronize_rcu_expedited, |
| 405 | .cb_barrier = NULL, | 418 | .cb_barrier = NULL, |
| 419 | .fqs = rcu_force_quiescent_state, | ||
| 406 | .stats = NULL, | 420 | .stats = NULL, |
| 407 | .irq_capable = 1, | 421 | .irq_capable = 1, |
| 408 | .name = "rcu_expedited" | 422 | .name = "rcu_expedited" |
| @@ -465,6 +479,7 @@ static struct rcu_torture_ops rcu_bh_ops = { | |||
| 465 | .deferred_free = rcu_bh_torture_deferred_free, | 479 | .deferred_free = rcu_bh_torture_deferred_free, |
| 466 | .sync = rcu_bh_torture_synchronize, | 480 | .sync = rcu_bh_torture_synchronize, |
| 467 | .cb_barrier = rcu_barrier_bh, | 481 | .cb_barrier = rcu_barrier_bh, |
| 482 | .fqs = rcu_bh_force_quiescent_state, | ||
| 468 | .stats = NULL, | 483 | .stats = NULL, |
| 469 | .irq_capable = 1, | 484 | .irq_capable = 1, |
| 470 | .name = "rcu_bh" | 485 | .name = "rcu_bh" |
| @@ -480,6 +495,7 @@ static struct rcu_torture_ops rcu_bh_sync_ops = { | |||
| 480 | .deferred_free = rcu_sync_torture_deferred_free, | 495 | .deferred_free = rcu_sync_torture_deferred_free, |
| 481 | .sync = rcu_bh_torture_synchronize, | 496 | .sync = rcu_bh_torture_synchronize, |
| 482 | .cb_barrier = NULL, | 497 | .cb_barrier = NULL, |
| 498 | .fqs = rcu_bh_force_quiescent_state, | ||
| 483 | .stats = NULL, | 499 | .stats = NULL, |
| 484 | .irq_capable = 1, | 500 | .irq_capable = 1, |
| 485 | .name = "rcu_bh_sync" | 501 | .name = "rcu_bh_sync" |
| @@ -621,6 +637,7 @@ static struct rcu_torture_ops sched_ops = { | |||
| 621 | .deferred_free = rcu_sched_torture_deferred_free, | 637 | .deferred_free = rcu_sched_torture_deferred_free, |
| 622 | .sync = sched_torture_synchronize, | 638 | .sync = sched_torture_synchronize, |
| 623 | .cb_barrier = rcu_barrier_sched, | 639 | .cb_barrier = rcu_barrier_sched, |
| 640 | .fqs = rcu_sched_force_quiescent_state, | ||
| 624 | .stats = NULL, | 641 | .stats = NULL, |
| 625 | .irq_capable = 1, | 642 | .irq_capable = 1, |
| 626 | .name = "sched" | 643 | .name = "sched" |
| @@ -636,6 +653,7 @@ static struct rcu_torture_ops sched_sync_ops = { | |||
| 636 | .deferred_free = rcu_sync_torture_deferred_free, | 653 | .deferred_free = rcu_sync_torture_deferred_free, |
| 637 | .sync = sched_torture_synchronize, | 654 | .sync = sched_torture_synchronize, |
| 638 | .cb_barrier = NULL, | 655 | .cb_barrier = NULL, |
| 656 | .fqs = rcu_sched_force_quiescent_state, | ||
| 639 | .stats = NULL, | 657 | .stats = NULL, |
| 640 | .name = "sched_sync" | 658 | .name = "sched_sync" |
| 641 | }; | 659 | }; |
| @@ -650,12 +668,45 @@ static struct rcu_torture_ops sched_expedited_ops = { | |||
| 650 | .deferred_free = rcu_sync_torture_deferred_free, | 668 | .deferred_free = rcu_sync_torture_deferred_free, |
| 651 | .sync = synchronize_sched_expedited, | 669 | .sync = synchronize_sched_expedited, |
| 652 | .cb_barrier = NULL, | 670 | .cb_barrier = NULL, |
| 671 | .fqs = rcu_sched_force_quiescent_state, | ||
| 653 | .stats = rcu_expedited_torture_stats, | 672 | .stats = rcu_expedited_torture_stats, |
| 654 | .irq_capable = 1, | 673 | .irq_capable = 1, |
| 655 | .name = "sched_expedited" | 674 | .name = "sched_expedited" |
| 656 | }; | 675 | }; |
| 657 | 676 | ||
| 658 | /* | 677 | /* |
| 678 | * RCU torture force-quiescent-state kthread. Repeatedly induces | ||
| 679 | * bursts of calls to force_quiescent_state(), increasing the probability | ||
| 680 | * of occurrence of some important types of race conditions. | ||
| 681 | */ | ||
| 682 | static int | ||
| 683 | rcu_torture_fqs(void *arg) | ||
| 684 | { | ||
| 685 | unsigned long fqs_resume_time; | ||
| 686 | int fqs_burst_remaining; | ||
| 687 | |||
| 688 | VERBOSE_PRINTK_STRING("rcu_torture_fqs task started"); | ||
| 689 | do { | ||
| 690 | fqs_resume_time = jiffies + fqs_stutter * HZ; | ||
| 691 | while (jiffies - fqs_resume_time > LONG_MAX) { | ||
| 692 | schedule_timeout_interruptible(1); | ||
| 693 | } | ||
| 694 | fqs_burst_remaining = fqs_duration; | ||
| 695 | while (fqs_burst_remaining > 0) { | ||
| 696 | cur_ops->fqs(); | ||
| 697 | udelay(fqs_holdoff); | ||
| 698 | fqs_burst_remaining -= fqs_holdoff; | ||
| 699 | } | ||
| 700 | rcu_stutter_wait("rcu_torture_fqs"); | ||
| 701 | } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); | ||
| 702 | VERBOSE_PRINTK_STRING("rcu_torture_fqs task stopping"); | ||
| 703 | rcutorture_shutdown_absorb("rcu_torture_fqs"); | ||
| 704 | while (!kthread_should_stop()) | ||
| 705 | schedule_timeout_uninterruptible(1); | ||
| 706 | return 0; | ||
| 707 | } | ||
| 708 | |||
| 709 | /* | ||
| 659 | * RCU torture writer kthread. Repeatedly substitutes a new structure | 710 | * RCU torture writer kthread. Repeatedly substitutes a new structure |
| 660 | * for that pointed to by rcu_torture_current, freeing the old structure | 711 | * for that pointed to by rcu_torture_current, freeing the old structure |
| 661 | * after a series of grace periods (the "pipeline"). | 712 | * after a series of grace periods (the "pipeline"). |
| @@ -745,7 +796,11 @@ static void rcu_torture_timer(unsigned long unused) | |||
| 745 | 796 | ||
| 746 | idx = cur_ops->readlock(); | 797 | idx = cur_ops->readlock(); |
| 747 | completed = cur_ops->completed(); | 798 | completed = cur_ops->completed(); |
| 748 | p = rcu_dereference(rcu_torture_current); | 799 | p = rcu_dereference_check(rcu_torture_current, |
| 800 | rcu_read_lock_held() || | ||
| 801 | rcu_read_lock_bh_held() || | ||
| 802 | rcu_read_lock_sched_held() || | ||
| 803 | srcu_read_lock_held(&srcu_ctl)); | ||
| 749 | if (p == NULL) { | 804 | if (p == NULL) { |
| 750 | /* Leave because rcu_torture_writer is not yet underway */ | 805 | /* Leave because rcu_torture_writer is not yet underway */ |
| 751 | cur_ops->readunlock(idx); | 806 | cur_ops->readunlock(idx); |
| @@ -798,11 +853,15 @@ rcu_torture_reader(void *arg) | |||
| 798 | do { | 853 | do { |
| 799 | if (irqreader && cur_ops->irq_capable) { | 854 | if (irqreader && cur_ops->irq_capable) { |
| 800 | if (!timer_pending(&t)) | 855 | if (!timer_pending(&t)) |
| 801 | mod_timer(&t, 1); | 856 | mod_timer(&t, jiffies + 1); |
| 802 | } | 857 | } |
| 803 | idx = cur_ops->readlock(); | 858 | idx = cur_ops->readlock(); |
| 804 | completed = cur_ops->completed(); | 859 | completed = cur_ops->completed(); |
| 805 | p = rcu_dereference(rcu_torture_current); | 860 | p = rcu_dereference_check(rcu_torture_current, |
| 861 | rcu_read_lock_held() || | ||
| 862 | rcu_read_lock_bh_held() || | ||
| 863 | rcu_read_lock_sched_held() || | ||
| 864 | srcu_read_lock_held(&srcu_ctl)); | ||
| 806 | if (p == NULL) { | 865 | if (p == NULL) { |
| 807 | /* Wait for rcu_torture_writer to get underway */ | 866 | /* Wait for rcu_torture_writer to get underway */ |
| 808 | cur_ops->readunlock(idx); | 867 | cur_ops->readunlock(idx); |
| @@ -1030,10 +1089,11 @@ rcu_torture_print_module_parms(char *tag) | |||
| 1030 | printk(KERN_ALERT "%s" TORTURE_FLAG | 1089 | printk(KERN_ALERT "%s" TORTURE_FLAG |
| 1031 | "--- %s: nreaders=%d nfakewriters=%d " | 1090 | "--- %s: nreaders=%d nfakewriters=%d " |
| 1032 | "stat_interval=%d verbose=%d test_no_idle_hz=%d " | 1091 | "stat_interval=%d verbose=%d test_no_idle_hz=%d " |
| 1033 | "shuffle_interval=%d stutter=%d irqreader=%d\n", | 1092 | "shuffle_interval=%d stutter=%d irqreader=%d " |
| 1093 | "fqs_duration=%d fqs_holdoff=%d fqs_stutter=%d\n", | ||
| 1034 | torture_type, tag, nrealreaders, nfakewriters, | 1094 | torture_type, tag, nrealreaders, nfakewriters, |
| 1035 | stat_interval, verbose, test_no_idle_hz, shuffle_interval, | 1095 | stat_interval, verbose, test_no_idle_hz, shuffle_interval, |
| 1036 | stutter, irqreader); | 1096 | stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter); |
| 1037 | } | 1097 | } |
| 1038 | 1098 | ||
| 1039 | static struct notifier_block rcutorture_nb = { | 1099 | static struct notifier_block rcutorture_nb = { |
| @@ -1109,6 +1169,12 @@ rcu_torture_cleanup(void) | |||
| 1109 | } | 1169 | } |
| 1110 | stats_task = NULL; | 1170 | stats_task = NULL; |
| 1111 | 1171 | ||
| 1172 | if (fqs_task) { | ||
| 1173 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_fqs task"); | ||
| 1174 | kthread_stop(fqs_task); | ||
| 1175 | } | ||
| 1176 | fqs_task = NULL; | ||
| 1177 | |||
| 1112 | /* Wait for all RCU callbacks to fire. */ | 1178 | /* Wait for all RCU callbacks to fire. */ |
| 1113 | 1179 | ||
| 1114 | if (cur_ops->cb_barrier != NULL) | 1180 | if (cur_ops->cb_barrier != NULL) |
| @@ -1154,6 +1220,11 @@ rcu_torture_init(void) | |||
| 1154 | mutex_unlock(&fullstop_mutex); | 1220 | mutex_unlock(&fullstop_mutex); |
| 1155 | return -EINVAL; | 1221 | return -EINVAL; |
| 1156 | } | 1222 | } |
| 1223 | if (cur_ops->fqs == NULL && fqs_duration != 0) { | ||
| 1224 | printk(KERN_ALERT "rcu-torture: ->fqs NULL and non-zero " | ||
| 1225 | "fqs_duration, fqs disabled.\n"); | ||
| 1226 | fqs_duration = 0; | ||
| 1227 | } | ||
| 1157 | if (cur_ops->init) | 1228 | if (cur_ops->init) |
| 1158 | cur_ops->init(); /* no "goto unwind" prior to this point!!! */ | 1229 | cur_ops->init(); /* no "goto unwind" prior to this point!!! */ |
| 1159 | 1230 | ||
| @@ -1282,6 +1353,19 @@ rcu_torture_init(void) | |||
| 1282 | goto unwind; | 1353 | goto unwind; |
| 1283 | } | 1354 | } |
| 1284 | } | 1355 | } |
| 1356 | if (fqs_duration < 0) | ||
| 1357 | fqs_duration = 0; | ||
| 1358 | if (fqs_duration) { | ||
| 1359 | /* Create the stutter thread */ | ||
| 1360 | fqs_task = kthread_run(rcu_torture_fqs, NULL, | ||
| 1361 | "rcu_torture_fqs"); | ||
| 1362 | if (IS_ERR(fqs_task)) { | ||
| 1363 | firsterr = PTR_ERR(fqs_task); | ||
| 1364 | VERBOSE_PRINTK_ERRSTRING("Failed to create fqs"); | ||
| 1365 | fqs_task = NULL; | ||
| 1366 | goto unwind; | ||
| 1367 | } | ||
| 1368 | } | ||
| 1285 | register_reboot_notifier(&rcutorture_nb); | 1369 | register_reboot_notifier(&rcutorture_nb); |
| 1286 | mutex_unlock(&fullstop_mutex); | 1370 | mutex_unlock(&fullstop_mutex); |
| 1287 | return 0; | 1371 | return 0; |
