首页 > GPU虚拟化, 虚拟技术 > VFIO Mediated Device学习摘要

VFIO Mediated Device学习摘要

2022年1月30日 发表评论 阅读评论 253 次浏览

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 -&gt; ../../../../class/mtty
`-- uevent

Step 2 用这个dummy设备创建一个mdev设备:

# echo "83b8f4f2-509f-382f-3c1e-e6bfe0fa1001" &gt; \
/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 &gt;TAbort-
&lt;TAbort- &lt;MAbort- &gt;SERR- &lt;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 -&gt; Link[LNKA] -&gt; GSI 10 (level, high) -&gt; 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/3014http://lenky.info/?p=3014


备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。

法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。

分类: GPU虚拟化, 虚拟技术 标签:
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.