Linux per memcg lru lock

news2024/12/23 12:23:52

内核关于per memcg lru lock的重要提交:

f9b1038ebccad354256cf84749cbc321b5347497

6168d0da2b479ce25a4647de194045de1bdd1f1d

背景

自电子计算机诞生以来,内存性能一直是行业关心的重点。内存也随着摩尔定律,在大小和速度上一直增长。云服务器动辄单机接近 TB 的内存大小,加上数以百记的 CPU 数量也着实考验操作系统的资源管理能力。

作为世间最流行的操作系统 Linux, 内核使用 LRU(Last Recent Used)链表来管理全部用户使用的内存,用一组链表串联起一个个的内存页,并且使用 lru lock 来保护链表的完整性。

所有应用程序常用操作都会涉及到 LRU 链表操作,例如,新分配一个页,需要挂在 inactive lru 链上, 2 次访问同一个文件地址, 会导致这个页从 inactive 链表升级到 active 链表, 如果内存紧张, 页需要从 active 链表降级到inactive 链表, 内存有压力时,页被回收导致被从 inactive lru 链表移除。不单大量的用户内存使用创建,回收关系到这个链表, 内核在内存大页拆分,页移动,memcg 移动,swapin/swapout, 都要把页移进移出 lru 链表。

可以简单计算一下 x86 服务器上的链表大小:x86 最常用的是 4k 内存页, 4GB 内存会分成 1M 个页, 如果按常用服务器 256GB 页来算, 会有超过 6 千万个页挂在内核 lru 链表中。超大超长的内存链表和频繁的 lru 操作造成了 2 个著名的内核内存锁竞争, zone lock 和 lru lock。这 2 个问题也多次造成麻烦,系统很忙,但是业务应用并没得到多少 cpu 时间, 大部分 cpu 都花在 sys 上了。一个简单 2 次读文件的 benchmark 可以显示这个问题, 它可以造成 70% 的 cpu 时间花费在 LRU lock 上。https://git.kernel.org/pub/scm/Linux/kernel/git/wfg/vm-scalability.git/tree/case-lru-file-readtwice

