sched/proxy:try_to_wake_up() 内联处理 return-migration — 代理调度唤醒路径去锁往返

代理调度(sched/proxy)· 唤醒路径去 proxy_force_return 锁往返 · 返回迁移前移 · Linux 7.2

💡 一句话总结

在启用代理调度(sched/proxy,一种让"阻塞在互斥锁上的任务"把 CPU 执行权让渡给锁持有者代为运行的调度机制)的内核上,一个被"代理迁移"到别的 CPU 的任务被唤醒时,需要返回迁移回它能运行的 CPU。补丁前这个动作发生在 __schedule() 深处的 proxy_force_return()——它要释放 rq 锁、重新拿 task_rq_lock(含 pi_lock)、重新校验、出队、选核、挂回目标 rq、再重新拿回 rq 锁,一轮锁往返外加一次强制重新选核。本补丁把这个返回迁移前移进 try_to_wake_up():唤醒路径发现任务被代理迁移过,就在try_to_wake_up() 已持有的 rq 锁下把它出队,随后的正常唤醒流程(select_task_rq() → ttwu_queue())自然把它放到一个能运行的 CPU 上——整段 proxy_force_return() 连同它的锁往返直接删除。补丁未提供基准数据,性能收益为逻辑分析(解读(AI 分析))。

📋 补丁基本信息

项目内容
补丁类型优化(performance)· 热路径减锁往返/减路径长度(hot-path)
状态Merged(绿)· 合入版本:Linux 7.2(v7.2-rc1 已含)
当前版本主线合入版(本 commit 即最终版)· git.kernel.org 链接
版本演进本 commit 为合入主线的最终版,无独立 v1/v2 演进。所属 sched/proxy(代理调度)系列由 John Stultz(Google)2026-05-12 在 LKML 发布,本补丁是系列第 6 补丁(Link 标签)。commit message 注明"本补丁从更大的 proxy 补丁中拆出并大幅重写",原始功绩归属 Peter Zijlstra / Juri Lelli / Valentin Schneider / Connor O'Brien 等多方。
讨论/演进信息来自本地内核 git 的 commit message(Link + Signed-off-by 链),lore 索引不可用已如实标注。
作者机构John Stultz(Google);Signed-off-by: Peter Zijlstra (Intel)
提交日期2026-05-12
改动范围include/linux/sched.h、kernel/sched/core.c,+97/-100 行,2 文件
核心函数try_to_wake_up() / ttwu_runnable() / proxy_needs_return()(新增) / find_proxy_task() / proxy_deactivate();删除 proxy_force_return()
原始链接commit f13beb010e4a(Link 标签:patch.msgid.link)

📊 速览卡片

核心机制
迁移前移
优化目标
去锁往返
适用场景
代理调度内核
特性等级
★★★
实测提升
未提供

特性等级依据:优化方向明确(唤醒热路径整段删除 proxy_force_return() 的锁往返 + 选核循环)、代码纯内联删除、无 ABI/配置变更、落地零风险;但收益仅在 CONFIG_SCHED_PROXY_EXEC 编译开启且代理迁移发生时才体现(非 proxy 内核中 proxy_needs_return() 编译期恒 false、几乎免费),且补丁未提供任何量化数据、受益场景窄——综合 4 原则评 ★★★(解读(AI 分析))。

🎯 解决什么问题

