RFC-0201:回收客户机内存

RFC-0201:回收 guest 虚拟机的内存
状态已接受
区域
  • 虚拟化
说明

允许主机使用之前分配并延迟释放的客户虚拟机内存

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2022-12-15
审核日期(年-月-日)2022-12-01

摘要

允许主机回收访客使用的内存。

设计初衷

即使访客内存不再使用,在访客中运行内存消耗型应用并在主机中启动内存消耗型应用也可能会导致 OOM。

原因有以下两点。

  1. 我们目前不允许主机自动从 guest 中获取内存。
  2. 我们不支持访客主动告知宿主“这是一份未使用的内存页列表,如果需要,请随意使用”的方式。

用户转化历程示例

  1. 启动 Termina 或 Debian 访客
  2. 在 guest 中启动内存消耗型应用
  3. 退出访客中占用大量内存的应用
  4. 在宿主中启动内存消耗型应用
  5. 观察主机达到 OOM 状态 💥

背景

当操作系统启动时,它会询问硬件(如果是 guest,则为 hypervisor)有多少物理内存。如果宿主机告知 guest 它有 4GiB 的 RAM,guest 将知道它可以分配大约一百万个 4KiB 页面。

实现此目的的方法是在客机操作系统认为的物理地址(称为客机物理地址)与实际物理地址(称为主机物理地址)之间引入一个映射层。提醒一下,第一级地址转换是指客户机虚拟内存地址与客户机物理地址之间的映射。换句话说,客户机虚拟地址首先由硬件转换为客户机物理地址,硬件使用由客户机操作系统管理的页表进行转换,然后转换为宿主机物理地址 - 后一种转换由 Hypervisor 管理的页表控制。

当访问尚未映射的客户机虚拟地址到客户机物理地址时,客户机会自行处理发生的缺页中断。Hypervisor 处理从客户机物理地址到主机物理地址的转换的缺页中断。

我们使用的是分页内存,这意味着当 Hypervisor 为 guest 虚拟机提供 X GiB RAM 时,它不会预先分配任何内存,而仅在实际需要这些内存页时才进行分配。在此示例中,“必需”是指访客尝试访问相应网页。从 Hypervisor 的角度来看,已分配的物理内存页属于客户机,不能用于主机进程。

因此,客户机最终可能会分配到大量未使用的物理内存页,而主机可能内存不足,无法运行自己的进程。

下面我们将介绍 2 种使主机可以使用客户内存的方法:

Virtio-balloon

主机可以通过配置目标气球大小,告知 guest virtio-balloon 驱动程序将气球 inflate 到特定大小。膨胀气球意味着客机会预留所需数量的内存页,并将分配的内存页的客机物理地址报告给宿主机。从这一刻起,主机可以取消提交报告的内存页,并使用之前支持报告的内存的物理内存。

如果 virtio-balloon 协商了 VIRTIO_BALLOON_F_DEFLATE_ON_OOM,则 guest 可以随时开始再次使用内存。请参阅 Virtio 规范 5.5.6.1。目前,我们启用了 VIRTIO_BALLOON_F_DEFLATE_ON_OOM。

宿主机可以通过减小气球的目标大小,允许客户机重新使用气球中的页面。如果配置的 balloon 大小小于 balloon 中的实际页面数,客户机可能会重复使用之前提供给 balloon 的页面。如果客户机想再次使用内存,它会使气球膨胀 (deflate),从而告知主机客户机将来会使用一系列物理客户机地址。在 deflate 之后,当 guest 访问已从气球中移除的页面时,会遇到 guest 物理到 host 物理页面错误,并且 hypervisor 会为 guest 分配一个新的物理内存页面以供使用。

如需详细了解 virtio-balloon 核心功能,请参阅 Virtio Balloon. slidesVirtio Balloon. video

免费网页报告

在 2020 年,virtio-ballon 获得了一项名为“空闲内存页报告”的新功能。

空闲内存页报告功能可让客户机将空闲内存页报告给宿主机。为此,guest 会将 4MiB 大小的(Linux 实现常量)空闲页面添加到空闲页面报告中,并将报告发送给 host。在主机确认报告之前,guest 保证不会重复使用任何空闲页面。

当宿主机收到空闲内存页报告时,它会取消提交内存页,使这些内存页可供宿主机应用使用,并向客户机确认报告。此时,客户机可能会重复使用之前已释放并确认的页面。如果访客决定重新使用该页面,宿主会检测到访客物理内存到宿主物理内存的页面错误,并分配新的物理内存页面来满足访客请求。

如需详细说明,请参阅 Free page reporting: by Alexander Duyck ( Intel ). Slide 10

利益相关方

辅导员

  • cpu@google.com

审核者

  • abdulla@google.com
  • dahastin@google.com
  • tjdetwiler@google.com

已咨询

  • cwd@google.com

共同化

此 RFC 已通过虚拟化团队的审核。我与 cwd@google.com 讨论了该方法,他正在为 ChromeOS 解决类似但更大的问题。

