嵌入式开发论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 79|回复: 3

The Design and Implementation of the Linux ARM Hypervisor

[复制链接]

59

主题

64

帖子

255

积分

版主

Rank: 7Rank: 7Rank: 7

积分
255
发表于 2018-12-24 11:37:28 | 显示全部楼层 |阅读模式
摘要
       ARM架构的CPU在移动设备和服务器中使用得越来越普遍,为基于ARM架构的设备提供虚拟化的新需求也越来越大。我们介绍了构建Linux ARM hypervisor KVM/ARM的经验,这是第一个完整的ARM虚拟化系统解决方案,可以在ARM多核硬件上运行未经修改的guest操作系统。KVM/ARM引入了分裂模式虚拟化,运行hypervisor跨CPU模式分割执行,并将其集成到Linux内核中。这样运行KVM/ARM利用现有的Linux硬件支持和功能来简化虚拟化的开发和维护,同时利用ARM硬件虚拟化的扩展来运行性能与本机执行效率相当的虚拟机。KVM/ARM已经成功合并到Linux内核中,确保该方案得到广泛的使用。我们对在一个真实ARM硬件虚拟化支持的完整hypervisor的性能进行了测量,结果表明,与X86的多核硬件Linux虚拟化相比,KVM/ARM在性能和功耗方面都具有优势。

1. 介绍
      基于ARM架构的设备在智能手机、上网本和嵌入式计算机领域都出现了巨大的增长。虽然ARM的CPU在这些领域的发展受益于其在功耗方面的优势,但ARM的CPU在性能方面也在不断提高。这就促进了新的基于ARM架构的微服务器芯片和CPU进入到传统的服务器、PC和网络系统中。
      为了给基于ARM的设备提供更好的虚拟化支持,ARM架构提供了对虚拟化的硬件支持。尽管虚拟化通常用于X86的系统,但是ARM和X86虚拟化之间是由一些关键的区别的。第一,ARM和X86虚拟化扩展有重要的区别,比如X86 hypervisor的设计不能直接适用于ARM,这些差异会影响hypervisor的性能和设计,特别是对于多核系统,但是这点尚未在真实的硬件设备上进行评估。第二,与基于X86的系统不同,ARM并没有PC硬件标准。ARM市场的碎片化很严重,有许多不同的垂直集成的ARM平台,他都是非标准的硬件。在没有任何实际硬件标准的情况下,以一种能够跨ARM硬件多样性工作的方式虚拟化ARM是一个关键的挑战。
      我们介绍了在主线Linux内核中构建ARM hypervisor (KVM/ARM[13,23])的经验。KVM/ARM是第一个利用ARM硬件虚拟化运行未经修改的guest操作系统的hypervisor。我们的工作主要由四个贡献。第一,我们引入了分裂模式虚拟化(split-mode virtualization),这是一种新的hypervisor的设计,它使系统能跨不同的特权CPU模式运行,从而利用每个CPU模式提供的特定优点和功能。这种方法为ARM虚拟化上下文提供了关键的益处。ARM为运行虚拟机监控程序引入了一种新的CPU模式,称为HYP模式,但是HYP模式具有与现有内核模式不同的独立特性。HYP模式的目标是在操作系统内核下运行一个独立的hypervisor程序,在hypervisor被集成到主内核的情况下,该种设计不能很好的与主hypervisor一起工作。例如,其需要对Linux的标准操作系统机制进行重大的重新设计,以便其在HYP模式下运行。split-mode的虚拟化方式可利用托管hypervisor设计的优点,即在正常的特权CPU模式下运行未经修改的操作系统机制,同时十四用HYP模式来运行ARM硬件虚拟化的特性程序。第二,我们从头开始设计并实现KVM/ARM机制,它是一个开源项目,易于维护并集成到Linux内核中,这一点对于ARM系统尤为重要,因为其缺乏集成硬件组件的标准方法、硬件探测的特性(如标准BIOS或PCI总线),以及安装低级软件的标准机制。将hypervisor移植到一个独立的硬件平台,并对其进行可开发和维护将是一个巨大的挑战。但是,几乎所有ARM平台都支持Linux,通过将KVM/ARM与Linux内核进行集成,KVM/ARM可自动地跟随LInux内核版本进行更新。通过使用分列式的虚拟化方案,我们可以利用LInux中现有的基于内核的虚拟机(KVM)hypervisor接口,并可重用大量现有内核代码和接口,以降低该方案的实现复杂度。KVM/ARM需要向Linux内核添加不到6000行的ARM代码,这比hypervisor的代码量要小得多。KVM/ARM作为Linux 3.9内核的主要ARM hypervisor,这确保了其能被广泛使用,因为Linux在ARM平台上占据了主导地位。基于我们的开源经验,我们提供了一些有用的提水,可将研究思想转换为开源社区可能采用的实现。第三,我们证明了KVM/ARM在实际多核ARM硬件上的有效性,我们的结果是使用在真实硬件ARM虚拟化支持的hypervisor进行测量的。我们将其与标准的KVM x86架构的hypervisor进行对比,并评估虚拟机中运行的应用程序的工作负载与本机非虚拟化执行的性能开销,结果表明,在大多的情况下,KVM/ARM实现了类似的性能开销,并且显著降低了Apache和MySQL两个重要应用程序的性能开销。这些结果首次比较了ARM和X86在实际硬件上的虚拟化扩展,定量地展示了不同的设计选择如何影响虚拟化的性能。测量结果表明KVM/ARM也KVM/x86具有更低的功耗。最后,我们根据构建和评估一个完整的ARM hypervisor的经验,就虚拟化的未来硬件支持提出了一些建议。我们确认了一些特性,其将对降低hypervisor实现的软件复杂性至关重要,并讨论了一些有益的机制,使其能最大化hypervisor性能,特别是在多核系统的上下文方面。
       本文将介绍KVM/ARM的设计和实现。第二章概述了ARM虚拟化的扩展,并与X86进行比较。第三章将描述KVM/ARM hypervisor的设计。第四章将讨论KVM/ARM的实现以及将其发布到Linux社区并将其合入到LInux主分支的经验。第五章给出了KVM/ARM定量的性能和能耗测试结果,以及真实ARM硬件和X86硬件虚拟化的定量比较。第六章给出了对未来赢家虚拟化支持的建议。第七章讨论了相关工作,最后给出结束语。

2. ARM虚拟化扩展
    因为ARM架构不是一个可虚拟化的经典架构,ARM在最新的ARMv7和ARMv8体系架构中引入了可支持虚拟化的扩展选项。例如,Cortex-A15就是支持硬件虚拟化的ARMv7的CPU。我们将简要介绍ARM虚拟化扩展。
    CPU虚拟化:图一展示了在ARMv7架构中CPU的各种模式,包括TrustZone和一个新CPU模式,叫作HYP模式。TrustZone将模式分为两种状态,安全态和非安全态。一个特殊的monitor模式提供了两种状态之间的切换

