当前位置:首页>Linux>Linux MM 2026-05-01~05 最新 Feature 分析报告

Linux MM 2026-05-01~05 最新 Feature 分析报告

  • 2026-06-21 22:01:39
Linux MM 2026-05-01~05 最新 Feature 分析报告

目录

  1. MGLRU-FG:频率引导的 workingset 晋升与 refault distance 支持
  2. mmap_lock 竞争减少与 page fault 性能提升
  3. 1GB superpageblock 内存分配
  4. 避免碎片化下的 reclaim/eviction 循环
  5. 批量页面清零替代逐页清零
  6. SLUB:延迟 freelist 构造
  7. KSM:优化 rmap_walk_ksm 反向映射遍历
  8. cgroup-bpf mthp_ext:使 mTHP 更加透明
  9. alloc_tag:基于 IOCTL 的过滤接口
  10. RWF_DONTCACHE 脏页追踪提升写入性能
  11. pghot:热页追踪与晋升基础设施
  12. 匿名 PMD 级别直接 COW 支持
  13. mTHP 始终启用支持
  14. DAMON RECLAIM 监控间隔自动调优
  15. 每 cgroup 脏页控制

1. MGLRU-FG:频率引导的 workingset 晋升与 refault distance 支持

系列[RFC PATCH 00/32] mm/mglru: MGLRU-FG and refault distance support作者: Kairui Song版本: RFC v1(32个patch)

背景

多代 LRU(Multi-Gen LRU,MGLRU)自 6.1 合入主线以来,已在生产环境中广泛使用,但在特定工作负载下暴露出两个关键缺陷。首先,现有的 tier 保护机制反馈环路过长:folio 需要经历反复的 refault 才能在 eviction 路径上获得保护,而当保护触发时该 folio 很可能已不再是热点页面。其次,受限于 MAX_NR_TIERS=4 的 tier 数量,当一组 folio 的访问计数超过 tier 上限(即 LRU_REFS_MAX)时 MGLRU 无法区分哪个 folio 更热。匿名页的 tier 行为也不理想:anon folio 的访问会激进地在 tier 0 和 tier 3 之间跳变,极少停留在中间 tier。

此外,与经典 active/inactive LRU 不同,当前 MGLRU 完全缺乏 refault distance 机制。经典 LRU 通过 workingset.c 中的 refault distance 估算来判定一个 refault 页面是否属于 workingset,在 memory size 接近但略小于 workingset 的场景中将纯 LRU 近乎 0% 的命中率大幅提升。MGLRU 缺少此能力,在文件页 workingset 超过总内存、所有文件页陷入最老 generation 不停换入换出时表现很差。

最后,PG_workingset 和 PG_referenced 两个 page flag 在经典 LRU 和 MGLRU 中用不同语义编码,导致 PSI、smap、readahead 等内核机制在 MGLRU 下产生不一致的行为读数。

解决的问题

  • 长反馈环路的 tier 保护:旧 tier 保护仅在 eviction 路径触发,改为在访问时(folio_inc_lru_refs)主动晋升
  • tier 溢出不可区分热度:新设计让达到 LRU_REFS_MAX 的 folio 晋升到下一个 generation 并重置计数,每 8 次访问晋升一代
  • MGLRU 缺乏 refault distance 机制:引入 refault distance 支持允许 MGLRU 识别并保护部分 workingset 驻留在内存中
  • PSI/smap/readahead 不一致:统一 LRU refs API 使子系统在经典 LRU 和 MGLRU 下获得一致的行为
  • DAMON 等组件合入困难:新 API 提供 folio_is_referenced()folio_mark_referenced()folio_is_workingset() 等通用 helper

如何做

整个系列分为三大部分,修改 30 个文件(+916/-598 行)。

第一部分:频率引导的 MGLRU-FG 核心实现。 重新编码 MGLRU 的 folio flag 格式。PG_workingset 和 PG_referenced 不再是独立 flag,而是与 LRU_REFS_MASK 组成统一引用计数。新 tier 计算公式为 fls(refs - 1)(对 refs > 1),tier 0 为冷页,tier 1 为 workingset 可回收页,tier 2+ 有 PID 保护,到达 LRU_REFS_MAX 后再次访问触发晋升并重置。关键的 folio_inc_lru_refs() 实现了无 LRU 锁的懒晋升——使用 folio_test_clear_lru 先隔离 folio,然后通过 try_cmpxchg 原子更新 flag,最后更新 atomic generation 大小计数器。PID 控制器参数从 2:3 调整为 1:2。

第二部分:通用 LRU refs API 与子系统转换。 在 include/linux/mm_inline.h 中引入通用 helper:folio_lru_refs()folio_set_lru_refs()folio_is_referenced()folio_is_workingset()。用新 API 替换 smap、huge_memory、khugepaged、gup/madvise/damon、shmem/vmscan/swap 中的调用。最终 PG_workingset 和 PG_referenced 两个 flag 被完全移除,释放了 page flag 的占用。

第三部分:Refault distance 支持。 重写 mm/workingset.c 的 refault distance 模型。新模型仅在 evict 时递增 nonresident_age,定义 Shadow Position SP = NA@refault - NA@eviction,激活条件 SP <= (NR_ACTIVE + NR_INACTIVE) / 2。使用两者之和而非仅 NR_ACTIVE 作为阈值具有自平衡特性。MGLRU 适配:shadow entry 格式将 seq number 替换为 eviction timestamp,lru_gen_refault() 计算 refault distance 并判断是否激活和恢复引用计数。

收益

测试场景
指标
改进前
改进后
提升幅度
GET/SCAN LevelDB
throughput_avg (Ops/s)
951.12
1344.72
+41.4%
MongoDB YCSB workload B
ops/sec
82,127.65
95,933.99
+16.8%
Kernel build (MGLRU)
refault_file
259,681
201,472
-22.4%
Kernel build (MGLRU)
refault_anon
3,006,202
3,048,107
基本不变
Kernel build (MGLRU)
real time
1m49.005s
1m49.050s
基本不变
FIO with zipf
MB/s
73,557.72
74,553.44
+1.35%

额外收益:PSI/smap/readahead 准确性改善;PG_workingset flag 被完全移除,节省 page flag 占用;MySQL 重测试无性能回退。


2. mmap_lock 竞争减少与 page fault 性能提升

系列[PATCH v2 00/05] mm: reduce mmap_lock contention and improve page fault performance作者: Barry Song, Oven Liyang版本: v2(5个patch)

背景

当前 Linux 内核在 page fault 处理中,当 per-VMA lock 因等待 I/O 完成(如 folio_lock())而释放时,重试 page fault 总会回退到重量级的 mmap_lock(读锁)。Oven Liyang 观察到"most mmap_lock contention and priority inversion come from page fault retries after waiting for I/O completion",并提出了核心思路:"There is no need to always fall back to mmap_lock when the per-VMA lock is released only to wait for the page cache to become ready. On a page fault retry, the per-VMA lock can still be reused."

Barry Song 将该思路扩展到匿名页(anonymous folios)场景。此外,还存在一种更微妙的情况:I/O 已完成、folio 已标记为 uptodate,但因并发线程正在安装 PTE 导致 folio_trylock() 失败,此时完全不需要重试整个 page fault——所谓"likely only waiting for a concurrent PTE mapping to complete, which should be brief"。

Kunwu 和 Lian 还建模了 Matthew Wilcox 描述的场景:当 memcg 限制内存在首次 page fault 时发起 I/O,在重试时这些 folios 可能已被回收(reclaim),导致工作负载几乎没有前进进度(forward progress)。

解决的问题

  1. 文件页(filemap)page fault 重试时不必要的 mmap_lock 回退:per-VMA lock 仅因等待 I/O 而释放,重试时却退化到 mmap_lock,导致"dramatically increased mmap_lock contention"和严重的优先级反转问题(在移动设备上尤为明显)。
  2. 匿名页(swap-in)page fault 重试时同样的回退问题do_swap_page() 中等待 swap I/O 完成后同样可以复用 per-VMA lock,但此前未实现。
  3. folio 已 uptodate 时不必要的 page fault 重试:并发 PTE 安装场景下,folio_lock_or_retry() 本可以短暂自旋等待获取 folio lock,但原逻辑会释放所有锁并重试整个 PF("retrying the entire page fault seems excessive"),浪费 CPU 和 I/O。

如何做

该方案在架构层面引入了一个新的 fault 返回标志 VM_FAULT_RETRY_VMA(数值 0x000800,原 VM_FAULT_FALLBACK 及其后续标志依次后移两位)。语义为:"当 page fault 首次尝试使用了 per-VMA lock 且仅因等待 I/O 完成而释放该锁时,重试应继续使用 per-VMA lock 而非回退到 mmap_lock。"

具体实现分为四个层次:

(1) 文件页 filemap fault(Oven Liyang 的 patch):在 mm/filemap.c 的 filemap_fault() 中,当 folio_lock() 失败且检测到 fpin(表示正在等待 I/O)和 FAULT_FLAG_VMA_LOCK 时,设置 retry_by_vma_lock = true。最终返回 VM_FAULT_RETRY | VM_FAULT_RETRY_VMA

(2) 匿名页 swap-in fault(Barry Song 的 patch):在 mm/memory.c 的 do_swap_page() 中,当 folio_lock_or_retry() 返回 VM_FAULT_RETRY 时,检查 fault_flag_allow_retry_first() 且非 FAULT_FLAG_RETRY_NOWAIT 且持有 FAULT_FLAG_VMA_LOCK,则设置 retry_by_vma_lock = true 并通过 VM_FAULT_RETRY_VMA 传递给架构层。