系列整体定位(本补丁所属 sched/proxy 代理调度系列)
sched/proxy(代理调度)系列整体解决的是调度器不感知"任务因互斥锁阻塞"的问题:传统上任务在互斥锁上阻塞,CPU 只能空转或换跑别的任务,锁持有者(往往是更高优先级的调度上下文)却可能被排在别处。代理调度让阻塞任务把自己的 CPU 执行权让渡(donate)给锁持有者,以阻塞任务的调度上下文代为运行,直到锁释放——这既避免 CPU 空转,也天然缓解优先级反转。系列由 Peter Zijlstra 长期开发、John Stultz(Google)主导上游化(与 ghOSt 的 mutex proxy execution 方向一致)。本补丁是系列中处理"代理迁移后的返回"的机制补丁:系列主体引入了 blocked_on 关联、find_proxy_task() 链式查找、proxy_migrate_task() 迁移、proxy_deactivate() 停用等机制,本补丁把其中最重的一段"返回迁移"处理(proxy_force_return())从 __schedule() 挪进 try_to_wake_up()。它依赖父提交 f0c1ecde6447(block_task() 可直调改造),也为后续优化(如 abc40cca0efd 移除唤醒路径防御性清理)提供了基础。
与本站同日已有分析的关系:sched/proxy: Optimize try_to_wake_up()(abc40cca0efd) 是同一系列的后续优化——本补丁(f13beb010e4a)先加入 return-migration 处理并顺带在唤醒路径加了 if (task_is_blocked(p)) clear_task_blocked_on(p, NULL) 防御;abc40cca0efd 则通过让 proxy_deactivate() 保证 cleared blocked_on,把这个防御从句再从公共唤醒路径删掉。两者同机制、同系列、不同方面,本补丁在前(更早合入)、abc40cca0efd 在后(收尾瘦身)。
背景 / 原始动机
commit message 把动机讲得很直白(原文):
"This patch adds logic so try_to_wake_up() will notice if we are waking a task where blocked_on == PROXY_WAKING, and if necessary dequeue the task so the wakeup will naturally return-migrate the donor task back to a cpu it can run on. This helps performance as we do the dequeue and wakeup under the locks normally taken in the try_to_wake_up() and avoids having to do proxy_force_return() from __schedule(), which has to re-take similar locks and then force a pick again loop."
即:try_to_wake_up() 唤醒一个 blocked_on == PROXY_WAKING 的任务时,应当在唤醒路径上就识别并出队它,让唤醒自然完成返回迁移;这样出队和唤醒都在 try_to_wake_up 已持有的锁下完成,避免了从 __schedule() 调用 proxy_force_return() 所必须的重取锁 + 强制重新选核循环。commit message 还注明本补丁从更大的 proxy 补丁中拆出并大幅重写。
系统层面:代理迁移后的任务被唤醒时,返回迁移走了一条锁往返 + 重选核的重路径
代理调度里有一个关键数据结构关系:p->wake_cpu(任务该在哪个 CPU 运行)与 task_cpu(p)(任务当前挂在哪)。正常情况下二者相等;但 proxy_set_task_cpu() 会保留原 wake_cpu、只改 task_cpu,把任务暂存到一个它可能无法真正运行的 rq 上(__set_task_cpu(p, cpu); p->wake_cpu = wake_cpu;,保留原值)。当这样的任务被唤醒时:
  • 补丁前:ttwu_runnable() 看到任务已在 rq 上,直接返回"已可运行"(返回 1),不移动它。等这个 rq 后续执行 __schedule() → pick_next_task() 选中它、发现 blocked_on == PROXY_WAKING 时,才在 find_proxy_task() 里调用 proxy_force_return():释放 rq 锁 → 重拿 task_rq_lock(rq 锁 + pi_lock)→ 重新校验任务还在同一 rq → deactivate → select_task_rq() 选核 → set_task_cpu() → attach 到目标 rq → 再重新拿回 rq 锁。这是"释放/重取锁 + 强制 pick again 循环"的完整往返。
  • 补丁后:ttwu_runnable() 先调用新增的 proxy_needs_return(),发现 task_cpu(p) != p->wake_cpu(被代理迁移过)就在已持有的 rq 锁下清 blocked_on、若任务是 donor 则 proxy_reset_donor() 换回 idle、然后 block_task(rq, p, TASK_WAKING) 出队,返回 0。于是 try_to_wake_up() 走正常唤醒流程:select_task_rq(p, p->wake_cpu) 选一个它能运行的 CPU,set_task_cpu() 迁移、ttwu_queue() 入队——返回迁移在 try_to_wake_up() 已持有的锁下一次性完成,proxy_force_return() 整段删除。
场景层面:代理调度激活、互斥锁跨核传递的负载
  • 构建前提:内核编译时打开 CONFIG_SCHED_PROXY_EXEC。只有这种构建里 proxy_needs_return() 才有实际逻辑(非 proxy 构建中它编译期恒 false)。
  • 负载场景:代理调度面向"任务频繁阻塞在互斥锁上、且锁持有者可能在别的 CPU"的用法——例如 Google 的 ghOSt 式用户态调度、实时/低延迟敏感负载、锁密集的并发计算。当锁持有者与等待者在不同 CPU 时,代理调度会把等待者(donor)的 CPU 让给持有者;持有者结束或释放锁、等待者被唤醒时,等待者需要从"暂存的 rq"回到它本来能运行的 CPU(wake_cpu)。
  • 触发条件:task_cpu(p) != p->wake_cpu 即发生代理迁移。跨核互斥锁传递越频繁、代理迁移越频繁,返回迁移发生得越多,锁往返路径被踩中的次数越多。
  • 为什么该场景遇到上面的系统缺陷:跨核锁传递场景下任务频繁被代理迁移、频繁被唤醒,而补丁前的返回迁移要经过"释放 rq 锁 → 重拿 task_rq_lock → 重校验 → deactivate → select_task_rq → attach → 重拿 rq 锁"的完整锁往返,外加一次强制重新选核;唤醒越频繁,这段额外开销越明显。
