VFIO Mediated Device学习摘要
Mediated Device(vfio-mdev)是Linux里用来支持Intel GVT-g,NVIDIA vGPU的重要组件,找了几篇文章看看,Mark下重点。
找到一篇VFIO Mediated Device(vfio-mdev)内核文档的译文,转载下:http://blog.jcix.top/2019-04-25/vfio-mdev-doc/
—
1. Virtual Function I/O (VFIO) Mediated devices[1]
对没有内置SR-IOV功能的设备进行DMA虚拟化的需求越来越多。以前,为了虚拟化一个这样的设备,开发者需要自己开发管理接口和API,然后把它们集成到用户态应用中。为了简化这种用户空间软件集成,我们找出了这种设备公共需求然后开发了一种统一的接口。
VFIO驱动框架为直接设备访问提供了统一的API。它将设备直接访问安全地(以一种IOMMU保护的环境)暴露给用户,是一种IOMMU/设备无关的框架。此框架用于多种设备,如GPU、网卡和计算加速器等。有了这种直接设备访问,虚拟机或者用户态应用可以直接访问物理设备。Mdeiated devices便是重用了VFIO这种框架。
Mediated core driver为mdiated device提供了一个公共的管理接口,它可以被不同类型的设备驱动所利用。这个模块提供了的通用接口可以进行如下操作:
* 创建和删除mediated device
* 把一个mediated deivce加入或移出某个总线驱动
* 把一个mediated device加入或移出某个IOMMU group
Mediated core driver也提供注册总线驱动的接口。比如,一个mediated VFIO mdev驱动就是为mediated devices设计的,并且支持VFIO的API。Mediated bus driver可以将一个mediated device加入或者移出一个VFIO group。
以下的上层图展示了VFIO mediated driver框架的主要组件和接口。这张图展示了NVIDIA、Intel和IBM设备,因为这些设备是首先使用这些模块的。
+---------------+ | | | +-----------+ | mdev_register_driver() +--------------+ | | | +<------------------------+ | | | mdev | | | | | | bus | +------------------------>+ vfio_mdev.ko |<-> VFIO user | | driver | | probe()/remove() | | APIs | | | | +--------------+ | +-----------+ | | | | MDEV CORE | | MODULE | | mdev.ko | | +-----------+ | mdev_register_device() +--------------+ | | | +<------------------------+ | | | | | | nvidia.ko |<-> physical | | | +------------------------>+ | device | | | | callbacks +--------------+ | | Physical | | | | device | | mdev_register_device() +--------------+ | | interface | |<------------------------+ | | | | | | i915.ko |<-> physical | | | +------------------------>+ | device | | | | callbacks +--------------+ | | | | | | | | mdev_register_device() +--------------+ | | | +<------------------------+ | | | | | | ccw_device.ko|<-> physical | | | +------------------------>+ | device | | | | callbacks +--------------+ | +-----------+ | +---------------+
2. Registration(注册)接口
Mediated core driver提供了如下类型的注册接口:
* mediated总线驱动的注册接口
* 物理设备驱动接口
Mediated总线驱动注册接口
为mediated总线驱动设计的注册接口提供了如下接口来表示mediated设备的驱动:
/* * struct mdev_driver [2] - Mediated device's driver * @probe: called when new device created * @remove: called when device removed * @driver: device driver structure */ struct mdev_driver { int (*probe) (struct mdev_device *dev); void (*remove) (struct mdev_device *dev); struct device_driver driver; };
一个mdev的dediated总线驱动应该在函数调用中使用这个结构来从mediated core driver中注册和注销(unregister)他自己:
* 注册:
extern int mdev_register_driver(struct mdev_driver *drv);
* 注销:
extern void mdev_unregister_driver(struct mdev_driver *drv);
这个mediated总线驱动是负责从VFIO group中添加(设备bound时)和删除(设备unbound时)mediated设备(mdev)。
物理设备驱动接口
物理设备驱动接口提供了mdev_parent_ops[3]结构来定义API,用于管理mediated core driver中和物理设备相关的工作。
mdev_parent_ops结构中的数据结构如下:
* dev_attr_groups: parent device的属性
* mdev_attr_groups: mediated device的属性
* supported_config: 定义所支持配置的属性
mdev_parent_ops结构中的函数如下:
* create: 在driver中为一个mdev分配基本的资源
* remove: 当一个mdev被销毁时在driver中free掉相关资源
注意,mdev-core不为每个mdev parent device、每个mdev类型或者任何其他配置提供create/remove回调的隐式序列化。提供商驱动被期望完全得同步或者提供他们自己的内部资源保护。
mdev_parent_ops结构中的回调如下:
* open: 打开mdev的回调
* close: 关闭mdev的回调
* ioctl: mdev的ioctl回调 callback of mediated device
* read : read模拟回调
* write: write模拟回调
* mmap: mmap模拟回调
一个驱动应该用在注册到mdev core driver时用mdev_parent_ops这个结构
extern int mdev_register_device(struct device *dev, const struct mdev_parent_ops *ops);
但是,mdev_parent_ops结构在从mdev core driver注销时并不需要:
extern void mdev_unregister_device(struct device *dev);
3. Mediated设备管理接口sysfs
管理接口是通过sysfs来让用户态软件(如libvirt)进行请求和配置mdevs的,这种管理是一种硬件无关的形式。这种管理接口给底层硬件设备驱动提供了灵活的特性支持,比如:
* mdev的热插拔
* 多个mdev在一个虚拟机中
* 来自不同物理设备的多个mdev
mdev_bus类目录中的链接
/sys/class/mdev_bus/这个目录包含到已注册到mdev core driver设备的链接。
|- [parent physical device] |--- Vendor-specific-attributes [optional] |--- [mdev_supported_types] | |--- [<type-id>] | | |--- create | | |--- name | | |--- available_instances | | |--- device_api | | |--- description | | |--- [devices] | |--- [<type-id>] | | |--- create | | |--- name | | |--- available_instances | | |--- device_api | | |--- description | | |--- [devices] | |--- [<type-id>] | |--- create | |--- name | |--- available_instances | |--- device_api | |--- description | |--- [devices]
其中:
* [mdev_supported_types]:列出了当前支持的mdev的种类(mediated device type)和详细信息,type-id, device_api, 和available_instances应给被厂商驱动提供的信息:
* type-id: type-id这个名字是用设备驱动字符串作为厂商驱动字符串前缀的名。这个名字的格式如下:
sprintf(buf, "%s-%s", dev_driver_string(parent->dev), group->name);
(或者用mdev_parent_dev(mdev)来访问core mdev代码外的parent device)
* device_api:这个属性用来指明什么设备API被创建了,比如,”vfio-pci”是给PCI设备用的。
* available_instances:这个属性展示了可以被创建的type-id类设备数。
* [device]:这个目录包含了到被创建的type-id设备的链接。
* name:展示了人类能看懂的名字,此项可选。
* description:展示简单说明的类型特性、描述,此项可选
### sysfs下每个mdev设备的目录和文件 ```bash |- [parent phy device] |--- [$MDEV_UUID] |--- remove |--- mdev_type {link to its type} |--- vendor-specific-attributes [optional]
其中:
* remove (只写):往其中写”1″会销毁mdev设备。如果设备处于活跃状态并且厂商的驱动不支持热插拔,那么厂商的驱动可以让remove回调失败。
例子:
echo 1 > /sys/bus/mdev/devices/$mdev_UUID/remove
mdev设备的热插拔
mdev设备可以在运行时进行创建和绑定。热插拔mdev的步骤和热插拔PCI设备的步骤相同。
4. mdev的转换(Translation)API
以下API用于提供在VFIO驱动中从User PFN到Host PFN的转换:
extern int vfio_pin_pages(struct device *dev, unsigned long *user_pfn, int npage, int prot, unsigned long *phys_pfn); extern int vfio_unpin_pages(struct device *dev, unsigned long *user_pfn, int npage);
这些函数会回调后端IOMMU模块(struct vfio_iommu_driver_ops[4]结构中的pin_pages函数和unpin_pages函数)。当前这些回调在TYPE1 IOMMU模块中被支持。其他IOMMU后端模块中(如PPC64 sPAPR模块)若想支持,他们就需要提过这两个回调函数的实现。
5. 简单代码样例
samples/vfio-mdev/文件夹中的mtty.c是一个展示mdev框架怎么用的简单驱动程序。
这个简单驱动创建了一个mdev设备来模拟一个PCI串口设备。
Step 1 创建和加载mtty.ko模块:
这步会创建一个dummy设备/sys/devices/virtual/mtty/mtty/
sysfs中的设备目录如下:
# tree /sys/devices/virtual/mtty/mtty/ /sys/devices/virtual/mtty/mtty/ |-- mdev_supported_types | |-- mtty-1 | | |-- available_instances | | |-- create | | |-- device_api | | |-- devices | | `-- name | `-- mtty-2 | |-- available_instances | |-- create | |-- device_api | |-- devices | `-- name |-- mtty_dev | `-- sample_mtty_dev |-- power | |-- autosuspend_delay_ms | |-- control | |-- runtime_active_time | |-- runtime_status | `-- runtime_suspended_time |-- subsystem -> ../../../../class/mtty `-- uevent
Step 2 用这个dummy设备创建一个mdev设备:
# echo "83b8f4f2-509f-382f-3c1e-e6bfe0fa1001" > \ /sys/devices/virtual/mtty/mtty/mdev_supported_types/mtty-2/create
Step 3 为qemu-kvm添加如下参数:
-device vfio-pci,\ sysfsdev=/sys/bus/mdev/devices/83b8f4f2-509f-382f-3c1e-e6bfe0fa1001
Step 4 启动虚拟机
在Linux为GuestOS的虚拟机中,设备会被这样显示:
# lspci -s 00:05.0 -xxvv 00:05.0 Serial controller: Device 4348:3253 (rev 10) (prog-if 02 [16550]) Subsystem: Device 4348:3253 Physical Slot: 5 Control: I/O+ Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap- 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Interrupt: pin A routed to IRQ 10 Region 0: I/O ports at c150 [size=8] Region 1: I/O ports at c158 [size=8] Kernel driver in use: serial 00: 48 43 53 32 01 00 00 02 10 02 00 07 00 00 00 00 10: 51 c1 00 00 59 c1 00 00 00 00 00 00 00 00 00 00 20: 00 00 00 00 00 00 00 00 00 00 00 00 48 43 53 32 30: 00 00 00 00 00 00 00 00 00 00 00 00 0a 01 00 00
在Linux为GuestOS的虚拟机中,dmesg会有如下输出:
serial 0000:00:05.0: PCI INT A -> Link[LNKA] -> GSI 10 (level, high) -> IRQ 10 0000:00:05.0: ttyS1 at I/O 0xc150 (irq = 10) is a 16550A 0000:00:05.0: ttyS2 at I/O 0xc158 (irq = 10) is a 16550A
Step 5 在Linux为guestOS的虚拟机中,查看串口设备:
# setserial -g /dev/ttyS* /dev/ttyS0, UART: 16550A, Port: 0x03f8, IRQ: 4 /dev/ttyS1, UART: 16550A, Port: 0xc150, IRQ: 10 /dev/ttyS2, UART: 16550A, Port: 0xc158, IRQ: 10
Step 6 用minicom或者其他终端模拟程序,打开串口/dev/ttyS1或者/dev/ttyS2并禁用硬件流控制:
Step 7 向minicom终端打字或者发数据给终端模拟程序并读数据
数据会在host mtty驱动回显。
Step 8 销毁所创建的mdev:
# echo 1 > /sys/bus/mdev/devices/83b8f4f2-509f-382f-3c1e-e6bfe0fa1001/remove
参考
[1] 内核中的VFIO文档Documentation/vfio.txt有VFIO的更多信息,我也在以前的博客中翻译过。
[2] include/linux/mdev.h文件中的struct mdev_driver结构
[3] include/linux/mdev.h文件中的struct mdev_parent_ops结构
[4] include/linux/vfio.h文件中的struct vfio_iommu_driver_ops结构
[5] https://github.com/torvalds/linux/blob/master/Documentation/vfio-mediated-device.txt
参考2:
http://blog.jcix.top/2019-04-25/vfio-mdev-doc/
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/driver-api/vfio-mediated-device.rst
https://blog.csdn.net/isclouder/article/details/79392625
https://blog.sciencenet.cn/blog-279072-1035996.html
http://www.linux-kvm.org/images/f/f3/01x08b-KVMGT-a.pdf
https://www.usenix.org/conference/atc14/technical-sessions/presentation/tian
https://blog.csdn.net/Rong_Toa/article/details/110845945
转载请保留地址:http://www.lenky.info/archives/2022/01/3014 或 http://lenky.info/?p=3014
备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来信讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。
法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。