(3) 架构层适配:在 7 个架构的 page fault 处理函数(arm、arm64、loongarch、powerpc、riscv、s390、x86)中,新增 retry_vma: 标签。当检测到 fault & VM_FAULT_RETRY_VMA 时,直接跳转到 lock_vma_under_rcu() 而非 lock_mmap:。关键代码模式:

retry_vma:
    vma = lock_vma_under_rcu(mm, address);
    ...
if (fault & VM_FAULT_RETRY_VMA)
goto retry_vma;
lock_mmap:

(4) uptodate folio 优化(Barry Song 的两个 patch):对于 swap-in 和 filemap 场景,在调用 folio_lock_or_retry() / lock_folio_maybe_drop_mmap() 之前,若 folio_test_uptodate(folio),则清除 FAULT_FLAG_ALLOW_RETRY 标志。这将告诉锁函数使用阻塞式等待而非释放锁重试,因为"we are likely only waiting for another concurrent PTE mapping to complete, which should be brief. No need to drop the lock and retry the fault."

(5) API 清理:将 folio_lock_or_retry() 从 include/linux/pagemap.h 和 mm/filemap.c 迁移至 mm/memory.c 并 static,因为"effectively only used in mm/memory.c, not in the filemap code"。

收益

1. 抖音(Douyin)warm start on 8GB 智能手机

== mmap_lock Acquisitions And Wait Time ==

Metric
Before (Avg)
After (Avg)
Change
Read Lock Count
20,010
5,719
-71.42%
Read Total Wait (us)
10,695,877
408,436
-96.18%
Read Avg Wait (us)
534.00
71.00
-86.70%
Write Lock Count
838
909
+8.47%
Write Total Wait (us)
501,293
97,633
-80.52%
Write Avg Wait (us)
598.00
107.00
-82.11%

== Read Lock Waiting Time Distribution of mmap_lock ==

Range (us)
Before (Avg)
After (Avg)
Change
[0, 1)
9,927
4,286
-56.82%
[1, 10)
9,179
1,327
-85.54%
[10, 100)
191
88
-53.93%
[100, 1000)
57
6
-89.47%
[1000, 10000)
328
9
-97.26%
[10000, 100000)
328
6
-98.17%
[100000, 1000000)
0
0
N/A
[1000000, +)
0
0
N/A

== Write Lock Waiting Time Distribution of mmap_lock ==

Range (us)
Before (Avg)
After (Avg)
Change
[0, 1)
250
300
+20.00%
[1, 10)
483
556
+15.11%
[10, 100)
52
41
-21.15%
[100, 1000)
12
5
-58.33%
[1000, 10000)
22
4
-81.82%
[10000, 100000)
16
1
-93.75%
[100000, 1000000)
0
0
N/A
[1000000, +)
0
0
N/A

作者总结:"After the optimization, the number of read lock acquisitions is significantly reduced, and both lock waiting time and tail latency are dramatically improved."

2. 多线程文件 page cache thrashing 模型(256核 x86, 500线程, 16MB文件, memcg 限制 MemoryHigh=1G MemoryMax=1.2G)

Case
Total Rounds
Throughput
Miss/Drop(%)
RETRY_MMAP_DROP
RETRY_IO_MISS
Baseline (Run 1)
22,711
37.85 /s
45.04
970,078
436,956
Baseline (Run 2)
23,530
39.22 /s
44.96
972,043
437,077
With Series (Run A)
54,428
90.71 /s
1.69
1,204,124
20,398
With Series (Run B)
35,949
59.91 /s
0.03
327,023
99

作者指出:"Without this series, nearly half of the retries fail to observe completed I/O results, leading to significant CPU and I/O waste. With the finer-grained VMA lock, faulting threads avoid the heavily contended mmap_lock during retries and are therefore able to complete the page fault."

关键指标:RETRY_IO_MISS 从约 437,000 降至 99-20,398(改善超过 95%),说明重试时几乎总能观察到已完成的 I/O 结果。

3. Swap-in 带宽模型(5线程 MADV_PAGEOUT + 5线程 swap-in, 100MB 匿名 VMA, 30秒)

Metric
W/o patches
W/patches
Change
Pageout rounds
1,324,847
1,330,550
+0.43%
Swap-in rounds (approx)
874
1,017
+16.36%

Swap-in 吞吐从 874 轮提升至 1,017 轮,改善 16.36%,同时 swap-out 基本不受影响。


3. 1GB superpageblock 内存分配

系列[RFC PATCH 00/20] 1GB superpageblock memory allocation作者: Rik van Riel, Johannes Weiner版本: RFC v1(20个patch)

背景

现代服务器内存趋向大容量,同时运行多个工作负载,各工作负载对大页的需求不同。当前 Linux 将内存按 2MB pageblock 管理并在此基础上做 defragmentation,这对于 1GB THP(PUD-sized huge pages, 即 PUD 大小的大页)的分配来说粒度太粗。Rik van Riel 指出:"Neither of those are great solutions, given that modern servers tend to be large, often run multiple workloads simultaneously, and each workload wants something else."

本系列的核心设计思路是:"aggressively tries to steer unmovable, reclaimable, and highatomic allocations into those superpageblocks that have already been 'tainted' by such allocations." 目标:"leave as many 1GB superpageblocks as possible used by only movable allocations, so they can be easily defragmented for either regular PMD sized huge pages, or for PUD sized huge pages."

该系列仍处于早期 WIP 阶段,代码"largely written by AI, then nitpicked over by me (with some early feedback from Johannes and Usama)"。

解决的问题

  1. zone->lock 扩展性瓶颈:大机器上 zone->lock 是 page allocation 的扩展性瓶颈。两个典型场景驱动竞争:(a) 亲和性违规(affinity violations):页面在 CPU A 分配但在 CPU B 释放(jemalloc、进程退出、reclaim),"The freeing CPU's PCP drains to zone buddy, and the allocating CPU refills from zone buddy -- both under zone->lock, defeating PCP batching entirely";(b) 并发进程退出时 PCP 容量被打满,串行争抢 zone->lock。
  2. 1GB THP(PUD-sized huge pages)分配失败:因 unmovable 分配散布在几乎所有 pageblock 中,即使有充足的空闲内存也无法找到连续的 1GB 区域。
  3. ABBA 死锁:新 per-CPU pageblock 分配器在 free 路径使用 spin_lock_irqsave() 阻塞式加锁,与分配路径 pcp_spin_trylock()(IRQ 开启)产生 ABBA 死锁。

如何做

该系列的核心变更集中在 mm/page_alloc.c(+575/-163 行)和配套数据结构:

(1) pageblock_data 结构体重构(Johannes Weiner):将 packed bitmap unsigned long *pageblock_flags(共享 unsigned long 中逐位编码)替换为 per-pageblock 的 struct pageblock_data

structpageblock_data {
unsignedlong flags;
int cpu;                    /* owning cpu + 1, or 0 for zone-owned */
unsignedlong block_pfn;    /* first PFN of pageblock */
structlist_headcpu_node;/* per-CPU owned-blocks list */
};

内存开销:x86_64 上约 4KB/GB(原 packed bitmap 约 ~0.5-1 byte/pageblock),但换来热路径 cache line 局部性——migratetype 查找和 ownership 查找现在触及同一 cache line。同时消除了所有位打包索引运算(pfn_to_bitidx、intra-word shifts)。

(2) Per-CPU pageblock buddy 分配器(Johannes Weiner):四级 PCP refill 策略(rmqueue_bulk() 重构):

  • Phase 0: 恢复:从 pcp->owned_blocks 链表恢复之前被 partial drain 到 zone buddy 的已提交 block 片段。
  • Phase 1: 整块获取:从 zone buddy 获取完整 pageblock,声明 PCP 所有权(set_pcpblock_owner),设置 PagePCPBuddy。这些页面有资格在 PCP 级别进行 buddy merging。
  • Phase 2: 子 pageblock chunk:从 zone free lists 按相同 migratetype 获取高阶 chunk,无所有权、无 PagePCPBuddy,仅批处理缓存。
  • Phase 3: __rmqueue() fallback:传统 migratetype fallback。

关键机制:释放路径根据 pageblock_data.cpu 将页面路由回 owning CPU 的 PCP,"This eliminates affinity violations: the owner CPU's PCP absorbs both allocations and frees for its blocks without touching zone->lock."

(3) PCP 级别 buddy mergingfree_pcppages_bulk() 中新增 bottom-up merge pass,仅在 PagePCPBuddy 页面上进行自底向上合并,完全在 pcp->lock 下完成。合并后的完整 pageblock 在 zone->lock 下一并释放。

(4) ABBA 死锁修复(Rik van Riel):将 free 路径从 spin_lock_irqsave 改为始终 spin_trylock_irqsave,失败则通过 free_one_page() 回退到 zone buddy。原因:"callers may hold locks (e.g. xa_lock via slab/stack_depot) that are also taken in hardirq context, and pcp->lock is acquired with IRQs enabled on the allocation side. A blocking lock here would create an ABBA deadlock potential."

收益

作者在 256GB 系统上进行了实验性验证:

指标
数据
Normal zone 中的 superpageblock 总数
238
通常被 unmovable/reclaimable 分配的 superpageblock
少于 20
可分配的 1GB huge pages 数量(带 syzkaller + AI 工作负载)
50
作者预期空闲时可分配的数量
"a lot more if I shut down the workload"

作者原文:"On my 256GB system, I can run syzkaller with AI automatically analyzing crashes... using up all of memory. Out of the 238 superpageblocks in the normal zone, normally less than 20 get used for unmovable and reclaimable allocations. I can allocate 50 1GB huge pages without driving the workload out of memory."

此外,pageblock_data 结构体重构本身带来每对 migratetype+ownership 查询节省 1 次 cache line 访问。BUILD_BUG_ON 检查 NR_PAGEBLOCK_BITS != 4/8 的代码被完全移除——结构体方式天然支持任意位宽。


