前言:X86架构有哪些中断虚拟化技术

X86架构对于中断虚拟化的技术一直在演进和优化。自英特尔硬件辅助的虚拟化技术出现以来,出现了VT-x, VT FlexPriority, APICv, Post Interrupt等等中断相关的虚拟化技术,这些技术如何理解?它们之间有何区别?本文希望通过示意图方式对它们进行解析。本文旨在针对中断虚拟化相关技术进行对比阐述,不对虚拟化的其他方面作过多阐述。

缘起:硬件辅助的虚拟化

x86平台虚拟化技术组成部分 x86架构cpu虚拟化的实现技术_架构


由于x86 ISA中有十多条敏感指令不是特权指令, 因此x86无法使用经典的虚拟化技术1完全虚拟化。所以在硬件辅助的虚拟化出现以前,对于X86架构的虚拟化主要有两种方式:

  1. Full virtualization派:VMware为代表的BT,其主要思想是在执行时将, VM上执行的Guest OS之指令, 翻译成x86 ISA的一个子集, 其中的敏感指令被替换成陷入指令,翻译过程与执行交替执行,不含敏感指令的用户态程序可以不经翻译直接执行。这种技术虽能够以纯软件的方式完成虚拟化并解决了许多问题,但同时也带来了极大的设计复杂性和性能下降。
  2. Paravirtualizaiton派:以Xen为代表,其基本思想是通过修改Guest OS的代码, 将含有敏感指令的操作, 替换为对VMM的超调用(Hypercall), 可将控制权转移给VMM. 该技术的优势在于性能能接近于物理机. 缺点在于需要修改Guest OS。

英特尔推出硬件辅助的虚拟化技术解决了上述两种软件虚拟化方式带来的问题:通过引入一组VMX指令,使得CPU能够运行在两种模式下VMX root modeVMX Non-root mode。在两种模式下,CPU都可以有Ring0~Ring3 4个特权级。Guest OS运行在VMX Non-root Mode下的Ring 0,Guest的应用程序运行在VMX Non-root Mode的Ring3;Host OS及VMM运行于VMX Root Mode的Ring0。在进入虚拟机之前,Hypervisor预先设置好Guest可以操作的资源,如果Guest里面访问了受限的资源或者发生了异常等,就会被Trap到VMM里面,即发生VM-Exit,由VMM代为访问或处理,然后再返回到虚拟机里面,即VM Entry。在Guest和VMM相互切换时,需要有个地方存放各自之前的状态。VMCS里面正是存放VCPU和PCPU的上下文信息的一个内存块,其由VMX指令负责操作,每个VCPU都会拥有一个对应的VMCS。

通过硬件辅助的虚拟化,简化了VMM的设计,并且不需要修改Guest OS,能够做到完全的虚拟化。但硬件辅助的虚拟化只是一个开始,并不是结束,其自身也经历着不断的演进。自从Intel引入VT硬件辅助虚拟化后,虚拟化的大部分损耗都存在于VM enter/exit上,这跟进程/线程切换的道理相一致。通常地,每一次VM exit都会带来2000~7000个时钟周期的消耗,如果存在大量的虚拟机,每个虚拟机又存在着大量的VM exit,那么虚拟化主机的效率将会大为降低。提升虚拟化IO效率的目标就在于降低VM exit。

中断虚拟化

x86平台虚拟化技术组成部分 x86架构cpu虚拟化的实现技术_架构_02


中断可以分为两种类型:

  1. 外部中断,即设备IO引入的中断。
  2. 内部中断,即核间通信以及时钟等引入的中断。

Guest里的操作系统和Host上一模一样,Host操作系统是怎么读写中断控制器的,那么Guest就是怎么读写中断控制器的,由于Guest并没有真正的硬件单元,Guest对中断控制器的读写只能由VMM拦截住做特殊处理。Guest读写中断控制器时VMM进行拦截,Guest要exit出来,中断控制器逻辑复杂,寄存器众多,Guest要经常exit出来,性能影响很大。

具体说来,就是LAPIC有很多寄存器, 通常OS以MMIO方式访问. 其中, OS使用TPR(Task Priority Register)来屏蔽中断优先级小于或等于TPR的外部中断. 通过虚拟化Guest的MMU, 当Guest试图访问LAPIC(!!!MMIO!!!)时, 发生一个缺页异常类型的VM-Exit, 从而被VMM拦截到。VMM经过分析, 知道Guest正试图访问LAPIC后, 会模拟Guest对LAPIC的访问。对于Guest的每个虚拟CPU, VMM都会分配一个vLocal APIC结构与之对应!!!Guest的MMIO操作不会真正影响物理的LAPIC, 而只反映到相应的vLocal APIC结构!!!里面。VMM这种模拟开销很大, 如果客户机的每个LAPIC访问都导致一次缺页异常类型的VM-Exit并由VMM模拟, 会严重影响客户机的性能。

