大数跨境
0
0

Linux 6.18 CFS调度器看点:拿下延迟收益并化解 PREEMPT_RT 死锁

Linux 6.18 CFS调度器看点:拿下延迟收益并化解 PREEMPT_RT 死锁 Tina讲出海
2025-09-03
10
导读:面向 Linux 6.18 的一组调度器补丁将 CFS 带宽限制 从“立即把队列移出运行队列”改为基于任务的限制。这样可避免任务在内核态持锁时被强制剥离引发的高尾延迟,并化解 PREEMPT_RT(实

 

摘要

面向 Linux 6.18 的一组调度器补丁将 CFS 带宽限制(throttling) 从“立即把队列移出运行队列”改为基于任务的限制:当 CFS 运行队列(cfs_rq)需要限制时,不立刻整体摘除,而是在任务被挑中后挂上 task_work,使其在用户态返回路径上再执行限制(再真正出队)。这样可避免任务在内核态持锁时被强制剥离引发的高尾延迟,并化解 PREEMPT_RT(实时补丁)场景下的潜在死锁。同时,补丁同步调整 PELT 行为与限制时间记账,提升 Observability(可观测性)与行为一致性。该系列已进入 tip 的 sched/core 队列,预计随 6.18 合入。

这里的“限制”到底指什么?
限制(CFS 带宽限制,CFS bandwidth throttling):当某个 cgroup的 CPU 配额 用尽时,禁止该层级下的 CFS 任务继续消耗 CPU 运行时,直到下一周期配额补充为止。

  • • 触发条件:配额用尽(cgroup v2 的 cpu.max,或 v1 cpu.cfs_quota_us/cpu.cfs_period_us)。
  • • 限制动作:把相关任务从“可继续运行”的状态移除/挂起于过渡列表(新模型改为在任务返回用户态时再实际出队)。
  • • 解除限制(unthrottle):周期到来配额补充后,再把这些任务重新入队,恢复调度。

背景:为什么要把“限制动作”挪到用户态返回路径上?

传统 CFS 带宽控制在配额用尽时,会立即把对应 cfs_rq 从 CPU 的 runqueue 移出,达成“限制”目标。然而这带来两个突出问题:

  • • 尾延迟被放大:若被限制的任务此时正持有内核锁(例如 read_lock),会阻塞其它未受限制任务的锁获取,导致资源获取被拒、尾延迟拉长。
  • • PREEMPT_RT 潜在死锁:在 RT 内核中,读/写锁与软中断线程(如 ksoftirqd/ktimers)可能形成依赖环:被限制任务持读锁→写者阻塞→新的读者走 slowpath;若计时器/软中断线程也被链路牵制,可能无法解开

核心思路:把“限制动作”从“立刻摘队”改为在 return-to-user(用户态返回)路径上触发。这样就不会在内核态持锁的中间态被硬剥离,从而缓解尾延迟并破除 RT 中的环形依赖。


核心改动与设计要点

1)从“队列级限制”到“任务级限制

  • • 旧模型cfs_rq 一旦被限制,整体从 CPU runqueue 移出,附属任务全部不可被调度。
  • • 新模型cfs_rq 被限制时仅标记限制状态立即移出;当其中某个任务被挑中运行时,给它挂一个 task_work,使其在用户态返回路径上再执行限制(再真正出队/进入过渡列表)。
    • • 效果:避免“内核态持锁时被强摘”,打断锁链,降低尾延迟消除死锁条件
    • • 实现点:为任务与 cfs_rq 增加限制标记与“limbo”过渡列表;在 解除限制(unthrottle) 时把这些任务批量恢复入队。

2)PELT(Per-Entity Load Tracking,逐实体负载跟踪)行为调整

  • • 旧行为cfs_rq 一旦被限制就冻结 PELT 时钟
  • • 新行为:只要被限制的 cfs_rq 仍有实体排队/在内核态运行,就保持 PELT 时钟运行;当最后一个实体出队时才冻结,使负载估计更贴近真实。

3)限制时间记账(throttle time accounting)口径更新

  • • cpu.stat.local 中的 throttled_time:记账口径由“配额用尽瞬间”改为“该层级首个任务真正被延后(在 return-to-user 路径上触发)的时刻”,更贴近实际受影响的区间。
  • • 对 Observability(可观测性) 的影响:监控趋势与报警阈值可能需要重新基线

4)关键路径与数据结构清理

  • • 在任务迁移/换组/切换调度类等场景,保证“被限制任务”的入/出队一致性(如精简 detach_task_cfs_rq()enqueue_throttled_task() 快路径)。
  • • 清理与限制相关的遗留负载均衡逻辑,降低复杂度;将部分 int 标志改为 bool,并补全注释与告警。

适用场景与落地建议

受益最大的场景

  • • PREEMPT_RT / 低延迟:避免“内核态持锁 + 立刻限制”引发的环形依赖/死锁,改善 latency tail。
  • • 大规模多层级配额管理:在服务器混部场景中广泛启用 CFS 带宽控制(cpu.max 或 cpu.cfs_*)的集群。

对 Performance Engineering(性能工程)与 Observability(可观测性)的注意点

  • • 记账口径变更throttled_time 的定义更贴近“真正受影响的时段”,对趋势线、百分位尾延迟报警可能产生影响,建议重建基线
  • • PELT 行为:在“部分限制”状态下 PELT 不再立即冻结,建议结合 /proc/sched_debug 观测 cfs_rq 负载、队列深度与权重变化。
  • • CFS调度器变更行为提醒:不再是“限制会立刻把任务从 runqueue 摘除”。现在的行为是:先打限制标记,任务在 return-to-user(返回用户态)时才真正从 rq 移除

工程实践清单

  • • 压测/回归:使用 fork/exit 压力 与 cgroup_threadgroup_rwsem 压力脚本验证迁移后是否消除锁链卡顿、降低尾延迟。
  • • 指标留档:升级或打补丁前后,记录 throttled_time、软中断延迟、关键业务 P99/P999 延迟做对比。
  • • 代码审计:检查 out-of-tree 模块与定制补丁,避免依赖旧语义导致的边界行为偏差。

与上游进展(简述)

该系列补丁已进入 tip 的 sched/core 队列,若无异议,预计随 6.18 合入。主要改动集中于 kernel/sched/fair.csched.hpelt.h 等,伴随注释与字段 bool 化等清理。强烈建议依赖 CFS 带宽控制与 RT 的系统尽早预研与测试。


总结

把 CFS 带宽限制的执行点从“立刻摘队”移动到用户态返回路径,并配合 PELT 与限制时间记账的同步调整,使调度器在延迟控制与 RT 安全性之间取得更好的平衡:

  • • 更低尾延迟:避免内核态持锁时被强制剥离;
  • • 更稳 RT:破除潜在的锁链环路;
  • • 更准可观测:记账更贴近业务实际受影响区间。

预计随 6.18 合入。

 

【声明】内容源于网络
0
0
Tina讲出海
跨境分享间 | 每日提供跨境资讯
内容 47307
粉丝 1
Tina讲出海 跨境分享间 | 每日提供跨境资讯
总阅读234.0k
粉丝1
内容47.3k