sched discussion
[PATCH] sched/core: Preserve wake flags across queued wakeups
LLM 分析
Scheduler 核心:跨排队唤醒保留 wake flags
系列概况
- 标题: [PATCH] sched/core: Preserve wake flags across queued wakeups
- 作者: Shubhang Kaushik (Ampere) sh@gentwo.org
- 版本: v1(单补丁)
- 规模: 2 个文件,+10/-5 行
- 修改文件:
include/linux/sched.h、kernel/sched/core.c - 代码统计: 10 insertions(+), 5 deletions(-)
- Message-ID: 20260710-b4-sched-ttwu-wake-flags-v1-1-23182a4c5367@gentwo.org
- 完整性: 完整;包含 1 封 PATCH 主帖 + 2 封回复;base-commit
d96fcfe1b7f94ac742984ae7986b94a116abff1b
补丁目的
修复 queued wakeup 路径丢失激活相关 wake flags 的问题。当前 __ttwu_queue_wakelist() 只能记下唤醒方是否迁移,把 WF_MIGRATED 以外的所有位(WF_SYNC、WF_TTWU、WF_RQ_SELECTED)都丢弃;当目标 CPU 通过 sched_ttwu_pending() 排空 wakelist 时,ttwu_do_activate() 收到的标志集与直接唤醒路径不一致,唤醒抢占决策可能产生分歧。
旧流程的问题
直接唤醒路径里,调用者把完整 wake_flags 一路传到 ttwu_do_activate(),下游(如 ttwu_do_wakeup() → check_preempt_curr())能完整看到所有位。但走排队路径时:
p->sched_remote_wakeup只占 1 bit,只能写入布尔结果- 真正影响激活/抢占的
WF_SYNC、WF_TTWU、WF_RQ_SELECTED全部丢失 - 排空 wakelist 时
sched_ttwu_pending()只能重建出WF_MIGRATED或 0 - 同一任务可能因走排队或直接路径而在抢占决策上得到不同结果
新流程
task_struct把字段从sched_remote_wakeup:1改成sched_remote_wakeup_flags:8- 排队侧写入掩码:
WF_TTWU | WF_SYNC | WF_MIGRATED | WF_RQ_SELECTED - 排空侧读出 → 清零 → 透传给
ttwu_do_activate() WF_CURRENT_CPU显式排除(属于 CPU选位提示,而非激活标志)
Patch 概览
/* include/linux/sched.h */
- unsigned sched_remote_wakeup:1;
+ unsigned sched_remote_wakeup_flags:8;
/* kernel/sched/core.c :: sched_ttwu_pending() */
+ int wake_flags;
+ wake_flags = p->sched_remote_wakeup_flags;
+ p->sched_remote_wakeup_flags = 0;
- ttwu_do_activate(rq, p, p->sched_remote_wakeup ? WF_MIGRATED : 0, &rf);
+ ttwu_do_activate(rq, p, wake_flags, &rf);
/* kernel/sched/core.c :: __ttwu_queue_wakelist() */
- p->sched_remote_wakeup = !!(wake_flags & WF_MIGRATED);
+ p->sched_remote_wakeup_flags = wake_flags &
+ (WF_TTWU | WF_SYNC | WF_MIGRATED | WF_RQ_SELECTED);
关键实现
1.字段重定义
sched_remote_wakeup:1 改为 sched_remote_wakeup_flags:8。位宽扩到 8 bit 足够覆盖当前所有 WF_* 位,注释里同步说明新用途。
2. 排队侧:保存关键 wake flags
p->sched_remote_wakeup_flags = wake_flags &
(WF_TTWU | WF_SYNC | WF_MIGRATED | WF_RQ_SELECTED);
用位与白名单而不是简单布尔。WF_CURRENT_CPU 主动被排除,因为它影响的是 CPU 选择,不会影响激活阶段的抢占判断。
3. 排空侧:读出即清零
wake_flags = p->sched_remote_wakeup_flags;
p->sched_remote_wakeup_flags = 0;
ttwu_do_activate(rq, p, wake_flags, &rf);
清零放在读出后,避免下次入队/排空之间产生标志残留。
类比
把 wakeup 比作快递派送:
- 直接派送:快递员亲手把包裹连同"易碎/签收/加急/转运"等所有标签一起交到收件人手中。
- 旧队列派送:快递员只把包裹丢进中转站桶,在包裹上贴一张"曾经转运过"的便签,另一名快递员来取件时只能看到那一张便签,所有特殊处理要求全丢。
- 新队列派送:快递员把激活相关的所有标签都贴在包裹上,中转站取件员照原样读出并送达。
wake_flags old: WF_MIGRATED bool -> [1 bit]
new: MASK_4_BITS -> [8 bit]
storage old: p->sched_remote_wakeup:1
new: p->sched_remote_wakeup_flags:8
drain old: WF_MIGRATED or 0 -> ttwu_do_activate()
new: saved flags (post-clear) -> ttwu_do_activate()
waker
|
| direct wakeup: full wake_flags
v ttwu_do_activate() ----> preemption / placement ^
|
waker --WF_MIGRATED bool--> ttwu_queue_wakelist() --llist-->
|
v target CPU
sched_ttwu_pending()
read p->sched_remote_wakeup_flags
clear field
pass to ttwu_do_activate()
Highlight:风险与注意点
- 位域原子性:1 bit 写入在所有架构上原子,但 8 bit 位域写入依赖编译器布局与目标 ABI,存在被拆成两次字节写入的可能。回复里作者已提到"按字节大小的写入通常原子",reviewer 进一步建议改成显式
u8字段、紧跟personality放置,避免与rq_lock串行化字段共享 cache line 时出现踩踏。 - 白名单维护:当前只覆盖 4 个位,将来
wake_flags新增位时要同步更新这里的掩码,否则新位会再次悄悄丢失。 - 重复入队竞争:清零紧贴读出之后执行,需要确认在
ttwu_queue_wakelist()那一侧的写入同样处在p->pi_lock/rq->lock的串行化窗口内,否则理论存在窗口让"清零 → 再入队"的两次写入顺序错乱。 - 真实收益的工作负载:作者说
perf bench sched messaging与hackbench上结果在运行间噪声范围内;收益更可能出现在强WF_SYNC或激进 wakeup-preemption 的负载上,需要更针对性的测试。 - 与
WF_CURRENT_CPU的边界:必须明确这是选位 hint 而非激活 flag,提交说明要持续维护这个区分,否则后续 contributor 容易把它也加进白名单。
版本变化
仅有 v1。后续两封回复(K Prateek Nayak 回引 diff;作者引用 Peter Zijlstra 在 2020 年的相关讨论 20201116142005.GE3121392@hirez.programming.kicks-ass.net 并提议改 u8)属 review/cleanup 提议,尚未形成 v2。
一句话总结
把 sched_remote_wakeup:1 扩展成 sched_remote_wakeup_flags:8,让排队唤醒路径透传 WF_TTWU/WF_SYNC/WF_MIGRATED/WF_RQ_SELECTED,使远端排空时的 ttwu_do_activate() 与直接唤醒行为一致;review 阶段建议改为显式 u8 字段以避免位域原子性歧义。