设计

目标

  • 在客机中运行应用后,为宿主机回收内存。
  • 最大限度地降低内存回收对 guest 和 host 的性能影响。

非目标

  • 在多个 guest 之间平衡高内存使用量。
  • 当 guest 和 host 争用内存时,动态确定应用的优先级。
  • 支持为 Fuchsia 访客回收内存。

成功标准

  • 在“动机”部分所述的用户历程中,Fuchsia 未达到 OOM。
  • 主机可用内存大致恢复到在客机中运行内存消耗型应用之前的水平。
  • 在内存回收之前和之后,guest 都可以继续运行占用大量内存的应用。
  • 除非主机面临低内存压力,否则访客页面缓存不会受到影响。
  • 在至少一个 guest 运行时,主机 OOM 的数量显著减少。

解决方法

  • 使用 virtio_balloon 中的空闲页面报告功能回收空闲内存以供宿主机使用。
  • 在主机内存压力为低和严重时,膨胀气球以清空客户机页面缓存并回收碎片化内存页。
  • 概念验证证实,免费页面报告确实会按预期回收内存。

实现

使用免费的网页报告

我们将使用空闲内存报告功能向宿主报告并回收所有空闲内存。

全部:指顺序为 PAGE_REPORTING_MIN_ORDER 或更高级别的任何内容(在 Linux 内核中定义为 4MiB)。

使用空闲页面报告将在未来 30 秒内回收大部分内存。报告的可用内存页大小为 2MiB 或 4MiB,预计会出现一定程度的内存碎片。在嘉宾端,免费页面报告会随时间分批生成,以最大限度地减少对性能的影响。

Linux-5.15 将以 2MiB 和 4MiB 块为单位报告 30 秒内的大部分可用内存。

空闲页面报告不会逐出 Linux 页面缓存,如果 guest 运行的是 IO 密集型工作负载,这可能会成为问题。如需了解 Linux 中的页面缓存,请参阅 Linux 页面缓存基础知识

当我们的 Linux guest 映像开始使用 MGLRU 时,这种情况可能会在未来发生变化。请参阅 MGLRU 的“缺点、替代方案和未知问题”部分。

不随意丢弃页面缓存是一件好事,因为页面缓存的存在是有原因的。只有当主机确实需要内存时,才应从客户机页面缓存中获取内存。这意味着,当宿主内存不足时,我们需要提供一种方法来回收用于页面缓存的客户内存。

在主机内存压力过大时膨胀 guest 气球

第二项更改是使用 memorypressure 提供程序在主机内存级别为 WARNING 和 CRITICAL 时膨胀气球。

充气气球可实现两个目标:

  • 逐出访客页面缓存,如果访客正在执行密集的文件 IO 并填充页面缓存,这一点非常重要。
  • 回收大部分碎片化内存页,因为气球膨胀以 4KiB 为粒度完成,而空闲页报告使用 4MiB。

膨胀量将与可用的客机内存成正比。在出现 WARNING 和 CRITICAL 主机内存事件时,气球将膨胀到可用客户内存的 90%。我们必须同时增加 WARNING 和 CRITICAL 事件,以防可用内存从 NORMAL резко 下降到 CRITICAL。当宿主机内存压力恢复到正常水平时,气球将放气至 0%。

我们希望避免在主机内存压力过大时不断膨胀和收缩气球。气球膨胀会给 guest 和 host 带来性能开销。此外,根据 Intel 的说法,气球大小不断调整会导致许多 TLB shootdown

为防止气球大小来回跳动,我们将气球膨胀操作限制为每 X 秒膨胀一次。X 将在 teamfood 测试期间配置。初始值为 1 分钟。可能会出现更多超时,例如,如果主机长时间处于内存压力警告状态,则可能会出现压缩气球的超时。可以根据 teamfood 测试遥测数据添加额外的超时。

性能

实现内存回收功能可提高用户在客户机中运行内存密集型应用时的主机内存性能。主机将有更多可用内存来处理工作,而无需采用内存压缩和其他 CPU 消耗大的方式来获取内存,同时客机有可用内存可供回收。

在 Linux 实现中,免费页面报告操作在 30 秒内交错进行,以减少对客户的性能影响。我们预计,在内存密集型 guest 工作负载中,性能会受到 1%-2% 的影响。请参阅免费网页报告基准

当主机内存不足时,膨胀气球可能会给主机和客户机都增加额外的负载。

我们需要在主机在内存压力下运行时,分别在启用和未启用内存回收的情况下,测量客户机“TLB shootdown”中断的数量。

基准:

要捕获的指标

  • 基准测试报告的用于检测性能回归的一般指标
  • 基准测试前后,guest 和 host 中的可用内存
  • 访客中的 TLB shootdown 中断

安全注意事项

回收的空闲页面在取消提交操作中会被清零,与气球膨胀相同。这样可以防止访客信息泄露给房东和其他房客。

测试

