arm64:用 sme_active_cpus 掩码替代 mm_cpumask 采样 — 恢复 C1-Pro SME DVMSync erratum 下的 TLB 回收批量收益

arm64 内存子系统 · C1-Pro SME DVMSync erratum workaround 优化 · TLB 批量刷(reclaim batching)

💡 一句话总结

在带 C1-Pro SME DVMSync erratum(ARM64_ERRATUM_4193714)的 Arm 服务器上,上一版 erratum workaround(0baba94a9779)在页面回收(kswapd)的批量 TLB 刷路径里,为了安全采样 mm_cpumask() 给每一次批量都硬加了一次 dsb(ish) 屏障,把“多页凑一批、只同步一次”的批量收益打散,合入说明指出造成 kswapd 约 30% 性能回归(数据来自 arm64-fixes 合并请求摘要)。本补丁改用全局 sme_active_cpus 掩码(跟踪哪些 CPU 正以 SME 用户态运行)替代逐 mm 的 cpumask 采样:批量刷完成时直接对该掩码发一次 IPI,删掉每次批量的 DSB、每任务 cpumask 分配与相关生命周期管理(净删 87 行),恢复批量收益、修复该回归。

📋 补丁基本信息

项目内容
补丁类型优化(性能回归修复 + 简化重构)——主导为修复 0baba94a9779 erratum workaround 引入的 kswapd 性能回归;同时是纯删减重构(+17/-87 行)
状态状态(Merged)· 合入版本:Linux 7.2(git describe --contains 显示首个含此 commit 的 tag 为 v7.2-rc3)
当前版本主线单版本(经 arm64-fixes 标签合入)· 合入版链接
版本演进首版,暂无多版本系列可回溯(lore MCP 本环境不可用,见“方案演进”数据源说明)
作者机构Catalin Marinas(Arm,arm64 维护者)<catalin.marinas@arm.com>
提交日期2026-06-10(committed);2026-07-10 经 arm64-fixes 标签合入 v7.2-rc3(merge commit d96fcfe1b7f9)
改动范围arch/arm64/include/asm/tlbbatch.h + arch/arm64/include/asm/tlbflush.h + arch/arm64/kernel/fpsimd.c + arch/arm64/kernel/process.c,+17/-87 行,4 文件
核心函数sme_dvmsync_batch() / sme_set_active() / sme_clear_active() / arch_tlbbatch_add_pending()
原始链接git.kernel.org(规范 commit 链接)
Fixes 链Fixes: 0baba94a9779(“arm64: errata: Work around early CME DVMSync acknowledgement”,2026-04 合入)
Review / 测试Cc: Will Deacon(arm64 维护者)· Tested-by: Joshua Liu(Google)· Signed-off-by: Will Deacon

📊 速览卡片

核心机制
全局活跃掩码
优化目标
去批量 DSB
适用场景
kswapd 回收
特性等级
★★★
实测提升
30% 回归修复

特性等级依据:修复的是 30% kswapd 回归、幅度显著且纯删减(+17/-87),兼容性极好(无行为破坏、非 erratum 硬件 no-op)——但收益场景窄(仅 C1-Pro SME 硬件 + 内存回收批量刷路径),30% 数字来自合入说明而非 commit 内独立 benchmark,故未给更高星,综合 ★★★。

🎯 解决什么问题

