2026-07-09 内核社区日报

今日内存子系统的两个关注点:David Hildenbrand 继续清理 x86 早启动页表基础设施,去除 vmemmap 上的 SECTION_INFO/MIX_SECTION_INFO 标记——这是 memblock 废弃 SECTION_IN_PAGE_FLAGS 之后的自然收尾;另一个是 Ackerley Tng 提交了 HugeTLB 分配失败路径的完整错误修复系列(v2),并附带一套自制的可重构测试用例。此外,alloc_tag IOCTL 接口进入 v8Mike Rapoport 开始给膨胀的 internal.h“瘦身”

🧠 重点 · 内存

1. David Hildenbrand 清理 x86 早启动页表:去除 vmemmap SECTION_INFO / MIX_SECTION_INFO 标记

发生了什么:
David Hildenbrand 发布了一个 10 补片系列,统一/清理 x86 早启动期间页表内存的释放与标记逻辑。核心改动包括:

(1)free_reserved_pages() 通用化——将 arch/x86 的实现移到 page_alloc.c,去掉 x86 专有变体;同步清理 free_pagetable() 的无用 order 参数(调用方全部传 0)。

(2)停止在 vmemmap 区域的 page table pages 上标记 SECTION_INFO——SECTION_INFO 原是 2015 年 commit 04753278769f(memory hotplug 注册 section/node id)引入的 bootmem 信息,其唯一用途是让 hotplug 在卸载 section 时识别页表是否由 bootmem 分配。如今该逻辑已被废弃(memblock 已不再需要 SECTION_IN_PAGE_FLAGS),去掉标记不会产生功能差异。

(3)停止在 boot page tables 上标记 MIX_SECTION_INFO——MIX_SECTION_INFO 是对 SECTION_INFO 的补充,同样用于识别来自 bootmem 的 page table pages,现在已不再需要。清理后早启动页表在释放时不再触发无关的 section 记账。

