Linux RT 分析Linux 6.12.107

06 · 内存分配器:无锁快路径失效与上下文限制

结论:非 RT 的 SLUB 快路径靠「抢占关闭 + this_cpu_cmpxchg」做到无锁,慢路径关中断;页分配器的 per-CPU 页列表用 preempt_disable + spin_lock。这些在 RT 上都不成立:local_lock 既不关中断也不关抢占,无法排除并发的慢路径,于是 USE_LOCKLESS_FAST_PATH() 恒为 false,每次 kmalloc/kfree 都拿 cpu_slab->lock(一把 rt_mutex);页分配器改用 migrate_disable + spin_trylock,失败退回伙伴系统。此外 RT 的策略是 hardirq 里不允许任何分配/释放THPNUMA_BALANCING 直接不可选,compact_unevictable_allowed 默认 0。

第一层

先看结论:RT 上分配器全程可抢占,但 SLUB 无锁快路径失效、每次分配都拿锁,且 hardirq 内禁止分配;THP 与 NUMA balancing 不可用。

非 RT快路径无锁,慢路径关中断,任何上下文可分配
非 RT:快路径无锁,慢路径关中断,任何上下文可分配
同一场景 · 换成 PREEMPT_RT 内核
PREEMPT_RT全程可抢占,快路径也要拿锁,hardirq 禁止分配
RT:全程可抢占,快路径也要拿锁,hardirq 禁止分配

第二层

先看结论:变化集中在三个宏——USE_LOCKLESS_FAST_PATH()=falseslub_get_cpu_ptr()=migrate_disablepcpu_task_pin()=migrate_disable;其余是 local_lockpreempt_disable_nested 的点状替换。

非 RTmm/slub.c 非 RT 分支
非 RT:mm/slub.c 非 RT 分支
同一场景 · 换成 PREEMPT_RT 内核
PREEMPT_RTslub / page_alloc / vmstat / kmap 的 RT 分支
RT:slub / page_alloc / vmstat / kmap 的 RT 分支

SLUB

设计注释 mm/slub.c:119-149 讲清了两种模式:非 RT 下 cpu_slab->lock 只保护慢路径,快路径靠 tid 事务号检测被抢占/迁移;RT 下(:126-129)「local lock 既不关中断也不关抢占,所以无锁快路径不能用,local lock 总是被拿,但仍然利用 freelist」。

切换宏(:186-205):

非 RT RT
slub_get_cpu_ptr() get_cpu_ptr() = preempt_disable() migrate_disable(); this_cpu_ptr()
USE_LOCKLESS_FAST_PATH() true false

后果:

页分配器

mm/page_alloc.c:109-123 的注释与宏:

preempt_disable is used on !RT because it is faster than migrate_disable. migrate_disable is used on RT because otherwise RT spinlock usage is interfered with and a high priority task cannot preempt the allocator.

其它 mm 子系统