背景 / 原始动机
这条修复链的源头是一个 Arm CPU 勘误(erratum):C1-Pro 会在 DVMSync(Device Virtual Memory Synchronization,SME/CME 设备内存访问的同步原语)真正完成之前就提前确认它(“acknowledges DVMSync messages before completing the SME/CME memory accesses”)。对运行 SME(Scalable Matrix Extension,可伸缩矩阵扩展,AI/ML 矩阵运算常用)用户态代码的进程,这会让 CPU 在 TLBI + DSB 已经完成后仍可能用过期的地址翻译访问设备内存。
2026-04 合入的首版 workaround(0baba94a9779)的思路:在做 TLB 维护时,对正以 SME 用户态运行的 CPU 补发一次 IPI——IPI 处理器为空函数,靠 SCTLR_EL1.IESB=1 下“取异常到 EL1”即完成 SME 内存访问这一性质,无需显式 DSB。同时为了避免恶意程序(如 madvise(MADV_PAGEOUT) 死循环)触发 IPI 风暴,它用 mm_cpumask() 跟踪“进程活跃在哪些 CPU”,只打断这些 CPU。
问题出在这个 workaround 与批量 TLB 刷(reclaim batching)的交互:workaround 从 arch_tlbbatch_add_pending() 采样 mm_cpumask(),而为了让该读取在硬件 DVMSync 之后有序,每批都要先做一次 dsb(ish)——这正好抵消了“批量刷”想省的 DSB 同步等待。合入说明(arm64-fixes 合并请求摘要)明确记载:这造成 30% 的 kswapd 性能回归。
系统层面:批量 TLB 刷路径被 erratum workaround 的“每批一次 DSB”打散
问题在 arch/arm64/include/asm/tlbflush.h 的批量刷接口:
① arch_tlbbatch_add_pending()(每 unmap 一页调用一次):只发逐页 TLBI,不同步——这是批量刷的核心:把“每页 TLBI;DSB”折叠成“多页只 TLBI,最后统一一次 DSB”,省掉每页等 DSB 的时间。
② 旧 workaround 在这条路径里额外调了 sme_dvmsync_add_pending():先 dsb(ish)(让 mm_cpumask() 读取在硬件 DVMSync 之后有序),再把 mm_cpumask(mm) 累加进每任务(tlb_ubc)的 batch cpumask(首次使用时 zalloc_cpumask_var(GFP_ATOMIC) 分配)。
③ 于是每一批(无论是否真有 SME 进程在跑,只要硬件是 C1-Pro)都多一次 dsb(ish) 屏障 + 一次 cpumask 读取/累加。页面回收(kswapd)的 unmap 路径批量小而频繁,这次多出的屏障开销在回收热路径上被放大成 30% 回归。
④ 代码负担不止性能:还要在任务复制/释放时管理 batch cpumask 的生命周期(arch_dup_tlbbatch_mask / arch_release_tlbbatch_mask,process.c),并处理 CPUMASK_OFFSTACK 两种布局。
场景层面:内存压力下的页面回收在 C1-Pro 上每批白付一次屏障
执行路径:内存回收(kswapd / try_to_unmap / 直接回收)在 PTL 下批量 unmap 匿名/文件页,用 tlb_ubc(per-task tlbflush_unmap_batch)缓存待刷 TLB,攒够一批后在 try_to_unmap_flush() 统一 arch_tlbbatch_flush()(mm/rmap.c)——这是 arm64 上 CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH 的标准回收路径。
高频触发场景:任何持续内存压力下的服务器——数据库/容器/内存密集型负载在回收线程(kswapd)与进程直接回收中高频触发该路径;回收越频繁、批量越小,每批一次 DSB 的占比越高。
为什么该场景踩中系统缺陷:批量刷的收益本质是“省掉每页一次 DSB 等待”;旧 workaround 在每一批末尾再塞一次 DSB,等于把“批量”省下的同步重新交回去,且完全不区分“此刻是否有进程正以 SME 运行”——这是设计层面的结构性开销,不是偶发。
硬件前提:仅 CONFIG_ARM64_ERRATUM_4193714(C1-Pro,依赖 ARM64_SME,default y)且 CPU 实际带该勘误时生效(alternative_has_cap_unlikely 运行时检查);非 C1-Pro 上旧路径同样每批做 dsb(ish) 却无 DVMSync 可等,白白付出屏障成本。
受影响负载:内存回收 / 页面换出(kswapd、直接回收、MADV_PAGEOUT)· 为什么此特性解决此场景:把“每批一次 DSB + cpumask 采样”从批量刷热路径上移除,恢复“多页只同步一次”的批量收益,修复 kswapd 30% 回归

🧩 核心机制

