专栏首页虚拟化笔记IOMMU(六)-post interrupt
原创

IOMMU(六)-post interrupt

惠伟:IOMMU(五)-interrupt remmaping​zhuanlan.zhihu.com

post interrupt

post interrupt是intel提供的一种硬件机制,不用物理cpu从root模式exit到non-root模式就能把虚拟中断注入到non-root模式里,大概实现就是把虚拟中断写到post interrupt descriptor,预定义了一个中断号,然后给non-root模式下的cpu发送这个中断,non-root模式下cpu收到这个中断触发对virtual-apic page的硬件模拟,从post interrupt descriptor取出虚拟中断更新到virtual-apic page中,虚拟机中读virtual-access page,就能取到虚拟中断,处理中断,然后写EOI,触发硬件EOI virtulization,就能把virtual-apic page和post interrupt descriptor中数据清除。

post interrupt descriptor的格式如下:

PIR总共256位,一位代表一个虚拟中断。ON代表预先定义的中断已经发出,如果已经为1就不要再发预先定义的中断了。SN代表不要再发中断。NV就是预先定义好的那个中断号,NDST就是物理CPU的local apic id。

vt-x post interrupt

VMCS中增加如下字段控制post interrupt。

kvm用post interrupt模式给vcpu注入中断,kvm修改这个vcpu的post interrupt descriptor,然后给这个vcpu所运行的物理cpu发送中断号是post interrupt notification vector的中断。kvm只用到了post interrupt descriptor中的ON,用到的notification vector存放在VMCS中而不是post interrupt descriptor中,主要是kvm运行在另一个物理cpu上,一个vcpu有没有运行,运行在哪个物理cpu上,这个vcpu可不可以接收中断,kvm很好判断,如果没有运行或者不能接收中断kvm在把虚拟中断存放在其它地方。

vt-d post interrupt

IOMMU硬件单元也可以借用post interrupt机制把passthrough设备产生的中断直接投递到虚拟机中,不需要虚拟机exit出来,不需要VMM软件介入,性能非常高。这种情况设备产生的中断从原来interrupt remapping格式变成post interrupt格式,IRTE内容也变了,它中存放post interrupt descriptor的地址和虚拟中断vector,物理中断到了IOMMU,IOMMU硬件单元直接IRTE中虚拟中断vector写到post interrupt descriptor中pir对应的位,然后给vcpu所在的物理cpu发送一个中断,中断号就是post interrupt descriptor中的NV。

一个passthrough给虚拟机的外设,虚拟机里driver给外设分配虚拟中断,qemu拦截到对外设pci config space的写,然后把虚拟中断更新到kvm的irq routing entry中,kvm再调用update_pi_irte把post interrupt descriptor地址和虚拟中断号更新到IRTE中。

update_pi_irte
  └─irq_set_vcpu_affinity
       └─intel_ir_set_vcpu_affinity
             └─modify_irte

vt-x posted interrupt就是另一个CPU更新了vcpu的post interrupt descriptor,发送一个ipi给vcpu运行的物理CPU。vt-d posted interrupt就是IOMMU硬件单元更新了vcpu的post interrupt descriptor。vt-x和vt-d post interrupt都不会导致vcpu运行的物理CPU从non-root模式exit到root模式,而且能把vcpu的中断注入到guest。但vt-d相比vt-x就弱智多了,一个vcpu有没有运行,运行在哪个物理cpu上,这个vcpu可不可以接收中断,或者vcpu从一个物理cpu迁移到另一个物理cpu,vt-d IOMMU都不能自己判断,只能通过kvm告诉它,所以kvm就把这些信息写到post interrupt descriptor的其它位中,IOMMU来读,这些位就是SN,NDST和NV。

vcpu转换为运行状态,vmx_vcpu_pi_load清除SN,更新NDST。

vcpu_load
  └─preempt_notifier_register

context_switch
  └─finish_task_switch
      └─fire_sched_in_preempt_notifiers
          └─ __fire_sched_in_preempt_notifiers
               └─kvm_sched_in
                   └─kvm_arch_vcpu_load
                      └─kvm_x86_vcpu_load
                           └─vmx_vcpu_pi_load

vcpu暂时挂起,设置SN。

vmx_vcpu_put
    └─vmx_vcpu_pi_put

