From nobody Sat Jul 25 21:59:18 2026 Received: from sender4-pp-o92.zoho.com (sender4-pp-o92.zoho.com [136.143.188.92]) (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 E42C8361640 for ; Mon, 13 Jul 2026 06:22:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.92 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783923775; cv=pass; b=e/QEyQlJ5OWH3bVQy/lfb1Q06HqyYaG6QEH+qeMp0U1dkh2yEEX7c1sB3S3/ltoEKhHt23pVdHpioaUkVDk2/E0jll7jCY/2K+C/GsJT6yygmrcXCIewMEUtco03A/F0Zsv5zEIoM7Ekmp5M/oqwFPkXTk2F5FEoHY6uuO4gGi0= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783923775; c=relaxed/simple; bh=ScwDEnelQmwYnhUJv9ukAZUXHa0rXOm58k7vuurJD5Q=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=s9jXMWtv89T18nhENhhAjbvUR+e570WjTGpZw1RJ7izGEszsfH2pDQIsVAVMG5q6OLX2ySwWcETYa4qdMxVoxzDpHfn4QEsTqnfigzNeSY+8JgAalNyRVeXHA77Fsm27zPuouCNqg/zjiERmV057eaY58rNT/HxSJQCk3Xo2obY= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=zohomail.com; spf=pass smtp.mailfrom=zohomail.com; dkim=pass (1024-bit key) header.d=zohomail.com header.i=kingxukai@zohomail.com header.b=PvM/cjIM; arc=pass smtp.client-ip=136.143.188.92 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=zohomail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=zohomail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=zohomail.com header.i=kingxukai@zohomail.com header.b="PvM/cjIM" ARC-Seal: i=1; a=rsa-sha256; t=1783923734; cv=none; d=zohomail.com; s=zohoarc; b=bxOI6A7+OJptOSx95iA6LaO09eVssssqd2rE8Uz8IiuSPLBYTrssDHZCmcwdr6KXMf/O3YUa32GbVb9O8AhcBwXdPRSaMdXX/jdXuoT6FajOjkpenLUg4tNOwBgLGkyHh1fSOvqCZ8WXXR0e/WbdmgDpc5Gs8Uxy5oapPqvN0XM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783923734; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=I1YsAKanHBVymLpPPHC8Y3H30N/H2k9wL9iKDUg7i+8=; b=GaJ5TH1YVb2daix55w2lc6tXEJihvxF25wzpMMs0Bu1Z5uvMd9aFL8rFZZleiBy790PK+n6ct+CkP4pODnHxqXyq920wBMZVljdLAeaomwzZWU+HcoZDRBGneQSNrVlZxKzuZdI9dU+Ksn++10BeolZMrNH44fSx9zeZ0ClGUwU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=zohomail.com; spf=pass smtp.mailfrom=kingxukai@zohomail.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1783923734; s=zm2022; d=zohomail.com; i=kingxukai@zohomail.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Feedback-ID:Reply-To; bh=I1YsAKanHBVymLpPPHC8Y3H30N/H2k9wL9iKDUg7i+8=; b=PvM/cjIMWPPbwJRE15Nb3ZvLAR1gN2lG0N7dMQtGaEN+p5ZvgMROPR4HrKY1JP3L LnwTHmLOUO4j0N+CJkIldFCaB0wFAloUZ0ZQ2E5vfCfqDQl6xzZJE/IqlzToVQQP2E5 QjUfirhbUi3Bb1Tc1iNPyND5Yh2Je3yz7FKpb4IQ= Received: by mx.zohomail.com with SMTPS id 1783923732092283.01103888106854; Sun, 12 Jul 2026 23:22:12 -0700 (PDT) From: Xukai Wang Date: Mon, 13 Jul 2026 14:21:34 +0800 Subject: [PATCH RFC v2] sched/proxy: Defer donor commit until after proxy resolution Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260713-sched-proxy-v2-1-729170082633@zohomail.com> References: <20260713-sched-proxy-v2-0-729170082633@zohomail.com> In-Reply-To: <20260713-sched-proxy-v2-0-729170082633@zohomail.com> To: Ingo Molnar , John Stultz , Peter Zijlstra , Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , K Prateek Nayak Cc: linux-kernel@vger.kernel.org, Xukai Wang X-Mailer: b4 0.15.2 Feedback-ID: zu08011227ab083ca142d12e7c202ddbdf0000c289c75246b22cf5559d655caec893e8ef3dda1bca6182d11d:ZohoMail X-Zoho-CM-AccountID: 2ee5dd3c83366259b2ba1e9826250ffebed1ef2dd213857d649ad25aba73b429 X-ZohoMailClient: External pick_next_task() currently commits the selected task into the scheduling class state before proxy execution has resolved whether that task should really become the committed donor. With proxy execution enabled, the task returned by pick_next_task() may be blocked. __schedule() then calls find_proxy_task() to resolve the proxy chain. If find_proxy_task() returns NULL, __schedule() retries through pick_again, but the picked donor has already gone through put_prev_set_next_task(). In that case the class current state was speculatively switched to a donor which is not the final scheduling decision. Make pick_next_task() return only the selected candidate task. Move the put_prev_set_next_task() call into __schedule(), next to rq_set_donor(), so the scheduling class commit happens after proxy-chain resolution. For proxy execution, keep the picked donor separate from the actual task to run: donor =3D task selected by pick_next_task() next =3D task returned by find_proxy_task(), possibly the proxy owner Only commit the donor after proxy-chain resolution succeeds: put_prev_set_next_task(rq, rq->donor, donor); rq_set_donor(rq, donor); If find_proxy_task() returns NULL, the candidate donor has not been committed and __schedule() can retry without first undoing a speculative set_next() on that donor. A proxy candidate is not necessarily the committed rq->donor anymore. Update proxy_deactivate() accordingly. If the task being blocked is still the committed donor, proxy_resched_idle() is needed to drop rq/class current references before block_task() clears ->on_rq. If it is only an uncommitted proxy candidate, it is still queued and can be blocked directly. proxy_migrate_task() still switches the rq to idle before dropping the rq lock. Migrating a task found in the proxy chain abandons the current proxy pick attempt, and switching the committed donor to idle leaves rq->donor and the class current state in a defined state while the chain is modified and the task is attached elsewhere. Move zap_balance_callbacks() into proxy_resched_idle(), next to the idle commit. The outer NULL/idle proxy-resolution paths no longer zap unconditionally: if no put/set was done, no callbacks should have been generated there; if the path went through proxy_resched_idle(), callbacks are cleared at the point that performed the idle commit. Signed-off-by: Xukai Wang --- kernel/sched/core.c | 90 +++++++++++++++++++++++++++++--------------------= ---- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 2e7cde033a31..f09f3f2bea13 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6146,7 +6146,6 @@ __pick_next_task(struct rq *rq, struct rq_flags *rf) if (!p) p =3D pick_task_idle(rq, rf); =20 - put_prev_set_next_task(rq, rq->donor, p); return p; } =20 @@ -6157,10 +6156,8 @@ __pick_next_task(struct rq *rq, struct rq_flags *rf) p =3D class->pick_task(rq, rf); if (unlikely(p =3D=3D RETRY_TASK)) goto restart; - if (p) { - put_prev_set_next_task(rq, rq->donor, p); + if (p) return p; - } } =20 BUG(); /* The idle class should always have a runnable task. */ @@ -6257,7 +6254,7 @@ pick_next_task(struct rq *rq, struct rq_flags *rf) rq->dl_server =3D rq->core_dl_server; rq->core_pick =3D NULL; rq->core_dl_server =3D NULL; - goto out_set_next; + goto out_return_next; } =20 prev_balance(rq, rf); @@ -6311,7 +6308,7 @@ pick_next_task(struct rq *rq, struct rq_flags *rf) */ WARN_ON_ONCE(fi_before); task_vruntime_update(rq, next, false); - goto out_set_next; + goto out_return_next; } } =20 @@ -6441,8 +6438,7 @@ pick_next_task(struct rq *rq, struct rq_flags *rf) resched_curr(rq_i); } =20 -out_set_next: - put_prev_set_next_task(rq, rq->donor, next); +out_return_next: if (rq->core->core_forceidle_count && next =3D=3D rq->idle) queue_core_balance(rq); =20 @@ -6745,6 +6741,14 @@ static inline struct task_struct *proxy_resched_idle= (struct rq *rq) rq->next_class =3D &idle_sched_class; rq_set_donor(rq, rq->idle); set_tsk_need_resched(rq->idle); + + /* + * This helper performs a real idle commit. Some callers return to + * __schedule() without dropping rq->lock, so clear callbacks + * generated by the idle commit here. Paths that later drop + * rq->lock may zap again. + */ + zap_balance_callbacks(rq); return rq->idle; } =20 @@ -6755,15 +6759,23 @@ static void proxy_deactivate(struct rq *rq, struct = task_struct *donor) WARN_ON_ONCE(state =3D=3D TASK_RUNNING); WARN_ON_ONCE(donor->blocked_on); /* - * Because we got donor from pick_next_task(), it is *crucial* - * that we call proxy_resched_idle() before we deactivate it. - * As once we deactivate donor, donor->on_rq is set to zero, - * which allows ttwu() to immediately try to wake the task on - * another rq. So we cannot use *any* references to donor - * after that point. So things like cfs_rq->curr or rq->donor - * need to be changed from next *before* we deactivate. + * A proxy candidate is not necessarily the committed rq->donor. + * pick_next_task() only selected it; the class current state is + * updated later, after proxy-chain resolution. + * + * If @donor is still the committed donor, the rq and the scheduling + * class may hold current references to it, such as rq->donor or + * cfs_rq->curr/h_curr. Drop those references before block_task(), + * because block_task() clears donor->on_rq and a concurrent wakeup + * may then move the task elsewhere. + * + * If @donor is only an uncommitted proxy candidate, it is still a + * queued task, not the class current task, so it can be blocked + * directly. */ - proxy_resched_idle(rq); + if (donor =3D=3D rq->donor) + proxy_resched_idle(rq); + block_task(rq, donor, state); } =20 @@ -6816,15 +6828,17 @@ static void proxy_migrate_task(struct rq *rq, struc= t rq_flags *rf, lockdep_assert_rq_held(rq); WARN_ON(p =3D=3D rq->curr); /* - * Since we are migrating a blocked donor, it could be rq->donor, - * and we want to make sure there aren't any references from this - * rq to it before we drop the lock. This avoids another cpu - * jumping in and grabbing the rq lock and referencing rq->donor - * or cfs_rq->curr, etc after we have migrated it to another cpu, - * and before we pick_again in __schedule. + * Migrating a task found in the proxy chain abandons the current proxy + * pick attempt and drops this rq's lock. * - * So call proxy_resched_idle() to drop the rq->donor references - * before we release the lock. + * The picked proxy candidate has not necessarily been committed, so + * @p is not necessarily rq->donor. Switch the currently committed + * donor to idle before dropping the lock, leaving rq->donor and the + * class current state in a well-defined state while the chain is + * modified and @p is attached elsewhere. + * + * If @p is rq->donor, this also drops the direct rq/class-current + * references to @p before it leaves this rq. */ proxy_resched_idle(rq); =20 @@ -7057,7 +7071,7 @@ find_proxy_task(struct rq *rq, struct task_struct *do= nor, struct rq_flags *rf) */ static void __sched notrace __schedule(int sched_mode) { - struct task_struct *prev, *next; + struct task_struct *prev, *next, *donor; /* * On PREEMPT_RT kernel, SM_RTLOCK_WAIT is noted * as a preemption by schedule_debug() and RCU. @@ -7145,24 +7159,18 @@ static void __sched notrace __schedule(int sched_mo= de) assert_balance_callbacks_empty(rq); next =3D pick_next_task(rq, &rf); rq->next_class =3D next->sched_class; + donor =3D next; if (sched_proxy_exec()) { - struct task_struct *prev_donor =3D rq->donor; - - rq_set_donor(rq, next); - next->blocked_donor =3D NULL; - if (unlikely(next->is_blocked)) { - next =3D find_proxy_task(rq, next, &rf); - if (!next) { - zap_balance_callbacks(rq); + donor->blocked_donor =3D NULL; + if (unlikely(donor->is_blocked)) { + next =3D find_proxy_task(rq, donor, &rf); + if (!next) goto pick_again; - } - if (next =3D=3D rq->idle) { - zap_balance_callbacks(rq); + if (next =3D=3D rq->idle) goto keep_resched; - } } - if (rq->donor =3D=3D prev_donor && prev !=3D next) { - struct task_struct *donor =3D rq->donor; + + if (donor =3D=3D rq->donor && prev !=3D next) { /* * When transitioning like: * @@ -7178,9 +7186,9 @@ static void __sched notrace __schedule(int sched_mode) donor->sched_class->put_prev_task(rq, donor, donor); donor->sched_class->set_next_task(rq, donor, true); } - } else { - rq_set_donor(rq, next); } + put_prev_set_next_task(rq, rq->donor, donor); + rq_set_donor(rq, donor); =20 picked: clear_tsk_need_resched(prev); --=20 2.34.1