4. mm/drm/ttm/drm/xe: 避免碎片化下的 reclaim/eviction 循环

系列[PATCH v5 0/5] mm, drm/ttm, drm/xe: Avoid reclaim/eviction loops under fragmentation作者: Matthew Brost版本: v5(5个patch)

背景

Intel Xe GPU 驱动通过 TTM(Translation Table Maps)在高阶分配失败时触发 shrinker 回收,但这一机制在内存碎片化且仍有大量空闲内存时会进入病态的 reclaim/eviction 循环:

"kswapd -> shrinker -> eviction -> rebind (exec ioctl) -> repeat"

在这个状态下,"reclaim is triggered despite substantial free memory, but fails to produce contiguous higher-order pages. The Xe shrinker then evicts active buffer objects, increasing faulting and rebind activity and further feeding the loop." 结果是高 CPU 开销和极差的 GPU 前进进度。

该问题首次在 Freedesktop patchwork 上报告 [1],并被 Intel 内部和 Google 独立观察 [2]。

解决的问题

  1. 内存碎片化下的病态 reclaim/eviction 循环:kswapd 因高阶分配失败而唤醒 shrinker,但回收 native page order 页面无法帮助生成高阶连续块,反而驱逐活跃 GPU 工作集,增加 faulting/rebind 活动,形成正反馈循环。
  2. TTM 直接回收在不合适的 order 上触发:当请求 order 大于 beneficial_order 时,同步直接回收几乎不可能产生所需的高阶连续块,应快速失败而非同步等待回收。
  3. Xe shrinker 在高阶可失败回收时不应 tear down GPU 工作集:对此类 allocation,GPU 驱动的 shrinker 应避免驱逐活跃 buffer objects,因为回收 native order 页面对 compaction 无帮助。

如何做

该方案分 MM、TTM、Xe 三层协同工作:

(1) MM 层:向 shrink_control 传递 order 并引入 opportunistic_compaction 概念

在 struct shrink_control 中新增 s8 order 字段,将触发回收的分配阶数传递给 shrinker。shrink_slab() 签名从 4 参数扩展为 5 参数,贯穿所有调用点(shrink_oneshrink_node_memcgsdrop_slab_node)。

在 vmscan.c 中,kswapd 聚合所有唤醒者的 GFP flags 到一个 per-pgdat 三态枚举 enum kswapd_opportunistic_compaction_type。当所有唤醒者都是可失败高阶分配(__GFP_NORETRY 或 __GFP_RETRY_MAYFAIL,不含 __GFP_NOFAIL)时设置该 hint;任何 order-0 或不可失败唤醒者清除该 hint。核心哲学:"callers that would rather see the allocation fail than have working sets torn down to satisfy it."

(2) TTM 层:限制直接回收至 beneficial_order

在 ttm_pool_alloc_page() 中,修改条件逻辑:当 order 非 0 且 beneficial_order 非 0 且 order != beneficial_order 时,清除 __GFP_RECLAIM(而非原来的仅清除 __GFP_DIRECT_RECLAIM)。这意味着所有不在最优阶数的回收请求都快速失败,同时也将 __GFP_NORETRY 语义上浮到 kswapd 的 opportunistic 判断。

(3) Xe 层:消费 opportunistic_compaction hint

在 Xe shrinker 中,当 shrink_control::opportunistic_compaction 被设置且为高阶扫描 pass 时,shrinker 跳过 TTM backup work 的广告和执行——因为"TTM backup work -- which operates at native page order and would not help compaction -- and avoids tearing down active GPU working sets." Order-0 和非 opportunistic 的回收行为保持不变,因此 shrinker 在真实内存压力下仍完全参与。

收益

复现环境

  • iGPU 系统 + mem=8G
  • 10 个 Chrome tabs 运行 WebGL aquarium demo,每个 tab 约 5k fish
  • Ubuntu 24.04 / 24.10,PTL (Panther Lake) 平台

修复前的观测

/proc/buddyinfo Normal zone:

Count: 4063 4595 3455 3400 3139 2762 2293 1655 643 0 0

约 2.8GB 空闲内存(4063+4595+...+643 = ~28648 页 × 4KB),但 order-9 (2MB) 块数量为 0,order-10 (4MB) 块数量为 0,证实严重碎片化。

ftrace 观测到 xe_shrinker_scan (kswapd) 与 xe_vma_rebind_exec 之间的持续循环。

FPS:每个 tab 约 2 FPS。

修复后的观测

/proc/buddyinfo Normal zone:

Count: 8526 7067 3092 1959 1292 660 194 28 20 13 1

高阶可用性完全恢复,出现 13 个 order-9 (2MB) 块和 1 个 order-10 (4MB) 块。reclaim/eviction 循环被消除,kswapd 活动平息。

指标
Before
After
改善倍数
每 tab FPS (Ubuntu 24.04)
~2
~10
5x
每 tab FPS (Ubuntu 24.10)
~2
~15
7.5x
Normal zone order-9 (2MB) 块
0
13
从零到有
Normal zone order-10 (4MB) 块
0
1
从零到有
kswapd reclaim/eviction 循环状态
持续
消除
定性改善
空闲内存总量
~2.8GB
~4.6GB
+64%(碎片整理后更多可统计 free 页)

作者总结:"With these changes, the reclaim/eviction loop is eliminated."


5. mm/page_alloc:批量页面清零替代逐页清零,HugeTLB 分配 2.68x 加速

系列[PATCH v4] mm/page_alloc: replace kernel_init_pages() with batch page clearing作者: Hrushikesh Salunke版本: v4(1个patch)

背景

当内核配置 init_on_alloc=1 时,页面分配器在将页面交给调用者之前对每个页面清零以防止信息泄露。当前实现 kernel_init_pages() 通过 clear_highpage_kasan_tagged() 逐页清零,内部调用 kmap_local_page()/kunmap_local() 进行临时映射。这种逐页方式有两个开销来源:(1)每页都要执行 kmap/kunmap 操作;(2)架构级清零原语(如 x86 的 rep stos)无法跨连续页面范围一次性执行。当分配大块连续物理内存时(高阶分配、HugeTLB),这种开销非常显著。

解决的问题

  • init_on_alloc=1 下逐页清零的 per-page kmap/kunmap 开销,在大块分配时成为可观测性能瓶颈
  • 逐页清零阻止了架构清零原语在连续物理地址范围上的批量操作

如何做

在 mm/page_alloc.c 中新增静态函数 clear_highpages_kasan_tagged() 替代原有 kernel_init_pages()。在无 HIGHMEM 的 64 位系统上,直接使用 clear_pages(kasan_reset_tag(page_address(page)), numpages) 一次性清零整个连续 page block,完全绕过 per-page kmap 开销。clear_pages() 内部调用架构优化的批量清零原语(x86 上是 rep stosq 风格)。对于 CONFIG_HIGHMEM 系统(主要是 32 位),保留逐页清零 fallback。两个调用点 post_alloc_hook() 和 __free_pages_prepare() 中原有调用被替换。

收益

HugeTLB 分配 8192 个 2MB 页面(共 16GB),init_on_alloc=1

场景
耗时
变化
Before
0.445s
--
After
0.166s
-62.7%(2.68x 加速)

工作负载内核时间(sys time)对比,init_on_alloc=1

工作负载
Before
After
变化
Graph500 64C128T
30m 41.8s
15m 14.8s
-50.3%
Graph500 16C32T
15m 56.7s
9m 43.7s
-39.0%
Pagerank 32T
1m 58.5s
1m 12.8s
-38.5%
Pagerank 128T
2m 36.3s
1m 40.4s
-35.7%

6. mm/slub:延迟 freelist 构造,新鲜 slab 批量分配每对象耗时降低 42%-70%

系列[PATCH v8] mm/slub: defer freelist construction until after bulk allocation from a new slab作者: Shengming Hu版本: v8(1个patch)

背景

SLUB 分配器中,当 allocate_slab() 从伙伴系统获得新 slab 后,传统流程立即构建完整 freelist 将所有对象链接起来,再从 freelist 头部取出对象分配。这在新鲜 slab 上分配全部或大部分对象时是纯粹浪费——构建好的 freelist 被立即消耗掉。代码中有一段 TODO 注释:"possible optimization - if we know we will consume the whole slab we might skip creating the freelist?" 正是此优化的直接动机。

解决的问题

  • 新鲜 slab 创建后立即构造完整 freelist,但 bulk allocation 可能消耗全部对象,导致 freelist 构造工作被完全浪费
  • CONFIG_SLAB_FREELIST_RANDOM=y 和 =n 使用不同构造路径,代码重复且难以统一优化

如何做

核心改变:将 freelist 构造从 slab 分配阶段推迟到对象分配之后。

  1. allocate_slab() 不再构建 freelist,仅分配页面并初始化元数据,slab->freelist 构造被完全延后
  2. 引入 struct slab_obj_iter 迭代器抽象,统一 FREELIST_RANDOM=y(随机遍历)和 =n(顺序递增)到同一个 next_slab_obj() 接口下
  3. alloc_from_new_slab() 改为先分配后构造:用迭代器直接取出对象,然后 build_slab_freelist() 仅对剩余对象构造 freelist。若分配全部对象则直接 slab->freelist = NULL,零开销
  4. alloc_single_from_new_slab() 同样改造;debug 路径上在 alloc_debug_processing() 之前先设置 freepointer(v7→v8 关键修复)
  5. 移除 next_freelist_entry() 和 shuffle_freelist(),代码 156 insertions/144 deletions

收益

slub_bulk_bench(qemu-system-x86, 1024M, 8核, x86_64_defconfig, 20轮, 256MB total):

