首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

KVM 虚拟化技术在 AMD 平台上的实现(5)

KVM 虚拟化技术在 AMD 平台上的实现(5)

内存虚拟化Shadow Paging  作者 Shawn 在其中文博客中很详尽地介绍了 KVM 在只支持一级分页的 x86 平台上用 “Shadow Paging”进行 MMU 虚拟化的实现,由于目前新的 X86 硬件平台提供的虚拟化扩展都能支持两维分页处理,所以笔者在此没必要再细节描述“Shadow Paging” 的实现, 但仍有必要概括一下其特点 :
1. 每个虚拟机对应的 qemu-kvm 进程通过分配不同的虚拟内存区间来映射虚拟机不同的物理内存区域。 每个虚拟机对应的 struct kvm 的 memslots 数组用来描述虚拟机物理内存和 qemu-kvm 虚拟区间的对应关系。 ( 在采用二维分页技术的 KVM 实现中,这种通过 qemu-kvm 的虚拟内存区域来映射虚拟机物理内存的方法是相同的 )
2. 虚拟机运行时,Shadow 页表或 CPU 的 TLB 将客操作系统上的虚拟地址 (gva) 翻译成主机上的物理地址 (hpa)。 每个虚拟机的 CPU 的 CR3 寄存器指向的是 shadow 页表的根目录的物理地址。 KVM 能截取并屏蔽客操作系统对 CR3 的访问。 在采用分页模式的虚拟机中,客操作系统内的页表记录的是客操作系统意义上的物理地址 (gpa)
3. 虚拟机进行内存访问时,只有 CPU 的 TLB 记录及 Shadow 页表项的缺失或访问控制才会导致“Page Fault”, 产生 VMEXIT 状态切换。  客操作系统的页表状态和“Page Fault”无关
4.KVM 在处理 “Page Fault”时,会首先检查客操作系统页表的状态,如果客操作系统页表本身就不存在从 gva 到 gpa 的映射, 则 KVM 要向虚拟机注入一个“Page Fault”, 由客操作系统首先完成其逻辑上的页分配; 如果客操作系统上已经存在 gva 到 gpa 的映射,则 KVM 需要根据 gpa 和 memslots 的记录确定该 gpa 对应的 qemu-kvm 进程空间的虚拟地址,即 hva, 然后调用 get_user_pages() 确定或分配物理页, 即确定 hpa, 最后根据 gva 和 hpa, 建立他们在 Shadow 页表中的映射关系。 ( get_user_pages 是 Linux 上用于用户进程空间的物理页分配接口, 也就是说 KVM 的实现完全利用了 Linux 已有的物理页管理和分配功能 )
5. 为了维护客操作系统页表和 Shadow 页表的一致性,KVM 实现上采取了一些技巧 :
  • 在第一次建立 gva 到 hpa 的映射时,KVM 需要将该 hpa 标记为只读的, 以便于下一次该页被写时,KVM 能将 CPU TLB 及 Shadow 页表的 “dirty”标记同步到客操作系统对应的页表项上。
  • 在 shadow 页表上建立 gva 到 hpa 的映射时,如果该 gva 对应的是客操作系统上的页表页, 则 KVM 将该 hpa 映射为只读的。 这样做的目的是能随时控制客操作系统对其页表的修改,及时将客操作系统页表的变化同步到 Shadow 页表上。
  • 为维护客操作系统的页表页和其对应的 Shadow 页表页的对应关系, KVM 在描述每个虚拟机物理内存区域的 struct kvm_memory_slot 中提供了逆向映射信息,能将一个客操作系统页表页的 gfn 映射到其所有 Shadow 页表项, 其中对于有多个 Shadow 页表项对应于相同 gfn 的情况, 采用了一个 struct kvm_rmap_desc 数据结构来组织映射关系。