尽管ARM架构的CPU总是在安全态启动,但是ARM的bootloader程序通常会在早期就会将CPU切换到非安全态。安全态只用于特定的用例,例如数字版权管理。通过在安全态下运行虚拟机监控程序TrustZone可能对虚拟化很有用,但这样并不合适,因为其不支持trap和模拟。并不存在将非安全态中执行的操作trap到安全安全态的方法。而在非安全态下则可以自由配置,例如虚拟内存。热河运行在最高非安全态的软件都可以访问所有非安全物理内存,从而不可能隔离在飞蛾诶安全环境中运行的多个虚拟机。HYP模式被作为trap和模拟需求被引入,用于在非安全态下支持虚拟化。与其他CPU模式、用户模式和内核模式相比,HYP模式具有更高的特权。在HYP模式下运行的软件可通过配置,使系统能够在触发了敏感指令或硬件中断的情况下从内核模式陷入到HYP模式。为了运行虚拟机,hypervisor程序必须至少有一部分时常驻在HYP模式中。虚拟机将以用户和内核模式正常运行,直到其达到陷入HYP模式的条件为止。此时,CPU将陷入HYP模式,将控制权限交给hypervisor,其可管理硬件资源,并为所有的虚拟机提供所需的隔离。一旦该触发条件被hypervisor处理完毕,CPU将会切换回用户态或内核态,这样虚拟机就可以继续执行。
      ARM架构运行每个trap被配额制成直接切换到虚拟机的内核模式,而不是通过HYP模式来完成。例如:系统调用或用户模式的页错误引起的trap可被配置成直接陷入到虚拟机的内核模式,以便它们由guest操作系统处理,而无需hypervisor的参与。这也就避免了每次系统调用或者页错误时进入到HYP模式,减少虚拟化的开销。此外,所有进入HYP模式的trap都可以被禁用,一个非虚拟化的内核可在内核模式下运行,并完全控制系统。
     ARM围绕HYP模式对虚拟化的支持进行了设计,HYP模式不同于现有的内核模式,因为其是设想在一个更复杂的rich OS下有一个独立的管理程序。他们希望简化hypervisor开发人员实现hypervisor的过程,因此与内核模式相比,HYP模式将具有更少的控制寄存器。类似地,其要求在页表条目中设置某些位,因为其不会与运行在用户模式的软件共享页表,例如LInux内核在啊内核模式中所做的事情。

      内存虚拟化:ARM也提供了对虚拟物理内存的硬件支持。当运行一个虚拟机时,虚拟机管理的物理地址实际是中间物理地址(IPA),也被称作客户物理地址。其需要被转换成实际物理地址,即主机物理地址。类似于X86中的嵌套页表,ARM提供了第二组页表,即二级页表,其分别根据guest和host物理地址将IPA转换成PA。二级转换可以在HYP模式中被禁止或使能。二级页表使用ARM的新的LPAE页表格式,与内核模式使用的页表格式略有不同。
     中断虚拟化:ARM定义了通用终端控制器(GIC)的架构。GIC将中断从设备转发到CPU上,CPU通过查询GIC来探测该中断的来源。GIC在多核配置中尤为重要,因为它用于生成处理器间中断(IPI)从一个CPU核到另一个CPU核。GIC分为分发服务器和CPU接口两部分。系统中只有一个分发服务器,但是每个CPU核都有一个CPU接口。CPU接口和分发服务器都是通过内存映射接口(MMIO)来进行访问的。分发服务器被用来配置GIC,例如,配置中断的CPU核与一个中断进行关联,禁止或者使能系统中的所有中断,发送IPI到另外一个CPU核。CPU接口用于确认(ACK)和发出中断接口信号(EOI)。例如当一个CPU核接收到一个中断时,它将读取GIC的CPU接口上的一个特殊寄存器,该寄存器将标记中断,并返回中断数量。在CPU将从ACK寄存器中读取到的值写入CPU接口的EOI寄存器之前,中断不会再次被转发到CPU中。
     中断可被配置为使CPU核陷入HYP或者内核模式。将所有中断捕获到内核模式中,并让其被在内核模式下运行的操作系统软件直接处理是很高效的,但是其不能再虚拟机上下文中工作,因为hypervisor将事情对硬件的控制。将所有中断捕获到HYP模式可确保hypervisor保留控制权,但是需要在软件中模拟虚拟中断,以便向虚拟机发出事件信号。这种方式就很麻烦且开销很大,因为处理中断的每一步和虚拟中断的处理,例如ACK和EOI操作都必须通过hypervisor来完成。
      GIC v2.0以虚拟化GCI(VGIC)的形式提供了中断硬件虚拟化的支持,因为不需要在hypervisor软件中进行模拟来接收虚拟中断。VGIC为每个CPU引入了VGIC CPU接口,并为每个CPU引入了相应的hypervisor控制接口。虚拟机被配置成查看VGIC CPU接口而再是GIC CPU接口。虚拟中断是通过向VGIC hypervisor控制接口中写入特殊寄存器(list registers)而生成的,VGIC CPU接口将虚拟中断直接引入到虚拟机的内核态中。因为VGIC CPU接口包含对ACK和EOI的支持,所以这些操作不再需要陷入到hypervisor来被模拟,从而减少了在CPU上接收中断的开销。例如,模拟虚拟设备通常通过软阿健API将虚拟中断引入到hypervisor,后者可以利用虚拟中断VGIC将模拟设备的虚拟中断号写入到列表寄存器(list register)中。这样可以让VGIC直接中断虚拟机的内核模式,并在不需要陷入HYP模式的情况下让guest操作系统ACK和EOI虚拟中断。注意,分发服务器仍然必须在软件中进行模拟,虚拟机对分发服务器的所有访问仍然必须捕获到hypervisor中。例如,当一个虚拟CPU发送了一个虚拟IPI到另外一个虚拟CPU,这就会导致陷入hypervisor,其将模拟软件中的分发服务器访问并对接收到的CPU的GIC hypervisor控制接口上的列表寄存器(list register)进行编程。
    时钟虚拟化:ARM定义了通用计时器框架,其中包括对计时器虚拟化的支持。通用定时器提供了一个计数器,其可以用来测量系统运行的时间,定时器还可被编程,是其在到达一定的时间后会想CPU发出一个时钟中断。计时器可被hypervisor和guest操作系统使用,但是为了对计时器提供隔离并保留控制权,hypervisor使用的计时器不能直接被guest操作系统配置和操作。如果guest操作系统需要访问此类计时器,则需要让CPU陷入到HYP模式来实现,对于某些工作负载相对频繁的操作则会产生额外的开销。hypervisor也可能希望虚拟化虚拟机的时钟,因为让虚拟机直接访问计时器硬件将是是问题。
     ARM通过引入一个新的计数器来实现虚拟化支持。可以将hypervisor配置为使用物理计时器,而将虚拟机配置成使用虚拟计时器。这样就可以使虚拟机在不陷入HYP模式的情况下访问、编程和取消虚拟计时器。从内核态访问物理计时器和计数器是由HYP模式控制的,但是在内核模式下运行的软件总是可以访问虚拟计时器和虚拟计数器。
     与X86的比较:Intel和AMD对x86的虚拟化扩展与ARM对虚拟化的扩展以及硬件虚拟化的支持具有许多相似和不同之处。Intel和AMD的扩展是非常相似的,所以我们仅限比较ARM和Intel.。ARM通过一个独立的CPU模式(HYP模式)来实现对虚拟化的支持。其具有比用户模式和内核模式更高的权限。相比之下,Intel使用root和non-root模式来实现虚拟化。在ARM架构中,通过敏感指令和特殊操作让CPU核陷入HYP模式。这两种硬件架构设计的一个关键区别点在于,Intel的root模式支持与non-root模式相同的全部用户和内核模式的功能。而ARM的HYP模式则是一种完全不同的CPU模式,却具有自己的特性。
     ARM和Intel都会陷入他们各自的HYP模式和root模式,但是Intel为虚拟控制模块提供了特定的硬件支持,当只使用一条指令切入或者切出root模式时,虚拟机控制块会自动保存和恢复。当guest和hypervisor之间进行切换时,该操作用于自动保存和恢复guesst状态。相比之下,ARM并不提供这样的硬件支持,任何状态的保存和恢复都需要在软件中显式地完成。这为从HYP模式中切入或者切出时保存和恢复状态提供了灵活性。例如,如果没有需要保存的额外状态,ARM陷入到HYP模式可能比Intel切换到root模式更快。
     ARM和Intel在支持物理内存虚拟化方面非常类似。两者都引入了一组附加的页表,用于将IPA转换成主机物理地址。ARM受益于后见之明,在最初就将二级地址转换包含在内,而这一点在Intel第二代虚拟化硬件技术才实现。
    ARM对虚拟计时器的支持并不是x86实现的翻版,直到最近引入了Intel的虚拟APIC的支持。ARM对虚拟中断的支持也并不是X86实现的翻版。在没有虚拟APIC支持的情况下,x86的虚拟机中的EOI终端需要在root模式下被捕获,而ARM对中断的虚拟化则避免了需要将CPU陷入HYP模式来完成,这一点将减少中断的开销。在x86上执行类似于计时器功能会使其陷入root模式,而在ARM中则无需陷入HYP模式。然而,在x86架构中,读取计数器并不是一个特权操作,即使计数器并不支持虚拟化,该操作也不会让CPU陷入到root模式。

