系列:[PATCH v2 0/3] use vma locks for proc/pid/{smaps|numa_maps} reads作者: Suren Baghdasaryan版本: v2(3 个 patch)
/proc/pid/maps 已先行支持 per-VMA lock + RCU 遍历来避免持有 mmap_lock,但 /proc/pid/smaps 和 /proc/pid/numa_maps 却无法简单套用相同方案,原因是这两个接口需要做 page table walk,而 page table walk 可能睡眠(例如 shmem_swap_usage() 会访问 swap cache)。在 RCU read lock 下睡眠是不允许的,因此这两者长期以来必须全程持有 mmap_lock,导致与并发地址空间修改(mmap/munmap/mprotect)产生严重锁竞争。
mmap_lock,阻塞并发 VMA 操作核心思路是在 page table walk 前 drop RCU(但保持 VMA 被 per-VMA lock 锁定),walk 完成后重新 acquire RCU 并重置 VMA iterator。
引入关键辅助函数:lock_ctx_mm() / unlock_ctx_mm() 封装 mmap_lock 操作并追踪 mmap_locked 状态;drop_rcu(priv) 在 per-VMA lock 模式下释放 rcu_read_lock()(VMA 本身已被 vma_start_read() 锁定保持稳定);reacquire_rcu(priv) 重新 rcu_read_lock() 并调用 vma_iter_set() 重置 VMA iterator 确保从锁定 VMA 结束后继续安全遍历。
为 page table walk 引入新的 mm_walk_ops 变体,使用 PGWALK_VMA_RDLOCK_VERIFY 标记表示 walk 是在 per-VMA lock 下进行的。具体流程:smap_gather_stats() 先检查 gate VMA(跳过),然后 drop_rcu(),执行 walk_page_vma(),最后 reacquire_rcu()。get_smaps_walk_ops() 和 get_show_numa_ops() 根据 mmap_locked 状态动态选择 walk ops。
测试方面引入 FIXTURE_VARIANT 机制让已有 maps 测试同时运行 smaps 变体,实现 parse_vma_line() 确保 sscanf() 输入 NUL-terminated,以及对 smaps 差异化输出格式的适配。
作者未提供性能数据,[从代码逻辑推断:对于大地址空间(数百个 VMA),smaps 读取涉及完整 page table walk 并可能触发 I/O,在此期间 mmap_lock 被持续持有。改为 per-VMA lock 后,mmap_lock 的持有时间变为零(仅 RCU + per-VMA lock),并发 mmap/munmap 操作可以在遍历间隙正常进行,大幅降低锁竞争]。
系列:[RFC PATCH v2 0/7] Implement a new generic pagewalk API作者: Oscar Salvador版本: RFC v2(7 个 patch)
LSFMM/BPF 2025 达成共识:需要一个通用 pagewalk API 替代当前基于 mm_walk_ops 回调的旧接口。痛点包括:/proc/pid/smaps、numa_maps、pagemap 各自维护几乎相同的页表遍历逻辑,HugeTLB 总是被当作 PTE 处理(需要特殊 .hugetlb_entry 回调),大量代码在锁管理、contiguous 条目批处理等方面存在重复。
/proc 接口中 page table walk 代码高度重复(smaps: ~200 行,numa_maps: ~120 行,pagemap: ~450 行专用遍历代码)基础设施(Patch 1-3):softleaf_from_pud() 从 PUD 中提取 softleaf entry,需为 arm64/x86/powerpc/s390/loongarch 添加 __pud_to_swp_entry/__swp_entry_to_pud 宏。pmd_huge_lock() / pud_huge_lock() 统一的 huge page 锁获取函数,THP 和 HugeTLB 共用。folio_pmd_batch() 模拟 folio_pte_batch() 的 PMD 批处理,支持 arm64 CONT_PMDS。
核心 API(Patch 4):pt_range_walk_start() / pt_range_walk_next() / pt_range_walk_done() 三类函数。输出 struct pt_range_walk 包含 folio、page、nr_entries、size、writable/young/dirty/present、level(PUD/PMD/PTE)等。内部统一处理 pgd→p4d→pud→pmd→pte 层次遍历、自动批处理 contiguous PTEs/PMDs、自动锁管理、HugeTLB PMD shared 检测。
消费者转换(Patch 5-7):smap_gather_stats() 从回调模式转为 pt_range_walk 循环(精简 307 行);show_numa_map() 精简 159 行;pagemap 最复杂的转换(1826 行重构),pagemap_read_walk_range() 替代旧的 pagemap_pmd_range()+pagemap_hugetlb_range()。
tools/mm/page-types.c | |
pagemap_ioctl | |
/proc/pid/numa_maps | |
/proc/pid/smaps | |
作者标注待完成工作:i_mmap lock 集成(HugeTLB WP vs pmd-sharing)、make_uffd_wp_huge_pte 统一化、更多 pagewalk 消费者有待转换。
系列:[RFC PATCH] mm/hugetlb: fix resv_map memory leak in __mmap_region error path作者: Mingyu Wang版本: RFC v1(1 个 patch)
通过 Syzkaller + failslab fault injection 进行模糊测试时,发现 hugetlb mmap 错误路径中存在 persistent resv_map 内存泄漏。这是近期 VMA iterator 和 mmap region 重构引入的回归,该重构将 mmap 准备阶段与 VMA 完成阶段解耦。
__mmap_region() 中 call_mmap_prepare() 触发 hugetlbfs_file_mmap_prepare(),成功分配 resv_map(通过 resv_map_alloc() 和 hugetlb_reserve_pages())__mmap_new_vma() 失败(如 vma_iter_prealloc() 因 failslab 返回 -ENOMEM),代码跳转到 abort_munmap,desc 结构被直接丢弃无任何清理set_vma_user_defined_fields(),vm_area_free() 不会调用 hugetlb_vm_close() 正常释放路径在 struct mmap_action 中新增 abort_hook 回调,与已有的 success_hook 形成对称。hugetlbfs_file_mmap_prepare() 设置 abort_hook 为 hugetlb_file_mmap_prepare_abort。__mmap_region() 错误路径在 vms_abort_munmap_vmas() 之前调用 desc.action.abort_hook(&desc)。当前 hugetlb_file_mmap_prepare_abort() 为桩函数,标注 TODO:需要实现 hugetlb_reserve_pages() 的正确回滚逻辑并释放 resv_map 引用。
作者未提供性能数据,[这是一个正确性修复:在内存压力下每次失败的 hugetlb mmap 调用将永久泄漏 resv_map(512 字节),持续累积可能 OOM。abort_hook 架构提供通用机制,任何 mmap_prepare 回调都可利用它清理资源]。
系列:[PATCH v9] mm/damon: add node_eligible_mem_bp goal metric作者: Ravi Jonnalagadda版本: v9(1 个 patch)
在异构内存系统(如 DRAM + CXL)中,跨 NUMA 节点的内存分布控制对性能优化至关重要。DAMON 的 quota goal 框架已提供 node_mem_free_bp 等指标,允许用户设置"保持 NUMA 节点 0 可用内存不低于某阈值"的目标。但这些指标都是绝对值导向——跟踪某节点上的绝对资源量,而非跨节点的相对分布比例。用户无法表达"60% 热内存在 DRAM 上,40% 在 CXL 上"这样的分布目标。
新增 metric 类型 DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP(单位:basis points,0-10000)。核心计算函数 damos_get_node_eligible_mem_bp() 完成三级计算:遍历所有 target region,通过 __damos_valid_target() 过滤匹配该 scheme 访问模式的 region;对每个 eligible region 逐 folio 遍历物理地址范围,使用 folio_nid() 判定 NUMA 节点归属;大 folio 精确处理 region 边界与 folio 的部分重叠。每处理完一个 region 调用 cond_resched()。
重构 quota auto-tuning 内部接口:damos_set_quota_goal_current_value() 等函数签名从接受 struct damos_quota * 改为 struct damon_ctx *, struct damos *,因为新 metric 需要访问 DAMON context 获取 addr_unit 和 scheme 的 access pattern filters。
安全机制:damon_commit_ctx() 中非 DAMON_OPS_PADDR 返回 -EINVAL;三重防御(非 PADDR 返回 0、无效 nid 返回 0、total_eligible==0 返回 0);#ifdef CONFIG_DAMON_PADDR 编译守卫。sysfs 新增 "node_eligible_mem_bp" 名称映射。
作者在两节点 DRAM+CXL 异构内存系统上进行了功能验证。使用 TEMPORAL tuner 时系统快速收敛到目标分布——tuner 在未达标时驱动 esz 到最大值,达标后立即置零。CONSIST tuner 也能收敛但速度较慢。作者未提供量化收敛时间或吞吐量数据,[从代码逻辑推断,主要开销在 damos_calc_eligible_bytes() 中逐 folio 遍历,对于大内存系统可产生可观 CPU 开销,但 cond_resched() 缓解了调度延迟]。
系列:[PATCH v2 0/4] mm/damon: repost non-hotfix reviewed patches in damon/next tree作者: SeongJae Park版本: v2
背景:damon_hot_score() 中 age_in_log 的计算原使用手动 for 循环逐次右移,效率低下。
解决的问题:手动 for 循环实现 log2 计算在 10,000,000 次迭代的模拟内核模块测试中平均延迟为 12ns。
如何做:用 ilog2(age_in_sec) + 1 替换整个 for 循环,min_t() 限制上限。ilog2() 在多数架构映射为 fls() 或硬件指令(如 x86 的 bsr),O(1) 复杂度。
收益:
背景:DAMOS_HUGEPAGE 在 THP=never 时完全失效(khugepaged 不运行);khugepaged collapse 整个 VMA 而非仅 hot region;无法实现基于 DAMON 访问模式的确定性 THP collapse。
如何做:新增 DAMOS_COLLAPSE action,在 damon_va_apply_scheme() 中映射为 MADV_COLLAPSE,调用 madvise_collapse() 同步对指定地址范围执行 collapse,不依赖 khugepaged。
收益(MariaDB + sysbench,ARM 物理服务器,read only + gaussian row hitting):
关键发现:DAMOS_COLLAPSE 在 THP=never 下仍能产生大量 huge page(1.27 GB),collapse 速度更快(5 秒 vs 45 秒分配 190 huge pages),且因只 collapse hot region 而内存消耗略低于 khugepaged 方案。
系列:[RFC PATCH 00/19] mm/damon: introduce data attributes monitoring作者: SeongJae Park版本: RFC v1(19 个 patch)
DAMON 自诞生以来始终只监控数据访问这一种属性。6.14 内核合入了基于 DAMOS filter 的 page-level properties monitoring,该功能允许对每个 DAMON region 的所有 folio 逐一应用 filter 判定页面属性(如 cgroup 归属),但开销与内存大小成正比,只能用于测试/调试。用户需要始终在线(always-on)、开销可控的多维度数据属性监控能力。
引入"data attributes monitoring"概念,核心思想是复用 DAMON 现有采样机制和 region 结构,在每次 access check 采样时同时应用用户注册的 data probe。
数据结构:struct damon_probe 代表数据属性探测器,链接到 damon_ctx->probes,包含一组 struct damon_filter。struct damon_region 中新增 unsigned char probe_hits[DAMON_MAX_PROBES] 数组(每个 region 仅 4 字节)。struct damon_operations 新增 .apply_probes() 回调。
执行流程:check_accesses() 后调用 ops->apply_probes();每次 aggregation 重置 probe_hits;用户通过 sysfs(probes/ 目录树)配置和读取。983 行新增代码实现完整 sysfs 分层目录结构。
作者未提供性能数据(RFC 阶段),[从代码逻辑推断:复用 access check 采样时机,无额外地址空间遍历;probe_hits 使用 unsigned char 数组每 region 增加 4 字节;采样粒度与 access check 一致,额外开销与 sampling interval 成反比,最大程度上保持了 DAMON 的有界低开销承诺]。
系列:[PATCH v3 0/4] mm: improve write performance with RWF_DONTCACHE作者: Jeff Layton版本: v3(4 个 patch)
RWF_DONTCACHE 标志允许应用告诉内核"这份数据不需要缓存"。当前主线实现中,generic_write_sync() 检测到 IOCB_DONTCACHE 时调用 filemap_flush_range() 在写入者上下文中内联提交 writeback。这导致 p99.9 延迟从 buffered 的 23ms 膨胀到 93ms。此外内联 writeback 不区分 dontcache 脏页和普通 buffered 脏页,可能刷出大量不相关的缓冲数据。
filemap_flush_range() 在写入者热路径中内联执行,阻塞写入者并推高尾延迟采用 Jan Kara 和 Christoph Hellwig 建议的方案:不再内联执行 writeback,而是 kick flusher 线程执行与写入量成比例的回写。
第一层:添加 NR_DONTCACHE_DIRTY 节点页面计数器,在 folio_account_dirtied() 中与 NR_FILE_DIRTY 同时递增(folio 设置了 dropbehind 标志时),通过 /proc/vmstat 暴露为 nr_dontcache_dirty。
第二层:新增 WB_start_dontcache 标志位和 wb_check_start_dontcache() 处理函数,替换 filemap_flush_range() 调用。新函数 filemap_dontcache_kick_writeback() 定位正确的 cgroup writeback 域,设置标志并唤醒 flusher。Flusher 读取 NR_DONTCACHE_DIRTY 计数确定回写页数,使用 wb_split_bdi_pages() 按比例分配。新增 WB_REASON_DONTCACHE。
测试环境:双路 Xeon Gold 6138(80 CPU、256 GB RAM、Samsung NVMe、本地 XFS)。
| -72.9% | ||||
| -76.9% | ||||
| +398.3% |
NFS 多写者 dontcache 吞吐提升近 5 倍,几乎追平 buffered I/O。脏页足迹在顺序负载下下降 85-95%。
系列:[PATCH RFC v4 00/22] mm/virtio: skip redundant zeroing of host-zeroed reported pages作者: Michael S. Tsirkin版本: RFC v4(22 个 patch)
当虚拟机通过 virtio-balloon 的 free page reporting 向 hypervisor 报告空闲页面时,host 通常将其清零(如通过 MADV_DONTNEED)。Guest 重新分配时内核再次清零造成冗余。更严重的是,在具有别名缓存(aliasing caches)的架构上启用 init_on_alloc 时存在双重清零:post_alloc_hook 中 kernel_init_pages() 清零一次 + clear_user_highpage() 再次清零。
init_on_alloc 启用时存在双重清零(kernel_init_pages() + clear_user_highpage())横跨 mm 核心、page reporting 和 virtio 子系统的大型系列(22 patch)。
Cache-friendly zeroing 基础设施(Patch 1-6):将 vma_alloc_folio() 移到 page_alloc.c,新增 vma_alloc_folio_user_addr() 传递精确 fault 地址。post_alloc_hook() 中使用 folio_zero_user() 替代 kernel_init_pages(),按从远到近(相对 fault 地址)的顺序清零。
消除双重清零(Patch 7-12):使用 __GFP_ZERO 统一清零,移除 clear_user_highpage() 第二次清零以及 x86/s390/m68k/alpha 上的 arch vma_alloc_zeroed_movable_folio 覆盖。
PG_zeroed 标志位跟踪(Patch 13-17):PG_zeroed(别名到 PG_private,仅在空闲列表页面有效)标记已知为零的 buddy 页面。设置路径:page_reporting_drain + buddy 合并传播到合并后页面 + expand 传播到子页面。清除路径:buddy 合并前清除 + post_alloc_hook 消耗后清除 + __isolate_free_page() 清除。
测试环境:2GB VM,1 vCPU,分配 256MB 匿名页面。
| -77% | ||||
| -69% | ||||
| -71% | ||||
| -75% |
优化在 THP 启用时效果最佳(整个 2MB 页面直接从报告的 order-9+ buddy 页面分配)。不使用 THP 时仅约 21% 的 order-0 分配来自报告页面。
系列:[PATCH v2] mm/lruvec: preemptively free dead folios during lru_add drain作者: JP Kobryn版本: v2(1 个 patch)
Meta 在生产环境(Instagram 工作负载)中观测到 lruvec 锁竞争中约 24% 发生在 lru_add batch drain 时存在已死亡(dead)folio。Dead folio 是指引用计数仅剩下 batch 持有的那一份引用(refcount == 1),即将被释放的 folio。这些 folio 被加入 LRU 后立即被 folios_put_refs() 移除,导致两次不必要的锁获取。
仅在 mm/swap.c 的 folio_batch_move_lru() 中修改约 40 行。当 move_fn == lru_add 时,使用 folio_ref_freeze(folio, 1) 检测 refcount 是否恰好为 1。对检测到的 dead folio:清除 PG_active 和 PG_unevictable 标志(可能由 migration 路径设置),folio_unqueue_deferred_split() 从 deferred split 列表移除,将 folio 加入临时 free_fbatch。遍历结束后 mem_cgroup_uncharge_folios() + free_unref_folios() 批量释放。
v2 改进:增加了 PG_active 和 PG_unevictable 标志的清除,处理通过 migration 路径加入 LRU 的 folio。
Meta 生产环境 Instagram 工作负载 A/B 测试(每侧 60 台主机,3 次 60 秒采样,95% CPU 负载):
| -99.999% | |||
无性能退化,requests per second 和 p99 尾延迟均未观测到退化。
系列:[PATCH v7 0/6] mm: Fix vmemmap optimization accounting and initialization作者: Muchun Song版本: v7(6 个 patch)
vmemmap 优化(HugeTLB 和 DAX 的 compound page 复用 tail struct page 以减少元数据开销)自 v5.14 引入后持续演进。该优化在 DAX 内存热插拔(memory hotplug)和 ZONE_DEVICE 页面初始化路径中存在多个边界条件 bug,包括页面计数错误、altmap 传递错误、migratetype 未初始化以及 tail struct pages 未初始化,特定配置下可能导致内存损坏(machine check)或系统崩溃。
create_memory_block_devices() 失败后的错误路径向 arch_remove_memory() 传入 NULL altmap,导致 vmemmap 页面被错误释放到 buddy allocator,后续分配使用触发 machine check@pgmap 参数导致计数错误采用"先建立正确信息传递,后基于信息做正确决策"的分层修复策略。
计数和 altmap 修复:patch 1 修复下溢;patch 2 将错误路径的 NULL 改为 params.altmap 确保正确归还。
pgmap 信息传递链:patch 3 将 @pgmap 参数沿 memory deactivation 路径传递(涉及 x86、arm64、loongarch、powerpc、riscv、s390 六个架构);patch 4 利用 pgmap 通过 vmemmap_can_optimize() 正确计算 DAX vmemmap 页面数量。
DIVICE 初始化修复(patch 5 最关键):从 __init_zone_device_page() 中分离 migratetype 初始化,新增 pageblock_migratetype_init_range() 函数,以 pageblock 粒度遍历整个 PFN 范围初始化 migratetype 为 MIGRATE_MOVABLE,在 section 边界插入 cond_resched()。
patch 6 修改 compound_nr_pages() 签名新增 pfn 参数,通过 early_section() 检查判断是否复用早期 section,若是则回退到完整的 struct page 数量。
作者未提供性能数据,[修复避免:内存损坏/machine check(patch 2)、buddy allocator 污染导致的不可预知分配行为、ZONE_DEVICE tail struct pages 未初始化导致的崩溃、pageblock migratetype 未初始化导致的内存迁移异常。所有修复带 Fixes: 和 Cc: stable 标签,表明是生产环境中可触发的真实 bug]。
系列:[RFC 0/7] mm: dual-bitmap page allocator consistency checker作者: Sasha Levin版本: RFC v1(7 个 patch)
现有内核内存调试工具(KASAN、KFENCE、page_poisoning)均检测非法内存访问或内容损坏,但无人检测 page allocator 自身元数据的静默损坏(silent corruption)。当硬件 bit flip 破坏了 buddy allocator 的分配位图时,可能分配已被占用的页面或无法分配空闲页面,而内核对此毫无察觉。面向 ISO 26262(汽车功能安全)等场景,需要可审计、可认证的内存分配完整性检测机制。
核心数据结构:struct dual_bitmap 维护两个独立 memblock 分配的位图,主位图(PRIMARY)0=空闲/1=已分配,辅位图(SECONDARY)取反,不变量 primary == ~secondary。
原子操作原语(include/linux/dual_bitmap.h,header-only):dual_bitmap_set() 两端 atomic test_and_set/clear 并返回旧值检测 double-alloc;dual_bitmap_clear() 检测 double-free;dual_bitmap_validate() 冷路径诊断函数带重试逻辑和 smp_rmb() 屏障应对并发更新。
初始化由 page_consistency_init()(mm/mm_init.c 中调用)通过 memblock_alloc() 分配两个独立位图。通过 static key(page_consistency_enabled)控制,未启用时编译为近乎零开销的预测不跳转分支。热路径集成在 post_alloc_hook() 和 free_pages_prepare() 中。CONFIG_DEBUG_PAGE_CONSISTENCY_PANIC 控制检测到违规时 panic 还是 WARN。debugfs 接口提供统计和手动全量扫描。
作者未提供性能数据,[内存开销:每物理页帧 2 bits,64 GB 系统约需 4 MB;性能开销:每次 alloc/free 增加两次原子 bitop,对安全关键部署可接受;正确性保证:任何单 bit 损坏被立即检测,设计简单到可审计和认证]。static key 控制使未启用时零开销。
系列:[PATCH 7.2 v10 0/2] skip redundant sync IPIs when TLB flush sent them作者: Lance Yang版本: v10(2 个 patch)
当页表操作需要与软件/无锁页表遍历器(如 GUP-fast)同步时,TLB flush 之后须调用 tlb_remove_table_sync_one() 或 tlb_remove_table_sync_rcu() 广播 IPI 或等待 RCU。然而在 x86 上,TLB flush 已通过 on_each_cpu_mask() 向所有目标 CPU 发送了 IPI,随后的 sync IPI 广播完全冗余。这在大型系统上尤为昂贵,64 核 x86 服务器折叠 20 GiB 内存时 CAL 中断从 646,316 降至 785(-99.88%)。
采用两阶段方案。Step 1(本系列)在 100% 确定 TLB flush 已发送 IPI 时跳过冗余 sync。
Patch 1(通用框架):引入 tlb_table_flush_implies_ipi_broadcast()(默认 false),在 tlb_remove_table_sync_one() 和 tlb_remove_table_sync_rcu() 开头插入早期返回检查。
Patch 2(x86 启用):native_pv_tlb_init() 启动时一次性决策,通过 DEFINE_STATIC_KEY_FALSE(tlb_ipi_broadcast_key) 实现零开销热路径。三个条件:使用 native TLB flush(非 PV backend)、不支持 INVLPGB 指令、通过 static key 固定决策。flush_tlb_info.freed_tables 重命名为 wake_lazy_cpus 更准确描述语义。
| -99.88% |
每次 tlb_remove_table_sync_one() 节省一次 smp_call_function() 对所有 CPU 的 IPI 广播;每次 tlb_remove_table_sync_rcu() 节省一次 synchronize_rcu() 宽限期等待。这在 hugetlb PMD unshare 和 khugepaged collapse 场景中频繁触发。
系列:[PATCH v2 00/13] Dynamic Kernel Stacks作者: David Stevens, Pasha Tatashin版本: v2(13 个 patch)
Linux 为每个线程静态分配固定大小内核栈(x86_64 上通常 16 KiB = 4 个页面)。在 Android 等拥有大量线程的系统上,常规设备系统进程通常有 2000-3000 个线程,4 GB 设备可能将 1-2% 总内存用于内核线程栈。动态内核栈按需增长,仅在栈深度不足时才分配新页面。
栈初始仅分配 THREAD_PREALLOC_PAGES(默认 1 页 = 4 KiB),剩余 THREAD_DYNAMIC_PAGES(默认 3 页)初始未映射形成"栈洞"。当栈使用超出已映射区域时触发 page fault,处理函数从 per-CPU 页面池分配新页建立映射。
关键数据结构:task_struct.stack 替换为 packed_stack,指针低位编码已记账页面数;per-CPU 页面池 dynamic_stack_pages[DYNSTK_PAGE_POOL_NR]。
Page fault 处理:检查 fault 地址落在内核栈区域后,从 hole_end 向下逐页分配映射,设置 PF_DYNAMIC_STACK 标记。调度路径集成:dynamic_stack() 在 __schedule() 前补充 per-CPU 池(GFP_ATOMIC)并完成 memcg 记账。
x86 FRED 支持:FRED 可用时内核页错误在栈级别 1 处理;FRED 不可用时使用专用 IST 栈(新增 PF/PF2/UDI 三个 IST 栈)处理页错误,非动态栈故障弹回常规栈。外部中断无条件弹回原始栈避免中断丢失。
所有测试机器仅消耗原始栈内存约 25%,节省 65-70%。仅约 5% 活动任务在其生命周期中触发栈页错误。
系列:[PATCH] mm/migrate_device: fix spinlock leak in migrate_vma_insert_huge_pmd_page作者: Sunny Patel版本: v1(1 个 patch)
migrate_vma_insert_huge_pmd_page() 在设备迁移过程中插入 huge PMD 页面映射,需持有 PMD spinlock(通过 pmd_lock() 获取)确保 PMD 修改的原子性。
pmd_lock() 获取 PMD spinlock 后 check_stable_address_space() 可能返回非零(地址空间不稳定),代码跳转到 abort 标签而非 unlock_abort,直接跳过 spin_unlock(ptl) 调用将 check_stable_address_space() 失败后的跳转目标从 abort 改为 unlock_abort(先释放 spinlock 再进入 abort 清理)。修复恢复正确的 lock/unlock 配对,确保任何错误路径 spinlock 都能释放。
作者未提供性能数据,[消除确定死锁场景——并发内存热插拔或 memory failure 与 device migration 同时发生时,修复后系统优雅 abort 迁移操作而非死锁]。
系列:[PATCH] mm/migrate_device: fix pgtable leak in migrate_vma_insert_huge_pmd_page作者: Sunny Patel版本: v1(1 个 patch)
与 Feature 14 同一函数。migrate_vma_insert_huge_pmd_page() 通过 pte_alloc_one() 分配 pgtable 页用于 deposit 到 PMD 级别页表。在多个异常路径上 pgtable 未得到释放。
unlock_abort 标签可从多条路径到达:check_stable_address_space() 失败后、userfaultfd_missing() 返回 true、PMD 检查失败pte_alloc_one() 分配的 pgtable 直接泄漏(通常 4KB,64KB 页架构为 64KB)在 unlock_abort 标签处(spin_unlock(ptl) 之后)增加 pte_free(vma->vm_mm, pgtable) 调用。正常成功路径中 pgtable 或被 pte_free() 释放(flush 路径)或被 pgtable_trans_huge_deposit() 存入 PMD,unlock_abort 作为统一异常出口补充释放逻辑。
作者未提供性能数据,[消除确定内存泄漏——每次触发 unlock_abort 路径泄漏一个页帧,GPU 计算等重度场景累积影响显著]。
系列:[PATCH v3 0/3] kasan: hw_tags: Disable tagging for stack and page-tables作者: Dev Jain版本: v3(3 个 patch)
Hardware Tag-Based KASAN 利用硬件内存标签机制(如 ARM MTE)检测越界访问和 use-after-free。内核栈和页表始终通过 match-all tag(0xFF,KASAN_TAG_KERNEL)的指针访问,给这些内存打随机标签完全是无效开销。
kasan_unpoison_range() 在 HW-tags 模式下无意义Patch 1(vmalloc 层):在 __vmalloc_node_range_noprof() 中如果 __GFP_SKIP_KASAN 置位且 prot 为 PAGE_KERNEL,跳过标签启用和 kasan_unpoison_vmalloc()。
Patch 2(内核线程栈):THREADINFO_GFP 和 GFP_VMAP_STACK 添加 __GFP_SKIP_KASAN;复用缓存 vmap 栈时 kasan_hw_tags_enabled() 守卫跳过 kasan_unpoison_range()。
Patch 3(页表页):GFP_PGTABLE_KERNEL 添加 __GFP_SKIP_KASAN,GFP_PGTABLE_USER 自动继承。__GFP_SKIP_KASAN 在非 HW-tags 模式下为 0,软件 KASAN 不受影响。
每次 4KB 页面分配和释放原本需执行约 256 条标签设置指令,移除后直接减少指令数和 cache miss。
系列:[PATCH v7] mm/userfaultfd: detect VMA type change after copy retry in mfill_copy_folio_retry()作者: David Carlier版本: v7(1 个 patch)
mfill_copy_folio_retry() 由 __mfill_atomic_pte() 调用:当在 mmap_lock 下执行 copy_from_user() 失败时需要 drop lock、不持锁重试、再重新获取 VMA 引用。然而在此期间 VMA 可能被其他进程/线程替换为完全不同类型(如被 hugetlb VMA 替换),而调用者仍持有旧的 ops 指针。
__mfill_atomic_pte() 仍持有 stale ops 指针(如 anon_uffd_ops)ops 函数指针导致 kernel crash 或数据损坏mfill_atomic_pte_copy() 对 MAP_PRIVATE 文件映射 VMA 将 ops 覆盖为 anon_uffd_ops,使得简单 ops 比较不足以检测变化在 drop lock 前捕获 vma_uffd_ops(state->vma) 为 orig_ops,重新获取 VMA 后比较 vma_uffd_ops(state->vma) != orig_ops。若 VMA 类型确实变化则返回 -EAGAIN让上层重试。使用 vma_uffd_ops() 而非 caller's ops 进行比较,对于 MAP_PRIVATE 文件映射 VMA 被替换为 anonymous VMA 时不触发虚假 -EAGAIN。
作者未提供性能数据,[消除 TOCTOU 竞态条件:修复后检测到 VMA 类型变化返回 -EAGAIN 重试,避免 stale ops 导致的 crash。-EAGAIN 额外开销仅在 VMA 被并发替换的极端情况下发生]。测试覆盖:uffd-unit-tests 67 项全部通过,uffd-stress anon/shmem/shmem-private 各 4 次 bounce 均通过。
系列:[PATCH v6 00/14] mm/mglru: improve reclaim loop and dirty folio handling作者: Kairui Song版本: v6(14 个 patch)
MGLRU(Multi-Gen LRU)回收循环中 aging、扫描数量计算和回收循环三者紧密耦合,dirty folio 处理逻辑与常规路径差异较大,代码难以理解和维护。生产环境中观察到 MongoDB 等混合读写负载下性能不佳,文件 refault 率偏高,特定压力场景下出现意外 OOM kill。
get_nr_to_scan() 每次迭代重新计算扫描数量,逻辑不清evictable_size 在两处重复实现从整体上重构 MGLRU 回收循环。核心思路:引入 scan budget 机制——循环开始时一次性计算需扫描的 folio 数量(nr_to_scan = lruvec_evictable_size() >> sc->priority),后续每次迭代从中扣除直至预算耗尽或无法继续回收。
具体变更:提取 lruvec_evictable_size() 统一计算可回收 folio 总量;重构 get_nr_to_scan() 与 aging 解耦;should_run_aging() 纯粹判断是否需要 aging,在 DEF_PRIORITY 温和回收中尽量避免 aging 但当只有 3 代 folio 时主动触发;batch 分割提升到调用者层面;scan_folios 返回精确扫描数量,try_to_inc_min_seq 改为 void;dirty/writeback folio 的重新激活逻辑使用公共函数 folio_check_dirty_writeback() 统一处理,writeback 冲刷移入回收循环内部。
两个 OOM 问题被修复。
系列:[PATCH v5 0/4] mm/memory-failure: add panic option for unrecoverable pages作者: Breno Leitao版本: v5(4 个 patch)
在大规模服务器集群中,multi-bit ECC 错误命中内核 slab 页是常见问题。memory_failure() 遇到无法恢复的内核页时当前仅记录"Ignored"并继续运行,意味着已损坏数据仍可被内核访问,导致静默数据损坏或在无关代码路径上延迟崩溃。生产实例:arm64 服务器上 multi-bit ECC 命中 dentry cache slab 页 67 秒后 d_lookup() 访问已中毒缓存行触发 synchronous external abort。
MF_MSG_GET_HWPOISON 而非 MF_MSG_KERNELPatch 1:修复 PageReserved(p) 分类为 MF_MSG_KERNEL 替代 MF_MSG_GET_HWPOISON。
Patch 2:核心 patch。新增 sysctl vm.panic_on_unrecoverable_memory_failure(默认 0),对三类 result == MF_IGNORED 情况触发 panic。MF_MSG_KERNEL_HIGH_ORDER 处理 TOCTOU 竞态:先 cpu_relax() 再重新检查 page_count/mapping/LRU/free buddy,真正确认是内核尾页。
Patch 3:sysctl 文档。Patch 4:selftest 验证用户空间页 SIGBUS 恢复路径不受影响。
作者未提供性能数据,[启用后不可恢复故障第一时间触发 panic,生成包含完整调用栈和硬件错误信息的 crash dump,根因分析从"在无关代码路径崩溃"变为"在故障发生点崩溃",大幅缩短故障定位时间]。
系列:[PATCH v2] mm/gup: honour FOLL_PIN in NOMMU __get_user_pages_locked()作者: Greg Kroah-Hartman版本: v2(1 个 patch)
!CONFIG_MMU 配置下,__get_user_pages_locked() 对每个获取的 page 调用 get_page()(refcount +1),无视 FOLL_PIN。但 pin_user_pages*() 设置 FOLL_PIN,对应的 unpin_user_page() 无条件调用 gup_put_folio(..., FOLL_PIN)——该函数减去 GUP_PIN_COUNTING_BIAS(1024)而非 1。导致 pin +1 、unpin -1024 的严重非对称。
pin_user_pages*() / unpin_user_page() 的 refcount 不对称导致 use-after-free用 try_grab_folio(page_folio(pages[i]), 1, foll_flags) 替代裸 get_page(pages[i])。try_grab_folio() 在 FOLL_PIN 时添加 GUP_PIN_COUNTING_BIAS(1024),FOLL_GET 时添加 1,与 MMU 路径完全对称。v2 改进错误处理:保存 try_grab_folio() 返回值作为错误码传播给调用者。
作者未提供性能数据,[消除 NOMMU 系统严重安全漏洞(use-after-free leading to memory corruption),使 pin_user_pages*/unpin_user_page 在 NOMMU 与 MMU 路径行为一致。由 Anthropic 报告]。
系列:[PATCH v9 0/3] kho: add support for deferred struct page init作者: Michal Clapinski, Evangelos Petrongonas版本: v9(3 个 patch)
CONFIG_DEFERRED_STRUCT_PAGE_INIT 在内核启动时将 struct page 元数据初始化推迟到后期由并行 kthread 完成以加速早期启动。KHO (Kexec HandOver) 的 Kconfig 声明 depends on !DEFERRED_STRUCT_PAGE_INIT,两者互斥,大内存服务器无法同时享受两者的好处。
MIGRATE_CMA 但被 deferred_init_pages() 用 MIGRATE_MOVABLE 覆盖!DEFERRED_STRUCT_PAGE_INIT 阻止大内存系统同时使用 KHO 和延迟初始化Patch 1:新增 kho_get_preserved_page() 函数——遍历 preserved 区域所有页框,对每个调用 init_deferred_page()(已初始化时 no-op)。移除 Kconfig 互斥限制。
Patch 2:将 KHO scratch migratetype 设置集成到现有 page init 路径。新增 memblock_is_kho_scratch_memory()(二分查找 memblock.memory)和 inline 辅助函数 kho_scratch_migratetype(pfn, mt)(属于 scratch 区域返回 MIGRATE_CMA)。在 __init_page_from_nid()、memmap_init()、deferred_free_pages() 和 deferred_init_memmap_chunk() 中使用该函数。删除独立的 kho_release_scratch() 和 memmap_init_kho_scratch_pages()。
Patch 3:CI 配置启用相关 config。
作者未提供性能数据,[消除 KHO 与 DEFERRED_STRUCT_PAGE_INIT 互斥限制,大内存服务器可获得快速启动和在线更新双重能力;kho_scratch_migratetype() 在 !CONFIG_MEMBLOCK_KHO_SCRATCH 时编译为空操作零开销;从根本上防止覆盖问题]。
系列:[PATCH 0/2] mm/damon/sysfs-schemes: fix use-after-free for [memcg_]path作者: SeongJae Park版本: v1(2 个 patch)
mm/damon/sysfs-schemes.c 中 damon_sysfs_scheme_filter->memcg_path 和 damos_sysfs_quota_goal->path 是两个动态分配的字符串缓冲区,通过 sysfs 属性文件读写。_store() 回调 kfree 旧缓冲区后赋新值,_show() 读取指针输出字符串。DAMON 内部使用全局 damon_sysfs_lock 保护 sysfs 目录项并发访问,但用户态直接通过 read()/write() 访问这两个属性文件时未持有该锁。
使用 mutex_trylock(&damon_sysfs_lock) 而非 mutex_lock():避免在 damon_sysfs_lock 已被持有的上下文中被 sysfs 读/写路径调用时导致死锁。_store() 先分配拷贝新字符串(锁外操作),再尝试获取锁——失败返回 -EBUSY 保持旧值不变;成功则 kfree 旧值赋新值。_show() 同理。
作者未提供性能数据,[非竞争场景 mutex_trylock() 几乎总是立即成功仅一次原子操作;竞争场景竞争方收到 -EBUSY 而非 page fault,正确性得到保证。两个 patch 带有 Fixes: 标签和 Cc: stable]。
系列:[PATCH v2 0/5] mm, drm/ttm, drm/xe: Avoid reclaim/eviction loops under fragmentation作者: Matthew Brost版本: v2(5 个 patch)
当系统内存碎片化严重时 Intel Xe GPU 驱动的 TTM 内存管理器陷入病态回收循环:kswapd → shrinker → eviction → rebind(exec ioctl)→ 重复。空闲内存充裕(~2.8GB)但因没有 order-9 连续块,kswapd 持续运行、CPU 占用高、GPU 前向推进严重受损。复现:iGPU + mem=8G + 10 个 Chrome 标签 WebGL aquarium demo,每标签帧率降至 ~2 FPS。
核心 MM 层:zone_appears_fragmented() 轻量级内联启发式——当 zone 空闲页面数超过 high watermark 两倍时判定为碎片化。
TTM 层:对超过 bo->beneficial_order 的分配使用 __GFP_NORETRY 快速返回避免深度回收。新增 ttm_bo_shrink_kswap_fragmented() 辅助函数供驱动 shrinker 调用。
Xe 驱动层:beneficial_order = 9;xe_shrinker_scan() 中 kswapd 上下文 + 碎片化判定成立时尽早返回 SHRINK_STOP。
该系列体现跨子系统协作模式:核心 MM 提供轻量探测接口,TTM 提供分配策略抽象层,驱动层实现最终决策逻辑。
系列:[RFC PATCH 0/9 v2] mm/memcontrol: Make memory cgroup limits tier-aware作者: Joshua Hahn版本: RFC v2(9 个 patch)
在 tiered memory 系统(DRAM + CXL)中,不同类型内存效用不均质。当前 memcg 的 memory.{min, low, high, max} 限制只看内存占用量不区分 tier。占用 10G DRAM 的 cgroup 和占用 10G CXL 的 cgroup 在 memcg 看来"消耗相同",但实际上前者抢占的稀缺资源远多于后者。在多租户场景下先启动的 workload 可独占 DRAM,违反内存隔离初衷。
五层架构:
第一层:cgroup2 mount option memory_tiered_limits(CGRP_ROOT_MEMORY_TIERED_LIMITS)。
第二层:mm/memory-tiers.c 中 toptier_capacity 变量在 establish_demotion_targets() 重建降级目标时更新。提供 mt_scale_by_toptier(val) 缩放函数(mult_frac(val, toptier_capacity, totalram_pages()))。
第三层:struct mem_cgroup 新增独立 struct page_counter toptier,启用 per-CPU stock。try_charge_memcg() 中 folio 节点 node_is_toptier() 时同时向 toptier 记账;uncharge 和 migrate 路径相应处理。
第四层:设置 memcg 限制时,当 mem_cgroup_tiered_limits() 返回 true,将用户 limit 值经 mt_scale_by_toptier() 缩放后写入 toptier page_counter。
第五层:toptier 节点定向回收——当 toptier 用量超限时在 toptier 节点上发起回收将冷内存降级到 lowtier。导出 4 个只读 sysfs 文件 memory.toptier_{min, low, high, max}。
测试环境:1TB 主机(750G DRAM + 250G CXL),4 个 220G workload(3 个 hog + 1 个 victim)。
DRAM 分布从"hog 独占全部 220G"变为"所有 cgroup 均分 181-187G DRAM",CXL 使用也相应均衡。
| Generic Pagewalk API | pt_range_walk API 替代旧 mm_walk_ops,统一 HugeTLB/THP 处理,已转换 smaps/numa_maps/pagemap | |
| DAMON node_eligible_mem_bp | ||
| DAMOS_COLLAPSE action | ||
| DAMON data attributes monitoring | ||
| Dual-bitmap allocator checker | ||
| Dynamic Kernel Stacks | ||
| panic_on_unrecoverable_memory_failure | ||
| Tier-aware memcg limits |
| VMA locks for smaps/numa_maps | ||
| DAMON ilog2 optimization | damon_hot_score() | |
| RWF_DONTCACHE writeback | ||
| Virtio skip redundant zeroing | ||
| Free dead folios in lru_add drain | ||
| Skip redundant sync IPIs | ||
| KASAN skip stack/pgtable tagging | ||
| MGLRU reclaim loop optimization | ||
| KHO deferred page init | ||
| Avoid TTM reclaim/eviction loops |
| Hugetlb resv_map leak | ||
| Vmemmap optimization fixes | ||
| migrate_device spinlock leak | ||
| migrate_device pgtable leak | ||
| Userfaultfd VMA type change | ||
| NOMMU FOLL_PIN refcount skew | ||
| DAMON sysfs use-after-free |