6. 分页模式的客操作系统的每一个进程在 KVM 上都有一个独立的 Shadow 页表,为了避免减低性能,在客操作系统进程切换时,KVM 是不能直接释放以前进程的 Shadow 页表的,即系统中所有的 Shadow 页表页都是缓存的。 为了在虚拟机的 CR3 发生变化后重新使用必要的 Shadow 页表页, KVM 为每一个 Shadow 页表页提供了一个 struct kvm_mmu_page 数据结构,该结构中包含该 Shadow 页表页对应的 gfn。 在每个虚拟机对应的 struct kvm_arch 结构中有一个 hash 表,mmu_page_hash[], 用于按 hash 方式组织虚拟机全部的 Shadow 页表页。 每次发生 CR3 改变或 Shadow 页表的“Page Fault”行为时,KVM 以 gfn 为参数,通过查找 hash 表,可确定对应的 Shadow 页表页是否在缓存中存在。
7. 对于那些采用非分页模式的客操作系统 ( 如 Linux 启动阶段的实模式,非分页保护模式 ),KVM 仍然采用 Shadow 页表的分页模式来实现,这是提供统一的物理内存分配所必须的, 只不过对于非分页模式的虚拟机而言,免去了客操作系统和 Shadow 页表同步的问题。 当然,KVM 能通过对 CR0 的截取,向客操作系统屏蔽分页机制的存在。
Nested Paging     作为纯粹用软件方法实现的解决虚拟机使用物理内存的技术,“Shadow Paging”明显的缺点是在传统操作系统分页处理的基础上增加了额外的开销,影响虚拟化解决的性能。 这种额外的开销包括 VMM 对客操作系统页表修改的截取以及相关的同步操作,对 CR3 切换的截取以及相关的 Shadow 页表上下文切换操作, 这些操作通常是相当频繁的。 为了解决 “Shadow Paging”的性能问题,AMD 为其 AMD-V 扩展增加了一个新的特征,称之为 NPT 或 “Nested Paging”。  NPT 是采用二维分页的技术,即运行的客操作系统会使用二个体系的页表来执行虚拟地址到物理地址的映射,第一个页表映射从 gva 到 gpa,完全由客操作系统的页表来控制, 第二个页表映射从 gpa 到 hpa, 由 VMM 上的页表来控制。
     为实现”Nested Paging”,AMD-V 提供了如下硬件特征 :
  • 提供了一个 nCR3 寄存器用来存放虚拟机运行时第二维页表的物理地址, 该寄存器的值由 VMCB.CONTROL 的 N_CR3 字段在 VMRUN 时进行设定, 软件无法直接读取该寄存器的值。
  • 提供了一个 gPAT 寄存器用来影子虚拟机的 PAT MSR 寄存器, gPAT 的值由 VMCB.SAVE 的 G_PAT 字段在 VMRUN 时进行设定,VMM 软件无法读取该寄存器的值。显然,用 gPAT 来对客操作系统的 PAT 进行影子,是必要的硬件支持, 因为一方面客操作系统的页表需要运行时参考 PAT, 所以 PAT 不可能象其他 MSR 一样通过截取来仿真,另一方面也不能直接开放物理的 PAT 寄存器给客操作系统,因为 Host 层进程的页表也需要使用它。
  • CR0, CR4, EFER 寄存器具有复制的硬件状态。 也就是说当 NPT 功能激活时,VMRUN 从 VMCB.SAVE 加载的这三个客操作系统的寄存器不会破坏 VMRUN 运行前该 CPU 的 CR0,CR4,EFER. 虚拟机和主机有二套分开的 CR0,CR4,EFER 来分别控制其各自层次的分页行为。
  • 提供了一个无名的 NPT 状态控制位,该位的值由 VMCB.CONTROL 的 NP_ENABLE 字段来初始化。当该 NPT 状态控制位为 1 时,运行在该 CPU 上的客操作系统才能使用二维分页。
    图 1 所示的是 Nested Paging 的物理结构 . 运行在 NPT 情况下,虚拟机 CPU 的 CR3 寄存器指向的是客操作系统上页表的 gpa, nCR3 指向的是由 VMM 维护的第二维页表的 hpa。 在硬件的驱动下,客操作系统的 gva 首先被第一维页表翻译成 gpa, 然后 VMM 上的第二维页表又将该 gpa 翻译称 hpa。 物理的页表遍历硬件逻辑是相当复杂的,其中第一维页表 CR3 及每一级翻译输出的 gpa 都要经过第二维页表的翻译才能转换成 hpa, 因此翻译一个 gva 可能需要经过 20 多次的物理内存访问, 导致较高的物理延迟。  在 NPT 情况下,已有的 TLB 用来缓存 gva 到 hpa 的映射。 此外,支持 NPT 的 AMD 的处理器一般都提供了一个 Nested TLB 来缓存 gpa 到 hpa 的映射, 以平衡在 TLB 的 “Cache Miss” 情况下 NPT 二维页表遍历延迟较高所带来的性能损失。
图 1. 性能损失     另外一个和 NPT 相关的问题是物理页属性控制问题,在 NPT 情况下,针对一个 CPU 上运行的客操作系统,有两套控制寄存器 (CR0,CR4,EFER,PAT,MTRRx) 以及两层页表上的属性位来控制一个物理页的访问属性, 针对每个控制寄存器和属性位,NPT 一般采用 guest 和 host 两级的交集来执行控制,如只有当 guest 和 host 两级的页表和控制寄存器都允许写某物理页时,该物理页才允许被写。 MTRRx 的处理比较特殊,目前客操作系统的 MTRR 寄存器实际上对页的访问控制是无效的, 一方面 AMD-V 硬件上没有对 MTRR 寄存器建立影子,另一方面 KVM 截取客操作系统对 MTRR 的访问的处理比较简单,在 MTRR 被设置时会重新初始化整个第二维的页表, 释放所有的页表页,当以后 KVM 上“page fault” 重新建立这些页表页时,这些 guest 层的 MTRR 的设置才会同步到页表项中去。
   在 NPT 情况下,客操作系统对其自己的页表 ( 第一维页表 ) 有完全的控制,因此 KVM 不必要截获客操作系统对页表的修改。 另外,由于第二维页表执行的是从 gpa 到 hpa 的映射,在一个虚拟机运行过程中,KVM 只需要维护一个唯一的第二维页表,也就免去了截获客操作系统切换 CR3 的开销。 当然,NPT 的另外一个优点是比 Shadow Paging 节省很多 VMM 层的页表页, 减少物理内存的总体消耗。 很多测试表明,在采用 NPT 技术后,虚拟化应用的性能会显著提高,内存分配密集型的应用尤其如此。
返回列表