受影响负载:CONFIG_SCHED_PROXY_EXEC 构建上的互斥锁跨核传递/高唤醒率负载 · 因果:返回迁移被唤醒路径高频触发 → 补丁前每次走 proxy_force_return 锁往返+重选核 → 补丁后前移到 try_to_wake_up 已持锁下完成 → 每次返回迁移省一轮锁释放/重取 + 一次强制选核

🧩 核心机制

核心一句话:把"被代理迁移的任务返回它能运行的 CPU"这个动作,从 __schedule() 深处的 proxy_force_return()(要释放/重取 rq 锁 + pi_lock + 强制重选核)前移到 try_to_wake_up(),在唤醒路径已持有的锁下完成出队,让正常唤醒流程自然完成返回迁移。

从系统层面看
五处改动共同实现"返回迁移前移":
① 判定(机制核心):新增 proxy_needs_return()——检查 task_cpu(p) != p->wake_cpu。正常任务二者相等;代理迁移时 proxy_set_task_cpu() 保留 wake_cpu 原值只改 task_cpu,所以不等即"被代理迁移、需要返回"。命中后:拿 p->blocked_lock 清 blocked_on;若 p 就是 rq 的 donor(正在让渡执行权),调 proxy_reset_donor() 把它换回 idle(put_prev_set_next_task + rq_set_donor + resched_curr);最后 block_task(rq, p, TASK_WAKING) 出队并返回 true。
② 消费:ttwu_runnable() 改为 guard 式拿锁,在 task_on_rq_queued(p) 确认后调用 proxy_needs_return()——若返回 true(已出队),ttwu_runnable() 返回 0,让 try_to_wake_up() 进入正常唤醒路径(select_task_rq → set_task_cpu → ttwu_queue)。
③ 收尾:try_to_wake_up() 正常路径尾部加 else if (cpu != p->wake_cpu) p->wake_cpu = cpu;——如果 select_task_rq() 选了与 wake_cpu 不同的 CPU 返回(而不是回原 CPU),就把 wake_cpu 修正为实际选中的 CPU,避免留下指向旧位置的过期 wake_cpu。
④ 删除重路径:整段删除 proxy_force_return()(约 50 行)。find_proxy_task() 里原来 goto force_return 的两处改为 goto deactivate;deactivate: 标签处由"proxy_deactivate() 失败则 force_return"改为直接 proxy_deactivate(rq, p); return NULL。
⑤ 配套语义修正:proxy_deactivate() 从"返回 bool + try_to_block_task() 处理失败"改为"void + 直接调 block_task()(父提交 f0c1ecde6447 使 block_task() 可直调)",并用 WARN_ON_ONCE(state == TASK_RUNNING) 断言替代原来的提前返回;is_special_task_state() 加入 TASK_WAKING——因为现在 block_task(rq, p, TASK_WAKING) 会把 TASK_WAKING 当任务状态传入,须让它被识别为"特殊状态"(block_task() 里据此置 DEQUEUE_SPECIAL,豁免 DELAY_DEQUEUE 的延迟出队,避免特殊状态任务遭受伪唤醒延迟)。
为什么能支撑场景提升:跨核锁传递场景下返回迁移高频发生,补丁把每次返回迁移的"锁往返 + 强制重选核"降为"在已持有的锁下顺手出队、唤醒流程自然迁移"——省掉的不是一条路径,而是每次返回迁移都要付的固定开销。
返回迁移处理路径:补丁前(__schedule 侧 proxy_force_return)vs 补丁后(try_to_wake_up 内联)
图 1:返回迁移路径对比。上图=补丁前:唤醒后在 __schedule() 的 find_proxy_task() 里走 proxy_force_return(),释放/重取 rq 锁 + task_rq_lock + 强制重选核;下图=补丁后:try_to_wake_up() 的 ttwu_runnable() 用 proxy_needs_return() 在已持有的 rq 锁下出队,随后正常唤醒流程 select_task_rq → ttwu_queue 自然完成返回迁移。
来源:基于 git show f13beb010e4a 真实 diff 绘制
步骤操作目的
① 判定proxy_needs_return() 检查 task_cpu(p) != p->wake_cpu识别"被代理迁移、需要返回"的任务
② 出队在 rq 锁下 block_task(rq, p, TASK_WAKING)(必要时先 proxy_reset_donor())让 ttwu_runnable 返回 0,唤醒流程接管返回迁移
③ 唤醒select_task_rq(p, p->wake_cpu) → set_task_cpu() → ttwu_queue()在能运行的 CPU 上入队,自然完成返回迁移
④ 收尾else if (cpu != p->wake_cpu) p->wake_cpu = cpu;修正过期 wake_cpu
⑤ 删除删除 proxy_force_return();goto force_return → goto deactivate移除 __schedule 侧的锁往返 + 强制重选核路径
关键代码片段 ①:返回迁移判定与出队(机制核心)
@@ -3735,6 +3735,53 @@ void update_rq_avg_idle(struct rq *rq)
 	rq->idle_stamp = 0;
 }
 