作为一个知名内核性能瓶颈, 社区也多次尝试以各种方法解决这个问题, 例如,使用更多的 LRU list, 或者 LRU contention 探测(https://lwn.net/Articles/753058/)。

但是都因为各种原因被 Linux 内核拒绝。

寻找解决方案

通过仔细的观察发现, 内核在 2008 年引进内存组 -memcg 以来, 系统单一的 lru lists 已经分成了每个内存组一个 lru list, 由每个内存组单独管理自己的 lru lists。那么按道理 lru lock 的 contention 应该有所减小啊?为什么还是经常在内部服务器观察到 lru lock hot 引起的 sys 高?

原来, 内核在引入 per memcg lru lists 后,并没有使用 per memcg lru lock, 还在使用旧的全局 lru lock 来管理全部 memcg lru lists。这造成了本来可以自治的 memcg A, 却要等待 memcg B 释放使用的 lru lock。然后 A 拿起的 lru lock 又造成 memcg C 的等待。

那么把全局 lru lock 拆分到每一个 memcg 中, 不是可以理所当然的享受到了 memcg 独立的好处了吗?这样每个memcg 都不会需要等待其他 memcg 释放 lru lock。锁竞争限制在每个 memcg 内部了。

要完成 lru lock 拆分,首先要知道 lru lock 保护了多少对象, 通常情况中, page lru lock 需要保护 lru list 完整性, 这个是必须的。与 lru list 相关的还有 page flags 中的 lru bit,这个 lru bit 用作页是否在 lru list 存在的指示器, 可以避免查表才能知道页是否在 list 中。那么 lru lock 保护它也说的通。

但是 lru lock 看起来还有一些奇怪的保护对象,承担了一些不属于它的任务:

1. PageMlock bit 保护 munlock_vma 和 split_huge_page 冲突,

其实, 上述 2 个函数在调用链中都需要 page lock, 所以冲突可以完全由 page lock 来保证互斥。这里 lru lock 使用属于多余。

2. pagecache xa_lock 和 memcg->move_lock,

xa_lock 并没有需要 lru lock 保护的场景,这个保护也是多余。相反,lru lock 放到 xa_lock 之下, 符合xa_lock/lock_page_memcg 的使用次序。反而可以优化 lru lock 和 memcg move_lock 的关系。

3. lru bit in page_idle_get_page 用在这里是因为担心 page_set_anon_rmap 中, mapping 被提前预取访问,造成异常。用 memory barrier 方式可以避免这个预取, 所以可以在 page idle 中撤掉 lru lock。

+  WRITE_ONCE(page->mapping, (struct address_space *) anon_vma);

经过这样的修改, lru lock 可以在 memory lock 调用层次中降级到最底层。

这时,lru lock 已经非常简化,可以用 per memcg lru lock 来替换全局的 lru lock 了吗?还不行,使用 per memcg lru lock 有一个根本问题,使用者要保证 page 所属的 memcg 不变,但是页在生命周期中是可能转换 memcg 的,比如页在 memcg 之间 migration,导致 lru_lock 随着 memcg 变化, 拿到的 lru lock 是错误的,好消息是 memcg 变化也需要先拿到 lru lock 锁,这样我们可以获得 lru lock 之后检查这个是不是正确的锁:

如果不是, 由反复的 relock 来保证锁的正确性。bingo!完美解决!

由此, 这个 feature 曲折的 upstream 之路开始了。

最终解决

这个 patchset 2019 年发出到社区之后, Google 的 Hugh Dickins 提出, 他和 facebook 的 Konstantin Khlebnikov 同学已经在 2011 发布了非常类似的 patchset,当时没有进主线。不过 Google 内部生产环境中一直在使用。所以现在 Hugh Dickins 发出来他的 upstream 版本。关键路径和我的版本是一样的。

2 个相似 patchset 的 PK, 引起了memcg 维护者 Johannes 的注意, Johannes 发现在 compaction 的时候, relock 并不能保护某些特定场景:

所以他建议,也许增加原子的 lru bit 操作作为 lru_lock 的前提也许可以保护这个场景。Hugh Dickins 则不认为这样会有效,并且坚持他 patchset 已经在 Google 内部用了 9 年了。一直安全稳定。

Johannes 的建议的本质是使用 lru bit 代替 lru lock 做 page isolation 互斥,但是问题的难点在其他地方, 比如在通常的一个 swap in 的场景中:

swap in 的页是先加入 lru,然后 charge to memcg,这样造成页在加入 lru 时,并不知道自己会在那个 memcg 上, 我们也拿不到正确的 per memcg lru_lock,所以上面场景中左侧 CPU 即使提前检查 PageLRU 也找不到正确的 lru lock 来阻止右面 cpu 的操作, 然并卵。

正确的解决方案, 就是上面第 9 步移动到第 7 步前面, 在加入 lru 前 charge to memcg。并且在取得 lru lock 之前检查lru bit是否存在, 这样才可以保证我们可以拿到的是正确的 memcg 的 lru lock。由此提前清除/检查 lru bit 的方法才会有效。这个 memcg charge 的上升, 在和Johannes讨论后, Johannes 在 5.8 完成了代码实现并且和入主线。

在新的代码基础上, 增加了 lru bit 的原子操作 TestClearPageLRU, 把 lru bit 移出了 lru lock 的保护,相反用这个 bit 来做 page isolation 的互斥条件, 用 isolation 来保护页在 memcg 间的移动, 让 lru lock 只完成它的最基本任务, 保护 lru list 完整性。至此方案主体完成。lru lock 的保护对象也由 6 个减小到一个。编码实现就很容易了。

测试结果

方案完成后, 上面提到的 file readtwice 测试中,多个 memcg 的情况下,lru lock 竞争造成的 sys 从 70% 下降了一半,throughput 提高到260%(80 个 cpu 的神龙机器)。

Upstream 过程

经过漫长 4 轮的逐行 review, 目前这个 feature 已经进入了 linus 的 5.11。https://github.com/torvalds/Linux

第一版 patch 发到了社区后, Google 的 skakeel butt 立刻提出, Google 曾经在 2011 发过一样的 patchset 来解决 per memcg lru lock 问题。所以,skakeel 要求我们停止自己开发, 基于 Google 的版本来解决这个问题。然后我才发现真的 2011 年 Google Hugh Dickins 和 Facebook Konstantin Khlebnikov 就大约同时提出类似的 patchset。但是当时引起的关注比较少,也缺乏 benchmark 来展示补丁的效果, 所以很快被社区遗忘了。不过 Google 内部则一直在维护这组补丁,随他们内核版本升级。

对比 Google 的补丁,我们的实现共同点都是使用 relock 来确保 page->memcg 线性化, 其他实现细节则不尽相同。测试表明我们的 patch 性能更好一点。于是我基于自己的补丁继续修改并和 Johannes 讨论方案改进。这也导致了以后每一版都有 Google 同学的反对:我们的测试发现你的 patchset 有 bug,请参考 Google 可以工作的版本。并在 Linux-next 上发现一个小 bug 时达到顶峰(https://lkml.org/lkml/2020/3/6/627)。Google 同学批评我们抄他们的补丁还抄出一堆 bug。

其实这些补丁和 Hugh Dickins 的补丁毫无关联, 并且在和 Johannes 的持续讨论中,解决方案的核心:page->memcg 的线性化已经进化了几个版本了, 从 relock 到 lock_page_memcg,再到 TestClearPageLRU,和 Google 的补丁是路线上的不同。

面对这样的无端指责,memcg 维护者 Johannes 看不下去, 出来说了一些公道话:我和 Alex 同学都在尝试和你不同的方案来解决上次提出的 compacion 冲突问题,而且我记得你当时是觉得这个冲突你无能为力的:

之后 Google 同学分享了他们的测试程序,然后在这个话题上沉默了一段时间。

后来 memcg charge 的问题解决后, 就可以用 lru bit 来保证 page->memcg 互斥了。v17 coding 很快完成后。intel 的 Alexander Duyck,花了 5 个星期, 逐行逐字的 review 整个 patchset,并其基于补丁的改进, 提出了一些后续优化补丁。5 个星期的 review, 足以让一个 feature 错过合适的内核 upstream 窗口。但是也增强了社区的信心。

(重大内核的feature 的merge窗口是这样的:大的 feature 在进入 linus tree 之前, 要在 Linux-next tree 待一段时间, 主要的社区测试如 Intel LKP、Google syzbot 等等也会在着重测试 Linux-next。所以为了保证足够的测试时间, 进入下个版本重要 feature 必须在当前版本的 rc4 之前进入 Linux-next。而当前版本 -rc1 通常 bug 比较多,所以最佳 rebase 版本是 rc2,错过最佳 merge 窗口 rc2-rc4. 意味着需要在等2个月到下一个窗口。并且还要适应新的内核版本的相关修改。)

基于 5.9-rc2 的 v18 版本完成后, Google hugh dickins 同学强势归来,主动申请测试和 review,根据他的意见 v18 做了很多删减和合并,甚至推翻了一些 Alexander Duyck 要求的修改。patch 数量从 32 个压缩到 20 个。Hugh Dickin 逐行 review 了整整 4 个星期。也完美错过了 5.10 和入窗口。之后 v19,Johannes 同学终于回来开始 review。Johannes 比较快,一个星期就完成了 review。现在 v20, 几乎每个 patch 都有了 2 个 reviewed-by: Hugh/Johannes。

然而, 这次不像以前, 以前 patchset 没有人关心, 这次大家的 review 兴趣很大,来了就停不住, SUSE 的 Vlastimil Babka 同学又过来开始 review, 并且提出了一些 coding style 和代码解释要求。不过被强势的 Hugh Dickins 驳回:

Hugh 的影响力还是很大的, Vlastimil 和其他潜在的 reivewer 都闭上了嘴。代码终于进了基于 5.10-rc 的 Linux-next。不过这个驳回也引起一个在 5.11 提交窗口的麻烦, memory 总维护者 Andrew Morthon 突然发现 Vlastimil Babka 表示过一些异议。所以他问我:是不是舆论还不一致, 还有曾经推给你一个 bug, 你解决了吗?

I assume the consensus on this series is "not yet"?

Hugh 再次出来护场:我现在觉得 patchset 足够好了, 足够多人 review 过足够多的版本了, 已经在 Linux-next 安全运行一个多月了,没有任何功能和性能回退, Vlastimil 也已经没有意见了。至于那个 bug, Alex 有足够的证据表明和这个补丁无关。

最终这个 patchset 享受到了 Andrew 向 Linus 单独推送的待遇。进了 5.11。


 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统上搭建Vulhub靶场

Linux系统上搭建Vulhub靶场 ​vulhub​ 是一个开源的漏洞靶场,它提供了各种易受攻击的服务和应用程序,供安全研究人员和学习者测试和练习。要在 Linux 系统上安装和运行 vulhub​,可以按照以下步骤进行: 1. 安装 Docker 和 Docke…

数据结构(八)——Java实现七大排序

一、插入排序 1.直接插入排序 public static void insertSort(int []arr){for (int i 0; i < arr.length; i) {int j i-1;int tmp arr[i];for (; j >0 ; j--) {if(arr[j] > tmp){arr[j1] arr[j];}else{break;}}arr[j1] tmp;}}直接插入排序特性总结 1. 元素集合越…

【算法】滑动窗口—最小覆盖子串

题目 ”最小覆盖子串“问题&#xff0c;难度为Hard&#xff0c;题目如下&#xff1a; 给你两个字符串 S 和 T&#xff0c;请你在 S 中找到包含 T 中全部字母的最短子串。如果 S 中没有这样一个子串&#xff0c;则算法返回空串&#xff0c;如果存在这样一个子串&#xff0c;则可…

【三大运营商】大数据平台体系架构【顶层规划设计】

在国内运营商&#xff08;如中国移动、中国联通、中国电信&#xff09;的大数据平台建设中&#xff0c;顶层规划设计至关重要。以下是针对三大运营商为例【如电信】的大数据平台体系架构的顶层规划设计方案&#xff0c;涵盖整体架构、关键组件、数据管理、应用场景等方面。 1. …

C#数据结构与算法实战入门指南

前言 在编程领域&#xff0c;数据结构与算法是构建高效、可靠和可扩展软件系统的基石。它们对于提升程序性能、优化资源利用以及解决复杂问题具有至关重要的作用。今天大姚分享一些非常不错的C#数据结构与算法实战教程&#xff0c;希望可以帮助到有需要的小伙伴。 C#经典十大排…

音视频入门基础:AAC专题(6)——FFmpeg源码中解码ADTS格式的AAC的Header的实现

一、引言 通过FFmpeg命令&#xff1a; ./ffmpeg -i XXX.aac 可以获取到ADTS格式的AAC裸流的音频采样频率、声道数、采样位数、码率等信息&#xff1a; 在vlc中也可以获取到这些信息&#xff08;vlc底层也使用了FFmpeg进行解码&#xff09;&#xff1a; 所以FFmpeg和vlc是怎样…

【混淆矩阵】Confusion Matrix!定量评价的基础!如何计算全面、准确的定量指标去衡量模型分类的好坏??

【混淆矩阵】Confusion Matrix&#xff01;定量评价的基础&#xff01; 如何计算全面、准确的定量指标去衡量模型分类的好坏&#xff1f;&#xff1f; 文章目录 【混淆矩阵】Confusion Matrix&#xff01;定量评价的基础&#xff01;1. 混淆矩阵2.评价指标3.混淆矩阵及评价指标…

Redis基础数据结构之 ziplist 压缩列表 源码解读

目录标题 ziplist 是什么?ziplist 特点ziplist 数据结构ziplist 节点pre_entry_lengthencoding 和 lengthcontent ziplist 基本操作插入&#xff08;Insertion&#xff09;删除&#xff08;Deletion&#xff09;查找&#xff08;Search&#xff09;更新&#xff08;Update&…

Qt多元素控件——QTableWidget

文章目录 QTabWidget核心属性、方法和信号使用示例 QTabWidget核心属性、方法和信号 QTableWidget表示一个表格控件&#xff0c;一个表格中包含若干行&#xff0c;每一行包含若干列。 表格中的每一个单元格&#xff0c;是一个QTableWidgetItem对象。 QTableWidget核心方法&a…

Java 每日一刊(第9期):数组

文章目录 前言什么是数组初始化数组如何访问和操作数组遍历数组多维数组数组的常见操作复制数组排序数组搜索数组 数组的长度和异常处理Arrays 工具类本期小知识 “简单是效率的灵魂。” 前言 这里是分享 Java 相关内容的专刊&#xff0c;每日一更。 本期将为大家带来以下内…

云计算和虚拟化技术 背诵

https://zhuanlan.zhihu.com/p/612215164 https://zhuanlan.zhihu.com/p/612215164 云计算是指把计算资源、存储资源、网络资源、应用软件等集合起来&#xff0c;采用虚拟化技术 &#xff0c;将这些资源池化&#xff0c;组成资源共享池&#xff0c;共享池即是“云”。 云计算…

从零开始学习Linux(12)---进程间通信(信号量与信号)

1.信号量 信号量是计算机科学中用于同步和互斥的一种抽象数据类型。在并发编程中&#xff0c;当多个进程或线程需要访问共享资源时&#xff0c;信号量用来确保资源在同一时刻只被一个进程或线程访问&#xff0c;从而避免竞争条件。 信号量通常具有以下特性&#xff1a; 整…

Fisco Bcos 2.11.0配置console控制台2.10.0及部署调用智能合约

Fisco Bcos 2.11.0配置console控制台2.10.0及部署调用智能合约 文章目录 Fisco Bcos 2.11.0配置console控制台2.10.0及部署调用智能合约前言版本适配一、启动FIsco Bcos区块链网络二、获取控制台文件三、配置控制台3.1 执行download_console.sh脚本3.2 拷贝控制台配置文件3.3 修…

读构建可扩展分布式系统:方法与实践06异步消息传递

1. 异步消息传递 1.1. 通信是分布式系统的基础&#xff0c;也是架构师需要纳入其系统设计的主要问题 1.2. 客户端发送请求并等待服务器响应 1.2.1. 这就是大多数分布式通信的设计方式&#xff0c;因为客户端需要得到即时响应后才能继续 1.2.2. 并非所有系统都有这个要求 1…

数据时代,职场离不开的远程控制工具

中秋了大概率是在正常放假了吧&#xff0c;如果突发遇到需要你处理的文件怎么办呢&#xff1f;其实有远程操作工具你就不用到办公室了。向日葵远程控制软件这些工具就可以帮我们远程实现控制电脑操作。如果你也有这方面需求就继续看吧&#xff0c;这次我将介绍几款我用过效果比…

Redis常见应用场景

目录 一、实现博客点赞功能 二、实现博客点赞用户列表功能 三、好友关注和取关以及求共同关注 四、实现关注推送 1、拉模式 2、推模式 3、推拉结合 四、三种模式对比 这里简单记录一下&#xff0c;没有实现方法&#xff0c;只是帮助记忆 一、实现博客点赞功能 可以通…

[NSSRound#4 SWPU]hide_and_seek-用gdb调试

看反汇编 ; __unwind { .text:0000000000001514 F3 0F 1E FA endbr64 .text:0000000000001518 55 push rbp .text:0000000000001519 48 89 E5 mov rbp, rsp .text:000000000000151C 53 …

python tkinter

基本使用 基于tkinter创建 GUI基本四步&#xff1a;窗口->组件->布局->事件 1.创建窗口对象 from tkinter import *root Tk() # 创建窗口root.mainloop() # 进入事件循环 2.创建组件 按钮文本等组件 btn Button(root) # 创建Button组件&#xff0c;使组件在…

re题(25)BUUFCTF-[GUET-CTF2019]re

BUUCTF在线评测 (buuoj.cn) 查下壳&#xff0c;是upx壳 脱一下 查看字符串&#xff0c;定位到主函数&#xff0c;也可以用ctrlE的方式找到主函数 明显&#xff0c;sub_4009AE是对flag加密的关键函数 进入sub_4009AE看一下 看到这儿有一堆大数和方程&#xff0c;我们知道要用z…

Transformer模型详细步骤

Transformer模型是nlp任务中不能绕开的学习任务&#xff0c;我将从数据开始&#xff0c;每一步骤都列举出来&#xff0c;然后对应重点的代码进行讲解 ------------------------------------------------------------------------------------------------------------- Trans…