3. Hypervisor框架
     KVM/ARM是利用LInux内核中现有的基础来实现的,如果重新设计和实现hypervisor复杂的核心功能,这将可能引入一些知名的bug。虽然单独的hypervisor设计方式具有更好的性能和更小的TCB,但是该种方式并不适合ARM架构。ARM硬件在很多方面都比x86更加多样化。不同的设备制造商通常以非标准的方式将硬件组件集成到ARM设备中。ARM硬件缺乏用于硬件探测的功能,例如标准BIOS或APCI总线,并且没有为安装低级软件建立相关机制。但是巨虎所有的ARM平台都支持Linux,通过将KVM/ARM与Linux进行集成,KVM/ARM可自动得进行版本迭代。而XEN则必须积极支持每个安装在XEN hypervisor的平台。例如,对于XEN需要支持的每个新的SoC,开发人员必须在核心Xen hypervisor中实现一个新的串行设备驱动程序。而KVM/ARM得益于其集成在Linux中,其可移植性和硬件支持方面,我们需要解决的一个关键问题是ARM硬件虚拟化扩展是被设计成完全独立于任何标准内核的功能。接下来我们将描述KVM/ARM的新设计,该设计使将有利于将其集成到现有的Linux内核中,同时是其支持硬件虚拟化。

3.1 split-mode虚拟化
        完全以ARM的Hyp模式运行hypervisor是很有吸引力的,因为它是最具有特权的级别。然而,由于KVM/ARM是利用现有的内核为基础,如调度器,在HYP模式下运行KVM/ARM也就意味着Linux内核需要运行在HYP模式中。这样至少有两个问题点。第一,Linux中依赖于底层架构的代码是在内核模式下工作的,并且不能在未进行任何修改的情况下在HYP模式下运行,因为HYP模式与普通内核模式是完全不同的CPU模式。为了能在HYP模式下运行内核所需要的重大更改不太可能被Linux内核社区接受。更重要的是,保持没有HYP模式的硬件的兼容性将Linux作为guest操作系统运行,底层代码将不得不编写成能够在两种模式下运行,该种做法的潜在结果是执行效率变慢且复杂。一个简单的例子,一个页错误处理时需要获取到引起该页错误的虚拟地址。与内核模式相比,在HYP模式下,该地址是被保存在不同的寄存器中。
        第二,在HYP模式下运行整个内核会对本机性能产生负面影响。例如,HYP模式有自己独立的地址空间。内核模式使用两个页表基寄存器来提供用户地址空间和内核地址空间,其将两个分为3GB/1GB进行分割,而HYP模式使用单个页表寄存器,因此不能直接访问地址的用户空间部分。当经常调用函数来访问用户内存时将会需要内核显式地将用户空间数据映射到内核地址空间,然后执行必要的拆卸和TLB的维护操作,从而导致ARM上的性能很差。
       使用HYP模式运行Linux内核进行虚拟化时存在的这些问题在x86架构的虚拟化中并不存在。x86的root模式与其CPU特权模式正交。整个Linux内核可以作为系统管理程序在root模式下运行,因为相同的资源在non-root模式下和root模式下都可用。然而,考虑到ARM的广泛使用和LInux在ARM方面的优势,为ARM中一个有效的虚拟化解决方案至关重要,它可利用LInux并利用其虚拟化的硬件支持。
    KVM/ARM引入了分裂模式虚拟化,这是一种新的hypervisor的设计,它将hypervisor的核心进行分割,以便它能跨不同的CPU特权模式运行,从而利用每个CPU模式体的特定优点和功能。KVM/ARM使用分裂模式来实现虚拟化,利用HYP模式启动ARM的硬件虚拟化支持,同事利用以内核模式运行的现有服务。在无需对现有代码库进行大规模修改的前提下,分裂模式虚拟化将KVM/ARM与Linux进行集成。
     如图2所示,其是通过将hypervisor分解为两个组件(lowvisor和highvisor)来实现的。

lowvisor利用HYP模式下硬件虚拟化的支持来提供三个关键功能。第一,lowvisor通过适当的硬件配置,设置正确的执行上下文,并在不同的执行上下文之间提供保护和隔离。lowvisor直接与硬件保护功能交互,因此非常关键,其能保障代码库尽可能小的更改。第二,lowviso负责r从虚拟机执行上下文切换到主机上下文,反之亦然。主机执行上下文用于运行Highvisor和主机LInux内核。我们将执行上下文称为world, 将从一个world切换到另外一个world称为world的切换,因为系统的整个状态都发生了更改。由于lowvisor是在HYP模式下运行的唯一组件,因此只有它才能负责执行world切换所需要的硬件的重新配置。第三,lowvisor提供了一个虚拟化trap处理程序,它将处理必须陷入到HYP模式的中断和异常。lowvisor只执行最小的处理清楚,而大部分的工作将会被推迟到highvisor中来完成。
       highvisor作为宿主Linux内核的一部分,其以内核模式运行。因此,它可以直接利用现有的Linux功能,如调度器,并可以使用标准内核软件数据结构和机制来实现其功能,如锁机制和内存分配函数。这使得在highvisor中更容易实现高级功能。例如,虽然lowvisor体用了一个低级的trap处理和两个world的切换功能,但是highvisor将处理虚拟主机的二级页错误并执行指令模拟。注意,部分虚拟机运行在内核模式下,就像highvisor那样,但是其支持二级转换和支持HYP模式。
       由于hypervisor是跨内核模式和HYP模式划分的,所有在虚拟机和highvisor之间切换将涉及到多个模式的额转换。在运行虚拟机的过程中,highvisor所触发的trap首先会进入到HYP模式中的lowvisor。lowvisor将导致另外一个trap来运行highvisor。类似地,从highvisor到虚拟机需要从内核模式trap到HYP模式,然后切换回虚拟机。因此,从Highvisor中切出或者切入highvisor时,分裂模式虚拟化将需要两次trap操作。在ARM平台中,从HYP模式中切入或者切出的唯一方式就是trap,然而如第五章所说,这个额外的trap将会是一个显著的性能开销。
       KVM/ARM使用内存映射接口在highvisor和lowvisor之间根据需要进行共享数据。有内存管理可能很复杂,我们在Highvisor的功能里使用Linux中现有的内存管理子系统来管理highvisor和lowvisor之间的内存。不过,管理lowvisor的内存需要面临额外的挑战,因为它需要管理HYP模式的独立地址空间。一种简单的方法就是重用主机内核的页表,同事在HYP模式下使用它们,这样就能保持地址空间的一致性。不幸的是,这种想法无法实现,因为HYP模式使用了与内核模式不同的页表格式。因此,highvisor显式地管理HYP模式的页表,将在HYP模式下执行的任何代码以及在highvisor和lowvisor之间共享的任何数据结构映射到HYP模式和内核模式相同的虚拟地址空间中。

3.2 CPU虚拟化
     为了实现CPU的虚拟化,KVM/ARM必须向虚拟机提供一个接口,该接口本质上与底层的实际硬件CPU相同,同时确保hypervisor具有硬件的控制权。这需要确保在虚拟机中运行的软件必须具有访问实际物理CPU上寄存器状态的能力,以及确保hypervisor及其主机内核相关联的物理硬件状态在运行的虚拟机中是持久的。不影响虚拟机的寄存器状态可通过保存虚拟机状态并在从虚拟机切换到主机时从内存中恢复主机状态来完成上下文的切换,反之亦然。KVM/ARM将对所有其他敏感状态的访问配置为trap到HYP模式。

      表一展示了以内核和用户模式运行的软件可见的CPU寄存器状态以及每个寄存器组的KVM/ARM虚拟化方法。lovvisor有自己专用的配置寄存器,仅能被HYP模式使用,其在表一种病没有显示。在硬件支持的情况下,world切换时,KVM/ARM上下文也会被切换,因为它需要让虚拟机直接访问硬件。例如,虚拟机在未陷入到hypversor的情况下可直接对一级页表基寄存器进行编程,这是多数guest操作系统中相当常见的操作。KVM/ARM对敏感指令执行trap和仿真,当访问硬件状态是,其将影响hypervisor或泄漏硬件的相关信息,这将违反虚拟化抽象层的理念。例如,如果虚拟机执行WFI指令,KVM/ARM就会trap,这回导致CPU断点,因为这样的操作应该只由hypervisor来执行和维护。在KVM/ARM中将某些待定寄存器的状态延迟到必须时间进行处理,这样在某些工作负载下略微提高了性能。
      在内核或用户模式下运行虚拟机与在内核或用户模式下运行hypervisor之间的区别取决于在world切换期间HYP模式如何配置虚拟化扩展。从主机到虚拟机的world切换执行以下操作:
