Linux 内存管理新特性 - Memory folios 解读 | 龙蜥技术

图片

本文内容基于 Linux 5.16,folio 基础部分开始合入。截止到目前 Linux 6.5,folio 已经有很大进展,会在后续文章中介绍。作者:徐宇。

01 folio [ˈfoʊlioʊ] 是什么

引用 LWN: Memory folios :https://lwn.net/Articles/856016/ 和 Merge tag 'folio-5.16':https://github.com/torvalds/linux/commit/49f8275c7d92,重点如下。

1.1 folio 的定义

Add memory folios, a new type to represent either order-0 pages or the head page of a compound page.

folio 可以看成是 page 的一层包装,没有开销的那种。folio 可以是单个页,也可以是复合页。

图片

(图片引用围绕 HugeTLB 的极致优化)

上图是 page 结构体的示意图,64 字节管理 flags, lru, mapping, index, private, {ref_, map_}count, memcg_data 等信息。当 page 是复合页的时候,上述 flags 等信息在 head page 中,tail page 则复用管理 compound_{head, mapcount, order, nr, dtor} 等信息。

struct folio {/* private: don't document the anon union */union {struct {/* public: */unsigned long flags;struct list_head lru;struct address_space *mapping;pgoff_t index;void *private;atomic_t _mapcount;atomic_t _refcount;
#ifdef CONFIG_MEMCGunsigned long memcg_data;
#endif/* private: the union with struct page is transitional */};struct page page;};
};

folio 的结构定义中,flags, lru 等信息和 page 完全一致,因此可以和 page 进行 union。这样可以直接使用 folio->flags  而不用 folio->page->flags。

#define page_folio(p)           (_Generic((p),                          \const struct page *:    (const struct folio *)_compound_head(p), \struct page *:          (struct folio *)_compound_head(p)))#define nth_page(page,n) ((page) + (n))
#define folio_page(folio, n)    nth_page(&(folio)->page, n)

第一眼看 page_folio 可能有点懵,其实等效于:

switch (typeof(p)) {case const struct page *:return (const struct folio *)_compound_head(p);case struct page *:return (struct folio *)_compound_head(p)));
}

就这么简单。

_Generic 是 C11 STANDARD - 6.5.1.1 Generic selection(https://www.open-std.org/JTC1/sc22/wg14/www/docs/n1570.pdf) 特性,语法如下:

Generic selection
Syntaxgeneric-selection:_Generic ( assignment-expression , generic-assoc-list )generic-assoc-list:generic-associationgeneric-assoc-list , generic-associationgeneric-association:type-name : assignment-expressiondefault : assignment-expression

page 和 folio 的相互转换也很直接。不管 head,tail page,转化为 folio 时,意义等同于获取 head page 对应的 folio;folio 转化为 page 时,folio->page 用于获取 head page,folio_page(folio, n) 可以用于获取 tail page。

问题是,本来 page 就能代表 base page,或者 compound page,为什么还需要引入 folio?

1.2 folio 能做什么?

The folio type allows a function to declare that it's expecting only a head page. Almost incidentally, this allows us to remove various calls to VM_BUG_ON(PageTail(page)) and compound_head().

原因是,page 的含义太多了,可以是 base page,可以是 compound head page,还可以是 compound tail page。

如上述所说,page 元信息都存放在 head page(base page 可以看成是 head page)上,例如 page->mapping, page->index 等。但在 mm 路径上,传递进来的 page 参数总是需要判断是 head page 还是 tail page。由于没有上下文缓存,mm 路径上可能会存在太多重复的 compound_head 调用。

图片

这里以 mem_cgroup_move_account 函数调用举例,一次 mem_cgroup_move_account 调用,最多能执行 7 次 compound_head。

static inline struct page *compound_head(struct page *page)
{unsigned long head = READ_ONCE(page->compound_head);if (unlikely(head & 1))return (struct page *) (head - 1);return page;
}

再以 page_mapping(page) 为例具体分析,进入函数内部,首先执行 compound_head(page) 获取 page mapping 等信息。另外还有一个分支 PageSwapCache(page) ,当执行这个分支函数的时候,传递的是 page,函数内部还需执行一次 compound_head(page) 来获取 page flag 信息。

struct address_space *page_mapping(struct page *page)
{struct address_space *mapping;page = compound_head(page);/* This happens if someone calls flush_dcache_page on slab page */if (unlikely(PageSlab(page)))return NULL;if (unlikely(PageSwapCache(page))) {swp_entry_t entry;entry.val = page_private(page);return swap_address_space(entry);}mapping = page->mapping;if ((unsigned long)mapping & PAGE_MAPPING_ANON)return NULL;return (void *)((unsigned long)mapping & ~PAGE_MAPPING_FLAGS);
}
EXPORT_SYMBOL(page_mapping);

当切换到 folio 之后,page_mapping(page) 对应 folio_mapping(folio) ,而 folio 隐含着 folio 本身就是 head page,因此两个 compound_head(page) 的调用就省略了。

mem_cgroup_move_account 仅仅是冰山一角,mm 路径上到处是 compound_head 的调用。积少成多,不仅执行开销减少了,开发者也能得到提示,当前 folio 一定是 head page,减少判断分支。

1.3 folio 的直接价值

1)减少太多冗余 compound_head 的调用。

