mirror of
https://github.com/gatieme/LDD-LinuxDeviceDrivers.git
synced 2026-08-17 16:52:08 +08:00
description/memory: PCP high vs batch
This commit is contained in:
@@ -1059,19 +1059,19 @@ Mel Gorman 认为最好基于现有的 Per CPU Allocator/PCP 针对这种情况
|
||||
#### 2.2.5.5 Adjust PCP high and batch
|
||||
-------
|
||||
|
||||
percpu 页分配器(PCP)旨在减少对区域锁的争用, 但是 PCP 中的页面数量也要有限制. 因此 PCP 引入了 high 和 batch 来控制 pcplist 中的页面大小. 当 PCP 中的页面超过了 pcp->high 的时候, 则会释放 batch 的页面回到 BUDDY 中.
|
||||
PCP 页分配器旨在减少对区域锁的争用, 但是 PCP 中的页面数量也要有限制. 因此 PCP 引入了 [low, high 和 batch](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=a206231bbe6ffb988cdf9fcbdfd98e49abaf4819) 来控制 pcp->list 中的页面大小. 当 PCP 中的页面[超过了 pcp->high 的时候](https://elixir.bootlin.com/linux/v2.6.15/source/mm/page_alloc.c#L700), 则会释放 batch 的页面回到 BUDDY 中. 同样当 PCP 中的页面数量[少于 pcp->low 的时候](https://elixir.bootlin.com/linux/v2.6.15/source/mm/page_alloc.c#L744), 则会尝试再从 BUDDY 中申请 batch 张页面到 PCP 中.
|
||||
|
||||
2.6.16 时, 内核通过 [commit Making high and batch sizes of per_cpu_pagelists configurable](https://lore.kernel.org/patchwork/patch/47659) 引入了一个参数 percpu_pagelist_fraction 用来设置 PCP->high 的大小. 而 PCP->batch 的大小将被设置为 min(high / 4, PAGE_SHIFT * 8).
|
||||
v2.6.16-rc1 时, 大家发现 pcp->low 水线貌似并没有什么太大的用处, 直接被干掉. 参见 [commit e7c8d5c9955a ("a206231bbe6f ("mm: remove pcp low")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=2d92c5c9150a2a9ca3dc25da58d5042e17a96b6a).
|
||||
|
||||
时间来到 2021 年, batch 和 high 的大小已经过时了, 既不考虑 zone 大小, 也不考虑一个区域的本地节点上 CPU 的数量. PCP 的空间往往非常有限, 随着更大的 zone 和每个节点更多的 CPU, 将导致争用情况越来越糟, 特别是通过 `release_pages()` 批量释放页面时 zone->lock 争用尤为明显. 虽然可以使用 vm.percpu_pagelist_fraction 来增加 PCP->high 来减少争用, 但是由于 vm.percpu_pagelist_fraction 同时调整 high 和 batch 两个值, 虽然可以一定程度减少 zone->lock 锁的争用, 但也会增加分配延迟.
|
||||
同样还是 v2.6.16 时, 内核通过 [commit 8ad4b1fb8205 ("Making high and batch sizes of per_cpu_pagelists configurable")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=8ad4b1fb8205340dba16b63467bb23efc27264d6) 引入了一个参数 vm.percpu_pagelist_fraction 用来设置 PCP->high 的大小. 而 PCP->batch 的大小将被设置为 min(high / 4, PAGE_SHIFT * 8).
|
||||
|
||||
Mel Gorman 发现了这一问题, 开发了 [Calculate pcp->high based on zone sizes and active CPUs](https://lore.kernel.org/patchwork/patch/1435878) 将 pcp->high 和 pcp->batch 的设置分离, 然后根据 local zone 的大小扩展 pcp->high, 对活动 CPU 的回收和计算影响有限, 但 PCP->batch 保持静态. 它还根据最近的释放模式调整可以在 PCP 列表上的页面数量.
|
||||
时间来到 2021 年, batch 和 high 的大小已经过时了, 既不考虑 zone 大小, 也不考虑一个区域的本地节点上 CPU 的数量. PCP 的空间往往非常有限, 随着更大的 zone 和每个节点更多的 CPU, 将导致争用情况越来越糟, 特别是通过 `release_pages()` 批量释放页面时 zone->lock 争用尤为明显. 虽然可以使用 vm.percpu_pagelist_fraction 来增加 pcp->high 来减少争用, 但是由于 vm.percpu_pagelist_fraction 同时调整 high 和 batch 两个值, 虽然可以一定程度减少 zone->lock 锁的争用, 但也会增加分配延迟. Mel Gorman 发现了这一问题, v5.14 时开发了 [Calculate pcp->high based on zone sizes and active CPUs](https://lore.kernel.org/lkml/20210525080119.5455-1-mgorman@techsingularity.net) 将 pcp->high 和 pcp->batch 的设置分离, 然后根据 local zone 的大小扩展 pcp->high, 对活动 CPU 的回收和计算影响有限, 但 PCP->batch 保持静态. 它还根据最近的释放模式调整可以在 PCP 列表上的页面数量.
|
||||
|
||||
|
||||
| 时间 | 作者 | 特性 | 描述 | 是否合入主线 | 链接 |
|
||||
|:----:|:----:|:---:|:----:|:---------:|:----:|
|
||||
| 2005/12/09 | Rohit Seth <rohit.seth@intel.com> | [Making high and batch sizes of per_cpu_pagelists configurable](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=8ad4b1fb8205340dba16b63467bb23efc27264d6) | 引入了 percpu_pagelist_fraction 来调整各个 zone PCP 的 high, 同时将 batch 值设置为 min(high / 4, PAGE_SHIFT * 8). | v1 ☑ 2.6.16-rc1 | [LORE](https://lore.kernel.org/patchwork/patch/47659), [commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8ad4b1fb8205340dba16b63467bb23efc27264d6) |
|
||||
| 2017/01/25 | Mel Gorman | [Recalculate per-cpu page allocator batch and high limits after deferred meminit](https://lore.kernel.org/patchwork/patch/1141598) | 由于 PCP(Per CPU Page) Allocation 中不正确的高限制导致的高阶区域 zone->lock 的竞争, 在初始化阶段, 但是在初始化结束之前, PCP 分配器会计算分批分配/释放的页面数量, 以及 Per CPU 列表上允许的最大页面数量. 由于 zone->managed_pages 还不是最新的, pcp 初始化计算不适当的低批量和高值. 在某些情况下, 这会严重增加区域锁争用, 严重程度取决于共享一个本地区域的cpu数量和区域的大小. 这个问题导致了构建内核的时间比预期的长得多时, AMD epyc2 机器上的系统 CPU 消耗也过多. 这组补丁修复了这个问题 | v5 ☑ 4.11-rc1 | [PatchWork v5](https://lore.kernel.org/patchwork/patch/1141598) |
|
||||
| 2017/01/25 | Mel Gorman | [Recalculate per-cpu page allocator batch and high limits after deferred meminit](https://lore.kernel.org/patchwork/patch/1141598) | 由于 PCP(Per CPU Page) Allocation 中不正确的高限制导致的高阶区域 zone->lock 的竞争, 在初始化阶段, 但是在初始化结束之前, PCP 分配器会计算分批分配/释放的页面数量, 以及 Per CPU 列表上允许的最大页面数量. 由于 zone->managed_pages 还不是最新的, pcp 初始化计算不适当的低批量和高值. 在某些情况下, 这会严重增加区域锁争用, 严重程度取决于共享一个本地区域的cpu数量和区域的大小. 这个问题导致了构建内核的时间比预期的长得多时, AMD epyc2 机器上的系统 CPU 消耗也过多. 这组补丁修复了这个问题 | v5 ☑ 4.11-rc1 | [LORE 0/3](https://lore.kernel.org/lkml/20191018105606.3249-1-mgorman@techsingularity.net)<br>*-*-*-*-*-*-*-* <br>[LORE v2,0/3](https://lore.kernel.org/all/20191021094808.28824-1-mgorman@techsingularity.net), [commit1](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3e8fc0075e24338b1117cdff6a79477427b8dbed) |
|
||||
| 2021/05/25 | Mel Gorman <mgorman@techsingularity.net> | [Calculate pcp->high based on zone sizes and active CPUs](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=74f44822097c665041010994502b5971d6cd9f04) | pcp->high 和 pcp->batch 根据 zone 内内存的大小进行调整. 移除了不适用的 vm.percpu_pagelist_fraction 参数. | v2 ☑ 5.14-rc1 | [LORE v1,0/6](https://lore.kernel.org/all/20210521102826.28552-1-mgorman@techsingularity.net)<br>*-*-*-*-*-*-*-* <br>[LORE v2,0/6](https://lore.kernel.org/lkml/20210525080119.5455-1-mgorman@techsingularity.net) |
|
||||
|
||||
|
||||
@@ -1101,19 +1101,18 @@ Mel Gorman 发现了这一问题, 开发了 [Calculate pcp->high based on zone s
|
||||
#### 2.2.5.7 PCP 分配器框架变更与优化时间线(总结)
|
||||
-------
|
||||
|
||||
|
||||
[commit e7c8d5c9955a ("a206231bbe6f ("hot-n-cold pages: page allocator core")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=a206231bbe6ffb988cdf9fcbdfd98e49abaf4819) 引入了 PCP 最早的框架, 其中 per_cpu_pages 维护了 PCP 的基础结构, per_cpu_pageset 包含了 per_cpu_pages 的数组 pcp[2](0: hot. 1: cold), 每个 zone 中都包含了 per_cpu_pageset 的数组 pageset[NR_CPUS], 维护了 zone 下所有 CPU 的 PCP 页面.
|
||||
[commit e7c8d5c9955a ("hot-n-cold pages: page allocator core")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=a206231bbe6ffb988cdf9fcbdfd98e49abaf4819) 引入了 PCP 最早的框架, 其中 per_cpu_pages 维护了 PCP 的基础结构, per_cpu_pageset 包含了 per_cpu_pages 的数组 pcp[2](0: hot, 1: cold`), 每个 zone 中都包含了 per_cpu_pageset 的数组 pageset[NR_CPUS], 维护了 zone 下所有 CPU 的 PCP 页面.
|
||||
|
||||
之前的版本每个 zone 都有 PCP 的页面集阵列 struct per_cpu_pageset pageset[NR_CPUS]. 这样 NUMA 系统下, 任何特定的 CPU 在每个 zone 结构中都有一些属于它自己的内存, 即使 CPU 不是该区域的本地 CPU. 因此 v2.6.13-rc1 实现了 NUMA 感知的 PCP, 修改了 NUMA 系统下 struct zone 中 PCP 页面集的管理方式.这个版本, NUMA 系统下, zone->pageset 成为一个数组指针 `*pageset[NR_CPUS]`, 然后使用 process_zones() 通过 kmalloc 来动态分配指定 CPU 的 zone->pageset[cpu] 空间. 其中 boot CPU(CPU 0) 的 PCP pageset 在 start_kernel()-=>setup_per_cpu_pageset() 中完成, 并注册 register_cpu_notifier(&pageset_notifier), 而非 boot CPU 的 PCP pageset, 就通过注册 pageset_notifier 的 CallBack 函数 pageset_cpuup_callback 来完成. 但是这样依旧有一些问题, 在 zone 初始化阶段 free_area_init_core() 中, SLAB 分配器还没有初始化, 因此在这个阶段先使用了静态 boot_pageset(曾经叫 pageset_table) 完成启动阶段的初始化. 这个静态变量被标记为 `__initdata`, 在系统启动后会其空间将被释放掉. 参见 [commit e7c8d5c9955a ("node local per-cpu-pages](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e7c8d5c9955a4d2e88e36b640563f5d6d5aba48a) 和 [Reduce size of huge boot per_cpu_pageset, 0/2](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=2caaad41e4aa8f5dd999695b4ddeaa0e7f3912a4), [commit b7c84c6ada2b ("boot_pageset must not be freed")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=b7c84c6ada2be942eca6722edb2cfaad412cd5de)
|
||||
|
||||
v2.6.16-rc1 时 pcp->low 水线貌似并没有什么太大的用处, 直接被干掉. [commit e7c8d5c9955a ("a206231bbe6f ("mm: remove pcp low")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=2d92c5c9150a2a9ca3dc25da58d5042e17a96b6a).
|
||||
|
||||
v2.6.25-rc1, PCP 不再明确用 2 个数组区分冷热页, 而是使用单一列表管理. 参见 [Page allocator: get rid of the list of cold pages](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3dfa5721f12c3d5a441448086bee156887daa961), 此时 struct per_cpu_pageset 结构中 struct per_cpu_pages pcp[2] 变成 pcp.
|
||||
|
||||
v2.6.32-rc1, [commit 5f8dcc21211a ("page-allocator: split per-cpu list into one-list-per-migrate-type")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=5f8dcc21211a3d4e3a7a5ca366b469fb88117f61), 此时 per_cpu_pages 中维护 PCP 页面的 list 演变为 lists[MIGRATE_PCPTYPES].
|
||||
v2.6.24-rc1, 引入 migratetype 来环节内存碎片化的时候, [commit 535131e6925b ("Choose pages from the per-cpu list based on migration type")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=535131e6925b4a95f321148ad7293f496e0e58d7) 新增了 PCP 对 migratetype 的感知, 不过此时每次需要遍历 pcp->list 查找到对应 migratetype 的页面, 如果 PCP 中没有对应 migratetype 的页面, 则通过 rmqueue_bulk() 从 Buddy 中继续缓存一些出来. 这种遍历查找的方式效率较低. 因此 v2.6.32-rc1 时 [commit 5f8dcc21211a ("page-allocator: split per-cpu list into one-list-per-migrate-type")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?id=5f8dcc21211a3d4e3a7a5ca366b469fb88117f61), 将 PCP 的页面列表也按照 migratetype 分开管理. 不过并不是所有的 migratetype 都会缓存在 PCP 中. 因此使用 MIGRATE_PCPTYPES 管理, 至此 per_cpu_pages 中维护 PCP 页面的 list 演变为 lists[MIGRATE_PCPTYPES].每次要从 PCP 获取某个 migratetype 的时候, 直接从对应 migratetype 的 pcp->list[migratetype] 去获取就行, 不用再费劲地区遍历 pcp->list, 归还的时候也按照 migratetype 归还.
|
||||
|
||||
v2.6.34-rc1, [commit 99dcc3e5a94e ("this_cpu: Page allocator conversion")](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=99dcc3e5a94ed491fbef402831d8c0bbb267f995) 开始使用了 PER CPU 变量替代 NR_CPUS 的数组. 于是 boot_pageset 被定义为 DEFINE_PER_CPU, zone->pageset 变成了一个指针, 而是用 alloc_percpu() 来分配. 然后直接使用 per_cpu_ptr(zone->pageset, cpu) 访问指定的 PCP 页面集. 以及 [commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=43cf38eb5cea91245502df3fcee4dbfc1c74dd1c).
|
||||
|
||||
|
||||
|
||||
| 时间 | 作者 | 特性 | 描述 | 是否合入主线 | 链接 |
|
||||
|:-----:|:----:|:---:|:---:|:----------:|:----:|
|
||||
| 2005/06/21 | Christoph Lameter <christoph@lameter.com> | [node local per-cpu-pages](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e7c8d5c9955a4d2e88e36b640563f5d6d5aba48a) | TODO | v1 ☑✓ v2.6.13-rc1 | [LORE](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e7c8d5c9955a4d2e88e36b640563f5d6d5aba48a) |
|
||||
|
||||
@@ -147,8 +147,7 @@ jeremy很早就写了一个pv ticketlock, 原理大概就是vcpu在拿锁了一
|
||||
| 时间 | 作者 | 特性 | 描述 | 是否合入主线 | 链接 |
|
||||
|:----:|:----:|:---:|:----:|:---------:|:----:|
|
||||
| 2021/10/05 | David Hildenbrand <david@redhat.com> | [proc/vmcore: sanitize access to virtio-mem memory](https://patchwork.kernel.org/project/linux-mm/cover/20211005121430.30136-1-david@redhat.com) | NA | v2 ☑ 4.19-rc1 | [PatchWork v2,0/9](https://patchwork.kernel.org/project/linux-mm/cover/20211005121430.30136-1-david@redhat.com) |
|
||||
| 2021/11/11 | Chao Peng <chao.p.peng@linux.intel.com> | [KVM: mm: fd-based approach for supporting KVM guest private memory](https://patchwork.kernel.org/project/linux-mm/cover/20211111141352.26311-1-chao.p.peng@linux.intel.com) | [Private memory for KVM guests
|
||||
](https://lwn.net/Articles/890224) | RFC ☐ | [LORE RFC,0/6](https://patchwork.kernel.org/project/linux-mm/cover/20211111141352.26311-1-chao.p.peng@linux.intel.com)<br>*-*-*-*-*-*-*-* <br>[LORE v5,00/13](https://lore.kernel.org/lkml/20220310140911.50924-1-chao.p.peng@linux.intel.com) |
|
||||
| 2021/11/11 | Chao Peng <chao.p.peng@linux.intel.com> | [KVM: mm: fd-based approach for supporting KVM guest private memory](https://patchwork.kernel.org/project/linux-mm/cover/20211111141352.26311-1-chao.p.peng@linux.intel.com) | [Private memory for KVM guests](https://lwn.net/Articles/890224) | RFC ☐ | [LORE RFC,0/6](https://patchwork.kernel.org/project/linux-mm/cover/20211111141352.26311-1-chao.p.peng@linux.intel.com)<br>*-*-*-*-*-*-*-* <br>[LORE v5,00/13](https://lore.kernel.org/lkml/20220310140911.50924-1-chao.p.peng@linux.intel.com) |
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user