1. 将所有主机GP寄存器存储在HYP的堆栈上
2. 为虚拟机配置VGIC
3. 配置虚拟机的时钟
4. 将所有主机待定的配置寄存器保存到HYP堆栈中
5. 将虚拟机的配置寄存器加载到硬件上,这可以在不影响当前执行的情况下完成,因为HYP模式使用独立于主机状态的自己的配置寄存器
6. 配置HYP模式来捕获浮点操作,用于延迟上下文的切换、捕获中断、捕获CPU停止指令(WFI/WFE)、捕获SMC指令、陷入特定的配置寄存器访问、陷入调试寄存器访问
7. 将特定的虚拟机ID写入影子ID寄存器
8. 设置二级页表的基寄存器(VTTBR),并启动二级地址转换
9. 恢复所有guest GP寄存器,并陷入用户或内核模式
    CPU将停留在虚拟机的world,直到事件发生,触发trap进入HYP模式。这种事件可以由上面提到的任何trap、二级页错误或硬件中断引起。由于事件需要来自highvisor的服务,为了模拟虚拟机的预期硬件行为或服务设备中断,KVM/ARM必须执行另一个切换回highvisor及其主机的world。从虚拟机切换回主机的world执行以下操作:
1. 存储所有虚拟机GP寄存器
2. 禁止二级地址转换
3. 配置HYP模式使其能不捕获任何寄存器访问或指令
4. 保存所有虚拟机特定的配置寄存器
5. 将主机的配置寄存器加载到硬件上
6. 配置主机的时钟
7. 保存虚拟机特定的VGIC的状态
8. 恢复所有主机GP寄存器
9. 陷入到内核模式

3.3 内存虚拟化
        KVM/ARM通过在虚拟机中运行时为所有内存访问使能二级地址转换来提供内存虚拟化。二级地址转换只能在HYP模式下进行配置,它的使用对虚拟机时透明的。highvisor管理二级转换页表,只允许访问专门为一个虚拟机配置的内存;其他的访问操作将导致二级页错误,该错误将被hypervisor捕获。这种机制确保一个虚拟机不能访问属于hypervisor或其他虚拟机的内存和任何敏感数据。在highvisor和lowviser中运行时需要禁用二级地址转换,因为highvisor完全控制整个系统,并直接管理主机的物理地址。当hypervisor执行world切换,使系统进入到虚拟机时,它将启用二级地址转换,并相应地配置二级页表基寄存器。虽然highvisor和所有虚拟机共享相同的CPU模式,但是二级地址转换确保highvisor不接受虚拟机的任何访问。
       KVM/ARM使用分裂模式虚拟化,利用现有的内核内存分配、页引用计数和页表操作的代码。KVM/ARM通过判定IPA的错误来处理二级页错误,如果出错的地址属于虚拟机内存映射中的正常内存,KVM/ARM只需调用现有的内核函数,如get_user_pages来为虚拟机分配一页,同时将该分配的页映射到虚拟机的二级页表中。而对于单纯的hypervisor方案,其将被迫向虚拟机实现静态分配内存,或者编写一个全兴的内存分配子系统。

3.4 I/O虚拟化
       KVM/ARM利用现有的QEMU和Virtio用户空间设备模拟来提供I/O虚拟化,在硬件层面,ARM架构上的所有I/O机制都是通过加载/存储操作MMIO设备区域来实现的。除了直接分配给某个虚拟机的设备外,所有其他虚拟机都无法访问MMIO区域。KVM/ARM使用二级地址转换来确保虚拟机不能直接访问物理设备。某个虚拟机访问任何不属于其的内存区域的行为都会被捕获得到hypervisor,hypervisor会根据故障地址将刚问转发到QEMU中的特定模拟设备。这与x86有些不同,x86使用特定的x86硬件指令,例如inl和outl来操作I/O端口而不是MMIO。正如我们在第五章所介绍,KVM/ARM实现是其能使用很少的I/O开销来完成虚拟化的工作量。

3.5 中断虚拟化
      KVM/ARM利用与LInux的紧密集成重用了现有设备驱动和相关功能,包括中断的处理。在虚拟机中运行时,KVM/ARM将CPU配置为将所有硬件终端捕获到HYP模式。对于每个中断,其将陷入到highvisor中,由主机来处理中断,这样hypervisor仍然完全控制硬件资源。在这两种情况下,所有硬件中断处理都是通过重用主机中的现有的LInux中断处理功能来完成。然而,虚拟机必须从模拟设备中接收虚拟中断形式的通知,多核guest操作系统必须能够将一个虚拟的IPI从一个核发送给另外一个核。KVM/ARM使用VGIC将虚拟中断注入虚拟机,以减少陷阱HYP模式的次数。如第二章所描述的,虚拟中断通过编程存在于hypervisor CPU控制接口的VGIC中的list寄存器将虚拟中断提交给虚拟CPU。KVM/ARM对二级页表进行配置,以防止虚拟机访问控制接口,并且只允许访问VGIC虚拟CPU接口,这样可以确保只有hypervisor可以对控制接口进行编程,虚拟机可直接访问VGIC虚拟CPU接口。但是,guest操作系统仍然会尝试访问GIC分发器来配置GIC,并将IPIs从一个虚拟核发送到另外一个虚拟核。这种访问将被捕获到hypervisor,并且hypervisor必须模拟分发服务器。
      KVM/ARM引入了虚拟分发器,这是作为highvisor的一部分的GIC分发器的软件模型。虚拟分发服务器会向用户空间暴露一个接口,因此用户空间中的模拟设备可以向虚拟分发服务器发出虚拟中断,并向虚拟机暴露一个与物理GIC分发服务器相同的MMIO接口。虚拟分发服务器将保持关于每个中断的状态的内部软件状态,并在调度虚拟机时使用该状态,以便对list寄存器进行编程来注入虚拟中断。例如,如果虚拟CPU0向虚拟CPU1发送一个IPI,分发服务器将为虚拟CPU1的list寄存器编写程序,以便在下一次虚拟CPU1运行时引发一个虚拟IPI中断。
     理想情况下,虚拟分发服务器只在必要时访问硬件list寄存器,因为设备MMIO操作通常比缓存的内存访问慢得多。当将一个虚拟机调度到一个物理核上运行时,必须切换list寄存器的上下文,但是在虚拟机和hypervisor之间进行切换时,List寄存器的上下文则不需切换。例如,如果没有一个中断没有挂起,那么就没有逼样访问任何list寄存器。注意,一旦系统hypervisor在切换到虚拟机时向list寄存器写入了虚拟中断,那么当切换回hypervisor时,它也必须重新读取list寄存器。因为list寄存器描述了虚拟中断的状态,例如,是否虚拟机ACK了虚拟中断。KVM/ARM的初始未优化版本使用了一种简化的方法,它完全切换所有VGIC状态,包括每个world交换时的List寄存器。

3.6 时钟虚拟化
       读取计数器和编程计时器是许多操作系统中用于进程调度和定期轮询设备状态的常见操作。例如,Linux读取计数器以确定进程的时间片是否已过期,并编写计时器以确保进程不超过其允许的时间片。出于各种原因,应用程序工作负载也经常使用计时器。如果将每一个这样的操作都捕获到hypervisor中可能会导致明显的性能开销,并且运行虚拟机直接访问计时器硬件通常意味着放弃对硬件资源的计时控制,因为虚拟机可禁用计时器并在较长时间内控制CPU。
      KVM/ARM利用ARM的对通用计时器的硬件虚拟化特性,允许虚拟机在不需陷入HYP模式的情况下直接访问计数器和编程计时器,同时确保hypervisor仍然控制硬件。由于对物理计时器的访问是使用HYP模式控制的,所以任何控制HYP模式的软件都可以访问物理计时器。KVM/ARM通过在hypervisor中使用物理计时器并禁止从虚拟机访问物理计时器来维护对硬件的控制。作为guest操作系统运行的Linux内核只访问虚拟计时器,因此其可在不需陷入hypervisor的情况下直接访问计时器硬件。
     不幸的是,由于体系架构的限制,虚拟计时器不能直接引发虚拟中断,而总是死会引发硬件中断,这就会使CPU陷入hypervisor。KVM/ARM检测虚拟机的虚拟计时器何时到期,并向虚拟机注入相应的虚拟中断,并在highvisor中执行所有的硬件ACK和EIO操作。硬件仅为每个物理CPU提供一个虚拟计时器,多个虚拟CPU可以跨这个硬件实例进行多路复用。为了在此场景中支持虚拟计时器,如果虚拟机一直运行的话,KVM/ARM在虚拟机捕获到hypervisor时检测未过期的计时器,并利用现有的操作系统功能,在虚拟机计时器启动时编写一个软件计时器。当这样一个软件计时器触发时,将执行一个回调函数,该函数使用上面描述的虚拟分发服务器想虚拟机发出一个虚拟计时器中断。

