2026-06-09 内核社区日报

今日焦点:zsmalloc zs_free() 锁竞争优化 v4 将 perf 提升归因于 Android 场景;memory-failure 新增 panic_on_unrecoverable_memory_failure sysctl,允许对不可恢复内核页 panic。

🧠 重点 · 内存

1. zsmalloc zs_free() 锁竞争优化 v4:编码 class 索引实现无锁查找,提前释放 class 锁

发生了什么:
Wenchao Hao 发布 v4 系列,降低 zs_free() 在内存压力下的锁开销。该路径在 Android unmap 和 zswap 重压场景中占主导地位。改动分四个补丁:

  1. 将 size_class 索引编码进 obj 值:在 zs_free() 入口,将 class 索引与 PFN、obj_idx 一同编码,使函数无需通过 handle 间接查找 class,避免获取 pool->lock(64-bit 系统下)。
  2. 在 64-bit 上移除 pool->lock:基于上述编码,直接拿 class->lock 即可;32-bit 保持不变。
  3. 在释放 zspage 前释放 class 锁:原逻辑中 class->lock 一直持有到 zspage 归还给 buddy。现改为:确定 zspage 已空后先释放 class 锁,再调用 free_zspage(),缩短临界区。
  4. 文档化三种 free_zspage 辅助变体:为因补丁 3 拆分出的变体添加统一注释块。

性能测试(各进程独立 mmap 256MB→写入→MADV_PAGEOUT 换出至 zram→并发 munmap):

  • Raspberry Pi 4B (4 核 ARM64 Cortex-A72):单进程 59.0ms→56.0ms (1.05x);2 进程 94.6ms→66.7ms (1.42x);4 进程 202.9ms→110.6ms (1.83x)。
  • x86 (20 核 i7-12700,16 并发):单进程 11.7ms→9.8ms (1.19x);2 进程 24.1ms→17.2ms (1.40x);4 进程 63.0ms→45.3ms (1.39x)。

v3 曾获得 Nhat Pham(补丁 1-3)和 Joshua Hahn(补丁 3)的 Reviewed-by;v4 中因补丁 1、2 逻辑变化较大,Nhat Pham 的 Reviewed-by 已被撤销。补丁 4 仍有 Nhat Pham 的 Reviewed-by。

为什么重要:
该系列针对 zs_free() 的锁竞争瓶颈——pool->lock 的缓存行 bouncing 和 class->lock 在释放页时持有过久——通过编码 class 索引实现无锁 class 查找,并在释放 zspage 前释放 class 锁。性能数据表明,多进程并发场景下速度提升可达 1.83x(树莓派 4B)和 1.40x(x86),对 Android 内存压力下的 LMK 行为及服务器 zswap 工作负载有直接收益。

来源

2. memory-failure:新增 panic_on_unrecoverable_memory_failure sysctl,对不可恢复内核页 panic

发生了什么:
Breno Leitao 发布 v9 系列,完善对 HWPoison 内核页的处理:

  • 引入 -ENOTRECOVERABLE 返回值,区分可重试的 transient 失败(如 page 正在迁移)和确定的不可恢复错误(PG_reserved、slab、page tables、large kmalloc 等)。此前 get_any_page() 把所有不可处理情况都折成 -EIO,导致调用方无法区分。
  • 新增 sysctl vm.panic_on_unrecoverable_memory_failure(默认 disable)。当该 sysctl 置 1 且 memory_failure() 遇到不可恢复的内核页时,直接 panic。
  • 配套 selftest(hwpoison-panic)验证三种内核页类型:reserved、slab、page tables。

为什么重要:
对于生产环境 ECC 错误,遇到内核关键数据损坏时,立即 panic 比冒险继续运行更安全(避免静默数据损坏)。该 sysctl 让运维人员按需选择行为。本系列延续了 LSF/MM 2026 关于 HWPoison 讨论的成果,已获 David Hildenbrand 等多位 reviewer 的 Reviewed-by。

来源

3. MM: Tighten control over zero-page remapping:修复 KSM 与 THP split 交互 bug

发生了什么:
Nico Pache 发布新系列,解决 zero-page 重映射相关问题。commit b1f202060afe 在分裂 folio 时进行无条件的零页重映射,导致 KSM 的 pages_to_scan 限制被绕过,且无视 use_zero_pages 策略。该系列将零页重映射限制在 split_underused_thp 启用时才执行,为用户提供更多控制。包含 3 个补丁:导出 ksm_is_running() 以检查 KSM 合并状态;防止 folio 分裂与 KSM 交互;当 underused THP shrinker 禁用时跳过零页重映射。

为什么重要:
避免 KSM 效率因 THP 分裂中的零页重映射而降低,并尊重用户对 use_zero_pages 的配置。

