Linux RT 分析Linux 6.12.107

12 · 部署与调优(ARM64):让 RT 内核真正拿到 10 µs 级延迟

结论:RT 内核提供的是「可抢占的内核」,不是「不被打扰的 CPU」。ARM64 上要把最坏延迟压到 10–50 µs,还需要四层配合:固件与电源管理收口(ARM64 没有 SMI,但有 EL3 安全世界、PSCI 深度 idle、DVFS/SCMI 这些等价噪声源)、CPU 分区isolcpus/nohz_full/rcu_nocbs/irqaffinity,big.LITTLE 平台选大核簇)、优先级栅格(内核自建 FIFO 线程一律 50,必须重排)、应用侧(锁内存、预分配、PI 互斥锁、钉核)。x86 的 intel_idle.max_cstateprocessor.max_cstatetsc=idle=poll 在 arm64 上不存在,别照抄。

第一层:部署形态

先看结论:RT 内核 + CPU 分区 + 优先级栅格三者叠加才有 10 µs 级;缺任何一个都会被固件 / 中断 / 内核线程的噪声吃掉。

非 RT非 RT 默认:所有 CPU 共享一切
非 RT:非 RT 默认:所有 CPU 共享一切
同一场景 · 换成 PREEMPT_RT 内核
PREEMPT_RTRT 推荐:housekeeping / 隔离 CPU 分区 + 优先级栅格
RT:RT 推荐:housekeeping / 隔离 CPU 分区 + 优先级栅格

第二层:抖动来源与检查清单

先看结论:按固件 → 配置 → 启动参数 → 运行时 → 优先级 → 应用 → 验证七层逐层落实,每层都有对应的验证命令。

抖动来源全景(红色 = RT 直接消除)
抖动来源全景(红色 = RT 直接消除)
按层的部署检查清单(ARM64)
按层的部署检查清单(ARM64)

1. 固件 / SoC(ARM64 的「SMI 等价物」)

噪声源 做法 验证
EL3 安全世界(TF-A、OP-TEE、SCP 固件) 热路径不调用 OP-TEE / SCMI 同步接口;固件升级到厂商 RT 推荐版本;关闭固件侧周期性任务(温控、DVFS 轮询) hwlatdetect --duration=1h --threshold=20:hwlat tracer 架构无关,能抓到内核看不见的固件停顿
PSCI idle 状态 只留最浅态:启动参数 cpuidle.off=1,或运行时 echo 1 > /sys/devices/system/cpu/cpuN/cpuidle/stateM/disabledrivers/cpuidle/sysfs.c:302-325);ARM_PSCI_CPUIDLE 在分层 idle 布局下 RT 只能用 per-CPU 状态(drivers/cpuidle/Kconfig.arm:27-33_DOMAIN 默认 y);PSCI 默认 PC 模式、OSI 探测到也不启用(drivers/firmware/psci/psci.c:736-740),进入深态的 SMC(:469-480)之后的唤醒延迟由固件决定、内核看不见 cpupower idle-info/sys/devices/system/cpu/cpu*/cpuidle/state*/{usage,time} 在测试期间不增长
DVFS / SCMI cpufreq.default_governor=performancecpupower frequency-set -g performance;SCMI 平台确认 SCP 固件不会自行降频 cpupower frequency-infoscaling_cur_freq 稳定
big.LITTLE / DynamIQ RT 线程钉在大核簇;隔离集选大核;kernel.sched_energy_aware=0kernel/sched/topology.c:211, 317-322, 432,默认 1)减少 CFS 侧能效迁移噪声 /sys/devices/system/cpu/cpu*/cpu_capacitydrivers/base/arch_topology.c:215,来自 DT capacity-dmips-mhz :321-322);lscpu -e
EFI runtime(UEFI 服务器) RT 默认已禁(drivers/firmware/efi/Kconfig:240-249);U-Boot 嵌入式板无此项 dmesg | grep -i efi
RAS / SDEI firmware-first RAS 通过 SDEI 事件打断 normal world;生产环境评估是否关闭轮询式 RAS 扫描 dmesg | grep -i sdeihwlatdetect
GIC 伪 NMI CONFIG_ARM64_PSEUDO_NMI + irqchip.gicv3_pseudo_nmi=1 让 perf 采样与硬死锁检测用 NMI;NMI 处理函数必须只用 raw 锁;调试期开、生产按需 dmesg | grep -i pseudo-NMI