4. 实现和采用
      我们已经成功地将我们的工作集成到Linux内核中,KVM/ARM现在是Linux平台上的标准ARM hypervisor,因为从39内核开始,每个内核都包含了它。它相对简单且能快速完成虚拟化的需求,这得益于特定的设计,这些设计选择允许它利用Linux内核现有的基础,尽管底层硬件存在一些差异化。我们分享了一些从我们的经验中学到的教训,希望他们可以帮助其他人获得开源社区广泛采用的研究思路。

代码可维护性是关键:一个常见的误解是,提供潜在改进或有趣的新特性的研究软件实现可以简单地开源,从而被开源社区快速集成。经常没有考虑到的一个重要问题就是是必须对任何实现进行维护。如果一个实现需要很多人或很多工作量来进行维护,那么它就不太课鞥被集成到现有的开源代码库中。因为可维护性非常重要,所以重用代码和接口非常重要。例如,KVM/ARM构建在KVM和QEMU等现有基础设施上,从一开始我们就优先处理代码评审注释,使我们的代码适合于集成到现有系统中。这个决策的一个意想不到但重要的好处是,我们可以利用社区来帮助解决困难的bug或理解ARM架构的复杂部分。

成为一个知名的贡献者:说服维护者集成代码不仅关乎代码本身,还关乎提交代码的人。对于研究人员来说,抱怨内核维护者不接受他们的代码进入Linux并不稀奇,只是因为一些已知的内核开发人员提交了相同的想法并已经被接受。原因是信任的问题,建立信任是一个难题,提交代码必须是众所周知的,但是不提交代码就不能被人知道。一种方法是从小处着手,作为我们工作的一部分,我们还对KVM进行了各种小的更改,以准备对ARM进行更好的支持,其中包括清理现有代码,使其更通用,以及改进跨平台支持。KVM维护者很高兴接受这些小的改进,它们产生了良好的信誉,并帮助我们为KVM社区所熟知。

结交朋友,融入社区:开源开发实际上是一种社会企业。与社区建立联系非常有帮助,不仅在网上,而且在会议和其他场合亲自参与。例如,在KVM/ARM开发的早期阶段,我们前往英国剑桥的ARM总部,与ARM管理和ARM内核工程团队这两家公司建立了联系,他们都为我们的工作做出了贡献。再举一个例子,在将KVM/ARM集成到Linux内核期间遇见一个问题就是将各种虚拟化接口的集成,例如控制寄存器的读写操作。由于这是一项既定的策略,即永远不要破坏已发布的接口以及与用户空间应用程序的兼容性,所以不能更改现有的接口,并且社区将大量精力放在设计可扩展和可重用的接口上。决定接口的适当性是一种判断,而不是一门精确的科学。我们很幸运地得到了著名内核开发人员的帮助,比如Rusty Russell,他们帮助我们驱动关于接口的实现和通信,特别是用户空间保存和寄存器恢复,这一特性对于调试和虚拟机的迁移都很有用。与Rusty这样的成熟开发人员合作是一个巨大的帮助,因为我们受益于他的经验和在内核社区中的强大声音。

尽早让社区参与:在开发KVM/ARM时,一个重要的问题是如何跨Linux支持的大量可用ARM SoC平台访问HYP模式,一种方法是在初始化KVM时初始化和配置HYP模式,这将把代码更改隔离到KVM子系统中。但是,因为从内核进入HYP模式涉及到一个trap,所以早期引导装载程序必须已经在Hyp模式中安装了代码来处理trap并允许KVM运行。如果没有安装这样的trap处理程序,当ARM core捕获到HYP事件时可能会导致内核崩溃。我们与内核社区合作,在KVM和引导加载程序之间定义正确的ABI,但是很快就发现,与SoC供应商就ABIs达成一致是很困难的。
      通过与ARM和开源社区的合作,我们得出了这样的结论:如果我们只是要求内核以HYP模式启动,那么我们就不必依赖于脆弱的ABIs。然后,内核在启动时简单地测试它是否处于HYP模式,在这种情况下,它安装一个trap处理程序来提供一个hook,以便在稍后阶段重新进入HYP模式。必须向内核引导过程中添加少量代码,但其结果是一个更干净、更健壮的机制。如果引导加载程序不知道HYP模式,并且内核没有在HYP模式下启动,
KVM/ARM将检测到这一点,并将保持禁用状态。这个解决方案避免了设计新ABI,而且原来的内核仍然可以工作,因为它们总是将显式切换到内核模式作为它们的第一个指令。这些更改被合并到主线Linux 3.6内核中,并修改了官方的ARM内核引导建议,建议所有引导加载程序以HYP模式引导内核。

了解指挥系统:KVM/ARM有多个可能的上游路径。在历史上,KVM支持的其他架构(如x86和PowerPC)通过KVM树直接合并到Linus Torvalds的树中,并得到相应架构维护人员的适当批准。然而,KVM/ARM需要对其进行一些小的更改,ARM特定的头文件和idmap子系统,因此不清楚代码是通过KVM树进行集成,获得ARM内核维护者的批准,还是通过ARM内核树进行集成。Russell King是ARM内核维护人员,Linus直接从他的ARM内核树中获取ARM相关的代码。这种情况特别有趣,因为Russell King不想在主线内核中合并虚拟化支持,他也没有检查我们的代码。与此同时,KVM社区对集成我们的代码非常感兴趣,但是如果没有ARM维护者的批准,就不能这样做,Russell King拒绝参与关于这个过程的讨论。

持之以恒:当我们尝试合并我们的代码到Linux中时,Linux ARM其实已经发生了很多变化。对于维护人员来说,SoC支持代码中的流失量正成为一个越来越大的问题,许多工作都是为了减少板特定的代码,并支持单个ARM内核镜像可通过多SoC进行引导。鉴于这些正在发生的变化,获得足够的时间让ARM内核维护人员来评审代码是很有挑战性的,而且对于任何合并到ARM树中的新代码,维护人员都有额外的压力,要求他们严格批评。我们别无选择,只能继续维护和改进代码,并定期发布随上游内核更改而更新的补丁系列。最终,ARM维护者之一Will Deacon抽出时间进行了几次全面而有益的评审,在解决了他的问题之后,他批准将我们的代码合入到Linux内核中。经过这一切,当我们认为我们完成了,我们终于收到了Russel King的一些反馈。
        当MMIO操作陷入到hypervisor时,虚拟化扩展将填充一个寄存器,其中包含用于模拟指令的有用信息(无论是装载还是存储、源/目标寄存器,以及MMIO访问的长度)。一个老的Linux内核使用的某些指令类型则不是这样填充该寄存器的。因此,KVM/ARM会从内存中加载指令并在软件中对其进行解码。尽管解码实现经过了很多人的测试和审查,但Russell King反对包含这个特性。他已经在其他子系统中实现了多种形式的指令解码,并要求我们要么重写ARM内核的重要部分,以统一所有的指令解码,以提高代码重用,要么从我们的实现中放弃对MMIO指令解码的支持。我们没有追求可能会拖上几个月的重写工作,而是放弃了其他方面都很受欢迎和有用的代码库。我们只能推测这一决定背后的真正动机,因为ARM维护者不会参与有关这个问题的讨论。
        经过15个主要补丁的修订和18个多月的时间,KVM/ARM代码在2013年2月被成功通过Russell King的ARM分支合并到Linus的代码树中。在Linux 3.9合并窗口关闭之前让所有这些东西最终走到一起,关键是与内核开发人员保持一个良好的关系,同时也从他们那里得到了很多的帮助,并将持之以恒的推动代码合入所面临的各种挑战。