2)给开发者提示,看到 folio,就能认定这是 head page。

3)修复潜在的 tail page 导致的 bug。

Here's an example where our current confusion between "any page"
and "head page" at least produces confusing behaviour, if not an
outright bug, isolate_migratepages_block():page = pfn_to_page(low_pfn);if (PageCompound(page) && !cc->alloc_contig) {const unsigned int order = compound_order(page);if (likely(order < MAX_ORDER))low_pfn += (1UL << order) - 1;goto isolate_fail;}compound_order() does not expect a tail page; it returns 0 unless it's
a head page.  I think what we actually want to do here is:if (!cc->alloc_contig) {struct page *head = compound_head(page);if (PageHead(head)) {const unsigned int order = compound_order(head);low_pfn |= (1UL << order) - 1;goto isolate_fail;}}Not earth-shattering; not even necessarily a bug.  But it's an example
of the way the code reads is different from how the code is executed,
and that's potentially dangerous.  Having a different type for tail
and not-tail pages prevents the muddy thinking that can lead to
tail pages being passed to compound_order().
1.4 folio-5.16 已经合入

This converts just parts of the core MM and the page cache.

willy/pagecache.git 共有 209 commit。这次 5.16 的合并窗口中,作者 Matthew Wilcox (Oracle) <willy@infradead.org> 先合入 folio 基础部分,即 Merge tag folio-5.16,其中包含 90 commits,74 changed files with 2914 additions and 1703 deletions。除了 folio 定义等基础设施之外,这次改动主要集中在 memcg, filemap, writeback 部分。

folio-5.16 用 folio 逐步取代 page 的过程,似乎值得一提。mm 路径太多了,如果强迫症一次性替换完,就得 top-down 的方式,从 page 分配的地方改成 folio,然后一路改下去。这不现实,几乎要修改整个 mm 文件夹了。

folio-5.16 采用的是 bottom-up 的方式,在 mm 路径的某个函数开始,将 page 替换成 folio,其内部所有实现都用 folio,形成一个“闭包”。然后修改其 caller function,用 folio 作为参数调用该函数。直到所有 caller function 都改完了,那么这个“闭包”又扩展了一层。有些函数的调用者很多,一时改不完,folio-5.16 就提供了一层 wrapper。这里以 page_mapping/folio_mapping 为例。

首先闭包里是 folio_test_slab(folio),folio_test_swapcache(folio) 等基础设施,然后向上扩展到 folio_mapping。page_mapping 的调用者很多,mem_cgroup_move_account 能顺利地调用 folio_mapping,而 page_evictable 却还是保留使用 page_mapping。那么闭包在这里停止扩展。

