2026-06-23 内核社区日报
今天内存子系统的两个修复值得关注:MGLRU 在 memcg reparenting 后 stale batch 更新导致警告的问题发布了 v2 修复,以及 memcg 在 cgroup 被删除时同步 reclaim 引发系统级 stall 的问题提出了提前退出的方案。此外,DAMON 修复了极端采样间隔下的计算错误,zsmalloc 优化了 zs_free 路径的锁粒度。
🧠 重点 · 内存
1. MGLRU:修复 memcg reparenting 后 stale batch 更新
发生了什么: Qi Zheng 提交了 v2 修复。MGLRU 的 page table walker 在 walk->nr_pages 中累积 per-generation delta,然后由 reset_batch_size() 在 lruvec lock 下提交至 walk->lruvec。当 walker 与 memcg_reparent_memcg 并发时,子 memcg 的 folio 被重设给父 memcg,而 reset_batch_size() 可能将 delta 写入已 reparent 的子 lruvec,触发 lru_gen_exit_memcg() 中的警告(VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0, sizeof(lruvec->lrugen.nr_pages))))。v2 的修复:在 lru_gen_folio 中新增 reparented 字段,reparent 时由 lru_gen_reparent_memcg 将其设为父 lruvec;同时在 reset_batch_size() 中通过 lock_batch_lruvec() 循环检查并锁定正确的 lruvec。补丁还修改了 evict_folios 和 walk_mm 中的调用点。
为什么重要: 该修复防止了并发 reparenting 下 stale batch 写入错误 lruvec,消除了触发警告的条件,确保了 MGLRU 在 memcg 热迁移场景中 per-generation 统计的正确性。
2. memcg:当 cgroup 正在 dying 时提前退出 reclaim
发生了什么: Jiayuan Chen 提交了 3 个补丁,解决一个生产环境遇到的实际问题:当一个 cgroup 被删除(rmdir)时,如果此时有进程正在写 memory.high、memory.max 或 memory.reclaim 文件,这些操作会触发同步 reclaim,可能执行很长时间——尤其是要等待 swap I/O 或者回收已经 thrash 的页面。在最坏情况下,cgroup removal 被阻塞几十秒甚至更久,导致整个节点负载升高甚至软锁。
三个补丁各自处理一条路径:
- patch 1: 在
memory.high的 reclaim loop 中,每次迭代检查memcg->css.cgroup->self.count是否为 0(即 cgroup 是否已从 cgroupfs 中删除),若是则直接返回-EAGAIN,不再继续 reclaim。 - patch 2:同理处理
memory.max路径,同时还避免了在 dying 的 cgroup 上触发 OOM killer(memory.max超过时会走 OOM),因为 OOM killer 在空 cgroup 上毫无意义且增加延迟。 - patch 3:处理 proactive reclaim(
memory.reclaim),同样提前退出。
为什么重要: 在多租户/overcommitted 环境中,节点控制器(node controller)会动态调整各 cgroup 的限值,cgroup 删除和限值调整频繁发生。同步 reclaim 阻塞 cgroup 删除会造成管理操作延迟不可控,甚至反转优先级(higher priority job 因为等待 low priority cgroup 的 reclaim 而卡住)。该系列直接解决了这个生产级稳定性问题。
3. DAMON:修复极端间隔下 damon_hot_score() 计算错误
发生了什么: SeongJae Park 发布 RFC v1.3,修复 damon_hot_score() 在用户设置的监测间隔(sample_interval、aggr_interval)为 0 或 过高时导致的三个问题:
- 采样间隔为 0 时的除零:
damon_max_nr_accesses()因sample_interval为 0 触发除零错误。 - 聚合间隔为 0 时返回错误值:
damon_max_nr_accesses()在aggr_interval为 0 时返回 0,但实际最大访问次数应为 1;该返回值作为分母导致调用者damon_hot_score()再次产生除零。 - 聚合间隔过高导致数组越界:
damon_hot_score()返回值超出[0, DAMOS_MAX_SCORE]范围,用作regions_score_histogram数组(大小为DAMOS_MAX_SCORE+1)的索引,造成越界访问。
修复方法:在 damon_max_nr_accesses() 中显式处理零间隔(将采样间隔为 0 视为 1,且确保返回至少为 1),并在 damon_hot_score() 返回前对结果做 [0, DAMOS_MAX_SCORE] 范围的 clamp。
为什么重要: 这些 bug 直接导致内核除零错误(divide error)和数组越界访问,可被用户通过 sysfs 写入极端参数轻易复现,造成内核崩溃。该修复增强了 DAMON 对用户误设间隔的健壮性,避免因参数配置错误引发的系统稳定性问题。
4. zsmalloc:在释放 zspage 前提前释放 class lock,缩短临界区
发生了什么: Xueyuan Chen 提交的补丁指出,当前 zs_free() 持有 class->lock 直到 zspage 完全释放(包含释放回 buddy allocator,需获取 zone lock),在内存压力下 zone lock 竞争会阻塞其他 CPU 等待同一 class->lock。补丁将实际释放页面的过程移出 class->lock 临界区,缩短锁持有的时间。测试在 RADXA O6 平台用 12 个 CPU 并发执行 zs_free(),执行时间减少 20%。
为什么重要: 该改动缩短了 zs_free() 中 class->lock 的临界区,降低多线程并发时的锁竞争,提升高并发场景下的性能。来源
5. compaction:修复 free_pages_prepare() 失败时 compaction_free() 未正确处理
发生了什么: Zi Yan 提交补丁,修复 compaction_free() 未处理 free_pages_prepare() 失败的情况。free_pages_prepare() 可能因页面是 PageHWPoison 或 free_page_is_bad() 而失败,这些失败页面不应被放回 cc->freepages 供后续使用,否则可能导致数据损坏(data corruption)。
为什么重要: 该修复防止了 compaction 过程中因重用已损坏或非法页面而引起的数据损坏,提升了系统的稳定性。补丁同时标记为 Cc: stable,表明需回溯到稳定内核。
6. memcg:当 cgroup 正在 dying 时提前退出 reclaim(补充:proactive reclaim 路径)
发生了什么: 同上一条,第 3 个补丁专门处理 memory.reclaim(proactive reclaim)路径。同步 reclaim 可能为了回收目标量而执行多轮扫描、swap I/O 甚至触发 thrash,同样可以长时间阻塞 cgroup 删除。
为什么重要: 这一路径对节点控制器(node controller)尤其关键——托管平台经常通过 memory.reclaim 主动回收冷内存以降低延时,而 cgroup 删除又常常并行操作。提前退出避免了管理操作的死锁和延迟敏感任务的优先级反转。
[来源](https://lore.kernel.org/linux-mm/20260623062800.298514-4-jiayuan.chen@linux.dev/)
7. page_alloc:在 buddy fastpath 中优先使用已存在的 highatomic 保留块
发生了什么: JP Kobryn 提交 v3 补丁,调整 ALLOC_HIGHATOMIC 在 buddy fastpath 中的行为。当前实现中,ALLOC_HIGHATOMIC 同时允许访问 MIGRATE_HIGHATOMIC 列表中的空闲页,也允许创建新的 highatomic pageblock 保留区,这使其不适合 fastpath 场景。在 fastpath 中,即使 MIGRATE_HIGHATOMIC 保留区有空闲页可用,分配也可能回退到其他 migratetype 而不优先尝试这些保留页。
v3 补丁的改动包括:
- 收紧创建新 highatomic pageblock 的准则,使增长仅在 slowpath(通过 ALLOC_WMARK_MIN 识别)中发生。
- 允许高优先级非阻塞分配在 buddy fastpath 中使用现有 MIGRATE_HIGHATOMIC 保留页,无需创建新的保留区。具体做法是将非阻塞判断逻辑从
gfp_to_alloc_flags()中提取为gfp_to_alloc_flags_nonblocking(),并让 fastpath 也通过该函数获取ALLOC_HIGHATOMIC标志。 - 在
rmqueue_buddy()中,原本无条件调用reserve_highatomic_pageblock()的条件改为仅在alloc_flags同时包含ALLOC_HIGHATOMIC和ALLOC_WMARK_MIN时才执行,即只对 slowpath 的原子分配进行 pageblock 保留。
补丁附带 UDP 接收负载测试:在目标 zone 有空闲 MIGRATE_HIGHATOMIC pageblock 的情况下,打补丁前工作负载未使用这些 block;打补丁后,进入 buddy 路径的 order-1 分配会消耗现有 MIGRATE_HIGHATOMIC pageblock,未观察到 highatomic miss。同时,工作负载未增长 highatomic 保留区,NAPI page-frag 分配保持健康,没有失败或 order-0 回退。
为什么重要: 该优化使高优先级非阻塞原子分配在 buddy fastpath 中能优先利用已有的 highatomic 保留页,避免不必要的 migratetype 回退,从而在存在空闲 highatomic 页面时提升分配效率。UDP 测试验证了该机制生效,且不带来副作用(保留区不增长、其他分配不受影响)。
🔧 其它子系统
文件系统 / VFS
- ovl: make cachestat() work: Pavel Tikhomirov 提交系列,为 overlayfs 添加
->cachestat()文件操作。当前cachestat()系统调用在 overlayfs 上返回 -ENOSYS,因为 overlayfs 没有实现该 fops。该系列为 overlayfs 添加代理实现,使其底层文件的 page cache 信息能够通过 overlayfd 正确返回。来源 - filemap: Remove file pinning during fault handling (RFC): Matthew Wilcox 提出 RFC,考虑在 page fault 处理中移除对 file 结构体的 pins(通过
vma->vm_file引用计数增加)。当前filemap_fault会通过get_file/fput临时 pin 住 file,以避免在 page fault 执行期间 file 被释放。但如果 fault 路径本身保证了 file 不会被意外释放(例如持有 mmap_lock),则可以去掉这个开销。Willy 想通过 RFC 收集社区意见。来源 - shmem: Use invalidate_lock instead of shmem_falloc (RFC): 同一作者的另一个 RFC,在 shmem 中替换掉
shmem_falloc锁(一种 rwsem 的私有实现),改用 core VFS 提供的invalidate_lock(继承自 struct inode)。这样可以减少 shmem 特有逻辑,并统一与 page cache truncation/invalidation 的加锁顺序。来源 - liveupdate: convert serialized ptr to KHOSER_PTR: Tarun Sahu 提交 v5 系列(基于 v3,将之前 v4 的 memfd_luo 修复拆为独立提交以便回传),将序列化数据使用 KHOSER_PTR 类型,并修复 memfd_luo 中未先检查
args->serialized_data就调用phys_to_virt的 bug。来源
eBPF / BPF
- bpf: add bpf_memcmp and bpf_strpbrk kfuncs: Longjun Tang 提交 5 个补丁,在 kernel/bpf/helpers.c 中增加两个新的 BPF kfunc:
bpf_memcmp提供二进制内存比较(不同于bpf_strcmp,不因空字节终止),返回 0、-1 或 1 及标准错误码;bpf_strpbrk返回字符串中第一个匹配 accept 集合的字符索引。两者均注册在common_btf_ids中,对所有 BPF 程序类型可用。Selftests 覆盖功能正确性与负路径。来源 - BPF exceptions support on RISC-V: Varun R Mallya 提交了 riscv64 JIT 对 BPF exceptions(
bpf_throw())的支持。异常边界程序需保存全部 callee-saved GP 寄存器(ra, s0-s11)以便异常回调恢复;异常回调复用边界程序的栈帧,通过 a2 传入的帧指针设置 SP。bpf_jit_supports_exceptions()仅在CONFIG_FRAME_POINTER启用时返回 true。来源
drm / GPU
- drm/shmem_helper: Check VMA boundaries for PMD mappings: Christian A. Ehrhardt 修复了 drm/shmem helper 在
->huge_fault处理中安装 PMD 透明大页映射时未检查 VMA 边界的问题。如果 VMA 的范围不足以覆盖整个 PMD(2M),安装 PMD 映射会导致映射超出 VMA 范围,可能泄漏到相邻 VMA 的物理映射。补丁在drm_gem_shmem_huge_fault中增加边界检查。来源
mm/page_owner 清理
- mm/page_owner: extract skip_buddy_pages() helper + 小清理: Ye Liu 提交 6 个补丁,对 page_owner 做了一系列小固定和代码美化:
- 抽取
skip_buddy_pages()统一 buddy 页跳过逻辑; - 用
MIGRATE_REASON_NONE代替 Magic Number-1; - 将
CONFIG_MEMCG条件编译从函数体内提到函数级; - 补上
count_threshold格式字符串缺失的换行符; - 挪动
free_ts_nsec输出位置;
- 抽取
- 提取
skip_buddy_pages()辅助函数,统一 page_owner.c 中三处检查 Buddy 页并跳过整块的内存页遍历逻辑,没有功能变化。来源
👀 值得追的讨论 / Patch
- Dynamic Kernel Stacks (DKS) 系列面临评审门槛:Thomas Gleixner 对 v2 系列给出详细评审,质疑其声称的 Petabyte 级别聚合内存节省在单机上的实际收益(如 256 核/256GiB/64k 线程场景仅节省 0.29% 内存),强调必须提供微基准测试的开销数据,并建议与其引入复杂机制,不如识别并修复那些实际导致深层栈的调用链(如 direct reclaim)。David Rientjes 随后提出一个过渡方案:在选定内核线程上试用较小栈并禁止 direct reclaim/compaction。来源
- swap tiers for cgroup based swap control:Youngjun Park 提交 v9 补丁系列。Yosry Ahmed 指出 zswap 作为 tier 时需依赖 vswap 设备的限制。作者总结协议:zswap 可作为第一个 tier,但无 vswap 时不能是唯一允许的 tier。讨论仍在持续。来源
⚡ 一句话速览
- compaction hotfix 提交:Zi Yan 提交 patch,修复
compaction_free()未处理free_pages_prepare()失败的情况——失败页面(可能为 PageHWPoison 或 free_page_is_bad())不应重新加入cc->freepages,否则可能造成数据损坏。Lance Yang 回复 LGTM 并添加 Reviewed-by。来源 - mm: page_ext: add count limit to prevent invalid PFN access:Ketan 提交补丁,为
page_ext_iter_next增加 PFN 有效性校验,防止在内存热插拔在线路径中迭代器越过已初始化的 section 导致 NULL 指针解引用。David Hildenbrand 给出了 Acked-by。来源 - KASAN: wild-memory-access on x86:Ihor Solodrai 汇报了一个 KASAN wild-memory-access,发生在
do_raw_spin_lock中(启动时)。Borislav Petkov 指出 CPUID 相关代码正在重写,应能解决此问题,并提供了补丁集;Ihor 确认该问题在该补丁集上可重现。来源 - mm: avoid KCSAN false positive in memdesc_nid():Hui Zhu 提交补丁,通过增加
ASSERT_EXCLUSIVE_BITS()标记让 KCSAN 忽略page_to_nid()中与folio_trylock()之间的良性竞争。David Hildenbrand 给予 Acked-by。来源 - mm/slub: deduplicate NUMA policy calculation in allocation paths:Hao Li 提交 v2,将
alloc_from_pcs()和__slab_alloc_node()中重复的 NUMA policy 计算提取到一个公共路径,减少 CPU 开销。来源 - mm/page_alloc: bypass watermark when PCP has pages for allocation:Suhua 提交补丁,指出 NR_FREE_PAGES 仅跟踪 buddy 系统页面,未计入 PCP 列表缓存页,导致 watermark 检查失败时分配器即使 PCP 仍有缓存页也会直接进入慢路径。补丁添加
pcp_has_pages()函数跳过 watermark 检查并直接尝试从 PCP 分配。Johannes Weiner 表示补丁前提不无道理,但指出在区域远未接近水位线时,为所有快速路径添加检查和分支可能并非好的权衡,并询问该补丁源于代码审查还是实际工作负载。来源 - mm/swap: rename subpage->page in folio_dup_swap/folio_put_swap; mm/mprotect: drop ‘sub’ from batching context:Dev Jain 提交 2 个补丁:第1个将
folio_dup_swap/folio_put_swap中的变量名subpage改为page;第2个在 mprotect 的 batching 上下文中移除 “sub” 前缀(涉及函数和变量)。来源 - mm/dmapool: untangle CONFIG_SLUB_DEBUG_ON abuse and switch to static key:lirongqing 提交 v2,将 dmapool 中错误使用
CONFIG_SLUB_DEBUG_ON宏的行为改为使用 static key,使功能开关在运行时动态生效。来源