The nohz_full state (tick_nohz_full_mask) is currently a boot-time configuration that cannot be changed at runtime. This prevents dynamic CPU isolation from being fully effective, as the scheduler tick continues to run on newly isolated CPUs.
Remove the __init restriction from tick_nohz_full_setup() and tick_nohz_init(). Implement a housekeeping notifier in the tick-sched subsystem to handle HK_TYPE_TICK updates. When the mask is updated, the new isolation mask is calculated and applied, and all online CPUs are kicked to re-evaluate their tick dependency. This allows the kernel to dynamically enter or exit full dynticks mode on any CPU at runtime. Signed-off-by: Qiliang Yuan <[email protected]> Signed-off-by: Qiliang Yuan <[email protected]> --- include/linux/tick.h | 2 +- kernel/time/tick-sched.c | 63 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/include/linux/tick.h b/include/linux/tick.h index ac76ae9fa36d..40047523ec8c 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -271,7 +271,7 @@ static inline void tick_dep_clear_signal(struct signal_struct *signal, extern void tick_nohz_full_kick_cpu(int cpu); extern void __tick_nohz_task_switch(void); -extern void __init tick_nohz_full_setup(cpumask_var_t cpumask); +extern void tick_nohz_full_setup(cpumask_var_t cpumask); #else static inline bool tick_nohz_full_enabled(void) { return false; } static inline bool tick_nohz_full_cpu(int cpu) { return false; } diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 2f8a7923fa27..01d62dfc153f 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -27,6 +27,7 @@ #include <linux/posix-timers.h> #include <linux/context_tracking.h> #include <linux/mm.h> +#include <linux/sched/isolation.h> #include <asm/irq_regs.h> @@ -619,9 +620,14 @@ void __tick_nohz_task_switch(void) } /* Get the boot-time nohz CPU list from the kernel parameters. */ -void __init tick_nohz_full_setup(cpumask_var_t cpumask) +void tick_nohz_full_setup(cpumask_var_t cpumask) { - alloc_bootmem_cpumask_var(&tick_nohz_full_mask); + if (!tick_nohz_full_mask) { + if (system_state < SYSTEM_RUNNING) + alloc_bootmem_cpumask_var(&tick_nohz_full_mask); + else + zalloc_cpumask_var(&tick_nohz_full_mask, GFP_KERNEL); + } cpumask_copy(tick_nohz_full_mask, cpumask); tick_nohz_full_running = true; } @@ -643,10 +649,61 @@ static int tick_nohz_cpu_down(unsigned int cpu) return tick_nohz_cpu_hotpluggable(cpu) ? 0 : -EBUSY; } -void __init tick_nohz_init(void) +static int tick_nohz_housekeeping_reconfigure(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct housekeeping_update *upd = data; + int cpu; + + if (action == HK_UPDATE_MASK && upd->type == HK_TYPE_TICK) { + cpumask_var_t non_housekeeping_mask; + + if (!alloc_cpumask_var(&non_housekeeping_mask, GFP_KERNEL)) + return NOTIFY_BAD; + + cpumask_andnot(non_housekeeping_mask, cpu_possible_mask, upd->new_mask); + + if (!tick_nohz_full_mask) { + if (!zalloc_cpumask_var(&tick_nohz_full_mask, GFP_KERNEL)) { + free_cpumask_var(non_housekeeping_mask); + return NOTIFY_BAD; + } + } + + /* Kick all CPUs to re-evaluate tick dependency before change */ + for_each_online_cpu(cpu) + tick_nohz_full_kick_cpu(cpu); + + cpumask_copy(tick_nohz_full_mask, non_housekeeping_mask); + tick_nohz_full_running = !cpumask_empty(tick_nohz_full_mask); + + /* Kick all CPUs again to apply new nohz full state */ + for_each_online_cpu(cpu) + tick_nohz_full_kick_cpu(cpu); + + free_cpumask_var(non_housekeeping_mask); + } + + return NOTIFY_OK; +} + +static struct notifier_block tick_nohz_housekeeping_nb = { + .notifier_call = tick_nohz_housekeeping_reconfigure, +}; + +void tick_nohz_init(void) { int cpu, ret; + if (!tick_nohz_full_mask) { + if (system_state < SYSTEM_RUNNING) + alloc_bootmem_cpumask_var(&tick_nohz_full_mask); + else + zalloc_cpumask_var(&tick_nohz_full_mask, GFP_KERNEL); + } + + housekeeping_register_notifier(&tick_nohz_housekeeping_nb); + if (!tick_nohz_full_running) return; -- 2.51.0

