LWN:拆分struct page的合适时机!

共 3246字,需浏览 7分钟

 ·

2023-08-07 18:38

关注了就能看到更多这么棒的文章哦~

The proper time to split struct page

By Jonathan Corbet
July 14, 2023
ChatGPT assisted translation
https://lwn.net/Articles/937839/

页面结构(page struct)是内核内存管理子系统的核心组成部分;针对已安装上来的内存条中的每个 page 都会有一个这样的结构。然而,这个结构越来越被视为一个问题,因此跟 folio 转换相关的众多附带工作之一就是要移除它。尽管如此,朝着这个目标走出的第一步工作当前却受到了内存管理开发者的一些阻碍,他们认为其中一部分改动进行得太早了。

struct page 的目的是让内核可以跟踪每个 page 的状态,比如它是如何被使用的、它在 LRU 列表中的位置、对它存在多少处引用等等。所需的信息因页面使用情况的不同而差异巨大。例如,用户空间的匿名内存(anonymous memory)的管理与用于内核空间 DMA buffer 的内存的管理方式就不一样。由于必须让 struct page 尽可能小(现代系统中有数百万个这样的结构所以每个字节都非常重要),数据必须被尽可能高效地存放。因此,struct page 的声明就是嵌套的各种 union,跟迷宫差不多了,目的是希望让各种用途下的的数据字段能重叠起来。

这一切就导致出现了一个过于庞大的 struct。系统中有大约有 1.6%的内存被用来在最底层跟踪系统内存。许多用途下不需要 struct page 提供出来的所有空间,但 struct 的 size 不能改动,因此会有额外的内存浪费。同时,struct page 又太小了,需要不断努力才能塞一个新的 bit 进来。即使经过了清理,人类大脑来说这个 struct 也仍然是很难理解的。某个特定内存类型中哪些字段是可供使用的,并不总是很清晰。这个 struct 还暴露了很多内存内部管理细节,这些细节最好隐藏在内存管理子系统中,这一切使得许多改动就变得更加困难。

当前子系统中的众多目标之一,就是能摆脱当前这种方式的 struct page。系统的内存映射目前是一个完全有这种结构组成的数组,后续会被改为一个指向当前页面使用类型的的描述符的指针的数组。这些描述符将会是动态分配的,并根据需要来适当地调整大小,从而包含它们需要包含的信息。

这并不是一个简单的改动;由于这个结构暴露给整个内核使用了,因此到处都有代码直接使用它,其中就包括大量的设备驱动程序。改变所有这些代码不会在一天之内完成。事实上可能需要一年的时间。

因此,在朝着这个目标迈进的过程中需要采取较小的步骤。其中一个步骤是停止让代码直接使用 struct page,并改为使用这个特定用途下的专用结构类型。内核 5.17 引入了 struct slab,描述了由 slab 分配器所管理的内存页面。这个结构经过精心设计,与 struct page 可以完全 overlay 起来,也不会破坏其他用途下的结构字段。这个改动不改变数据仍然存储在跟之前相同的 page struct 中的这个情况,但它使 slab 相关的部分变得更明确了,也确保了 struct page 的其余部分在 slab 分配器中不再使用。

接下来的一步可能应该是 Vishal Moola 提出的 struct ptdesc 提案了。在此内存存放的是一个 page table 的情况下这个 struct 描述了 struct page 应该是什么样子:

struct ptdesc {
unsigned long __page_flags;

union {
struct rcu_head pt_rcu_head;
struct list_head pt_list;
struct {
unsigned long _pt_pad_1;
pgtable_t pmd_huge_pte;
};
};
unsigned long _pt_s390_gaddr;

union {
struct mm_struct *pt_mm;
atomic_t pt_frag_refcount;
};

union {
unsigned long _pt_pad_2;
#if ALLOC_SPLIT_PTLOCKS
spinlock_t *ptl;
#else
spinlock_t ptl;
#endif
};
unsigned int __page_type;
atomic_t _refcount;
#ifdef CONFIG_MEMCG
unsigned long pt_memcg_data;
#endif
};

正如我们所看到的,即使在这个用例已经从 struct page 中分离出来后,仍然有许多 union。其中许多代表了某个体系结构中特有的用法;例如,pt_mm 是给 x86 系统使用的,而 pt_frag_refcount 在 PowerPC 和 s390 上都需要。但这个结构仍然简单得多,使得 page table 特有用法可以表达得更加清晰和明确。

这项工作已经进入第六个修订版,人们提出的大部分担忧似乎都已经得到了解决。然而,Hugh Dickins 抱怨说:“我不明白这个 patch set 的意义:对我来说,它只是把 page table 和 struct page 之间的现有紧密关系给混杂起来。”他接着说,“如果用更正面一些的方式来说的话”,可以说这项工作超前了一些时间。但如果有必要,他愿意接受。David Hildenbrand 补充说,他“不喜欢这些'overlay'”,并补充说,只有在描述符可以动态分配时,它们才有意义。这两位开发者似乎认为这项工作当下没有提供任何现实的好处却又使内存管理代码混乱不堪。

Matthew Wilcox 回答说,现在进行这项工作的一个原因是更好地记录每种用途类型是如何管理 page structure 的:

“通过为 struct page 的每个使用者创建特定的类型,我们可以看到实际发生了什么。在 ptdesc 转换开始之前,我无法告诉您 struct page 中的哪些位由 s390 代码使用。我知道他们在 refcount 上玩一些有趣的游戏(甚至在 s390 代码中有文档记录!),但我不知道他们在使用…更不知道是在使用什么,比如是不是 page->private 指向了 kvm 私有数据?”

他说,已经添加了 assertion 来以确保使用特定用途的结构在每个体系结构上都仍然符合 struct page。可以在 Moola 系列的 patch 末尾看到 TABLE_MATCH()宏形式的代码。

虽然在内存管理开发者中似乎有一致的共识,即要用动态分配的跟特定用途相关的描述符来替换 struct page,但显然还没有关于这些改动应该按照什么顺序来进行的讨论。可能先进行动态分配,但这也会造成大量代码混乱而没有眼下的明显好处。开发者们需要进行两个转换才能达到他们试图实现的目标。这项工作已经开始,首先添加了新的结构类型;很有可能在此期间继续进行(也许 zsmalloc 描述符是下一步)。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~



浏览 457
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报