虚拟机执行hlt指令vcpu暂停,保留原先运行的物理cpu到NDST,设置NV为wakeup vector

vcpu_block
    └─vmx_pre_block
         └─pi_pre_block

如果此时IRTE中URG为1,IOMMU就给物理cpu发送wakeup vector,pi_wakeup_handler让vcpu开始运行。

DEFINE_IDTENTRY_SYSVEC(sysvec_kvm_posted_intr_wakeup_ipi)
{
	ack_APIC_irq();
	inc_irq_stat(kvm_posted_intr_wakeup_ipis);
	kvm_posted_intr_wakeup_handler();
}
kvm_set_posted_intr_wakeup_handler(pi_wakeup_handler);

vcpu开始运行,更新NDST,理想NV为post interrupt vector。

vmx_post_block           
  └─pi_post_block

pv ipi

虚拟机中一个vcpu要向另一个vcpu发送ipi或者向其它vcpu广播ipi,怎么利用post interrupt?

首先源vcpu需要把ipi的目的vcpu的local apic id写到apic寄存器,再写icr寄存器,写icr寄存器就会导致vcpu exit,然后kvm就可以利用vt-x posted interrupt把虚拟中断注入到另一个vcpu。如果是广播ipi,那么源vcpu要exit出来很多次。

kvm_lapic_reg_write->kvm_apic_send_ipi

所以腾讯云李万鹏就想了招,通过hypercall传bitmap一次把所有目的vcpu都传出来,这样源vcpu就可以少exit出来几次。

kvm_emulate_hypercall->kvm_pv_send_ipi

有些虚拟机中的业务会大量用到ipi,导致虚拟机exit出来很多很多次,性能影响太大。有人就想到把源vcpu所运行的物理cpu的lapic的icr寄存器透传给vcpu,把其它vcpu的post interrupt descriptor也透传给虚拟机源vcpu,源vcpu要给目的vcpu发送ipi,源vcpu修改目的vcpu的post interrupt descriptor,源vcpu给真正的硬件寄存器icr写post interrupt notification vector,这样源vcpu不用exit出来。问题就是有点不安全,一个有问题的虚拟机可以频繁给其它物理cpu发送ipi,造成其它物理cpu ipi DDOS攻击,私有云可以用,公有云不行。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • IOMMU(五)-interrupt remmaping

    惠伟:IOMMU(四)-dma remapping​zhuanlan.zhihu.com

    惠伟
  • x86中断和中断虚拟化

    网上有很多写中断虚拟化的博客,讲qemu/kvm是怎么模拟中断的,贴了大段大段的代码,看起来很吃力,对于一个虚拟化新手来说太困难太困难,我这儿写点简单的,讲讲中...

    惠伟
  • 图说ARM架构知识

    ARM架构分为系统架构、安全架构、CPU架构。 ? CPU架构主要是指系统架构上的芯片实现。 安全架构主要是指PSA,前面介绍过。请参考: ARM公司PSA平台...

    安智客
  • Dynamic DMA mapping Guide

    这是一篇指导驱动工程师如何使用DMA API的文档,为了方便理解,文档中给出了伪代码的例程。另外一篇文档dma-api.txt给出了相关API的简明描述,有兴趣...

    Linux阅码场
  • Thread类源码(2)

    jvm.cpp中调用了Thread::interrupt,回到thread.hpp源码的源码:

    黑洞代码
  • Boost::asio io_service 实现分析

    Boost::asio io_service 实现分析 io_service的作用 io_servie 实现了一个任务队列,这里的任务就是void(void)的...

    知然
  • 性能工具之JMeter5.0核心类StandardJMeterEngine源码分析

    JMeter 默认单机压测引擎,运行 JMeter 测试,直接用于本地 GUI 和非 GUI 调用,或者RemoteJMeterEngineImpl 在服务器模...

    高楼Zee
  • 如何阅读JVM 源码

    JDK中JVM(安装在本地C:\Program Files\Java\jdk1.8.0_121\jre\bin\server下jvm.dll)本身并不开源,只能...

    王小明_HIT
  • Docker desktop 与VWare 冲突解决方案

    问题描述: docker desktop 和vware 这俩个工具无法同时使用,如果需要单独使用vware,需要做一些配置才行不然会报错。 报错内容为: V...

    happlyfox

扫码关注云+社区

领取腾讯云代金券