5. 试验结果
        我们提出了一些实验结果,量化了KVM/ARM在多核ARM硬件上的性能。我们通过在虚拟机中直接在硬件上运行微基准测试和实际应用程序工作负载来评估KVM/ARM与本地执行相比的虚拟化开销。我们测量并比较KVM/ARM与KVM x86的性能、能耗和开销成本,以便证明KVM/ARM在更成熟的硬件虚拟化平台上的有效性。这些结果提供了对ARM硬件虚拟化支持性能的第一个真实硬件度量,以及ARM和x86之间的比较。

5.1 方法
        ARM的测量结果是使用一个Insignal Arndale board获得的,该开发板是具有一个1.7GHz的双核Cortex -15的三星5250 SoC。这是第一个基于CortexA-15具有硬件虚拟化支持的ARM CPU。USB总线提供100Mb以太网,外部120GB三星840系列SSD驱动器通过eSATA连接到Arndale板。x86测量是使用低功耗移动笔记本电脑平台和行业标准服务器平台获得的。笔记本电脑的平台是一台2011年的MacBook Air,双核1.8GHz内核i7-677M中央处理器,一个三星SM256C 256GB SSD驱动器,和一个苹果100Mb USB以太网适配器。服务器平台是一台专用的OVH SP 3服务器,双核3.4GHz Intel XeonE3 1245v2 CPU,两个物理SSD驱动器,其中只有一个被使用,1GB以太网连接到100Mb的网络。在我们进行实验时,x86硬件还不支持虚拟APIC。
      考虑到硬件平台的差异,我们的重点不是测量绝对性能,而是测量每个平台上虚拟化和本机执行之间的相对性能差异。由于我们的目标是评估hypervisor,而不是原始硬件性能,因此这个相对测量是为比较KVM/ARM与KVM x86的虚拟化的性能和功耗提供了一个有用的跨平台基础。
      为了提供可比较的测量结果,我们尽可能保持所有硬件平台上的软件环境相同。所有平台上的主机和虚拟机都是Ubuntu 12.10。我们在实验中使用了Linux 3.10内核,并在源代码树的顶部集成了大量支持页面操作的补丁。由于实验是在许多不同的平台上进行的,所以内核配置必须略有不同,但是所有的公共特性在所有平台上都是类似地配置。特别是,在ARM和x86上的来guest虚拟机中都使用了Virtio驱动程序。我们使用QEMU v1.5.0用于我们的测量。所有系统都配置了最多1.5GB RAM,以供测试的guest虚拟机或主机使用。此外,所有多核测量都是使用两个物理内核和带有两个虚拟cpu的guest虚拟机以及单核测量完成的,在客户端和主机系统的内核配置中禁用SMP。x86平台上禁用了超线程,禁用CPU频率缩放,以确保在每个平台上以相同的时钟速率测量本机和虚拟化的性能。
      对于涉及网络和另一台服务器的测量,所有系统都使用100Mb以太网。ARM和x86笔记本电脑平台使用Netgear GS608v3交换机连接,2010年iMac使用3.2GHz Core i3 CPU连接,12GB运行Mac OS X Mountain Lion的RAM被用作服务器。x86服务器平台连接到一个100Mb端口,使用OVH网络基础架构和同一数据中心中的另一台相同服务器作为服务器。虽然x86服务器平台所使用的网络基础设施有一些不同,因为它是由其他人控制的,但是我们不认为这些不同会对虚拟化和本机执行之间的相对性能产生任何重大影响。
     我们给出了四组实验的结果。首先,我们使用定制的小客户端在多核硬件上测量了hypervisor的各种微观体系结构特征的成本.我们进一步安装了KVM/ARM和KVM x86上的代码,以便在关键路径上的特定点上读取循环计数器,以便更精确地确定开销时间花在哪里。其次,我们使用lmbench v3.0在单核和多核硬件上测量了一些常见的低级操作系统操作的成本。在多核配置上运行lmbench时,我们将每个基准测试过程固定在一个单独CPU的进程上,用于测量多核系统上虚拟机中处理器间通信的真实开销。第三,我们使用单核和多核硬件上的各种工作负载来测量实际应用程序的性能。表2描述了我们使用的8个应用程序的工作负载

第四,我们使用与测量应用程序性能相同的8个应用程序的工作负载来测量功耗结果。ARM功率的测量是用ARM能耗探针来进行的,用于测量连接到Arndale板电源上的分流器的功耗。外部SSD的电源是通过将USB电源线连接到Arndale板上的USB端口来提供的,从而将存储电源纳入在电源处测量的总SoC电源中。x86电源测量使用powerstat工具执行,该工具会读取ACPI信息。powerstat会从电池中获得总的系统电量,所以x86系统上的电量测量来自电池电量,只能在x86笔记本电脑平台上运行。虽然我们没有测量x86服务器平台的功耗,但是x86笔记本的效率预期要低得多,所以使用x86笔记本可以对ARM的能源效率进行保守的比较。x86笔记本电脑的显示和无线功能被关闭,以确保公平的比较。这两种工具都报告了10Hz间隔的瞬时功率消耗,单位是瓦特。测量值取平均值,再乘以测试时间得到能量测量值。

