RCU-tasks GP
ftrace / BPF trampoline 等待
非 idle 运行任务
记录 nvcsw 快照
holdout 链表
反复扫描并移出
链表清空
GP 结束
四种静止状态
自愿切换
上下文切换
用户态
执行用户代码
idle
进入空闲
主动上报
cond_resched_tasks_rcu_qs()
非自愿抢占不在其中:被抢占任务可能停在读侧临界区中间
direct reclaim 的 shrink_lruvec() 扫描循环,也受这个约束
每轮扫描结束的 cond_resched() 是唯一让出点
回收路径的主循环
shrink_lruvec()
按 LRU 逐轮扫描
每轮扫描结束
调用 cond_resched()
cond_resched()
本意:让出 CPU
CONFIG_PREEMPTION 下它是空操作
CONFIG_PREEMPT_DYNAMIC 下静态调用修正为直接返回 0
结果:既不让出,也不上报
无自愿切换
不进入调度器
不上报静止状态
rcu_tasks_qs 未被调用
留在 holdout 链表
GP 继续等待
direct reclaim 没有时间上限,回收任务可长时间停在扫描循环
rcu_tasks stall
滞留超时触发告警
打印任务状态与调用栈
调用栈
shrink_lruvec()
...
调用栈停在 shrink_lruvec()
为什么会告警
holdout 滞留
任务未过静止状态
超过 stall 超时
GP 判定卡住
打印告警
任务状态 + 调用栈
不是回收慢,而是任务一直没有机会上报静止状态
cond_resched() 不保证调度,循环里没有别的上报点
替换扫描循环里的调用
cond_resched()
只在有调度需求时工作
替换
cond_resched_tasks_rcu_qs()
每轮结束主动上报
不再以调度为前提
上报独立发生
cond_resched_tasks_rcu_qs()
#define cond_resched_tasks_rcu_qs() \
do { \
rcu_tasks_qs(current, false); \
cond_resched(); \
} while (0)
先上报,再调度
rcu_tasks_qs 清 holdout 标记
cond_resched 保持原语义
两步互不依赖
每轮扫描结束
进入下轮之前
主动上报 QS
清掉 holdout 标记
holdout 及时清空
GP 不被拖住
cond_resched_tasks_rcu_qs
每轮结束调用
rcu_tasks_qs(current, false)
检查并清 holdout 标记
cond_resched()
有调度需求才让出 CPU
上报与调度就此解耦
第二个参数是分界
false
自愿切换:算静止状态
true
被抢占:不算静止状态
安全性不变
每轮结束、不持 lru_lock;RCU 长循环同用
开销可忽略
无 GP:一次 per-task 读取 · GP 期间:多一次写
不再滞留 holdout
消除 rcu_tasks
stall 告警
GP 不再被拖延
trampoline 回调
及时执行
仅多一次读取
无 GP 时
开销可忽略
每轮上报 QS
不再等调度
holdout 及时移出
任务不再滞留
GP 按时结束
回调及时推进
作者未提供性能数据 · 以上为基于代码逻辑的预期收益
cond_resched() 空操作
不进入调度器
无 QS 上报
rcu_tasks_qs 未调用
holdout 滞留
任务仍在链表
stall 告警
调用栈打印
cond_resched_tasks_rcu_qs()
每轮扫描结束调用
主动 QS + 按需调度
上报不再依赖调度
GP 及时结束
回调不再被拖住
把静止状态上报从调度中解耦
上报点仍是合法的 cond_resched 位置,安全性不变
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x