中断虚拟化的主要任务就是实现该图描述的虚拟中断架构。包括虚拟中断控制器vLocal APICvIOAPIC的创建,中断采集中断注入

对于外部中断来说,中断的采集是指将Guest VM的设备中断请求送入对应的虚拟中断控制器中。Guest VM的中断有两种可能的来源:

  1. 来自于软件模拟的虚拟设备,比如一个模拟出来的串口,可以产生一个虚拟中断。从VMM的角度来看,虚拟设备只是一个软件模块,可以通过调用虚拟中断控制器提供的接口函数,实现虚拟设备的中断发送。
  2. 来自于直接分配给guest VM的物理设备的中断,比如一个物理网卡,可以产生一个真正的物理中断。一个物理设备被直接分配给一个guest VM,意味着当该设备发生中断时,中断的处理函数(ISR)应该位于guest OS中。

中断注入是指 虚拟中断控制器采集到的中断请求,将按照VMM排定的优先级,被逐一注入到对应的虚拟CPU中。只有在VM entry,也就是某个虚拟CPU被调度到重新获得物理CPU的使用权时,VMM才可以将中断注入到该虚拟CPU中。为了保证中断的及时注入,就需要通过一定的手段,强制虚拟CPU发生VM exit,然后在VM entry返回guest VM的时候注入中断。强制产生VM exit最常用的办法就是往虚拟CPU对应的物理CPU发送一个IPI核间中断。这个IPI就像一把手枪,把guest VM打下来,落回到VMM中。

为什么中断注入一定要让CPU退出,这是因为intel硬件设计使然。只有在VM-Entry这个时间硬件才检查VMCS域中的VM-entry interruption-information的字段,所以要让虚机先退出,然后复现一个VM-Entry的时机。

VT FlexPriority

x86平台虚拟化技术组成部分 x86架构cpu虚拟化的实现技术_x86平台虚拟化技术组成部分_03


在2008年,Intel在Xeon 7400上引入的Intel VT FlexPriority技术主要是对外部中断虚拟化的一个改进。

处理器本地高级可编程中断处理器(Local APIC)上有一个操作系统可修改的任务优先级寄存器(Task-Priority Register),IO-APIC 将外部中断转发到 TPR 值最低的处理器上(期望该处理器正在执行低优先级的线程),从而优化中断的处理。TPR 是一个特权寄存器,某些操作系统会频繁设置(Linux Kernel 只在初始化阶段为每个处理器的 TPR 设置相同的值)。

当处理器执行任务时,往往会收到需要注意的其它设备或应用发出的请求或 “ 中断 ” 命令。为了最大程度减少对性能的影响,处理器内的一个专用寄存器将对任务优先级进行监控。如此一来,只有优先级高于当前运行任务的中断才会被及时关注。英特尔VT FlexPriority 可创建 TPR6 的一个虚拟副本,该虚拟副本可读取,在某些情况下,如在无需干预时,还可由客户操作系统进行更改。上述举措可以使频繁使用 TPR 的 32 位操作系统获得显著的性能提升。

通过 CR8 访问 Task Priority Register(TPR)的时候,使用 VMCS 中的影子 TPR,可以避免触发 VM exit。同时执行控制区还有一个 TPR 阈值的设置,只有当 Guest OS 设置的 TR 值小于该阈值时,才触发 VM exit。

此时,virtual-APIC page 中仅实现了 VTPR 一个寄存器,所以是对外部中断虚拟化的部分优化。

APICv

x86平台虚拟化技术组成部分 x86架构cpu虚拟化的实现技术_服务器_04


APICv优化针对的是内部中断:虚拟机处理IO中断的时候需要不断访问APCI寄存器,每一次访问均需要VMM(或者Hypervisor,虚拟机管理员)进行指令截取、解码,因而导致VM exit。如果存在大量的虚拟机,每个虚拟机又存在着大量的VM exit,那么虚拟化主机的效率将会大为降低。

为了让Guest对其(虚拟)APIC的访问不必引起VM Exit,引入了Virtual-APIC Page的概念。它相当于是一个Shadow APIC,Guest对其APIC的部分甚至全部访问都可以被硬件翻译成对Virtual-APIC Page的访问,这样就不必频繁引起VM Exit了。Virtual-APIC Page受VMCS管理,这是一个可以由虚拟机直接访问的内存页面。

通过APICv,原有的寄存器访问便可以在虚拟机内部完成,不再需要VM exit,因而提升了性能。

Post Interrupt

x86平台虚拟化技术组成部分 x86架构cpu虚拟化的实现技术_运维_05


