Page 52 - 《软件学报》2020年第10期
P. 52
3028 Journal of Software 软件学报 Vol.31, No.10, October 2020
内核进行交互.大量紧耦合的复杂功能带来了错误隔离方面的一些不便,宏内核中存在大量涉及段寄存器的非
常规内存访问机制,如果发生一些数据访问的错误往往会造成例如系统崩溃等无法挽回的后果,所以在代理执
行的过程中保证与内核交互的正确性是异常重要的.
现代操作系统大都使用 SYSCALL 指令进行系统调用,CPU 会根据 LSTAR MSR 的值跳转到系统调用处理
函数的入口,所以 Wormhole 在第②步操作中在服务端虚拟机的地址空间中建立从客户端虚拟机 LSTAR MSR
到服务端虚拟机系统调用入口代码页的映射,保证可以通过客户端 LSTAR MSR 的值找到正确的系统调用
入口.
出于权限分离与安全上的考虑,宏内核中的用户态与内核态使用的包括栈在内的一系列内存结构是不同
的,所以在用户态进程下陷到内核时需要保存用户态的上下文并切换到内核专用栈或中断专用栈.例如,在
Linux 内核中这些栈顶的地址存储在一些 per-CPU(每个 CPU 一个)的变量中.在 AMD64 平台的 Linux 内核中,
这些 per-CPU 的变量使用 GS 段寄存器来进行访问,它们的基地址存储在专门的 MSR 中,不会随着地址空间的
变化而改变.Wormhole 通过第④步操作保证了控制流切换前后 GS 段内存访问机制的正确性.同样地,第④步操
作也保证了 FS 段内存访问机制的正确性.例如,Linux 操作系统在 AMD64 平台上会利用段寄存器机制通过
FS:0x28 来访问一个特殊的“哨兵(sentinel)”值用于检查栈缓冲区溢出(stack buffer overflow)情况.
(3) 控制流切换过程中零虚拟机下陷.
控制流切换在整个通信流程的关键路径上,而每次 API 转发调用都会有两次虚拟机间的控制流来回切换,
因此要想尽量降低虚拟化额外开销,则必须尽量缩短其所消耗的时间.虚拟化额外开销与虚拟机下陷的次数是
强相关的,为了尽可能地消除特权模式和非特权模式间的切换耗时,Wormhole 的设计保证了控制流切换过程中
不会主动触发任何虚拟机下陷,所有配置操作均在非特权模式下完成.
在以 KVM 为代表的虚拟化平台上,非特权模式下对于 CR3 以及 LSTAR MSR 的修改操作会默认下陷到特
权模式中,因此,Wormhole 在第①、②步中采用了添加映射的方式,虽然在地址空间切换的前后虚拟机看到的寄
存器的值保持不变,但是借助地址翻译机制实际访问到了正确的物理内存区域.这样既实现了零下陷的特性,又
达到了与执行特权操作同样的效果.非特权模式下对于 FS.base 和 GS.base 的 MSR 修改默认不会造成下陷,并
且段寄存器的访问机制涉及的内存页数量较多、范围较广,不太适合采用在服务端地址空间添加映射的方法,
故在第④步中发起虚拟机系统调用来临时更换两个 MSR 中的值.
(4) 支持在控制流切换后扩展页表缺页的正确处理.
在扩展页表技术的支持下,现代虚拟化系统对于虚拟机的内存分配请求采用了惰性分配策略,即在客户虚
拟机最初申请一块内存时只在其页表内添加 GVA 到 GPA 的映射,直到该内存区域第 1 次被访问时触发扩展页
表缺页错误才会由虚拟机监视器在扩展页表中补充 GPA 到 HPA 的映射.在代理执行期间如果发生了虚拟机下
陷,从虚拟机监视器的视角来看,当前的下陷仍然来自客户端虚拟机,默认会根据 VMCS 中的相关信息对于客户
端虚拟机执行下陷处理函数,而实际上造成这次下陷的原因来自于服务端虚拟机中,所以必须将处理对象从客
户端虚拟机变更为服务端虚拟机.
Wormhole 的设计是,当发生了扩展页表缺页导致的下陷后,让虚拟机监视器判断是否在代理执行过程中发
生的缺页错误.如果是,则提取客户端虚拟机 VMCS 中缺页错误有关的参数,对于服务端虚拟机执行缺页处理函
数,将 GPA 到 HPA 的映射添加到服务端虚拟机的扩展页表中.
4 Wormhole 原型系统实现
为了验证本加速器虚拟化框架的设计,本文在 Intel 的 x86-64 平台上,基于主流的 Linux 操作系统以
QEMU-KVM 作为虚拟化平台,按照上文的设计对于常用的 NVIDIA GPU 实现了一个加速器虚拟化的原型系
统,支持了 CUDA 9.0 版本.原型系统架构如图 6 所示,具体实现细节如下.