网卡直通时有什么区别:
大量的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