2. 内核配置(arm64)

CONFIG_PREEMPT_RT=yEXPERT=yarch/arm64/Kconfig:105 提供 ARCH_SUPPORTS_RT)、HZ_1000NO_HZ_FULL=yRCU_NOCB_CPU=yHIGH_RES_TIMERS=y;页大小按需选 ARM64_16K_PAGES/ARM64_64K_PAGES(补偿 THP 缺失);可选 ARM64_PSEUDO_NMIARM_PSCI_CPUIDLE 留意 RT 限制。关 DEBUG_PREEMPTLOCKDEPKASANDEBUG_RT_MUTEXESSLUB_CPU_PARTIAL(help 建议)。验证:zcat /proc/config.gz | grep -E 'PREEMPT_RT|NO_HZ_FULL|RCU_NOCB|ARM64_.*_PAGES|PSEUDO_NMI'

3. 启动参数(arm64)

isolcpus=domain,managed_irq,4-7 nohz_full=4-7 rcu_nocbs=4-7 irqaffinity=0-3 skew_tick=1
cpuidle.off=1 cpufreq.default_governor=performance rcutree.kthread_prio=3
nowatchdog nosoftlockup

(示例为 8 核 big.LITTLE:0–3 小核做 housekeeping,4–7 大核隔离;同构核按需分配。)

