本文讲述一个网络数据包从到达物理网卡,一直到中断注入给VM的整个过程。

为了讲述清晰,假设宿主物理机有两个物理CPU,分别为CPU0和CPU1。假设GuestOS运行在CPU1上,物理网卡接到数据包后把中断请求发送到CPU0.

1.网络数据包Package到达物理网卡NIC, NIC收到数据包后,向CPU0发送中断请求,通知CPU0有网络数据包到达。

2.CPU0收到中断请求后,调用中断处理函数,处理这个中断请求。但是,这个数据包可能是发给VM的,所以这里Host不会直接调用Host的中断处理函数,而是会调用QEMU中实现的Bridge(Brigde就是一个网桥)。Bridge会把请求再转发给QEMU模拟的TAP设备。

3.TAP设备会进行判断这个请求是发给谁的,如果是发给Host的,则直接调用Host的中断处理函数;如果是发给VM的,那么就会TAP就会调用一系列函数,比如select,知道调用到kvm_vm_ioctl(, KVM_IRQ_LINE_STATUS,)。到此为止,QEMU模拟结束,开始进入KVM执行。

4.kvm_set_irq()  (irqchip.c)函数说明

  1. int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,bool line_status){

  2. ...

  3. if (irq < irq_rt->nr_rt_entries)

  4. /*提取中断路由表中对应的中断路由实体,map[irq]是一个对应中断的路由实体表头结点,这里遍历它能够得到所有对应的路由实体。*/

  5. hlist_for_each_entry(e, &irq_rt->map[irq], link)

  6. while(i--) {

  7. int r;

  8. /*触发对应路由实体的触发函数,这个函数在之前的安装中断路由的时候已经注册,注册函数是 <span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px; text-align: -webkit-left; ">setup_routing_entry() </span>*/

  9. r = irq_set[i].set(&irq_set[i], kvm, irq_source_id, level);

  10. if (r < 0)

  11. continue;

  12. ret = r + ((ret < 0) ? 0 : ret);

  13. }

  14. return ret;

  15. }

由 setup_routing_entry() (在irqchip.c中)代码,可知上面ira_set[i].set(..) 调用的就是kvm_set_ioapic_irq().

具体注册路径为:setup_routing_entry() -->kvm_set_routing_entry()-->e->set = kvm_set_ioapic_irq()

5. ioapic_service()说明

  1. static int ioapic_service(struct kvm_ioapic *ioapic, unsigned int idx,

  2. bool line_status)

  3. {

  4. union kvm_ioapic_redirect_entry *pent;

  5. int injected = -1;

  6. /*读取“中断重定向表”*/

  7. pent = &ioapic->redirtbl[idx];

  8. /*检查中断是否被屏蔽,如果被屏蔽,则不触发中断*/

  9. if (!pent->fields.mask) {

  10. /*发送到LAPIC*/

  11. injected = ioapic_deliver(ioapic, idx, line_status);

  12. if (injected && pent->fields.trig_mode == IOAPIC_LEVEL_TRIG)

  13. pent->fields.remote_irr = 1;

  14. }

  15. return injected;

  16. }

6.ioapic_deliver()

这个函数主要功能:读取中断重定向表,设置中断请求的dest_id, vector,dest_mode等请求信息,然后再调用kvm_irq_delivery_to_apic()把中断请求发送给LAPIC

7.kvm_irq_delivery_to_apic()函数

这是一个非常关键的函数! 是一个枢纽函数,无论IOAPIC发给LAPIC中断请求(外部I/O中断),还是LAPIC发个LAPIC中断请求(IPI中断),最后都是调用的这个函数!

这个函数的功能有: 根据传入的参数irq,获取目标LAPIC编号dest_id,再根据dest_id找到其对应的vcpu,最后调用kvm_apic_set_irq() 设置目标LAPIC的中断请求寄存器。

8.__apic_accept_irq()

函数功能:向lapic添加一个Pending  IRQ

首先根据delivery_mode选择添加的方式,基本都会调用下面两个函数:

kvm_make_request(..,vcpu): 这个函数就是向目标vcpu添加一个reqest请求。当目标vcpu再次enter_guest时,check到这个中断请求,进行中断注入,vcpu重新运行,捕获这个中断。

kvm_vcpu_kick(vcpu): 这个函数的功能就是,判断vcpu是否正在物理cpu上运行,如果是,则让vcpu退出,以便进行中断注入。

----------------------------------------

