.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
---|
1 | 2 | /* |
---|
2 | 3 | * Common functions for in-kernel torture tests. |
---|
3 | 4 | * |
---|
4 | | - * This program is free software; you can redistribute it and/or modify |
---|
5 | | - * it under the terms of the GNU General Public License as published by |
---|
6 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
7 | | - * (at your option) any later version. |
---|
8 | | - * |
---|
9 | | - * This program is distributed in the hope that it will be useful, |
---|
10 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
12 | | - * GNU General Public License for more details. |
---|
13 | | - * |
---|
14 | | - * You should have received a copy of the GNU General Public License |
---|
15 | | - * along with this program; if not, you can access it online at |
---|
16 | | - * http://www.gnu.org/licenses/gpl-2.0.html. |
---|
17 | | - * |
---|
18 | 5 | * Copyright (C) IBM Corporation, 2014 |
---|
19 | 6 | * |
---|
20 | | - * Author: Paul E. McKenney <paulmck@us.ibm.com> |
---|
| 7 | + * Author: Paul E. McKenney <paulmck@linux.ibm.com> |
---|
21 | 8 | * Based on kernel/rcu/torture.c. |
---|
22 | 9 | */ |
---|
23 | 10 | |
---|
.. | .. |
---|
53 | 40 | #include "rcu/rcu.h" |
---|
54 | 41 | |
---|
55 | 42 | MODULE_LICENSE("GPL"); |
---|
56 | | -MODULE_AUTHOR("Paul E. McKenney <paulmck@us.ibm.com>"); |
---|
| 43 | +MODULE_AUTHOR("Paul E. McKenney <paulmck@linux.ibm.com>"); |
---|
| 44 | + |
---|
| 45 | +static bool disable_onoff_at_boot; |
---|
| 46 | +module_param(disable_onoff_at_boot, bool, 0444); |
---|
| 47 | + |
---|
| 48 | +static bool ftrace_dump_at_shutdown; |
---|
| 49 | +module_param(ftrace_dump_at_shutdown, bool, 0444); |
---|
57 | 50 | |
---|
58 | 51 | static char *torture_type; |
---|
59 | 52 | static int verbose; |
---|
.. | .. |
---|
75 | 68 | static struct task_struct *onoff_task; |
---|
76 | 69 | static long onoff_holdoff; |
---|
77 | 70 | static long onoff_interval; |
---|
| 71 | +static torture_ofl_func *onoff_f; |
---|
78 | 72 | static long n_offline_attempts; |
---|
79 | 73 | static long n_offline_successes; |
---|
80 | 74 | static unsigned long sum_offline; |
---|
.. | .. |
---|
96 | 90 | { |
---|
97 | 91 | unsigned long delta; |
---|
98 | 92 | int ret; |
---|
| 93 | + char *s; |
---|
99 | 94 | unsigned long starttime; |
---|
100 | 95 | |
---|
101 | 96 | if (!cpu_online(cpu) || !cpu_is_hotpluggable(cpu)) |
---|
102 | 97 | return false; |
---|
| 98 | + if (num_online_cpus() <= 1) |
---|
| 99 | + return false; /* Can't offline the last CPU. */ |
---|
103 | 100 | |
---|
104 | 101 | if (verbose > 1) |
---|
105 | 102 | pr_alert("%s" TORTURE_FLAG |
---|
.. | .. |
---|
107 | 104 | torture_type, cpu); |
---|
108 | 105 | starttime = jiffies; |
---|
109 | 106 | (*n_offl_attempts)++; |
---|
110 | | - ret = cpu_down(cpu); |
---|
| 107 | + ret = remove_cpu(cpu); |
---|
111 | 108 | if (ret) { |
---|
| 109 | + s = ""; |
---|
| 110 | + if (!rcu_inkernel_boot_has_ended() && ret == -EBUSY) { |
---|
| 111 | + // PCI probe frequently disables hotplug during boot. |
---|
| 112 | + (*n_offl_attempts)--; |
---|
| 113 | + s = " (-EBUSY forgiven during boot)"; |
---|
| 114 | + } |
---|
112 | 115 | if (verbose) |
---|
113 | 116 | pr_alert("%s" TORTURE_FLAG |
---|
114 | | - "torture_onoff task: offline %d failed: errno %d\n", |
---|
115 | | - torture_type, cpu, ret); |
---|
| 117 | + "torture_onoff task: offline %d failed%s: errno %d\n", |
---|
| 118 | + torture_type, cpu, s, ret); |
---|
116 | 119 | } else { |
---|
117 | 120 | if (verbose > 1) |
---|
118 | 121 | pr_alert("%s" TORTURE_FLAG |
---|
119 | 122 | "torture_onoff task: offlined %d\n", |
---|
120 | 123 | torture_type, cpu); |
---|
| 124 | + if (onoff_f) |
---|
| 125 | + onoff_f(); |
---|
121 | 126 | (*n_offl_successes)++; |
---|
122 | 127 | delta = jiffies - starttime; |
---|
123 | 128 | *sum_offl += delta; |
---|
.. | .. |
---|
145 | 150 | { |
---|
146 | 151 | unsigned long delta; |
---|
147 | 152 | int ret; |
---|
| 153 | + char *s; |
---|
148 | 154 | unsigned long starttime; |
---|
149 | 155 | |
---|
150 | 156 | if (cpu_online(cpu) || !cpu_is_hotpluggable(cpu)) |
---|
.. | .. |
---|
156 | 162 | torture_type, cpu); |
---|
157 | 163 | starttime = jiffies; |
---|
158 | 164 | (*n_onl_attempts)++; |
---|
159 | | - ret = cpu_up(cpu); |
---|
| 165 | + ret = add_cpu(cpu); |
---|
160 | 166 | if (ret) { |
---|
| 167 | + s = ""; |
---|
| 168 | + if (!rcu_inkernel_boot_has_ended() && ret == -EBUSY) { |
---|
| 169 | + // PCI probe frequently disables hotplug during boot. |
---|
| 170 | + (*n_onl_attempts)--; |
---|
| 171 | + s = " (-EBUSY forgiven during boot)"; |
---|
| 172 | + } |
---|
161 | 173 | if (verbose) |
---|
162 | 174 | pr_alert("%s" TORTURE_FLAG |
---|
163 | | - "torture_onoff task: online %d failed: errno %d\n", |
---|
164 | | - torture_type, cpu, ret); |
---|
| 175 | + "torture_onoff task: online %d failed%s: errno %d\n", |
---|
| 176 | + torture_type, cpu, s, ret); |
---|
165 | 177 | } else { |
---|
166 | 178 | if (verbose > 1) |
---|
167 | 179 | pr_alert("%s" TORTURE_FLAG |
---|
.. | .. |
---|
194 | 206 | int cpu; |
---|
195 | 207 | int maxcpu = -1; |
---|
196 | 208 | DEFINE_TORTURE_RANDOM(rand); |
---|
| 209 | + int ret; |
---|
197 | 210 | |
---|
198 | 211 | VERBOSE_TOROUT_STRING("torture_onoff task started"); |
---|
199 | 212 | for_each_online_cpu(cpu) |
---|
200 | 213 | maxcpu = cpu; |
---|
201 | 214 | WARN_ON(maxcpu < 0); |
---|
| 215 | + if (!IS_MODULE(CONFIG_TORTURE_TEST)) { |
---|
| 216 | + for_each_possible_cpu(cpu) { |
---|
| 217 | + if (cpu_online(cpu)) |
---|
| 218 | + continue; |
---|
| 219 | + ret = add_cpu(cpu); |
---|
| 220 | + if (ret && verbose) { |
---|
| 221 | + pr_alert("%s" TORTURE_FLAG |
---|
| 222 | + "%s: Initial online %d: errno %d\n", |
---|
| 223 | + __func__, torture_type, cpu, ret); |
---|
| 224 | + } |
---|
| 225 | + } |
---|
| 226 | + } |
---|
202 | 227 | |
---|
203 | 228 | if (maxcpu == 0) { |
---|
204 | 229 | VERBOSE_TOROUT_STRING("Only one CPU, so CPU-hotplug testing is disabled"); |
---|
.. | .. |
---|
211 | 236 | VERBOSE_TOROUT_STRING("torture_onoff end holdoff"); |
---|
212 | 237 | } |
---|
213 | 238 | while (!torture_must_stop()) { |
---|
| 239 | + if (disable_onoff_at_boot && !rcu_inkernel_boot_has_ended()) { |
---|
| 240 | + schedule_timeout_interruptible(HZ / 10); |
---|
| 241 | + continue; |
---|
| 242 | + } |
---|
214 | 243 | cpu = (torture_random(&rand) >> 4) % (maxcpu + 1); |
---|
215 | 244 | if (!torture_offline(cpu, |
---|
216 | 245 | &n_offline_attempts, &n_offline_successes, |
---|
.. | .. |
---|
231 | 260 | /* |
---|
232 | 261 | * Initiate online-offline handling. |
---|
233 | 262 | */ |
---|
234 | | -int torture_onoff_init(long ooholdoff, long oointerval) |
---|
| 263 | +int torture_onoff_init(long ooholdoff, long oointerval, torture_ofl_func *f) |
---|
235 | 264 | { |
---|
236 | | - int ret = 0; |
---|
237 | | - |
---|
238 | 265 | #ifdef CONFIG_HOTPLUG_CPU |
---|
239 | 266 | onoff_holdoff = ooholdoff; |
---|
240 | 267 | onoff_interval = oointerval; |
---|
| 268 | + onoff_f = f; |
---|
241 | 269 | if (onoff_interval <= 0) |
---|
242 | 270 | return 0; |
---|
243 | | - ret = torture_create_kthread(torture_onoff, NULL, onoff_task); |
---|
244 | | -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ |
---|
245 | | - return ret; |
---|
| 271 | + return torture_create_kthread(torture_onoff, NULL, onoff_task); |
---|
| 272 | +#else /* #ifdef CONFIG_HOTPLUG_CPU */ |
---|
| 273 | + return 0; |
---|
| 274 | +#endif /* #else #ifdef CONFIG_HOTPLUG_CPU */ |
---|
246 | 275 | } |
---|
247 | 276 | EXPORT_SYMBOL_GPL(torture_onoff_init); |
---|
248 | 277 | |
---|
.. | .. |
---|
259 | 288 | onoff_task = NULL; |
---|
260 | 289 | #endif /* #ifdef CONFIG_HOTPLUG_CPU */ |
---|
261 | 290 | } |
---|
262 | | -EXPORT_SYMBOL_GPL(torture_onoff_cleanup); |
---|
263 | 291 | |
---|
264 | 292 | /* |
---|
265 | 293 | * Print online/offline testing statistics. |
---|
.. | .. |
---|
445 | 473 | } |
---|
446 | 474 | shuffler_task = NULL; |
---|
447 | 475 | } |
---|
448 | | -EXPORT_SYMBOL_GPL(torture_shuffle_cleanup); |
---|
449 | 476 | |
---|
450 | 477 | /* |
---|
451 | 478 | * Variables for auto-shutdown. This allows "lights out" torture runs |
---|
.. | .. |
---|
503 | 530 | torture_shutdown_hook(); |
---|
504 | 531 | else |
---|
505 | 532 | VERBOSE_TOROUT_STRING("No torture_shutdown_hook(), skipping."); |
---|
506 | | - rcu_ftrace_dump(DUMP_ALL); |
---|
| 533 | + if (ftrace_dump_at_shutdown) |
---|
| 534 | + rcu_ftrace_dump(DUMP_ALL); |
---|
507 | 535 | kernel_power_off(); /* Shut down the system. */ |
---|
508 | 536 | return 0; |
---|
509 | 537 | } |
---|
.. | .. |
---|
513 | 541 | */ |
---|
514 | 542 | int torture_shutdown_init(int ssecs, void (*cleanup)(void)) |
---|
515 | 543 | { |
---|
516 | | - int ret = 0; |
---|
517 | | - |
---|
518 | 544 | torture_shutdown_hook = cleanup; |
---|
519 | 545 | if (ssecs > 0) { |
---|
520 | 546 | shutdown_time = ktime_add(ktime_get(), ktime_set(ssecs, 0)); |
---|
521 | | - ret = torture_create_kthread(torture_shutdown, NULL, |
---|
| 547 | + return torture_create_kthread(torture_shutdown, NULL, |
---|
522 | 548 | shutdown_task); |
---|
523 | 549 | } |
---|
524 | | - return ret; |
---|
| 550 | + return 0; |
---|
525 | 551 | } |
---|
526 | 552 | EXPORT_SYMBOL_GPL(torture_shutdown_init); |
---|
527 | 553 | |
---|
.. | .. |
---|
568 | 594 | static struct task_struct *stutter_task; |
---|
569 | 595 | static int stutter_pause_test; |
---|
570 | 596 | static int stutter; |
---|
| 597 | +static int stutter_gap; |
---|
571 | 598 | |
---|
572 | 599 | /* |
---|
573 | 600 | * Block until the stutter interval ends. This must be called periodically |
---|
574 | 601 | * by all running kthreads that need to be subject to stuttering. |
---|
575 | 602 | */ |
---|
576 | | -void stutter_wait(const char *title) |
---|
| 603 | +bool stutter_wait(const char *title) |
---|
577 | 604 | { |
---|
578 | 605 | int spt; |
---|
| 606 | + bool ret = false; |
---|
579 | 607 | |
---|
580 | 608 | cond_resched_tasks_rcu_qs(); |
---|
581 | 609 | spt = READ_ONCE(stutter_pause_test); |
---|
582 | 610 | for (; spt; spt = READ_ONCE(stutter_pause_test)) { |
---|
| 611 | + ret = true; |
---|
583 | 612 | if (spt == 1) { |
---|
584 | 613 | schedule_timeout_interruptible(1); |
---|
585 | 614 | } else if (spt == 2) { |
---|
.. | .. |
---|
590 | 619 | } |
---|
591 | 620 | torture_shutdown_absorb(title); |
---|
592 | 621 | } |
---|
| 622 | + return ret; |
---|
593 | 623 | } |
---|
594 | 624 | EXPORT_SYMBOL_GPL(stutter_wait); |
---|
595 | 625 | |
---|
.. | .. |
---|
599 | 629 | */ |
---|
600 | 630 | static int torture_stutter(void *arg) |
---|
601 | 631 | { |
---|
| 632 | + int wtime; |
---|
| 633 | + |
---|
602 | 634 | VERBOSE_TOROUT_STRING("torture_stutter task started"); |
---|
603 | 635 | do { |
---|
604 | 636 | if (!torture_must_stop() && stutter > 1) { |
---|
605 | | - WRITE_ONCE(stutter_pause_test, 1); |
---|
606 | | - schedule_timeout_interruptible(stutter - 1); |
---|
| 637 | + wtime = stutter; |
---|
| 638 | + if (stutter > HZ + 1) { |
---|
| 639 | + WRITE_ONCE(stutter_pause_test, 1); |
---|
| 640 | + wtime = stutter - HZ - 1; |
---|
| 641 | + schedule_timeout_interruptible(wtime); |
---|
| 642 | + wtime = HZ + 1; |
---|
| 643 | + } |
---|
607 | 644 | WRITE_ONCE(stutter_pause_test, 2); |
---|
608 | | - schedule_timeout_interruptible(1); |
---|
| 645 | + schedule_timeout_interruptible(wtime); |
---|
609 | 646 | } |
---|
610 | 647 | WRITE_ONCE(stutter_pause_test, 0); |
---|
611 | 648 | if (!torture_must_stop()) |
---|
612 | | - schedule_timeout_interruptible(stutter); |
---|
| 649 | + schedule_timeout_interruptible(stutter_gap); |
---|
613 | 650 | torture_shutdown_absorb("torture_stutter"); |
---|
614 | 651 | } while (!torture_must_stop()); |
---|
615 | 652 | torture_kthread_stopping("torture_stutter"); |
---|
.. | .. |
---|
619 | 656 | /* |
---|
620 | 657 | * Initialize and kick off the torture_stutter kthread. |
---|
621 | 658 | */ |
---|
622 | | -int torture_stutter_init(int s) |
---|
| 659 | +int torture_stutter_init(const int s, const int sgap) |
---|
623 | 660 | { |
---|
624 | | - int ret; |
---|
625 | | - |
---|
626 | 661 | stutter = s; |
---|
627 | | - ret = torture_create_kthread(torture_stutter, NULL, stutter_task); |
---|
628 | | - return ret; |
---|
| 662 | + stutter_gap = sgap; |
---|
| 663 | + return torture_create_kthread(torture_stutter, NULL, stutter_task); |
---|
629 | 664 | } |
---|
630 | 665 | EXPORT_SYMBOL_GPL(torture_stutter_init); |
---|
631 | 666 | |
---|