目的
本文主要跟踪Linux kerenl中断系统虚拟化方向的演进,
希望能尽量用少的篇幅把整个虚拟化中断说清楚。
读者
本文假设读者已有linux中断的编程经验,熟悉ARM GIC的基本概念和工作原理。
总述
QEMU/KVM中,设备虚拟化有3种方法:纯软件模拟,virtio虚拟和SRIOV物理分配。
本文先集中解析virtio pci虚拟设备的中断,后续再补充SRIOV设备的中断,
不讨论纯软件模拟设备的中断过程。其中1到4章节简单介绍下虚拟化和virtio,
如果只想了解中断部分,可以从第5章节开始。
QEMU/KVM软件堆栈和virtio简介
在介绍中断细节前先介绍下整个系统的组成,再以网络收发包为例介绍中断细节。
QEMU/KVM软件堆栈
系统的部署图如下图:
QEMU中组件关系如下图:
- QEMU Core: 模拟外设、虚拟硬件平台,实现前端设备模拟
- Event Loop:监听后端fd,一旦fd收到event即调用回调函数
- vCPU Thread: 执行guest代码的
- Worker Thread:执行阻塞代码,完成时通知Event Loop
VirtIO关键数据结构关系
VirtIO在guest kernel模拟设备驱动,在qemu中模拟设备。
数据通道上借助内存映射做到guest和host的0内存拷贝通信;
中断方面通过中断注入或者中断透传实现。
其中virtqueue_ops结构体在2.6内核之后已经被去掉,直接使用函数代替;代码位置请以最新内核为准。
vring_virtqueue封装了vring和vritqueue,vring是底层的物理内存实现。
已知virtqueue的话,可以通过container_of找到vring_virtqueue.
VirtIO设备和外界数据交互
交互的流程图如下:
- 虚拟机中,VirtIO设备驱动通过virtqueue的add_buf函数,把request写入到virtqueue中;
- 虚拟机中,VirtIO设备驱动通过virtqueue的kick函数写eventfd(vp_notify),该eventfd会和某块内存绑定;
- host中的QEMU Event Loop监听到该eventfd的signal,执行eventfd的回调函数;
- host中回调函数把虚拟机中的request数据转发给真实物理设备;
- host把真实物理设备response数据通过virtqueue的get_buf写入到virtqueue中;
- QEMU中irqfd接口收到请求,调用回调函数,并通过写内存地址产生中断上报给虚拟机(irqfd的底层实现是eventfd,QEMU中虚拟机初始化的时候,会给这个eventfd挂回调函数);
- 虚拟机中的vgic代码产生vIRQ把上报给GICC;
- 虚拟机收到中断请求,处理response数据。
以virtio net为例,细节如下图:
###组件关系图:
发包流程:
收包流程:
GIC简介
软件主要控制GIC的GICD、GICR和GICC。
ITS和GIC的关系如下,注意LPI可以不经过ITS直接发给GICR,非常重要,虚拟化的伏笔。
GIC虚拟化也是分成GIC和ITS两部分。
- GIC的模拟,并没有模拟全部的GIC部分,只是VGICC部分,当前是在host的kernel中virt/kvm/arm/vgic/vgic-vx.c中实现。
- ITS的模拟,是使用纯软件模拟,当前是在host的kernel中virt/kvm/arm/vgic/vgic-its.c中。
GICv2时代的虚拟机中断处理机制
GICv2的硬件拓扑结构如下:
GICv2中断虚拟化实现细节如下:
GICv3时代的虚拟机中断处理机制
GICv4时代的虚拟机中断处理机制
MSI中断处理
PCIe设备中MSI 配置空间中保存ITS的地址,
- QEMU中virtio PCI模拟该配置空间和能力
- 虚拟机内核PCIe设备初始化的时候,向该配置空间写入ITS或者GICR地址。
- host中QEMU通过向该PCIe设备配置地址写中断消息,从而虚拟机中产生msi中断。