一句话机制:用一个全局 sme_active_cpus 掩码(跟踪“哪些 CPU 此刻正以 SME 用户态运行”)替代“每批采样 mm_cpumask() 并累加”。这个全局掩码在 SME 用户态进出时(sme_set_active() / sme_clear_active())就维护好了,程序序保证它先于用户态 SME 访问可见——所以批量刷完成时直接读它并发 IPI 即可,无需每次批量再 dsb(ish)。

从系统层面看(两个逻辑点)
① 维护面从“每次 flush 采样”前移到“SME 状态切换时”:旧设计在 flush 时刻现算“哪些 CPU 需要打断”(读 mm_cpumask + DSB 排序);新设计把这份信息变成始终新鲜、随时可读的全局状态。sme_set_active()(返回 SME 用户态前/ERET 前)把当前 CPU 置入 sme_active_cpus(同时仍置 mm_cpumask(current->mm) 供非批量路径 sme_dvmsync(mm) 使用);sme_clear_active()(异常进入 EL1,IESB=1 保证 SME 访问已完成)清掉两处。这两个函数本就是旧 workaround 在 fpsimd.c 里已有的,本补丁只是让它们顺带维护新掩码——增量开销只有一次 cpumask 位操作。
② 批量路径不再“记账”,只在一个时点做一次广播:sme_dvmsync_batch() 从“对累加的 per-batch cpumask 发 IPI”改为“对全局 sme_active_cpus 发 IPI”。批量结束时唯一的同步开销仍是原有的 dsb(ish) + __repeat_tlbi_sync(vale1is, 0)(完成逐页 TLBI),DVMSync workaround 的 IPI 在之后顺带发出。取舍:sme_active_cpus 是超集——它包含“任何正运行 SME 进程的 CPU”,而不只“被 unmap 的 mm 所在 CPU”,可能多打断个别 CPU(但只限 SME 活跃的小集合),换来的是批量热路径上每批一次 DSB 的彻底移除。
批量 TLB flush 路径修复前后对比:修复前每批一次 dsb(ish)+mm_cpumask 累加,修复后改用全局 sme_active_cpus 掩码、无每批 DSB
图 1:批量 TLB flush 路径修复前后对比——修复前每批一次额外 dsb(ish)(打散批量收益),修复后批量结束统一对全局 sme_active_cpus 发 IPI、无每批 DSB
来源:基于本地内核 git(commit 534eb6940a89 + 被修 commit 0baba94a9779)真实 diff 绘制
步骤操作目的
1kswapd 批量 unmap PTE,逐页 arch_tlbbatch_add_pending():只发 TLBI、不同步批量累积,省掉每页 TLBI;DSB 的 DSB 等待
2(修复前)sme_dvmsync_add_pending():dsb(ish) + 读 mm_cpumask 累加进 batch cpumask保证 mask 读在硬件 DVMSync 之后 —— 但每批一次 DSB ← 被移除
3(修复后)删除该记账:无每批 DSB、无 per-task cpumask 分配恢复批量收益(-87 行:含 arch_dup/release_tlbbatch_mask 生命周期代码)
4batch 结束 arch_tlbbatch_flush():dsb(ish) + __repeat_tlbi_sync(vale1is, 0) 完成 TLBI 同步;随后 sme_dvmsync_batch() 对 sme_active_cpus 发 IPI统一同步 + DVMSync workaround 打断(IPI 处理器为空函数,靠 IESB 完成 SME 访问)
关键代码片段(一):批量刷的 DVMSync 记账被整段删除,改为直接对全局掩码广播
@@ arch/arm64/include/asm/tlbflush.h
-static inline void sme_dvmsync_add_pending(struct arch_tlbflush_unmap_batch *batch,
-					   struct mm_struct *mm)
+static inline void sme_dvmsync_batch(void)
 {
 	if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714))
 		return;
 
