sched discussion
[PATCH v3] sched/core: Preserve wake flags across queued wakeups
LLM 分析
sched/core:跨排队 wakeup 保留 wake flags
系列概况
- 标题: [PATCH v3] sched/core: Preserve wake flags across queued wakeups
- 作者: Shubhang Kaushik (Ampere) sh@gentwo.org
- 版本: v3(前置 v2、v1)
- 规模: 3 封邮件(本 thread)
- 修改文件:
include/linux/sched.h、kernel/sched/core.c - 代码统计: +18 / -18(2 文件)
- Message-ID:
20260721-b4-sched-ttwu-wake-flags-v3-1-07b92122bf28@gentwo.org - 完整性: 完整 changelog、v1/v2/v3 链接、Sign-off、base-commit
v7.2-rc4 (248951ddc14d)、change-id 齐全;测试覆盖 perf bench / hackbench / SPECjBB
补丁目的
try_to_wake_up() 在跨 CPU 时把 wakeup 排到目标 CPU 的 llist(ttwu_queue_wakelist()),但目前只保存 "wakee 是否迁移" 这一个 bit。目标 CPU 排空 wakelist 调用 sched_ttwu_pending() 时只能传 WF_MIGRATED 或 0,丢掉 WF_RQ_SELECTED 等在 select_task_rq() 之后仍然重要的 flag。这会让 queued wakeup 路径在 ENQUEUE_RQ_SELECTED 和 wakeup_preempt_fair() 抢占阈值计算上与 direct wakeup 行为不一致。
本 patch 把这些 flag 完整保存下来,让两条路径对齐。
旧流程的问题
direct : select_task_rq() -> WF_RQ_SELECTED
ttwu_do_activate(WF_RQ_SELECTED)
wakeup_preempt_fair(WF_RQ_SELECTED)
queued : select_task_rq() -> WF_RQ_SELECTED
ttwu_queue_wakelist() -> save only WF_MIGRATED (1 bit)
sched_ttwu_pending() -> ttwu_do_activate(WF_MIGRATED or 0)
[LOST] WF_RQ_SELECTED and others
target CPU 看到的 wake flags 不完整,ENQUEUE 路径和抢占判断走错分支。
新流程
select_task_rq() -> set WF_RQ_SELECTED
ttwu_queue_wakelist() -> WRITE_ONCE(p->sched_remote_wakeup_flags,
wake_flags & WF_TTWU_QUEUE_MASK)
sched_ttwu_pending() -> wake_flags = READ_ONCE(p->sched_remote_wakeup_flags)
ttwu_do_activate(rq, p, wake_flags, &rf)
WF_TTWU_QUEUE_MASK = WF_TTWU | WF_SYNC | WF_MIGRATED | WF_RQ_SELECTED;WF_CURRENT_CPU 只是 CPU 选择提示,跨队列无意义,刻意丢弃。
Patch 概览
include/linux/sched.h:删除sched_remote_wakeup:1,新增独立u8 sched_remote_wakeup_flags+ cross-CPU ordering 注释kernel/sched/core.c:ttwu_queue_wakelist()用WF_TTWU_QUEUE_MASK写入新字段;sched_ttwu_pending()用READ_ONCE读出再传入ttwu_do_activate()
关键实现
/* include/linux/sched.h */
struct task_struct {
...
/*
* Must not share the scheduler bitfield word because wakelist
* queueing is not serialized by p->on_cpu.
*
* smp_load_acquire(&p->on_cpu) in ttwu_queue_wakelist() pairs
* with smp_mb__after_spinlock() in schedule(), making current's
* stores visible before the target CPU uses these flags from
* sched_ttwu_pending().
*/
u8 sched_remote_wakeup_flags;
};
/* kernel/sched/core.c */
#define WF_TTWU_QUEUE_MASK (WF_TTWU | WF_SYNC | WF_MIGRATED | \
WF_RQ_SELECTED)
void sched_ttwu_pending(void *arg)
{
...
llist_for_each_entry_safe(p, t, llist, wake_entry.llist) {
int wake_flags;
...
wake_flags = READ_ONCE(p->sched_remote_wakeup_flags);
ttwu_do_activate(rq, p, wake_flags, &rf);
}
}
static void __ttwu_queue_wakelist(struct task_struct *p, int cpu,
int wake_flags)
{
struct rq *rq = cpu_rq(cpu);
WRITE_ONCE(rq->ttwu_pending, 1);
WRITE_ONCE(p->sched_remote_wakeup_flags,
wake_flags & WF_TTWU_QUEUE_MASK);
...
}
字段从 scheduler word 拆出的根本原因是 wakelist 队列不再由 p->on_cpu 串行化,必须用独立字段 + READ_ONCE / WRITE_ONCE 保证跨 CPU 可见性。
类比
把 ttwu_queue_wakelist() 想象成快递公司把包裹扔进目的站点的"待上架寄存柜":旧版柜子上只贴了一张"是否跨城转运"标签,目的站点柜员(sched_ttwu_pending)开柜后只知道转运状态,不知道"客户已选好货架号"(WF_RQ_SELECTED)和"加急件"(WF_SYNC),只能盲投默认货架。补丁相当于把 1 bit 标签换成完整托运单,柜员看到全部指令。
另一类比是餐厅排队:旧版只告诉取餐员"外卖改堂食(迁移)",新版把"加急、已选座、换桌"等全部写在取餐牌上,厨师按完整指令出餐。
Highlight:风险与注意点
- READ_ONCE/WRITE_ONCE 是否必要:K Prateek Nayak 质疑,llist 添加已有 release 语义,接收方看到对象时
sched_remote_wakeup_flags一定已稳定;其它 llist 上的 csd 字段就没用这些 barrier。 - 注释措辞误导:
smp_load_acquire(&p->on_cpu) in ttwu_queue_wakelist()应当是 before;且真正配对的是finish_task()里的smp_store_release()(目的 CPU context switch 后、开中断前),而不是schedule()里的smp_mb__after_spinlock()。 - 字段独立化的代价:从 scheduler bitfield word 拆出后,必须靠 atomic/barrier 风格访问,不能再依赖 scheduler lock 隐式同步。
- 跨拓扑验证缺失:仅在 80 CPU Ampere Altra(单 socket 假设)上验证,跨 die / 跨 socket 仍需观察。
- v4 预期:注释措辞会改,
READ_ONCE/WRITE_ONCE可能被回退。
版本变化
- v1 → v2
sched_remote_wakeup_flags独立成u8,拆出 scheduler bitfield- 删除
sched_ttwu_pending()中多余 reset - 引入
WF_TTWU_QUEUE_MASK集中定义 - 强化 direct vs queued 一致性的 changelog
- v2 → v3
- 增加
READ_ONCE/WRITE_ONCE - 以
WF_RQ_SELECTED抢占行为差异作为 changelog 主线 - 保留 cross-CPU ordering 注释(但内容仍可商榷)
- 增加
一句话总结
把跨 CPU 排队 wakeup 保存的 wake flag 从 1 bit 升级为 8 bit 完整 mask,让 sched_ttwu_pending() 看到与直接 wakeup 一致的 flags,修复 ENQUEUE_RQ_SELECTED 与 wakeup_preempt_fair() 路径偏差。