CONFIG_SLAB_FREELIST_RANDOM=n:

对象大小
batch
Before (ns/obj)
After (ns/obj)
改善
16
256
5.44 ± 0.07
3.12 ± 0.03
-42.6%
32
128
7.57 ± 0.32
3.79 ± 0.07
-49.9%
64
64
11.27 ± 0.09
4.83 ± 0.06
-57.2%
128
32
19.38 ± 0.13
6.43 ± 0.08
-66.8%
256
32
23.59 ± 0.18
6.97 ± 0.07
-70.5%
512
32
21.06 ± 0.14
7.12 ± 0.17
-66.2%

CONFIG_SLAB_FREELIST_RANDOM=y:

对象大小
batch
Before (ns/obj)
After (ns/obj)
改善
16
256
9.42 ± 0.11
4.36 ± 0.19
-53.7%
32
128
12.19 ± 0.62
4.93 ± 0.07
-59.6%
64
64
17.01 ± 0.73
6.14 ± 0.12
-63.9%
128
32
23.71 ± 1.10
8.35 ± 0.18
-64.8%
256
32
29.20 ± 0.35
9.44 ± 1.32
-67.7%
512
32
29.35 ± 0.79
9.21 ± 0.34
-68.6%

作者指出该 benchmark "is intended to isolate the cost removed by this change",收益不仅限于 kmem_cache_alloc_bulk()/kmem_cache_free_bulk() 用户——常规分配负载在从新鲜 slab 重填 sheaf 时同样受益。


7. KSM:优化 rmap_walk_ksm 反向映射遍历,消除 99.9% 无效区间树迭代

系列[PATCH v4 0/5] KSM: Optimizations for rmap_walk_ksm作者: xu xin版本: v4(5个patch)

背景

当 KSM 将多个进程的相同物理页面合并后,对该 KSM 页面的反向映射遍历(rmap_walk_ksm())需要找到所有引用该页面的 VMA。当前实现使用 anon_vma_interval_tree_foreach() 遍历 anon_vma 区间树,但传入的 pgoff_start = 0 和 pgoff_end = ULONG_MAX 覆盖全地址空间,导致遍历器需要检查树中几乎所有 VMA 节点。作者深度调查发现:"rmap_walk_ksm's 99.9% of iterations inside the anon_vma_interval_tree_foreach loop are skipped due to the first check if (addr < vma->vm_start || addr >= vma->vm_end)"。当 KSM 页面被大量进程共享时(如常见 libc 页面被数百个进程映射),线性扫描开销急剧增长。直接用 rmap_item->address >> PAGE_SHIFT 作为 pgoff 在 mremap 场景下错误(mremap 后地址与 pgoff 不再对应)。

解决的问题

  • rmap_walk_ksm() 中 99.9% 的区间树迭代被 vm_start/vm_end 检查跳过,退化为近乎线性扫描
  • mremap 后无法直接用 rmap_item->address 作为 pgoff 缩小搜索范围
  • 缺乏 rmap_walk 路径的可观测性和性能基准测试手段

如何做

通过 5 个 patch 系统性解决问题:

Patch 1: 新增 trace_rmap_walk tracepoint,记录每次反向映射遍历的纳秒级延迟、folio 类型(KSM/anon/file)、folio 地址等信息。这是发现 99.9% 无效迭代问题的关键工具——KSM rmap_walk 单次可达 828,682ns 和 905,966ns。

Patch 2: 新增 tools/testing/rmap/rmap_benchmark.c(488行)用户态 benchmark,通过 move_pages() 触发页面迁移,分别测试 KSM/anon/file 页面的 rmap_walk 延迟。

Patch 3: 在 struct ksm_rmap_item 新增 vm_pgoff 字段,记录 KSM 页面在对应 VMA 中的文件偏移。

Patch 4: 核心优化。rmap_walk_ksm() 利用 rmap_item->vm_pgoff 计算紧凑 pgoff 范围,将区间树搜索从全地址空间缩小到精确匹配的 VMA 范围,消除 99.9% 无效迭代。mremap 场景下 vm_pgoff 在创建 rmap_item 时快照自 vma->vm_pgoff,始终对应 anon_vma 区间树中的正确位置。

Patch 5: KSM + mremap 回归测试,验证 vm_pgoff 方案在 mremap 后 rmap_walk 的正确性。

收益

rmap_walk 延迟对比(20000页,VMA分割后):

页面类型
最大延迟 (ms)
平均延迟 (ms)
事件数
KSM 页面
705.12
532.04
4
匿名页面
0.07
0.05
2
文件页面
0.07
0.03
4

KSM 页面 rmap_walk 延迟高达 705.12ms(0.7秒),而匿名/文件页面仅约 0.05ms——四个数量级的差距。优化后通过精确 vm_pgoff 将搜索范围缩至精确匹配的 VMA 范围,预期 KSM rmap_walk 延迟从 ~500ms 降至微秒级(3-4 个数量级提升)。


8. 通过 cgroup-bpf 引入 mthp_ext 使 mTHP 更加透明

系列[PATCH 00/04] mm: introduce mthp_ext via cgroup-bpf to make mTHP more transparent作者: Vernon Yang版本: 4 个 patch

背景

Linux v6.8 引入多粒度透明大页(mTHP)后,系统仍只能全局固定一个 mTHP 大小,无法根据不同 workload 自动选择不同 mTHP 尺寸。正如作者所述:"a system can simultaneously run multiple different scenarios. However, THP is not beneficial in every scenario -- it is only most suitable for memory-intensive applications that are not sensitive to tail latency." 例如 Redis 对尾延迟(tail latency)敏感,不适合 THP,但实际运维中常因 Redis 问题而全局关闭 THP,导致其他场景无法受益。

之前出现过的方案有 "Cgroup-based THP control"(破坏了 cgroup 层级属性)和 "BPF-MM, BPF-THP"(未解决 per-process 模式问题,且 attach st_ops 到 mm_struct 会面临与 cgroup-bpf 相同的生命周期问题)。本系列旨在综合解决上述所有问题。

解决的问题

  • 场景隔离缺失:无法为不同 cgroup 定制不同 mTHP 大小(如 Redis 用 4KB,数据库用 2MB)
  • 全局配置僵化:为规避 Redis 问题而全局关闭 THP,其他场景损失大页收益
  • 内存压力下自适应缺失:高内存压力时仍使用 mTHP 导致性能崩塌(Redis RPS 从 1.4M 暴跌至 24K)
  • 小内存进程浪费:小内存进程使用 mTHP 造成内存浪费和分配/释放性能下降
  • cgroup 层级破坏问题:只支持 sibling-cgroups attach 多个不同的 ebpf 程序,所有相同 parent-cgroup 使用相同 ebp f程序

如何做

整体方案采用 cgroup-bpf struct_ops 机制,允许 eBPF 程序注册 mthp_choose 回调函数来覆盖内核默认的 mTHP 大小选择逻辑。

1. PSI 统计刷新导出(Patch 1)

新增 psi_group_flush_stats() 函数,将原来 psi_show() 内部的 PSI 平均值刷新逻辑提炼为独立公共接口,使 eBPF 程序能够获取最新的内存压力统计数据。

2. BPF kfunc 扩展(Patch 2)

新增两个 BPF kfunc:

  • bpf_cgroup_flush_stats():刷新指定 cgroup 的 PSI 统计,标记为 KF_SLEEPABLE
  • bpf_cgroup_stall():返回指定 cgroup 的 PSI 全阻塞(PSI_MEM_FULL)总阻塞时间,单位为毫秒

3. bpf_mthp_ops struct_ops 框架(Patch 3)

核心架构变更包括:

  • 定义 struct bpf_mthp_ops,包含 mthp_choose(struct cgroup *cgrp, unsigned long orders) 回调
  • 在 struct cgroup 中添加 struct bpf_mthp_ops *mthp_ops 指针
  • 实现 bpf_mthp_choose() 函数,在 thp_vma_allowable_orders() 中调用,从进程的 mm_struct 找到其 memcg 和 cgroup,然后调用该 cgroup 关联的 BPF 程序的 mthp_choose 回调来过滤 orders
  • 注册(reg)时将 BPF 程序设置到目标 cgroup 及其所有后代 cgroup;注销(unreg)时清除
  • 使用 SRCU(mthp_bpf_srcu)保护对 cgrp->mthp_ops 的并发访问
  • 新增 CONFIG_BPF_TRANSPARENT_HUGEPAGE Kconfig 选项(标记为 EXPERIMENTAL)

关键代码路径:thp_vma_allowable_orders() → bpf_mthp_choose() → ops->mthp_choose(cgrp, orders),BPF 返回的 orders 位掩码与原有 orders 做 AND 操作,从而限缩允许的 mTHP 大小范围。若返回 0 则回退到 4KB。

4. mthp_ext 示例程序(Patch 4)

在 samples/bpf/ 中添加完整的 mthp_ext 用户空间+eBPF 程序,展示实际使用方式:

  • cgroup iter BPF 程序cgroup_scan):遍历根 cgroup 下所有后代 cgroup,对每个有进程的 cgroup 执行以下检查:
    • 若匿名页+共享内存(anon+shmem)用量低于 min_mem(默认 16MB),将 order 设为 0(4KB)
    • 计算当前 PSI 全阻塞时间与上次的差值 delta,若 delta >= threshold(默认 100ms,在 interval=1000ms 窗口内),将 order 设为 0
    • 否则 order 设为 PMD_ORDER(order 9),表示允许所有 mTHP 大小
  • struct_ops BPF 程序mthp_choose_impl):根据 cgrp_storage 中记录的 order 值来限缩 mTHP 的 orders 掩码
  • 用户空间程序:设置 PSI trigger 监控内存压力、周期性触发 cgroup iter 扫描、epoll 等待压力事件、可选 ring_buffer 调试输出