-	/*
-	 * Order the mm_cpumask() read after the hardware DVMSync.
-	 */
-	dsb(ish);
-	if (cpumask_empty(mm_cpumask(mm)))
-		return;
-
-	/*
-	 * Allocate the batch cpumask on first use. Fall back to an immediate
-	 * IPI for this mm in case of failure.
-	 */
-	if (!cpumask_available(batch->cpumask) &&
-	    !zalloc_cpumask_var(&batch->cpumask, GFP_ATOMIC)) {
-		sme_do_dvmsync(mm_cpumask(mm));
-		return;
-	}
-
-	cpumask_or(batch->cpumask, batch->cpumask, mm_cpumask(mm));
-}
-
-static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch)
-{
-	if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714))
-		return;
-
-	if (!cpumask_available(batch->cpumask))
-		return;
-
-	sme_do_dvmsync(batch->cpumask);
-	cpumask_clear(batch->cpumask);
+	sme_do_dvmsync(&sme_active_cpus);
 }

▲ 这段改动是机制成立的关键:被删掉的 dsb(ish) 正是“每批一次”的屏障;被删掉的 zalloc_cpumask_var(GFP_ATOMIC) 是每任务首次使用时的原子分配(批量刷热路径上不可忽略)。sme_dvmsync_batch() 精简到只剩一次对全局 sme_active_cpus 的广播——因为该掩码由 sme_set_active() 在程序序中先于用户态 SME 访问更新(见下段),读取它无需再排序。

关键代码片段(二):全局掩码在 SME 用户态进出时维护
@@ arch/arm64/kernel/fpsimd.c
 static cpumask_t sme_dvmsync_cpus;
+cpumask_t sme_active_cpus;
 ...
 	cpumask_set_cpu(cpu, mm_cpumask(current->mm));
+	cpumask_set_cpu(cpu, &sme_active_cpus);
 	/*
 	 * A subsequent (post ERET) SME access may use a stale address
 	 * translation. On C1-Pro, a TLBI+DSB on a different CPU will wait for
-	 * the completion of cpumask_set_cpu() above as it appears in program
-	 * order before the SME access. The post-TLBI+DSB read of mm_cpumask()
-	 * will lead to the IPI being issued.
+	 * the completion of the cpumask_set_cpu() operations above as they
+	 * appear in program order before the SME access. The post-TLBI+DSB
+	 * read of mm_cpumask() or sme_active_cpus will lead to the IPI being
+	 * issued.
 	 *
 	 * https://lore.kernel.org/r/ablEXwhfKyJW1i7l@J2N7QTR9R3
 	 */
@@
 	cpumask_clear_cpu(cpu, mm_cpumask(current->mm));
+	cpumask_clear_cpu(cpu, &sme_active_cpus);

▲ 这段保证新掩码的正确性:sme_set_active() 在返回 SME 用户态(ERET)之前置位 sme_active_cpus,程序序保证该置位对其它 CPU“后做 TLBI+DSB 的读取”可见——因此批量路径读到的掩码必然包含真正需要打断的 CPU,无需再用每批 DSB 去排序。sme_clear_active() 在异常进入 EL1 时清位(此时 IESB=1 已保证 SME 访问完成)。注释同步更新,把两种读取都写进文档。

📈 性能影响

