diff options
author | Peter Zijlstra <peterz@infradead.org> | 2018-06-07 04:52:03 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2018-07-03 03:20:43 -0400 |
commit | 9cf57731b63e37ed995b46690adc604891a9a28f (patch) | |
tree | c8bba79ceb4c998a5f18222a60747ac52236c5a0 | |
parent | 4520843dfa34417eb1e2061f60d2345d9ca614e1 (diff) |
watchdog/softlockup: Replace "watchdog/%u" threads with cpu_stop_work
Oleg suggested to replace the "watchdog/%u" threads with
cpu_stop_work. That removes one thread per CPU while at the same time
fixes softlockup vs SCHED_DEADLINE.
But more importantly, it does away with the single
smpboot_update_cpumask_percpu_thread() user, which allows
cleanups/shrinkage of the smpboot interface.
Suggested-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | include/linux/cpuhotplug.h | 1 | ||||
-rw-r--r-- | include/linux/nmi.h | 5 | ||||
-rw-r--r-- | kernel/cpu.c | 5 | ||||
-rw-r--r-- | kernel/watchdog.c | 137 |
4 files changed, 71 insertions, 77 deletions
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 8796ba387152..4cf06a64bc02 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h | |||
@@ -164,6 +164,7 @@ enum cpuhp_state { | |||
164 | CPUHP_AP_PERF_POWERPC_NEST_IMC_ONLINE, | 164 | CPUHP_AP_PERF_POWERPC_NEST_IMC_ONLINE, |
165 | CPUHP_AP_PERF_POWERPC_CORE_IMC_ONLINE, | 165 | CPUHP_AP_PERF_POWERPC_CORE_IMC_ONLINE, |
166 | CPUHP_AP_PERF_POWERPC_THREAD_IMC_ONLINE, | 166 | CPUHP_AP_PERF_POWERPC_THREAD_IMC_ONLINE, |
167 | CPUHP_AP_WATCHDOG_ONLINE, | ||
167 | CPUHP_AP_WORKQUEUE_ONLINE, | 168 | CPUHP_AP_WORKQUEUE_ONLINE, |
168 | CPUHP_AP_RCUTREE_ONLINE, | 169 | CPUHP_AP_RCUTREE_ONLINE, |
169 | CPUHP_AP_ONLINE_DYN, | 170 | CPUHP_AP_ONLINE_DYN, |
diff --git a/include/linux/nmi.h b/include/linux/nmi.h index b8d868d23e79..80664bbeca43 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h | |||
@@ -33,10 +33,15 @@ extern int sysctl_hardlockup_all_cpu_backtrace; | |||
33 | #define sysctl_hardlockup_all_cpu_backtrace 0 | 33 | #define sysctl_hardlockup_all_cpu_backtrace 0 |
34 | #endif /* !CONFIG_SMP */ | 34 | #endif /* !CONFIG_SMP */ |
35 | 35 | ||
36 | extern int lockup_detector_online_cpu(unsigned int cpu); | ||
37 | extern int lockup_detector_offline_cpu(unsigned int cpu); | ||
38 | |||
36 | #else /* CONFIG_LOCKUP_DETECTOR */ | 39 | #else /* CONFIG_LOCKUP_DETECTOR */ |
37 | static inline void lockup_detector_init(void) { } | 40 | static inline void lockup_detector_init(void) { } |
38 | static inline void lockup_detector_soft_poweroff(void) { } | 41 | static inline void lockup_detector_soft_poweroff(void) { } |
39 | static inline void lockup_detector_cleanup(void) { } | 42 | static inline void lockup_detector_cleanup(void) { } |
43 | #define lockup_detector_online_cpu NULL | ||
44 | #define lockup_detector_offline_cpu NULL | ||
40 | #endif /* !CONFIG_LOCKUP_DETECTOR */ | 45 | #endif /* !CONFIG_LOCKUP_DETECTOR */ |
41 | 46 | ||
42 | #ifdef CONFIG_SOFTLOCKUP_DETECTOR | 47 | #ifdef CONFIG_SOFTLOCKUP_DETECTOR |
diff --git a/kernel/cpu.c b/kernel/cpu.c index 0db8938fbb23..191097c45fb1 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
@@ -1344,6 +1344,11 @@ static struct cpuhp_step cpuhp_hp_states[] = { | |||
1344 | .startup.single = perf_event_init_cpu, | 1344 | .startup.single = perf_event_init_cpu, |
1345 | .teardown.single = perf_event_exit_cpu, | 1345 | .teardown.single = perf_event_exit_cpu, |
1346 | }, | 1346 | }, |
1347 | [CPUHP_AP_WATCHDOG_ONLINE] = { | ||
1348 | .name = "lockup_detector:online", | ||
1349 | .startup.single = lockup_detector_online_cpu, | ||
1350 | .teardown.single = lockup_detector_offline_cpu, | ||
1351 | }, | ||
1347 | [CPUHP_AP_WORKQUEUE_ONLINE] = { | 1352 | [CPUHP_AP_WORKQUEUE_ONLINE] = { |
1348 | .name = "workqueue:online", | 1353 | .name = "workqueue:online", |
1349 | .startup.single = workqueue_online_cpu, | 1354 | .startup.single = workqueue_online_cpu, |
diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 576d18045811..b81f777838d5 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c | |||
@@ -18,18 +18,14 @@ | |||
18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/sysctl.h> | 20 | #include <linux/sysctl.h> |
21 | #include <linux/smpboot.h> | ||
22 | #include <linux/sched/rt.h> | ||
23 | #include <uapi/linux/sched/types.h> | ||
24 | #include <linux/tick.h> | 21 | #include <linux/tick.h> |
25 | #include <linux/workqueue.h> | ||
26 | #include <linux/sched/clock.h> | 22 | #include <linux/sched/clock.h> |
27 | #include <linux/sched/debug.h> | 23 | #include <linux/sched/debug.h> |
28 | #include <linux/sched/isolation.h> | 24 | #include <linux/sched/isolation.h> |
25 | #include <linux/stop_machine.h> | ||
29 | 26 | ||
30 | #include <asm/irq_regs.h> | 27 | #include <asm/irq_regs.h> |
31 | #include <linux/kvm_para.h> | 28 | #include <linux/kvm_para.h> |
32 | #include <linux/kthread.h> | ||
33 | 29 | ||
34 | static DEFINE_MUTEX(watchdog_mutex); | 30 | static DEFINE_MUTEX(watchdog_mutex); |
35 | 31 | ||
@@ -169,11 +165,10 @@ static void lockup_detector_update_enable(void) | |||
169 | unsigned int __read_mostly softlockup_panic = | 165 | unsigned int __read_mostly softlockup_panic = |
170 | CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE; | 166 | CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE; |
171 | 167 | ||
172 | static bool softlockup_threads_initialized __read_mostly; | 168 | static bool softlockup_initialized __read_mostly; |
173 | static u64 __read_mostly sample_period; | 169 | static u64 __read_mostly sample_period; |
174 | 170 | ||
175 | static DEFINE_PER_CPU(unsigned long, watchdog_touch_ts); | 171 | static DEFINE_PER_CPU(unsigned long, watchdog_touch_ts); |
176 | static DEFINE_PER_CPU(struct task_struct *, softlockup_watchdog); | ||
177 | static DEFINE_PER_CPU(struct hrtimer, watchdog_hrtimer); | 172 | static DEFINE_PER_CPU(struct hrtimer, watchdog_hrtimer); |
178 | static DEFINE_PER_CPU(bool, softlockup_touch_sync); | 173 | static DEFINE_PER_CPU(bool, softlockup_touch_sync); |
179 | static DEFINE_PER_CPU(bool, soft_watchdog_warn); | 174 | static DEFINE_PER_CPU(bool, soft_watchdog_warn); |
@@ -335,6 +330,25 @@ static void watchdog_interrupt_count(void) | |||
335 | __this_cpu_inc(hrtimer_interrupts); | 330 | __this_cpu_inc(hrtimer_interrupts); |
336 | } | 331 | } |
337 | 332 | ||
333 | /* | ||
334 | * The watchdog thread function - touches the timestamp. | ||
335 | * | ||
336 | * It only runs once every sample_period seconds (4 seconds by | ||
337 | * default) to reset the softlockup timestamp. If this gets delayed | ||
338 | * for more than 2*watchdog_thresh seconds then the debug-printout | ||
339 | * triggers in watchdog_timer_fn(). | ||
340 | */ | ||
341 | static int softlockup_fn(void *data) | ||
342 | { | ||
343 | __this_cpu_write(soft_lockup_hrtimer_cnt, | ||
344 | __this_cpu_read(hrtimer_interrupts)); | ||
345 | __touch_watchdog(); | ||
346 | |||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | static DEFINE_PER_CPU(struct cpu_stop_work, softlockup_stop_work); | ||
351 | |||
338 | /* watchdog kicker functions */ | 352 | /* watchdog kicker functions */ |
339 | static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) | 353 | static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) |
340 | { | 354 | { |
@@ -350,7 +364,9 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) | |||
350 | watchdog_interrupt_count(); | 364 | watchdog_interrupt_count(); |
351 | 365 | ||
352 | /* kick the softlockup detector */ | 366 | /* kick the softlockup detector */ |
353 | wake_up_process(__this_cpu_read(softlockup_watchdog)); | 367 | stop_one_cpu_nowait(smp_processor_id(), |
368 | softlockup_fn, NULL, | ||
369 | this_cpu_ptr(&softlockup_stop_work)); | ||
354 | 370 | ||
355 | /* .. and repeat */ | 371 | /* .. and repeat */ |
356 | hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period)); | 372 | hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period)); |
@@ -448,17 +464,12 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) | |||
448 | return HRTIMER_RESTART; | 464 | return HRTIMER_RESTART; |
449 | } | 465 | } |
450 | 466 | ||
451 | static void watchdog_set_prio(unsigned int policy, unsigned int prio) | ||
452 | { | ||
453 | struct sched_param param = { .sched_priority = prio }; | ||
454 | |||
455 | sched_setscheduler(current, policy, ¶m); | ||
456 | } | ||
457 | |||
458 | static void watchdog_enable(unsigned int cpu) | 467 | static void watchdog_enable(unsigned int cpu) |
459 | { | 468 | { |
460 | struct hrtimer *hrtimer = this_cpu_ptr(&watchdog_hrtimer); | 469 | struct hrtimer *hrtimer = this_cpu_ptr(&watchdog_hrtimer); |
461 | 470 | ||
471 | WARN_ON_ONCE(cpu != smp_processor_id()); | ||
472 | |||
462 | /* | 473 | /* |
463 | * Start the timer first to prevent the NMI watchdog triggering | 474 | * Start the timer first to prevent the NMI watchdog triggering |
464 | * before the timer has a chance to fire. | 475 | * before the timer has a chance to fire. |
@@ -473,15 +484,14 @@ static void watchdog_enable(unsigned int cpu) | |||
473 | /* Enable the perf event */ | 484 | /* Enable the perf event */ |
474 | if (watchdog_enabled & NMI_WATCHDOG_ENABLED) | 485 | if (watchdog_enabled & NMI_WATCHDOG_ENABLED) |
475 | watchdog_nmi_enable(cpu); | 486 | watchdog_nmi_enable(cpu); |
476 | |||
477 | watchdog_set_prio(SCHED_FIFO, MAX_RT_PRIO - 1); | ||
478 | } | 487 | } |
479 | 488 | ||
480 | static void watchdog_disable(unsigned int cpu) | 489 | static void watchdog_disable(unsigned int cpu) |
481 | { | 490 | { |
482 | struct hrtimer *hrtimer = this_cpu_ptr(&watchdog_hrtimer); | 491 | struct hrtimer *hrtimer = this_cpu_ptr(&watchdog_hrtimer); |
483 | 492 | ||
484 | watchdog_set_prio(SCHED_NORMAL, 0); | 493 | WARN_ON_ONCE(cpu != smp_processor_id()); |
494 | |||
485 | /* | 495 | /* |
486 | * Disable the perf event first. That prevents that a large delay | 496 | * Disable the perf event first. That prevents that a large delay |
487 | * between disabling the timer and disabling the perf event causes | 497 | * between disabling the timer and disabling the perf event causes |
@@ -491,77 +501,63 @@ static void watchdog_disable(unsigned int cpu) | |||
491 | hrtimer_cancel(hrtimer); | 501 | hrtimer_cancel(hrtimer); |
492 | } | 502 | } |
493 | 503 | ||
494 | static void watchdog_cleanup(unsigned int cpu, bool online) | 504 | static int softlockup_stop_fn(void *data) |
495 | { | 505 | { |
496 | watchdog_disable(cpu); | 506 | watchdog_disable(smp_processor_id()); |
507 | return 0; | ||
497 | } | 508 | } |
498 | 509 | ||
499 | static int watchdog_should_run(unsigned int cpu) | 510 | static void softlockup_stop_all(void) |
500 | { | 511 | { |
501 | return __this_cpu_read(hrtimer_interrupts) != | 512 | int cpu; |
502 | __this_cpu_read(soft_lockup_hrtimer_cnt); | 513 | |
514 | if (!softlockup_initialized) | ||
515 | return; | ||
516 | |||
517 | for_each_cpu(cpu, &watchdog_allowed_mask) | ||
518 | smp_call_on_cpu(cpu, softlockup_stop_fn, NULL, false); | ||
519 | |||
520 | cpumask_clear(&watchdog_allowed_mask); | ||
503 | } | 521 | } |
504 | 522 | ||
505 | /* | 523 | static int softlockup_start_fn(void *data) |
506 | * The watchdog thread function - touches the timestamp. | ||
507 | * | ||
508 | * It only runs once every sample_period seconds (4 seconds by | ||
509 | * default) to reset the softlockup timestamp. If this gets delayed | ||
510 | * for more than 2*watchdog_thresh seconds then the debug-printout | ||
511 | * triggers in watchdog_timer_fn(). | ||
512 | */ | ||
513 | static void watchdog(unsigned int cpu) | ||
514 | { | 524 | { |
515 | __this_cpu_write(soft_lockup_hrtimer_cnt, | 525 | watchdog_enable(smp_processor_id()); |
516 | __this_cpu_read(hrtimer_interrupts)); | 526 | return 0; |
517 | __touch_watchdog(); | ||
518 | } | 527 | } |
519 | 528 | ||
520 | static struct smp_hotplug_thread watchdog_threads = { | 529 | static void softlockup_start_all(void) |
521 | .store = &softlockup_watchdog, | ||
522 | .thread_should_run = watchdog_should_run, | ||
523 | .thread_fn = watchdog, | ||
524 | .thread_comm = "watchdog/%u", | ||
525 | .setup = watchdog_enable, | ||
526 | .cleanup = watchdog_cleanup, | ||
527 | .park = watchdog_disable, | ||
528 | .unpark = watchdog_enable, | ||
529 | }; | ||
530 | |||
531 | static void softlockup_update_smpboot_threads(void) | ||
532 | { | 530 | { |
533 | lockdep_assert_held(&watchdog_mutex); | 531 | int cpu; |
534 | |||
535 | if (!softlockup_threads_initialized) | ||
536 | return; | ||
537 | 532 | ||
538 | smpboot_update_cpumask_percpu_thread(&watchdog_threads, | 533 | cpumask_copy(&watchdog_allowed_mask, &watchdog_cpumask); |
539 | &watchdog_allowed_mask); | 534 | for_each_cpu(cpu, &watchdog_allowed_mask) |
535 | smp_call_on_cpu(cpu, softlockup_start_fn, NULL, false); | ||
540 | } | 536 | } |
541 | 537 | ||
542 | /* Temporarily park all watchdog threads */ | 538 | int lockup_detector_online_cpu(unsigned int cpu) |
543 | static void softlockup_park_all_threads(void) | ||
544 | { | 539 | { |
545 | cpumask_clear(&watchdog_allowed_mask); | 540 | watchdog_enable(cpu); |
546 | softlockup_update_smpboot_threads(); | 541 | return 0; |
547 | } | 542 | } |
548 | 543 | ||
549 | /* Unpark enabled threads */ | 544 | int lockup_detector_offline_cpu(unsigned int cpu) |
550 | static void softlockup_unpark_threads(void) | ||
551 | { | 545 | { |
552 | cpumask_copy(&watchdog_allowed_mask, &watchdog_cpumask); | 546 | watchdog_disable(cpu); |
553 | softlockup_update_smpboot_threads(); | 547 | return 0; |
554 | } | 548 | } |
555 | 549 | ||
556 | static void lockup_detector_reconfigure(void) | 550 | static void lockup_detector_reconfigure(void) |
557 | { | 551 | { |
558 | cpus_read_lock(); | 552 | cpus_read_lock(); |
559 | watchdog_nmi_stop(); | 553 | watchdog_nmi_stop(); |
560 | softlockup_park_all_threads(); | 554 | |
555 | softlockup_stop_all(); | ||
561 | set_sample_period(); | 556 | set_sample_period(); |
562 | lockup_detector_update_enable(); | 557 | lockup_detector_update_enable(); |
563 | if (watchdog_enabled && watchdog_thresh) | 558 | if (watchdog_enabled && watchdog_thresh) |
564 | softlockup_unpark_threads(); | 559 | softlockup_start_all(); |
560 | |||
565 | watchdog_nmi_start(); | 561 | watchdog_nmi_start(); |
566 | cpus_read_unlock(); | 562 | cpus_read_unlock(); |
567 | /* | 563 | /* |
@@ -580,8 +576,6 @@ static void lockup_detector_reconfigure(void) | |||
580 | */ | 576 | */ |
581 | static __init void lockup_detector_setup(void) | 577 | static __init void lockup_detector_setup(void) |
582 | { | 578 | { |
583 | int ret; | ||
584 | |||
585 | /* | 579 | /* |
586 | * If sysctl is off and watchdog got disabled on the command line, | 580 | * If sysctl is off and watchdog got disabled on the command line, |
587 | * nothing to do here. | 581 | * nothing to do here. |
@@ -592,24 +586,13 @@ static __init void lockup_detector_setup(void) | |||
592 | !(watchdog_enabled && watchdog_thresh)) | 586 | !(watchdog_enabled && watchdog_thresh)) |
593 | return; | 587 | return; |
594 | 588 | ||
595 | ret = smpboot_register_percpu_thread_cpumask(&watchdog_threads, | ||
596 | &watchdog_allowed_mask); | ||
597 | if (ret) { | ||
598 | pr_err("Failed to initialize soft lockup detector threads\n"); | ||
599 | return; | ||
600 | } | ||
601 | |||
602 | mutex_lock(&watchdog_mutex); | 589 | mutex_lock(&watchdog_mutex); |
603 | softlockup_threads_initialized = true; | ||
604 | lockup_detector_reconfigure(); | 590 | lockup_detector_reconfigure(); |
591 | softlockup_initialized = true; | ||
605 | mutex_unlock(&watchdog_mutex); | 592 | mutex_unlock(&watchdog_mutex); |
606 | } | 593 | } |
607 | 594 | ||
608 | #else /* CONFIG_SOFTLOCKUP_DETECTOR */ | 595 | #else /* CONFIG_SOFTLOCKUP_DETECTOR */ |
609 | static inline int watchdog_park_threads(void) { return 0; } | ||
610 | static inline void watchdog_unpark_threads(void) { } | ||
611 | static inline int watchdog_enable_all_cpus(void) { return 0; } | ||
612 | static inline void watchdog_disable_all_cpus(void) { } | ||
613 | static void lockup_detector_reconfigure(void) | 596 | static void lockup_detector_reconfigure(void) |
614 | { | 597 | { |
615 | cpus_read_lock(); | 598 | cpus_read_lock(); |