收益

测试环境:x86_64 机器,AMD Ryzen9 9950X 16C32T,32G 内存,8G zram。

Redis benchmark(redis-benchmark, SET 操作, 16 并发)

无内存压力场景(cgroup memory.high=max):

指标
always(全 mTHP)
never(纯 4KB)
always+mthp_ext
redis-noBGSAVE RPS
1,410,824
1,210,388 (-14.2%)
1,265,660 (-10.3%)
redis-noBGSAVE avg_latency (ms)
0.220
0.259 (-17.7%)
0.247 (-12.3%)
redis-noBGSAVE p95_latency (ms)
0.618
0.708 (-14.6%)
0.676 (-9.4%)
redis-noBGSAVE p99_latency (ms)
0.687
0.818 (-19.1%)
0.756 (-10.0%)
redis-BGSAVE RPS
1,418,032
1,212,307 (-14.5%)
1,261,069 (-11.1%)
redis-BGSAVE avg_latency (ms)
0.218
0.259 (-18.8%)
0.248 (-13.8%)
redis-BGSAVE p95_latency (ms)
0.620
0.714 (-15.2%)
0.687 (-10.8%)
redis-BGSAVE p99_latency (ms)
0.684
0.828 (-21.1%)
0.756 (-10.5%)

高内存压力场景(cgroup memory.high=2G):

指标
always(全 mTHP)
never(纯 4KB)
always+mthp_ext
redis-noBGSAVE RPS
24,814
1,049,255 (+4128.5%)
1,063,171 (+4184.6%)
redis-noBGSAVE avg_latency (ms)
13.317
0.302 (-97.7%)
0.298 (-97.8%)
redis-noBGSAVE p95_latency (ms)
23.220
0.754 (-96.8%)
0.828 (-96.4%)
redis-noBGSAVE p99_latency (ms)
369.492
1.154 (-99.7%)
1.615 (-99.6%)
redis-BGSAVE RPS
48,373
1,058,404 (+2088.0%)
1,070,806 (+2113.6%)
redis-BGSAVE avg_latency (ms)
6.884
0.300 (-95.6%)
0.296 (-95.7%)
redis-BGSAVE p95_latency (ms)
16.474
0.743 (-95.5%)
0.820 (-95.0%)
redis-BGSAVE p99_latency (ms)
326.058
1.170 (-99.6%)
1.586 (-99.5%)

unixbench shell8 结果

配置
Score
相对 always 改善
always
23,019.4
基准
never
24,378.3
+5.90%
always+mthp_ext
24,314.5
+5.63%

kernbench 结果

无内存压力时(memory.high=max),mthp_ext 无退化(user time +0.08%, system time -0.02%)。never 配置的 system time 反而恶化 91.18%(因缺少大页导致更多的 page table walking 开销)。

内存限制 2G 时:

指标
always
never
always+mthp_ext
Amean user-32
20,459.89
18,517.24 (+9.49%)
19,963.73 (+2.43%)
Amean syst-32
11,890.63
6,681.95 (+43.80%)
9,395.94 (+20.98%)
Amean elsp-32
1,305.29
928.13 (+28.89%)
1,109.37 (+15.01%)

mthp_ext 将系统时间改善 20.98%,总耗时改善 15.01%。核心洞察:高内存压力下使用全 mTHP(always)性能极差,而 mthp_ext 能自动检测压力并回退到 4KB,比纯 never(无 THP 收益)更好,因为它只在必要时回退。


9. 为内存分配分析引入基于 IOCTL 的过滤接口

系列[PATCH 00/06] alloc_tag: introduce IOCTL-based filtering for MAP作者: Abhishek Bapat, Suren Baghdasaryan版本: 6 个 patch

背景

当前内核内存分配分析(Memory Allocation Profiling, MAP)数据仅通过 /proc/allocinfo 文本接口暴露。该接口虽适合手动查看,但在生产监控和大规模分析中存在严重瓶颈。作者指出三个核心问题:

  1. "Userspace must parse large amounts of text to extract specific fields"(用户空间需解析大量文本来提取特定字段)
  2. "To find specific tags, userspace must read the entire dataset, requiring many context switches and high data copying"(为找到特定 tag,必须读取整个数据集,导致大量上下文切换和数据拷贝)
  3. "The kernel currently aggregates per-CPU counters for every allocation size, even those the user intends to filter out immediately"(内核无条件聚合所有 per-CPU 计数器,即使用户打算过滤掉大部分数据)

解决的问题

  • 生产环境无法高效监控:文本解析开销大、无法增量读取
  • 无内核侧过滤:所有 alloc_tag 数据都必须从内核拷贝到用户空间再过滤
  • 无内容一致性验证:module load/unload 会导致 allocinfo 内容变化,用户无法检测
  • 大量内核 CPU 时间浪费在聚合用户不需要的 counter 上

如何做

方案核心是将 /proc/allocinfo 从纯 seq_file 接口升级为支持 IOCTL 的 proc_ops 接口,新增三个 IOCTL 命令和内核侧过滤逻辑。

Patch 1(Suren):IOCTL 基础框架

将 allocinfo 从 proc_create_seq_private() 改为 proc_create() + 自定义 allocinfo_proc_ops,保持 seq_read_iter / seq_lseek 兼容性。

新增三个 IOCTL 命令(ioctl base 0xA6):

  • ALLOCINFO_IOC_CONTENT_ID_IOR):通过 codetag_get_content_id() 获取内容标识符(基于模块加载计数器 next_mod_seq),用户可在读取前后比较 ID 来验证数据一致性
  • ALLOCINFO_IOC_GET_AT_IOWR):按位置(pos)获取指定 record 的二进制数据(modname, function, filename, lineno, bytes, calls, accurate)
  • ALLOCINFO_IOC_GET_NEXT_IOR):获取上一个已获取 record 的下一个 record,首次调用返回第一条

新增 UAPI 头文件 include/uapi/linux/alloc_tag.h,定义固定大小 64 字节的字符串字段 allocinfo_tag 和 allocinfo_counter 结构体。IOCTL 使用独立的 ioctl_iter 迭代器避免干扰 seq_file 的读取迭代。

Patch 2(Abhishek):基于 tag 的过滤

扩展 ALLOCINFO_IOC_GET_AT 的参数结构体 allocinfo_get_at,新增 allocinfo_filter 字段。过滤维度包括:

  • ALLOCINFO_FILTER_MODNAME:模块名
  • ALLOCINFO_FILTER_FUNCTION:函数名
  • ALLOCINFO_FILTER_FILENAME:文件名
  • ALLOCINFO_FILTER_LINENO:行号

matches_filter() 函数在迭代过程中过滤不匹配的 codetag。ALLOCINFO_IOC_GET_NEXT 也遵守同一个 session 中设置的 filter。

Patch 3(未详读):基于 size 和 accuracy 的过滤

进一步增加按 alloc size 和 accuracy(统计精度)的过滤能力,进一步完善内核侧过滤维度。

收益

测试环境:Intel Xeon Platinum 8481C (224 CPUs),每次运行前清除缓存。

作者描述:"The IOCTL mechanism shows a ~20x performance improvement for filtered queries. The kernel avoids the expensive per-CPU counter aggregation (alloc_tag_read) for any tags that fail the initial string or location filters."

全场景性能对比

场景
传统方式(cat/proc/allocinfo + 过滤)
IOCTL 接口
改善
特定文件过滤(arch/x86/events/rapl.c)
22ms (sys)
1ms (sys)
~22x
复合过滤(文件名 + Size)
21ms (sys)
1ms (sys)
~21x
基于 Size 过滤(min_size = 1MB)
21ms (sys)
14ms (sys)
~1.5x

场景 1 使用 cat /proc/allocinfo | grep rapl.c vs IOCTL 直接过滤,系统时间从 22ms 降至 1ms。场景 2 使用 cat ... | grep | awk vs IOCTL 复合过滤,同样从 21ms 降至 1ms。场景 3 纯 size 过滤的改善较小(21ms 到 14ms),因为内核仍需为所有 tag 做 per-CPU counter 聚合才能判断 size 是否匹配。

总体而言,该方案在标签过滤场景下实现了约 20 倍性能提升,大幅减少了内核到用户空间的无用数据拷贝和上下文切换次数。


10. 通过 RWF_DONTCACHE 脏页追踪提升写入性能

系列[PATCH v6 0/2] mm: improve write performance with RWF_DONTCACHE作者: Jeff Layton版本: v6,2 个 patch

背景

RWF_DONTCACHE 是文件 I/O 中的一个标志,表示写入的数据预期不会被立即读取,应尽快从 page cache 中回收。原始实现中,generic_write_sync() 对每个 DONTCACHE 写入调用 filemap_flush_range(),在写入者上下文中内联(inline)提交 writeback。作者通过 perf lock 竞争分析发现:问题的根源不是锁竞争,而是 writeback 提交工作本身--遍历 page tree 和提交 I/O 会阻塞写入者数毫秒。

正如 patch 2 的 commit message 所述:该方案"inflating p99.9 latency from 23ms (buffered) to 93ms (dontcache)"(将 p99.9 延迟从 23ms 膨胀到 93ms)。

解决的问题

  • DONTCACHE 写入路径尾延迟极高:内联 filemap_flush_range() 在写入者上下文中阻塞数毫秒
  • 无法限制 writeback 范围:要么 flush 整个 BDI 的脏页(刷掉不该刷的),要么仅 flush 当前文件(无法批量化)
  • DONTCACHE 脏页无单独追踪:无法针对性回写 DONTCACHE 脏页
  • DONTCACHE 与 buffered 写入者带宽不公平:二者竞争时 DONTCACHE 写入者被严重饿死(2:1)

