diff options
Diffstat (limited to 'kernel/rcutorture.c')
| -rw-r--r-- | kernel/rcutorture.c | 257 |
1 files changed, 242 insertions, 15 deletions
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index a89b381a8c6e..e66b34ab7555 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c | |||
| @@ -64,6 +64,7 @@ static int irqreader = 1; /* RCU readers from irq (timers). */ | |||
| 64 | static int fqs_duration; /* Duration of bursts (us), 0 to disable. */ | 64 | static int fqs_duration; /* Duration of bursts (us), 0 to disable. */ |
| 65 | static int fqs_holdoff; /* Hold time within burst (us). */ | 65 | static int fqs_holdoff; /* Hold time within burst (us). */ |
| 66 | static int fqs_stutter = 3; /* Wait time between bursts (s). */ | 66 | static int fqs_stutter = 3; /* Wait time between bursts (s). */ |
| 67 | static int n_barrier_cbs; /* Number of callbacks to test RCU barriers. */ | ||
| 67 | static int onoff_interval; /* Wait time between CPU hotplugs, 0=disable. */ | 68 | static int onoff_interval; /* Wait time between CPU hotplugs, 0=disable. */ |
| 68 | static int onoff_holdoff; /* Seconds after boot before CPU hotplugs. */ | 69 | static int onoff_holdoff; /* Seconds after boot before CPU hotplugs. */ |
| 69 | static int shutdown_secs; /* Shutdown time (s). <=0 for no shutdown. */ | 70 | static int shutdown_secs; /* Shutdown time (s). <=0 for no shutdown. */ |
| @@ -96,6 +97,8 @@ module_param(fqs_holdoff, int, 0444); | |||
| 96 | MODULE_PARM_DESC(fqs_holdoff, "Holdoff time within fqs bursts (us)"); | 97 | MODULE_PARM_DESC(fqs_holdoff, "Holdoff time within fqs bursts (us)"); |
| 97 | module_param(fqs_stutter, int, 0444); | 98 | module_param(fqs_stutter, int, 0444); |
| 98 | MODULE_PARM_DESC(fqs_stutter, "Wait time between fqs bursts (s)"); | 99 | MODULE_PARM_DESC(fqs_stutter, "Wait time between fqs bursts (s)"); |
| 100 | module_param(n_barrier_cbs, int, 0444); | ||
| 101 | MODULE_PARM_DESC(n_barrier_cbs, "# of callbacks/kthreads for barrier testing"); | ||
| 99 | module_param(onoff_interval, int, 0444); | 102 | module_param(onoff_interval, int, 0444); |
| 100 | MODULE_PARM_DESC(onoff_interval, "Time between CPU hotplugs (s), 0=disable"); | 103 | MODULE_PARM_DESC(onoff_interval, "Time between CPU hotplugs (s), 0=disable"); |
| 101 | module_param(onoff_holdoff, int, 0444); | 104 | module_param(onoff_holdoff, int, 0444); |
| @@ -139,6 +142,8 @@ static struct task_struct *shutdown_task; | |||
| 139 | static struct task_struct *onoff_task; | 142 | static struct task_struct *onoff_task; |
| 140 | #endif /* #ifdef CONFIG_HOTPLUG_CPU */ | 143 | #endif /* #ifdef CONFIG_HOTPLUG_CPU */ |
| 141 | static struct task_struct *stall_task; | 144 | static struct task_struct *stall_task; |
| 145 | static struct task_struct **barrier_cbs_tasks; | ||
| 146 | static struct task_struct *barrier_task; | ||
| 142 | 147 | ||
| 143 | #define RCU_TORTURE_PIPE_LEN 10 | 148 | #define RCU_TORTURE_PIPE_LEN 10 |
| 144 | 149 | ||
| @@ -164,6 +169,7 @@ static atomic_t n_rcu_torture_alloc_fail; | |||
| 164 | static atomic_t n_rcu_torture_free; | 169 | static atomic_t n_rcu_torture_free; |
| 165 | static atomic_t n_rcu_torture_mberror; | 170 | static atomic_t n_rcu_torture_mberror; |
| 166 | static atomic_t n_rcu_torture_error; | 171 | static atomic_t n_rcu_torture_error; |
| 172 | static long n_rcu_torture_barrier_error; | ||
| 167 | static long n_rcu_torture_boost_ktrerror; | 173 | static long n_rcu_torture_boost_ktrerror; |
| 168 | static long n_rcu_torture_boost_rterror; | 174 | static long n_rcu_torture_boost_rterror; |
| 169 | static long n_rcu_torture_boost_failure; | 175 | static long n_rcu_torture_boost_failure; |
| @@ -173,6 +179,8 @@ static long n_offline_attempts; | |||
| 173 | static long n_offline_successes; | 179 | static long n_offline_successes; |
| 174 | static long n_online_attempts; | 180 | static long n_online_attempts; |
| 175 | static long n_online_successes; | 181 | static long n_online_successes; |
| 182 | static long n_barrier_attempts; | ||
| 183 | static long n_barrier_successes; | ||
| 176 | static struct list_head rcu_torture_removed; | 184 | static struct list_head rcu_torture_removed; |
| 177 | static cpumask_var_t shuffle_tmp_mask; | 185 | static cpumask_var_t shuffle_tmp_mask; |
| 178 | 186 | ||
| @@ -197,6 +205,10 @@ static unsigned long shutdown_time; /* jiffies to system shutdown. */ | |||
| 197 | static unsigned long boost_starttime; /* jiffies of next boost test start. */ | 205 | static unsigned long boost_starttime; /* jiffies of next boost test start. */ |
| 198 | DEFINE_MUTEX(boost_mutex); /* protect setting boost_starttime */ | 206 | DEFINE_MUTEX(boost_mutex); /* protect setting boost_starttime */ |
| 199 | /* and boost task create/destroy. */ | 207 | /* and boost task create/destroy. */ |
| 208 | static atomic_t barrier_cbs_count; /* Barrier callbacks registered. */ | ||
| 209 | static atomic_t barrier_cbs_invoked; /* Barrier callbacks invoked. */ | ||
| 210 | static wait_queue_head_t *barrier_cbs_wq; /* Coordinate barrier testing. */ | ||
| 211 | static DECLARE_WAIT_QUEUE_HEAD(barrier_wq); | ||
| 200 | 212 | ||
| 201 | /* Mediate rmmod and system shutdown. Concurrent rmmod & shutdown illegal! */ | 213 | /* Mediate rmmod and system shutdown. Concurrent rmmod & shutdown illegal! */ |
| 202 | 214 | ||
| @@ -327,6 +339,7 @@ struct rcu_torture_ops { | |||
| 327 | int (*completed)(void); | 339 | int (*completed)(void); |
| 328 | void (*deferred_free)(struct rcu_torture *p); | 340 | void (*deferred_free)(struct rcu_torture *p); |
| 329 | void (*sync)(void); | 341 | void (*sync)(void); |
| 342 | void (*call)(struct rcu_head *head, void (*func)(struct rcu_head *rcu)); | ||
| 330 | void (*cb_barrier)(void); | 343 | void (*cb_barrier)(void); |
| 331 | void (*fqs)(void); | 344 | void (*fqs)(void); |
| 332 | int (*stats)(char *page); | 345 | int (*stats)(char *page); |
| @@ -417,6 +430,7 @@ static struct rcu_torture_ops rcu_ops = { | |||
| 417 | .completed = rcu_torture_completed, | 430 | .completed = rcu_torture_completed, |
| 418 | .deferred_free = rcu_torture_deferred_free, | 431 | .deferred_free = rcu_torture_deferred_free, |
| 419 | .sync = synchronize_rcu, | 432 | .sync = synchronize_rcu, |
| 433 | .call = call_rcu, | ||
| 420 | .cb_barrier = rcu_barrier, | 434 | .cb_barrier = rcu_barrier, |
| 421 | .fqs = rcu_force_quiescent_state, | 435 | .fqs = rcu_force_quiescent_state, |
| 422 | .stats = NULL, | 436 | .stats = NULL, |
| @@ -460,6 +474,7 @@ static struct rcu_torture_ops rcu_sync_ops = { | |||
| 460 | .completed = rcu_torture_completed, | 474 | .completed = rcu_torture_completed, |
| 461 | .deferred_free = rcu_sync_torture_deferred_free, | 475 | .deferred_free = rcu_sync_torture_deferred_free, |
| 462 | .sync = synchronize_rcu, | 476 | .sync = synchronize_rcu, |
| 477 | .call = NULL, | ||
| 463 | .cb_barrier = NULL, | 478 | .cb_barrier = NULL, |
| 464 | .fqs = rcu_force_quiescent_state, | 479 | .fqs = rcu_force_quiescent_state, |
| 465 | .stats = NULL, | 480 | .stats = NULL, |
| @@ -477,6 +492,7 @@ static struct rcu_torture_ops rcu_expedited_ops = { | |||
| 477 | .completed = rcu_no_completed, | 492 | .completed = rcu_no_completed, |
| 478 | .deferred_free = rcu_sync_torture_deferred_free, | 493 | .deferred_free = rcu_sync_torture_deferred_free, |
| 479 | .sync = synchronize_rcu_expedited, | 494 | .sync = synchronize_rcu_expedited, |
| 495 | .call = NULL, | ||
| 480 | .cb_barrier = NULL, | 496 | .cb_barrier = NULL, |
| 481 | .fqs = rcu_force_quiescent_state, | 497 | .fqs = rcu_force_quiescent_state, |
| 482 | .stats = NULL, | 498 | .stats = NULL, |
| @@ -519,6 +535,7 @@ static struct rcu_torture_ops rcu_bh_ops = { | |||
| 519 | .completed = rcu_bh_torture_completed, | 535 | .completed = rcu_bh_torture_completed, |
| 520 | .deferred_free = rcu_bh_torture_deferred_free, | 536 | .deferred_free = rcu_bh_torture_deferred_free, |
| 521 | .sync = synchronize_rcu_bh, | 537 | .sync = synchronize_rcu_bh, |
| 538 | .call = call_rcu_bh, | ||
| 522 | .cb_barrier = rcu_barrier_bh, | 539 | .cb_barrier = rcu_barrier_bh, |
| 523 | .fqs = rcu_bh_force_quiescent_state, | 540 | .fqs = rcu_bh_force_quiescent_state, |
| 524 | .stats = NULL, | 541 | .stats = NULL, |
| @@ -535,6 +552,7 @@ static struct rcu_torture_ops rcu_bh_sync_ops = { | |||
| 535 | .completed = rcu_bh_torture_completed, | 552 | .completed = rcu_bh_torture_completed, |
| 536 | .deferred_free = rcu_sync_torture_deferred_free, | 553 | .deferred_free = rcu_sync_torture_deferred_free, |
| 537 | .sync = synchronize_rcu_bh, | 554 | .sync = synchronize_rcu_bh, |
| 555 | .call = NULL, | ||
| 538 | .cb_barrier = NULL, | 556 | .cb_barrier = NULL, |
| 539 | .fqs = rcu_bh_force_quiescent_state, | 557 | .fqs = rcu_bh_force_quiescent_state, |
| 540 | .stats = NULL, | 558 | .stats = NULL, |
| @@ -551,6 +569,7 @@ static struct rcu_torture_ops rcu_bh_expedited_ops = { | |||
| 551 | .completed = rcu_bh_torture_completed, | 569 | .completed = rcu_bh_torture_completed, |
| 552 | .deferred_free = rcu_sync_torture_deferred_free, | 570 | .deferred_free = rcu_sync_torture_deferred_free, |
| 553 | .sync = synchronize_rcu_bh_expedited, | 571 | .sync = synchronize_rcu_bh_expedited, |
| 572 | .call = NULL, | ||
| 554 | .cb_barrier = NULL, | 573 | .cb_barrier = NULL, |
| 555 | .fqs = rcu_bh_force_quiescent_state, | 574 | .fqs = rcu_bh_force_quiescent_state, |
| 556 | .stats = NULL, | 575 | .stats = NULL, |
| @@ -606,6 +625,11 @@ static int srcu_torture_completed(void) | |||
| 606 | return srcu_batches_completed(&srcu_ctl); | 625 | return srcu_batches_completed(&srcu_ctl); |
| 607 | } | 626 | } |
| 608 | 627 | ||
| 628 | static void srcu_torture_deferred_free(struct rcu_torture *rp) | ||
| 629 | { | ||
| 630 | call_srcu(&srcu_ctl, &rp->rtort_rcu, rcu_torture_cb); | ||
| 631 | } | ||
| 632 | |||
| 609 | static void srcu_torture_synchronize(void) | 633 | static void srcu_torture_synchronize(void) |
| 610 | { | 634 | { |
| 611 | synchronize_srcu(&srcu_ctl); | 635 | synchronize_srcu(&srcu_ctl); |
| @@ -620,7 +644,7 @@ static int srcu_torture_stats(char *page) | |||
| 620 | cnt += sprintf(&page[cnt], "%s%s per-CPU(idx=%d):", | 644 | cnt += sprintf(&page[cnt], "%s%s per-CPU(idx=%d):", |
| 621 | torture_type, TORTURE_FLAG, idx); | 645 | torture_type, TORTURE_FLAG, idx); |
| 622 | for_each_possible_cpu(cpu) { | 646 | for_each_possible_cpu(cpu) { |
| 623 | cnt += sprintf(&page[cnt], " %d(%d,%d)", cpu, | 647 | cnt += sprintf(&page[cnt], " %d(%lu,%lu)", cpu, |
| 624 | per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[!idx], | 648 | per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[!idx], |
| 625 | per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[idx]); | 649 | per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[idx]); |
| 626 | } | 650 | } |
| @@ -635,13 +659,29 @@ static struct rcu_torture_ops srcu_ops = { | |||
| 635 | .read_delay = srcu_read_delay, | 659 | .read_delay = srcu_read_delay, |
| 636 | .readunlock = srcu_torture_read_unlock, | 660 | .readunlock = srcu_torture_read_unlock, |
| 637 | .completed = srcu_torture_completed, | 661 | .completed = srcu_torture_completed, |
| 638 | .deferred_free = rcu_sync_torture_deferred_free, | 662 | .deferred_free = srcu_torture_deferred_free, |
| 639 | .sync = srcu_torture_synchronize, | 663 | .sync = srcu_torture_synchronize, |
| 664 | .call = NULL, | ||
| 640 | .cb_barrier = NULL, | 665 | .cb_barrier = NULL, |
| 641 | .stats = srcu_torture_stats, | 666 | .stats = srcu_torture_stats, |
| 642 | .name = "srcu" | 667 | .name = "srcu" |
| 643 | }; | 668 | }; |
| 644 | 669 | ||
| 670 | static struct rcu_torture_ops srcu_sync_ops = { | ||
| 671 | .init = srcu_torture_init, | ||
| 672 | .cleanup = srcu_torture_cleanup, | ||
| 673 | .readlock = srcu_torture_read_lock, | ||
| 674 | .read_delay = srcu_read_delay, | ||
| 675 | .readunlock = srcu_torture_read_unlock, | ||
| 676 | .completed = srcu_torture_completed, | ||
| 677 | .deferred_free = rcu_sync_torture_deferred_free, | ||
| 678 | .sync = srcu_torture_synchronize, | ||
| 679 | .call = NULL, | ||
| 680 | .cb_barrier = NULL, | ||
| 681 | .stats = srcu_torture_stats, | ||
| 682 | .name = "srcu_sync" | ||
| 683 | }; | ||
| 684 | |||
| 645 | static int srcu_torture_read_lock_raw(void) __acquires(&srcu_ctl) | 685 | static int srcu_torture_read_lock_raw(void) __acquires(&srcu_ctl) |
| 646 | { | 686 | { |
| 647 | return srcu_read_lock_raw(&srcu_ctl); | 687 | return srcu_read_lock_raw(&srcu_ctl); |
| @@ -659,13 +699,29 @@ static struct rcu_torture_ops srcu_raw_ops = { | |||
| 659 | .read_delay = srcu_read_delay, | 699 | .read_delay = srcu_read_delay, |
| 660 | .readunlock = srcu_torture_read_unlock_raw, | 700 | .readunlock = srcu_torture_read_unlock_raw, |
| 661 | .completed = srcu_torture_completed, | 701 | .completed = srcu_torture_completed, |
| 662 | .deferred_free = rcu_sync_torture_deferred_free, | 702 | .deferred_free = srcu_torture_deferred_free, |
| 663 | .sync = srcu_torture_synchronize, | 703 | .sync = srcu_torture_synchronize, |
| 704 | .call = NULL, | ||
| 664 | .cb_barrier = NULL, | 705 | .cb_barrier = NULL, |
| 665 | .stats = srcu_torture_stats, | 706 | .stats = srcu_torture_stats, |
| 666 | .name = "srcu_raw" | 707 | .name = "srcu_raw" |
| 667 | }; | 708 | }; |
| 668 | 709 | ||
| 710 | static struct rcu_torture_ops srcu_raw_sync_ops = { | ||
| 711 | .init = srcu_torture_init, | ||
| 712 | .cleanup = srcu_torture_cleanup, | ||
| 713 | .readlock = srcu_torture_read_lock_raw, | ||
| 714 | .read_delay = srcu_read_delay, | ||
| 715 | .readunlock = srcu_torture_read_unlock_raw, | ||
| 716 | .completed = srcu_torture_completed, | ||
| 717 | .deferred_free = rcu_sync_torture_deferred_free, | ||
| 718 | .sync = srcu_torture_synchronize, | ||
| 719 | .call = NULL, | ||
| 720 | .cb_barrier = NULL, | ||
| 721 | .stats = srcu_torture_stats, | ||
| 722 | .name = "srcu_raw_sync" | ||
| 723 | }; | ||
| 724 | |||
| 669 | static void srcu_torture_synchronize_expedited(void) | 725 | static void srcu_torture_synchronize_expedited(void) |
| 670 | { | 726 | { |
| 671 | synchronize_srcu_expedited(&srcu_ctl); | 727 | synchronize_srcu_expedited(&srcu_ctl); |
| @@ -680,6 +736,7 @@ static struct rcu_torture_ops srcu_expedited_ops = { | |||
| 680 | .completed = srcu_torture_completed, | 736 | .completed = srcu_torture_completed, |
| 681 | .deferred_free = rcu_sync_torture_deferred_free, | 737 | .deferred_free = rcu_sync_torture_deferred_free, |
| 682 | .sync = srcu_torture_synchronize_expedited, | 738 | .sync = srcu_torture_synchronize_expedited, |
| 739 | .call = NULL, | ||
| 683 | .cb_barrier = NULL, | 740 | .cb_barrier = NULL, |
| 684 | .stats = srcu_torture_stats, | 741 | .stats = srcu_torture_stats, |
| 685 | .name = "srcu_expedited" | 742 | .name = "srcu_expedited" |
| @@ -1129,7 +1186,8 @@ rcu_torture_printk(char *page) | |||
| 1129 | "rtc: %p ver: %lu tfle: %d rta: %d rtaf: %d rtf: %d " | 1186 | "rtc: %p ver: %lu tfle: %d rta: %d rtaf: %d rtf: %d " |
| 1130 | "rtmbe: %d rtbke: %ld rtbre: %ld " | 1187 | "rtmbe: %d rtbke: %ld rtbre: %ld " |
| 1131 | "rtbf: %ld rtb: %ld nt: %ld " | 1188 | "rtbf: %ld rtb: %ld nt: %ld " |
| 1132 | "onoff: %ld/%ld:%ld/%ld", | 1189 | "onoff: %ld/%ld:%ld/%ld " |
| 1190 | "barrier: %ld/%ld:%ld", | ||
| 1133 | rcu_torture_current, | 1191 | rcu_torture_current, |
| 1134 | rcu_torture_current_version, | 1192 | rcu_torture_current_version, |
| 1135 | list_empty(&rcu_torture_freelist), | 1193 | list_empty(&rcu_torture_freelist), |
| @@ -1145,14 +1203,17 @@ rcu_torture_printk(char *page) | |||
| 1145 | n_online_successes, | 1203 | n_online_successes, |
| 1146 | n_online_attempts, | 1204 | n_online_attempts, |
| 1147 | n_offline_successes, | 1205 | n_offline_successes, |
| 1148 | n_offline_attempts); | 1206 | n_offline_attempts, |
| 1207 | n_barrier_successes, | ||
| 1208 | n_barrier_attempts, | ||
| 1209 | n_rcu_torture_barrier_error); | ||
| 1210 | cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); | ||
| 1149 | if (atomic_read(&n_rcu_torture_mberror) != 0 || | 1211 | if (atomic_read(&n_rcu_torture_mberror) != 0 || |
| 1212 | n_rcu_torture_barrier_error != 0 || | ||
| 1150 | n_rcu_torture_boost_ktrerror != 0 || | 1213 | n_rcu_torture_boost_ktrerror != 0 || |
| 1151 | n_rcu_torture_boost_rterror != 0 || | 1214 | n_rcu_torture_boost_rterror != 0 || |
| 1152 | n_rcu_torture_boost_failure != 0) | 1215 | n_rcu_torture_boost_failure != 0 || |
| 1153 | cnt += sprintf(&page[cnt], " !!!"); | 1216 | i > 1) { |
| 1154 | cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); | ||
| 1155 | if (i > 1) { | ||
| 1156 | cnt += sprintf(&page[cnt], "!!! "); | 1217 | cnt += sprintf(&page[cnt], "!!! "); |
| 1157 | atomic_inc(&n_rcu_torture_error); | 1218 | atomic_inc(&n_rcu_torture_error); |
| 1158 | WARN_ON_ONCE(1); | 1219 | WARN_ON_ONCE(1); |
| @@ -1337,6 +1398,7 @@ static void rcutorture_booster_cleanup(int cpu) | |||
| 1337 | 1398 | ||
| 1338 | /* This must be outside of the mutex, otherwise deadlock! */ | 1399 | /* This must be outside of the mutex, otherwise deadlock! */ |
| 1339 | kthread_stop(t); | 1400 | kthread_stop(t); |
| 1401 | boost_tasks[cpu] = NULL; | ||
| 1340 | } | 1402 | } |
| 1341 | 1403 | ||
| 1342 | static int rcutorture_booster_init(int cpu) | 1404 | static int rcutorture_booster_init(int cpu) |
| @@ -1484,13 +1546,15 @@ static void rcu_torture_onoff_cleanup(void) | |||
| 1484 | return; | 1546 | return; |
| 1485 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_onoff task"); | 1547 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_onoff task"); |
| 1486 | kthread_stop(onoff_task); | 1548 | kthread_stop(onoff_task); |
| 1549 | onoff_task = NULL; | ||
| 1487 | } | 1550 | } |
| 1488 | 1551 | ||
| 1489 | #else /* #ifdef CONFIG_HOTPLUG_CPU */ | 1552 | #else /* #ifdef CONFIG_HOTPLUG_CPU */ |
| 1490 | 1553 | ||
| 1491 | static void | 1554 | static int |
| 1492 | rcu_torture_onoff_init(void) | 1555 | rcu_torture_onoff_init(void) |
| 1493 | { | 1556 | { |
| 1557 | return 0; | ||
| 1494 | } | 1558 | } |
| 1495 | 1559 | ||
| 1496 | static void rcu_torture_onoff_cleanup(void) | 1560 | static void rcu_torture_onoff_cleanup(void) |
| @@ -1554,6 +1618,152 @@ static void rcu_torture_stall_cleanup(void) | |||
| 1554 | return; | 1618 | return; |
| 1555 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_stall_task."); | 1619 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_stall_task."); |
| 1556 | kthread_stop(stall_task); | 1620 | kthread_stop(stall_task); |
| 1621 | stall_task = NULL; | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | /* Callback function for RCU barrier testing. */ | ||
| 1625 | void rcu_torture_barrier_cbf(struct rcu_head *rcu) | ||
| 1626 | { | ||
| 1627 | atomic_inc(&barrier_cbs_invoked); | ||
| 1628 | } | ||
| 1629 | |||
| 1630 | /* kthread function to register callbacks used to test RCU barriers. */ | ||
| 1631 | static int rcu_torture_barrier_cbs(void *arg) | ||
| 1632 | { | ||
| 1633 | long myid = (long)arg; | ||
| 1634 | struct rcu_head rcu; | ||
| 1635 | |||
| 1636 | init_rcu_head_on_stack(&rcu); | ||
| 1637 | VERBOSE_PRINTK_STRING("rcu_torture_barrier_cbs task started"); | ||
| 1638 | set_user_nice(current, 19); | ||
| 1639 | do { | ||
| 1640 | wait_event(barrier_cbs_wq[myid], | ||
| 1641 | atomic_read(&barrier_cbs_count) == n_barrier_cbs || | ||
| 1642 | kthread_should_stop() || | ||
| 1643 | fullstop != FULLSTOP_DONTSTOP); | ||
| 1644 | if (kthread_should_stop() || fullstop != FULLSTOP_DONTSTOP) | ||
| 1645 | break; | ||
| 1646 | cur_ops->call(&rcu, rcu_torture_barrier_cbf); | ||
| 1647 | if (atomic_dec_and_test(&barrier_cbs_count)) | ||
| 1648 | wake_up(&barrier_wq); | ||
| 1649 | } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); | ||
| 1650 | VERBOSE_PRINTK_STRING("rcu_torture_barrier_cbs task stopping"); | ||
| 1651 | rcutorture_shutdown_absorb("rcu_torture_barrier_cbs"); | ||
| 1652 | while (!kthread_should_stop()) | ||
| 1653 | schedule_timeout_interruptible(1); | ||
| 1654 | cur_ops->cb_barrier(); | ||
| 1655 | destroy_rcu_head_on_stack(&rcu); | ||
| 1656 | return 0; | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | /* kthread function to drive and coordinate RCU barrier testing. */ | ||
| 1660 | static int rcu_torture_barrier(void *arg) | ||
| 1661 | { | ||
| 1662 | int i; | ||
| 1663 | |||
| 1664 | VERBOSE_PRINTK_STRING("rcu_torture_barrier task starting"); | ||
| 1665 | do { | ||
| 1666 | atomic_set(&barrier_cbs_invoked, 0); | ||
| 1667 | atomic_set(&barrier_cbs_count, n_barrier_cbs); | ||
| 1668 | /* wake_up() path contains the required barriers. */ | ||
| 1669 | for (i = 0; i < n_barrier_cbs; i++) | ||
| 1670 | wake_up(&barrier_cbs_wq[i]); | ||
| 1671 | wait_event(barrier_wq, | ||
| 1672 | atomic_read(&barrier_cbs_count) == 0 || | ||
| 1673 | kthread_should_stop() || | ||
| 1674 | fullstop != FULLSTOP_DONTSTOP); | ||
| 1675 | if (kthread_should_stop() || fullstop != FULLSTOP_DONTSTOP) | ||
| 1676 | break; | ||
| 1677 | n_barrier_attempts++; | ||
| 1678 | cur_ops->cb_barrier(); | ||
| 1679 | if (atomic_read(&barrier_cbs_invoked) != n_barrier_cbs) { | ||
| 1680 | n_rcu_torture_barrier_error++; | ||
| 1681 | WARN_ON_ONCE(1); | ||
| 1682 | } | ||
| 1683 | n_barrier_successes++; | ||
| 1684 | schedule_timeout_interruptible(HZ / 10); | ||
| 1685 | } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); | ||
| 1686 | VERBOSE_PRINTK_STRING("rcu_torture_barrier task stopping"); | ||
| 1687 | rcutorture_shutdown_absorb("rcu_torture_barrier_cbs"); | ||
| 1688 | while (!kthread_should_stop()) | ||
| 1689 | schedule_timeout_interruptible(1); | ||
| 1690 | return 0; | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | /* Initialize RCU barrier testing. */ | ||
| 1694 | static int rcu_torture_barrier_init(void) | ||
| 1695 | { | ||
| 1696 | int i; | ||
| 1697 | int ret; | ||
| 1698 | |||
| 1699 | if (n_barrier_cbs == 0) | ||
| 1700 | return 0; | ||
| 1701 | if (cur_ops->call == NULL || cur_ops->cb_barrier == NULL) { | ||
| 1702 | printk(KERN_ALERT "%s" TORTURE_FLAG | ||
| 1703 | " Call or barrier ops missing for %s,\n", | ||
| 1704 | torture_type, cur_ops->name); | ||
| 1705 | printk(KERN_ALERT "%s" TORTURE_FLAG | ||
| 1706 | " RCU barrier testing omitted from run.\n", | ||
| 1707 | torture_type); | ||
| 1708 | return 0; | ||
| 1709 | } | ||
| 1710 | atomic_set(&barrier_cbs_count, 0); | ||
| 1711 | atomic_set(&barrier_cbs_invoked, 0); | ||
| 1712 | barrier_cbs_tasks = | ||
| 1713 | kzalloc(n_barrier_cbs * sizeof(barrier_cbs_tasks[0]), | ||
| 1714 | GFP_KERNEL); | ||
| 1715 | barrier_cbs_wq = | ||
| 1716 | kzalloc(n_barrier_cbs * sizeof(barrier_cbs_wq[0]), | ||
| 1717 | GFP_KERNEL); | ||
| 1718 | if (barrier_cbs_tasks == NULL || barrier_cbs_wq == 0) | ||
| 1719 | return -ENOMEM; | ||
| 1720 | for (i = 0; i < n_barrier_cbs; i++) { | ||
| 1721 | init_waitqueue_head(&barrier_cbs_wq[i]); | ||
| 1722 | barrier_cbs_tasks[i] = kthread_run(rcu_torture_barrier_cbs, | ||
| 1723 | (void *)(long)i, | ||
| 1724 | "rcu_torture_barrier_cbs"); | ||
| 1725 | if (IS_ERR(barrier_cbs_tasks[i])) { | ||
| 1726 | ret = PTR_ERR(barrier_cbs_tasks[i]); | ||
| 1727 | VERBOSE_PRINTK_ERRSTRING("Failed to create rcu_torture_barrier_cbs"); | ||
| 1728 | barrier_cbs_tasks[i] = NULL; | ||
| 1729 | return ret; | ||
| 1730 | } | ||
| 1731 | } | ||
| 1732 | barrier_task = kthread_run(rcu_torture_barrier, NULL, | ||
| 1733 | "rcu_torture_barrier"); | ||
| 1734 | if (IS_ERR(barrier_task)) { | ||
| 1735 | ret = PTR_ERR(barrier_task); | ||
| 1736 | VERBOSE_PRINTK_ERRSTRING("Failed to create rcu_torture_barrier"); | ||
| 1737 | barrier_task = NULL; | ||
| 1738 | } | ||
| 1739 | return 0; | ||
| 1740 | } | ||
| 1741 | |||
| 1742 | /* Clean up after RCU barrier testing. */ | ||
| 1743 | static void rcu_torture_barrier_cleanup(void) | ||
| 1744 | { | ||
| 1745 | int i; | ||
| 1746 | |||
| 1747 | if (barrier_task != NULL) { | ||
| 1748 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_barrier task"); | ||
| 1749 | kthread_stop(barrier_task); | ||
| 1750 | barrier_task = NULL; | ||
| 1751 | } | ||
| 1752 | if (barrier_cbs_tasks != NULL) { | ||
| 1753 | for (i = 0; i < n_barrier_cbs; i++) { | ||
| 1754 | if (barrier_cbs_tasks[i] != NULL) { | ||
| 1755 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_barrier_cbs task"); | ||
| 1756 | kthread_stop(barrier_cbs_tasks[i]); | ||
| 1757 | barrier_cbs_tasks[i] = NULL; | ||
| 1758 | } | ||
| 1759 | } | ||
| 1760 | kfree(barrier_cbs_tasks); | ||
| 1761 | barrier_cbs_tasks = NULL; | ||
| 1762 | } | ||
| 1763 | if (barrier_cbs_wq != NULL) { | ||
| 1764 | kfree(barrier_cbs_wq); | ||
| 1765 | barrier_cbs_wq = NULL; | ||
| 1766 | } | ||
| 1557 | } | 1767 | } |
| 1558 | 1768 | ||
| 1559 | static int rcutorture_cpu_notify(struct notifier_block *self, | 1769 | static int rcutorture_cpu_notify(struct notifier_block *self, |
| @@ -1598,6 +1808,7 @@ rcu_torture_cleanup(void) | |||
| 1598 | fullstop = FULLSTOP_RMMOD; | 1808 | fullstop = FULLSTOP_RMMOD; |
| 1599 | mutex_unlock(&fullstop_mutex); | 1809 | mutex_unlock(&fullstop_mutex); |
| 1600 | unregister_reboot_notifier(&rcutorture_shutdown_nb); | 1810 | unregister_reboot_notifier(&rcutorture_shutdown_nb); |
| 1811 | rcu_torture_barrier_cleanup(); | ||
| 1601 | rcu_torture_stall_cleanup(); | 1812 | rcu_torture_stall_cleanup(); |
| 1602 | if (stutter_task) { | 1813 | if (stutter_task) { |
| 1603 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task"); | 1814 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task"); |
| @@ -1665,6 +1876,7 @@ rcu_torture_cleanup(void) | |||
| 1665 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_shutdown task"); | 1876 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_shutdown task"); |
| 1666 | kthread_stop(shutdown_task); | 1877 | kthread_stop(shutdown_task); |
| 1667 | } | 1878 | } |
| 1879 | shutdown_task = NULL; | ||
| 1668 | rcu_torture_onoff_cleanup(); | 1880 | rcu_torture_onoff_cleanup(); |
| 1669 | 1881 | ||
| 1670 | /* Wait for all RCU callbacks to fire. */ | 1882 | /* Wait for all RCU callbacks to fire. */ |
| @@ -1676,7 +1888,7 @@ rcu_torture_cleanup(void) | |||
| 1676 | 1888 | ||
| 1677 | if (cur_ops->cleanup) | 1889 | if (cur_ops->cleanup) |
| 1678 | cur_ops->cleanup(); | 1890 | cur_ops->cleanup(); |
| 1679 | if (atomic_read(&n_rcu_torture_error)) | 1891 | if (atomic_read(&n_rcu_torture_error) || n_rcu_torture_barrier_error) |
| 1680 | rcu_torture_print_module_parms(cur_ops, "End of test: FAILURE"); | 1892 | rcu_torture_print_module_parms(cur_ops, "End of test: FAILURE"); |
| 1681 | else if (n_online_successes != n_online_attempts || | 1893 | else if (n_online_successes != n_online_attempts || |
| 1682 | n_offline_successes != n_offline_attempts) | 1894 | n_offline_successes != n_offline_attempts) |
| @@ -1692,10 +1904,12 @@ rcu_torture_init(void) | |||
| 1692 | int i; | 1904 | int i; |
| 1693 | int cpu; | 1905 | int cpu; |
| 1694 | int firsterr = 0; | 1906 | int firsterr = 0; |
| 1907 | int retval; | ||
| 1695 | static struct rcu_torture_ops *torture_ops[] = | 1908 | static struct rcu_torture_ops *torture_ops[] = |
| 1696 | { &rcu_ops, &rcu_sync_ops, &rcu_expedited_ops, | 1909 | { &rcu_ops, &rcu_sync_ops, &rcu_expedited_ops, |
| 1697 | &rcu_bh_ops, &rcu_bh_sync_ops, &rcu_bh_expedited_ops, | 1910 | &rcu_bh_ops, &rcu_bh_sync_ops, &rcu_bh_expedited_ops, |
| 1698 | &srcu_ops, &srcu_raw_ops, &srcu_expedited_ops, | 1911 | &srcu_ops, &srcu_sync_ops, &srcu_raw_ops, |
| 1912 | &srcu_raw_sync_ops, &srcu_expedited_ops, | ||
| 1699 | &sched_ops, &sched_sync_ops, &sched_expedited_ops, }; | 1913 | &sched_ops, &sched_sync_ops, &sched_expedited_ops, }; |
| 1700 | 1914 | ||
| 1701 | mutex_lock(&fullstop_mutex); | 1915 | mutex_lock(&fullstop_mutex); |
| @@ -1749,6 +1963,7 @@ rcu_torture_init(void) | |||
| 1749 | atomic_set(&n_rcu_torture_free, 0); | 1963 | atomic_set(&n_rcu_torture_free, 0); |
| 1750 | atomic_set(&n_rcu_torture_mberror, 0); | 1964 | atomic_set(&n_rcu_torture_mberror, 0); |
| 1751 | atomic_set(&n_rcu_torture_error, 0); | 1965 | atomic_set(&n_rcu_torture_error, 0); |
| 1966 | n_rcu_torture_barrier_error = 0; | ||
| 1752 | n_rcu_torture_boost_ktrerror = 0; | 1967 | n_rcu_torture_boost_ktrerror = 0; |
| 1753 | n_rcu_torture_boost_rterror = 0; | 1968 | n_rcu_torture_boost_rterror = 0; |
| 1754 | n_rcu_torture_boost_failure = 0; | 1969 | n_rcu_torture_boost_failure = 0; |
| @@ -1872,7 +2087,6 @@ rcu_torture_init(void) | |||
| 1872 | test_boost_duration = 2; | 2087 | test_boost_duration = 2; |
| 1873 | if ((test_boost == 1 && cur_ops->can_boost) || | 2088 | if ((test_boost == 1 && cur_ops->can_boost) || |
| 1874 | test_boost == 2) { | 2089 | test_boost == 2) { |
| 1875 | int retval; | ||
| 1876 | 2090 | ||
| 1877 | boost_starttime = jiffies + test_boost_interval * HZ; | 2091 | boost_starttime = jiffies + test_boost_interval * HZ; |
| 1878 | register_cpu_notifier(&rcutorture_cpu_nb); | 2092 | register_cpu_notifier(&rcutorture_cpu_nb); |
| @@ -1897,9 +2111,22 @@ rcu_torture_init(void) | |||
| 1897 | goto unwind; | 2111 | goto unwind; |
| 1898 | } | 2112 | } |
| 1899 | } | 2113 | } |
| 1900 | rcu_torture_onoff_init(); | 2114 | i = rcu_torture_onoff_init(); |
| 2115 | if (i != 0) { | ||
| 2116 | firsterr = i; | ||
| 2117 | goto unwind; | ||
| 2118 | } | ||
| 1901 | register_reboot_notifier(&rcutorture_shutdown_nb); | 2119 | register_reboot_notifier(&rcutorture_shutdown_nb); |
| 1902 | rcu_torture_stall_init(); | 2120 | i = rcu_torture_stall_init(); |
| 2121 | if (i != 0) { | ||
| 2122 | firsterr = i; | ||
| 2123 | goto unwind; | ||
| 2124 | } | ||
| 2125 | retval = rcu_torture_barrier_init(); | ||
| 2126 | if (retval != 0) { | ||
| 2127 | firsterr = retval; | ||
| 2128 | goto unwind; | ||
| 2129 | } | ||
| 1903 | rcutorture_record_test_transition(); | 2130 | rcutorture_record_test_transition(); |
| 1904 | mutex_unlock(&fullstop_mutex); | 2131 | mutex_unlock(&fullstop_mutex); |
| 1905 | return 0; | 2132 | return 0; |
