sched discussion
[PATCH] sched/deadline: Do not pull when the local CPU is the only overloaded one
LLM 分析
sched/deadline: 本地 CPU 是唯一过载时不发起 pull
系列概况
- 标题: [PATCH] sched/deadline: Do not pull when the local CPU is the only overloaded one
- 作者: Liang Hao haohlliang@gmail.com
- 版本: v1(无版本号,单 patch)
- 规模: 1 个 patch,1 文件改动
- 修改文件:
kernel/sched/deadline.c - 代码统计: +7 / -1(
kernel/sched/deadline.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)) - Message-ID:
20260807150709.47181-1-haohlliang@gmail.com - 完整性: 单封邮件,commit message 完整(含 Signed-off-by),diff 与说明一致
补丁目的
DEADLINE 调度器在 pull_dl_task() 中会扫描 rd->dlo_mask 寻找过载 CPU,再尝试把对方的 deadline 任务拉过来。如果当前 CPU 自己就是唯一被标记为过载的 CPU,那么 for_each_cpu() 循环每次迭代都会通过 if (this_cpu == cpu) continue; 跳过自己,最终什么都不做——这是一次空转。
本 patch 把 RT 调度器在 commit f73c52a5bcd1 ("sched/rt: Do not pull from current CPU if only one CPU to pull") 中加入的短路判断镜像到 DEADLINE:当 dlo_count == 1 且当前 CPU 拥有 dlo_mask 里唯一的 bit 时直接 return,避免无意义的循环扫描。
旧流程的问题
旧代码即使本地是唯一过载 CPU,仍会执行 smp_rmb()、进入 for_each_cpu() 遍历 dlo_mask,每次都用 if (this_cpu == cpu) continue; 跳过自己再走完一轮循环。功能上不影响正确性,但:
- 多余的 cache line 读取(
dlo_mask/dlo_count) - 多余的 branch 与 loop 迭代
- 与 RT 侧形成不对称,DEADLINE 这条路径遗漏了同类短路
新流程
新流程把"本地是唯一过载源"这一情形提前到 fast-path 之后立即返回,不再进入遍历循环:
+----------------------------------------+
| pull_dl_task(this_rq) |
+----------------------------------------+
|
v
+----------------------------------------+
| count = dl_overloaded(this_rq) |
+----------------------------------------+
|
v
+----------------------------------------+
| if (count == 0) return |
+----------------------------------------+
|
v
+----------------------------------------+
| smp_rmb() |
+----------------------------------------+
|
v
+----------------------------------------+
| if (count == 1 && |
| cpumask_test_cpu(this_cpu, dlo_mask))|
| return |
+----------------------------------------+
|
v
+----------------------------------------+
| for_each_cpu(cpu, dlo_mask) { |
| if (this_cpu == cpu) continue |
| /* original pull logic */ |
| } |
+----------------------------------------+
RT 与 DEADLINE 两侧的 short-circuit 现状对比:
Before patch (asymmetric): After patch (symmetric):
pull_rt_task: pull_rt_task:
if (count == 1 && own) if (count == 1 && own)
return return
for_each_cpu(rto_mask) { for_each_cpu(rto_mask) {
if (this_cpu == cpu) if (this_cpu == cpu)
continue continue
... ...
} }
pull_dl_task: pull_dl_task:
(no short-circuit) if (count == 1 && own)
for_each_cpu(dlo_mask) { return <-- NEW if (this_cpu == cpu) for_each_cpu(dlo_mask) {
continue if (this_cpu == cpu)
... continue
} ...
}
Patch 概览
kernel/sched/deadline.c 的 pull_dl_task() 顶部只动两处:
- 把
dl_overloaded(this_rq)的结果保存到dl_overload_count,并用它替代原 fast-path 判断条件。 - 在
smp_rmb()之后、for_each_cpu()之前插入 4 行短路判断:count == 1 && 本 CPU 在 dlo_mask 中就return。
关键实现
static void pull_dl_task(struct rq *this_rq)
{
int dl_overload_count = dl_overloaded(this_rq);
- if (likely(!dl_overloaded(this_rq)))
+ if (likely(!dl_overload_count))
return;
smp_rmb();
+ /* If we are the only overloaded CPU do nothing */
+ if (dl_overload_count == 1 &&
+ cpumask_test_cpu(this_rq->cpu, this_rq->rd->dlo_mask))
+ return;
+
for_each_cpu(cpu, this_rq->rd->dlo_mask) {
if (this_cpu == cpu)
continue;
...
}
}
要点:
dl_overloaded()内部读的是rd->dlo_count,保存到本地变量后既能复用做 fast-path,也能在短路判断里继续用,避免重复读取。cpumask_test_cpu(this_rq->cpu, this_rq->rd->dlo_mask)直接复用现有的dlo_mask,与 RT 侧用rto_mask判this_cpu的写法完全对称。- 提交者明确说明 DEADLINE 没有
RT_PUSH_IPI对等路径,因此这次只是单边对称补齐,没有引入 push 端短路。 - 整体内存序:依靠
smp_rmb()与dl_set_overload()里的smp_wmb()配对保护dlo_count/dlo_mask的可见性,新增判断位于smp_rmb()之后,时序正确。
类比
想象一个快递员走进分拣中心,先扫一眼大厅里"有待取件"的窗口清单(dlo_mask / dlo_count)。如果清单上写着"只有你自己的窗口有待取件",那他根本没必要再沿着清单走一圈——绕到头来也只能跳过自己。补丁做的就是:在大厅门口看一眼,发现清单只有自己那行,就直接掉头回去,把这一圈白跑省掉。
另一个角度:RT 调度器是已经"装了这种感应门"的版本,DEADLINE 调度器这边一直没装,这次给同一栋楼的对称入口补上了同款感应门,让两边走法对称。
Highlight:风险与注意点
- 功能不变:作者明确写"No functional change",仅做对称补齐和微小性能优化,理论上不应改变可观察行为;下游 stable backport 不必着急。
- 关注点 1:
dl_overloaded()与smp_rmb()之间没有重新读dlo_count;本地变量复用是安全的,因为读、写配对 (smp_wmb/smp_rmb) 仍然成立。 - 关注点 2:DEADLINE 没有
RT_PUSH_IPI等价的 push 路径,因此这里只是"pull 端"的对称化;如果未来加 push,需要同样在 push 端判断"只有自己是过载源"时跳过。 - 关注点 3:
pull_dl_task()的调用方(如dl_task_offline_migration、balance_callback)依赖它尝试拉任务;这次新增的 early return 不会改变"是否值得调用"的上层决策,仅在该函数内部更早退出。 - 关注点 4:建议关注 review 端是否要求把判断下沉到
dl_overloaded()内部做封装,或要求同时给pull_rt_task()加注释引用本次 commit,保持两侧注释对称。
一句话总结
把 RT 调度器已有的"唯一过载就是自己就跳过 pull"短路逻辑镜像到 DEADLINE 的 pull_dl_task(),避免本地 CPU 是唯一过载源时空转 for_each_cpu(),并保持两侧 short-circuit 对称。