如何做

方案的核心思路是将 DONTCACHE 写入的内联 filemap_flush_range() 替换为异步 flusher 线程踢醒(kick),并通过 per-bdi_writeback 的 DONTCACHE 脏页计数器精确控制回写范围。

Patch 1:per-wb DONTCACHE 脏页追踪

新增 WB_DONTCACHE_DIRTY 统计项(enum wb_stat_item),在以下路径准确追踪带有 dropbehind 标志的脏页数量:

  • 递增folio_account_dirtied()):当 folio 设置了 dropbehind 标志时,在递增 WB_RECLAIMABLE 的同时递增 WB_DONTCACHE_DIRTY
  • 递减 1folio_clear_dirty_for_io()):提交 writeback 前清除 dirty 标志时递减
  • 递减 2folio_account_cleaned()):folio 被清理时递减
  • 递减 3__filemap_get_folio_mpol()):非 DONTCACHE 路径的读取遇到带有 dropbehind 标志的 folio 时,使用原子操作 folio_test_clear_dropbehind() 清除标志(防止并发重复递减),并在 mapping_can_writeback() 保护下递减计数器(与递增路径匹配)

Patch 2:flusher 踢醒机制

核心变更包括:

  • 用 filemap_dontcache_kick_writeback() 替代 generic_write_sync() 中的 filemap_flush_range() 调用
  • 新增 WB_start_dontcache 位标志和 WB_REASON_DONTCACHE 回写原因
  • filemap_dontcache_kick_writeback():在 unlocked_inode_to_wb_begin/end 区间内设置 WB_start_dontcache 位(确保正确的 cgroup writeback domain),使用 test_and_set_bit 原子操作合并多个并发 DONTCACHE 写入者的 kick 请求为一次 flusher 运行
  • key设计决策:wb_wakeup() 在 unlocked_inode_to_wb_end() 之后调用,因为前者使用 spin_unlock_irq() 会无条件重新开启中断,而此时 i_pages xa_lock 可能在 irqsave 状态下被持有(cgroup writeback switch 路径)
  • wb_check_start_dontcache():flusher 线程通过 test_and_clear_bit 原子消费 kick 请求,然后使用 wb_stat_sum()(聚合 per-CPU 批次,而非仅读全局计数器的 wb_stat())读取 WB_DONTCACHE_DIRTY 计数器,回写等量页
  • 回写不限于 DONTCACHE 页,而是从最老的 dirty inode 中回写对应数量的页(保持了 I/O 批量化,同时限定了范围)

收益

测试环境:T6F_SKL_1920GBF, 251 GiB RAM, xfs on NVMe, fio io_uring。作者声明:"Buffered and direct I/O paths are unaffected by this patchset. All improvements are confined to the dontcache path."

单流吞吐量(MB/s)

工作负载
修改前
修改后
变化
seq-write/dontcache
298
897
+201%
rand-write/dontcache
131
236
+80%

单流尾延迟改善(seq-write/dontcache)

百分位
修改前
修改后
变化
p99
135,266 us
23,986 us
-82%
p99.9
8,925,479 us
28,443 us
-99.7%

多写入者场景(4 jobs, sequential write)

指标
修改前
修改后
变化
dontcache 总吞吐量 (MB/s)
2,529
4,532
+79%
dontcache p99 (us)
8,553
1,002
-88%
dontcache p99.9 (us)
109,314
1,057
-99%

"Dontcache multi-writer throughput now matches buffered (4,532 vs 4,616 MB/s)."

32 文件写入(Axboe test)

指标
修改前
修改后
变化
dontcache 总吞吐量 (MB/s)
1,548
3,499
+126%
dontcache p99 (us)
10,170
602
-94%
峰值脏页 (MB)
1,837
213
-88%

"Dontcache now reaches 81% of buffered throughput (was 35%)."

竞争写入者(dontcache vs buffered, 不同文件)

指标
修改前
修改后
buffered writer 吞吐量 (MB/s)
868
433
dontcache writer 吞吐量 (MB/s)
415
433
总吞吐量 (MB/s)
1,284
866

修改前 buffered 写入者以 2:1 饿死 dontcache 写入者。修改后两者获得完全均等的带宽。总吞吐量与 buffered-vs-buffered 基准(863 MB/s)匹配,表明无论 I/O 模式如何都公平共享。dontcache 写入者的 p99.9 延迟从 119ms 降至 33ms(-73%),消除了严重周期性卡顿。

Per-bdi_writeback 脏页追踪将 32 文件测试的峰值脏页从 1,837 MB 大幅降至 213 MB。DONTCACHE 顺序写入吞吐量三倍增长,多写入者吞吐量达到与 buffered I/O 持平水平,尾延迟下降 1-2 个数量级。


11. 热页追踪与晋升基础设施 (pghot)

系列[PATCH v7 0/7] mm: Hot page tracking and promotion infrastructure作者: Bharata B Rao, Gregory Price版本: v7(7 个 patch)

背景

在当前的 Linux 内核中,多个子系统(NUMA Balancing、klruscand、folio_mark_accessed 等)各自独立检测内存页面访问,导致热页检测逻辑分散、互不统一。NUMA Balancing 的 tiering 模式(kernel.numa_balancing=2)将热页检测、分类和晋升全部耦合在调度器代码路径内,限制了代码复用和策略扩展性。同时,随着 CXL 内存等异构内存层级(Tiered Memory)的普及,统一的、来源无关的热页追踪框架成为一个明确的架构需求。

解决的问题

  • 热页检测逻辑分散于多个子系统,无法复用同一套晋升速率限制和策略。
  • NUMA Balancing 的晋升逻辑内嵌于调度器(kernel/sched/fair.c),难以被其他热页来源(如硬件 hints)利用。
  • 缺乏独立于 VMA 上下文的 batched 迁移能力——内核线程发起的迁移无法获取 VMA 信息。

如何做

pghot 子系统引入了一套热页追踪与晋升基础设施,核心包含以下组件:

1. 热度记录(Per-PFN hotness record):嵌入到现有 mem_section 数据结构中。默认模式使用 1 字节(u8)记录:2 bit 访问频率(4 个级别)、5 bit 桶化时间(HZ=1000 时约 4s 窗口)、1 bit 迁移就绪标志(MSB)。精度模式(CONFIG_PGHOT_PRECISE)扩展至 4 字节(u32):10 bit 目标节点 NID、3 bit 频率(8 个级别)、14 bit 时间(约 16s 窗口)、1 bit 迁移就绪标志。mem_section->hot_map 指针的 LSB 被复用为 per-section "hot" 标志,用于门控扫描。

2. 事件记录 APIpghot_record_access(pfn, nid, src, now) 供各来源调用。来源包括 hint faults(PGHOT_HINTFAULTS)和 AMD IBS Memory Profiler 硬件 hints(PGHOT_HWHINTS)。

3. 晋升引擎(kmigrated):每个低层节点(lower-tier node)一个 kmigrated 内核线程,周期性扫描被标记为 hot 的 section,检查迁移就绪位,批量调用 promote_misplaced_memcg_folios() 进行迁移。该函数内部使用 migrate_pages() 实现批量迁移,要求列表内所有 folio 属于同一 memcg,以正确归属 NUMA_PAGE_MIGRATE 统计。

4. 调度器解耦:将 NUMA Balancing tiering 模式的晋升逻辑从 kernel/sched/fair.c 移至 pghot,包括迁移速率限制(migration rate limiting)和动态阈值逻辑。引入新的 CONFIG_NUMA_BALANCING_TIERING 配置选项控制 hint faults 来源。

5. IBS Memory Profiler 集成:新增 arch/x86/mm/ibs-mprof.c 驱动,将 AMD IBS Memory Profiler(未来 AMD 处理器中的硬件内存访问分析器)作为热度来源接入 pghot。

收益

指标
数据
测试条件
默认模式内存开销
1 字节/PFN,1TB 低层内存约 256MB
假设 4K 页
精度模式内存开销
4 字节/PFN,1TB 低层内存约 1GB
假设 4K 页
时间窗口(默认模式)
约 4s(HZ=1000)
5 bit 桶化时间
时间窗口(精度模式)
约 16s(HZ=1000)
14 bit 时间字段

微基准测试(Micro-benchmark,64 线程,8GB 随机访问,2 节点 AMD + CXL):

来源
基础内核耗时 (us)
pghot-default 耗时 (us)
NUMAB0(无迁移)
181,393,365
184,331,381
NUMAB2(hint faults 晋升)
42,287,528
--
HWHINTS(IBS 硬件 hints)
N/A
50,422,862
统计项
Base-NUMAB2
Pghot-default-HWHINTS
pgpromote_success
2,097,152
1,961,087
numa_hint_faults
2,358,069
0
pghot_recorded_accesses
N/A
1,962,696
pghot_recorded_hwhints
N/A
5,532,979
hwhint_total_events
N/A
5,532,979

Graph500 基准测试(mpirun -n 128,3 节点 AMD + CXL):

模式
harmonic_mean_TEPS
mean_time
pgpromote_success
Base NUMAB0(无迁移)
4.09614e+08
10.4853
0
Base NUMAB2
1.28401e+09
3.34492
13,746,029
pghot-default NUMAB2
1.47926e+09
2.90342
13,412,213
pghot-default HWHINTS
1.52334e+09
2.81941
3,415,599

作者指出:"HWHINTS with pghot is able to provide similar benchmark numbers even when not migrating as aggressively as base NUMAB2",即 IBS 硬件 hints 在迁移页数仅为基础 NUMAB2 的 1/4(341 万 vs 1374 万)时达到了相近的性能。此外,在多 toptier 节点场景,精度模式(pghot-precise)比默认模式显著提升了 harmonic_mean_TEPS(7.45e+08 vs 5.46e+08),因为精度模式能够追踪每个 PFN 的目标 NID。


