sched discussion
[PATCH 0/2] sched: Fix SCHED_FLAG_KEEP_PARAMS side effects
LLM 分析
sched: SCHED_FLAG_KEEP_PARAMS 副作用修复
系列概况
- 标题:
[PATCH 0/2] sched: Fix SCHED_FLAG_KEEP_PARAMS side effects - 作者: Andrea Righi arighi@nvidia.com
- 版本: v1(无明确版本号)
- 规模: 2 个 patch
- 修改文件:
kernel/sched/syscalls.c - 代码统计: 1 file changed, 4 insertions(+), 2 deletions(-)
- Message-ID:
20260730055011.2267333-1-arighi@nvidia.com - 完整性: 完整(cover letter + 2 patches + 7 封维护者/审阅者回复,合计 10 封)
补丁目的
SCHED_FLAG_KEEP_PARAMS 允许 sched_setattr() 只更新 task 的通用属性(例如 uclamp),保留原有的调度参数和 class。但 __sched_setscheduler() 中仍有两条路径在受保护的参数更新之前对 requested policy 产生副作用:
- 把
prev_class != next_class当作 class change,给DEQUEUE_CLASS置位并触发switching_from()/switched_from()/switching_to()/switched_to()四个 callback,但p->sched_class实际不会变。 - 跑 deadline admission control
sched_dl_overflow(),把一个永远不会真正进入 deadline class 的任务的带宽占用记入 root domain。
本系列在两个分支前增加 SCHED_FLAG_KEEP_PARAMS 短路条件,从运行时消除这两类副作用。
旧流程的问题
sched_setattr(p, flags=KEEP_PARAMS, policy=X)
|
v
__sched_setscheduler(p, attr)
[step 1] if (prev_class != next_class)
-> queue_flags |= DEQUEUE_CLASS
-> switching_*()/switched_*() callbacks fire [WRONG]
[step 2] if (dl_policy(policy) || dl_task(p))
&& sched_dl_overflow(...)
-> root_domain reserves dl_bw [WRONG]
[step 3] scoped_guard(sched_change) -- real param/class update
(KEEP_PARAMS makes this a no-op for class/params)
副作用路径在 step 3 之前执行,但 KEEP_PARAMS 又让 step 3 不修改 class / 参数,于是出现「没有 class change,却跑了 class change 回调」与「task 不是 deadline,却消耗了 dl 带宽」两类不一致。
新流程
两处都加上 !(attr->sched_flags & SCHED_FLAG_KEEP_PARAMS) 短路:
/* Patch 1/2 */
- if (prev_class != next_class)
+ if (!(attr->sched_flags & SCHED_FLAG_KEEP_PARAMS) &&
+ prev_class != next_class)
queue_flags |= DEQUEUE_CLASS;
/* Patch 2/2 */
- if ((dl_policy(policy) || dl_task(p)) && sched_dl_overflow(p, policy, attr)) {
+ if (!(attr->sched_flags & SCHED_FLAG_KEEP_PARAMS) &&
+ (dl_policy(policy) || dl_task(p)) && sched_dl_overflow(p, policy, attr)) {
KEEP_PARAMS 模式下直接跳过这两个分支:class 不变、带宽不记账;参数更新路径在 scoped_guard 内部仍然按通用属性处理。
Patch 概览
- Patch 1/2
sched: Skip class callbacks with SCHED_FLAG_KEEP_PARAMS- 修复点:
__sched_setscheduler()里DEQUEUE_CLASS置位判断。 - 关联 commit:
Fixes: 637b0682821b ("sched: Fold sched_class::switch{ing,ed}_{to,from}() into the change pattern")。
- 修复点:
- Patch 2/2
sched/deadline: Skip bandwidth accounting with SCHED_FLAG_KEEP_PARAMS- 修复点: 同一函数里 deadline 准入与带宽记账。
- 关联 commit:
Fixes: a509a7cd7974 ("sched/uclamp: Extend sched_setattr() to support utilization clamping")。
关键实现
__sched_setscheduler(p, attr)
|
+-------------------------+-------------------------+
| (Patch 1/2) | (Patch 2/2) |
v v |
if (!KEEP_PARAMS if (!KEEP_PARAMS |
&& prev != next) && (dl_policy(policy) |
-> DEQUEUE_CLASS || dl_task(p)) |
-> class callbacks && sched_dl_overflow(...)) |
-> dl_bw accounting |
| | |
+-------------------------+-------------------------+
|
v
scoped_guard(sched_change, p, queue_flags)
-> guarded real update (already respects KEEP_PARAMS)
Peter 在回复中提出另一种「入口拒绝」的修法:
SYSCALL_DEFINE3(sched_setattr, ...)
|
v
if (attr.sched_flags & SCHED_FLAG_KEEP_PARAMS) {
if (attr.sched_policy != SETPARAM_POLICY
&& attr.sched_policy != p->policy)
return -EINVAL;
get_params(p, &attr, 0);
}
|
v
sched_setattr(p, &attr);
也就是说,与其到处打补丁短路副作用,不如让 sched_setattr() 直接拒绝 KEEP_PARAMS 与策略变更的组合,从源头上消除矛盾。
类比
把 sched_setattr() 想象成去物业办理「只换门牌号、不动房子结构」的申请:SCHED_FLAG_KEEP_PARAMS 就是那张不动结构的申请单。
旧流程里,物业一看申请单上写了别的房型,就先把承重墙敲一下(switching_*() 回调),再回头告诉你「其实没动」;同时物业的电量配额管理员,看到「DEADLINE」字样就先从公共池里扣一块配额到你账上,可你根本没装 DEADLINE 设备,配额永远释放不掉。
新流程里,物业先看一眼申请单有没有 KEEP_PARAMS 标记,有的话这两步直接跳过——只换门牌号,不动结构、不占配额。
Peter 的进一步建议则是:在窗口直接拒收「KEEP_PARAMS + 想换房型」这种自相矛盾的申请单(返回 -EINVAL),从源头就不让这种单子进入流水线。
Highlight:风险与注意点
- 设计层面的争议: Peter 在多封回复里主张「
KEEP_PARAMS应当在sched_setattr()syscall 入口就要求attr.sched_policy == p->policy || attr.sched_policy == SETPARAM_POLICY,否则-EINVAL」,而不是在__sched_setscheduler()里到处打补丁。如果按他的思路,这两处 patch 实际上是「症状级」修复。 - KEEP_PARAMS vs KEEP_POLICY 语义: Prateek 提到还存在
SCHED_FLAG_KEEP_POLICY,两条 flag 的语义边界要厘清,避免后续 syscall 层清理时互相打架。 - 是否还有第三条路径: 目前 fix 只覆盖了 class callback 和 dl 带宽两个分支,是否还有其他「在 guarded update 之前消耗 requested policy」的路径(例如
SCHED_FLAG_UTIL_CLAMP_MIN/MAX的极端组合)需要逐一确认。 - 可重现性: dl 带宽被反复消耗而永不释放这条路径,要确认能否在没有这个 fix 时用一个简单 test 程序反复
sched_setattr()触发,以验证 fix 有效。 - 历史 Fixes: 两个
Fixes:commit 一个在 sched core 一个在 sched/uclamp,需要在 CI 上跑相关 test 确保没有引入回归。
版本变化
本帖只有 v1;从回复走向看,作者预计会出 v2:
- 把 Patch 1/2 的
if (!(attr->sched_flags & SCHED_FLAG_KEEP_PARAMS))改写成更紧凑的判断(Prateek 给出了一个备选 hunk)。 - 可能采纳 Peter 的建议,在 syscall 入口加 policy 一致性判断;那样 Patch 2/2 的核心改动可能就不需要了,行为也由「运行时短路副作用」变成「入口拒绝非法组合」。
一句话总结
Andrea 通过两个一行补丁在 __sched_setscheduler() 内补齐 SCHED_FLAG_KEEP_PARAMS 遗漏的两条副作用路径(class transition 回调与 deadline 带宽记账),而 Peter 倾向于在 sched_setattr() syscall 入口就拒绝 KEEP_PARAMS 与 policy 变更的组合,从源头消除矛盾。