mm: Unconditional per-VMA locks and cleanups

内存管理 · per-VMA 锁全配置可用化 · mmap_lock 热路径绕开

💡 一句话总结

在缺页、Binder、TCP 这类频繁按地址查找/读 VMA 的负载下,内核传统的 mmap_read_lock() 每次拿**整个地址空间的全局读锁**,多线程并发时成为争用点。作者把 per-VMA 锁从"仅部分架构 + SMP + MMU"扩展到**全配置可用**,并引入 vma_start_read_unlocked() 新 API,让热路径**彻底绕开 mmap_lock**——减少全局锁争用,同时砍掉大量 #ifdef 分支。补丁未提供基准数据,收益为逻辑分析。

📋 补丁基本信息

项目内容
补丁类型优化(性能 + 简化)
性能类别锁竞争(锁粒度细化:全局 mmap_lock → 单 VMA 锁)
状态In Review(v3)
当前版本v3 · 当前版链接
版本演进 v2(Dave Hansen 原稿,2026-06)→ v3(Suren 接手,2026-08-02)
作者机构Suren Baghdasaryan (Google)
提交日期2026-08-02
改动范围28 文件,+13/-317 行
核心函数vma_start_read_unlocked() / lock_vma_under_rcu()
原始链接lore Message-ID

📊 速览卡片

核心机制
锁全配置化
优化目标
绕开 mmap_lock
适用场景
VMA 高频访问
实测提升
未提供

🎯 解决什么问题

背景 / 原始动机
作者在做 x86 shadow stack 代码时,**避免 mmap_lock 递归锁问题非常痛苦**(cover letter 原话)。他想用 per-VMA 锁替代(细粒度、只锁单个 VMA 而非整个地址空间),但发现 **per-VMA 锁只在部分架构 + SMP + MMU 配置下可用**,导致 Binder、TCP 等通用代码无法依赖它。
系统层面:per-VMA 锁可用性受架构/配置限制
执行路径:任何按地址访问 VMA 的路径(vma_lookup()、page fault 的 find_vma())都要先拿 mmap_read_lock(mm)——这把**整个地址空间的读写锁**。
机制缺陷:per-VMA 锁通过 ARCH_SUPPORTS_PER_VMA_LOCK Kconfig 门槛控制,仅 arm/arm64/x86 等少数架构且要求 SMP+MMU。通用代码要么不能用它(退回 mmap_lock),要么到处 #ifdef。
为什么能解:per-VMA 锁的底层依赖(RCU、maple tree、refcount)在无 SMP/MMU 下也能工作,所以移除门槛在技术上是安全的。
场景层面:执行路径 → 高频场景 → 为什么遇到缺陷(框架 A)
执行路径:page fault 的 find_vma() / vma_lookup() → Binder 按地址找 VMA → TCP 某些路径。
高频触发场景:多线程进程密集访问内存(数据库、容器运行时、高并发 IPC)。每线程每次缺页/找 VMA 都要拿 mmap_lock 读锁,**多线程并发时所有线程排队等这把全局锁**。
为什么该场景遇到缺陷:mmap_lock 粒度是整个地址空间,即使各线程访问的是**不同 VMA**,也要串行等同一把锁——争用随线程数放大。per-VMA 锁只锁单 VMA,并发访问不同 VMA 可并行。
受影响负载:page fault 密集 / Binder IPC / TCP 高并发 · 因果:per-VMA 锁全配置化 → 通用代码可绕开 mmap_lock → 减少全局锁争用

🧩 核心机制

核心逻辑点(框架 B 识别):① 移除架构门槛 ② 引入新 API ③ 调用方迁移——三者构成"让 per-VMA 锁全配置可用 + fast path 绕开 mmap_lock"的完整链条。

从系统层面看
逻辑点①:移除架构门槛——删除 arm/arm64/loongarch/powerpc/riscv/s390/x86 各 Kconfig 的 ARCH_SUPPORTS_PER_VMA_LOCK,改为 mm/Kconfig 统一声明(RCU/maple tree/refcount 可在无 SMP/MMU 下工作)。这是"全配置可用"的前提。

逻辑点②:新 API vma_start_read_unlocked()——把最常见惯用法 mmap_read_lock(mm); vma = vma_lookup(); ...; mmap_read_unlock(mm) 简化为 vma = vma_start_read_unlocked(mm, addr); ...; vma_end_read(vma),fast path 完全不碰 mmap_lock。这是"绕开"的实现。

逻辑点③:调用方迁移——Binder 一处、TCP 一处改用新 API(cover letter 明说 cc 这两个子系统的原因)。这是"落地"。
逻辑点操作目的
① 架构解耦移除 ARCH_SUPPORTS_PER_VMA_LOCKper-VMA 锁全配置可用(前提)
② 新 APIvma_start_read_unlocked()fast path 绕开 mmap_lock(实现)
③ 调用方迁移Binder / TCP 改用新 API消除全局锁争用(落地)
mmap_lock 全局锁 vs per-VMA 锁 before/after 对比图
图 1:补丁前后对比——左边多线程访问不同 VMA 也串行排队等 mmap_lock 全局锁;右边 per-VMA 锁只锁单个 VMA,并发访问不同 VMA 可并行,且 vma_start_read_unlocked() 在 fast path 彻底绕开 mmap_lock。
来源:基于 lore 真实补丁 diff 绘制