12. 匿名 PMD 级别直接 COW 支持

系列[PATCH 0/5] mm: Support selecting doing direct COW for anonymous pmd entry作者: Luka Bai版本: v1(5 个 patch)

背景

当前匿名大页(anonymous THP)的 COW(Copy-On-Write)处理方式高度保守:当两个进程共享一个 2MB 的匿名 PMD,子进程触发写操作时,内核将整个 PMD 拆分为 512 个 4K PTE,仅在写操作的 4K 页面做 COW。这种方法内存效率优秀,但存在两个根本性问题:

  1. TLB 收益突然消失:拆分后子进程的页表映射从 1 个 PMD 变为 512 个 PTE,大页的 TLB 优势瞬间丧失,造成"可观测的性能退化"(observable performance degeneration)。之后只能被动等待 khugepaged 重新合并,而这个过程"并不容易触发"(not easy to happen)。

  2. **THP 的"不确定性"**:THP 在回收(reclaim)、COW 等常见路径上被透明拆分,导致"吞吐量波动"(throughput fluctuation)。对于需要大页稳定性的工作负载,THP 缺乏类似 hugetlbfs 的"确定性"。

解决的问题

  • 写时复制后 PMD 被无条件拆分为 512 个 PTE,TLB 收益瞬间丢失。
  • THP 在 COW 场景下的不可预测性——应用无法预先知道大页是否会被保留。
  • 缺乏按 VMA 粒度控制 COW 行为的机制。

如何做

系列引入了一套三态策略控制机制(always/madvise/never),与现有 THP 控制接口保持一致:

1. madvise 接口扩展:定义新的 madvise 行为区域 MADV_THP_SETUP_BASE(256 起始),新增 MADV_THP_COW 标志位。使用 VMA 的 vm_flags 第 43 位(VM_THP_COW)持久化 per-VMA 配置。madv_thp_behavior() 和 madv_thp_cow() 辅助函数封装行为判断。

2. sysfs 接口:新增 /sys/kernel/mm/transparent_hugepage/thp_cow,支持 always(所有 THP 始终做 PMD 级 COW)、madvise(按 VMA 标记决定)、never(永不 PMD 级 COW,回退到 PTE 级)。引入 TRANSPARENT_HUGEPAGE_COW_FLAG 和 TRANSPARENT_HUGEPAGE_REQ_MADV_COW_FLAG 标志位。

3. COW 判断辅助函数hugepage_cow_always()hugepage_cow_madvise() 和 hugepage_cow_enabled(vma) 封装策略查询。hugepage_cow_enabled() 要求全局 THP 已启用(always 或 madvise),然后根据 COW 策略和 VMA 标志做出决策。

4. PMD 映射重构:修改 map_anon_folio_pmd_nopf() 函数签名,接受 struct vm_fault *vmf 参数而非直接传递 pmd/vma/haddr,以支持 FAULT_FLAG_UNSHARE 路径。在 unshare 场景下,复制原 PMD 的 soft_dirty 和 uffd_wp 标志而非设置写权限。

5. 核心 COW 实现:新增 wp_huge_pmd_page_copy() 函数,完整实现 PMD 级别 COW:分配新匿名 2MB folio → copy_user_large_folio() 拷贝数据 → kmsan 元数据复制 → MMU notifier 保护 → 清除旧 PMD entry → 刷新 TLB → 映射新 folio → 清理旧 folio 的 rmap。在 do_huge_pmd_wp_page() 中,当 hugepage_cow_enabled() 返回 true 且 folio 引用计数不为 1 时,走 PMD 级 COW 路径。

作者描述道:对于共享匿名 PMD 写时复制,子进程"its pmd mapping will be split into 511 4K ptes which still point to the original pmd sized folio, and 1 4K pte pointing to the new 4K page"——虽然内存利用率好,但"it also make the tlb gain caused by pmd entry suddenly 'vanish'"。

收益

作者未提供量化性能数据。从代码逻辑推断,预期收益包括:

  • 保留 COW 后的 PMD 级 TLB 覆盖,避免 512 条目 TLB 压力骤增导致的性能退化。
  • 通过 sysfs 和 madvise 的双层控制,允许工作负载根据自身特点(内存敏感 vs 延迟敏感)选择策略。
  • THP 在 COW 后保持大页形态,降低 khugepaged 重新合并的负载。

13. mTHP 始终启用支持

系列[PATCH v4 0/9] mm: thp: always enable mTHP support作者: Luiz Capitulino版本: v4(9 个 patch)

背景

当前 THP 代码中,如果架构实现了 has_transparent_hugepage() 且返回 false(即 CPU 不支持 PMD 大小的页面),内核会禁用所有透明大页(THP),包括 multi-size THP(mTHP)。例如,某些嵌入式架构或配置下 CPU 不支持 PMD 大小但支持 64KB 等中间大小,却因为 PMD 检查而被完全剥夺 mTHP 的能力。此外,内核缺乏一个明确定义的 API 来查询 PMD 大小的页面支持——has_transparent_hugepage() 和 thp_disabled_by_hw() 语义模糊,与 THP 支持概念过度绑定。

解决的问题

  • PMD 不支持时 mTHP 全部被禁用,即使 CPU 支持 64KB 等其他大页大小。
  • has_transparent_hugepage() 名称暗示检查 THP 是否启用,实际仅检查 PMD 大小页面支持,语义混乱。
  • thp_disabled_by_hw() 本质是缓存版的 has_transparent_hugepage(),仅用于快速路径,名称同样误导。

如何做

系列引入全新的明确定义的 API pgtable_has_pmd_leaves(),并在此基础上解耦 PMD 级别 THP 与 mTHP:

1. pgtable_has_pmd_leaves() API:使用 static key(__arch_has_pmd_leaves_key)实现,默认启用(DEFINE_STATIC_KEY_TRUE)。通过 init_arch_has_pmd_leaves() 在 start_kernel() 中初始化,位于 parse_early_param() 之后、parse_args() 之前,确保 early_param 处理程序可以修改 CPU 标志。当 !has_transparent_hugepage() 时,通过 static_branch_disable() 关闭。无 MMU 时直接返回 false。

2. 全树范围替换:用 pgtable_has_pmd_leaves() 替换所有 thp_disabled_by_hw() 调用点(mm/huge_memory.cmm/memory.cmm/shmem.c),并移除 TRANSPARENT_HUGEPAGE_UNSUPPORTED 标志位和 thp_disabled_by_hw() 函数。drivers(dax、nvdimm)、debug_vm_pgtable 和 shmem 中的 has_transparent_hugepage() 调用也全部替换。

3. mTHP 始终启用:关键变更在 hugepage_init() 中——删除 if (!pgtable_has_pmd_leaves()) return -EINVAL; 早期返回(此前不启用任何 THP)。在 __thp_vma_allowable_orders() 和 shmem_allowable_huge_orders() 中,当 PMD 不支持时仅过滤掉 PMD_ORDER 和 PUD_ORDER,保留所有 mTHP 大小。sysfs 中也不为 PMD/PUD 创建条目。huge_anon_orders_inherit 和 huge_shmem_orders_inherit 仅在 PMD 支持时才默认设为 BIT(PMD_ORDER)

收益

作者未提供量化性能数据。预期收益为:

  • 在 CPU 不支持 PMD 大小的平台上,应用仍然可以利用 mTHP(如 64KB、1MB 等中间大页)获得 TLB 收益。
  • 消除 has_transparent_hugepage() 和 thp_disabled_by_hw() 的语义歧义,使代码审查者和维护者能清楚理解每个检查点的真实意图。
  • 统一的 API 使未来添加新的大页大小或架构支持更加简洁。

14. DAMON RECLAIM 监控间隔自动调优

系列[PATCH 0/2] mm/damon/reclaim: support monitoring intervals auto-tuning作者: SeongJae Park版本: v1(2 个 patch)

背景

DAMON (Data Access MONitor) 的监控间隔(sampling interval & aggregation interval)对回收效率有直接影响:采样间隔过短导致 CPU 开销过高,过长则可能错过短暂的访问模式(access pattern)。DAMON 核心层已经实现了监控间隔自动调优功能,作者表示该功能"在多种环境中被证明有用"(has proven to be useful in multiple environments)。然而,DAMON_RECLAIM(基于 DAMON 的主动内存回收模块)目前仍要求用户手动调整这些间隔参数,使用门槛高且难以达到最优配置。

解决的问题

  • DAMON_RECLAIM 用户需要手动调节 sample_interval 和 aggr_interval 参数,缺乏自动调优能力。
  • 手动调优难以在"捕获足够访问事件"和"控制 CPU 开销"之间找到平衡。

如何做

新增 autotune_monitoring_intervals 模块参数(默认 disable),当设置为 Y 时,DAMON_RECLAIM 利用 DAMON 核心的 intervals_goal 机制自动调优:

  • 初始设置:sample_interval = 5000 us(5ms),aggr_interval = 100000 us(100ms)。
  • 目标:每个 DAMON 快照中捕获有意义数量的访问事件(access_bp = 40,即 40 基点),聚合周期数为 3(aggrs = 3)。
  • 边界:最小采样间隔 5ms(min_sample_us = 5000),最大采样间隔 10s(max_sample_us = 10 * 1000 * 1000)。

在 damon_reclaim_apply_parameters() 中,当自动调优启用时,用自动调优参数覆盖用户手动设置的 damon_reclaim_mon_attrs 并传递给 damon_set_attrs()。同时将动态调整后的 aggr_interval 传递给 damon_reclaim_new_scheme(),确保 min_age 计算(min_age / aggr_interval)与实时间隔保持正确比例。

