| .. | .. |
|---|
| 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 | |
|---|
| .. | .. |
|---|
| 753 | 788 | VERBOSE_TOROUT_STRING(buf); |
|---|
| 754 | 789 | while (!kthread_should_stop()) { |
|---|
| 755 | 790 | torture_shutdown_absorb(title); |
|---|
| 756 | | - schedule_timeout_uninterruptible(1); |
|---|
| 791 | + schedule_timeout_uninterruptible(HZ / 20); |
|---|
| 757 | 792 | } |
|---|
| 758 | 793 | } |
|---|
| 759 | 794 | EXPORT_SYMBOL_GPL(torture_kthread_stopping); |
|---|