Linux-RT 4.19.188中PREEMPT_RT_FULL导致孤立CPU的NO_HZ_FULL失效问题排查
孤立CPU重新激活tick的核心触发逻辑
按照4.19内核rt77补丁的实现,能导致本该关闭tick的孤立CPU,重新启动周期性APIC定时器的逻辑,逃不开三类常见场景:
- NO_HZ_FULL的进入条件判定失效:正常流程下,只有当隔离CPU上仅存在1个可运行任务、没有其他可调度实体、没有绑定在本地的非可迁移hrtimer时,内核才会真正关闭周期性tick。但PREEMPT_RT_FULL场景下,调度器的运行队列统计因为锁的可抢占改造,会出现偶发的统计偏差——比如临时触发的软中断、kworker被短暂挂入运行队列,让判定逻辑误以为需要保留tick,进而触发本地定时器重启。
- 本地hrtimer未被正确迁移:CPU进入nohz_full状态时,内核会把所有可迁移的hrtimer从本地队列迁移到其他非隔离CPU。但RT补丁新增的几类实时专用hrtimer(比如实时任务的周期调度定时器、抢占延迟统计定时器)没有被正确标记为可迁移属性,一旦留在本地队列,就会按照系统HZ的固定周期触发,表现为本地APIC中断计数按HZ频率持续增长。
- tick状态标记的竞争覆盖:隔离CPU进入nohz状态后,会在per-cpu的tick状态里标记“关闭本地tick、依赖其他CPU做时钟广播”。但RT补丁的上下文切换路径上,没有对这个状态标记做严格的锁保护,偶发的并行访问竞争会把这个状态标记清零,触发内核重新把本地APIC设置为周期性tick模式。
问题定位的代码切入路径
针对上述几个可能的触发点,可以按优先级从高到低从以下几个代码段切入排查:
- 第一优先级:NO_HZ_FULL的状态判定逻辑
对应代码位置为kernel/sched/core.c中的tick_nohz_idle_enter、tick_nohz_stop_sched_tick函数,重点排查:- RT补丁对运行队列
nr_running统计值的修改逻辑,确认是否存在无锁读写导致的统计值偶发偏高的问题(单任务判定的核心条件就是rq->nr_running == 1,只要统计值大于1,内核就不会关闭tick) - per-cpu变量
tick_nohz_full_running的置位/清零路径,检查是否存在调度上下文切换时的竞争窗口
- RT补丁对运行队列
- 第二优先级:hrtimer的解绑迁移逻辑
对应代码位置为kernel/time/hrtimer.c中的hrtimer_try_to_cancel、hrtimer_migrate函数,重点排查:- CPU进入nohz_full状态时,遍历本地hrtimer队列的逻辑,是否遗漏了RT补丁新增的非可迁移类定时器
- 绑定在SCHED_FIFO实时任务上的hrtimer,是否因为任务亲和性的临时改动,没有被正确迁移到其他CPU
- 第三优先级:本地APIC定时器的配置逻辑
对应代码位置为arch/x86/kernel/apic/apic.c、kernel/time/tick-common.c中的tick_setup_periodic、tick_resume_local函数,因为问题是偶发出现,静态分析很难定位,需要在这两个函数的入口处加动态跟踪点:- 记录每次触发周期性tick配置时的完整调用栈,直接定位到触发该动作的代码路径
- 对比正常状态下的APIC寄存器配置,检查是否偶发把LVT定时器从one-shot模式切回了周期模式
注:该问题和2018年社区上报的同类问题根因一致,属于PREEMPT_RT_FULL和NO_HZ_FULL集成时的遗留竞争窗口,偶发出现时不会引发系统可观测的异常,仅会表现为孤立CPU的tick计数按HZ频率持续增长。
内容的提问来源于stack exchange,提问作者wangt13
相关产品推荐
相关产品推荐