作者特别说明:使用自动调优后,建议将 min_age 设置得足够短,并结合使用 quota_mem_pressure_us 等冷度阈值自动调优功能。

收益

作者未提供量化性能数据,但引用了 DAMON 核心层自动调优在多种环境中已验证有效的经验。预期收益为:

  • 降低 DAMON_RECLAIM 的调优门槛,用户无需理解 sampling/aggregation 的深层含义。
  • 自动平衡 CPU 开销和回收精度:在访问稀疏时降低采样频率节省 CPU,在访问密集时提高频率保证回收时机准确。
  • min_age 随间隔等比例动态调整,保持回收方案的冷热判断一致性。

15. 每 cgroup 脏页控制

系列[PATCH RFC] memcg: add per-cgroup dirty page controls作者: Alireza Haghdoost, Kshitij Doshi版本: RFC(1 个 squashed patch)

背景

balance_dirty_pages()(BDP)是一个全局写节流机制。一旦宿主机范围内的脏页计数超过全局阈值,所有写者都被停顿在 io_schedule_timeout() 中——包括那些"一页都没有弄脏的" cgroup。cgroup v2 已有 per-memcg 脏页统计(NR_FILE_DIRTY/NR_WRITEBACK),但这些统计没有转化为 per-memcg 的节流策略。

作者描述了生产环境中的真实问题:"a buffered write-heavy container generates multi-second stalls for co-located latency-sensitive workloads"。即一个缓冲写入密集容器产生多秒级停顿,影响共存的延迟敏感工作负载。此外,"dirty-page accumulation from a single noisy neighbor is a recurring contributor to mount-responsiveness degradation on shared hosts"。

该问题有着悠久的历史脉络:Andrea Righi(2010)、Greg Thelen(2011 v9)和 Konstantin Khlebnikov(2015)都提出过 per-memcg 脏页限制。Tejun Heo 的 cgroup-writeback 重构(Linux 4.2)提供了基础架构但有意推迟了用户面策略接口。2023 年 Chengming Zhou 独立发现了相同的节流退化并得到 Jan Kara 认可,但没有后续 patch。本系列填补了这个长达 15 年的空白。

解决的问题

  • "吵闹邻居"问题(noisy neighbor):一个高写入 cgroup 触发全局 BDP,导致同宿主机上未写入的 cgroup 也被停顿。
  • 缺乏 per-cgroup 脏页天花板,无差别节流影响延迟敏感工作负载。

如何做

新增两个 cgroup v2 内存控制器文件(仅对非根 cgroup 可见,CFTYPE_NOT_ON_ROOT):

**memory.dirty_ratio**(0-100):per-cgroup 脏页天花板,以 cgroup 的 dirtyable memory(mdtc->avail,即文件缓存 + 可回收 slack)为基数。0 表示继承全局阈值。实现上,domain_dirty_limits() 中通过 mult_frac(available_memory, cg_ratio, 100) 计算 cgroup 阈值,与全局阈值取 min(),确保"the knob can never widen the cgroup past the global ceiling"。当 clamp 生效时,用 memcg 级的 NR_FILE_DIRTY + NR_WRITEBACK 替代 per-wb 脏页计数,防止页面从 NR_FILE_DIRTY 迁移到 NR_WRITEBACK 时绕过限制。

**memory.dirty_min**(字节值):脏页保障地板。当 cgroup_dirty < dirty_min 时,写者跳转到 free_running,完全绕过 BDP 节流。这是基于写者(writer-keyed)的保障,而非 inode 拥有者视角。实现于 balance_dirty_pages() 的循环开头:以 rcu_read_lock() 获取 current->memcgREAD_ONCE(dirty_min) 做快照检查。当脏页量低于预约量时,goto free_running

快速路径开销(两个 knob 均未设置时):每 balance_dirty_pages() 迭代一次 rcu_read_lock/unlock 对 + READ_ONCE(dirty_min),每 domain_dirty_limits() 调用一次 READ_ONCE(dirty_ratio)。作者预期这些开销"in the noise of existing BDP bookkeeping"。

收益

测试环境: QEMU 客户机,virtio-blk 磁盘限速 256 KB/s(bps_wr=262144)。两个同级 cgroup,无 io.weight,共享磁盘带宽。dirty_bytes=32MBdirty_background_bytes=16MB(freerun = 24MB)。Noisy cgroup:单个 fio 作业,无限制写入速率;Victim cgroup:单个 fio 作业,限制 500 KB/s(128 IOPS 目标),4KiB 顺序 write()memory.dirty_min = 16MB。N=5 次迭代取中位数。

原版内核(问题):

指标
Solo(无竞争)
Contended(竞争)
退化倍数
Victim IOPS
125
5.1
24.5x worse
Victim p99 延迟
0.6 ms
152 ms
253x worse

竞争场景下 p99 延迟触及 BDP 硬上限 MAX_PAUSE = HZ/5 = 200ms。victim 自身几乎没有脏页,但被迫睡眠因为全局脏页计数超过阈值。

修改后内核(修复):

指标
Solo(无竞争)
Contended(竞争)
退化倍数
Victim IOPS
125
125
1.0x
Victim p99 延迟
0.9 ms
0.7 ms
1.0x

作者总结道:"The 5/5 stock iters all hit BDP's throttled regime; the 5/5 patched iters all bypassed it via the dirty_min check."——5 次迭代中原版全部触发节流,修改后全部通过 dirty_min 检查绕过。


总结

性能优化

#
系列
核心量化数据
版本
8
cgroup-bpf mTHP
Redis 高压力下 RPS **+4184.6%**,p99 延迟 **-99%**;kernbench sys time -20.98%
v1, 4p
5
批量页面清零
HugeTLB 16GB 分配 2.68x 加速;Graph500 sys time -50.3%
v4, 1p
6
SLUB 延迟 freelist
新 slab 分配每对象耗时 **-42%~-70%(RANDOM=n)/-53%~-69%**(RANDOM=y)
v8, 1p
4
避免 reclaim/eviction 循环
WebGL aquarium 2→10-15 FPS(5-7.5x);order-9 block 0→13
v5, 5p
2
mmap_lock 竞争减少
读锁获取 **-71.42%**,总等待时间 **-96.18%**;thrashing 吞吐 +140%
v2, 5p
1
MGLRU-FG
LevelDB **+41.4%**,MongoDB **+16.8%**;refault_file -22.4%
RFC, 32p
15
per-cgroup dirty 控制
Victim IOPS 从 5.1 恢复至 125(24.5x),p99 从 152ms 降至 0.7ms
RFC, 1p
10
RWF_DONTCACHE
单流顺序写吞吐 **+201%**(298→897 MB/s),p99.9 延迟 -99.7%
v6, 2p
9
alloc_tag IOCTL
带过滤的系统时间 22ms→1ms(-95.5%)
v1, 6p
7
KSM rmap_walk 优化
消除 99.9% 无效区间树迭代;延迟预期 705ms→微秒级
v4, 5p
3
1GB superpageblock
256GB 系统 <20/238 被污染,50×1GB huge page 可分配
RFC, 45p
12
匿名 PMD 直接 COW
避免 COW 时 THP 拆分,保留 PMD 级 TLB 收益
v1, 5p
11
pghot 热页追踪
Graph500 TEPS **+15%**;IBS 仅迁移 1/4 页面即达相近性能
v7, 7p
13
mTHP 始终启用
在无 PMD 平台上仍可用 mTHP 获取部分大页收益
v4, 9p
14
DAMON reclaim 自动调优
自动调整监控间隔提高回收效率
v1, 2p

新机制 / 新接口

#
系列
要点
1
MGLRU-FG
 [RFC, 32p]
统一 LRU refs API + 频率引导懒晋升 + MGLRU refault distance;移除 PG_workingset/PG_referenced
11
pghot
 [v7, 7p]
统一 NUMAB2 和 AMD IBS 热页源,per-PFN 粒度热度追踪 + kmigrated 批量晋升
8
cgroup-bpf mTHP
 [v1, 4p]
cgroup-bpf struct_ops 自适应 mTHP 大小选择 + PSI 内存压力感知
15
per-cgroup dirty
 [RFC, 1p]
cgroup v2 dirty_ratio/dirty_min knobs,per-cgroup 粒度 balance_dirty_pages 节流
9
alloc_tag IOCTL
 [v1, 6p]
/proc/allocinfo 的 IOCTL 二进制接口,内核侧多维度过滤
14
DAMON auto-tuning
 [v1, 2p]
DAMON reclaim 监控间隔自适应调优

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-03 08:28:48 HTTP/2.0 GET : https://f.mffb.com.cn/a/491653.html
  2. 运行时间 : 0.154219s [ 吞吐率:6.48req/s ] 内存消耗:5,059.52kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=2913ee7d32a7d938b04deb40d3bc6a94
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000628s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000781s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000295s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000258s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000506s ]
  6. SELECT * FROM `set` [ RunTime:0.000244s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000541s ]
  8. SELECT * FROM `article` WHERE `id` = 491653 LIMIT 1 [ RunTime:0.010381s ]
  9. UPDATE `article` SET `lasttime` = 1783038528 WHERE `id` = 491653 [ RunTime:0.006653s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.003956s ]
  11. SELECT * FROM `article` WHERE `id` < 491653 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.006175s ]
  12. SELECT * FROM `article` WHERE `id` > 491653 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.002023s ]
  13. SELECT * FROM `article` WHERE `id` < 491653 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.004035s ]
  14. SELECT * FROM `article` WHERE `id` < 491653 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.003103s ]
  15. SELECT * FROM `article` WHERE `id` < 491653 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.042277s ]
0.156625s