到此为止,kvm模拟的IOAPIC, LAPIC就结束了。下面就是vcpu_enter_guest()进行中断注入的过程了

----------------------------------------

9.vcpu_enter_guest()

当vcpu再次被调度进行vm_entry时,就会执行这个vcpu_enter_guest()函数。

在vcpu run之前,会调用kvm_check_reqest(KVM_REQ_EVENT,vcpu)检查是否有中断请求需要注入。

在上面8中,调用过kvm_make_request(),所以这里if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win)条件判断成立,执行下面的inject_pending_event(vcpu)

10.inject_pending_event()

这个函数的功能就是,把中断请求写入到目标vcpu的中断请求寄存器。最后就是写VMCS中断的irq寄存器。

调用为kvm_x86_ops->set_irq(vcpu),这正调用的就是vmx_inject_irq()

在static struct kvm_x86_ops vmx_x86_ops ={ ..     .set_irq = vmx_inject_irq  }.

vmx_inject_irq() 调用vmx_write32() 写VMCS  的IRQ寄存器。

11. vmx_vcpu_run()

vcpu_enter_guest()最后调用 vmx_vcpu_run(), vcpu开始执行。

在执行前,会读取VMCS中的寄存器信息,由于前面写了IRQ,所以vcpu运行就会知道有中断请求到来,再调用GuestOS的中断处理函数处理中断请求。

----------------------

到此,从一个网络包到达网卡,到qemu 和kvm模拟中断,到中断注入给vcpu,再到vcpu运行发现中断,调用中断处理函数,完整过程大概如此。

----------------------

处理器间IPI中断

IPI中断处理过程要简单的多。

LAPIC中有两个重要的寄存器:

LAPIC_ICR: 存放中断向量

LAPIC_ICR2: 存放中断请求目标

如果一个vcpu0要个vcpu1发IPI中断,vcpu0只要调用apic_reg_write()写LAPIC的这两个寄存器就可以了。

再调apic_sent_ipi()-->kvm_irq_deliver_to_apic()

中断注入
中断注入实际是向客户机CPU注入一个事件,这个事件包括异常和外部中断和NMI。异常我们一般看作为同步,中断被认为异步。
硬件具体实现就中断注入实际就是设置VMCS中字段VM-Entry interruption-infomation字段。中断注入实际在VM运行前完成的,具体代码如下:
static int vcpu_enter_guest(struct kvm_vcpu *vcpu) {
     inject_pending_event(vcpu);
}
vcpu_enter_guest函数运行虚拟机,运行虚拟机代码已省掉。中断注入实际在VM运行前,接下来看看具体如何注入。
static void inject_pending_event(struct kvm_vcpu *vcpu)
{
   if (vcpu->arch.nmi_injected) {
                kvm_x86_ops->set_nmi(vcpu);
                return;
        }

if (vcpu->arch.interrupt.pending) {
                kvm_x86_ops->set_irq(vcpu);
                return;
        }
     /* try to inject new event if pending */
        if (vcpu->arch.nmi_pending) {
                if (kvm_x86_ops->nmi_allowed(vcpu)) {
                        vcpu->arch.nmi_pending = false;
                        vcpu->arch.nmi_injected = true;
                        kvm_x86_ops->set_nmi(vcpu);
                }
        } else if (kvm_cpu_has_interrupt(vcpu)) {
                if (kvm_x86_ops->interrupt_allowed(vcpu)) {
                        kvm_queue_interrupt(vcpu, kvm_cpu_get_interrupt(vcpu),
                                            false);
                        kvm_x86_ops->set_irq(vcpu);
                }
        }

}
首先用户态实现中断控制器,不可屏蔽中断和其他中断注入过程。用户态中断采集在qemu代码中实现
判断是否有等待注入中断,存在话立即注入
接下来内核态模拟的中断控制器,中断注入过程,不可屏蔽中断和其他中断注入过程。
判断KVM内核态是否有不可屏蔽中断,有并且客户机cpu允许中断话,注入中断到客户机cpu中。
判断KVM内核态是否有中断,有中断并且客户机cpu允许中断话,获取优先级高中断进行排队,注入中断到客户机cpu中。
另外一个情况,如果有中断但是客户机不允许中断,只能等待下一下中断注入。如果下一次有更高级别中断发生,该中断还是不能注入而选择更高级别中断注入。