🔬 关键代码

按核心逻辑点组织,每点选最能体现的 diff(非按改动大小):

逻辑点①:移除架构门槛(体现"全配置可用")
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
@@ -81,7 +81,6 @@ config ARM64
 	select ARCH_HAS_PTE_PROTNONE
 	select ARCH_SUPPORTS_NUMA_BALANCING
 	select ARCH_SUPPORTS_PAGE_TABLE_CHECK
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select ARCH_SUPPORTS_HUGE_PFNMAP if TRANSPARENT_HUGEPAGE

▲ 为什么这是核心:移除架构门槛是"让通用代码能依赖 per-VMA 锁"的前提——28 文件里 Kconfig 是主体(-317 行的主要来源)。

逻辑点②:新 API 的调用方用法(体现"绕开 mmap_lock")
// 传统惯用法(每次拿全局锁):
	mmap_read_lock(mm);
	vma = vma_lookup(mm, address);
	// fiddle with vma
	mmap_read_unlock(mm);

// 新 API(fast path 绕开 mmap_lock):
	vma = vma_start_read_unlocked(mm, address);
	// fiddle with vma
	vma_end_read(vma);

▲ 为什么这是核心:新 API 免去"先拿 mmap_lock 再 vma_lookup"两步,是"绕开全局锁"的实现关键。

📈 性能影响

场景/用例运行环境改进前改进后
多线程并发访问 VMA(page fault / Binder / TCP)多核mmap_lock 全局读锁争用per-VMA 锁细粒度

说明:**补丁未提供基准数据**。收益为逻辑分析(解读(AI 分析)),依据框架 A 从执行路径推断:
① off-CPU 收益:mmap_lock 全局读锁在多线程并发时是等待点,per-VMA 锁粒度更细 → 减少锁等待(off-CPU 收益)
② on-CPU 收益:新 API 免去 vma_lookup 后的再次加锁 → 减指令(on-CPU 收益)
收益放大场景:线程数越多、VMA 访问越频繁,争用越大 → 收益越明显。单线程或低频访问场景收益趋近于零(no-op)。

🔄 方案演进

v2 → v3 演进脉络
v2(Dave Hansen,2026-06):提出 per-VMA 锁全配置可用化,5 个 patch(含 binder/tcp 迁移)
v3(Suren,2026-08-02):按 review 调整——移除 bpf/stackmap.c 的 CONFIG_PER_VMA_LOCK 用法、binder 加 binder_alloc_is_mapped() 检查、Rust 版 vma_start_read_unlocked()
讨论焦点(每条带来源)
Barry Song 提议 page fault 路径推广(CAGsJ_4wXGf90BMoJp3_z8G4fMTAgED88RXvbp6C4Gf+iy_2nvg@mail.gmail.com):等 lock_vma_under_rcu() 失败后重试 VMA 锁而非退回 mmap_lock。Suren 回应(cover letter 内):Matthew(Wilcox)正在做 page-fault 代码统一重构,Barry 的建议在重构后更简单。

⚠️ 风险与局限

潜在回归 / 并发 / 边界
内存开销:!MMU / !SMP 构建的 VMA 变大(cover letter 明说,解读(AI 分析):对无 MMU 的小内存设备可能不划算)。
正确性风险:per-VMA 锁依赖 RCU + maple tree + refcount,在这些架构/配置下首次启用,需验证并发正确性(解读(AI 分析))。
待决:page fault 路径全面推广(Barry 提议,待 Matthew 重构)。
严重度:MINOR(内存开销在无 MMU 设备上)· review 质疑:Barry 提议 page fault 推广,作者回应待重构后

🔗 交叉引用

📌 关联工作
arm64/x86: use VMA lock for kernel faults — 同日报另一篇,per-VMA 锁应用到内核态缺页(同一方向的另一切面)
PATCH v3 0/5 cover letter — 系列问题/动机/方案/演进/讨论摘要

✅ 关键洞察

  • 发现:per-VMA 锁全配置可用化 + 新 API 绕开 mmap_lock,是消除 VMA 热路径全局锁争用的关键步骤;顺带简化大量 #ifdef
  • 证据:补丁未提供基准;收益基于"锁粒度更细"的逻辑分析(解读(AI 分析),框架 A 推断)
  • 边界:!MMU / !SMP 构建 VMA 变大;收益依赖 VMA 高频访问 + 多线程场景(单线程近 no-op)
  • 风险 / 建议:需关注全配置启用的并发正确性;page fault 全面推广待 Matthew 重构后
⚠️ 免责声明

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