diff --git a/study/kernel/00-DESCRIPTION/MEMORY_MANAGER.md b/study/kernel/00-DESCRIPTION/MEMORY_MANAGER.md index d442765..3bde85f 100644 --- a/study/kernel/00-DESCRIPTION/MEMORY_MANAGER.md +++ b/study/kernel/00-DESCRIPTION/MEMORY_MANAGER.md @@ -1942,7 +1942,7 @@ LRU 组织形式的变更和 LRU lock 的变更是无法割裂开的. 每次 LRU | 时间 | 作者 | 特性 | 描述 | 是否合入主线 | 链接 | |:----:|:----:|:---:|:----:|:---------:|:----:| | 2007/12/27 | Kamezawa Hiroyuki | [per-zone and reclaim enhancements for memory controller take 3](https://lore.kernel.org/patchwork/cover/98042) | per-zone LRU for memcg, 其中引入了 mem_cgroup_per_zone, mem_cgroup_per_node 等结构 | v7 ☑ 2.6.25-rc1 | [PatchWork v7](https://lore.kernel.org/lkml/20071127115525.e9779108.kamezawa.hiroyu@jp.fujitsu.com) | -| 2011/12/08 | Johannes Weiner | [memcg naturalization -rc5](https://lore.kernel.org/patchwork/cover/273527) | 引入 per-memcg lru, 消除重复的 LRU 列表, [全局 LRU 不再存在, page 只存在于 per-memcg LRU list 中](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=925b7673cce39116ce61e7a06683a4a0dad1e72a).
它使传统的页面回收能够从每个memcg LRU列表中查找页面, 从而消除了双LRU模式(除了每个memcg区域外, 每个全局区域)和系统中每个页面所需的额外列表头.
该补丁引入了 lruvec 结构. | v5 ☑ [3.3-rv1](https://kernelnewbies.org/Linux_3.3#Memory_management) | [PatchWork v5](https://lore.kernel.org/patchwork/cover/273527), [LWN](https://lwn.net/Articles/443241) | +| 2011/12/08 | Johannes Weiner | [memcg naturalization -rc5](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=6b208e3f6e35aa76d254c395bdcd984b17c6b626) | 引入 per-memcg lru, 消除重复的 LRU 列表, [全局 LRU 不再存在, page 只存在于 per-memcg LRU list 中](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=925b7673cce39116ce61e7a06683a4a0dad1e72a).
它使传统的页面回收能够从每个memcg LRU列表中查找页面, 从而消除了双LRU模式(除了每个memcg区域外, 每个全局区域)和系统中每个页面所需的额外列表头.
该补丁引入了 lruvec 结构. | v5 ☑ [3.3-rc1](https://kernelnewbies.org/Linux_3.3#Memory_management) | [PatchWork v5](https://lore.kernel.org/patchwork/cover/273527), [LWN](https://lwn.net/Articles/443241) | | 2012/02/20 | Hugh Dickins | [mm/memcg: per-memcg per-zone lru locking](https://lore.kernel.org/patchwork/cover/288055) | per-memcg lru lock | v1 ☐ | [PatchWork v1](https://lore.kernel.org/patchwork/cover/288055) | | 2020/12/05 | Alex Shi | [per memcg lru lock](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=15b447361794271f4d03c04d82276a841fe06328) | per memcg LRU lock | v21 ☑ [5.11](https://kernelnewbies.org/Linux_5.11#Memory_management) | [LORE v21,00/19](https://lore.kernel.org/all/1604566549-62481-1-git-send-email-alex.shi@linux.alibaba.com) | @@ -2192,50 +2192,55 @@ v2.6.17-rc1 [commit 1742f19fa920 ("vmscan: rename functions")](https://git.kerne [commit 5d5d36943259 ("Speed freeing memory for suspend.")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5d5d36943259171d2a90f2499541c8a8f4e90d54) -* 另外一个维护:匿名页和文件页的平衡(anon/file reclaim balancing) +* 另外一个平衡的维持:匿名页和文件页的平衡(anon/file reclaim balancing) -前面所有的操作都在处理如何维持 active 和 inactive LRU 列表的平衡, 但是 LRU 中页面也是分类型的, 虽然这个时候还没有区分 ANON LRU 和 FILE LRU, 但是维持匿名页和文件页的平衡也是有意义的. +前面所有的操作都在处理如何维持 active 和 inactive LRU 列表的平衡, 但是 LRU 中页面也是分类型的, 虽然这个时候还没有区分 ANON LRU 和 FILE LRU, 但是维持匿名页和文件页的平衡也是有意义的. 这个我们会在下一节展开讲. -早在 v2.5.43 [commit 8f7a14042e1f ("reduced and tunable swappiness")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8f7a14042e1f5fc814fa60956e3a2dcf5744a0ed) 就对此进行了探索, 引入 `/proc/sys/vm/swappiness` 控制 VM 取消页面映射(回收文件页)和交换内容(回收匿名页)的倾向. -随后 v2.6.25-rc1 引入 memcg LRU 的过程中, [commit ("per-zone and reclaim enhancements for memory controller: modifies vmscan.c for isolate globa/cgroup lru activity")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1cfb419b394ba82745c54ff05436d598ecc2dbd5) 将这个流程封装成了 calc_reclaim_mappe() 函数. [commit 58ae83db2a40 ("per-zone and reclaim enhancements for memory controller: calculate mapper_ratio per cgroup")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=58ae83db2a40dea15d4277d499a11dadc823c388) 引入了 mem_cgroup_calc_mapped_ratio() 对 MEMCG 中的回收倾向也做了处理. - -shrink_active_list()(前面已经提过了, 就是替代以前的内核版本的 refill_inactive_zone()) 函数中, 会通过 calc_reclaim_mappe() 判断是否需要回收文件页. - -[commit 9439c1c95b5c ("memcg: remove mem_cgroup_cal_reclaim()")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9439c1c95b5c25b8031b2a7eb7e1590eb84be7f5) - -[commit 76a33fc380c9 ("vmscan: prevent get_scan_ratio() rounding errors")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=76a33fc380c9a65e01eb15b3b87c05863a0d51db) - -### 4.2.7.1 inactive_is_low 探测 inactive 和 active 是否不平衡 +### 4.2.7.1 inactive_is_low() && get_scan_count() 共同维持 LRU 的平衡 ------- 早期内核采取的平衡策略相对简单: 仅仅控制 active list 的长度不要超过 inactive list 的长度一定比例, 参见 [inactive_list_is_low(), v3.14, mm/vmscan.c, line 1799](https://elixir.bootlin.com/linux/v3.14/source/mm/vmscan.c#L1799), 具体的比例[与 inactive_ratio 有关](https://elixir.bootlin.com/linux/v3.14/source/mm/page_alloc.c#L5697). 其中对于文件页更是直接[要求 active list 的长度不要超过 inactive list](https://elixir.bootlin.com/linux/v3.14/source/mm/vmscan.c#L1788). inactive LRU list 应该足够小, 这样 VM 就不需要做太多的工作, 但是应该足够大, 这样每个 inactive 页面在被交换之前都有机会再次被引用. 这本身是一个很矛盾的问题. 因此 v2.6.28-rc1 [VM pageout scalability improvements (V12)](https://lore.kernel.org/lkml/20080611184214.605110868@redhat.com) 系列中在将 LRU 拆成匿名页和文件页的时候, 这就又引入了另外一个问题. 以前我们不区分页面类型, 只需要进行 inactive 和 active 的比例均衡就可以了, 但是现在又引入了匿名页和文件页不同类型的 LRU, 他们之间的列表的长度应该保持怎样的均衡. +1. 首先是 active 和 inactive 的平衡(active/inactive balancing), 使用 inactive_is_low() 探测 active/inactive balancing. -1. 首先是匿名页和文件页的平衡(anon/file reclaim balancing): [commit 4f98a2fee8ac ("vmscan: split LRU lists into anon & file sets")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4f98a2fee8acdb4ac84545df98cccecfd130f8db) 对 LRU 中的页面进一步针对是匿名页和文件页进行了区分, 引入了 get_scan_ratio() 替代 calc_reclaim_mappe() 来确定应以多大力度扫描 anon 和 file LRU 列表. 每一组 LRU 列表的相对值是通过查看扫描的页面的比例来确定的, 我们将其旋转回活动列表, 而不是逐出. 计算的结果放到 percent[2] 的数组中, 其中 percent[0] 指定对匿名页 LRU 施加多大压力, 而 percent[1] 则制定对文件 LRU 施加的压力. +[vmscan: second chance replacement for anonymous pages](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=556adecba110bf5f1db6c6b56416cfab5bcab698). 引入 zone->inactive_ratio 控制 active 和 inactive 的比例. 然后 balance_pgdat() 和 shrink_zone() 以及 shrink_list() 中都通过 [inactive_anon_is_low()](https://elixir.bootlin.com/linux/v2.6.28/source/include/linux/mm_inline.h#L88) 检查当前 zone 上的 inactive LRU 的匿名页是否小于 active LRU 一定比例, 如果是, 则说明 inactive LRU list 过短, 需要将一部分 active 的页面 deactivated 到 inactive LRU. 这个比率 zone->inactive_ratio 在初始化阶段通过 setup_per_zone_inactive_ratio() 完成初始化. 与内存的大小有直接关系, 默认被设置为 sqrt(memory in GB * 10), 加入 zone->inactive_ratio 为 3 则意味着 active 和 inactive 的比例为: 3:1, 即 25% 的匿名页面应该保留在 inactive list 中. 注意这个阶段的 zone->inactive_ratio 只控制匿名页的占比. -2. 其次是 active 和 inactive 的平衡(active/inactive balancing): [vmscan: second chance replacement for anonymous pages](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=556adecba110bf5f1db6c6b56416cfab5bcab698). 引入 zone->inactive_ratio 控制 active 和 inactive 的比例. 然后 balance_pgdat() 和 shrink_zone() 以及 shrink_list() 中都通过 [inactive_anon_is_low()](https://elixir.bootlin.com/linux/v2.6.28/source/include/linux/mm_inline.h#L88) 检查当前 zone 上的 inactive LRU 的匿名页是否小于 active LRU 一定比例, 如果是, 则说明 inactive LRU list 过短, 需要将一部分 active 的页面 deactivated 到 inactive LRU. 这个比率 zone->inactive_ratio 在初始化阶段通过 setup_per_zone_inactive_ratio() 完成初始化. 与内存的大小有直接关系, 默认被设置为 sqrt(memory in GB * 10), 加入 zone->inactive_ratio 为 3 则意味着 active 和 inactive 的比例为: 3:1, 即 25% 的匿名页面应该保留在 inactive list 中. 注意这个阶段的 zone->inactive_ratio 只控制匿名页的占比. +2. 其次是匿名页和文件页的平衡(anon/file reclaim balancing), 使用 get_scan_count() 处理 anon/file reclaim balancing. -至此, shrink_list() 中同时处理了 anon/file reclaim balancing 和 active/inactive balancing. +[commit 4f98a2fee8ac ("vmscan: split LRU lists into anon & file sets")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4f98a2fee8acdb4ac84545df98cccecfd130f8db) 对 LRU 中的页面进一步针对是匿名页和文件页进行了区分, 引入了 get_scan_ratio() 替代 calc_reclaim_mappe() 来确定应以多大力度扫描 anon 和 file LRU 列表. 每一组 LRU 列表的相对值是通过查看扫描的页面的比例来确定的, 我们将其旋转回活动列表, 而不是逐出. 计算的结果放到 percent[2] 的数组中, 其中 percent[0] 指定对匿名页 LRU 施加多大压力, 而 percent[1] 则制定对文件 LRU 施加的压力. +至此, shrink_list() 中同时处理了 anon/file reclaim balancing 和 active/inactive balancing. 这套机制维持了很久. + + +* inactive_is_low() 探测 inactive 和 active 的平衡性(Reclaim pressure balance between active and inactive list) | 时间 | 作者 | 特性 | 描述 | 是否合入主线 | 链接 | |:----:|:----:|:---:|:----:|:---------:|:----:| | 2008/06/11 | Rik van Riel | [vmscan: second chance replacement for anonymous pages](https://lore.kernel.org/patchwork/cover/118976) | [VM pageout scalability improvements (V12)](https://lore.kernel.org/lkml/20080611184214.605110868@redhat.com) 系列中的一个补丁.
在大多数情况下, 我们避免了驱逐和扫描匿名页面, 但在某些工作负载下, 我们可能最终会让大部分内存充满匿名页面. 这时, 我们突然需要清除所有内存上的引用位, 这可能会耗时很长. 当取消激活匿名页面时, 我们可以通过不考虑引用状态来减少需要扫描的页面的最大数量. 毕竟, 每个匿名页面一开始都是被引用的.如果匿名页面在到达非活动列表的末尾之前再次被引用, 我们将其移回活动列表.
为了使所需的最大工作量保持合理, 这个补丁
1. 引入了 inactive_ratio 根据内存大小伸缩活动与非活动的比率, 使用公式 inactive_ratio = active/inactive = sqrt(memory in GB * 10). 注意当前只支持匿名页面的转换比率.
2. 引入了 inactive_anon_is_low() 检查当前 zone 上的 active LRU 的匿名页是否过多需要被 deactivated. | v12 ☑ [2.6.28-rc1](https://kernelnewbies.org/Linux_2_6_28#Various_core) | [PatchWork v2](https://lore.kernel.org/patchwork/cover/118976), [关键 commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=556adecba110bf5f1db6c6b56416cfab5bcab698) | | 2008/11/18 | Rik van Riel | [vmscan: evict streaming IO first](https://lore.kernel.org/patchwork/patch/135271) | 在用于驱动换页扫描代码的统计中计算新页面的插入. 这将帮助内核快速地退出流文件IO.
我们依赖于这样一个事实:新文件页面在非活动文件LRU上开始, 而新的匿名页面在活动的匿名列表上开始. 这意味着流式文件IO将增加最近扫描的文件统计, 而不影响最近旋转的文件统计, 驱动分页扫描到文件LRUs. Pageout活动执行它自己的列表操作. | v1 ☑ 2.6.16-rc1 | [PatchWork v2](https://lore.kernel.org/patchwork/cover/135271), [commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9ff473b9a72942c5ac0ad35607cae28d8d59ed7a) | -| 2008/12/01 | KOSAKI Motohiro | [memcg: split-lru feature for memcg take2](https://lkml.org/lkml/2008/12/1/99) | NA | v2 ☑ [2.6.29-rc1](https://kernelnewbies.org/Linux_2_6_29#Memory_controller_swap_management_and_other_improvements) | [LKML](https://lkml.org/lkml/2008/12/1/99), [PatchWork](https://lore.kernel.org/patchwork/cover/136809) | -| 2009/04/29 | Rik van Riel | [vmscan: evict use-once pages first (v3)](https://lore.kernel.org/patchwork/patch/153980) | 优先驱逐那些仅使用了一次的页面.
当文件 LRU 列表由流 IO 页面主导时, 在考虑驱逐其他页面之前, 先驱逐这些页面(因为这些页面往往只使用了一次, 而且后面不会再使用).
该补丁引入了 inactive_file_is_low(), 当发现 inactive_file_is_low() 的时候, 尝试驱逐 LRU_ACTIVE_FILE 上的页面. | v3 ☑ 2.6.16-rc1 | [Patchwork v1](https://lore.kernel.org/patchwork/patch/153805)
*-*-*-*-*-*-*-*
[PatchWork v2](https://lore.kernel.org/patchwork/cover/153955)
*-*-*-*-*-*-*-*
[PatchWork v3](https://lore.kernel.org/patchwork/cover/153980)
*-*-*-*-*-*-*-*
[commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=56e49d218890f49b0057710a4b6fef31f5ffbfec) | +| 2008/12/01 | KOSAKI Motohiro | [memcg: split-lru feature for memcg](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=7f016ee8b6a9a43f768e6252021f169abec4fa1f) | 为 MEMCG 实现了 mem_cgroup_inactive_anon_is_low(), 与全局的 inactive_anon_is_low_global() 共同服务于 inactive_anon_is_low_global(). 移除了 mem_cgroup_cal_reclaim(), 使用 get_scan_count() 统一处理全局和 MEMCG 的 anon/file reclaim balancing. 这样一个统一的 LRU 驱逐和回收流程就接管了全系统不管是全局还是 MEMCG 的 shrink 操作. | v1 ☑✓ 2.6.29-rc1 | [2008/11/30 LORE v1,0/9](https://lore.kernel.org/all/20081130193502.8145.KOSAKI.MOTOHIRO@jp.fujitsu.com)
*-*-*-*-*-*-*-*
[2008/12/01 LORE v2,00/11](https://lore.kernel.org/lkml/20081201205810.1CCA.KOSAKI.MOTOHIRO@jp.fujitsu.com) | +| 2009/04/29 | Rik van Riel | [vmscan: evict use-once pages first (v3)](https://lore.kernel.org/patchwork/patch/153980) | 优先驱逐那些仅使用了一次的页面.
当文件 LRU 列表由流 IO 页面主导时, 在考虑驱逐其他页面之前, 先驱逐这些页面(因为这些页面往往只使用了一次, 而且后面不会再使用).
该补丁引入了 inactive_file_is_low(), 当发现 inactive_file_is_low() 的时候, 尝试驱逐 LRU_ACTIVE_FILE 上的页面. | v3 ☑ 2.6.31-rc1 | [Patchwork v1](https://lore.kernel.org/patchwork/patch/153805)
*-*-*-*-*-*-*-*
[PatchWork v2](https://lore.kernel.org/patchwork/cover/153955)
*-*-*-*-*-*-*-*
[PatchWork v3](https://lore.kernel.org/patchwork/cover/153980)
*-*-*-*-*-*-*-*
[commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=56e49d218890f49b0057710a4b6fef31f5ffbfec) | | 2009/07/16 | Konstantin Khlebnikov | [throttle direct reclaim when too many pages are isolated already (v3)](https://lore.kernel.org/patchwork/patch/164156) | 当隔离的页面过多时限制直接回收的进行.
当太多进程进入直接回收时, 所有页面都有可能从 LRU 上取下. 这样做的一个结果是, 页面回收代码中的下一个进程认为已经没有可回收的页面了, 并触发内存不足终止.
这个问题的一个解决方案是永远不要让太多进程进入页面回收路径, 从而导致整个 LRU 被清空. 将系统限制为仅隔离一半的非活动列表进行回收应该是安全的.
这个补丁引入了 too_many_isolated() 函数. | v3 ☑ 2.6.32-rc1 | [PatchWork v3](https://lore.kernel.org/patchwork/cover/164156), [commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=35cd78156c499ef83f60605e4643d5a98fef14fd) | | 2009/11/25 | Rik van Riel | [vmscan: do not evict inactive pages when skipping an active list scan](https://lore.kernel.org/patchwork/cover/179466) | AIM7运行时, 发现最近的内核提前触发了匿名页的换出(swapping out). 这是由于 shrink_list() 中[在 !inactive_anon_is_low() 的时候依旧执行了 shrink_inactive_list()](https://elixir.bootlin.com/linux/v2.6.32/source/mm/vmscan.c), 而我们真正想做的是让一些匿名页面提前老化, 以便它们在非活动列表上有额外的时间被引用. 显而易见的解决办法是, 当我们调用 shrink_list() 来扫描一个活动列表时, 确保回收时不会落入扫描/回收非活动页面的陷阱. 因此如果 shrink 的是 active LRUs, 则不要再回收 inactive LRUs. 这个更改是安全的, 因为 [shrink_zone()](https://elixir.bootlin.com/linux/v2.6.32/source/mm/vmscan.c#L1630) 中的循环确保在应该回收时仍然会收缩 anon 和文件非活动列表.
这个补丁引入了 [inactive_list_is_low()](https://elixir.bootlin.com/linux/v2.6.33/source/mm/vmscan.c#L1464). | v1 ☑ 2.6.33-rc1 | [PatchWork](https://lore.kernel.org/patchwork/cover/179466) | | 2014/03/14 | Johannes Weiner | [mm: vmscan: do not swap anon pages just because free+file is low](https://lore.kernel.org/patchwork/cover/449613) | 当文件缓存下降到一个区域的高水位以下时, 页面回收强制扫描/交换匿名页面, 以防止残留的少量缓存发生抖动.
然而, 在较大的机器上, 高水印值可能相当大, 当工作负载由静态匿名/shmem集控制时, 文件集可能只是一个使用过一次的缓存的小窗口. 在这种情况下, 当本应该回收不再使用的缓存时, 虚拟机却开始大量交换.
要解决这个问题, 不要在文件页面较低时强制立即扫描, 而是依赖扫描/旋转比率来做出正确的预测. | v1 ☑ 2.6.33-rc1 | [PatchWork](https://lore.kernel.org/patchwork/cover/179466) | | 2016/07/21 | Mel Gorman
Minchan Kim | [mm: consider per-zone inactive ratio to deactivate](https://lore.kernel.org/patchwork/patch/699874) | Joonsoo Kim 和 Minchan Kim 都报告了在 32 位平台上过早地触发了 OOM. 常见的问题是区域约束的高阶分配失败. 两个问题的原因都是因为: pgdat 被过早地认为是不可回收, 并且活动/非活动列表的轮换不足.
这组补丁做了如下部分:
1. 不考虑扫描时跳过的页面. 这避免了pgdat被过早地标记为不可回收
2. 补丁 2-4 增加了每个区域的统计数据. 如果每个区域的LRU计数是可用的, 那么就没有必要估计是否应该根据pgdat统计信息重试回收和压缩, 因此重新实现了近似的 pgdat 统计数据.
3. inactive_list_is_low() 中的主动去激活的逻辑存在问题, 导致 Minchan Kim 报告的问题, 有可能当前 zone 上明明有 8M 的匿名页面, 但是通过非原子的 order-0 分配却触发了 OOM, 因为该区域中的所有页面都在活动列表中. 在补丁 5 中, 如果所有符合条件的区域的非活动/活动比例需要更正, 那么一个区域受限的全局回收将会旋转列表. 较高的区域页面在开始时可能会被提前旋转, 但这是维护总体LRU年龄的更安全的选择. | v5 ☑ [4.8-rc1](https://kernelnewbies.org/Linux_4.8#Memory_management) | [Patchwork RFC](https://lore.kernel.org/patchwork/patch/699587)
*-*-*-*-*-*-*-*
[Patchwork v1](https://lore.kernel.org/patchwork/patch/699874)
*-*-*-*-*-*-*-*
[Patchwork v2](https://lore.kernel.org/patchwork/patch/700111)
*-*-*-*-*-*-*-*
[关注 commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f8d1a31163fcb3bf93f6c78770c3c1915f065bf9) | | 2017/01/16 | Michal Hocko | [mm, vmscan: cleanup lru size claculations](https://lore.kernel.org/patchwork/cover/751448) | 使用 lruvec_lru_size() 精简了 inactive_list_is_low() 的流程. | v1 ☑ 4.11-rc1 | [PatchWork v1,1/3](https://lore.kernel.org/patchwork/cover/751448) | +2. 用 get_scan_count() 处理 anon/file reclaim balancing(Reclaim pressure balance between anon and file pages). +早在 v2.5.43 [commit 8f7a14042e1f ("reduced and tunable swappiness")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8f7a14042e1f5fc814fa60956e3a2dcf5744a0ed) 就对此进行了探索, 引入 `/proc/sys/vm/swappiness` 控制 VM 取消页面映射(回收文件页)和交换内容(回收匿名页)的倾向. + +随后 v2.6.25-rc1 引入 memcg LRU 的过程中, [commit ("per-zone and reclaim enhancements for memory controller: modifies vmscan.c for isolate globa/cgroup lru activity")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1cfb419b394ba82745c54ff05436d598ecc2dbd5) 将这个流程封装成了 calc_reclaim_mappe() 函数. [commit 58ae83db2a40 ("per-zone and reclaim enhancements for memory controller: calculate mapper_ratio per cgroup")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=58ae83db2a40dea15d4277d499a11dadc823c388) 引入了 mem_cgroup_calc_mapped_ratio() 对 MEMCG 中的回收倾向也做了处理. shrink_active_list()(前面已经提过了, 就是替代以前的内核版本的 refill_inactive_zone()) 函数中, 会通过 calc_reclaim_mappe() 判断是否需要回收文件页. + +后来在 v2.6.28 [commit 4f98a2fee8ac ("vmscan: split LRU lists into anon & file sets")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4f98a2fee8acdb4ac84545df98cccecfd130f8db) 对 LRU 中的页面进一步针对是匿名页和文件页进行了区分, 引入了 get_scan_ratio() 替代 calc_reclaim_mappe() 来确定应以多大力度扫描 anon 和 file LRU 列表. 每一组 LRU 列表的相对值是通过查看扫描的页面的比例来确定的, 我们将其旋转回活动列表, 而不是逐出. 计算的结果放到 percent[2] 的数组中, 其中 percent[0] 指定对匿名页 LRU 施加多大压力, 而 percent[1] 则制定对文件 LRU 施加的压力. + +随后在 v2.6.35-rc1 [commit 76a33fc380c9 ("vmscan: prevent get_scan_ratio() rounding errors")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=76a33fc380c9a65e01eb15b3b87c05863a0d51db) 这个补丁发现 get_scan_ratio() 计算百分比, 如果百分比 < 1%, 它将舍入到 0%, 这将导致我们完全忽略扫描匿名/文件页面来回收内存, 即使总匿名/文件页面非常大. 为了避免下溢, 我们不使用百分比, 而是直接计算应该扫描多少页. 通过这种方式, 我们应该获得几个 < 1% 的扫描页面. 这有一方面提高计算精度, 同时使扫描更流畅. 否则, 如果 percent[x] 为下流, 那么 shrink_zone() 不会扫描任何页面, 当优先级为 0 时, 它会突然扫描所有页面. 这样修改后, 即使优先级不为零, shrink_zone() 也有机会扫描一些页面. 因此将 get_scan_ratio() 修改为 get_scan_count(), 它不再返回匿名页和文件页的扫描比率 percent[2], 而是通过 nr_scan_try_batch() 返回各个 enum lru_list 上应该扫描的页面个数 nr[NR_LRU_LISTS]. 这个补丁并没有修改其他算法逻辑, 在提高精度的同时, 只是将 shrink_zone() 中把 percent[2] 转换为 nr[NR_LRU_LISTS] 的逻辑合并到了 get_scan_count() 中. 这里关于函数的返回值注释是错误的, 直到 v3.6-rc1 [commit be7bd59db71d ("mm: fix page reclaim comment error")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=be7bd59db71dfb6dc011c9880fec5a659430003a) 才修正了这个补丁引入的注释错误. 这个过程 get_scan_count() 中匿名页和文件页之间的回收压力平衡是通过分子和分母的元组 fraction[2] 和 denominator 标记来计算的, v3.9-rc1 [commit 9a2651140ef7 ("mm: vmscan: clean up get_scan_count()")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9a2651140ef740b3b67ad47ea3d0af75581aacc6) 引入了 scan_balance 来精简了标记, 使代码更容易阅读. + ### 4.2.7.2 Refault Distance 算法 ------- @@ -2337,7 +2342,7 @@ Refault Distance 算法是为了解决前者, 在第二次读时, 人为地把 p | 2019/10/22 | Johannes Weiner | [mm/vmscan: cgroup-related cleanups](https://lore.kernel.org/patchwork/cover/1142997) | 这里的 8 个补丁, 清理回收代码与cgroups的交互. 它们不应该改变任何行为, 只是让实现更容易理解和使用. | v1 ☑ 5.5-rc1 | [PatchWork 0/8](https://lore.kernel.org/patchwork/cover/1142997) | | 2019/11/07 | Johannes Weiner | [mm: fix page aging across multiple cgroups](https://lore.kernel.org/patchwork/cover/1150211) | 我们希望VM回收系统中最冷的页面. 但是现在VM可以在一个cgroup中回收热页, 而在其他cgroup中明明有更适合回收的冷缓存. 这是因为回收算法的一部分(非活动/活动列表的平衡, 这是用来保护热缓存数据不受一次性流IO影响的部分.)并不是真正意识到 cgroup 层次结构的.
递归 cgroup 回收方案将以相同的速率以循环方式扫描和旋转每个符合条件的 cgroup 的物理 LRU 列表, 从而在所有这些cgroup的页面之间建立一个相对顺序. 然而, 非活动/活动的平衡决策是在每个cgroup内部本地做出的, 所以当一个cgroup冷页面运行不足时, 它的热页面将被回收——即使在相同的回收运行中, 但是同级的 cgroup 中却可能有足够的冷缓存.
这组补丁通过将非活动/活动平衡决策提升到回收运行的顶层来修复这个问题. 这要么是一个cgroup达到了它的极限, 要么是直接的全局回收, 如果有物理内存压力. 从那里, 它采用了一个cgroup子树的递归视图来决定是否有必要取消页面. | v1 ☑ [5.5-rc1](https://kernelnewbies.org/Linux_5.5#Memory_management) | [PatchWork](https://lore.kernel.org/patchwork/cover/1150211) | | 2020/04/03 | Joonsoo Kim | [workingset protection/detection on the anonymous LRU list](https://lwn.net/Articles/815342) | 实现对匿名 LRU 页面列表的工作集保护和检测. 在之前的实现中, 新创建的或交换中的匿名页, 都是默认加入到 active LRU list, 然后逐渐降级到 inactive LRU list. 这造成在某种场景下新申请的内存(即使被使用一次cold page)也会把在a ctive list 的 hot page 挤到 inactive list. 为了解决这个的问题, 这组补丁, 将新创建或交换的匿名页面放到 inactive LRU list 中, 只有当它们被足够引用时才会被提升到活动列表. 另外, 因为这些更改可能导致新创建的匿名页面或交换中的匿名页面交换不活动列表中的现有页面, 所以工作集检测被扩展到处理匿名LRU列表. 以做出更优的决策. | v5 ☑ [5.9-rc1](https://kernelnewbies.org/Linux_5.9#Memory_management) | [PatchWork v5](https://lore.kernel.org/patchwork/cover/1219942), [Patchwork v7](https://lore.kernel.org/patchwork/patch/1278082), [ZhiHu](https://zhuanlan.zhihu.com/p/113220105) | -| 2020/05/20 | Johannes Weiner | [mm: balance LRU lists based on relative thrashing v2](https://lore.kernel.org/patchwork/cover/1245255) | 基于相对抖动平衡 LRU 列表(重新实现了页面缓存和匿名页面之间的 LRU 平衡, 以便更好地与快速随机 IO 交换设备一起工作). : 在交换和缓存回收之间平衡的回收代码试图仅基于内存引用模式预测可能的重用. 随着时间的推移, 平衡代码已经被调优到一个点, 即它主要用于页面缓存, 并推迟交换, 直到 VM 处于显著的内存压力之下. 因为 commit a528910e12ec Linux 有精确的故障 IO 跟踪-回收错误页面的最终代价. 这允许我们使用基于 IO 成本的平衡模型, 当缓存发生抖动时, 这种模型更积极地扫描匿名内存, 同时能够避免不必要的交换风暴. | v1 ☑ [5.8-rc1](https://kernelnewbies.org/Linux_5.8#Memory_management) | [PatchWork v1](https://lore.kernel.org/patchwork/cover/685701)
*-*-*-*-*-*-*-*
[PatchWork v2](https://lore.kernel.org/patchwork/cover/1245255) | +| 2020/05/20 | Johannes Weiner | [mm: balance LRU lists based on relative thrashing v2](https://lore.kernel.org/patchwork/cover/1245255) | 基于相对抖动平衡 LRU 列表(重新实现了页面缓存和匿名页面之间的 LRU 平衡, 以便更好地与快速随机 IO 交换设备一起工作). : 在交换和缓存回收之间平衡的回收代码试图仅基于内存引用模式预测可能的重用. 随着时间的推移, 平衡代码已经被调优到一个点, 即它主要用于页面缓存, 并推迟交换, 直到 VM 处于显著的内存压力之下. 因为 commit a528910e12ec Linux 有精确的故障 IO 跟踪-回收错误页面的最终代价. 这允许我们使用基于 IO 成本的平衡模型, 当缓存发生抖动时, 这种模型更积极地扫描匿名内存, 同时能够避免不必要的交换风暴. | v1 ☑ [5.8-rc1](https://kernelnewbies.org/Linux_5.8#Memory_management) | [PatchWork v1](https://lore.kernel.org/patchwork/cover/685701)
*-*-*-*-*-*-*-*
[PatchWork v2](https://lore.kernel.org/lkml/20200520232525.798933-1-hannes@cmpxchg.org) | ### 4.2.8 LRU 中的内存水线 @@ -3925,19 +3930,28 @@ RMAP 反向映射是一种物理地址反向映射虚拟地址的方法. ## 9.2 Cgroup-Aware OOM killer ------- -https://lwn.net/Articles/317814 -https://lwn.net/Articles/761118 +[Taming the OOM killer](https://lwn.net/Articles/317814) + +[Teaching the OOM killer about control groups](https://lwn.net/Articles/761118) + + +* MEMCG Priority + +| 时间 | 作者 | 特性 | 描述 | 是否合入主线 | 链接 | +|:----:|:----:|:---:|:----:|:---------:|:----:| +| 2017/03/17 | Roman Gushchin | [add support for reclaiming priorities per mem cgroup](https://lore.kernel.org/all/20170317231636.142311-1-timmurray@google.com) | 设置 CGROUP 的内存优先级, Kswapd 等优先扫描低优先级 CGROUP 来回收内存. 用于优化 Android 上的内存回收和 OOM 等机制 | v13 ☐ | [PatchWork RFC v1](https://lore.kernel.org/all/20170317231636.142311-1-timmurray@google.com) | +| 2021/04/14 | Yulei Zhang | [introduce new attribute "priority" to control group](https://lore.kernel.org/patchwork/cover/828043) | Cgroup 引入优先级. | v13 ☐ | [PatchWork v1](https://lwn.net/Articles/851649)
*-*-*-*-*-*-*-*
[LWN](https://lwn.net/Articles/852378) | + +* OOM Killer | 时间 | 作者 | 特性 | 描述 | 是否合入主线 | 链接 | |:----:|:----:|:---:|:----:|:---------:|:----:| -| 2017/03/17 | Roman Gushchin | [add support for reclaiming priorities per mem cgroup](https://lore.kernel.org/patchwork/cover/771278) | 设置 CGROUP 的内存优先级, Kswapd 等优先扫描低优先级 CGROUP 来回收内存. 用于优化 Android 上的内存回收和 OOM 等机制 | v13 ☐ | [PatchWork v7](https://lore.kernel.org/patchwork/cover/771278), [LKML](https://lkml.org/lkml/2017/3/17/658) | | 2017/09/04 | Tim Murray | [cgroup-aware OOM killer](https://lore.kernel.org/patchwork/cover/828043) | Cgroup 感知的 OOM, 通过优先级限定 OOM 时杀进程的次序 | v13 ☐ | [PatchWork v7](https://lore.kernel.org/patchwork/cover/828043) 带 oom_priority
*-*-*-*-*-*-*-*
[PatchWork v13](https://lore.kernel.org/patchwork/cover/828043) | | 2018/03/16 | David Rientjes | [rewrite cgroup aware oom killer for general use](https://lore.kernel.org/patchwork/cover/828043) | Cgroup 感知的 OOM, 通过优先级限定 OOM 时杀进程的次序 | v13 ☐ | [PatchWork v1](https://lore.kernel.org/patchwork/cover/934536) | -| 2021/04/14 | Yulei Zhang | [introduce new attribute "priority" to control group](https://lore.kernel.org/patchwork/cover/828043) | Cgroup 感知的 OOM, 通过优先级限定 OOM 时杀进程的次序 | v13 ☐ | [PatchWork v1](https://lwn.net/Articles/851649)
*-*-*-*-*-*-*-*
[LWN](https://lwn.net/Articles/852378) | | 2021/03/25 | Ybrookxu | [bfq: introduce bfq.ioprio for cgroup](https://lore.kernel.org/patchwork/cover/828043) | Cgroup 感知的 bfq.ioprio | v3 ☐ | [LKML](https://lkml.org/lkml/2021/3/25/93) | -* killed threads +* Killed Threads | 时间 | 作者 | 特性 | 描述 | 是否合入主线 | 链接 | |:----:|:----:|:---:|:----:|:---------:|:----:| @@ -4069,7 +4083,7 @@ v2.6.29-rc1 [memcg: synchronized LRU](https://git.kernel.org/pub/scm/linux/kerne | 时间 | 作者 | 特性 | 描述 | 是否合入主线 | 链接 | |:----:|:----:|:---:|:----:|:---------:|:----:| -| 2011/12/08 | Johannes Weiner | [memcg naturalization -rc5](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=6b208e3f6e35aa76d254c395bdcd984b17c6b626) | 参见 [LWN: Integrating memory control groups](https://lwn.net/Articles/443241). 引入 per-memcg lru, 消除重复的 LRU 列表, [全局 LRU 不再存在, page 只存在于 per-memcg LRU list 中](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=925b7673cce39116ce61e7a06683a4a0dad1e72a).
它使传统的页面回收能够从每个memcg LRU列表中查找页面, 从而消除了双LRU模式(除了每个memcg区域外, 每个全局区域)和系统中每个页面所需的额外列表头.
该补丁引入了 lruvec 结构. | v5 ☑ [3.3-rv1](https://kernelnewbies.org/Linux_3.3#Memory_management) | [LORE v5,00/10](https://lore.kernel.org/lkml/1320787408-22866-1-git-send-email-jweiner@redhat.com) | +| 2011/12/08 | Johannes Weiner | [memcg naturalization -rc5](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=6b208e3f6e35aa76d254c395bdcd984b17c6b626) | 参见 [LWN: Integrating memory control groups](https://lwn.net/Articles/443241). 引入 per-memcg lru, 消除重复的 LRU 列表, [全局 LRU 不再存在, page 只存在于 per-memcg LRU list 中](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=925b7673cce39116ce61e7a06683a4a0dad1e72a).
它使传统的页面回收能够从每个memcg LRU列表中查找页面, 从而消除了双LRU模式(除了每个memcg区域外, 每个全局区域)和系统中每个页面所需的额外列表头.
该补丁引入了 lruvec 结构. | v5 ☑ [3.3-rc1](https://kernelnewbies.org/Linux_3.3#Memory_management) | [LORE v5,00/10](https://lore.kernel.org/lkml/1320787408-22866-1-git-send-email-jweiner@redhat.com) | ### 9.4.4 per-memcg LRU lock 的血泪史 @@ -4089,7 +4103,7 @@ zone->lru_锁是一个竞争激烈的锁, 因此 2012 年左右 Konstantin Khleb | 时间 | 作者 | 特性 | 描述 | 是否合入主线 | 链接 | |:----:|:----:|:---:|:----:|:---------:|:----:| -| 2011/05/26 | KAMEZAWA Hiroyuki | [memcg async reclaim](https://lore.kernel.org/patchwork/cover/251835) | 实现 MEMCG 的异步回收机制(Asynchronous memory reclaim)
1. 当使用 memc g时, 应用程序可以看到由 memcg 限制引起的内存回收延迟. 一般来说, 这是不可避免的. 有一类应用程序, 它使用许多干净的文件缓存并执行一些交互式工作.
2. 如果内核能够帮助后台回收内存, 那么应用程序的延迟就会在一定程度上被隐藏(这取决于应用程序的睡眠方式). 这组补丁程序添加了控制开关 memory.async_control 启用异步回收. 采用动态计算的方法计算了边缘的大小. 该值被确定为减少应用程序达到限制的机会.
使用了新引入的 WQ_IDLEPRI 类型(使用 SCHED_IDLE 调度策略)的 kworker(memcg_async_shrinker) 来完整回收的操作. 通过使用 SCHED_IDLE, 系统繁忙的时候异步内存回收只能消耗 0.3% 的 CPU, 但如果cpu空闲, 可以使用很多cpu. | v3 ☐ | [PatchWork RFC,v3,0/10](https://lore.kernel.org/patchwork/cover/251835) | +| 2011/05/26 | KAMEZAWA Hiroyuki | [memcg async reclaim](https://lore.kernel.org/patchwork/cover/251835) | 实现 MEMCG 的异步回收机制(Asynchronous memory reclaim)
1. 当使用 memc g时, 应用程序可以看到由 memcg 限制引起的内存回收延迟. 一般来说, 这是不可避免的. 有一类应用程序, 它使用许多干净的文件缓存并执行一些交互式工作.
2. 如果内核能够帮助后台回收内存, 那么应用程序的延迟就会在一定程度上被隐藏(这取决于应用程序的睡眠方式). 这组补丁程序添加了控制开关 memory.async_control 启用异步回收. 采用动态计算的方法计算了边缘的大小. 该值被确定为减少应用程序达到限制的机会.
使用了新引入的 WQ_IDLEPRI 类型(使用 SCHED_IDLE 调度策略)的 kworker(memcg_async_shrinker) 来完整回收的操作. 通过使用 SCHED_IDLE, 系统繁忙的时候异步内存回收只能消耗 0.3% 的 CPU, 但如果cpu空闲, 可以使用很多 CPU. | v3 ☐ | [LORE FRC,0/7](https://lkml.org/lkml/2011/5/10/204)
*-*-*-*-*-*-*-*
[PatchWork RFC,v3,0/10](https://lore.kernel.org/lkml/20110526141047.dc828124.kamezawa.hiroyu@jp.fujitsu.com) | | 2017/05/30 | Johannes Weiner | [mm: per-lruvec slab stats](https://lore.kernel.org/patchwork/cover/793422) | Josef 正在研究一种平衡 slab 缓存和 page cache 的新方法. 为此, 他需要 lruvec 级别的 slab 缓存统计信息. 这些补丁通过添加基础设施来实现这一点, 该基础设施允许每个 lruvec 更新和读取通用 VM 统计项, 然后将一些现有VM记帐站点(包括slab记帐站点)切换到这个新的 cgroup 感知 API. | v1 ☑ 4.13-rc1 | [PatchWork 0/6](https://lore.kernel.org/patchwork/cover/793422) | | 2021/02/17 | Yang Shi | [Make shrinker's nr_deferred memcg aware](https://lore.kernel.org/patchwork/cover/1393744) | 最近, 在一些 vfs 元数据繁重的工作负载上看到了大量的 one-off slab drop, 造成收缩器了大量累积的 nr_deferred 对象.
这个是由多种原因导致的.
这组补丁集使 nr_deferred 变成 per-memcg 来解决问题. | v10 ☑ 5.13-rc1 | [PatchWork v10,00/13](https://patchwork.kernel.org/project/linux-mm/cover/20210217001322.2226796-1-shy828301@gmail.com) | | 2021/10/19 | Huangzhaoyang | [mm: skip current when memcg reclaim](https://patchwork.kernel.org/project/linux-mm/patch/1634628576-27448-1-git-send-email-huangzhaoyang@gmail.com) | NA | v2 ☐ | [PatchWork v1](https://patchwork.kernel.org/project/linux-mm/patch/1634278529-16983-1-git-send-email-huangzhaoyang@gmail.com)
*-*-*-*-*-*-*-*
[PatchWork v2](https://patchwork.kernel.org/project/linux-mm/patch/1634628576-27448-1-git-send-email-huangzhaoyang@gmail.com) |