+#ifdef CONFIG_SCHED_PROXY_EXEC
+static void zap_balance_callbacks(struct rq *rq);
+
+static inline void proxy_reset_donor(struct rq *rq)
+{
+	WARN_ON_ONCE(rq->donor == rq->curr);
+
+	put_prev_set_next_task(rq, rq->donor, rq->curr);
+	rq_set_donor(rq, rq->curr);
+	zap_balance_callbacks(rq);
+	resched_curr(rq);
+}
+
+/*
+ * Checks to see if task p has been proxy-migrated to another rq
+ * and needs to be returned. If so, we deactivate the task here
+ * so that it can be properly woken up on the p->wake_cpu
+ * (or whichever cpu select_task_rq() picks at the bottom of
+ * try_to_wake_up()
+ */
+static inline bool proxy_needs_return(struct rq *rq, struct task_struct *p)
+{
+	if (!task_is_blocked(p))
+		return false;
+
+	scoped_guard(raw_spinlock, &p->blocked_lock) {
+		/* Task is waking up; clear any blocked_on relationship */
+		__clear_task_blocked_on(p, NULL);
+
+		/* If already current, don't need to return migrate */
+		if (task_current(rq, p))
+			return false;
+
+		/* If we're return migrating the rq->donor, switch it out for idle */
+		if (task_current_donor(rq, p))
+			proxy_reset_donor(rq);
+	}
+	block_task(rq, p, TASK_WAKING);
+	return true;
+}
+#else /* !CONFIG_SCHED_PROXY_EXEC */
+static inline bool proxy_needs_return(struct rq *rq, struct task_struct *p)
+{
+	return false;
+}
+#endif /* CONFIG_SCHED_PROXY_EXEC */

▲ 这段是机制成立的关键:proxy_needs_return() 在 blocked_lock 保护下清掉 blocked_on,若任务恰好是 rq 的 donor(正在让渡执行权给锁持有者)则先用 proxy_reset_donor() 把它换回 idle,再 block_task(rq, p, TASK_WAKING) 出队。非 proxy 构建中该函数编译期恒 false——收益只在 CONFIG_SCHED_PROXY_EXEC 下有意义。

关键代码片段 ②:收益落点 —— 删除 proxy_force_return()、force_return 改走 deactivate
@@ -6747,71 +6811,6 @@ static void proxy_migrate_task(struct rq *rq, struct rq_flags *rf,
 	proxy_reacquire_rq_lock(rq, rf);
 }
 