参数 作用 相关源码/默认
isolcpus=domain,… 隔离 CPU 退出调度域,负载均衡不再碰它 kernel/sched/isolation.c
isolcpus=managed_irq,… managed 中断(NVMe、多队列网卡)不落在隔离 CPU 同上
nohz_full= 隔离 CPU 单任务时停 tick(arch timer 仍在,只是不再周期触发) NO_HZ_FULL
rcu_nocbs= RCU 回调 offload 到 rcuo 线程(在 housekeeping) 第 08 章
irqaffinity= 默认中断亲和性;GIC SPI 全部落 housekeeping 第 03 章
skew_tick=1 错开各 CPU tick 第 05 章
cpuidle.off=1 完全关 cpuidle,CPU 只做 WFI;耗电换延迟 drivers/cpuidle/cpuidle.c:40-45, 811-818kernel-parameters.txt:894-898
cpufreq.default_governor=performance 固定最高频 drivers/cpufreq/cpufreq.c:51, 3078-3084kernel-parameters.txt:903-906
rcutree.kthread_prio= rcuc/rcub 优先级(默认 1,kernel/rcu/tree.c:168 第 08 章
irqchip.gicv3_pseudo_nmi=1 GICv3 伪 NMI(可选;开启后伪 NMI 返回不抢占、关中断改走 PMR) arch/arm64/kernel/cpufeature.c:1093-1097kernel-parameters.txt:2417-2420
kpti=off 仅对 kpti_safe_list 外且 CSV3=0 的核(如 A75)有意义;A53/A55/A72/A73/A76+ 本就关闭 arch/arm64/kernel/cpufeature.c:1737, 1982kernel-parameters.txt:2657
mitigations=off / nospectre_bhb / nospectre_v2 / ssbd=force-off 去掉 Spectre 系列入口开销(几十 ns/次) kernel/cpu.c:3230arch/arm64/kernel/proton-pack.c:90, 413, 1027kernel-parameters.txt:3515, 4108, 4116需安全评审
arm64.nosve / arm64.nomte / arm64.nobti / arm64.nopauth 关闭对应扩展 kernel-parameters.txt:446-461;无 SVE 用户时关 SVE 可省切换开销
iommu.strict=0 / iommu.passthrough=1 SMMU 惰性失效 / 直通 drivers/iommu/iommu.c:44, 207-208kernel-parameters.txt:2345, 2360
cma=512M(按需) 预留连续内存,配合启动期一次性申请 kernel/dma/contiguous.c:91kernel-parameters.txt:728
threadirqs / preempt= RT 上不存在 kernel/irq/manage.c:27-35kernel/Kconfig.preempt:98
intel_idle.* / processor.max_cstate / tsc= / idle=poll x86 专用,arm64 无效 kernel-parameters.txt:2001-2002, 2260-2262, 4883, 7013-7016(标 [X86]

验证:cat /sys/devices/system/cpu/isolatedcat /sys/devices/system/cpu/nohz_fullcat /proc/interrupts 隔离列应几乎全 0(只剩 arch timer 与 IPI)。

4. 运行时

sysctl -w kernel.sched_rt_runtime_us=-1      # 取消 95% 限流(kernel/sched/rt.c:17-21 默认 950000)
sysctl -w kernel.timer_migration=0
sysctl -w kernel.sched_energy_aware=0        # big.LITTLE 平台
sysctl -w vm.stat_interval=120
sysctl -w kernel.hung_task_timeout_secs=0
sysctl -w net.core.busy_poll=50 -w net.core.busy_read=50   # 雷达/相机 UDP 高 pps 时(RT 上 NET_RX_BUSY_POLL 默认开)
sysctl -w kernel.printk='3 4 1 3'                          # 降低控制台输出量
systemctl stop irqbalance    # 或 IRQBALANCE_BANNED_CPUS 掩码
echo 0-3 > /proc/irq/default_smp_affinity
for i in /proc/irq/*/smp_affinity_list; do echo 0-3 > $i 2>/dev/null; done
for s in /sys/devices/system/cpu/cpu[4-7]/cpuidle/state[1-9]*/disable; do echo 1 > $s; done   # 未用 cpuidle.off 时

RHEL 用 tuned-adm profile realtime;Ubuntu Pro real-time(有 arm64 版)用对应 tuned profile,两者做的就是上面这些。

5. 线程与中断优先级

内核自己创建的 FIFO 线程一律在 50(kernel/sched/syscalls.c:836-861 的注释:「管理员必须配置系统」)。arm64 上 arch timer(per-CPU)与 IPI 不会被线程化,其余 GIC SPI 中断都有 irq/N-name 线程。建议栅格:

优先级
99 migration/Nwatchdog/N(内核固定,别动)
85 RT 应用真正依赖的设备的 irq/* 线程(如实时以太网、CAN、SPI 从设备)
80 RT 应用线程
50 其余 irq/*(默认)
3 rcuc/Nrcub/Nrcutree.kthread_prio=3
OTHER ksoftirqdkworkerpr/legacynapi/*、普通任务
chrt -f -p 85 $(pgrep -f 'irq/.*-eth1')
ps -eo pid,cls,rtprio,psr,comm | grep -E 'irq/|rcuc|ksoftirqd|napi/|pr/'

不要用 99:那是 stopper 与 watchdog 的位置,抢了它们系统会卡死。

6. 应用

7. 验证与验收

housekeeping / 隔离的原理、内核实现与友商做法见 第 13 章;自动驾驶 SoC 的完整参数集、三个分区样板与 hypervisor / 功能安全取舍见 第 14 章

常见坑(ARM64)

现象 原因 章节
RT 线程每秒卡 50 ms 95% 限流没关 01
隔离 CPU 偶发 100–300 µs 尖峰,内核 trace 里看不到 PSCI 深度 idle 退出,或安全世界/SCMI 调用;用 hwlatdetect 12
小核上跑 RT 线程延迟大 隔离集选了 LITTLE 核;频率低、缓存小 12
网络吞吐掉一半 网卡 irq 线程/ksoftirqd 被 RT 应用压住,或没开硬件合并 03, 04
RCU stall 告警 RT 线程 > kthread_prio 且长跑,没 rcu_nocbs 08
模块卸载/netns 销毁很慢 加急 GP 被禁 08
驱动在 RT 上 lockdep 报「原子上下文睡眠」 spin_lock_irqsave 不关中断 / hardirq 里 kmalloc / raw 锁里拿 spinlock_t 02, 06
开了 pseudo-NMI 后驱动崩溃 NMI 处理函数里用了 spinlock_t(RT 下会睡) 02, 03
hrtimer_cancel 死锁 / 睡眠告警 RT 上它可能睡眠,不能在 raw 区调用 05
64K 页内核下某些用户态程序/驱动异常 假设 4K 页(mmap 对齐、DMA 缓冲) 06

小结