5.2 功耗和性能测量
        表3展示了在ARM平台上使用KVM/ARM和在x86平台上使用KVM x86 进行虚拟化的各种微体系结构成本。测量结果以循环而不是时间的形式显示,以便在具有不同CPU频率的平台之间提供有用的比较。在可能的情况下,我们将为ARM平台显示两个数字,其中包含和不包含VGIC和虚拟计时器的支持。

      Hypercall是两个世界交换机的开销,从虚拟机到主机,然后立即返回,不需要在主机中做任何工作。由于两个主要因素,与KVM x86相比,KVM/ARM在此操作中需要3到4倍的时钟周期。首先,在ARM上保存和恢复VGIC状态以使用虚拟中断的开销是很大的,可用的x86硬件还没有提供这种机制。在没有VGIC和虚拟时钟的ARM平台上,测量时不包括保存和恢复VGIC状态的开销,结果表明,该操作在ARM的两个world切换时VGIC状态的恢复和保存占用了一半的开销。其次,x86提供了硬件支持,可以在world切换时自动保存和恢复状态,这要快得多。ARM需要软件显式地保存和恢复状态,这提供了更大的灵活性,但成本更高。但是,如果没有VGIC状态,hypercall的开销只比x86服务器平台上硬件加速hypercall的开销多大约600个周期。ARM world切换的成本还没有优化,还可以进一步降低。例如,一个小补丁消除了不必要的原子操作,从而降低了大约300个时钟周期的hypercall开销,但是直到v3.10发布之后才将其纳入主线内核。另一个例子是,如果部分VGIC状态执行惰性上下文切换而不是在每个world切换上保存和恢复,这也可以降低world切换的开销。
      Trap是将硬件模式从虚拟机模式切换到运行hypervisor的CPU模式、ARM上的HYP模式和x86上的root模式的成本。结果表明ARM的trap操作要比x86的trap要快得多,因为ARM只需要操作两个寄存器来完成这个trap,而trap在x86的开销也只是粗略的执行world切换,因为在这两种情况下,硬件保存的状态相同。ARM上的trap开销只占world开销中的很小一部分,这表明在ARM上由分裂模式虚拟化引起的双trap不会增加太多开销。
      I/O kernel是从虚拟机到设备的I/O操作的开销,其是在内核中被模拟的。
      I/O User显示向用户空间中模拟的设备发出I/O操作的开销,向I/O内核中添加从内核空间到用户空间进程的转换开销,以及在主机上的用户空间中为I/O做少量工作的开销。这代表了使用QEMU的成本。由于这些操作涉及到world切换,而保存和恢复VGIC状态再次成为ARM的一项重大成本。KVM x86在I/O内核上比KVM/ARM快,但是在I/O用户上稍微慢一些。这是因为x86上关于world的切换的硬件优化占据了在内核中执行I/O操作大部分开销,但是在x86上,从内核到主机端的用户空间进程的转换开销更大,因为x86 KVM在进入用户空间时惰性地保存和恢复额外的状态。注意,访问用户空间的附加开销包括保存额外的状态、在用户空间中做一些工作、返回内核并处理KVM_RUN ioctl对KVM的调用。
     IPI是一个虚拟CPU和向另一个虚拟CPU发出IPI的开销,且这两个虚拟CPU都运行在单独的物理内核上且都在虚拟机中积极运行。IPI测量从发送IPI到另一个虚拟CPU响应并完成IPI的时间。它涉及到多个world的切换以及发送和接收硬件IPI的操作。尽管ARM的world切换开销较高,但是ARM比x86更快,因为x86上的底层硬件IPI比较占据开销,x86上的APIC MMIO操作需要KVM x86执行ARM上不需要的指令解码,而在x86上完成中断的开销较大。没有VGIC/虚拟定时器的ARM比使用VGIC/虚拟定时器的ARM要慢得多,尽管它具有更低的world切换开销,因为发送、EOI和ACK都会导致陷入到hypervisor,并且由QEMU在用户空间中来处理。
    EOI+ACK是在两个平台上完成虚拟中断的开销,它包括中断确认和ARM上的完成操作,但是在x86平台上其值包含的完成操作。ARM需要一个额外的操作,确认中断控制器,以便确定中断的来源。而x86并不会这样做,因为在引发中断时,中断源是由中断描述符表条目直接指示的。但是,ARM上的操作速度大约是x86上的5倍,因为在ARM平台上并不需要陷入hhypervisor,这是因为VGIC本来就支持这个操作。在x86上,必须模拟EOI操作,因此会给hypervisor造成一个trap。每个虚拟中断都需要这个操作,包括虚拟ip和来自虚拟设备的中断。
    图3到7显示了相对于各自的本地执行系统的测量结果,虚拟化执行测试标准化了,开销更小。









     图3和图4显示了在虚拟机中运行lmbench与直接在主机上运行lmbench的规范化性能。图3显示了KVM/ARM和KVM x86在单个CPU配置中也有类似的虚拟化开销。作为对比,我们还展示了没有VGIC/vtimer的VM/ARM性能。总的来说,使用VGIC/ vtimer提供了稍好一点的性能,但是管道和ctxsw工作负载的性能相差很大。在本例中,造成高开销的原因是每当进程阻塞时需要更新Linux调度器中的运行队列时钟,因为在没有虚拟计时器的情况下需要将计数器读入ARM平台的用户空间。我们在支持VGIC但没有虚拟定时器的情况下通过运行工作负载时读取计时器的数值验证了这一点。
     图4显示了多核配置中KVM/ARM和KVM x86之间虚拟化开销存在更大差异,与KVM x86 fork和exec相比,KVM/ARM的开销更小,但开销更多被用于保护故障。但是,对于管道和ctxsw工作负载,这两个系统的开销都是最差的。这是由于每次从管道中的数据发送方向接收方重复发送IPI的开销以及在调度新进程时发送IPI的开销造成的。x86不仅具有比ARM更高的IPI开销,而且它还必须对每个IPI进行EOI,在x86上比在ARM上开销大得多,因为这需要让x86陷入hypervisor,而ARM并不需要。如果不使用VGIC/ 虚拟定时器,KVM/ARM与KVM x86的开销相当,因为它还会陷入hypervisor来对IPIs进行ACK和EOI。
    图5和图6显示了在虚拟机中运行应用程序工作负载与直接在主机上运行应用程序工作负载的规范化性能。图5显示,除了MySQL工作负载之外,KVM/ARM和KVM x86在单个核心配置中的所有工作负载中都有类似的虚拟化开销,但是图6显示多核上的性能差异更大。在多核平台上,Apache和MySQL的开销在KVM/ARM上明显少于KVM x86。总的来说,在多核的情况下,在KVM/ARM上的所有应用程序的工作负载比直接在硬件上运行的情况多执行10%的任务量。而更成熟的KVM x86系统对于Apache和MySQL有更高的虚拟化开销。KVM/ARM的分裂模式虚拟化设计允许它利用ARM硬件支持,其性能与使用x86硬件支持的传统hypervisor相当。测量结果还表明,KVM/ARM在支持VGIC/虚拟计时器的情况下比没有支持的请看看下整体表现更好。
     图7显示了对多核上的各种应用程序工作负载使用虚拟化和直接执行的标准化功耗。我们只比较了KVM/ARM的ARM平台与支持KVM x86的x86笔记本的功耗。在这些实验中使用的Intel Core i7 CPU是Intel最优的功耗处理器之一,我们预计服务器功耗会更高。测量结果表明,在除memcached和untar之外的所有情况下,使用VGIC/ 虚拟计时器的KVM/ARM都比KVM x86虚拟化更高效。这两个工作负载在两个平台上都不是CPU绑定的,功耗也不受虚拟化层的显著影响。但是,由于ARM对于这些工作负载的虚拟化开销略高,所以对于这两个工作负载,ARM的能源虚拟化开销略高。虽然对虚拟化的功耗方面的更详细的研究超出了本文的范围,但是这些测量提供了比较ARM和x86虚拟化功耗成本的有用数据。

5.3 实现的复杂度
      我们将KVM/ARM的代码复杂度与Linux 3.10中的KVM x86进行比较。KVM/ARM是5812行代码,仅计算添加到其中的特定于体系结构的代码,其中lowvisor仅仅是718行。作为保守的比较,KVM x86是25367行,但不包括客户性能监控支持,KVM/ARM还不支持,AMD支持需要3311行。这些数字不包括KVM的体系结构通用代码。表格四展示将整个特定于hypervisor体系结构的代码分解为其主要组件的相关情况。

      通过检查这些代码,我们注意到x86实现中惊人的额外复杂性主要来自以下五个原因。
1. 由于早期的硬件版本不支持EPT, KVM x86必须支持影子页表
2. 随着时间的推移,硬件虚拟化支持也在不断发展,要求软件有条件地检查对大量特性(如EPT)的支持
3. 许多操作需要对x86平台上的指令进行软件解码。而KVM/ARM的out-of-tree MMIO指令解码实现要简单得多,只有462行
4. x86上的各种分页模式需要更多的软件逻辑来处理页面错误
5. x86需要更多的软件逻辑来支持中断和计时器,而ARM提供VGIC/vtimer硬件支持,从而降低了软件的复杂性
     KVM/ARM的代码数量比单独为HYP模式编程的程序少得多,而lowvisor代码数几乎要小一个数量级。与独立的hypervisor不同,KVM/ARM的代码复杂度非常小,因为Linux已经提供了许多功能,所以不必重新实现这些功能。表4不包括其他非hypervisor特定于体系结构的Linux代码,例如基本引导,这是非常多的代码。将独立的hypervisor(如Xen)从x86移植到ARM要复杂得多,因为用于基本系统功能的所有ARM代码都需要从头编写。相反,由于Linux在ARM上占主导地位,KVM/ARM只是利用了现有的Linux ARM在Linux支持的每个平台上运行。