来源

4. folio refcount scalability:移除 page refcount zero state 语义,引入专用 bit 替代

发生了什么:
Gladyshev Ilya 发布 v4 系列,改变 folio refcount 的实现方式:

  • 放弃 "refcount = 0 表示 folio 已释放"的语义:目前内核将 _refcount == 0 作为 folio 死亡标志,这导致了大量 cmpxchg 循环用于安全递减。新方案改为使用一个专用 bit(类似 PG_locked 的作用)表示 folio 正在被释放或不可访问,从而允许 refcount 域接近标准的 atomic_t 操作。
  • 实现 refcount locking via dedicated bit:在 folio_put() 等路径上,检查并设置该 bit,避免误判活跃页为已释放。

为什么重要:
当前 refcount 语义是并发瓶颈之一——每个 get_page()/put_page() 都需要复杂的 memory ordering。新方案可显著降低 refcount 操作的延迟,特别是对 slab 回收、GUP 快速路径等高频场景。Matthew Wilcox 对此方向表示支持,但还需进一步性能验证。

来源

5. khugepaged:修复 PMD collapse 时 swap PTE 统计错误

发生了什么:
Lance Yang 发现 mthp_collapse_file 在 collapse 包含 swap entries 的 THP 时,未正确计算 swap PTE 数量——它使用了错误的 mask(~PMD_MASK 而非 ~HPAGE_PMD_MASK),导致在 alignment 检查中可能漏报或误报情况。补丁修正了 swap PTE accounting,确保与实际数量一致。

为什么重要:
该 bug 会影响 khugepaged 对 swapout 后 THP 的判断:如果统计不准,可能让 collapse 错误地推进或回退。reviewer 已确认修复正确。

来源

6. memory-failure 其他进展:serialize TestSetPageHWPoison with zone lock

发生了什么:
Michael S. Tsirkin 提交了一个拆分补丁,为 TestSetPageHWPoison 增加与 zone->lock 的序列化保护。此前,在 hotplug 路径中释放 HWPoison HugeTLB 页时,dissolve_free_hugetlb_folio() 可能和并发 TestSetPageHWPoison 产生竞争。补丁在热点路径上加上 zone->lock 预防。

为什么重要:
修复了 HWPoison 与 hotplug 交互的一个潜在 race,属于稳定性的提升。

来源

🔧 其它子系统

(今日没有明显跨子系统内容,eBPF 节只有少量 BTF 安全修复和 AI 生成 review 的讨论,均为日常维护。)

👀 值得追的讨论 / Patch

  • KHO 系列讨论版本控制与头文件解耦:Mike Rapoport 主张每个组件独立版本化、在 kexec 前检查兼容性,并使用数字版本号;Pasha Tatashin 接受独立版本化,但双方就 kho_preserve_vmalloc() 等 API 保留在主 KHO 头文件而非移出达成一致。来源

⚡ 一句话速览

  • page_table_check 排除 PFN-mapped PTEs:Andrey Smirnov 提交 v1,让 page_table_check 跳过 vDSO [vvar] 等特殊 PFN 映射的 tracking,避免 false positive 警告。来源

  • gup_test 拒绝包装用户范围:Samuel Moelius 修复 debugfs ioctl 中 addr + size 回绕导致循环被跳过并返回成功的问题。补丁通过 check_add_overflow 预先计算并验证终点,避免了回绕。来源

  • mm/lruvec: trace LRU add drains:JP Kobryn 添加 tracepoint 记录 LRU add batch 提前 drain 事件,便于诊断 LRU 锁竞争。来源

  • kho: granular compatibility:Pasha Tatashin 的 RFC v1 将各子系统兼容性版本与全局 KHO 版本解耦,获 Mike Rapoport 初步认可。来源

  • memblock: don’t touch arrays on free:memblock 修复在 memblock_free() 中不触发数组写入的问题(build success)。来源

  • constify nodemask in oom_control/scan_control/alloc_context:Gregory Price 将结构体中的 nodemask_t * 改为 const,防止意外修改。来源

  • samples/damon/mtier: 处理 damon_start/stop 失败:SeongJae Park 修复示例模块未检查返回值导致资源泄漏的问题。来源

  • zswap decompress task hung:工具报告 zswap_decompress 任务挂起 bug,内核版本 v7.0.6,已提供内核配置与报告附件;开发者称正在分析根因并开发可复现 PoC,尚无修复消息。来源

  • slab: introduce alloc_flags 系列:Vlastimil Babka 提交 RFC v1,用 alloc_flagsslab_alloc_context 替代 gfp_t 中部分 bit,为 slab 分配提供更精细控制。来源