From: Xiongfeng Wang <wangxiongfeng2@huawei.com> hulk inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9856 -------------------------------- Commit 08ae95f4fd3b ("nohz_full: Allow the boot CPU to be nohz_full") allows the boot CPU to be a nohz_full CPU, handing over its housekeeping duty. But it is only supported when CONFIG_PM_SLEEP_SMP is not configured, or when it is configured and the arch allows suspend on non-zero CPUs. We cannot disable CONFIG_PM_SLEEP_SMP because freeze_secondary_cpus(), which is used in kexec, is included in the config. For arm64, there is no restriction in kernel to prevent nonzero cpu entering suspend using PSCI interface. when ARCH_SUSPEND_NONZERO_CPU is enabled, PM_SLEEP_SMP_NONZERO_CPU will be also enabled. The only influence is that suspend_disable_secondary_cpus() will use a housekeeping CPU to enter suspend. If pm sleep/hibernate is not involved, only kexec will use this function, which is tested on an arm64 server, and the vmcore can be created successfully. We add 'support_cpu0_nohz_full' in the CONFIG_PM_SLEEP_SMP_NONZERO_CPU check branch. When 'support_cpu0_nohz_full' is zero. There is no functional change. Besides, the normal kexec path (kexec -e, not the crash path) calls machine_shutdown() -> smp_shutdown_nonboot_cpus(reboot_cpu), with reboot_cpu being 0. After this patch makes 'nohz_full=' masks including CPU0 possible, tick_do_timer_cpu is handed over to a non-zero housekeeping CPU, and the offline of that CPU is rejected by tick_nohz_cpu_down() with -EBUSY. smp_shutdown_nonboot_cpus() then hits its unconditional BUG_ON(num_online_cpus() > 1) and kexec fails. The vmcore test above does not cover this path (the crash path goes through machine_crash_shutdown() instead). Fix it by extending smp_shutdown_nonboot_cpus() to accept primary_cpu == -1, which selects a housekeeping TIMER CPU as the reboot CPU the same way freeze_secondary_cpus() does, and make arm64's machine_shutdown() pass -1 when 'support_cpu0_nohz_full' is given and CPU0 is in the nohz_full mask. Since the reboot task was pinned to reboot_cpu (CPU0) by migrate_to_reboot_cpu() and CPU0 is now offlined by the shutdown loop, also widen its affinity to the housekeeping TIMER cpumask before calling smp_shutdown_nonboot_cpus(), so the task which executes machine_kexec() ends up on the surviving reboot CPU, as it did before. Without the boot parameter, reboot_cpu keeps being used and the behavior is unchanged. Signed-off-by: Xiongfeng Wang <wangxiongfeng2@huawei.com> Signed-off-by: Xia Fukun <xiafukun@huawei.com> --- arch/arm64/Kconfig | 3 +++ arch/arm64/kernel/process.c | 19 ++++++++++++++++++- include/linux/cpu.h | 4 +++- kernel/cpu.c | 6 ++++++ kernel/time/tick-sched.c | 2 +- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 50146c8f8617..18080b5f0edf 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2871,6 +2871,9 @@ config ARCH_HIBERNATION_HEADER config ARCH_SUSPEND_POSSIBLE def_bool y +config ARCH_SUSPEND_NONZERO_CPU + def_bool y + config ARCH_CPUIDLE_HALTPOLL bool "Enable selection of the cpuidle-haltpoll driver" help diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index e9e5ce956f15..75b59e849501 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -11,6 +11,7 @@ #include <linux/elf.h> #include <linux/export.h> #include <linux/sched.h> +#include <linux/sched/isolation.h> #include <linux/sched/debug.h> #include <linux/sched/task.h> #include <linux/sched/task_stack.h> @@ -87,7 +88,23 @@ void __noreturn arch_cpu_idle_dead(void) */ void machine_shutdown(void) { - smp_shutdown_nonboot_cpus(reboot_cpu); + unsigned int primary_cpu = reboot_cpu; + + if (support_cpu0_nohz_full && tick_nohz_full_cpu(0)) { + primary_cpu = -1; + /* + * The reboot task was pinned to reboot_cpu (CPU0) by + * migrate_to_reboot_cpu(), but CPU0 will be offlined by + * smp_shutdown_nonboot_cpus() below. Move it to a + * housekeeping TIMER CPU first, so that the task which + * executes machine_kexec() stays on the reboot CPU, + * as it did before. + */ + set_cpus_allowed_ptr(current, + housekeeping_cpumask(HK_TYPE_TIMER)); + } + + smp_shutdown_nonboot_cpus(primary_cpu); } /* diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 98e06cf4b49d..b873498b6c3a 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -20,6 +20,8 @@ #include <linux/cpuhotplug.h> #include <linux/cpuhplock.h> #include <linux/cpu_smt.h> +#include <linux/kabi.h> +#include KABI_HIDE_INCLUDE(<linux/tick.h>) struct device; struct device_node; @@ -143,7 +145,7 @@ static inline int suspend_disable_secondary_cpus(void) { int cpu = 0; - if (IS_ENABLED(CONFIG_PM_SLEEP_SMP_NONZERO_CPU)) + if (support_cpu0_nohz_full && IS_ENABLED(CONFIG_PM_SLEEP_SMP_NONZERO_CPU)) cpu = -1; return freeze_secondary_cpus(cpu); diff --git a/kernel/cpu.c b/kernel/cpu.c index 8a0955256578..cefddfbab780 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -1530,6 +1530,12 @@ void smp_shutdown_nonboot_cpus(unsigned int primary_cpu) cpu_maps_update_begin(); + if (primary_cpu == -1) { + primary_cpu = cpumask_first(cpu_online_mask); + if (!housekeeping_cpu(primary_cpu, HK_TYPE_TIMER)) + primary_cpu = housekeeping_any_cpu(HK_TYPE_TIMER); + } + /* * Make certain the cpu I'm about to reboot on is online. * diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 33a471692def..549160ecfd88 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -582,7 +582,7 @@ void __init tick_nohz_init(void) } if (IS_ENABLED(CONFIG_PM_SLEEP_SMP) && - !IS_ENABLED(CONFIG_PM_SLEEP_SMP_NONZERO_CPU)) { + (!support_cpu0_nohz_full || !IS_ENABLED(CONFIG_PM_SLEEP_SMP_NONZERO_CPU))) { cpu = smp_processor_id(); if (cpumask_test_cpu(cpu, tick_nohz_full_mask)) { -- 2.34.1