6. 推荐
        根据我们构建KVM/ARM的经验,我们为硬件设计人员提供了一些建议,以简化和优化未来的hypervisor实现
        共享内核模式内存模型。运行hypervisor的硬件模式应该与运行操作系统内核的硬件模式使用相同的内存模型。这样软件设计人员在决定如何紧密地将hypervisor与现有操作系统内核集成方面具有更大的灵活性。不幸的是,ARM HYP模式并没有做到这一点,这使得KVM/ARM不能简单地在HYP模式下重用内核的页表。这种重用将简化实现,并允许性能关键的模拟代码以HYP模式运行,在某些情况下可避免world的切换。有些人可能会认为,这个建议使得独立的hypervisor实现更加复杂,但这并不是真的。例如,ARM内核模式已经提供了一个简单的选项,可以使用一个或两个页表基寄存器来统一或分割地址空间。我们建议与x86虚拟化方法不同,x86虚拟化方法没有独立的、更有特权的hypervisor CPU模式。拥有独立的CPU模式可能会提高独立hypervisor的性能和实现,但是不共享内核内存模型会使与主机内核集成的hypervisor的设计更复杂化。
       使VGIC状态访问快速,或至少不频繁。VGIC的支持可以提高性能,特别是在多核系统上。我们的测量还显示,访问VGIC状态会增加world切换时的开销。这是由于在关键路径中对VGIC控制接口的访问速度慢造成的。改进MMIO访问时间可能会提高虚拟机的性能,但如果这是不可能的或成本效益不高,至少可以减少对VGIC的MMIO访问频率。例如,可以引入一个摘要寄存器来描述每个虚拟中断的状态。当执行从虚拟机到hypervisor的world切换时,可以读取这些信息,以获取当前只能通过读取每个world切换上的所有list寄存器(请参阅3.5节)才能获得的信息。
      完全避免IPI trap操作。如果硬件支持在直接从虚拟机发送虚拟IPIs到另外一个虚拟CPU时不需要陷入到hypervisor,这样将会改善执行效率。硬件设计人员可能低估了IPIs在现代多核操作系统上的频率,我们的测量结果显示,发送IPIs会为某些工作负载增加大量开销。当前的VGIC设计需要陷入到hypervisor来模拟分发服务器中IPI寄存器的访问,并且这种模拟访问必须在虚拟内核之间使用软件锁定机制进行同步,这将增加IPIs的大量开销。当前的ARM硬件支持接收虚拟IPI,它可以在没有trap的情况下被ACK和EOI,但不幸的是,它没有解决发送虚拟IPI的重要问题。

7. 相关工作
       虚拟化已经有很长的历史了,但是从20世纪90年代末开始,它又重新流行起来。大多数工作几乎都集中在虚拟化x86体系结构上。在引入x86硬件虚拟化支持之前,VMware和Xen等系统最初都是基于纯软件的方法来实现的,而现在所有x86虚拟化平台、VMware、Xen和KVM都利用了x86硬件虚拟化支持来实现的。由于x86硬件虚拟化支持与ARM在以与内核相同的模式完全运行hypervisor的能力上有很大的不同,x86虚拟化方法本身并不能直接利用ARM硬件虚拟化支持。
      一些x86方法还利用主机内核为hypervisor提供功能,VMware工作站的hypervisor创建了一个与主机内核分离的VMM,这种做法在许多重要方面不同于KVM/ARM。首先,VMware VMM没有集成到主机内核源代码中,因此不能重用现有的主机内核代码,例如,无法填充与VM相关的页表。其次,由于VMware VMM是专门针对x86的,所以它不会运行在不同的特权CPU模式上,因此不使用类似于KVM/ARM的设计。第三,运行VM所需的大多数模拟和错误处理代码都在VMM内的最特权级别上执行。KVM/ARM在特权较少的内核模式下执行这段代码,并且在特权最多的模式下只执行很少的代码。相比之下,KVM得益于与Linux内核的集成,比如x86设计依赖于能够以相同的硬件hypervisor模式同时运行内核和hypervisor,这在ARM上是有问题的。
       ARM架构的全系统虚拟化是一个相对未开发的研究领域。多数实现方案都是纯软件的。目前已经开发了许多独立hypervisor,但是这些hypervisor并没有广泛使用,它们是专门针对嵌入式市场开发的,必须进行修改并移植到每个主机硬件平台,这就限制了其被广泛使用。ARM的一个废弃的XEN端口需要对客户内核进行全面的修改,而且从未完全开发过。ARM上的KVM的早期原型使用了一种自动化的轻量级半虚拟化方法来自动修补内核源代码,使之作为guest内核运行,但是性能很差。VMware Horizon Mobile使用托管虚拟化来利用Linux对各种硬件平台提供支持,但需要对客户操作系统进行修改,其性能尚未得到证实。这些半虚拟化方法都不能运行未经修改的客户操作系统。
      早期的一项研究层试图使用一种软件仿真来评估对ARM硬件虚拟化的支持,但一个简单的hypervisor缺少一些重要的特性,比如SMP支持,以及多个存储和网络设备。由于缺乏硬件或周期精确的模拟器,无法进行真正的性能评估。与此相反,我们首次评估了使用真实硬件的ARM虚拟化扩展,提供了与x86的直接比较,并介绍了使用ARM虚拟化扩展(包括SMP支持)的完整hypervisor的设计和实现。
      正在使用ARM硬件虚拟化支持开发一个专门针对服务器的新版本Xen。由于Xen是一个不利用内核功能的单独hypervisor,因此可以将其架构为完全在HYP模式下运行,而不是使用分裂模式进行虚拟化。同时,这需要大量的商业工程工作。由于Xen是一个独立的hypervisor,将Xen从x86移植到ARM非常困难,部分原因是所有与ARM相关的代码都必须从头编写。即使让Xen在一个ARM平台上工作之后,它也必须手动移植到每个不同的ARM设备上。由于Xen的自定义I/O模型使用虚拟机中的hypercall进行ARM上的设备模拟,因此Xen并不能运行guest操作系统,除非它们被配置为包含Xen的hypercall层并包含对XenBus半虚拟化驱动程序的支持。相比之下,KVM/ARM使用标准Linux组件支持更快的开发,完全支持SMP,以及可运行未经修改的操作系统的能力。在支持Linux的新设备上很容易支持KVM/ARM,而且我们也很容易的在ARM的通用Express板、Arndale板和硬件模拟器上支持KVM/ARM。虽然Xen可以潜在地减少两个world切换开销,这是因为Xen可以在hypervisor中处理直接处理,但是其切换到Dom0用于I/O支持或切换到其他虚拟机将涉及到与KVM/ARM相同状态的上下文切换。
      用于hypervisor的微内核方法已用于减少hypervisor对TCB的访问,并在用户模式下运行hypervisor服务。这些方法在设计和原理上与分裂模式虚拟化不同,分裂模式虚拟化将hypervisor功能跨特权模式分离,以便利用虚拟化硬件支持。分裂模式虚拟化还提供了不同的hypervisor功能分割。KVM/ARM的lowvisor是一个更小的代码库,它只实现最低级别的hypervisor机制。它不包括在这些其他方法中使用的hypervisor TCB中提供的更高级别的功能。

8. 结论
        KVM/ARM是Linux ARM hypervisor的主线,也是第一个可以在ARM多核硬件上运行未经修改的guest操作系统的系统。KVM/ARM的分裂模式虚拟化即利用Linux内核机制和硬件支持也利用了ARM硬件虚拟化扩展。我们的实验结果表明,1. KVM/ARM由分裂模式虚拟化引起的额外trap对性能的影响最小。2. 对于真实的应用程序工作负载,在多核硬件上比直接本地执行的功耗和效率提高了10%左右。3. 与广泛使用的KVM x86虚拟化相比,KVM/ARM在多核硬件上实现类似或更低的虚拟化开销和电力成本。基于我们将KVM/ARM集成到主流Linux内核的经验,我们提供了一些关于获得开源社区采用的研究思路和代码的提示,并为硬件设计人员提供了改进未来hypervisor实现的建议。

9.致谢
      Marc Zyngier帮助开发,实现了VGIC和vtimer支持,并帮助我们开发硬件。Rusty Russell负责协处理器用户空间界面的开发,并协助上传。Deacon和Avi Kivity会提供大量有用的代码评审。Peter Maydell在QEMU支持和调试方面提供了帮助。Gleb Natapov帮助我们更好地理解KVM x86性能。Marcelo TosattiNicolas Viennot帮助解决了voodoo bug.Keith Adams, Hani Jamjoom和Emmett对本文的早期草稿提出了有益的评论。这项工作部分得到了ARM和NSF赠款的支持。




















回复

使用道具 举报

7

主题

14

帖子

119

积分

注册会员

Rank: 2

积分
119
发表于 2018-12-24 16:36:18 | 显示全部楼层
good job!
如果能把图也截过来就完美了。
回复

使用道具 举报

0

主题

6

帖子

42

积分

新手上路

Rank: 1

积分
42
发表于 2019-1-21 14:38:50 | 显示全部楼层
armv8的虚拟化
回复

使用道具 举报

1

主题

4

帖子

31

积分

新手上路

Rank: 1

积分
31
发表于 2019-1-22 21:24:15 | 显示全部楼层
Good job!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|TEE and Virtualization

GMT+8, 2019-3-22 21:08 , Processed in 0.106792 second(s), 19 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表