intel的vt-x技术,让虚拟机大部分指令可以直接运行在CPU中,只有少部分敏感指令需要有VMM来模拟执行。其中,每个CPU的LAPIC接收到的中断是虚拟化的开销一个大头。
LAPIC接收到的中断分为外部中断,内部中断,IPI中断:
外部中断源主要是IO设备,重度使用的IO设备比如有网卡,磁盘控制器等。目前,dpdk,spdk技术在虚拟化中的应用,已经把网络,存储中断减少到了0。
内部中断源包括时钟,性能监控,错误检测,温度传感器。这几个中断,绝大多数日常使用虚拟机情况下发生频率极低。对虚拟化的开销影响很小。
IPI中断是多核CPU系统中CPU彼此通信的唯一方法。主要使用在分布在不同CPU上的进程/线程彼此唤醒的情况中。比较常见的是网络场景,比如有请求到达时唤醒后端网络服务程序,比如:redis,nginx。以及整合了网络服务的别的系统。比如:mysql。
目前公有云中,中断虚拟化中性能瓶颈点落在了IPI中断中。
如何向vcpu注入中断?是通过向真实CPU模拟注入NMI(非可屏蔽中断)中断来实现。
KVM要模拟一个中断控制芯片,这个是通过KVM_CREATE_IRQCHIP来实现的。
实际上注入中断就是写vmcs里面的VM_ENTRY_INTR_INFO_FIELD这个域。然后在vcpu的run函数里面设置cpu进入非根模式,vcpu会自动检查vmcs结构,然后注入中断,这是硬件自动完成的工作。而处理中断,这就是另外一个故事了,不知道后面有没有篇幅和时间继续看下吧。
iommu有两种中断投递格式,未开启remapping时使用compatible format,即传统投中断,开启remapping后就是我们所说的中断重映射场景,使用remapping格式投递。
中断重映射常用模式两种,remappable和posted,虚拟化主要用posted居多(设备直通)
- Present域(P):0b表示此IRTE还没有被分配到任何的中断源,索引到此IRTE的Remapping中断将被blocked掉,1b表示此IRTE是有效的,已经被分配到某个设备。
- Destination Mode域(DM):0b表示Destination ID域为Physical APIC-ID,1b表示Destination ID域是Logical APIC-ID。
- IRTE Mode域(IM):0b表示此中断请求是一个Remapped Interrupt中断请求,1b表示此中断请求是一个Posted Interrupt中断请求。
- Vector域(V):共8个Byte表示了此Remapped Interrupt中断请求的vector号(Remapped Interrupt)。
- Destination ID域(DST):表示此中断请求的目标CPU,根据当前Host中断方式不同具有不同的格式。xAPIC Mode (Cluster)为bit[40:47], xAPIC Mode (Flat)和xAPIC Mode (Physical)为bit[47:40], x2APIC Mode (Cluster)和x2APIC Mode (Physical)为bit[31:0]。
- SID, SQ, SVT则联合起来表示了中断请求的设备PCI/PCI-e request-id信息。
Posted
中断发布
Interrupt-posting是中断重映射功能的一个扩展功能,该功能也是针对的可重映射中断请求。Interrupt-posting功能让一个可重映射的中断请求能够被暂时以一定的数据形式post(记录)到物理内存中,并且可选择性地主动告知CPU物理内存中暂时记录着pending的中断请求。经Interrupt-posting扩展后的中断处理过程如下图。
VT-d重映射硬件是否支持Interrupt-posting功能可以通过查询Capability Register的bit 59 “Posted Interrupt Support(PI)”是否为1判断。对于VT-d而言,所有可重映射中断都需要经过IRTE的处理,在进行处理之前会先通过IRTE的bit 15(IRTE Mode)判断该IRTE的模式,如果为0,则VT-d硬件将会以Remapped Interrupt的形式来解析该IRTE,如果为1,则VT-d硬件将会以Posted Interrupt的形式来解析该IRTE,格式如下图所示:
与Remapped Interrupt相比,Posted Interrupt格式的IRTE的定义有一些不同,主要是:
■ 多了一个Posted Descriptor Address Low/High,该区域保存一个指向内存的指针,该指针指向的位置就是保存中断(Posted Interrupt)的结构体。
■ Urgent位,该位用于表示该中断是否是紧急的,是否需要目标CPU的立即响应。
■ Vector用于设置Posted Interrupt Descriptor数据结构中相应的PIR位,是虚拟中断向量。
每个Posted Interrupt Descriptor的大小为64 Byte,用于记录VT-d硬件那边post过来的中断,即在内存中暂时记录一些中断请求,等待CPU的处理,而不是主动打断CPU的运行,让其跳转过来处理,其格式如下所示:
■ Posted Interrupt Requests (PIR),一共256 bit,每个bit对应一个中断向量(Vector),当VT-d硬件将中断请求post过来的时候,中断请求中相应的vector对应的bit将会被置起来(对应于IRTE中的Vector字段,是被查表变换后的虚拟中断向量)。
■ Outstanding Notification(ON),表示对于该Posted Interrupt Descriptor当前是否已经发出了一个Notification Event等待CPU的处理。当VT-d硬件将中断请求记录到PIR的时候,如果ON为0,并且允许立即发出一个Notification Event时,则将会将ON置1,并且产生一个Notification Event;如果ON已经被置1,则VT-d硬件不做其他动作。
■ Suppress Notification(SN),表示当PIR寄存器记录到non-urgent的中断时,是否不发出Notification Event,如果该位为1,则当PIR记录到中断的时候,则不发出Notification Event,并且不更改Outstanding Notification位的值。
■ Notification Vector(NV)表示如果发出Notification Event时,具体的物理Vector值(物理中断向量,由VMM这类软件设置)。
■ Notification Destination(NDST),表示若产生Notification Event时,传递的目标逻辑CPU的LAPIC ID(系统中以逻辑CPU的LAPIC ID来表示具体的逻辑CPU,BIOS/UEFI其初始化系统的时候,会为每个逻辑CPU分配一个唯一的LAPIC ID)。
Posted Interrupt Descriptor
从软件的角度来看,VMM可能会对Interrupt Posting做如下设置和操作:
■ 对于每个vCPU而言,VMM都会分配一个对应的Posted Interrupt Descriptor用于记录和传递经过重定向,并且目的地为对应vCPU的所有中断请求。
■ VMM软件为所有的Notification Event分配两个物理中断vector:
● 第一个称作Active Notification Vector(ANV),该Vector对应到当中断目标vCPU当前正在被逻辑CPU执行(即vCPU的状态为active)时,Notification Event所使用的中断vector。
● 第二个称作Wake-up Notification Vector(WNV),该Vector对应到中断目标vCPU当前不在逻辑CPU上被执行时,由于Urgent被置起来产生的Notification Event所使用的中断Vector。
■ 对于从直接分配给VM的I/O设备产生的中断而言,VMM会为每个这样的中断分配一个IRTE。并且VMM可能会为vCPU使能硬件上的APIC Virtualization,APIC Virtualization主要包括两方面功能:virtual-interrupt delivery和process posted-interrupts,其主要工作形式表现在:
● 当一个vCPU被VMM调度即将执行的时候,该vCPU的状态为active,该状态的一个表现形式是VMM会将Posted Interrupt Descriptor的Notification Vector字段设置为ANV(Active Notification Vector)。这样就允许当这个vCPU在逻辑CPU上执行的时候,所有指向该vCPU的中断都能够直接被该vCPU处理,不需要经过VMM。vCPU通过将记录在Posted Interrupt Descriptor中的中断直接转移到Virtual-APIC Page中,并直接将中断信号传递给vCPU,让vCPU直接获取到该中断信号的方式来处理Notification Event。
● 当一个vCPU被抢占后,即vCPU的状态为ready-to-run,该状态的一个表现形式是VMM会将Posted Interrupt Descriptor的Notification Vector字段设置为WNV(Wake-up Notification Vector),并且SN(Suppress Notification)设置为1。只有当接收到的中断请求为Urgent的时候,才会发出Notification Event,触发VMM的执行,让VMM调度目标vCPU处理该紧急中断。
● 当一个vCPU处于Halt状态的时候,逻辑CPU执行VMM软件,VMM将该vCPU标记为halted状态。该状态的一个表现形式就是将Posted Interrupt Descriptor的Notification Vector字段设置为WNV(Wake-up Notification Vector),并且SN(Suppress Notification)设置为0,即任何到达该Posted Interrupt Descriptor的中断请求都会触发Notification Event,让VMM唤醒vCPU,让vCPU处理中断请求。
■ 当VMM调度并执行一个vCPU的时候,VMM会对被记录到Posted Interrupt Descriptor的中断请求进行处理:
● 首先,VMM会通过将Posted Interrupt Descriptor的Notification Vector字段的值改为ANV将vCPU的状态变为active。
● VMM检测在Posted Interrupt Descriptor中是否有待处理的中断请求。
● 如果有待处理的中断请求,则VMM会给当前CPU发送一个sefl-IPI中断(即CPU自己给自己发送一个中断),并且中断向量值为ANV。当vCPU一使能中断的时候,就能够立马识别到该中断。该中断的处理类似于vCPU处于active状态时,接收到了Active Notification Vector的中断请求,vCPU可以直接对其进行处理,不需要VMM的参与。
■ VMM也可以利用Posted Interrupt的处理机制,通过设置Posted Interrupt Descriptor向vCPU注入中断请求。
PS:Interrupt Posting的一个前提是CPU支持posted-interrupt processing。posted-interrupt下,当虚拟中断芯片需要注入中断时,其将中断信息更新到Posted Interrupt Descriptor中,然后虚拟中断芯片向CPU发送一个通知posted-interrupt notification,处于Guest模式的CPU通过VMCS记录的virtual-APIC page收到这个中断后,运行Guest的硬件中断评估逻辑,直接响应中断。这个中断通知并不特殊,就是一个常规的IPI,但是核间中断向量是专有的,目的CPU收到这个IPI后,不再触发VM exit,而是去处理被虚拟中断芯片写在Posted Interrupt Descriptor中的中断。
设备透传场景下,RC上iommu的Interrupt Posting和CPU的posted-interrupt相结合后,iommu代替KVM的virtual LAPIC,负责更新目标Guest的Posted Interrupt Descriptor。
Interrupt Posting功能在虚拟化环境中,能够防止外部中断频繁打断vCPU的运行,提高系统的性能。
虚拟中断寄存器页面(virtual-APIC page)
在APIC中,物理LAPIC有一个页面大小的内存用来存放各寄存器的值,Intel称这个页面为APIC-access page,CPU采用mmap的方式访问这些寄存器。起初,一旦Guest试图访问这个页面,CPU将从Guest模式切换到Host模式,KVM负责完成模拟,将Guest写给LAPIC的值写入虚拟LAPIC的APIC page,或者从虚拟LAPIC的APIC page读入值给Guest。但是很快开发者就发现,因为频繁地访问某些寄存器,导致Guest和Host之间频繁的切换,而这些大量的切换带来很大的性能损失。为了减少VM exit,Intel设计了一个所谓的virtual-APIC page来替代APIC-access page。CPU在Guest模式下使用这个virtual-APIC page来维护寄存器的状态,当Guest读寄存器时,直接读virtual-APICpage,不必再切换到Host模式。但是因为在写某些寄存器时,可能要伴随着一些副作用,比如需要发送一个IPI,所以在写寄存器时,还需要触发CPU状态的切换。那么Guest模式下的CPU从哪里找到virtual-APIC page呢?显然,我们再次想到了VMCS。VMX在VMCS中设计了一个字段VIRTUAL_APIC_PAGE_ADDR,在切入Guest模式前,KVM需要将virtual APIC page的地址记录在VMCS的这个字段中:
linux.git/arch/x86/kvm/vmx.c
static int vmx_vcpu_reset(struct kvm_vcpu *vcpu)
{
…
vmcs_write64(VIRTUAL_APIC_PAGE_ADDR,
__pa(vmx->vcpu.arch.apic->regs));
…
}
DMAR表组织结构
LAPIC地址访问方式
x86 CPU中一共有三种访问APIC的方式:
通过CR8来访问TPR寄存器(仅IA32-e即64位模式)
通过MMIO来访问APIC的寄存器,这是xAPIC模式提供的访问方式
通过MSR来访问APIC的寄存器,这是x2APIC模式提供的访问方式