struct address_space *folio_mapping(struct folio *folio)
{struct address_space *mapping;/* This happens if someone calls flush_dcache_page on slab page */if (unlikely(folio_test_slab(folio)))return NULL;if (unlikely(folio_test_swapcache(folio)))return swap_address_space(folio_swap_entry(folio));mapping = folio->mapping;if ((unsigned long)mapping & PAGE_MAPPING_ANON)return NULL;return (void *)((unsigned long)mapping & ~PAGE_MAPPING_FLAGS);
}
struct address_space *page_mapping(struct page *page)
{return folio_mapping(page_folio(page));
}mem_cgroup_move_account(page, ...) {folio = page_folio(page);mapping = folio_mapping(folio);
}page_evictable(page, ...) {ret = !mapping_unevictable(page_mapping(page)) && !PageMlocked(page);
}

02 folio 就这些吗?

很多小伙伴看到这里是不是和我有一样的感受:就这些吗?仅仅是 compound_head 的问题吗?

我不得不去学习 LWN: A discussion on folios(https://lwn.net/Articles/869942/),LPC 2021 - File Systems MC(https://www.youtube.com/watch?v=U6HYrd85hQ8&t=1475s) 大佬关于 folio 的讨论。然后发现 Matthew Wilcox 的主题不是《The folio》,而是《Efficient buffered I/O》。事情并不简单。

这次 folio-5.16 合入的都是 fs 相关的代码,组里大佬提到 “Linux-mm 社区大佬不同意全部把 page 替换成 folio,对于匿名页和 slab,短期内还是不能替换”。于是我继续翻阅 Linux-mm 邮件列表。

2.1 folio 的社区讨论

2.1.1 命名

首先是 Linus,Linus 表示他不讨厌这组 patch,因为这组 patch 确实解决了 compound_head 的问题;但是他也不喜欢这组 patch,原因是 folio 听起来不直观。

经过若干关于取名的讨论,当然命名最后还是 folio。

2.1.2 FS 开发者的意见

目前 page cache 中都是 4K page,page cache 中的大页也是只读的,例如代码大页(https://openanolis.cn/sig/Cloud-Kernel/doc/475049355931222178)特性。为什么 Transparent huge pages in the page cache 一直没有实现,可以参考这篇 LWN(https://lwn.net/Articles/686690/)。其中一个原因是,要实现 读写 file THP,基于 buffer_head 的 fs 对 page cache 的处理过于复杂。

  • buffer_head
    buffer_head 代表的是物理内存映射的块设备偏移位置,一般一个 buffer_head 也是 4K 大小,这样一个 buffer_head 正好对应一个 page。某些文件系统可能采用更小的block size,例如 1K,或者 512 字节。这样一个 page 最多可以有 4 或者 8 个buffer_head 结构体来描述其内存对应的物理磁盘位置。

    这样,在处理 multi-page 读写的时候,每个 page 都需要通过 get_block 获取 page 和 磁盘偏移的关系,低效且复杂。

  • iomap
    iomap 最初是从 XFS 内部拿出来的,基于 extent,天然支持 multi-page。即在处理 multi-page 读写的时候,仅需一次翻译就能获取所有 page 和 磁盘偏移的关系。

    通过 iomap,文件系统与 page cache 隔离开来了,例如,它们在表示大小的时候都使用字节,而不是有多少 page。因此,Matthew Wilcox 建议任何直接使用 page cache 的文件系统都应该考虑要换到 iomap 或 netfs_lib 了。

    隔离 fs 与 page cache 的方式或许不止 folio,但是例如 scatter gather 是不被接受的,抽象太复杂。

这也是为什么 folio 先在 XFS/AFS 中落地了,因为这两个文件系统就是基于 iomap 的。

这也是为什么 FS 开发者都强烈希望 folio 被合入,他们可以方便地在 page cache 中使用更大的 page,这个做法可以使文件系统的 I/O 更有效率。

buffer_head 有一些功能是当前 iomap 仍然缺乏的。而 folio 的合入,能让 iomap 得到推进,从而使 block-based 文件系统能够改成使用 iomap。

2.1.3 MM 开发者的意见

最大的异议来自 Johannes Weiner,他承认 compound_head 的问题,但觉得修复该问题而引入这么大的改动不值得;同时认为 folio 对 fs 的所做的优化,anonymous page 不需要。

Unlike the filesystem side, this seems like a lot of churn for very little tangible value. And leaves us with an end result that nobody appears to be terribly excited about.

But the folio abstraction is too low-level to use JUST for file cache and NOT for anon. It's too close to the page layer itself and would duplicate too much of it to be maintainable side by side.

最后在  Kirill A. Shutemov、Michal Hocko 等大佬的力挺 folio 态度下,Johannes Weiner 也妥协了。

2.1.4 达成一致

社区讨论到最后,针对 folio 的反对意见在 folio-5.15 的代码中都已经不存在了,但错过了 5.15 的合并窗口,因此这次 folio-5.16 原封不动被合入了。

2.2 folio 的深层价值

I think the problem with folio is that everybody wants to read in her/his hopes and dreams into it and gets disappointed when see their somewhat related problem doesn't get magically fixed with folio.

Folio started as a way to relief pain from dealing with compound pages. It provides an unified view on base pages and compound pages. That's it.

It is required ground work for wider adoption of compound pages in page cache. But it also will be useful for anon THP and hugetlb.

Based on adoption rate and resulting code, the new abstraction has nice downstream effects. It may be suitable for more than it was intended for initially. That's great.

But if it doesn't solve your problem... well, sorry...

The patchset makes a nice step forward and cuts back on mess I created on the way to huge-tmpfs.

I would be glad to see the patchset upstream.

--

Kirill A. Shutemov

大家都知道“struct page 相关的混乱”,但没有人去解决,大家都在默默忍受这长期以来的困扰,在代码中充斥着如下代码。

if (compound_head(page)) // do A;
else                     // do B;

folio 并不完美,或许因为大家期望太高,导致少数人对 folio 的最终实现表示失望。但多数人认为 folio 是在正确方向上的重要一步。毕竟后续还有更多工作要实现。

03 folio 后续工作及其他

3.1 folio 开发计划

For 5.17, we intend to convert various filesystems (XFS and AFS are ready; other filesystems may make it) and also convert more of the MM and page cache to folios. For 5.18, multi-page folios should be ready.

3.2 folio 还能提升性能

The 80% win is real, but appears to be an artificial benchmark (postgres startup, which isn't a serious workload). Real workloads (eg building the kernel, running postgres in a steady state, etc) seem to benefit between 0-10%.

folio-5.16 减少大量 compound_head 调用,在 sys 高的 micro benchmark 中应当有性能提升。未实测。

folio-5.18 multi-page folios 支持之后,理论上 I/O 效率能提升,拭目以待。

3.3 我应该怎么用 folio?

FS 开发者最应该做的就是把那些仍然使用 buffer head 的文件系统转换为使用 iomap 进行 I/O,至少对于那些 block-based 文件系统都应该这么做。

其他开发者欣然接受 folio 即可,基于 5.16+ 开发的新特性能用 folio 就用 folio,熟悉一下 API 即可,内存分配回收等 API 本质没有改变。

—— 完 ——

关于龙蜥

龙蜥社区是立足云计算打造面向国际的 Linux  服务器操作系统开源根社区及创新平台。龙蜥操作系统(Anolis OS)是龙蜥社区推出的 Linux 发行版,拥有三大核心能力:提效降本、更加稳定、更加安全。

目前,Anolis OS 23 已发布,全面支持智能计算,兼容主流 AI 框架,支持一键安装 nvidia GPU 驱动、CUDA 库等,完善适配 Intel、兆芯、鲲鹏、龙芯等芯片,并提供全栈国密支持。

加入我们,一起打造面向云时代的操作系统!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/85019.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于大模型的数据血缘异常归因分析

近日&#xff0c;以“元数据技术及应用创新”为主题&#xff0c;最新一季StartDT Hackathon&#xff08;奇点云黑客马拉松&#xff09;正式收官。 本期黑客松共吸引了近50位选手参赛&#xff0c;有的在实时数仓领域显神通&#xff0c;有的则再次请出了大模型。这些小组都有个共…

利用自动校对软件优化新闻稿件的拼写和语法

利用自动校对软件优化新闻稿件的拼写和语法&#xff0c;您可以按照以下步骤进行&#xff1a; 1.选择适合的校对软件&#xff1a;市场上有多种拼写和语法校对软件可供选择。根据您的需求和预算&#xff0c;选择一个功能强大且适合新闻稿件的软件。 2.导入稿件&#xff1a;将待校…

日常BUG ——乱码

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;日常BUG、BUG、问题分析☀️每日 一言 &#xff1a;存在错误说明你在进步&#xff01; 一、问题描述 A系统使用Feign调用B系统时&#xff0c;传递的String字符串&#xff0c;到了B系统中变为了乱…

Flutter:屏幕适配

flutter_screenutil flutter_screenutil是一个用于在Flutter应用程序中进行屏幕适配的工具包。它旨在帮助开发者在不同屏幕尺寸和密度的设备上创建响应式的UI布局。 flutter_screenutil提供了一些用于处理尺寸和间距的方法&#xff0c;使得开发者可以根据设备的屏幕尺寸和密度…

2023年游戏买量能怎么玩?

疫情过后&#xff0c;一地鸡毛。游戏行业的日子也不好过。来看看移动游戏收入&#xff1a;2022年&#xff0c;移动游戏收入达到920亿美元&#xff0c;同比下降6.4%。这告诉我们&#xff0c;2022年对移动游戏市场来说是一个小挫折。 但不管是下挫还是上升&#xff0c;移动游戏市…

Git 快速入门

Git 快速入门 文章目录 Git 快速入门一、代码托管平台&#xff08;远程仓库&#xff09;二、安装Git三、Git的命令实践Git 的四个区域Git 管理代码的3个场景Git 工作区的理念Git 工作区的生命周期Git 版本回退Git 文件重命名Git查看版本提交日志Git StashGit分支Git标签 四、创…

新利好带动 POSE 持续上扬,月内几近翻倍

PoseiSwap是Nautilus Chain上的首个DEX&#xff0c;得益于Nautilus Chain的模块化Layer3构架&#xff0c;PoseiSwap正在基于zk-Rollup方案构建全新的应用层&#xff0c;并基于此构建隐私、合规等全新的特性&#xff0c;为未来其布局RWA领域推动Web2、Web3世界的进一步融合构建基…

【Docker晋升记】No.1--- Docker工具核心组件构成(镜像、容器、仓库)及性能属性

文章目录 前言&#x1f31f;一、Docker工具&#x1f31f;二、Docker 引擎&#x1f30f;2.1.容器管理&#xff1a;&#x1f30f;2.2.镜像管理&#xff1a;&#x1f30f;2.3.资源管理&#xff1a;&#x1f30f;2.4.网络管理&#xff1a;&#x1f30f;2.5.存储管理&#xff1a;&am…

(二)结构型模式:2、桥接模式(Bridge Pattern)(C++实现示例)

目录 1、桥接模式&#xff08;Bridge Pattern&#xff09;含义 2、桥接模式应用场景 3、桥接模式的UML图学习 4、C实现桥接模式的示例 1、桥接模式&#xff08;Bridge Pattern&#xff09;含义 桥接模式是一种结构型设计模式&#xff0c;它将抽象部分与实现部分分离&#…

【算法】逆波兰表达式

文章目录 定义求法代码思想&#xff1a; 定义 逆波兰表达式也称为“后缀表达式”&#xff0c;是将运算符写在操作数之后的运算式。 求法 *如&#xff1a;(ab)c-(ab)/e的转换过程&#xff1a; 先加上所有的括号。 (((ab)*c)-((ab)/e))将所有的运算符移到括号外面 (((ab) c)* …

A33 QT 主线例程 opengl

点击查看 HW33-050 HW33-070 规格书 HW33-050 HW33-070 支持 android 系统和 Linux QT。 HW33-XXX采用4 核Cortex-A7 ARM、Mali400MP2 GPU架构&#xff0c;主频 1.2GHz 的 CPU。内存 存储标配分别为1GB、8GB&#xff0c;内置显卡为Mali400MP2&#xff0c;支持 H.264 1080P …

2023一带一路东盟工商领袖峰会在曼谷成功举行,发明家周初材被授予中泰友好交流大使

今年是共建“一带一路”倡议提出十周年。十年来&#xff0c;共建“一带一路”倡议从理念到行动&#xff0c;从愿景到现实&#xff0c;开展更大范围、更高水平、更深层次的区域合作&#xff0c;致力于维护全球自由贸易体系和开放型世界经济&#xff0c;推动文明交流互鉴&#xf…

uniapp input输入框placeholder文本右对齐

input输入框placeholder文本右对齐 给input标签加上placeholder-class&#xff0c;这个是给placeholder设置样式&#xff0c;右对齐这就是text-align:right;字体颜色之类依次编辑即可。

资讯速递 | ArkUI-X 预览版已正式开源!

OpenHarmony项目群技术指导委员会&#xff08;以下简称“TSC”&#xff09;-跨平台应用开发框架TSG所孵化项目 —— ArkUI-X&#xff0c;近期已正式开源 &#xff0c;开发者基于一套主代码&#xff0c;就可以将在OpenHarmony上开发的精美、高性能应用同时运行在Android、iOS等其…

物联网与5G引领智慧城市时代的到来

智慧城市需要依赖于多种技术&#xff0c;这些技术的应用将城市转变为高效、智能、可持续发展的现代化城市。智慧城市是基于信息技术、物联网和大数据等先进技术的融合&#xff0c;旨在提升城市的运行效率、资源利用效率和居民生活质量。以下是智慧城市需要依赖的主要技术&#…

opencv实战项目 实现手势跟踪并返回位置信息(封装调用)

OpenCV 是一个基于 Apache2.0 许可&#xff08;开源&#xff09;发行的跨平台计算机视觉和机器学习软件库&#xff0c;可以运行在Linux、Windows、Android和Mac OS操作系统上。 需要提前准备opencv 和 mediapipe库 pip --default-timeout5000 install -i https://pypi.tuna.tsi…

【JavaEE基础学习打卡02】是时候了解JavaEE了

目录 前言一、为什么要学习JavaEE二、JavaEE规范介绍1.什么是规范&#xff1f;2.什么是JavaEE规范&#xff1f;3.JavaEE版本 三、JavaEE应用程序模型1.模型前置说明2.模型具体说明 总结 前言 &#x1f4dc; 本系列教程适用于JavaWeb初学者、爱好者&#xff0c;小白白。我们的天…

如何在docker部署一个python项目

导语&#xff1a; 我之前已经实现了在服务器上直接部署一个文件&#xff0c;但是那种部署方式有个明显的缺陷&#xff1a;我如果需要在其他机器部署该项目时&#xff0c;需要重新配置项目所依赖的环境。因此我们需要一种只需要配置一次环境依赖&#xff0c;就可以在其他机器上随…

竞赛项目 深度学习的动物识别

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…

Flv格式视频怎么转MP4?视频格式转换方法分享

FLV格式的视频是一种早期的视频格式&#xff0c;不支持更高的分辨率和比特率&#xff0c;这意味着视频的清晰度和质量受限制&#xff0c;无法很好地保留细节和质量&#xff0c;这种格式的视频已经逐渐被更高质量的视频格式所替代&#xff0c;例如MP4格式&#xff0c;不仅具有很…