使用无退出中断实现虚拟机的裸金属性能
摘要
直接设备分配通过允许客户虚拟机与I/O设备通信而无需主机参与,从而提升了客户虚拟机的性能。但即使采用设备分配,客户机仍无法接近裸机性能,因为主机仍然会拦截所有中断,包括由已分配设备发出、用于通知客户机其I/O请求已完成的中断。主机的介入导致了多次不必要的客户机/主机上下文切换,严重影响了I/O密集型工作负载的性能。为解决此问题,我们提出了无退出中断(ELI),这是一种纯软件方法,可在客户虚拟机内直接且安全地处理中断。通过将主机从中断处理路径中移除,ELI使未修改的、不可信的客户机的吞吐量和延迟改善了1.3×–1.6×倍,使其即使在最苛刻的I/O密集型工作负载下也能达到裸机性能的97–100%。
1. 引言
I/O活动是虚拟化环境性能的主要影响因素,这推动了直接设备分配的发展,即主机将物理I/O设备直接分配给客户虚拟机。这类设备的示例包括磁盘控制器、网卡和GPU。相比其他I/O虚拟化方法,直接设备分配能够提供更优的性能,因为它几乎完全将主机从客户机的I/O路径中移除。如果没有直接设备分配,I/O密集型工作负载可能会遭受不可接受的性能下降。然而,在x86 CPU(目前最主流的虚拟化平台)上,仅靠直接设备分配仍无法使I/O密集型工作负载达到接近裸机(非虚拟化)性能的水平;根据我们的测量,此类工作负载的性能仅能达到裸机性能的60–65%。我们发现,几乎整个性能差距都是由已分配设备的中断所引起的。
I/O设备会生成中断,以通知中央处理器I/O操作已完成。在虚拟化环境中,每次设备中断都会触发一次代价高昂的退出,导致客户机被暂停而主机被恢复,无论该设备是否已被分配。主机首先按照x86规范要求向硬件发出信号,表示物理中断已完成;然后向客户机注入相应的(虚拟)中断,并恢复客户机的执行。客户机继而处理该虚拟中断,并像主机一样发出完成信号,认为自己是直接与硬件交互。此操作会触发另一次退出,促使主机模拟虚拟中断的完成,并再次恢复客户机。处理中断的事件链如图1所示。
由中断引起的客户机/宿主机上下文切换对于非I/O密集型工作负载而言会产生可容忍的开销,这一事实使得一些先前的虚拟化研究声称实现了裸机性能。但我们的测量结果表明,这种开销很快变得不可容忍,严重影响吞吐量低至50 Mbps的客户机。值得注意的是,以往的研究通过降低保护性或修改客户机的方式来改进虚拟I/O,而我们的研究重点是最具挑战性的虚拟化场景,即针对不可信且未修改的客户机。
许多先前的研究指出,中断是开销的主要来源之一,在裸金属环境和虚拟化环境中均提出了多种减少中断开销的技术。原则上,可以通过调整设备及其驱动程序以产生更少的中断,从而降低相关开销。但在实践中实现这一点远非易事,并可能对延迟和吞吐量产生不利影响。
我们的方法基于以下观察:运行I/O密集型客户机的核心所经历的高中断率,主要由分配给该客户机的设备产生。事实上,即使采用了诸如中断合并和混合轮询等标准技术来减少中断数量,我们测得每秒物理中断次数仍超过15万次。如前所述,由此产生的客户机/主机上下文切换几乎完全导致了相对于裸金属性能的下降。为了消除这些切换,我们提出了无退出中断(ELI),这是一种纯软件方法,以安全方式在客户机内部直接处理物理中断。
通过 ELI,物理中断会直接传递给客户机,使其能够在无需主机参与的情况下处理其设备的中断;ELI 确保每个客户机将所有其他中断转发给主机。在 x86 硬件上,中断通过一个指向函数的软件控制表进行传递,当类型为k的中断触发时,硬件会调用kth个函数。ELI 并不使用客户机的表,而是维护、操作并保护一个“影子表”,使得与已分配设备相关的表项指向客户机的代码,而其他表项则设置为触发到主机的退出。
我们通过微观和宏观基准测试对ELI进行了实验评估。我们的基线配置采用标准技术来减少(合并)中断次数,从而展示了ELI在现有技术水平之上的优势。我们证明了ELI能够降低限制可实现吞吐量的中央处理器开销,从而将客户机的吞吐量和延迟性能提高1.3×–1.6×倍。值得注意的是,以往 I/O密集型客户机的吞吐量仅能达到裸机性能的60–65%,而使用ELI后,其性能可达最优性能的97–100%。因此,ELI 使得例如整合传统数据中心工作负载成为可能,这些工作负载目前由于不可接受的性能损失而仍保持非虚拟化状态。
2. 动机与相关工作
在过去的几十年里,中断一直是硬件设备向操作系统发送异步事件的主要方式。与轮询设备相比,使用中断接收通知的主要优势在于,处理器在等待中断时可以自由执行其他任务。当中断发生频率相对较低时,这种优势尤为明显,这种情况一直持续到高性能存储和网络适配器出现为止。对于这些设备,中央处理器可能会被大量中断淹没,导致几乎没有时间执行中断处理程序之外的任何代码。当中断在客户机中运行操作系统时,其开销更高,因为每次中断都会引发多次退出。ELI 消除了其中大多数退出及其相关开销。
2.1. 通用中断处理方法
我们现在调研适用于裸金属和虚拟化环境的通用方法。
轮询完全禁用中断,并以固定时间间隔轮询设备以获取新事件。其优点是处理设备事件变为同步操作,使操作系统能够决定何时进行轮询,从而限制处理程序调用次数。缺点是增加了延迟、功耗上升(因为处理器无法进入空闲状态),以及在没有事件待处理时浪费CPU周期。如果在不同的核心上执行轮询,则可改善延迟,但会浪费一个核心。
一种用于减少中断处理开销的hybrid方法是在使用中断和动态切换之间进行转换轮询。Linux 默认通过 NAPI 机制使用这种方法。在实践中,中断与轮询之间的切换并不总是效果良好,部分原因是预测设备未来将发出的中断数量较为复杂。
另一种方法是中断合并,即操作系统编程设备在一个时间间隔内发送一次中断,或每多个事件发送一次中断,而不是每个事件都发送一次中断。与混合方法类似,中断合并会延迟中断,因此可能增加延迟和突发TCP流量。当工作负载在客户机中运行时,选择合适的模型和参数尤为复杂。要为各种不同类型的工作负载都配置得当,即使不是不可能,也极为困难。与中断合并不同,ELI不会减少中断的数量;相反,它优化了针对虚拟机的中断处理流程。因此,中断合并与ELI是互补的,正如我们在第5.4节中所示:中断合并减少了中断的数量,而ELI降低了每次中断的处理成本。
第5节中的所有评估均使用默认Linux配置进行,该配置结合了混合方法(通过NAPI)和合并。
2.2. 特定于虚拟化的方法
使用模拟或半虚拟化设备在主机端提供了很大的灵活性,但其性能远低于设备分配,更不用说裸金属了。Liu表明, SR-IOV设备的设备分配可以实现接近裸金属的吞吐量,但代价是CPU利用率高出多达2× 。他还证明,中断对性能有很大影响,并且在发送和接收路径中均带来显著开销。
存在一些软件技术,通过查找退出指令的代码块并对整个代码块仅执行一次退出来减少退出次数。当开销的主要原因在于客户机代码时,这些技术可以提高虚拟机运行的效率。然而,当开销的原因在于外部中断时,例如使用单根 I/O虚拟化的I/O密集型工作负载,此类技术无法减轻开销。
董等人讨论了在Xen虚拟机监控程序中实现单根I/O虚拟化支持的框架。他们的结果表明,单根I/O虚拟化可以在 10Gbps网络接口控制器(网卡)上达到线速。然而,CPU利用率是裸金属的148%。此外,该结果是通过使用自适应中断合并实现的,这会增加I/O延迟。
一些研究试图减少上述虚拟环境中中断的额外开销。vIC讨论了在虚拟存储设备中进行中断合并的方法,并在宏基准测试中显示出最高达5%的改进。他们的方法使用“飞行中的命令”数量来决定合并多少中断。因此,正如作者所说,由于缺乏关于飞行中的命令(或数据包)的信息,该方法不能用于网络设备。董等人在半虚拟化环境中通过在客户机中轮询和接收端扩展使用虚拟中断合并来降低网络开销。如上所述,轮询存在其缺点,而ELI则改进了更面向性能的设备分配环境。
NoHype认为现代虚拟机监控器容易受到其客户机的攻击。在NoHype模型中,虚拟机监控器是一个薄层,仅负责启动、停止和执行其他管理操作,而不参与其他事务。客户机使用已分配设备,中断直接传递给客户机。该实现的细节或性能结果未提供,作者主要关注描述该模型的安全性及其其他优势。
3. x86中断处理
为了将ELI的设计置于具体背景下,我们首先简要概述当前 x86平台上中断处理的工作方式。
3.1. 裸机环境中的中断
x86处理器使用中断和异常来通知系统软件有关传入事件的信息。中断是由外部实体(如I/O设备)产生的异步事件;异常是由正在执行的代码引起的同步事件,例如页面错误。在这两种情况下,当前正在执行的代码都会被中断,并跳转到预指定的中断或异常处理程序。
x86 操作系统使用一个架构规定的内存表——中断描述符表 (Interrupt Descriptor Table,IDT),为每个中断和异常指定处理程序。该表最多包含 256 个表项,每个表项包含一个指向处理程序的指针。每一个架构定义的异常或中断都有一个数字标识符——异常号或中断 向量——用作该表的索引。操作系统可以为所有核心使用一个 IDT,也可以为每个核心设置独立的 IDT。操作系统通过将 IDT 的虚拟内存地址写入中断描述符表寄存器(Interrupt Descriptor Table Register,IDTR)来通知处理器每个核心的 IDT 在内存中的位置。由于 IDTR 保存的是 IDT 的虚拟(而非物理)地址,因此操作系统必须始终确保对应的地址在当前页表集合中保持映射。除了表在内存中的位置外,IDTR 还保存了表的大小。
当外部I/O设备引发中断时,中央处理器会读取当前 IDTR的值以找到中断描述符表。然后,通过使用中断向量作为中断描述符表的索引,中央处理器获取相应处理程序的虚拟地址并调用它。在中断处理程序运行期间,可能会阻塞也可能不会阻塞其他中断。
系统软件需要执行诸如启用和禁用中断、通知中断处理程序完成、配置定时器中断以及发送处理器间中断(IPI) 等操作。软件通过本地高级可编程中断控制器(LAPIC)接口来执行这些操作。LAPIC 拥有多个寄存器,用于配置、传递和通知中断完成。其中对 ELI 尤为重要的是通过写入中断结束(EOI)LAPIC 寄存器来通知中断完成。最新的 LAPIC 接口 x2APIC通过模型特定寄存器(MSRs)暴露其寄存器,这些寄存器通过“读取MSR”和“写入MSR” 指令进行访问。之前的 LAPIC 接口仅在预定义的内存区域中暴露寄存器,该区域通过常规的加载和存储指令进行访问。
3.2. 虚拟环境中的中断
x86硬件虚拟化提供两种操作模式:客户机模式和主机模式。运行在主机模式下的主机使用客户机模式为运行客户虚拟机创建新的上下文。一旦处理器开始运行客户机,执行便持续在客户机模式下进行,直到某些敏感事件触发退出并返回到主机模式。主机处理完必要的事件后,将恢复客户机的执行,从而重新进入客户机模式。这些退出和进入是虚拟化开销的主要原因,在I/O密集型工作负载中尤为明显。这种开销来源于在上下文之间切换所消耗的处理器周期、在主机模式下处理退出所花费的时间以及由此引起的缓存污染。
本研究专注于运行未修改且不可信的操作系统。一方面,未修改的客户机并不知道自己运行在虚拟机中,它们期望像在裸金属上一样完全控制中断描述符表(IDT)。另一方面,主机无法轻易将每个核心的IDT控制权交给不可信且未修改的客户机,因为对物理IDT的完全控制意味着对整个核心的完全控制。因此,x86硬件虚拟化扩展为每种模式使用不同的IDT:每个核心上的客户机模式执行由客户机IDT控制,而主机模式执行由主机IDT控制。当CPU处于主机模式或客户机模式时,I/O设备均可引发物理中断。如果中断发生在 CPU处于客户机模式期间,CPU会强制退出,并通过主机 IDT将中断传递给主机。
客户机接收虚拟中断,这些中断不一定与物理中断相关。当主机收到相应的物理中断时,可能会决定向客户机注入虚拟中断;或者主机也可能决定主动制造一个虚拟中断并注入客户机。主机通过客户机中断描述符表(IDT)注入虚拟中断。当处理器在注入后进入客户机模式时,客户机将接收并处理该虚拟中断。
在中断处理期间,客户机会访问其LAPIC。与中断描述符表类似,对核心的物理LAPIC的完全访问意味着对该核心的完全控制,因此主机无法轻易地让不受信任的客户机访问物理LAPIC。对于使用第一代LAPIC的客户机,当客户机访问LAPIC内存区域时,处理器会强制产生退出。对于使用 x2APIC的客户机,主机根据MSR位图来捕获LAPIC的访问,该位图指定了客户机不能直接访问的敏感MSR寄存器。当客户机访问这些敏感MSR寄存器时,执行将退出到主机。通常情况下,x2APIC寄存器被视为敏感MSR寄存器。
3.3. 来自已分配设备的中断
虚拟化性能的关键在于中央处理器应将其大部分时间花在客户机模式下运行客户机,而不是在主机中处理客户机退出。I/O设备模拟和半虚拟化驱动在客户机中运行的I/O密集型工作负载上会产生显著开销。这种开销源于主机介入其客户机的I/O路径,涉及程序化I/O(PIO)、内存映射I/O (MMIO)、直接内存访问(DMA)以及中断。
MMU,1, 5可以使用IDT影子技术来虚拟化中断传递。如图2 所示并下文所述,该机制无需客户机配合。
)
通过影子化客户机的中断描述符表(IDT),主机能够明确控制中央处理器在中断传递时所调用的中断处理程序。主机可以配置影子IDT,将已分配的中断直接传递给客户机的中断处理程序,或对未分配的中断强制产生退出。引发退出的最简单方法是迫使中央处理器生成异常,因为主机可以选择性地捕获异常,并且只要主机有意错误配置影子IDT,就可轻松生成异常。在我们的实现中,主要通过生成不存在 (NP)异常来强制退出。每个IDT表项都有一个存在位。在调用表项进行中断传递之前,处理器会检查该表项是否存在 (即存在位是否被设置)。传递到不存在表项的中断将引发 NP异常。ELI对影子IDT的配置如下:对于属于分配给客户机的设备的异常和物理中断,影子IDT表项从客户机原始 IDT复制并标记为存在;影子IDT中所有其他表项应由主机处理,因此被标记为不存在,以便当处理器尝试调用处理程序时触发NP异常。此外,主机还配置处理器,一旦发生 NP异常,便强制从客户机模式退出至主机模式。
任何反映给主机的物理中断在主机中都会表现为NP异常,必须将其转换回原始的中断向量。主机检查该异常的原因。如果退出确实是由物理中断引起的,主机将引发一个与该物理中断相同向量的软件中断,使处理器调用相应的中断描述符表(IDT)项。如果退出不是由物理中断引起的,则为真正的客户机不存在异常(true guest NP exception),应由客户机进行处理。此时,主机会将该异常重新注入客户机。在正常执行过程中,真正的NP异常非常罕见。
主机有时还需要将由主机模拟的设备(例如键盘)引发的虚拟中断注入到客户机中。这些中断向量在影子中断描述符表中的表项会被标记为NP。为了通过客户机中断描述符表处理程序传递此类虚拟中断,ELI通过配置进入一种特殊的注入模式处理器以在任何物理中断时引发退出,并使用原始的客户机 IDT运行客户机。然后,ELI将虚拟中断注入客户机进行处理,这与通常的做法类似(图1)。当客户机发出注入的虚拟中断处理完成信号后,ELI通过重新配置处理器,使客户机能够直接处理物理中断,并使用影子IDT恢复客户机运行,从而退出注入模式。正如我们将在第5节中展示的那样,注入的虚拟中断数量比已分配设备产生的物理中断数量少几个数量级。因此,在注入模式下运行时由于物理中断导致的退出次数可以忽略不计。
即使所有中断都需要退出,ELI 也不比基线设备分配慢。退出次数从不增加,且每次退出的开销保持不变。常见的操作系统在系统初始化后很少修改中断描述符表内容。进入和退出注入模式仅需要两次内存写操作,一次用于更改中断描述符表指针,另一次用于更改中央处理器执行模式。
4. ELI:设计与实现
ELI 使未经修改且不受信任的客户机能够直接且安全地处理中断。ELI 不需要对客户机进行任何修改,因此应能与任何操作系统配合使用。它不依赖于任何特定设备的功能,因此应能与任何已分配设备配合使用。
4.1. 无退出中断传递
ELI 的设计基于以下观察:到达特定核心的几乎所有物理中断都是针对在该核心上运行的客户机。这出于几个原因:首先,在高性能部署中,客户机通常拥有自己的物理中央处理器核心(否则将浪费大量时间进行上下文切换);其次,高性能部署使用带有SR-IOV设备的设备分配;第三,中断率通常与执行时间成正比。每个客户机运行的时间越长,从其已分配设备接收到的中断就越多。基于这一观察,ELI 利用现有的硬件支持,将特定核心上的所有物理中断直接递送给在该核心上运行的客户机,因为这些中断大多数本应由该客户机处理,同时强制(未修改的)客户机将那些应由主机处理的中断反射回主机。
客户机操作系统继续准备并维护其自己的中断描述符表。ELI并未 使用该中断描述符表来运行客户机,而是在客户机模式下使用由主机准备的另一个中断描述符表来运行客户机。我们将这个第二个客户机中断描述符表称为影子 IDT。就像影子页表可用于虚拟化客户机MMU,1, 5可以使用IDT影子技术来虚拟化中断传递。如图2 所示并下文所述,该机制无需客户机配合。
通过影子化客户机的中断描述符表(IDT),主机能够明确控制中央处理器在中断传递时所调用的中断处理程序。主机可以配置影子IDT,将已分配的中断直接传递给客户机的中断处理程序,或对未分配的中断强制产生退出。引发退出的最简单方法是迫使中央处理器生成异常,因为主机可以选择性地捕获异常,并且只要主机有意错误配置影子IDT,就可轻松生成异常。在我们的实现中,主要通过生成不存在 (NP)异常来强制退出。每个IDT表项都有一个存在位。在调用表项进行中断传递之前,处理器会检查该表项是否存在 (即存在位是否被设置)。传递到不存在表项的中断将引发 NP异常。ELI对影子IDT的配置如下:对于属于分配给客户机的设备的异常和物理中断,影子IDT表项从客户机原始 IDT复制并标记为存在;影子IDT中所有其他表项应由主机处理,因此被标记为不存在,以便当处理器尝试调用处理程序时触发NP异常。此外,主机还配置处理器,一旦发生 NP异常,便强制从客户机模式退出至主机模式。
任何反映给主机的物理中断在主机中都会表现为NP异常,必须将其转换回原始的中断向量。主机检查该异常的原因。如果退出确实是由物理中断引起的,主机将引发一个与该物理中断相同向量的软件中断,使处理器调用相应的中断描述符表(IDT)项。如果退出不是由物理中断引起的,则为真正的客户机不存在异常(true guest NP exception),应由客户机进行处理。此时,主机会将该异常重新注入客户机。在正常执行过程中,真正的NP异常非常罕见。
主机有时还需要将由主机模拟的设备(例如键盘)引发的虚拟中断注入到客户机中。这些中断向量在影子中断描述符表中的表项会被标记为NP。为了通过客户机中断描述符表处理程序传递此类虚拟中断,ELI通过配置进入一种特殊的注入模式处理器以在任何物理中断时引发退出,并使用原始的客户机 IDT运行客户机。然后,ELI将虚拟中断注入客户机进行处理,这与通常的做法类似(图1)。当客户机发出注入的虚拟中断处理完成信号后,ELI通过重新配置处理器,使客户机能够直接处理物理中断,并使用影子IDT恢复客户机运行,从而退出注入模式。正如我们将在第5节中展示的那样,注入的虚拟中断数量比已分配设备产生的物理中断数量少几个数量级。因此,在注入模式下运行时由于物理中断导致的退出次数可以忽略不计。
即使所有中断都需要退出,ELI 也不比基线设备分配慢。退出次数从不增加,且每次退出的开销保持不变。常见的操作系统在系统初始化后很少修改中断描述符表内容。进入和退出注入模式仅需要两次内存写操作,一次用于更改中断描述符表指针,另一次用于更改中央处理器执行模式。
4.2. 放置影子中断描述符表
在客户机内存中放置影子中断描述符表(IDT)需满足若干要求。首先,它应对客户机隐藏,即放置在客户机通常不会访问的内存区域中。其次,必须将其置于始终映射到客户机内核地址空间的客户机物理页中。这是x86架构的要求,因为IDTR期望的是虚拟地址。第三,由于客户机未经修改且不可信,主机不能依赖客户机的任何协作来放置影子IDT。
ELI通过将影子IDT放置在设备PCI基地址寄存器(BAR)的一个额外页面中,满足了上述所有三个要求。
通过BAR寄存器,PCI设备将其寄存器以内存形式暴露给系统软件。BAR指定了设备寄存器在物理内存中的位置和大小。Linux和Windows驱动程序会将设备的整个PCI BAR映射到内核地址空间中,但在正常操作中仅访问映射的BAR中已知对应于设备寄存器的特定位置。将影子中断描述符表放置在设备BAR末尾附加的一个内存页面中,可使客户机(1)将其映射到自己的地址空间中,(2)保持该映射,以及(3)在正常操作期间不访问它。所有这些操作都是客户机正常运行的一部分,无需客户机感知或配合。为了检测客户机中断描述符表的运行时变化,主机还会对影子中断描述符表页面进行写保护。
4.3. 配置客户机和主机向量
主机和客户机都无法完全控制已分配设备中断的确切触发时间。由于主机和客户机可能在接收中断的核心上不同时运行,因此两者都必须能够处理同一中断。(主机通过将中断注入客户机来处理该中断。)中断向量还相对地控制该中断相对于其他中断的优先级。因此,ELI 确保对于每个设备中断,相应的客户机和主机中断处理程序被分配到同一个向量。
4.4. 无退出中断完成
尽管ELI IDT阴影能够在无需主机干预的情况下将硬件中断传递给客户机,但通知中断完成仍会强制退出到主机模式。这种退出是由客户机发出中断完成信号所引起的。如第3.2 节所述,客户机通过写入EOI LAPIC寄存器来通知中断完成。该寄存器以LAPIC区域的一部分(较旧的LAPIC接口)或x2APIC MSR(新的LAPIC接口)的形式暴露给客户机。
使用旧接口时,每次访问LAPIC都会导致退出;而使用新接口时,主机可以针对每个x2APIC寄存器决定哪些寄存器访问会引起退出。
在ELI之前,主机通过配置中央处理器的MSR位图,使得客户机访问EOI MSR时强制发生退出。ELI通过配置 MSR位图,使得客户机写入x2APIC EOI寄存器时不触发退出,从而将该寄存器直接暴露给客户机。将这种中断完成技术与ELI IDT阴影结合,可消除关键中断处理路径上的退出。
客户机无法区分物理中断和虚拟中断。它们通过写入 EOI寄存器的方式,以相同的方法通知所有中断的完成。当主机注入虚拟中断时,相应的完成操作应发送到主机进行模拟,而不是写入物理EOI寄存器。因此,在注入模式期间(如第4.1节所述),主机会暂时捕获对EOI寄存器的访问。一旦客户机通知所有待处理的虚拟中断已完成,主机便退出注入模式。
4.5. 保护
完整威胁模型的详细信息请参见全文。在此,我们简要描述可能的攻击以及ELI为防止这些攻击所采用的机制。
恶意客户机可能会通过永久禁用中断来窃取中央处理器时间。为了防止此类攻击,ELI 使用 x86 的preemption timer功能,该功能在经过可配置的一段时间后会触发无条件退出。
行为异常的客户机可能拒绝发送中断完成信号,从而屏蔽主机中断。为防止这种情况,当发生退出时,ELI会为任何仍处于服务状态的已分配中断发出中断完成信号。为了保持正确性,当ELI检测到客户机未完成之前已传递的任何中断时,将退回到注入模式,直到客户机发出所有正在服务的中断的完成信号为止。由于所有控制中央处理器可中断性的寄存器在退出时都会被重新加载,因此客户机无法影响主机的可中断性。
恶意客户机可能会尝试阻塞或消耗关键物理中断,例如热中断。为了防范此类攻击,ELI 使用以下机制之一:如果存在一个不运行任何 ELI 启用的客户机的核心,则 ELI 将关键中断重定向到该核心;如果没有可用的此类核心, ELI 将结合使用不可屏蔽中断(NMI)和 IDT 限制。
NMI 会触发无条件退出;客户机无法阻止它们。ELI 将关键中断重定向到核心的单一不可屏蔽中断处理程序。所有关键中断都向此处 理程序注册处理程序,并且每当发生 NMI 时,该处理程序会调用所有已注册的中断向量,以确定发生了哪个关键中断。NMI 共享的运行时开销可以忽略不计(因为关键中断很少发生)。然而,如果在未触发中断的情况下调用了某些设备或设备驱动程序的中断处理程序,可能会导致其死锁或出现其他异常行为。
对于必须仅在实际发生中断时才能调用其处理程序的关键中断,ELI 使用一种互补的粗粒度IDT限制机制。IDT 限制在受 ELI 保护且客户机无法更改的 IDTR 寄存器中指定。IDT 限制会降低影子中断描述符表的限制,导致所有向量号高于该限制的中断触发通常罕见的通用保护异常(GP)。GP 异常会被主机拦截并以类似于 NP 异常的方式进行处理。没有任何事件优先于 IDTR 限制检查,因此可确保所有位于该限制之上的处理程序在被调用时必定陷入主机。
5. 评估
我们在KVM虚拟机监控器中实现了ELI。本节评估了我们实现的性能。
5.1. 方法与实验设置
我们测量并分析了ELI对分配给客户虚拟机的高吞吐量网卡的影响。由于网络设备具有高吞吐量,且SR-IOV网络卡使得将一个物理网络卡分配给多个客户机变得容易,因此网络设备是设备分配最常见的使用场景。我们使用吞吐量和延迟来衡量性能,并对比虚拟化环境与裸金属环境下的结果,以证明前者可以接近后者的性能。如前所述,注重性能的应用通常会为客户机独占分配整个核心。我们的评估仅限于这种情况。
我们的测试机器是一台IBM System x3550 M2服务器,配备Intel Xeon X5570处理器、24GB内存和一块Emulex OneConnect 10Gbps网卡。我们使用另一台类似的远程服务器 (通过10Gbps光纤直连)作为工作负载生成器和I/O事务的目标。客户机模式和裸机配置均使用单个核心;每个系统分配 1GB内存。所有设置均运行Ubuntu 9.10和Linux 2.6.35。
我们在KVM虚拟机监控器(属于Linux 2.6.35)和 QEMU-KVM 0.14.0上运行所有客户机。为了验证ELI在其他配置中的正确性,我们还在使用不同设备(BCM5709 1Gbps网卡)和不同操作系统(Windows 7)的环境中部署了ELI;结果显示ELI确实能够正常工作。我们采用基线设备分配(即未修改的 KVM)、ELI以及无虚拟化的裸机系统进行性能评估和比较。
我们将虚拟机监控器配置为使用2MB大页和二维页表来支持客户机的内存。大页可最小化二维分页开销并减轻 TLB压力。需要注意的是,仅主机使用大页;在所有情况下,客户机仍以默认的4KB页面大小运行。我们量化了未使用大页时的性能,发现大页对基线和ELI运行的性能提升相似 (数据未显示)。
5.2. 吞吐量
I/O虚拟化性能在I/O密集型且产生大量中断的工作负载下表现最差。我们通过测量三个典型的网络密集型工作负载来开始评估,结果表明,在这些基准测试中,ELI相较于基线设备分配实现了显著的吞吐量提升(49–66%),并且几乎达到裸金属性能(差距仅为0–3%)。
我们考虑以下三个基准测试:Netperf TCP流,它向远程机器建立单个TCP连接,并尽可能快速地进行大量指定大小的write()调用;Apache HTTP服务器,使用远程的 ApacheBench进行测量,通过多个并发线程重复请求一个静态页面;以及Memcached,一个高性能的内存键值存储服务器,使用Memslap基准测试进行测量,该测试发送随机序列的get(90%)和set(10%)请求。
我们使用参数配置每个基准测试,以充分负载被测机器的核心(以便可以比较吞吐量),但不会使测试机器饱和。我们将Netperf配置为执行256字节写入,将ApacheBench配置为从4个并发线程请求4KB静态页面,将Memslap配置为从4个线程发起64个并发请求。
图3说明了ELI如何提高这三个基准测试的吞吐量。每个基准测试都在裸金属以及两种虚拟化设置下运行:基线设备分配和使用ELI的设备分配。
该图显示,基线设备分配性能仍显著低于裸机性能:客户机上的 Netperf 吞吐量为裸机吞吐量的 60%,Apache 为 65%, Memcached 为 60%。使用 ELI 后,Netperf 达到裸机吞吐量的 98%,Apache 达到 97%,Memcached 达到 100%。显然,使用 ELI 带来了显著的吞吐量提升,Netperf、Apache 和 Memcached 分别提升了 63%、49% 和 66%。
使用无退出中断实现虚拟机的裸金属性能
5. 评估(续)
5.3. 执行分解
将执行时间分解为主机、客户机和开销部分,有助于我们更好地理解ELI如何以及为何提升客户机的性能。表1显示了 Apache基准测试的分解结果(Netperf和Memcached出现在完整论文中)。此处我们总结三个基准测试的结果。
由于使用 ELI,客户机性能应得到提升,因为客户机获得了更大比例的中央处理器时间(主机占用更少),和/或客户机在运行时执行效率更高。在基线设备分配情况下,仅有 60–69 % 的中央处理器时间在客户机中执行;其余时间均在主机中处理退出。ELI 消除了大部分退出,从而将主机所占用的时间比例降至 1–2%,并将每秒退出次数减少至 764–1118 次。
在基线设备分配中,所有中断都会到达主机,然后被注入到客户机。注入率略高于中断率,因为主机还会注入额外的虚拟中断,例如定时器中断。当使用ELI时,“在主机中处理”的中断数量非常低(103–207),因为中央处理器运行主机的时间比例要低得多。
基线设备分配由于“IRQ窗口”退出而进一步变慢:在裸金属上,当设备中断发生时若中断被屏蔽,则该中断将由 LAPIC硬件在稍后时间进行传递。但当客户机运行时,中断总会立即引发一次退出。主机希望将此中断注入客户机(如果是来自已分配设备的中断),但如果客户机当前屏蔽了中断,则无法完成注入。x86架构的解决方案是,在运行客户机时启用“IRQ窗口”,即一旦客户机开启中断,就请求产生一次退出。在基线设备分配运行中,我们每秒观察到 7801至9069次此类退出。ELI通过消除大多数中断注入,基本消除了IRQ窗口开销。因此,正如预期,ELI大幅减少了退出次数,从基线设备分配运行中的90,506至123,134次,骤降至仅764至1118次。
| 统计信息 | 基线 | ELI | 裸机 |
|---|---|---|---|
| 每秒退出次数 | 90,506 | 1,118 | — |
| 在客户机中的时间 | 67% | 98% | — |
| 每秒中断次数 | 36,418 | 66,546 | 68,851 |
| 在主机中处理 | 36,418 | 195 | — |
| 每秒注入次数 | 36,671 | 458 | — |
| 每秒中断请求窗口次数 | 7,801 | 192 | — |
| 每秒请求数 | 7,729 | 11,480 | 11,875 |
| 平均响应时间(毫秒) | 0.518 | 0.348 | 0.337 |
表1. Apache基准测试执行分解。
5.4. 中断率的影响
上一节的基准测试表明,对于 I/O密集型工作负载,ELI 相比基线设备分配显著提高了吞吐量。但当工作负载花费在 I/O 上的时间减少而计算时间增加时,ELI 的改进效果似乎会减弱。然而,与直觉相反的是,我们将证明,直到达到相当高的每I/O计算量比率(以及相当低的吞吐量)之前, ELI 仍能持续提供相对较大的改进。为此,我们修改了 Netperf 基准测试,使其在向流中写入每个字节时执行指定数量的额外计算。这类似于许多有用的服务器工作负载,服务器在发送响应之前需要进行一定的计算。
衡量计算与I/O比率的一个有用指标是每字节周期数,即生成一个字节输出所消耗的中央处理器周期数;该比率可简单地通过CPU频率(周期/秒)除以工作负载吞吐量(字节/秒)得到。注意,每字节周期数与吞吐量成反比。图4展示了ELI的改进以及中断率随该比率变化的情况。如图所示,在达到60 每字节周期数之前——对应吞吐量仅为50Mbps—— ELI的改进始终超过25%,且中断率保持在3万至6万之间。
下文将说明,由于网卡(进行中断合并)和Linux驱动程序 (采用NAPI)的作用,中断率被维持在此范围内,否则中断率会更高。由于ELI降低了处理中断的开销,其带来的收益与中断率成正比,而非与吞吐量成正比,这一事实解释了为何在不同的计算‐I/O值范围内,改进程度相近。
我们现在开始研究 ELI 的改进程度对网卡合并量的依赖性,这直接关系到生成的中断数量。我们的网卡对合并设置了可配置的上限,允许用户设置一个时间间隔 T,使得网卡每 T μs 内不会产生超过一次中断(T 越长,意味着中断越少)。我们将网卡的合并上限设置为以下值:16 μs、24 μs、32 μs、⋯⋯、96 μs。图5绘制了相关实验的结果(曲线上的数据点表示 T 的取值)。更高的中断率意味着由于ELI而带来更高的节省。即使在最大合并情况下, ELI相比基线仍能提供10%的性能改进。在本节所述的所有实验中,ELI达到了至少99%的裸机吞吐量。这些结果表明,当使用ELI时,合并对吞吐量的影响较小。因此,可以采用更细粒度的合并,以避免粗粒度合并带来的延迟增加。
图4。具有不同计算‐输入输出比的修改版Netperf工作负载的吞吐量改进 和基线中断率。
图5. Netperf基准测试在不同中断合并间隔(标签中显示)下的吞吐量 改进和中断率
5.5. 延迟
通过消除由外部中断引起的退出,ELI显著减少了将中断传递给客户机所需的时间。这段时间对于延迟敏感型工作负载至关重要。我们使用Netperf UDP请求‐响应来测量ELI的延迟改进,该测试发送一个UDP数据包并在发送下一个数据包之前等待回复。为了模拟一个在运行延迟敏感型应用的同时还有其他任务要处理的繁忙客户机,我们在客户机内运行一个忙循环。如表2所示,基线设备分配使裸金属延迟增加了 8.21微秒,而ELI将这一差距缩小至仅0.58微秒,达到裸机延迟的98%水平。
| 配置 | 延迟(毫秒) | %开销 |
|---|---|---|
| 基线 | 36.14 | 29 |
| ELI | 28.51 | 2 |
| 裸机 | 27.93 | 0 |
表2. Netperf UDP请求‐响应基准测试测得的延迟。
6. 后续影响
在我们最初的 ASPLOS 2012 论文中,我们敦促硬件厂商增加硬件支持,以简化向客户虚拟机直接进行中断传递的实现。我们提出,ELI 所展示的显著性能改进值得投入精力来增加此类支持。值得高兴的是,自那以后,这方面已经迈出了一些积极的步伐。
为了减轻中断传递带来的一些开销,虚拟机监控器现在可以使用英特尔的“虚拟APIC”(APICv)功能。假设客户机当前在核心 C1 上运行。虚拟机监控器可以安排相关(物理)中断在另一个处于主机模式的核心 C2 上触发。当中断到达 C2, 时,APICv 允许虚拟机监控器将相应的(虚拟)中断“转发”到 C1 的客户机,而无需引发退出。尽管这种方案消除了客户机不必要的退出,但由于以下两个原因,其性能仍不如 ELI。首先,它需要专门分配特定的主机核心(如 C2)用于重定向客户机中断。其次,它增加了中断传递延迟,因为中断必须先由 C2 上的虚拟机监控器处理,然后才能传递给 C1 上的客户机。
英特尔和AMD均表示,他们计划在硬件中支持直接的类似ELI的传递。一些ARM芯片已经支持此类传递。然而,目前尚不清楚这种硬件支持是否能够兑现其承诺。例如,英特尔第一代实现方案会将每个客户机中断传递到某个特定核心,因此,对于操作系统将中断分布在多个客户机核心上的多核客户机而言,该实现可能无法使用。
7. 结论
实现高虚拟化性能的关键在于,中央处理器应将大部分时间花在客户机模式中运行客户机,而不是在主机中处理客户机退出。然而,当前的x86虚拟化方法由于在关键中断处理路径中需要主机参与,导致了多次退出。其结果是I/O性能下降。我们提出通过引入ELI来消除不必要的退出,该方法允许客户机直接且安全地处理中断。在以往诸多降低虚拟化开销的研究基础上,ELI最终使得不可信且未经修改的虚拟机即使在最高I/O密集型工作负载下也能达到接近裸机性能的水平。考虑到这一点,芯片厂商下一步的合理举措似乎是扩展投递中断架构,以在硬件层面支持ELI范式,从而简化其实现。
3431

被折叠的 条评论
为什么被折叠?



