When the cpuset code cycles a CPU through hotplug to apply kernel-noise isolation (remove_cpu followed by add_cpu), the CPU disappears from cpu_active_mask temporarily. cpuset_hotplug_update_tasks() sees an empty effective CPU set on the isolated partition and issues partcmd_invalidate, tearing down the partition. The subsequent add_cpu brings the CPU back online, but the partition has already been marked invalid and requires manual user intervention to restore.
Add a global dhm_cycling_cpus cpumask protected by dhm_cycling_lock. The isolation cycling path sets the bits for CPUs being cycled before calling remove_cpu(), clears them after add_cpu() completes. cpuset_hotplug_update_tasks() checks whether any of the cpuset's effective exclusive CPUs are in dhm_cycling_cpus and skips the invalidation command when they are, treating the transient empty-CPU state as expected rather than an error. A global cpumask avoids the need to walk the cpuset tree to find the owning cpuset during the cycling loop which runs without cpuset locks. Co-developed-by: Qiliang Yuan <[email protected]> Signed-off-by: Qiliang Yuan <[email protected]> Signed-off-by: Jing Wu <[email protected]> --- kernel/cgroup/cpuset.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 80f43a24d3c8a..62eb6798a0c3e 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -156,6 +156,14 @@ static bool update_housekeeping; /* RWCS */ */ static cpumask_var_t isolated_hk_cpus; /* T */ +/* + * CPUs currently being cycled through hotplug for kernel-noise isolation. + * Protected by dhm_cycling_lock; read in cpuset_hotplug_update_tasks() to + * suppress transient partition invalidation during the offline step. + */ +static DEFINE_SPINLOCK(dhm_cycling_lock); +static cpumask_var_t dhm_cycling_cpus; + /* * A flag to force sched domain rebuild at the end of an operation. * It can be set in @@ -3708,6 +3716,7 @@ int __init cpuset_init(void) BUG_ON(!zalloc_cpumask_var(&subpartitions_cpus, GFP_KERNEL)); BUG_ON(!zalloc_cpumask_var(&isolated_cpus, GFP_KERNEL)); BUG_ON(!zalloc_cpumask_var(&isolated_hk_cpus, GFP_KERNEL)); + BUG_ON(!zalloc_cpumask_var(&dhm_cycling_cpus, GFP_KERNEL)); cpumask_setall(top_cpuset.cpus_allowed); nodes_setall(top_cpuset.mems_allowed); @@ -3804,6 +3813,20 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp) if (remote && (cpumask_empty(subpartitions_cpus) || (cpumask_empty(&new_cpus) && partition_is_populated(cs, NULL)))) { + bool cycling; + + /* + * Suppress transient invalidation when the offline is part + * of a hotplug cycling step for kernel-noise isolation. + */ + spin_lock(&dhm_cycling_lock); + cycling = cpumask_available(dhm_cycling_cpus) && + cpumask_intersects(cs->effective_xcpus, + dhm_cycling_cpus); + spin_unlock(&dhm_cycling_lock); + if (cycling) + goto unlock; + cs->prs_err = PERR_HOTPLUG; remote_partition_disable(cs, tmp); compute_effective_cpumask(&new_cpus, cs, parent); @@ -3821,8 +3844,21 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp) if (is_local_partition(cs) && (!is_partition_valid(parent) || tasks_nocpu_error(parent, cs, &new_cpus) || - cpumask_empty(subpartitions_cpus))) - partcmd = partcmd_invalidate; + cpumask_empty(subpartitions_cpus))) { + bool cycling; + + /* + * Suppress transient invalidation when the offline is part + * of a hotplug cycling step for kernel-noise isolation. + */ + spin_lock(&dhm_cycling_lock); + cycling = cpumask_available(dhm_cycling_cpus) && + cpumask_intersects(cs->effective_xcpus, + dhm_cycling_cpus); + spin_unlock(&dhm_cycling_lock); + if (!cycling) + partcmd = partcmd_invalidate; + } /* * On the other hand, an invalid partition root may be transitioned * back to a regular one with a non-empty effective xcpus. -- 2.43.0

