/— layout: post title: “Paper reading - x86 interrupt and SR-IOV interrupts avoidance” date: 2012-01-03 21:37 comments: true

categories: Paper

元旦去奉化享受了,玩得超爽,吃的也超爽!感谢zbd和他爸妈的热情招呼,让我们享受了如此惬意的三天~

好了,言归正传吧,讲讲这篇paper:

    "ELI: Bare-Metal Performance for I/O virtualization"

是由IBM和Technion的学者做的,发表在ASPLOS’12上,按照海波的说法是Muli教授推荐的一篇他们写的paper,现在还没公布出来,拿到的是DRAFT版本,所以不会讲太多的细节,主要是谈谈自己学到的东西吧。

由于这篇paper是自己读的,所以也就看的比较仔细,说实话,看完整篇之后收获真的挺大的,加上之后Muli教授和他的同事对邮件的详细回答让我更加清楚了x86对interrupt的处理,特别是虚拟化环境下的一些细节,加上它最后做出来效果很好,对SR-IOV的优化(特别是万兆网络下小包的效率)提高很大,所以从总体来说我觉得这是一个很有趣而且有用的工作。

先简要介绍下这篇paper的motivation吧:在现在虚拟化的环境中,SR-IOV(single root I/O virtualization)可以说使得整个I/O的效率相对于传统的PV也好,用qemu模拟的HVM也好,提高了一个层次,由于可以将一个特定的device(比如网卡)或者virtual function直接赋予一个domain虚拟机,从而从本质上减少了host(比如xen)在整个I/O路径上的介入时间,同时也减少了相当一部分的内存拷贝操作,使得I/O的performance、latency都得到了提高。但是不管是不是用SR-IOV技术,由于x86体系结构的限制,在由物理设备产生的interrupt发生时还是会陷入host,由host来对interrupt进行第一步的处理,或是直接调用自己的interrupt handler,或是向虚拟机插入软件中断,再调度回虚拟机由虚拟机的interrupt handler处理,在处理完成之后由于对EOI寄存器的写操作还会再一次陷入host;由于这两次的context切换造成的overhead,包括context switch、cache pollution…也就造成了作者在做evaluation时测出的40%的overhead。(其实刚开始看到这个40%我还觉得挺不可思议的,我们实验室之前也对SR-IOV进行过评估,当时得出的结论是SR-IOV的性能很好,和native的环境几乎没有差别。不过后来发现是因为我们的测试环境是千兆,而作者的环境是万兆,而且是小包~~~看来测试环境的优劣还是很重要啊:-))。

baseline interrupt handle

于是乎,这篇paper的作者就提出了ELI(ExitLess Interrupt),其目的就是最大程度地减少由这些interrupt造成的context切换,从而达到性能的最大化。

简单来说,这个问题最大的障碍是以下两点:

    1.x86本身的架构不支持有虚拟机处理特定的硬件中断,也就是说,要不就让所有中断产生时
    直接陷入host,要不就让所有中断都由当前正在运行的虚拟机来处理;
    2.安全问题,如果让虚拟机能够随意直接处理硬件中断,那么会造成很多不可知的安全问题。

对于第一个问题,是由x86的specification定义的,除非改变整个x86架构,否则不会有本质的解决方案。那么,作者又是如何解决这个问题的呢?在这里,就像在virtual memory里的shadow page table一样,作者提出一个shadow IDT的概念。在解释这个shadow IDT之前,我想先介绍下x86虚拟化环境下的中断处理机制:

x86 virtualization interrupt handling

    由xen的机制举例,在整个系统中有两个IDT(Interrupt Descriptor Table),一个IDT由xen
    维护,一个由客户虚拟机维护,在一个硬件中断产生时,不管当前是xen在执行还是虚拟机在执
    行,都会先进入xen的IDT,由中断的vector相对应的handler进行处理,如果发现是由分配给虚
    拟机的硬件产生的中断,则有xen向虚拟机手动插入一个软件中断(software interrupt),当
    之后重新调度回虚拟机时会首先判断中断的flag是否有被置上,如果有某个vector的位被置上
    则先跳入相关的handler进行处理,处理完之后会向LAPIC(之前)或是x2APIC(现在)的相对应
    的EOI寄存器写入处理完成的标志,在写这个寄存器的同时,控制权会再次跳到xen,由xen对此
    进行模拟,告诉真实的硬件这个中断处理完成了。这是一个最简单的硬件处理过程,如果遇到当
    虚拟机在处理一个中断的时候又来了一个硬件中断,则会根据中断的优先级,或者这个中断是不
    是NMI等进行更加复杂的判断,这又是后话了。

那么shadow IDT又是怎么一回事呢?既然x86只支持两种模式的中断处理模式,那么ELI又是如何将特定的硬件中断交给客户虚拟机处理,而将其余的重新让xen进行处理的呢?整个流程简单来说是这样的:

    ELI用的是第二种模式:当有硬件中断产生时,并不是直接陷入xen,而是跳到客户虚拟机的"IDT",
    当然这个IDT不是客户虚拟机本身的IDT,而是由host在客户虚拟机启动时为其配置的shadow IDT,
    在这个IDT中,只有赋予客户虚拟机的设备(比如网卡)对应的中断处理函数是由意义的,而其它
    的entry则是被置上了一个NP(Non-present)位,也就是说,如果这个中断是由正确的中断产生的,
    则直接由客户虚拟机的handler处理,否则会向原来一样陷入xen,只是这次的陷入原因是NP,而不
    是像原来一样的中断原因。而在xen的NP处理时会判断这次的NP是不是由shadow IDT引起的,如果是
    则重新注入软中断,否则则按一般的NP(如page fault)进行处理。而对于EOI的写操作,ELI只支
    持x2APIC机制,因为在这个机制中可以设置相对应的bitmap来指定哪些寄存器的写需要陷入xen,由
    于这是可配置的,所以也就可以控制特定的硬件EOI直接由客户虚拟机处理而不需要陷入xen。这样
    就使得只有特定硬件产生的中断才会直接由客户虚拟机处理,其余的由host处理。但是这样并没有
    解决第二个问题:安全隐患。

对于第二个安全问题,有以下几点:

    1.既然shadow IDT是存在客户虚拟机的地址空间的,那么用户就有能力去篡改里面的值,比如修改
    IDT的virtual-to-physical映射,将自己的恶意IDT映射上去从而实现某些攻击,比如将时间中断
    截获从而让虚拟机不能调度。对于这种攻击,一种方法是shadow所有的virtual-to-physical映射,
    但是这样会造成很大的性能损失,其他的方法是作者提出的preemption timer(在某个可配置的时
    间内强制陷入xen),或者是NMI和IDT limiting的结合(详见paper);
    2.用户可以故意不写EOI寄存器,这个造成的危害是因为x86会自动屏蔽那些优先级小于正在被处理
    的中断的中断,所以它会影响host的interruptibility。对于这个问题,ELI会在每次陷入xen的时
    候,不过客户虚拟机有没有写EOI,就会自动模拟EOI的写操作,如果发现客户虚拟机确实还没有完
    成相对应的中断处理,则会进入injection mode,直到客户虚拟机进行了EOI的写。

总的来说,这篇paper给我最大的收获是熟悉了x86及其虚拟化环境下的interrupt机制,它们用纯软件的方式实现了ExitLess Interrupt,而且得到了很好的评测结果,确实是一篇很有实力的paper。同时还要感谢Muli Ben-Yehuda教授和Nadav Amit学者的热心解答。