提升角度(方法论分类)
主类:on-CPU 计算效率(减屏障指令)——dsb(ish) 是一条全系统屏障:执行时要等待此前所有 outstanding 的存储/TLBI 完成,期间流水线被卡住。每批一次 DSB 直接从回收热路径上移除,属于纯 on-CPU 的指令/等待开销削减。
次类:开销降低(消除冗余记账)——同时删掉每批一次的 mm_cpumask() 读取、每任务首次的 zalloc_cpumask_var(GFP_ATOMIC) 分配,以及 arch_dup/release_tlbbatch_mask 的任务复制/释放管理。这些都属于 Brendan Gregg on-CPU vs off-CPU 视角里的 on-CPU 开销(屏障等待 + 指令执行),不涉及 off-CPU 锁/I/O 等待。
受益场景(解读(AI 分析))
从改动路径反推(框架 A:arch_tlbbatch_add_pending/arch_tlbbatch_flush 批量刷 → kswapd/直接回收 unmap):
① C1-Pro(带 erratum 4193714)上的内存回收负载受益最大:旧路径每批白付一次 DSB,回收批量越小、越频繁,DSB 占比越高——这正是 kswapd 的典型形态(回收线程批量不大、持续进行),与合入说明的 30% 回归方向一致。
② 即便无 SME 进程在跑也受益:旧路径的 dsb(ish) 不区分“此刻有没有 SME 活跃 CPU”,只要硬件是 C1-Pro 就每批执行;新路径批量结束统一 sme_dvmsync_batch(),若 sme_active_cpus 为空则 sme_do_dvmsync() 直接返回,连 IPI 都不发。
③ 非 C1-Pro 硬件不受影响:alternative_has_cap_unlikely 运行时检查保证在无勘误 CPU 上整条路径 no-op(零开销)。
实测数据
30% kswapd 性能回归(已修复):来源是 arm64-fixes 合并请求摘要——Linus Torvalds 在 merge commit d96fcfe1b7f9 中转述 arm64 维护者 Will Deacon 的 pull request 说明:“Fix 30% kswapd performance regression introduced by C1-Pro SME erratum workaround”(修复 C1-Pro SME erratum workaround 引入的 30% kswapd 性能回归)。这是本补丁修复的目标幅度,属维护者在合入请求中自报的回归量,未附独立 benchmark 场景/环境/归一化细节,未独立验证。
本补丁自身未提供独立基准:commit message 只描述机制(移除了每批 DSB 以恢复批量收益),未给出带环境的 before/after 数据。
逻辑分析(解读(AI 分析)):修复收益近似等于“回收热路径上每批一次 DSB 屏障等待 × 回收批次频率”。在内存压力持续、回收批次频繁的负载上,该开销占 kswapd 时间相当比例(合入说明量化约 30%);在内存充足、回收稀发的负载上近乎无感。本报告不据此编造更细的百分比。

🔄 方案演进

本补丁是单版本回归修复(无 v1→v2 系列演进),但“erratum workaround → 性能回归 → 本修复”的脉络值得记录(基于本地内核 git 提交链,真实可查):

修复链时间线
0baba94a9779(2026-04,C1-Pro SME DVMSync erratum workaround):引入 ARM64_ERRATUM_4193714 + sme_set_active()/sme_clear_active()(mm_cpumask 跟踪)+ sme_dvmsync_add_pending() 批量记账,解决 C1-Pro 提前确认 DVMSync 的问题。
本补丁 534eb6940a89(2026-06-10):发现 workaround 的批量路径“每批一次 DSB”打散批量收益,引入全局 sme_active_cpus 掩码替代逐 mm 采样,删除批量记账与 per-task cpumask 生命周期管理(+17/-87 行)。
合入:2026-07-10 经 arm64-fixes 标签合入主线(merge d96fcfe1b7f9,随 v7.2-rc3 发布),与同批的“unmap_hotplug TLB flush 优化”(ff4c5a0de1f2)一起进入 Linux 7.2。
设计权衡 / 讨论推进
从 commit message 署名链可见 review 形态:arm64 维护者 Will Deacon 以 Cc + 最终 Signed-off-by 承接,Google 的 Joshua Liu 提供 Tested-by——说明该修复经过维护者审核与 Google 实测。核心设计权衡是:用“全局超集掩码”换“每批 DSB 移除”——sme_active_cpus 会打断“运行任何 SME 进程的 CPU”(略宽于“被 unmap 的 mm 所在 CPU”),但在批量刷场景下只多打断 SME 活跃的少数 CPU,远小于每批一次全系统屏障的成本,是明显划算的取舍。
数据源说明:本环境 lore MCP 查询持续超时不可用,未能回溯该 commit 对应的补丁系列邮件线程(v1/v2 演进与完整 review 讨论无法独立确认)。上述演进/讨论信息来自本地内核 git(commit message 的 Fixes: / Tested-by: / Signed-off-by 链 + git log 提交链 + arm64-fixes 合并请求摘要),是合入主线的权威记录。