子系统 RT 变化 位置
vmstat RMW 计数不能再依赖关中断;preempt_disable_nested() 在 RT 上是真 preempt_disable() mm/vmstat.c:351-357include/linux/preempt.h:438-478
memcg per-CPU stock 用 local_lock_t stock_lockmemcg_stats_lock()preempt_disable_nested() mm/memcontrol.c:535-554, 1700-1717
cgroup v1 memory memcg1_check_events() 直接返回;soft_limit_in_bytescgroup.event_control 返回 -EOPNOTSUPP mm/memcontrol-v1.c:1523-1524, 1946-1947, 2576-2577Documentation/admin-guide/cgroup-v1/memory.rst:68, 80
LRU batch cpu_fbatches 两把 local_lock_tlock / lock_irq mm/swap.c:49-70, 213-233
mlock / zsmalloc local_lock_t 保护 per-CPU batch / 映射区 mm/mlock.c:31-38mm/zsmalloc.c:261-266, 1224-1276
kmap_atomic RT 用 migrate_disable() 代替 preempt_disable()(arm64 无 HIGHMEM,走 highmem-internal.h:205-235 的非 highmem 分支,实际只是 migrate_disable + pagefault_disable include/linux/highmem-internal.h:97-132, 205-235include/linux/highmem.h:136-147
THP 不可选 mm/Kconfig:816-818
NUMA balancing 不可选 init/Kconfig:949-953
compaction compact_unevictable_allowed 默认 0,改动时告警 mm/Kconfig:648-652mm/compaction.c:3285-3294Documentation/admin-guide/sysctl/vm.rst:144-146
debugobjects RT 上若任务正阻塞在 rt_mutex 上则不能填充池(避免破坏 PI 状态) lib/debugobjects.c:608-624
调度器 pi_lock 时不能 kfree(),改 kfree_rcu() kernel/sched/core.c:2734-2739

Documentation/locking/locktypes.rst:470-489 给出了使用侧规则:持 raw_spinlock_t 时不能分配(分配器完全可抢占,不能在真原子上下文调用);持普通 spinlock_tkmalloc(GFP_ATOMIC) 完全没问题。

ARM64:DMA、CMA 与 SMMU(自动驾驶 SoC 的主要内存噪声)

机制 行为 对延迟的影响 位置
非一致性 DMA 的缓存维护 设备不与 CPU 缓存一致时,每次 dma_map/dma_sync 要按缓冲区大小做 dcache_clean_poc() / dcache_inval_poc() 与数据量成正比:一帧 8 MB 图像清缓存约 1–3 ms,且在驱动(RT 下是 irq 线程或调用线程)里执行 arch/arm64/mm/dma-mapping.c:15-31
dma-coherent DT 里声明设备一致性后跳过缓存维护 消除上一项;取决于 SoC 互连(CCI/CMN)是否真的一致 drivers/of/address.c:1000-1010
CMA 大块连续内存按需从 CMA 区迁移页面后分配 __cma_alloc() 可能迁移/回收,毫秒级尖峰;应启动期一次性申请 mm/cma.c:406kernel/dma/contiguous.c:91 cma=
swiotlb 弹跳 DMA 掩码不覆盖目标地址时拷贝到弹跳缓冲 隐式 memcpy;确保 64 位掩码或 IOMMU 映射 kernel/dma/swiotlb.c:206 swiotlb=
SMMU 严格/惰性失效 严格模式每次 unmap 同步 TLB 失效(cmdq sync);惰性模式批量延后 iommu.strict=0(或 IOMMU_DEFAULT_DMA_LAZY)降低 unmap 路径延迟;iommu.passthrough=1 彻底绕过(安全性换延迟) drivers/iommu/iommu.c:44, 207-208drivers/iommu/Kconfig:110-135kernel-parameters.txt:2345, 2360
SMMUv3 MSI 轮询 disable_msipolling 影响 cmdq 同步等待方式 平台相关 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c:36-37
MPAM(缓存/带宽分区) 6.12.107 无 MPAM 驱动 GPU/NPU 对 DDR 带宽的挤占只能靠 SoC QoS 寄存器与固件 全树无 mpam 驱动

这些机制与 RT 无关,但在 RT 上更显眼:因为内核其它不可抢占区都被消除后,一次 2 ms 的缓存清理会成为最坏延迟的主导项。它们的共同解法是把分配与映射挪到启动期(预分配 + 固定映射 + dma-coherent)。

性能影响

净影响:kmalloc/kfree 密集负载退化 10%–30%;分配路径不再制造不可抢占区;RT 应用仍应预分配。

指标 非 RT RT 说明
kmalloc 快路径 ~20–40 ns 无锁 拿/放一把 rt_mutex + migrate_disable/enable:约 2–3 倍 分配密集负载(网络 skb、文件系统元数据)可见
慢路径关中断时长 关中断 不关中断、可抢占 最坏延迟收益
pcp 争用 自旋 trylock 失败直接走 zone->lock(也可抢占) 更多退回伙伴系统的次数
直接回收 / 压缩 可能让分配挂 ms 级 同样存在但可被抢占;THP 折叠不存在 RT 应用仍需预分配
大页收益 THP 可用 只能显式 hugetlbfs TLB 压力可能上升
NUMA 局部性 自动 balancing 无;靠手工 numactl
hardirq 内分配 允许 禁止(约定) 驱动移植点

源码定位

主题 位置
SLUB 设计注释 mm/slub.c:119-149, 688-700
切换宏 mm/slub.c:186-205
kmem_cache_cpu.lock mm/slub.c:385-400, 3081-3089
快路径判定 mm/slub.c:4034-4054, 4621-4646
local_lock_irqsave 调用点 mm/slub.c:3222-3317, 3717-3951, 3969-3987, 4945-5009
SLUB_CPU_PARTIAL mm/Kconfig:319-328
页分配器宏 mm/page_alloc.c:95-190
rmqueue_pcplist / free_unref_page mm/page_alloc.c:3086-3117, 2709-2751
锁类型 include/linux/mmzone.h:683-684, 948-949
vmstat / memcg / swap / kmap 见上表
THP / NUMA / compaction Kconfig mm/Kconfig:648-652, 816-818init/Kconfig:949-953
arm64 DMA / CMA / SMMU arch/arm64/mm/dma-mapping.c:15-31drivers/of/address.c:1000-1010mm/cma.c:406kernel/dma/contiguous.c:91kernel/dma/swiotlb.c:206drivers/iommu/iommu.c:44, 207-208drivers/iommu/Kconfig:110-135

调优要点

小结