KVM虚拟机启动分析

本文详细分析了KVM中VFIO设备虚拟化的启动过程,涉及设备的创建、内存虚拟化、VFIO中断处理和DMA重定向。重点讨论了VFIO如何实现网卡直通的DMA映射,包括vfio_listener_region_add函数在内存拓扑改变时的作用,以及vfio_dma_map通过ioctl调用来完成的DMA重定向。此外,还介绍了vfio驱动的使用、VFIO的内核和QEMU中的架构,以及设备创建和具现化的步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

网卡直通时有什么区别:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

大量的vfio_listener_region_add:
由这个函数完成DMA remapping,即设置设备端的内存视图到QEMU进程虚拟地址之间的映射。
内存虚拟化,每当内存的拓扑逻辑改变时,都会调用注册在AddressSpace上的所有MemoryListener,如果是添加MemoryRegion, 会调用到vfio listener_region_add。
该函数首先判断MemoryRegionSection是否需要建立映射,调用vfo_listener_sipped,只有虚拟机实际的物理内存,也就是对应在QEMU中分配有实际虚拟地址空间的MemoryRegion,才能进行DMA Remapping。
然后算出iova, 实际上就是该MermoryRegionSection在AddressSpace中的起始位置,也就是虚拟机物理内存的地址。 然后算出vaddr, 也就是QEMU分配的虚拟机物理内存的虚拟地址,然后调用 vfio_dma map,在该函数中调用ioctl(VFIO_IOMMU_MAP_DMA)完成iova到vaddr的映射。

1、vfio驱动使用
除了加载驱动,绑定设备等,

modprobe vfio_pci
echo -n "8086 153a" > /sys/bus/pci/drivers/vfio-pci/new_id 

还可以设置能够锁定的内存为虚拟机内存+一些IO空间:

#ulimit -l 2621400     #  ((2048+512)*1024)

这个不知道怎么搭配使用。。。

2、架构分析

vfio内核模块代码(in linux)
drivers/vfio/pci
请添加图片描述

vfio (in qemu-kvm):
/hw/vfio/pci.c请添加图片描述

关系:
请添加图片描述
vfio的接口也分为3类,分别是container / group / device

第一类接口是container 层面的,通过打开“/dev/vfio/vfio“操作,
API有:VFIO_IOMMU_GET_INFO、VFIO_IOMMU_MAP_DMA等:
后者用来指定设备端看到的I0地址到进程的虚拟地址之间的映射,类似于KVM中的KVM_SET_USER_MEMORY_REGION指定虚拟机物理地址到进程虚拟地址之间的映射。

第二类接口是group 层面的,打开/dev/vfio/groupid

第三类接口是设备层面的,其fd是通过VFIO_GROUP_GET_DEVICE_FD接口返回的,device层面的ioctl 包括:
VFIO_DEVICE_GET_REGION_INFO: 用来得到设备的指定Region的数据,这里的region不单单指BAR,还包括ROM空间、PCI配置空间等。
VFIO_DEVICE_GET_IRQ_INFO: 得到设备的中断信息
VFIO DEVICE_ RESET:重置设备

vfio-pci driver:设备绑定vfio-pci驱动,init、probe、创建vfio_device
数据结构,向上提供接口

vfio iommu driver ,是vfio借口和底层iommu之间通信的桥梁,向上接受来自vfio的借口请求,向下利用IOMMU驱动完成DMA重定向功能,代码在:
/drivers/vfio/vfio_iommu_type1.c,将用户参数复制到内核后调用vfio_dma_do_dma来完成物理IOMMU的映射工作

in qemu :负责模拟设备IO地址空间,VFIO中断处理,DMA重定向

设备虚拟化

设备的创建

设备的创建有两种方式:
一是跟随主板初始化一起创建,典型的设备包括南北桥、一些传统的ISA设备、默认的显卡设备等,是通过qdev_create函数-qdev_device_add函数创建的。
二是在命令行通过-device或者在QEMU monitor中通过qdev_device_add函数创建。qdev_device_add首先解析参数,然后找到对应的设备类型,然后调用object_new创建设备。
这两种创建方式本质上都调用了object_new创建对应设备的QOM的对象:

 dev = DEVICE(object_new(type));

然后调用object_property_set_bool将设备的realized属性设置为true,从而实现设备的具现化:

object_property_set_bool(OBJECT(dev), true, "realized", &err);

创建对应设备的QOM的对象:

/**
 * object_property_set_bool:
 * @value: the value to be written to the property
 * @name: the name of the property
 * @errp: returns an error if this function fails
 *
 * Writes a bool value to a property.
 */
void object_property_set_bool(Object *obj, bool value, const char *name, Error **errp);

main(vl.c)
├── device_init_func//(vl.c)
│ ├── qdev_device_add//(qdev_monitor.c)
│ │ ├── dc = qdev_get_device_class(&driver, errp); //(find driver)
│ │ ├── bus = qbus_find(path, errp); // (find bus)
│ │ ├── dev = DEVICE(object_new(driver)); //(create device )
│ │ ├── object_property_set_bool(OBJECT(dev), true, “realized”, &err);//(set properties)
│ │ │ ├── 实际上是去调vfio_realize

vfio_realize(最终定位到vfio_initfn)是VFIO设备的具现函数

qemu/hw/vfio/pci.c
的 最后
type_init(register_vfio_pci_dev_type)
typeinit 是一个通用的抽象的初始化接口

请添加图片描述
type_init(register_vfio_pci_dev_type)//hw/vfio/pci.c
├── type_register_static(&vfio_pci_dev_info);//hw/vfio/pci.c
、、、、、、、、、、、、vfio_pci_dev_info是一个Typeinfo结构体
│ ├── .class_init = vfio_pci_dev_class_init
│ │ ├── pdc->init = vfio_initfn;
│ │ │ ├── vfio_initfn 初始化函数的真正实现

static const TypeInfo vfio_pci_dev_info = {
    .name = "vfio-pci",
    .parent = TYPE_PCI_DEVICE,
    .instance_size = sizeof(VFIOPCIDevice),
    .class_init = vfio_pci_dev_class_init,   //初始化函数 , 即下面
    .instance_init = vfio_instance_init,
    .instance_finalize = vfio_instance_finalize,
};
static void vfio_pci_dev_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    PCIDeviceClass *pdc = PCI_DEVICE_CLASS(kla
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值