RFC-0201:回收客户机内存

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

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

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

摘要

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

设计初衷

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

原因有以下两方面:

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

用户历程示例

  1. 启动 termina 或 debian 客机
  2. 在客机中启动内存消耗型应用
  3. 在客机中退出内存消耗型应用
  4. 在主机中启动内存消耗型应用
  5. 观察主机达到 OOM 💥

背景

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

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

客机会自行处理在访问尚未映射的客机虚拟地址到客机物理地址时发生的页面错误。Hypervisor 会处理客机物理地址到主机物理地址转换的页面错误。

我们使用的是分页内存,这意味着当 Hypervisor 为客机提供 X GiB 的 RAM 时,它不会预先分配任何内存,而仅在实际需要这些页面时才分配。在这种情况下,“需要”是指访客尝试访问该页面。从 Hypervisor 的角度来看,分配的物理内存页属于客机,不能用于主机进程。

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

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

Virtio 气球

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

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

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

如需详细了解 virtio 气球的核心功能,请参阅 Virtio Balloon. 幻灯片Virtio Balloon. 视频

空闲页面报告

2020 年,virtio 气球收到了一项名为“空闲页面报告”的新功能。

空闲页面报告为客机提供了一种将空闲内存页报告给主机的方式。客机通过将 4MiB 大小的(Linux 实现常量)空闲页面添加到空闲页面报告并将报告发送给主机来实现此目的。客机保证在主机确认报告之前不会重复使用任何空闲页面。

当主机收到空闲页面报告时,它会取消提交内存页,使其可供主机应用使用,并将确认报告发送回客机。此时,客机可以重复使用之前释放和确认的页面。如果客机决定重复使用该页面,主机将检测到客机物理地址到主机物理地址的页面错误,并分配一个新的物理页面以满足客机请求。

如需详细 说明,请参阅 Alexander Duyck ( Intel) 的空闲页面报告:幻灯片 10

利益相关方

教员

  • cpu@google.com

审核人

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

咨询对象

  • cwd@google.com

共同化

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

设计

目标

  • 在客机中运行应用后,回收主机的内存。
  • 最大限度地减少内存回收对客机和主机性能的影响。

非目标

  • 平衡多个客机之间的内存重度使用。
  • 当客机和主机争用内存时,动态确定应用的优先级。
  • 支持 Fuchsia 客机的内存回收。

成功标准

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

解决方法

  • 使用 空闲页面报告 功能回收 virtio_balloon 中的空闲内存 以供主机使用。
  • 在 LOW 和 CRITICAL 主机内存压力事件中膨胀气球,以刷新客机页面缓存并回收碎片化的内存页。
  • 概念验证证实,空闲页面报告确实按预期回收了内存。

实现

使用空闲页面报告

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

所有 指的是任何 PAGE_REPORTING_MIN_ORDER 或更高的顺序(在 Linux 内核中定义为 4MiB)。

使用空闲页面报告将在接下来的 30 秒内回收 大部分 内存。报告的空闲页面大小为 2MiB 或 4MiB,预计会出现一些内存碎片。在客机端,空闲页面报告会随着时间的推移而交错,以最大限度地减少对性能的影响。

Linux-5.15 将在 30 秒内以 2MiB 和 4MiB 的块报告大部分空闲内存。

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

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

随意丢弃页面缓存不是好事,它存在是有原因的。主机应仅在实际需要时才从客机页面缓存中获取内存。这意味着,当主机面临内存压力时,我们需要提供一种回收用于页面缓存的客机内存的方法。

在主机内存压力下膨胀客机气球

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

膨胀气球实现了两个目标:

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

膨胀量将与可用客户机内存成正比。在 WARNING 和 CRITICAL 主机内存事件中,气球将膨胀到可用客机内存的 90%。我们必须在 WARNING 和 CRITICAL 事件中都膨胀,以防空闲内存从 NORMAL 急剧下降到 CRITICAL。当主机内存压力恢复到 NORMAL 时,气球将 deflate 到 0%。

我们希望避免在主机面临内存压力时不断膨胀和 deflate 气球。气球膨胀会对客机和主机产生性能成本。最重要的是,根据 Intel 的说法,不断调整气球大小会导致许多 TLB 射击

为防止提示框大小来回跳动,我们将提示框膨胀操作限制为每 X 秒膨胀一次。X 将在 teamfood 测试期间配置。初始值为 1 分钟。可能会出现更多超时,例如,如果主机在内存压力 WARNING 中停留时间过长,则会出现 deflate 气球的超时。可以根据 teamfood 测试遥测数据添加其他超时。

性能

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

在 Linux 实现中,空闲页面报告操作交错超过 30 秒,以减少对客机性能的影响。我们预计在内存密集型客机工作负载中会产生 1%-2% 的性能影响。请参阅空闲页面报告 基准

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

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

基准:

要捕获的指标

  • 基准报告的常规指标,用于检测性能回归
  • 基准测试前后客机和主机中的可用内存
  • 客机中的 TLB 射击中断

安全注意事项

回收的空闲页面在取消提交操作期间会被清零,与气球膨胀相同。这将防止客机信息泄露给主机和其他客机。

测试

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

“设计初衷”部分所述的用户历程将手动进行测试。 用户历程对客机启动的依赖性使其不具有密封性。如果我们有自动端到端虚拟化测试,则可以对其进行扩展以涵盖此场景。我们认为,为此 RFC 依赖于构建自动端到端虚拟化测试是不切实际的。

缺点、替代方案和未知

多代 LRU 框架

多代 LRU 框架(又称 MGLRU)是 Linux 内核的内存改进功能。Linux 中当前的页面回收在 CPU 使用方面过于昂贵,并且在要逐出哪些内容方面通常会做出糟糕的选择。MGLRU 旨在做出比当前 Linux 内核页面回收代码更好的选择,并更高效地执行此操作。Google 工程师的数据显示,冷启动时间缩短了 16%,同时低内存终止次数减少;ChromeOS 的内存不足终止次数减少了 59%,浏览器中低内存标签页丢弃次数减少了 96%;服务器结果也非常有希望。

如需了解详情,请参阅 PATCH v14 00/14 Multi-Gen LRU Framework

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

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

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

不断调整提示框大小的替代方案

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

这是 ChromeOS 目前使用的方法。它有许多缺点

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

ChromeOS 方法

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

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

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

Fuchsia 虚拟化没有大型设备群组可供收集统计信息。我们尝试解决的问题要简单得多。Fuchsia 没有 OOM 终止守护程序可供连接。

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

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

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

使用 DAX 和 virtio-fs 共享主机和客机页面缓存

DAX 映射允许客机直接从 主机缓存访问文件内容,从而避免客机和主机之间的重复。添加对 DAX 的 virtio-fs 支持、启用页面缓存共享以及在内存压力下添加页面缓存丢弃是 virtio 气球膨胀的替代解决方案,用于清除客机页面缓存。由于气球膨胀,粒度页面缓存控制比全面页面缓存逐出更好。我们可以舍弃旧页面缓存,同时保留新页面缓存,以缓解内存压力,而不会对主机/客机性能产生太大影响。

在先技术和参考文档