⚠️ 风险与局限

收益成立的前提
① 仅 C1-Pro(ARM64_ERRATUM_4193714 + SME)硬件有收益;非该硬件(alternative_has_cap_unlikely 运行时为假)整条 sme_dvmsync_batch() no-op,行为与修复前一致、零开销。
② 收益在内存回收(批量刷)负载上体现;非回收负载该路径不被高频触发,收益接近 0。
③ 正确性前提:sme_active_cpus 必须与“用户态 SME 访问”在程序序上正确排序(置位先于 ERET 后的 SME 访问)——这正是 sme_set_active() 注释固化的架构约定(引用 lore 链接 abLEXwhfKyJW1i7l),是这类 workaround 的一贯做法。
生产落地影响
纯内核改动(arm64 arch 层),无新 Kconfig 开关、无 sysctl、无默认值/迁移路径问题,随主线 7.2 发布即可用。删除的 arch_dup_tlbbatch_mask/arch_release_tlbbatch_mask 是内部实现,无外部 ABI 影响。运维可观测性:回归修复后,回收热路径上的 DSB 屏障应显著减少(可用 perf 统计 barrier 相关事件或对比回收吞吐);若仍观察到回收变慢,可从 sme_active_cpus 是否异常偏大(IPI 广播面变宽)入手排查。
生态 / 兼容性
仅影响 arm64;非批量路径 sme_dvmsync(mm) 仍用 mm_cpumask(mm)(更精确的逐 mm 打断)未改,行为不变。新增的 sme_active_cpus 是全局变量(非 static),供 tlbflush.h 内联函数引用,链接范围内一致,无跨模块耦合风险。对 KVM/虚拟化路径无直接改动。
review 质疑(可追溯项)
lore MCP 本环境不可用,完整 review 讨论无法回溯。commit message 署名链显示:arm64 维护者 Will Deacon 参与(Cc + 最终 Signed-off-by),Google 的 Joshua Liu 提供 Tested-by——说明修复经维护者审核并有外部实测背书;未发现公开 NACK。最值得留意的设计质疑(推测):用超集掩码放宽 IPI 目标面,是否会在“大量 CPU 同时跑 SME 进程”时放大 IPI 广播——但这只影响 SME 活跃 CPU 子集,且批量路径本就低频(batch 结束一次),风险可控。
严重度:MINOR · 落地场景:最需关注的是“收益仅限 C1-Pro SME 硬件 + 内存回收负载”,其它平台/负载为 no-op;整体是低风险的纯删减回归修复,无配置门槛、无破坏性变化。

🔗 交叉引用

📌 关联工作 / 系列相关补丁
arm64: errata: Work around early CME DVMSync acknowledgement(0baba94a9779) — 本补丁 Fixes: 的被修 commit,引入 C1-Pro DVMSync workaround 与批量记账
Merge tag 'arm64-fixes'(d96fcfe1b7f9) — 合入请求摘要,含 “Fix 30% kswapd performance regression” 的 30% 数据出处
本补丁 534eb6940a89(git.kernel.org) — arm64: Avoid eager DVMSync reclaim batches with C1-Pro SME erratum
sme_active_cpus 注释引用的 lore 链接(Message-ID: abLEXwhfKyJW1i7l@J2N7QTR9R3) — C1-Pro DVMSync 早确认问题的原始技术说明(代码内注释引用;lore.kernel.org 对 curl 有 Anubis 反爬,浏览器可访问)
同目录相关报告:arm64/mm 优化 unmap_hotplug TLB flush(ff4c5a0de1f2) — 与本文同属 arm64 TLB 刷效率主题(修复型优化),且与本补丁同批合入 Linux 7.2
⚠️ 免责声明

本站内容均由 AI 基于公开知识辅助生成,仅供学习参考,请勿直接引用作为依据。作者不对信息的准确性、完整性及适用性作保证,亦不对因使用本站内容产生的任何损失承担责任。