2026-07-07 内核社区日报
今天社区活跃,多个新机制登场:Baoquan He 提出将 zswap entry 直接存入 swap table 的激进方案,取代现有 xarray;Usama Arif 全面修复 device-private PMD 在各 walk 路径中的遗漏;Nico Pache 的 khugepaged 代码清理也收到积极反馈。
🧠 重点 · 内存
1. khugepaged 代码清理:8 个补丁引入辅助函数、修复注释与锁顺序
发生了什么:
Nico Pache 提交了 8 个补丁,对 khugepaged 进行系统性清理:
(1)将每次扫描前的状态清除逻辑抽取为 collapse_control_init_scan() 辅助函数。
(2)将“PTE 是否 young”的判断提取为 collapse_is_young() 辅助函数。
(3)引入 count_collapse_event() 辅助函数,封装 PMD 级别 collapse 的事件计数模式。
(4)修复过时的注释。
(5)基于 Lance Yang 的系列并重写,将 PTE 状态检查重构为辅助函数。
(6)调整解锁顺序:在释放 vma write lock 之前先进行 unmap PTE。
(7)澄清关于 max_ptes_none 检查的注释。
(8)文档:在 Documentation/admin-guide/mm/transhuge.rst 中说明 mTHP 统计信息的实际路径。
为什么重要:
这些清理源自 khugepaged mTHP collapse 支持系列评审中的积累,通过引入辅助函数提高代码复用与可读性,并处理了评审中提出的遗留问题。
2. 全面修复 device-private PMD 在 walk 路径中的遗漏
发生了什么:
Usama Arif 提交 3 个补丁,解决 device-private PMD(non-present, non-none 的 PMD swap entry,用于 GPU 等设备内存)在页表遍历中的未处理情况:
(1)queue_folios_pmd()(mempolicy 的 mbind() 路径):pmd_trans_huge_lock() 的 pmd_is_huge() 检查对有 device-private PMD 返回 true,但后续代码未排除该情况,导致对 device-private PMD 调用 page_folio(pmd_page(orig_pmd)),从设备内存页面上取 folio 是未定义行为。补丁在 queue_folios_pmd() 中直接跳过 device-private PMD。
(2)madvise_cold_or_pageout_pte_range()(MADV_COLD/MADV_PAGEOUT 路径):同样走入 pmd_trans_huge_lock(),在处理 !pmd_present() 分支时存在 VM_BUG_ON 断言 migration entry,而 device-private PMD 也是 !pmd_present() 但不是 migration entry,触发 BUG。补丁提前跳过的同时修正断言。
(3)madvise_free_huge_pmd()(MADV_FREE 路径):madvise_free_pte_range() 中 pmd_trans_huge(*pmd) 检查为真,随后调用 madvise_free_huge_pmd(),但该函数未处理 device-private PMD,导致 orig_pmd 作为 device-private 被错误地尝试释放。补丁添加显式跳过。
为什么重要:
这是对 device-private memory(如 CXL 内存、GPU 内存通过 HMM 映射)的 PMD 级别缺页处理补全。当前代码在 pmd_is_huge() 返回 true 的所有路径中都假设 PMD 是 present 或迁移 entry,遗漏了 device-private 这个新类型,导致潜在的 BUG_ON、未定义行为或者内存泄漏。此系列补全了 mempolicy 和 madvise 路径,是为后续 PMD 级 device-private 架构铺路的关键一步。
3. DAMOS_COLLAPSE:DAMON 自动调优 huge page collapsing 讨论继续
发生了什么:
Asier Gutierrez 对 SJ Park 关于 v1 补丁的回复做出回应。该补丁为已有的 DAMOS_COLLAPSE action 引入了自动调优(autotuning),通过 damos_hugepage_mem_bp quota goal 监测 huge page 占总内存的百分比来动态调整 collapsing 的激进程度。讨论涉及 test result 中未达到 5% 目标的原因(可能为 TEMPORAL quota goals 的 bug),以及参数命名统一(quota_percentage_hugepage 改为 hugepage_mem_bp)等技术细节。
为什么重要:
该自动调优机制使 DAMON 能根据系统实际的 huge page 占用比例自动调节 collapse 强度,无需管理员手动介入,从而更灵活地平衡 huge page 收益与内存开销。
4. migrate_device:修复在 non-present PTE 上调用 pte_pfn/pte_dirty
发生了什么:
Kefeng Wang 发现 migrate_vma_collect_pmd() 中存在路径使得 pte_pfn() 和 pte_dirty() 可能在 non-present PTE(例如 device-private entries)上被调用,而这些函数对 non-present PTE 的行为未定义。修复方式为增加 pte_present() 检查,以避免潜在的崩溃和不正确的脏页统计。
为什么重要:
该补丁修复了 migrate_vma_collect_pmd() 中因对 non-present PTE 调用 pte_pfn() 和 pte_dirty() 而可能导致的崩溃或脏页统计错误,提升了代码的健壮性和数据一致性。
5. proactive reclaim:在冻结时提前退出,避免冻结超时
发生了什么:
Richard Chang 提交的补丁使 proactive reclaim(通过 memory.reclaim 或 node reclaim 触发)在收到 pending 信号时提前从内层扫描循环退出。原先只在 user_proactive_reclaim() 的外层循环检查 signal_pending(),但内层循环(shrink_many() 扫描 cgroup、try_to_shrink_lruvec() 回收 folio)可能运行较长时间后才返回外层,导致进程响应冻结信号的延迟。补丁在 should_abort_scan() 中为 proactive reclaim 路径(sc->proactive)添加 signal_pending() 检查,使内层循环能在信号到达后立即中止,快速返回外层循环。
为什么重要:
该延迟会阻塞 freezer(包括 cgroup freezer 和 system suspend),导致冻结超时。补丁确保 proactive reclaim 在冻结信号到达后及时放弃当前扫描,使任务能够及时进入 refrigerator,避免系统冻结失败。
6. [RFC] 存储 zswap entries 在 swap table(Pointer entry)
发生了什么:
Baoquan He 提交了一个 RFC 系列(7 patches),核心想法:用 swap table entry 的低位标记(0b100)表示 “Pointer entry”,将 zswap_entry 的指针直接存入 swap table,从而彻底移除 zswap 的 xarray 索引,所有 zswap 压缩数据的位置都由 swap table 自身记录。
该方案包括:
- 在
swp_entry_t中添加swp_type_pointer,与现有的swp_type_shadow、swp_type_pfn并列。 - 在
struct zswap_entry中保存原始 swap table value(用于 count 等)。 - 在
__swap_cache_do_del_folio写 shadow entry 时,如果 zswap 有数据,则转换为 Pointer entry 写入。 - 修改
__swap_cache_add_check和 add folio 路径,从 Pointer entry 读回 shadow。 - 修改
swp_count()/swp_dup()/swp_put()等操作,使得计数通过 Pointer entry 中的zswap_entry->swp_tb_val获取。 - 最后两个补丁将 zswap 入口查找完全从 xarray 迁移到 swap table 查找,删除 xarray 相关代码。
为什么重要:
这是对 zswap 索引结构的一次根本性重构。当前 zswap 用独立的 xarray 存储 entry,而 swap cache 用另一个 xarray(swap address_space),数据路径上需要两次 xarray 查找。如果成功,将统一交换系统索引,减少缓存缺失和内存开销,并可能简化 future 特性(如 large folio swap-in 的批量查找)。但风险大:现有架构的兼容性、并发语义、错误路径都需要仔细验证,目前只是 RFC 阶段的探索。
7. PMD 级 swap entry:v3 补丁讨论 readahead 与 zswap 处理
发生了什么:
Usama Arif 发布了 PMD 级 swap entry 的 v3 补丁,新增 do_huge_pmd_swap_page() 处理 PMD swap fault。Kairui Song 在审查时指出,该补丁中 PMD swapin 直接调用 swapin_sync() 而未使用 readahead,建议在提交消息中说明原因。Usama 同意添加说明,并解释 readahead 对 PMD 无用。Kairui 进一步指出 zswap 存储的是 order-0 对象,若在 PMD-order swapin 时 zswap 仍有条目,会导致 zswap_load() 失败并留下非 uptodate 的 PMD 级 folio,后续 PTE fault 可能 SIGBUS。Usama 确认应在初始查找时就针对 zswap 做 split fallback,避免该问题。
为什么重要:
讨论明确了两个关键设计决策:PMD swapin 禁用 readahead 以避免破坏 PMD 映射,以及必须在前置检查中处理 zswap 的 per-page 条目,确保从 PMD swap entry 安全回退到 PTE 粒度,从而避免潜在的 SIGBUS 崩溃。
来源
8. 将 zswap entry 直接存入 swap table
发生了什么: Baoquan He 提出 [PATCH] 方案,用 zswap_entry 直接替换 swap_info_struct->swap_map[] 中的 swp_entry_t,取代现有通过 xarray 间接索引的架构。核心改动包括:在 swap_map 中存储 struct zswap_entry * 指针,并在 zswap_store() 中直接写入该指针;zswap_load() 改为从 swap_map 读取指针并调用 zswap_entry_get() 获取引用;zswap_invalidate() 通过 swap_map 指针直接释放 entry。该方案移除了 zswap_tree 全局 xarray 及 swp_entry_t 到 entry 的查找路径,将 zswap_entry 生命周期与 swap slot 绑定。
为什么重要: 消除 xarray 查找的 RCU 锁开销和内存碎片,将 zswap 的存储路径从 O(log n) 降为 O(1),同时减少一次间接内存访问。但该方案要求 zswap_entry 在 swap slot 释放前必须有效,且需处理 swap_map 并发写入(如 swapoff 与 zswap_store 的竞争),目前讨论聚焦于 swap_map 的原子操作与 zswap_entry 引用计数的一致性。
9. Wandun Chen 的 mlock CMA 迁移补丁收到重大审查意见
发生了什么: Wandun Chen 提交了 mm/mlock: migrate folios out of CMA when mlocking a range 补丁 v2 第 4/4 部分。审阅者(Liam Howlett)指出该补丁存在严重问题:大量代码从 mlock_pte_range 直接复制粘贴,导致重复且难以维护;在 mlock.c 中混合 CMA 相关逻辑和迁移等待处理,且通过 compaction_allow_unevictable 系统控制参数来判断是否在 RT 上执行,被认为是不合适的做法;同时缺少注释,对 CMA 页面迁移的前向进度也提出疑问。审阅者明确要求作者去重代码,并认为这不是正确的解决方案。
为什么重要: 该审查阻止了当前实现方式(大量复制粘贴、不合理的功能混杂)被直接合入,迫使补丁作者重新设计更干净的解决方案,从而维护了内核 mm 子系统代码的整洁性、可维护性和性能保证。
10. khugepaged 注释澄清
发生了什么: Nico Pache 提交补丁,在 collapse_scan_pmd 中扩展注释,说明 max_ptes_none 的 bitmap 检查实际在 mthp_collapse() 中执行。补丁注释末尾附有问句“Could be combined with patch 4?”。
为什么重要: 该注释澄清了延迟检查的走向,降低后续开发者理解代码的困惑。
来源
🔧 其它子系统
工具 / 测试
- page_owner 新增 per-fd 过滤:print_mode 和 NUMA 节点:Zhen Ni 提交 v12,允许用户通过打开多个
/sys/kernel/debug/page_owner文件描述符并对其分别设置 print_mode(stack / handle / stack_handle)和 NUMA node 过滤,方便调试不同场景下的内存占用。同时新增了tools/mm/page_owner_filter用户态工具。来源 - alloc_tag 新增 IOCTL 过滤:模块、函数、大小、精度:Abhishek Bapat 提交 v7,扩展
/proc/allocinfo的 ioctl 接口,支持按标签的 module/function/file/line、分配总大小范围、精度值过滤。这对生产环境定位内存泄露很有用。来源 - mseal_test 修复 mmap 返回值检查:longlong yan 修复了
test_seal_zero_address中对mmap返回值的错误判断(应为 MAP_FAILED 而非 NULL)。来源
文件系统 / VFS
- readahead 存储 folio count 并批量加载:David Howells 提交 v5,
readahead_control新增字段记录 folio 总数,让 netfs 子系统可以一次性将所有预读 folio 加载到 folio queue 中,减少逐个处理的开销。来源 - 为 DRM 中 shmem 滥用场景提供包装函数:提供一个包装,为后续 swap I/O 重构做准备,使 swap_iocb 处理完全保留在
mm/目录下。来源
其它
- mm/sparse-vmemmap:热插拔后调用 flush_cache_vmap():Vivian Wang 提交 v5,确保 arm64 上通过
vmemmap_populate()填充的虚拟映射在 hotplug 之后刷新 CPU 的 cache/vmap 缓存,避免读取到 stale 的页表。来源 - 稳定内核热修复快照:Andrew Morton 发出 7.2-rc3 热修复请求,包含前述 migrate_device 修复、proactive reclaim 冻结中止、以及其它非内存侧修复。来源
👀 值得追的讨论 / Patch
1. device-private PMD 在 zap/change 路径的进一步处理
Usama Arif 的 3 个 patch 覆盖了 madvise、mempolicy 和 madvise_free,但 reviewer 指出剩余的路径(如 zap_huge_pmd、change_huge_pmd、甚至 khugepaged 的 collapse)也可能被 device-private PMD 命中间接调用,需要逐一审计。Usama 承诺在下一版覆盖更多路径。
2. zswap shrinker 统计刷新优化
Yunzhao Li 的 zswap_shrinker_count() 使用 mem_cgroup_flush_stats() 可能在高频调用时引入不必要开销。Johannes Weiner 建议改用 ratelimited 刷新以避免性能抖动。讨论还未结束。
3. cgroup/test_zswap 失败:zswpout 计数不增加
Zenghui Yu 报告 cgroup/test_zswap kslef test 失败:测试程序运行后 zswpout 计数器没有递增,怀疑是 zswap writeback 路径或统计更新问题。目前尚无补丁修复。
⚡ 一句话速览
- huge page collapsing 自动调优 DAMON 讨论持续:Asier Gutierrez 回应 SJ Park 关于 DAMOS_COLLAPSE 的反馈,细节涉及采样频率与 khugepaged 协调。来源
- zswap 前瞻写回讨论:Johannes Weiner 建议用
mem_cgroup_online()替代显式的 cgroup 读锁检查,简化shrink_worker。来源 - slab 批处理 CPU 空缓存改进:Vlastimil Babka 将
free_to_pcs_bulk()中空转检查提取为__free_to_pcs_batch()函数,便于后续优化。来源 - mm/rmap:重构 try_to_unmap_one:Dev Jain 提交 v1,将 HWPoison 检查从 per-page 升级为 per-folio,并添加
try_to_unmap_hugetlb_one辅助函数。来源 - memory tiers 缓存 top tier 节点:Joshua Hahn 提交 v1,在一次遍历中缓存
node_is_toptier()的结果,避免重复调用numa_type扫描。来源 - ACPI NUMA:移除冗余 node_set:Sang-Heon Jeon 提交补丁,在
acpi_parse_memory_affinity中移除多余的node_set(),因numa_add_memblk()已自行设置节点。该修改无功能变化,简化 ACPI NUMA 解析逻辑。来源 - mm/memory-tiers:修复空 check 路径:Joshua Hahn 补充上一条的遗漏,同时清理代码。来源
- pgtable 通过 RCU 释放 fix ptdump UAF:David Carlier 提交 v8,持续简化实现。来源
- anon_vma 移除 anon_vma_chain?:tao 提交 RFC v2(13 patches),提出通过碎片化 anon_vma 结构来消除
anon_vma_chain,但实现极其复杂,讨论刚起步。来源 - hugetlb:动态 NUMA 感知的 HugePage 缓存与 Free Page Reporting:Sourav Panda 提交系列 v1(6 patches),为 hugetlb 引入动态 NUMA 感知的大页缓存,旨在解决 gigantic hugepages(1GB)分配延迟与内存可交换性之间的矛盾。该缓存通过内核 shrinker 在内存压力下回收页面回 buddy 分配器,并集成 virtio-balloon 的 Free Page Reporting,提升虚拟化环境下宿主机的内存利用率。来源
- mm/hugetlb:修复同节点迁移缺少 migratable 标志:Wupeng Ma 修复当 hugetlb 页在同 NUMA 节点间迁移时,未设置
migratable标志导致后续操作失败。来源 - mm/migrate:避免复制 HWPoisoned folios:Kaitao Cheng 提交 v2,在迁移时检测源 folio 是否 poisoned,若已 poisoned 则直接终止迁移而非复制到目标页。来源
- mm/kconfig:清理冗余 memory hotplug 依赖:Kaitao Cheng 提交 v1,移除
MHP_MEMMAP_ON_MEMORY等 Kconfig 项上已经由if MEMORY_HOTPLUG覆盖的重复依赖。来源 - mm/ghost swapfile:支持 zswap 后端切换:Baoquan He 提交另一个 RFC(10 patches),引入 “ghost swapfile” 概念配合 “Redirect entry”,允许把 zswap 换入页透明地切换到后端 blob 存储(如少量 SSD),而无需真的写回 swap 设备。来源
- mm/hmm:用 hmm_range_fault_unlocked 简化 mshv:Stanislav Kinsburskii 提交 v6,新增
hmm_range_fault_unlocked()函数并在 mshv(Microsoft Hyper-V)驱动中使用,允许调用者不持锁进入,由函数内部统一管理 mmap lock。来源