sched discussion
[PATCH 0/2] sched/fair: Reset incompatible burst on quota change
LLM 分析
CFS 带宽控制:移除 cpu.max 与 cpu.max.burst 的写入顺序依赖
系列概况
- 标题:v1
[PATCH 0/2] sched/fair: Reset incompatible burst on quota change→ v2[PATCH v2 0/3] sched/fair: remove quota/burst write-order dependency - 作者:Zhe Liu
<liuzhe1@kylinos.cn> - 版本:v1(2 patches,2026-08-20)→ v2(3 patches,2026-09-04)
- 规模:v1 共 4 文件 +90/-8;v2 共 5 文件 +59/-13
- 修改文件:
kernel/sched/core.ckernel/sched/fair.ctools/testing/selftests/cgroup/test_cpu.cDocumentation/admin-guide/cgroup-v2.rstDocumentation/scheduler/sched-bwc.rst
- Message-ID 主线:
20260820033218.214259-1..3-liuzhe1@kylinos.cn(v1 三封)aobnZEh1hfIBhswD@localhost.localdomain(Michal Koutný 评审)20260826030006.171041-1-liuzhe1@kylinos.cn(作者回复,预告 v2 设计)20260904062013.504236-1..4-liuzhe1@kylinos.cn(v2 四封)ce007c25-8d49-4df4-9508-3af911f90eb7@linux.dev(Tao Cui 测试反馈)
- 完整性:cover letter + v1/v2 patch + 评审 + 作者回复 + 测试结果均齐全;
Fixes: f4183717b370指向上游引入 burst 的提交,回归定位清晰。
补丁目的
修复 cgroup v1/v2 CPU bandwidth 中 quota 与 burst 的写入顺序耦合。
当前 tg_set_bandwidth() 把 burst 的合法性挂在「当前 quota」上,导致两类失败:
- unlimited quota 下先写大 burst,再写有限 quota,因
burst_us > quota_us被-EINVAL拒绝; - quota 与 burst 同时增大、burst 先写也会失败。
v1 方案:在 quota 写入路径里把不兼容 burst 直接清零。
v2 方案:保留用户配置的 burst 值,在 CFS runtime refill 时用 min(burst, quota) 裁剪生效值,采纳 Michal Koutný 与 Sashiko「不让用户配置值丢失」的建议。
旧流程的问题
// kernel/sched/core.c tg_set_bandwidth()
if (quota_us != RUNTIME_INF && (burst_us > quota_us ||
burst_us + quota_us > max_bw_runtime_us))
return -EINVAL;
burst 在「写入时」就被强制 ≤ quota。复现路径:
# echo 100000000 > cpu.max.burst
# echo "50000 100000" > cpu.max
sh: write error: Invalid argument
quota 实际没生效,cgroup 仍为 unlimited;用户必须先清零 burst 再重试,运维/Docker/K8s 工具被迫引入写入顺序知识。
新流程
把 burst 的「配置合法性」与「运行时生效上限」拆开:
- 写入路径:仅校验
burst_us > max_bw_runtime_us,去掉与 quota 的耦合; - refill 路径:用
min(burst, quota)把运行时生效 burst 贴 quota 上限。
// kernel/sched/core.c
- if (quota_us != RUNTIME_INF && (burst_us > quota_us ||
- burst_us + quota_us > max_bw_runtime_us))
+ if (burst_us > max_bw_runtime_us)
return -EINVAL;
// kernel/sched/fair.c __refill_cfs_bandwidth_runtime()
- cfs_b->runtime = min(cfs_b->runtime, cfs_b->quota + cfs_b->burst);
+ cfs_b->runtime = min(cfs_b->runtime,
+ cfs_b->quota + min(cfs_b->burst, cfs_b->quota));
Patch 概览
- v1 PATCH 1/2:新增
tg_burst_on_quota_change(),在cpu_quota_write_s64与cpu_max_write调用处把不兼容 burst 清零;新增 selftesttest_cpucg_max_burst_reset。 - v1 PATCH 2/2:同步
cgroup-v2.rst与sched-bwc.rst文档。 - v2 PATCH 1/3:删除
tg_set_bandwidth()中 burst 对 quota 的耦合校验,改在__refill_cfs_bandwidth_runtime()用min(burst, quota)。 - v2 PATCH 2/3:新增
test_cpucg_max_burstselftest,覆盖 burst-first 与 quota-first 两种顺序,断言cpu.max.burst读出的「配置值」在 quota 变化前后不变。 - v2 PATCH 3/3:文档改述为「配置值保留 + 运行时裁剪」,并明确「写入顺序无关」。
关键实现
v1引入一个 quota 写入时的临时 helper:
static u64 tg_burst_on_quota_change(u64 quota_us, u64 burst_us) {
if (quota_us == RUNTIME_INF || quota_us > max_bw_runtime_us)
return burst_us;
if (burst_us > quota_us ||
burst_us > max_bw_runtime_us - quota_us)
return 0;
return burst_us;
}
v2 把它彻底删掉,换成「写入只卡总上限、refill 时裁剪」的两层模型——这样 cpu.max.burst 始终展示用户写入值,但实际生效值由 CFS 在每次 refill 时重新计算。
类比
把它想成银行账户的「月额度(quota)」与「透支缓冲(burst)」:v1 像「调月额度时直接把透支缓冲清零」,用户体感是数字被悄悄改写;v2 改成「保留你填写的透支数字,但实际可用透支上限不会超过新额度」,用户看到自己填的数字不变,系统按新额度自动封顶——既不丢信息也不超发。
ASCII 流程图
用户向 cpu.max / cpu.max.burst 写入
│
┌───────────┴───────────┐
▼ ▼
burst 先写 quota 先写
(quota 当时 unlimited) (任意 burst)
│ │
▼ ▼ v1: tg_set_bandwidth 拒绝 / burst→0
v2: tg_set_bandwidth 接受,burst 留存
│ │
└───────────┬───────────┘
▼
__refill_cfs_bandwidth_runtime()
│
cfs_b->runtime = min(runtime,
quota + min(burst, quota))
│
▼
实际可用 burst ≤ 当前 quota
Highlight:风险与注意点
- 配置值 ≠ 生效值:
cpu.max.burst读出仍是用户原值,但实际生效值被min(burst, quota)封顶。运维/K8s 在写入后立即检查可能误判,文档需明确这一语义差。 stable回溯风险:v2 patch 1/3 带Cc: stable,运行时语义变化涉及长期维护分支;依赖高峰 burst 的数据库、批处理等场景需要重点回归。- 旧校验契约被打破:
f4183717b370引入 burst 时要求burst ≤ quota,v2 改为「可超过、运行时裁剪」;依赖EINVAL做合法性自检的旧脚本会受影响。 - v1 selftest 弃用:v1 的
test_cpucg_max_burst_reset假设 burst 会被重置为 0,v2 已不适用,迁移时需删除或改写,避免回归误报。 - Tao Cui 测试反馈截断:第 10 封邮件正文仅报告在
next-20260903跑了 selftest,但结论部分被截断,需关注后续邮件确认全部通过。
版本变化
v1 → v2 关键改动:
- 策略反转:从「写入时把 burst 重置为 0」改为「写入保留 + refill 裁剪」,由 Michal 评审触发;
- 去掉 helper:
tg_burst_on_quota_change()删除,校验分散到tg_set_bandwidth与__refill_cfs_bandwidth_runtime; - 新增顺序无关 selftest:
test_cpucg_max_burst同时验证 burst-first 与 quota-first; - 文档改述:从「incompatible burst 被清零」改为「configured burst 保留、运行时封顶」;
- 拆 3 patch:v1 把代码与文档合成单 patch,v2 把 selftest 独立成 PATCH 2/3;
- 新增
Cc: stable:v2 把修复标记为可回溯到稳定分支。
一句话总结
把 CFS bandwidth 中 burst 的合法性校验从「写入时」搬到「refill 时」,让 quota/burst 的任意写入顺序都生效且不丢用户配置。