Post Interrupt就是进行直接中断传递,把外设产生的中断直接给了guest虚拟CPU,

是更完全彻底的外部中断优化。它提供了另外一种中断检查机制,通过VMCS域中的 Posted-Interrupt Descriptor字段就可以让Guest态的CPU感知到中断,并且这个字段Host可以直接写,不需要在VM-Entry时通过写VMCS间接设置。

基于软件的虚拟中断芯片方案,向 Guest注入中断的时机都是在 VM Entry 之前,没有硬件层面的中断评估等逻辑支持时,中断注入必须发生在 VM Entry 之前,只有在 VM Entry 时,Guest CPU 才会评估是否有中断需要处理。当 VM Entry 时 Guest关中断或正在执行不能被中断的指令,就可能导致中断延时较大。这就需要一旦 Guest开中断或没有执行不能被中断的指令,CPU 就立刻从 Guest陷入到 host 模式,就能在下一次 VM Entry 时注入中断。因此要注入中断就需要触发一次 VM Exit,这就是中断虚拟化的主要开销。

Guest支持中断评估后,Guest模式的 CPU 不需要在 VM Entry 时才能进行中断评估,Guest模式的 CPU 一旦识别出中断,即可在 Guest模式自动完成中断注入,无需触发 VM Exit。

当虚拟中断芯片需要注入中断时,将中断信息更新到 posted-interrupt descriptor,然后向 CPU 发送一个通知 posted-interrupt notification(IPI),Guest CPU 收到中断后,直接在 Guest 模式响应中断。

posted-interrupt descriptor 长度为 64 bytes,低 256 位每一位对应一个中断向量,置位表示有中断请求,bit-256 指示是否有中断需要通知。其地址记录在 VMCS posted-interrupt descriptor address 字段,同时 posted-interrupt notification 的中断向量也记录在 VMCS 中。

posted-interrupt processing 机制的核心就是完成两件事:一是向 posted-interrupt descriptor 写入中断信息;二是通知 CPU 处理中断。设置完描述符后,如果 CPU 在 Guest 模式,就发送专用的 posted-interrupt 中断向量,而如果 CPU 不在 Guest 模式,就发送一个重新调度的 IPI,在 VM Entry 后马上处理中断。

VT-d是X86架构里面可以对中断进行重新映射或者直接投递中断的一个硬件,它根据中断映射表将外部中断投递到目标Guest。VMM拥有一个Interrupt Remap Table Address Register指向中断映射表的入口,每passthrough一个设备,VMM就在这个表中分配一个entry,打上相应的属性,由于外设已经pasthrough给Guest了,Guest里驱动写外设的PCI config space,此时VMM要拦截对PCI config space的写,配置外设MSI产生的中断是remappable格式。Guest里的driver给这个passthroug的device分配了一个vector X,然后在IDT中添加了vector X的处理函数。由于device的interrupt是external interrupt,不能直接给了Guest,host也给device分配一个vector Y,host接收到了interrupt Y转换成interrupt X,再投递给Guest,Guest用自己的函数处理,投递时用post interrupt就不会导致Guest exit出来。

参考链接

  1. Intel-VT 技术详解: https://www.pudn.com/news/628f844ebf399b7f351eeecf.html
  2. 云计算与虚拟化技术发展编年史: https://mp.weixin.qq.com/s/_oHMtA2184h92YA3hjEyuw
  3. Interrupt Virtualization: https://notes.caijiqhx.top/ucas/virtualization/interrupt_virtualization/
  4. x86中断和中断虚拟化: https://zhuanlan.zhihu.com/p/349894852
  5. CPU虚拟化的实现: https://www.codenong.com/cs106477267/
  6. Intel SDM Chapter 29: APIC Virtualizaton & Virtual Interrupts: https://tcbbd.moe/ref-and-spec/intel-sdm/sdm-vmx-ch29/

注脚


  1. 经典的虚拟化方法:“特权解除”(Privilege deprivileging)"和"陷入-模拟(Trap-and-Emulation)。将Guest OS运行在非特权级(特权解除), 而将VMM运行于最高特权级(完全控制系统资源). 解除了Guest OS的特权后, Guest OS的大部分指令仍可在硬件上直接运行. 只有当运行到特权指令时, 才会陷入到VMM模拟执行(陷入-模拟)。由此可引入虚拟化对体系结构(ISA)的要求:须支持多个特权级;非敏感指令的执行结果不依赖于CPU的特权级;CPU需要支持一种保护机制, 如MMU, 可将物理系统和其他VM与当前活动的VM隔离;敏感指令需皆为特权指令,x86 ISA中有十多条敏感指令不是特权指令, 因此x86无法使用经典的虚拟化技术完全虚拟化。 ↩︎