大部分工作将由单元测试和 2 个集成测试完成。一个集成测试将涵盖空闲页面报告内存回收,另一个将涵盖客户机页面缓存与气球膨胀的交互。

“动机”部分中描述的用户体验历程将通过手动方式进行测试。 用户历程对访客启动的依赖使其不具备密封性。如果我们有自动端到端虚拟化测试,则可以扩展该测试以涵盖此场景。我们认为,为此 RFC 构建自动化端到端虚拟化测试并不实际。

缺点、替代方案和未知因素

多代 LRU 框架

多代 LRU 框架 (MGLRU) 是一项针对 Linux 内核的内存改进功能。Linux 中当前的页面回收在 CPU 使用方面成本过高,并且在选择要逐出的内容时往往会做出错误的选择。MGLRU 旨在比当前的 Linux 内核页面回收代码做出更好的选择,并以更高的效率实现这一目标。Google 工程师提供的数据显示,冷启动时间缩短了多达 16%,同时低内存终止次数也减少了;ChromeOS 在浏览器中内存不足终止次数减少了 59% 以上,低内存标签页舍弃次数减少了 96%;服务器结果也非常令人期待。

如需了解详情,请参阅 PATCH v14 00/14 多代 LRU 框架

我们目前使用的 Termina 5.15 没有 MGLRU 补丁。上游中没有 MGLRU。Termina 内核开发者正在等待 MGLRU 被上游接受,以便将其向后移植到 Termina 5.15

MGLRU API 可用于驱动空闲内存页报告逻辑。值得研究一下使用 MGLRU API 来提升 Linux 内核中的空闲页面报告性能。如果成功,则可以提议将其合并到上游。这将是对 Linux 内核中现有空闲内存页报告解决方案的优化。

允许宿主回收客户内存时,不依赖于 MGLRU。

气球大小调整替代方案

最初建议的执行内存回收的方式,因此得名内存守护程序。

这是 ChromeOS 目前使用的方法。它存在一些缺点

  • 需要选择轮询间隔。
  • Intel 报告称,即使是小幅气球调整也会产生性能开销。请参阅不断调整气球大小会导致许多 TLB shootdown
  • 为了使此功能正常运行,气球必须始终膨胀到大约 90% 的客户机内存。
  • 访客应用可以快速分配内存,并在下一个轮询间隔之前被 OOM 守护程序终止

ChromeOS 方法

ChromeOS 目前正在开发下一代自适应 virtio-balloon。简而言之,该想法是在客机和宿主机中使用低内存终止守护程序来调整气球大小,而不是终止应用。此外,还计划使用 MGLRU 来指导气球大小调整逻辑。

ChromeOS 正在解决一个更难且不同的问题:

  • ChromeOS 有多个客户机和主机,它们都争用内存
  • ChromeOS 和所有客户机都有 MGLRU 和低内存终止守护程序可供连接。
  • 为所有工作负载选择合适的气球大小是一个难以解决的问题。
    • 我们必须定义一组启发式方法来指导气球膨胀/收缩逻辑
    • 必须有设备群组的数据才能构建启发式方法并分析其效果。
    • 更重要的是,如果您使用 LMKD 触发气球膨胀/收缩,则必须快速调整气球大小,否则 OOM killer 会在气球膨胀/收缩时终止应用

Fuchsia 虚拟化没有庞大的设备群来收集统计信息。我们试图解决的问题要简单得多。Fuchsia 没有可挂钩的 OOM killer。

Fuchsia 宿主机允许同时运行多个 guest(Termina、Debian、Fuchsia)。目前,这不是主要用例,通常用户和测试运行单个 guest。一旦我们开始使用更强大的硬件,这种情况可能会发生变化。如果访客不使用所有可用内存,建议的解决方案将适用于多个超额订阅的访客。例如,空闲的 Debian 客户机和活跃的 Termina 客户机。

如果我们必须支持多个尝试使用所有可用内存的客户机,问题空间会变得更大。确定哪个来宾应用或哪个来宾更重要应是产品政策。我们应侧重于向产品公开合适的工具,但在平台层面,我们不希望对低内存处理方式做出规定。

我们将采用更简单、更可预测的解决方案来解决当前问题,同时添加数据收集功能来分析 OOM,并确定是否需要添加更复杂的启发式方法。

通过 DAX 和 virtio-fs 共享宿主和访客页面缓存

DAX 映射允许 guest 直接从主机缓存访问文件内容,从而避免 guest 和主机之间出现重复。添加了对 virtio-fs 的支持,并支持 DAX,从而能够共享页面缓存,并在内存压力过大时添加页面缓存丢弃,这是一种替代解决方案,可用于替代 virtio 气球膨胀来清除 guest 页面缓存。与因气球膨胀而导致的全面页面缓存逐出相比,精细的页面缓存控制效果更好。我们可以舍弃旧的页面缓存,同时保留新的页面缓存,以缓解内存压力,而不会对宿主/访客性能造成太大影响。

在先技术和参考资料