From nobody Fri Jul 24 22:51:47 2026 Received: from out-171.mta0.migadu.com (out-171.mta0.migadu.com [91.218.175.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BA1E11F4CB3 for ; Wed, 22 Jul 2026 11:53:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784721215; cv=none; b=HzR2Ej2NTsfgOqZzwl8ALUxUhLsOetczr1Qw5qIXfIKT/lVPjmV3icoLIA7HvooozGmfm1LwGX/USkSs4+XfyJtO8pK60Bqi7+BHBJdH3jUNjetDO6IGjjvLZ4WgCUbw1A3MKuZU92BWu9gAvpDQf+siBqdDq31Risob0/Nb8kE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784721215; c=relaxed/simple; bh=7+fwcN9NaXNpkWXppLOssY1H5IABqdNQhNOlr33rJeA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZLl72kJKN17OVXoA2GZ/RwRo5R5y+tWAZOirbBoan/f12acJP3oeY7uPgTSufGsyg6KYFvbuFLWR0xuz5P7jdRmUIY5OsM2TSRZkOdJzC7qC/EUwfrJcl6peR/3m8qAWvw2fvy5CDqBSIxVbFEGsxNb5m1vYCPIN98fZJxTbK9Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=Y1veDYHK; arc=none smtp.client-ip=91.218.175.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="Y1veDYHK" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1784721211; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=SP33Qvev1m/W4AQ53CwbMRSLhDuVOFiwNfxjGtC5Fzo=; b=Y1veDYHKeTvwnR6MGT+tmIisqNlIKKw8pqG+wzqXFRkZYQK7SZp0FeTObSR6OdIB1fsYPc 7IJC3trQomj1a0U9Rofh3lsbSI44K0UHNOHzeHufn6K60+iVZU9KY1Hg57WOg8xbA8gA+Y UPs0wfMHdDSOfZKYRucxhw54dc/Cv/U= From: Guopeng Zhang To: Peter Zijlstra , Ingo Molnar , Thomas Gleixner , Waiman Long Cc: Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , K Prateek Nayak , Frederic Weisbecker , Ridong Chen , Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , "Srivatsa S . Bhat" , Guopeng Zhang , cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Guopeng Zhang Subject: [RFC PATCH 1/3] sched: Preserve user affinity across frozen CPU fallback Date: Wed, 22 Jul 2026 19:52:36 +0800 Message-ID: <20260722115238.351821-2-guopeng.zhang@linux.dev> In-Reply-To: <20260722115238.351821-1-guopeng.zhang@linux.dev> References: <20260722115238.351821-1-guopeng.zhang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" From: Guopeng Zhang select_fallback_rq() uses set_cpus_allowed_force() when a task has no allowed active CPU. The forced update normally clears user_cpus_ptr because the fallback is treated as a permanent affinity override. CPU freeze is different. freeze_secondary_cpus() removes the secondary CPUs only for the duration of suspend, and a task restricted to those CPUs may need a temporary fallback before they are brought online again. Clearing user_cpus_ptr in this case makes the temporary forced affinity survive after the secondary CPUs have been brought back online. Preserve user_cpus_ptr for non-kthreads while a CPU freeze transaction is in progress and restore the effective affinity after the frozen CPUs have been brought back online. Stop accepting new temporary fallbacks before collecting affected tasks, copy each requested mask under the task locks, and retry the restore if a concurrent explicit affinity update replaces it. This makes the newer affinity request take precedence. If immediate restoration fails, retain user_cpus_ptr so that a later affinity or cpuset update can reapply the requested mask. Fixes: 851a723e45d1 ("sched: Always clear user_cpus_ptr in do_set_cpus_allo= wed()") Signed-off-by: Guopeng Zhang --- include/linux/sched/hotplug.h | 2 + kernel/cpu.c | 2 + kernel/sched/core.c | 184 ++++++++++++++++++++++++++++++++-- kernel/sched/sched.h | 4 +- 4 files changed, 185 insertions(+), 7 deletions(-) diff --git a/include/linux/sched/hotplug.h b/include/linux/sched/hotplug.h index 17e04859b9a4..2cfd447b345a 100644 --- a/include/linux/sched/hotplug.h +++ b/include/linux/sched/hotplug.h @@ -9,6 +9,8 @@ extern int sched_cpu_starting(unsigned int cpu); extern int sched_cpu_activate(unsigned int cpu); extern int sched_cpu_deactivate(unsigned int cpu); +void sched_cpu_fallback_begin(void); +void sched_cpu_fallback_end(void); =20 #ifdef CONFIG_HOTPLUG_CPU extern int sched_cpu_wait_empty(unsigned int cpu); diff --git a/kernel/cpu.c b/kernel/cpu.c index b3c8553d7bd6..67828c2158ae 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -1902,6 +1902,7 @@ int freeze_secondary_cpus(int primary) * with the userspace trying to use the CPU hotplug at the same time */ cpumask_clear(frozen_cpus); + sched_cpu_fallback_begin(); =20 pr_info("Disabling non-boot CPUs ...\n"); for (cpu =3D nr_cpu_ids - 1; cpu >=3D 0; cpu--) { @@ -1978,6 +1979,7 @@ void thaw_secondary_cpus(void) =20 cpumask_clear(frozen_cpus); out: + sched_cpu_fallback_end(); cpu_maps_update_done(); } =20 diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 2e7cde033a31..8160d3287ad9 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2771,6 +2771,13 @@ static inline void mm_update_cpus_allowed(struct mm_= struct *mm, const cpumask_t * sched_class::set_cpus_allowed must do the below, but is not required to * actually call this function. */ +static void swap_user_cpus_ptr(struct task_struct *p, struct affinity_cont= ext *ctx) +{ + if (p->migration_flags & MDF_CPUHP_FALLBACK) + p->migration_flags |=3D MDF_CPUHP_AFFINITY_CHANGED; + swap(p->user_cpus_ptr, ctx->user_mask); +} + void set_cpus_allowed_common(struct task_struct *p, struct affinity_contex= t *ctx) { if (ctx->flags & (SCA_MIGRATE_ENABLE | SCA_MIGRATE_DISABLE)) { @@ -2786,7 +2793,7 @@ void set_cpus_allowed_common(struct task_struct *p, s= truct affinity_context *ctx * Swap in a new user_cpus_ptr if SCA_USER flag set */ if (ctx->flags & SCA_USER) - swap(p->user_cpus_ptr, ctx->user_mask); + swap_user_cpus_ptr(p, ctx); } =20 static void @@ -2796,24 +2803,49 @@ do_set_cpus_allowed(struct task_struct *p, struct a= ffinity_context *ctx) p->sched_class->set_cpus_allowed(p, ctx); } =20 +static DEFINE_RAW_SPINLOCK(cpu_fallback_lock); +static bool cpu_fallback_active; + +static bool mark_cpu_fallback(struct task_struct *p) +{ + unsigned long flags; + bool temporary =3D false; + + if (p->flags & PF_KTHREAD) + return false; + + raw_spin_lock_irqsave(&cpu_fallback_lock, flags); + if (cpu_fallback_active) { + p->migration_flags |=3D MDF_CPUHP_FALLBACK; + temporary =3D true; + } + raw_spin_unlock_irqrestore(&cpu_fallback_lock, flags); + + return temporary; +} + /* - * Used for kthread_bind() and select_fallback_rq(), in both cases the user - * affinity (if any) should be destroyed too. + * Used for kthread_bind() and select_fallback_rq(). A user task which is + * forcefully moved while CPUs are frozen must retain its requested affini= ty + * so it can be restored when the CPUs come back online. */ void set_cpus_allowed_force(struct task_struct *p, const struct cpumask *n= ew_mask) { struct affinity_context ac =3D { .new_mask =3D new_mask, .user_mask =3D NULL, - .flags =3D SCA_USER, /* clear the user requested mask */ + .flags =3D SCA_USER, }; union cpumask_rcuhead { cpumask_t cpumask; struct rcu_head rcu; }; =20 - scoped_guard (__task_rq_lock, p) + scoped_guard (__task_rq_lock, p) { + if (mark_cpu_fallback(p)) + ac.flags =3D 0; do_set_cpus_allowed(p, &ac); + } =20 /* * Because this is called with p->pi_lock held, it is not possible @@ -3152,7 +3184,7 @@ static int __set_cpus_allowed_ptr_locked(struct task_= struct *p, if (!(ctx->flags & SCA_MIGRATE_ENABLE)) { if (cpumask_equal(&p->cpus_mask, ctx->new_mask)) { if (ctx->flags & SCA_USER) - swap(p->user_cpus_ptr, ctx->user_mask); + swap_user_cpus_ptr(p, ctx); goto out; } =20 @@ -3607,6 +3639,145 @@ static int select_fallback_rq(int cpu, struct task_= struct *p) return dest_cpu; } =20 +static bool task_has_cpu_fallback(struct task_struct *p) +{ + return READ_ONCE(p->migration_flags) & MDF_CPUHP_FALLBACK; +} + +static void restore_cpu_fallback(struct task_struct *p) +{ + struct affinity_context ac =3D { .flags =3D 0 }; + cpumask_var_t mask; + struct rq_flags rf; + struct rq *rq; + bool retry; + int ret; + + if (!alloc_cpumask_var(&mask, GFP_KERNEL)) { + pr_warn_ratelimited("Failed to allocate affinity mask for task %d\n", + task_pid_nr(p)); + goto clear; + } + + for (;;) { + rq =3D task_rq_lock(p, &rf); + if (!(p->migration_flags & MDF_CPUHP_FALLBACK) || + (p->flags & PF_EXITING)) { + p->migration_flags &=3D ~(MDF_CPUHP_FALLBACK | + MDF_CPUHP_AFFINITY_CHANGED); + task_rq_unlock(rq, p, &rf); + break; + } + + p->migration_flags &=3D ~MDF_CPUHP_AFFINITY_CHANGED; + cpumask_copy(mask, task_user_cpus(p)); + task_rq_unlock(rq, p, &rf); + + ac.new_mask =3D mask; + ret =3D __sched_setaffinity(p, &ac); + + rq =3D task_rq_lock(p, &rf); + retry =3D p->migration_flags & MDF_CPUHP_AFFINITY_CHANGED; + if (!retry) + p->migration_flags &=3D ~MDF_CPUHP_FALLBACK; + task_rq_unlock(rq, p, &rf); + + if (!retry) { + if (ret) + pr_warn_ratelimited("Failed to restore affinity for task %d: %d\n", + task_pid_nr(p), ret); + break; + } + } + + free_cpumask_var(mask); + return; + +clear: + rq =3D task_rq_lock(p, &rf); + p->migration_flags &=3D ~(MDF_CPUHP_FALLBACK | + MDF_CPUHP_AFFINITY_CHANGED); + task_rq_unlock(rq, p, &rf); +} + +static struct task_struct *find_cpu_fallback_task(void) +{ + struct task_struct *g, *p, *task =3D NULL; + + read_lock(&tasklist_lock); + for_each_process_thread(g, p) { + if (!task_has_cpu_fallback(p)) + continue; + task =3D get_task_struct(p); + break; + } + read_unlock(&tasklist_lock); + + return task; +} + +static void restore_cpu_fallback_tasks(void) +{ + struct task_struct **tasks, *g, *p, *task; + unsigned int count =3D 0, nr =3D 0, i; + + read_lock(&tasklist_lock); + for_each_process_thread(g, p) + count +=3D task_has_cpu_fallback(p); + read_unlock(&tasklist_lock); + + if (!count) + return; + + tasks =3D kvcalloc(count, sizeof(*tasks), GFP_KERNEL); + if (!tasks) + goto scan; + + read_lock(&tasklist_lock); + for_each_process_thread(g, p) { + if (!task_has_cpu_fallback(p)) + continue; + tasks[nr++] =3D get_task_struct(p); + if (nr =3D=3D count) + break; + } + read_unlock(&tasklist_lock); + + for (i =3D 0; i < nr; i++) { + restore_cpu_fallback(tasks[i]); + put_task_struct(tasks[i]); + } + kvfree(tasks); + + /* Catch a child which inherited a fallback while tasks were collected. */ +scan: + while ((task =3D find_cpu_fallback_task())) { + restore_cpu_fallback(task); + put_task_struct(task); + } +} + +void sched_cpu_fallback_begin(void) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&cpu_fallback_lock, flags); + WARN_ON_ONCE(cpu_fallback_active); + WRITE_ONCE(cpu_fallback_active, true); + raw_spin_unlock_irqrestore(&cpu_fallback_lock, flags); +} + +void sched_cpu_fallback_end(void) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&cpu_fallback_lock, flags); + WRITE_ONCE(cpu_fallback_active, false); + raw_spin_unlock_irqrestore(&cpu_fallback_lock, flags); + + restore_cpu_fallback_tasks(); +} + /* * The caller (fork, wakeup) owns p->pi_lock, ->cpus_ptr is stable. */ @@ -4612,6 +4783,7 @@ static void __sched_fork(u64 clone_flags, struct task= _struct *p) init_numa_balancing(clone_flags, p); p->wake_entry.u_flags =3D CSD_TYPE_TTWU; p->migration_pending =3D NULL; + p->migration_flags &=3D ~MDF_CPUHP_AFFINITY_CHANGED; init_sched_mm(p); } =20 diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index e3e4e68bb0c5..d44dff5186e3 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1402,7 +1402,9 @@ static inline int cpu_of(struct rq *rq) return rq->cpu; } =20 -#define MDF_PUSH 0x01 +#define MDF_PUSH 0x01 +#define MDF_CPUHP_FALLBACK 0x02 +#define MDF_CPUHP_AFFINITY_CHANGED 0x04 =20 static inline bool is_migration_disabled(struct task_struct *p) { --=20 2.43.0 From nobody Fri Jul 24 22:51:47 2026 Received: from out-185.mta0.migadu.com (out-185.mta0.migadu.com [91.218.175.185]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A3A301F4CB3 for ; Wed, 22 Jul 2026 11:53:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.185 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784721222; cv=none; b=PyAt20LDzjbwqntDdVosxXrAdNijlP6zcW2wmzSFj7Noh8irJ/RwnNhK/A7LUc5chbw4H7fK/MYwQrF2ey7iEmlbV2omlPoLX/sDm+vnUQOjTq76QIqJCz+Rt7CvojvFlLPFpm8/UZMWMxvoyccUW2Fq6UZ5LTn5dAQ/pyZHkMU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784721222; c=relaxed/simple; bh=kCIbQODaPIFKjJj/fH0W+dkBFWKUA23nflhhqT1ggho=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jEkXMBeThkJW1VS30S72chHo0DIhO6c7DZEdoHX+NAMysojX6AsNcABuwQkOs9eenIXJr/Bi9zug4+1OKsWdRTN8JFSmhnBxt4mwc11Bq4vNuzrC/sZDjTVvSYA4HO8vZhLhpUJ1IZVapLS0Qz3e8WNj3FOrjNUcnRcJpP1nrOk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=pyHTGln3; arc=none smtp.client-ip=91.218.175.185 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="pyHTGln3" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1784721217; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZarCgVr6XKsuj6RlRk5qNVeMs/1XC2cyTfQWnkNpbBA=; b=pyHTGln336t7kNvG9iLgbqJrrpi7Yc8EBmesFSJLVvA39EZTL94SZN5g+3XRFTyMQtxjE+ uFUD8rgOhDhANHhtyQ9yj6khGEGui4r5FoO8/rgbVd2EYQl4Ns/F0AhzakHFd0VBPJlUMP /Or00ZIr4YMGRzD9dzcix92FMUr7FaE= From: Guopeng Zhang To: Peter Zijlstra , Ingo Molnar , Thomas Gleixner , Waiman Long Cc: Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , K Prateek Nayak , Frederic Weisbecker , Ridong Chen , Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , "Srivatsa S . Bhat" , Guopeng Zhang , cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Guopeng Zhang Subject: [RFC PATCH 2/3] sched: Allow isolated CPUs as a last resort during CPU freeze Date: Wed, 22 Jul 2026 19:52:37 +0800 Message-ID: <20260722115238.351821-3-guopeng.zhang@linux.dev> In-Reply-To: <20260722115238.351821-1-guopeng.zhang@linux.dev> References: <20260722115238.351821-1-guopeng.zhang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" From: Guopeng Zhang task_cpu_fallback_mask() normally excludes domain-isolated CPUs. During CPU freeze, however, every CPU except the suspend primary is taken offline. If the primary CPU is excluded from HK_TYPE_DOMAIN, the normal fallback mask eventually contains no active CPU and select_fallback_rq() can exhaust its fallback states. When CPU freeze is in progress and the normal fallback mask contains no active CPU, use task_cpu_possible_mask() as a temporary last resort if it still contains an active CPU. select_fallback_rq() will select only an active CPU from that mask, while architecture-specific task CPU capability constraints remain intact. Keep the normal housekeeping fallback while it contains an active CPU. The temporary affinity is preserved and restored by the preceding change. Fixes: bf5b0c27fad2 ("sched: Switch the fallback task allowed cpumask to HK= _TYPE_DOMAIN") Signed-off-by: Guopeng Zhang --- kernel/sched/core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 8160d3287ad9..5f8ffded1a20 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2806,8 +2806,10 @@ do_set_cpus_allowed(struct task_struct *p, struct af= finity_context *ctx) static DEFINE_RAW_SPINLOCK(cpu_fallback_lock); static bool cpu_fallback_active; =20 -static bool mark_cpu_fallback(struct task_struct *p) +static bool mark_cpu_fallback(struct task_struct *p, + const struct cpumask **fallback_mask) { + const struct cpumask *possible_mask; unsigned long flags; bool temporary =3D false; =20 @@ -2817,6 +2819,11 @@ static bool mark_cpu_fallback(struct task_struct *p) raw_spin_lock_irqsave(&cpu_fallback_lock, flags); if (cpu_fallback_active) { p->migration_flags |=3D MDF_CPUHP_FALLBACK; + if (!cpumask_intersects(*fallback_mask, cpu_active_mask)) { + possible_mask =3D task_cpu_possible_mask(p); + if (cpumask_intersects(possible_mask, cpu_active_mask)) + *fallback_mask =3D possible_mask; + } temporary =3D true; } raw_spin_unlock_irqrestore(&cpu_fallback_lock, flags); @@ -2842,7 +2849,7 @@ void set_cpus_allowed_force(struct task_struct *p, co= nst struct cpumask *new_mas }; =20 scoped_guard (__task_rq_lock, p) { - if (mark_cpu_fallback(p)) + if (mark_cpu_fallback(p, &ac.new_mask)) ac.flags =3D 0; do_set_cpus_allowed(p, &ac); } --=20 2.43.0 From nobody Fri Jul 24 22:51:47 2026 Received: from out-177.mta0.migadu.com (out-177.mta0.migadu.com [91.218.175.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5A670443AAD for ; Wed, 22 Jul 2026 11:53:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784721228; cv=none; b=kyjeK08wfAjWGYQVoVAohZol5362lHRO8f2HmJ+KnHcbIXwYirigmKq588ERUZdsH/dxr9iAZokyxIFAowprP0h89hHteGnz873Gh/1l1WSbBRVeXJ+nLO5mrjr6BcZKOgoIFVZeF67a+tHrjtexKIIm1LOT8czG5CAb3b8iZcg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784721228; c=relaxed/simple; bh=iYMs7RNngqDNLdKkal1IsjhamRHp5KypEY/HBt/A8fk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ao/hWu5vwPINiZLD6X9rlZHpQO4GKdjjAJQzeIfkfZm9NZjMG4WnwEf40RsnIW17SUrQiFhZ3A0RtcGXw+JdYLOx6SSkOf2/cz1h4i6xGaVSPOh4WmjnEqJmTJ91NfBXYU5bbqPzbO0Usd5Ju67zPg8mYKmBZskXTAywf5lgoZc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=lqLuVUGD; arc=none smtp.client-ip=91.218.175.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="lqLuVUGD" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1784721223; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=NKx6H9tY68FCqpiKdD6mzmMC28uWm7YjX5IgYP0hdK8=; b=lqLuVUGDGJdaeyqm8zzweWV/LAopemMjdL9q0mQjYYELPkepJGJt270f7fN6KB1SM8O+0v 60vEdDHmuHHaWkERk2fJK+9XNglSRAKZopL99xM0Gp7qnvp1Nnf/OS8e+tMnWTRmX/Iemo msCsX9koTeT5FZ1hQtK31Xw0JNN3L00= From: Guopeng Zhang To: Peter Zijlstra , Ingo Molnar , Thomas Gleixner , Waiman Long Cc: Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , K Prateek Nayak , Frederic Weisbecker , Ridong Chen , Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , "Srivatsa S . Bhat" , Guopeng Zhang , cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Guopeng Zhang Subject: [RFC PATCH 3/3] sched/topology: Tear down domains without active domain housekeeping CPUs Date: Wed, 22 Jul 2026 19:52:38 +0800 Message-ID: <20260722115238.351821-4-guopeng.zhang@linux.dev> In-Reply-To: <20260722115238.351821-1-guopeng.zhang@linux.dev> References: <20260722115238.351821-1-guopeng.zhang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" From: Guopeng Zhang During suspend, freeze_secondary_cpus() takes every CPU except the primary offline. If the primary CPU is excluded from HK_TYPE_DOMAIN, offlining the last domain housekeeping CPU leaves no active CPU from which to build the fallback scheduler domain. The frozen CPU-hotplug callback nevertheless requests one domain by passing a non-zero domain count with a NULL domain array. partition_sched_domains_locked() then constructs the fallback span from cpu_active_mask and housekeeping_cpumask(HK_TYPE_DOMAIN), which is empty. build_sched_domains() warns about the empty span, followed by a general protection fault in build_perf_domains(). Make cpuset_reset_sched_domains(), including its !CONFIG_CPUSETS stub, request zero domains when no active HK_TYPE_DOMAIN CPU remains. Distinguish that explicit zero-domain request from a non-zero request with a NULL domain array, for which the existing fallback-domain behavior must be retained. Fixes: d35be8bab9b0 ("CPU hotplug, cpusets, suspend: Don't modify cpusets d= uring suspend/resume") Signed-off-by: Guopeng Zhang --- include/linux/cpuset.h | 7 ++++++- kernel/cgroup/cpuset.c | 6 +++++- kernel/sched/topology.c | 9 ++++----- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index a67ba1b32d16..461ba4876a8e 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -10,6 +10,7 @@ */ =20 #include +#include #include #include #include @@ -284,7 +285,11 @@ static inline void rebuild_sched_domains(void) =20 static inline void cpuset_reset_sched_domains(void) { - partition_sched_domains(1, NULL, NULL); + if (cpumask_intersects(cpu_active_mask, + housekeeping_cpumask(HK_TYPE_DOMAIN))) + partition_sched_domains(1, NULL, NULL); + else + partition_sched_domains(0, NULL, NULL); } =20 static inline void cpuset_print_current_mems_allowed(void) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 03e82d6026f5..1e66723e01b4 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -1076,7 +1076,11 @@ void rebuild_sched_domains(void) void cpuset_reset_sched_domains(void) { mutex_lock(&cpuset_mutex); - partition_sched_domains(1, NULL, NULL); + if (cpumask_intersects(cpu_active_mask, + housekeeping_cpumask(HK_TYPE_DOMAIN))) + partition_sched_domains(1, NULL, NULL); + else + partition_sched_domains(0, NULL, NULL); mutex_unlock(&cpuset_mutex); } =20 diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 622e2e01974c..e8d34cb1ed84 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -3365,9 +3365,8 @@ static int dattrs_equal(struct sched_domain_attr *cur= , int idx_cur, * and partition_sched_domains() will fallback to the single partition * 'fallback_doms', it also forces the domains to be rebuilt. * - * If doms_new =3D=3D NULL it will be replaced with cpu_online_mask. - * ndoms_new =3D=3D 0 is a special case for destroying existing domains, - * and it will not create the default domain. + * ndoms_new =3D=3D 0 and doms_new =3D=3D NULL is a special case for destr= oying + * existing domains, and it will not create the default domain. * * Call with hotplug lock and sched_domains_mutex held */ @@ -3387,7 +3386,7 @@ static void partition_sched_domains_locked(int ndoms_= new, cpumask_var_t doms_new if (new_topology) asym_cpu_capacity_scan(); =20 - if (!doms_new) { + if (ndoms_new && !doms_new) { WARN_ON_ONCE(dattr_new); n =3D 0; doms_new =3D alloc_sched_domains(1); @@ -3414,7 +3413,7 @@ static void partition_sched_domains_locked(int ndoms_= new, cpumask_var_t doms_new } =20 n =3D ndoms_cur; - if (!doms_new) { + if (ndoms_new && !doms_new) { n =3D 0; doms_new =3D &fallback_doms; cpumask_and(doms_new[0], cpu_active_mask, --=20 2.43.0