-static void proxy_force_return(struct rq *rq, struct rq_flags *rf,
-			       struct task_struct *p)
-	__must_hold(__rq_lockp(rq))
-{
-	struct rq *task_rq, *target_rq = NULL;
-	int cpu, wake_flag = WF_TTWU;
-
-	lockdep_assert_rq_held(rq);
-	WARN_ON(p == rq->curr);
-
-	if (p == rq->donor)
-		proxy_resched_idle(rq);
-
-	proxy_release_rq_lock(rq, rf);
-	/*
-	 * We drop the rq lock, and re-grab task_rq_lock to get
-	 * the pi_lock (needed for select_task_rq) as well.
-	 */
-	scoped_guard (task_rq_lock, p) {
-		task_rq = scope.rq;
-
-		/*
-		 * Since we let go of the rq lock, the task may have been
-		 * woken or migrated to another rq before we  got the
-		 * task_rq_lock. So re-check we're on the same RQ. If
-		 * not, the task has already been migrated and that CPU
-		 * will handle any futher migrations.
-		 */
-		if (task_rq != rq)
-			break;
-
-		/*
-		 * Similarly, if we've been dequeued, someone else will
-		 * wake us
-		 */
-		if (!task_on_rq_queued(p))
-			break;
-
-		/*
-		 * Since we should only be calling here from __schedule()
-		 * -> find_proxy_task(), no one else should have
-		 * assigned current out from under us. But check and warn
-		 * if we see this, then bail.
-		 */
-		if (task_current(task_rq, p) || task_on_cpu(task_rq, p)) {
-			WARN_ONCE(1, "%s rq: %i current/on_cpu task %s %d  on_cpu: %i\n",
-				  __func__, cpu_of(task_rq),
-				  p->comm, p->pid, p->on_cpu);
-			break;
-		}
-
-		update_rq_clock(task_rq);
-		deactivate_task(task_rq, p, DEQUEUE_NOCLOCK);
-		cpu = select_task_rq(p, p->wake_cpu, &wake_flag);
-		set_task_cpu(p, cpu);
-		target_rq = cpu_rq(cpu);
-		clear_task_blocked_on(p, NULL);
-	}
-
-	if (target_rq)
-		attach_one_task(target_rq, p);
-
-	proxy_reacquire_rq_lock(rq, rf);
-}
-
 /*
  * Find runnable lock owner to proxy for mutex blocked donor

▲ 被删的 proxy_force_return() 正是 commit message 抱怨的"重取锁 + 强制 pick again 循环":它从 __schedule() 的 find_proxy_task() 里被调用,先 proxy_release_rq_lock() 释放 rq 锁,再 task_rq_lock 重拿(rq 锁 + pi_lock),校验任务还在同一 rq 后 deactivate、select_task_rq、set_task_cpu、attach 到目标 rq,最后 proxy_reacquire_rq_lock() 重新拿回 rq 锁。整段删除后,返回迁移由 try_to_wake_up() 的正常唤醒路径承担。

本补丁为中等改动(+97/-100 行,机制迁移 + 路径删除),核心机制是"返回迁移从 __schedule 侧移到 ttwu 侧"的流程变化,配图 1(before/after 流程对比)即可完整表达,机制部分不再额外加图(决策依据:单点路径迁移 + 两段关键 diff + 一张对比图已讲透)。

📈 性能影响

提升角度(方法论分类)
主角度:on-CPU 计算效率(热路径去锁往返 + 去一次强制选核)。改动删掉的是 proxy_force_return() 里"释放 rq 锁 → 重拿 task_rq_lock(含 pi_lock)→ 重新校验 → deactivate → select_task_rq → attach → 重拿 rq 锁"的整段路径,以及在 find_proxy_task() 触发它之后 __schedule() 还要"force a pick again loop"(再次走 pick_next_task())的额外一轮选核。这些都在唤醒/调度线程自己的 CPU 上执行,属于典型的 on-CPU 指令与锁指令开销。
次角度:off-CPU 锁等待(少拿一把 pi_lock + 少一次锁争用窗口)。原路径在释放 rq 锁后要重新获取 task_rq_lock(含 p->pi_lock),在锁被释放到重新获取之间的窗口里其他 CPU 可能争用这些锁;补丁后整个返回迁移都在 try_to_wake_up 已持有的 rq 锁下完成,不再额外获取/释放一把 pi_lock。Brendan Gregg 视角:优化的是唤醒路径中 on-CPU 的锁指令段,以及锁往返释放/重取窗口对应的 off-CPU 等待。
受益场景
  • 启用代理调度的构建:CONFIG_SCHED_PROXY_EXEC=y。非 proxy 构建中 proxy_needs_return() 编译期 false,本补丁为 no-op。
  • 互斥锁跨核传递、代理迁移频繁的负载:锁持有者与等待者在不同 CPU、等待者被代理迁移到别的 rq 后又被唤醒的场景——每次这样的唤醒都省一轮锁往返 + 一次强制重选核。代理调度面向的用法(ghOSt 式用户态调度、实时/锁密集并发负载)正是此类。
  • 返回迁移频率越高收益越大:迁移+唤醒次数与收益成正比;低频场景几乎无感。
场景/用例运行环境改进前改进后
代理迁移任务被唤醒CONFIG_SCHED_PROXY_EXEC=y,跨核锁传递唤醒后在 __schedule 走 proxy_force_return:释放/重取 rq 锁 + task_rq_lock + 强制重选核try_to_wake_up 已持锁下出队,正常唤醒流程返回迁移(无额外锁往返)
非 proxy 构建CONFIG_SCHED_PROXY_EXEC=nproxy_needs_return() 编译期 falseno-op(无行为变化)

说明:补丁未提供基准数据——commit message 无任何 benchmark/数字,仅文字说明"helps performance"(避免 proxy_force_return 的重取锁 + 强制重选核)。上表为"改了什么、省了什么"的定性对比,属逻辑分析(解读(AI 分析)),无量化提升百分比。

逻辑收益分析(解读(AI 分析))
从"改动路径 → 高频场景 → 成本项"反推:
① 每次返回迁移省一轮锁释放/重取往返:proxy_force_return() 释放 rq 锁后要重新拿 task_rq_lock(rq 锁 + pi_lock),期间还有重新校验、select_task_rq、attach;补丁后这些都在 try_to_wake_up 已持有的锁下完成,省去锁的释放/重取以及 pi_lock 的一次额外获取(解读(AI 分析))。
② 省一次强制重新选核(pick again loop):原路径触发 proxy_force_return() 后 __schedule() 会回到 pick_again 再次执行 pick_next_task();补丁后返回迁移随正常唤醒完成,不再有这轮额外选核(解读(AI 分析))。
③ 对非 proxy 内核为 no-op:proxy_needs_return() 在非 proxy 构建编译期恒 false,调用为内联折叠的死分支,无行为变化(事实,非推测)。

🔄 方案演进

本补丁是合入主线的最终版,无独立 v1/v2 演进。它属于 sched/proxy(代理调度)上游化系列,演进信息基于本地内核 git 的 commit message(lore 索引不可用,讨论来源为本地 git,已如实标注)。

系列脉络:代理调度上游化 → 返回迁移前移 → 唤醒路径瘦身
代理调度是什么:当任务阻塞在互斥锁上时,不再让 CPU 空转,而是把执行权"代理"给锁持有者,让持有者以阻塞任务的调度上下文运行,直到锁可用。这是 Peter Zijlstra 长期开发的功能(与 Google ghOSt 的 mutex proxy execution 方向一致),在 Linux 7.x 周期推进上游化。
父提交链:本补丁依赖 f0c1ecde6447(sched: Rework block_task so it can be directly called,使 block_task() 可被直接调用、不必处理 try_to_block_task() 的失败分支)——这是本补丁能用 block_task(rq, p, TASK_WAKING) 直调出队的前提。
2026-05-12:John Stultz(Google)发布代理调度系列(本补丁为系列第 6 补丁,Link 标签)。commit message 注明"本补丁从更大的 proxy 补丁中拆出并大幅重写",原始功绩归属 Peter Zijlstra、Juri Lelli、Valentin Schneider、Connor O'Brien。
后续优化(2026-05-26):系列后续的 abc40cca0efd(sched/proxy: Optimize try_to_wake_up(),本站另有独立分析)把本补丁在唤醒路径加的防御从句(if (task_is_blocked(p)) clear_task_blocked_on(p, NULL))再删掉——通过让 proxy_deactivate() 保证 cleared blocked_on,把该防御从公共唤醒路径移除。
合入:v7.2-rc1 已含本 commit(git describe --contains 确认为 Linux 7.2)。
设计权衡
本补丁的取舍是把返回迁移从"调度侧的重路径"挪到"唤醒侧的热路径":表面看是把工作加到了 try_to_wake_up()(内核最热路径之一)上,但实际是删掉了更重的路径——proxy_force_return() 那轮锁往返 + 强制重选核,换成唤醒路径上一个 proxy_needs_return() 的快速判定(task_cpu(p) != p->wake_cpu,非代理迁移时几乎免费),命中时出队+唤醒在已持锁下完成。等价地,把"调度决策时发现任务不在能跑的核上再补救"换成"唤醒时就把它放到能跑的核上"。lore 不可用,无法回溯邮件列表上是否有更早版本或 review 讨论(解读(AI 分析):该权衡依据 commit message 与代码结构推断)。

注:本 commit 自身为合入主线的最终版,无独立 v1/v2 版本史;以上为"系列整体"的合入节奏与本补丁在系列中的位置。

⚠️ 风险与局限

收益成立的前提
  • 编译前提:内核须 CONFIG_SCHED_PROXY_EXEC=y。非 proxy 构建中 proxy_needs_return() 编译期恒 false,改动为 no-op(仅 is_special_task_state() 增加 TASK_WAKING 这一行对全内核生效,但 TASK_WAKING 只在唤醒路径短暂存在、此前本就不属于正常 wait-loop 状态,语义一致)。
  • 负载前提:收益与"代理迁移 + 唤醒"频率成正比——跨核互斥锁传递频繁、代理迁移频繁的负载收益明显;低频场景几乎无感。
生产落地影响
  • 无新配置/无 ABI 变化:纯代码迁移 + 路径删除,升级无需任何操作。
  • 唤醒路径多一次快速判定:ttwu_runnable() 现在每次都调用 proxy_needs_return()(先 task_is_blocked() 快判),对非代理迁移任务仅一次静态分支 + 字段读取;命中才走出队。这是对最热路径增加的一点成本,换来的是删除更重的锁往返路径——净收益(解读(AI 分析))。
  • 行为一致性:proxy_deactivate() 由"bool + try_to_block_task 失败降级"改为"void + WARN_ON(state==TASK_RUNNING)"——若未来出现 state==TASK_RUNNING 仍调 proxy_deactivate 的路径,会爆 WARN(开发期护栏);try_to_block_task() 里也把 set_task_blocked_on_waking(p, NULL) 改为 clear_task_blocked_on(p, NULL)(同一语义的更清晰表达)。
生态/兼容性
  • 依赖系列前置:本补丁依赖代理调度主体机制(blocked_on、find_proxy_task()、proxy_migrate_task())与父提交 f0c1ecde6447(block_task() 可直调)已合入,不能独立回移植到无代理调度的老内核。
  • 对用户态透明:无系统调用/API/sysctl 变更,工具链与用户态不受影响。
  • 架构无关:改动不涉及任何硬件特性,全架构适用。
review 质疑(若有)
lore 不可用:本次 lore MCP 查询全部超时,无法回溯邮件列表的具体 review 讨论(每条观点带 message-id 的要求无法满足)。可用的社区参与证据来自 commit message 的签名链:Signed-off-by: John Stultz (Google) + Signed-off-by: Peter Zijlstra (Intel);commit message 明确原始功绩归属 Peter Zijlstra / Juri Lelli / Valentin Schneider / Connor O'Brien(代理调度多方长期合作),并注明本补丁"从更大的 proxy 补丁中拆出并大幅重写"。未发现可呈现的公开 review 质疑(解读(AI 分析))。
严重度:MINOR(落地风险极低——纯代码迁移 + 路径删除,行为不变;唯一观察点是 ttwu_runnable() 热路径增加一次 proxy_needs_return() 快速判定,对非代理迁移任务近乎免费;is_special_task_state() 增加 TASK_WAKING 对全内核生效但语义一致)· 落地场景:最需要关注的是收益仅限 proxy 构建,非 proxy 内核升级无感

🔗 交叉引用

📌 关联工作 / 系列其他补丁
f0c1ecde6447 sched: Rework block_task so it can be directly called — 本补丁的父提交,同一系列第 5 补丁:把 try_to_block_task() 主要逻辑并入 block_task(),使本补丁能直调 block_task(rq, p, TASK_WAKING) 出队(本补丁依赖的前置)
jstultz@google.com 代理调度系列第 6 补丁(2026-05-12) — 本补丁在邮件列表的入口(本 commit 的 Link 标签)
[日报] sched/proxy: Optimize try_to_wake_up()(abc40cca0efd) — 本站同日独立分析:同一系列的后续优化,移除本补丁加入的唤醒路径防御从句,与本补丁同机制互补
abc40cca0efd sched/proxy: Optimize try_to_wake_up() — 同一系列的后续优化 commit(git.kernel.org 规范链接)
f13beb010e4a 本补丁本体 — 本补丁(git.kernel.org 规范链接)
⚠️ 免责声明

本站内容均由 AI 基于公开知识辅助生成,仅供学习参考,请勿直接引用作为依据。作者不对信息的准确性、完整性及适用性作保证,亦不对因使用本站内容产生的任何损失承担责任。