为什么重要:
这是对 bootmem registration 机制的进一步拆除。自 5 月 mm/bootmem_info 连续去除 NODE_INFOMIX_SECTION_INFO 之后,本系列清除了剩余的唯一用户。代码更易维护,也为未来对 section_deactivate() 的简化铺平了路。经 Reviewed-by 确认,改动安全。

  • [来源](https://lore.kernel.org/linux-mm/dd1bfb6a-4e3d-40dc-b1b1-6c169279b366@kernel.org/)
  • [来源](https://lore.kernel.org/linux-mm/e57dc812-8ada-4bed-9d6d-cb9a6dc5e9ea@kernel.org/)
  • 提出移除不再需要的 bootmem registration 机制:memory section 只能在其所有页面均可离线时才能下线,且 vmemmap 是唯一支持的热插拔路径,因此曾经用于记录 memmap 所属段的标记(SECTION_INFO)及其关联的引用计数已无必要;计划在 free_vmemmap_pages() 中直接释放保留页面,简化内存热插拔路径。来源
  • [来源](https://lore.kernel.org/linux-mm/521fbaf3-ea07-4794-8bec-ac12020e04d7@kernel.org/)

2. alloc_tag /proc/allocinfo 迎来 IOCTL 接口(v8),支持模块/函数/大小/精度过滤

发生了什么:
Suren Baghdasaryan 与 Abhishek Bapat 推送了 alloc_tag IOCTL 的第 8 版。该系列为 /proc/allocinfo 增加了一套 IOCTL 命令,替代传统的读取全部输出再 grep 的做法:

  • ALLOCINFO_IOC_CONTENT_ID —— 获取内容标识符,快速判断 file content 是否变化。
  • 按 tag 的 module namefunction namefile nameline number 做精确过滤。
  • total bytes allocated 区间([min_size, max_size])做大小过滤。
  • 精度(accuracy) 做过滤——精度衡量 alloc_tag 记录的可靠程度。

v8 也包含 kselftest 测试覆盖以上全部过滤功能。

为什么重要:
Memory Allocation Profiling(CONFIG_MEM_ALLOC_PROFILING)已用于生产环境定位内存泄漏,但 /proc/allocinfo 的全量 dump 在大型机器上可达数百 MB。有了 IOCTL 过滤后,用户程序可以直接在 kernel 侧筛选出关心的 allocation sites,大幅降低用户态解析开销和 procfs 读放大。这是该特性进入生产就绪的关键一步。

[来源](https://lore.kernel.org/linux-mm/cover.1783532853.git.abhishekbapat@google.com/)

3. HugeTLB 分配失败路径修复(v2)与可重现测试用例

发生了什么:
Ackerley Tng 提交了 HugeTLB folio 分配失败路径的修复 v2(5 个补丁),并提及可重现测试用例(单独发送)。修复覆盖以下问题:

  1. subpool usage leak:当 alloc_hugetlb_folio() 在 buddy 分配或 hugetlb cgroup 充电失败且 gbl_chg == 1 时,错误路径未正确恢复 subpool 的 used_hpages 计数;通过无条件调用 hugepage_subpool_put_pages()(若 map_chg 为真)修复。

  2. folio refcount mismatch on memcg charge failuremem_cgroup_charge_hugetlb() 失败后 folio refcount 为 1(由 folio_ref_unfreeze() 设置),但 free_huge_folio() 预期从池中取出时的 refcount 为 0,导致 VM_BUG_ON_FOLIO 或分配器状态损坏。修复将 free_huge_folio() 替换为 folio_put(),正确释放引用。

  3. Return -ENOSPC on memcg charge failure to prevent infinite loop:原本返回 -ENOMEM(映射为 VM_FAULT_OOM),由于 HugeTLB 物理分配使用 __GFP_RETRY_MAYFAIL 绕过 OOM killer,导致 #PF 处理程序无限重试。改为返回 -ENOSPC(映射为 VM_FAULT_SIGBUS),干净地终止进程。

  4. Reservation leak:将 mem_cgroup_charge_hugetlb() 提前到 vma_commit_reservation() 之前执行,若充电失败则保留尚未提交,避免 resv_huge_pages 泄漏。

为什么重要:
这些修复解决了 HugeTLB 分配失败路径上可能导致系统不稳定(refcount 错乱)、无限循环(OOM 绕过)或资源泄漏(subpool 计数、reservation)的关键缺陷。可重现测试用例有助于社区验证此类边界情况。

[来源](https://lore.kernel.org/linux-mm/20260708-hugetlb-alloc-failure-fixes-v2-0-c7f27cbb462b@google.com/)

4. Mike Rapoport 拆分膨胀的 mm/internal.h

发生了什么:
Mike Rapoport(Microsoft)发出 3 个补片,将 mm/internal.h 中的声明拆分为三个独立的头文件:

  • mm/mm_init.h —— 包含 mm_init.cmemblock.c 相关的声明。
  • mm/sparse.h —— SPARSE / SPARSE_VMEMMAP 内存模型相关声明。
  • mm/vmalloc.h —— vmalloc 相关声明。

拆分后,原本 include mm/internal.h 的 .c 文件只需 include 需要的子头,不降低编译速度。不引入功能变更。

为什么重要:
mm/internal.h 是内核中最大的 internal 头之一,任何修改都导致大量文件重编译。拆分后,涉及 memblock、sparse、vmalloc 的改动触发 rebuild 的范围大幅缩小。这是 MM 子系统代码组织的一次基础设施改进,Lorenzo Stoakes 和 Vlastimil Babka 在回复中表示支持,并提醒注意 mm 内部循环依赖问题。

[来源](https://lore.kernel.org/linux-mm/20260709-internal-h-v1-0-956d5092782d@kernel.org/)

5. 匿名 mTHP folio 加入 deferred split 列表(v1 讨论进展)

发生了什么:
Joanne Koong 提交了 RFC 级补丁:当匿名 PMD 级 THP 被拆分时,系统自动将 underused 的 mTHP(如 16K/32K/64K folio)加入 deferred_split 列表。但 David Hildenbrand 在 review 中提出顾虑:

  • 对于小尺寸 mTHP(< PMD size),deferred_split 的设计初衷是应对映射不可逆的情况(如 mlock/vmapped folio),但匿名 mTHP 的拆分是可选操作,直接加入 deferred_split 会增加不必要的扫描开销。
  • David 指出,该改动更合适的做法是将 mTHP 的拆分决策交给 khugepaged 或类似的节俭回收机制,而不是被动挂入 split list。

作者表示将评估这些意见,考虑转到不同的设计方向。

为什么重要:
该讨论关系到 mTHP 生命周期管理的设计:是否应对所有部分已使用的有序 folio 一律做 deferred split?David 的反对意见意味着社区仍在探索更精细的策略——可能只对 mlockmadvise 场景下的 mTHP 启动 deferred split。这对移动端 mTHP 落地有影响(手机场景下低内存压力下大量 underused mTHP 若不及时 split,会贡献不可回收内存),但具体方案未定。

[来源](https://lore.kernel.org/linux-mm/CAJnrk1Zc1cZa5edmb6Z18nO3xfLBww-7wobxkOe2OJmWE9qzpA@mail.gmail.com/)

6. vmalloc 连续映射提速 v6:arm64 CONT_PTE batching、page table walk 重构、VM 对齐

发生了什么:
Wen Jiang 和 Barry Song(Xiaomi)提交 vmalloc 连续内存映射加速系列 v6 共 6 个补片。针对 ioremap、vmalloc、vmap 调用中物理页连续的情况,通过批量映射减少 page table walk 和 TLB flush 开销:

(1)在 arm64 上,对小于 PMD 但对齐 CONT_PTE_SIZE(64K 页时为 2MB)的连续区域,一次 setup 整个 CONT_PTE block,而非逐 PTE。

(2)新增 arch_vmap_pte_range_map_size 返回可 batch 的映射大小(ARM64 上可跨越多个 CONT_PTE block)。

(3)提取 vmap_set_ptes() 通用函数,统一处理 CONT_PTE / 普通 PTE 映射。

(4)重构 vmap_pages_range_noflush_walk(),支持 page_shift 可变(即一次映射更大的 page chunk),消除不必要的 PTE table rewalk。

(5)vmap() 调用方:当传入的 struct page ** 包含高 order 连续页时,自动利用上述 batch 能力映射。

(6)vm_area 对齐:尝试将分配到的 VA 对齐到 PMD_SHIFT 或架构可 batch mapping 的大小,为 batch mapping 创造条件。

为什么重要:
vmalloc/vmap 频繁用于大量连续物理页的映射(如 GPU CPU map、DMA buf)。按 batch CONT_PTE block 一次处理 4-16 个 PTE,可显著降低 PTE setup 次数与相应的 TLB flush 延迟。在 v6 中,性能数据未直接给出,但来自同系列前版本的讨论指出,对 512 个连续页的高频 vmap/unmap 场景,延迟可以从几个 ms 降到几百 µs。本系列仍在 review,但核心方案已获得初步认可。

[来源](https://lore.kernel.org/linux-mm/20260709073823.6643-1-jiangwen6@xiaomi.com/)

🔧 其它子系统

文件系统 / VFS

  • Christian Brauner 提交 3 个补片的系列:将 binfmt_misc 的 entry 列表转为 hlist;为 handler lookup 使用 RCU;注释对 ->enabled 的竞争访问。来源
  • ext4 延迟 I/O 完成处的 use-after-free 修复:Longxing Li 提交单补片修复 ext4 延迟 I/O 完成路径中的 use-after-free——ext4_add_complete_io() 将 inode 的 i_rsv_conversion_work 加入工作队列时未持有额外引用。若 inode 在延迟工作运行前被 unlink 并 evict,随后 ext4_do_flush_completed_IO() 访问 io_end->inode 时即触发 use-after-free。修复在首次加入队列时用 igrab() 增加 inode 引用(仅列表为空时),在处理完所有 io_end 后由 ext4_do_flush_completed_IO()iput() 释放,并由 per-inode 位 i_rsv_need_iput 跟踪。来源

内核基础设施

  • proactive reclaim 的冻结检测 v2:Richard Chang 提交 v2,在 try_to_free_pages() 的重入位置加入 try_to_freeze() 检查。当系统进入 suspend(或 freeze 状态)时,proactive reclaim 线程立即退出,避免因等待 swap/reclaim 而延迟 suspend 流程。来源

👀 值得追的讨论 / Patch

1. zswap_shrinker_count() 的 ratelimited stats flush 争议

发生了什么:
Yunzhao Li 提交了补丁,在 zswap_shrinker_count() 中使用 mem_cgroup_flush_stats_ratelimited() 替代同步 flush,以减少高并发场景下 cgroup_rstat_lock 的争用。Johannes Weiner 给出了 Acked-by。Yosry Ahmed 回复指出,该 ratelimited 方式(2 秒间隔)在 vmscan 路径中可能引起提前 OOM(正在内部调查),因此建议改用 per-memcg atomics 替代 rstat 统计,从根本上消除 flush 开销。Jesper Dangaard Brouer 则认为该补丁只影响启用了 shrinker 的 zswap 路径,不应被阻塞,可以先应用后续再改进。Yosry 坚持认为应先尝试更彻底的方案,避免铤而走险。讨论未就解决方案达成一致。

为什么重要:
该讨论关乎 zswap shrinker 在高并发内存回收场景下的锁竞争缓解策略,以及 ratelimited flush 可能带来的内存回收精度风险,直接影响大内存系统上 zswap 的性能与正确性。

来源

2. KHO 支持保留未拆分 high-order pages 的 RFC 讨论持续

发生了什么:
Pranjal Shrivastava 发布了 RFC 补丁系列,旨在让 KHO 能够正确保留和恢复未拆分的 high-order pages(如 DMA 缓冲区)。当前 KHO 的 restore 会将所有多页块视为 split 页初始化引用计数,导致高 order 页的 tail page 引用计数被错误设置,触发调试检查或内存泄漏。讨论中,Pratyush Yadav 建议新增专门的 kho_preserve/restore_page_contig 类 API,让调用方根据自身用途选择正确的 restore 函数,避免在 KHO 元数据中存储页面类型信息。Samiullah Khawaja 和 Pranjal 均表示同意,并讨论了 API 命名。Pranjal 也同意移除此前提出的 kho_split_preserved_pages() 辅助函数,因为显式 restore API 可以避免分割需求。

为什么重要:
该系列是 KHO 支持跨内核重启保留 DMA 缓冲区等连续分配的关键,扩展当前仅支持 split 页的保留模型,使需要高 order 非 compound 页的子系统(如 DMA 分配器)能从 live update 中受益。

来源

⚡ 一句话速览

  • zone-device memmap 初始化优化 v6:Li Zhe 提交 8 补片系列,通过模板化 head/tail page 拷贝和 memcpy_nt()(non-temporal copy)加速 ZONE_DEVICE page 初始化。来源
  • DAMON 迁移热页时阻止回退到非目标节点:补丁在 migrate_pages() 所分配的 destination folio 中加入 __GFP_THISNODE 标志,防止当目标节点(如 fast tier node 0)内存不足时分配器降级到其他节点(如 CPU-less slow tier node 1),从而确保热页实际被迁移到指定 NUMA 节点,而非仅移动到同一节点的不同物理页。来源
  • selftests/mm 迁移 benchmark memleak 修复:Hongfu Li 修复 run_migration_benchmark() 中多条 early return 路径遗漏 hmm_buffer_free() 导致的内存泄漏,改用单一 cleanup 标签。问题由 Sashiko 发现。来源
  • mm/page_owner 文档构建警告修复:Randy Dunlap 修复 Documentation/mm/page_owner.rst 中 literal block 和缩进问题,以避免文档构建时出现 “Literal block expected; none found.” 警告。来源
  • nodemask 改用 nr_node_ids 而非 MAX_NUMNODES:Li Rongqing 提交细微修正,__nodemask_pr_numnodes() 在打印 nodemask 时使用实际在线节点数而非编译期常量 MAX_NUMNODES,避免在非连续节点配置下打印多余逗号。来源
  • arm64 移除无效果的 early_ioremap_reset() 调用:在 arm64 平台上,__early_set_fixmap__late_set_fixmap__late_clear_fixmap 均定义为 __set_fixmap,调用 early_ioremap_reset() 不会产生实际效果。该补丁删除该调用及相关宏,无功能变化,仅清理冗余代码。来源
  • cgroup/dmem 的 dmem.high 软限制实现:Jing Wu 提交 v7,为 cgroup dmem(device memory controller)添加 dmem.high 软限制,当超过水位线时触发 proactive reclaim。来源
  • mm: Standardize printing for pgtable entries v3:Anshuman Khandual 提交 v3,统一 bad page map 等报告中的页表项打印格式,减少架构特有实现、提高可读性。来源
  • mm/snapshot_page 修复 order-1 folio 的 __page_2 读取:Aboorva Devarajan 提交 v2 修复,当 folio 的 order == 1 时(即 2 个 pages),__page_2 非法越界——补丁跳过对超界 page 字段的访问。来源