QEMU和KVM 中断处理过程相关推荐

  1. Linux内核设计第五周学习总结 分析system_call中断处理过程

    陈巧然原创作品 转载请注明出处   <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 使用gdb跟踪分析一 ...

  2. 中断处理过程示意图_聊聊什么是中断机制?

    什么是中断 中断其实是一种"中断"事件,中断具体代表什么意思需要考虑它所处的上下文环境和参照对象是谁.考虑事件,我们可以简单把中断抽象为这样一种模型: 当我们分析某种中断事件时,我 ...

  3. x86异常处理与中断机制(3)中断处理过程

    上一节讲完了根据中断类型号找中断服务程序的过程,现在着重说明一下更加完整的中断处理过程吧. 本节以8086时代的中断处理过程为例进行说明,主要分两大部分 硬件处理 软件处理 需要注意,这不是绝对的,得 ...

  4. linux x86 关机 过程,linux在x86上的中断处理过程(详细)

    Linux在x86上的中断处理过程 一:引言 在Intel的文档中,把中断分为两种.一种是异常,也叫同步同断.一种称之为中断,也叫异常中断.同步中断指的是由CPU控制单元产生,之所以称之为同步,是因为 ...

  5. 中断处理过程示意图_中断和中断处理流程

    1. 中断概念 中断是指由于接收到来自外围硬件(相对于中央处理器和内存)的异步信号或来自软件的同步信号,而进行相应的硬件/软件处理.发出这样的信号称为进行中断请求(interrupt request, ...

  6. 实验5 :分析system_call中断处理过程

    分析system_call中断处理过程 上周我们使用gcc内嵌汇编调用系统调用,这次我们具体分析下过程. 将getpid嵌入menuos 代码从github下载,步骤如下: 1. 增加一个函数,get ...

  7. 分析system_call中断处理过程

    分析system_call中断处理过程 上周我们使用gcc内嵌汇编调用系统调用,这次我们具体分析下过程. 将getpid嵌入menuos 代码从github下载,步骤如下: 1. 增加一个函数,get ...

  8. ARM7中断处理过程、状态、嵌套

    ARM7中断处理过程.状态.嵌套 1.ARM7提供两种级别的中断, FIQ(Fast Interrupt Request快中断) 一般中断低速反应中断IRQ. 所有的中断请求一旦产生则中断反应会经过三 ...

  9. :I/O中断处理过程包括哪几个阶段?中断服务程序流程分为哪几部分?

    完整的中断处理过程分为 1)中断响应的事前准备: 系统要想能够应对各种不同的中断信号,总的来看就是需要知道每种信号应该由哪个中断服务程序负责以及这些中断服务程序具体是如何工作的.系统只有事前对这两件事 ...

最新文章

  1. 解决Failed to load class org.slf4j.impl.StaticLoggerBinder
  2. 全局脚手架了解一下【fle-cli】
  3. DeepLearning:模型之间的相互转化(keras-hdf5→Tensorflow-pb文件)
  4. NIPS 2016 | Best Paper, Dual Learning, Review Network, VQA 等论文选介
  5. OpenCASCADE:Modeling Algorithms模块几何工具之插值
  6. ECUG 早鸟票热卖中 | 大咖聚首 探索云计算下一个十年
  7. 《Oracle Life-DBA的一天》海报下载
  8. VB连接ACCESS数据库,使用 LIKE 通配符问题
  9. Photoshop - 新建纯色图层
  10. LCD显示屏和OLED显示屏的区别
  11. 在iOS微信里如何自动播放
  12. 关于台电X16 plus (Tpad)安装win10系统
  13. 计算机网络-自顶向下方法-笔记【第3章-传输层】
  14. mac下sourcetree设置代理
  15. 如何阅读一本专业书?
  16. 强化学习8-时序差分控制离线算法Q-Learning
  17. Windows窗口消息介绍
  18. citus多CN部署
  19. 从零开始做远控 簡介篇 做一个属于你自己的远控
  20. IATF16949认证是ISO9001在汽车行业的运用

热门文章

  1. 小米6发布,雷军亲手终结小米低价时代,低价竞争还能走多远?
  2. C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器
  3. 第三十五课:多普勒效应和宇宙大爆炸
  4. RGB_YUV_YCbCr
  5. C++基础知识(二)--左值右值--逻辑表达式求值优化--逗号运算符与表示式--输入输出格式控制...
  6. 脚手架koa2+mockjs
  7. js实现禁止右键 禁止f12 查看源代码
  8. 年度编程语言最佳候选人:Kotlin vs. C
  9. 全面接入「人脸识别」,智慧城市公共服务的另一种思路
  10. JUnit注解与hamcrest