Linux内核:VFIO Mediated Device(vfio-mdev)内核文档(翻译)【转】

时间:2021-01-07 12:01:57   收藏:0   阅读:0

转自:https://blog.csdn.net/rong_toa/article/details/110845945

ARM SMMU原理与IOMMU技术(“VT-d” DMA、I/O虚拟化、内存虚拟化)

提升KVM异构虚拟机启动效率:透传(pass-through)、DMA映射(VFIO、PCI、IOMMU)、virtio-balloon、异步DMA映射、预处理

内核引导参数IOMMU与INTEL_IOMMU有何不同?

DMAR(DMA remapping)与 IOMMU

Linux驱动:VFIO概述(vfio/iommu/device passthrough)

Linux内核:VFIO 内核文档 (翻译)

Linux内核:VFIO Mediated Device(vfio-mdev)内核文档(翻译)

目录

1. Virtual Function I/O (VFIO) Mediated devices[1]

2. Registration(注册)接口

物理设备驱动接口

3. Mediated设备管理接口sysfs

mdev_bus类目录中的链接

sysfs下每个物理设备的目录和文件

mdev设备的热插拔

4. mdev的转换(Translation)API

5. 简单代码样例

参考


原文为vfio mediated device内核文档[5],我翻译一下。

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 core driver也提供注册总线驱动的接口。比如,一个mediated VFIO mdev驱动就是为mediated devices设计的,并且支持VFIO的API。Mediated bus driver可以将一个mediated device加入或者移出一个VFIO group。

以下的上层图展示了VFIO mediated driver框架的主要组件和接口。这张图展示了NVIDIA、Intel和IBM设备,因为这些设备是首先使用这些模块的。

  1.  
    +---------------+
  2.  
    | |
  3.  
    | +-----------+ | mdev_register_driver() +--------------+
  4.  
    | | | +<------------------------+ |
  5.  
    | | mdev | | | |
  6.  
    | | bus | +------------------------>+ vfio_mdev.ko |<-> VFIO user
  7.  
    | | driver | | probe()/remove() | | APIs
  8.  
    | | | | +--------------+
  9.  
    | +-----------+ |
  10.  
    | |
  11.  
    | MDEV CORE |
  12.  
    | MODULE |
  13.  
    | mdev.ko |
  14.  
    | +-----------+ | mdev_register_device() +--------------+
  15.  
    | | | +<------------------------+ |
  16.  
    | | | | | nvidia.ko |<-> physical
  17.  
    | | | +------------------------>+ | device
  18.  
    | | | | callbacks +--------------+
  19.  
    | | Physical | |
  20.  
    | | device | | mdev_register_device() +--------------+
  21.  
    | | interface | |<------------------------+ |
  22.  
    | | | | | i915.ko |<-> physical
  23.  
    | | | +------------------------>+ | device
  24.  
    | | | | callbacks +--------------+
  25.  
    | | | |
  26.  
    | | | | mdev_register_device() +--------------+
  27.  
    | | | +<------------------------+ |
  28.  
    | | | | | ccw_device.ko|<-> physical
  29.  
    | | | +------------------------>+ | device
  30.  
    | | | | callbacks +--------------+
  31.  
    | +-----------+ |
  32.  
    +---------------+

 

2. Registration(注册)接口


Mediated core driver提供了如下类型的注册接口:

Mediated总线驱动注册接口

为mediated总线驱动设计的注册接口提供了如下接口来表示mediated设备的驱动:

  1.  
    /*
  2.  
    * struct mdev_driver [2] - Mediated device‘s driver
  3.  
    * @name: driver name
  4.  
    * @probe: called when new device created
  5.  
    * @remove: called when device removed
  6.  
    * @driver: device driver structure
  7.  
    */
  8.  
    struct mdev_driver {
  9.  
    const char *name;
  10.  
    int (*probe) (struct device *dev);
  11.  
    void (*remove) (struct device *dev);
  12.  
    struct device_driver driver;
  13.  
    };

一个mdev的dediated总线驱动应该在函数调用中使用这个结构来从mediated core driver中注册和注销(unregister)他自己:

  1.  
    extern int mdev_register_driver(struct mdev_driver *drv,
  2.  
    struct module *owner);
extern void mdev_unregister_driver(struct mdev_driver *drv);

这个mediated总线驱动是负责从VFIO group中添加(设备bound时)和删除(设备unbound时)mediated设备(mdev)。

 

物理设备驱动接口


物理设备驱动接口提供了mdev_parent_ops[3]结构来定义API,用于管理mediated core driver中和物理设备相关的工作。

The structures in the mdev_parent_ops structure are as follows:

mdev_parent_ops结构中的数据结构如下:

The functions in the mdev_parent_ops structure are as follows:

mdev_parent_ops结构中的函数如下:

(Note that mdev-core provides no implicit serialization of create/remove
callbacks per mdev parent device, per mdev type, or any other categorization.
Vendor drivers are expected to be fully asynchronous in this respect or
provide their own internal resource protection.)

(注意,mdev-core不为每个mdev parent device、每个mdev类型或者任何其他配置提供create/remove回调的隐式序列化。提供商驱动被期望完全得同步或者提供他们自己的内部资源保护。)

mdev_parent_ops结构中的回调如下:

一个驱动应该用在注册到mdev core driver时用mdev_parent_ops这个结构

  1.  
    extern int mdev_register_device(struct device *dev,
  2.  
    const struct mdev_parent_ops *ops);

However, the mdev_parent_ops structure is not required in the function call
that a driver should use to unregister itself with the mdev core driver::

但是,mdev_parent_ops结构在从mdev core driver注销时并不需要:

extern void mdev_unregister_device(struct device *dev);

 

3. Mediated设备管理接口sysfs


The management interface through sysfs enables user space software, such as
libvirt, to query and configure mediated devices in a hardware-agnostic fashion.
This management interface provides flexibility to the underlying physical
device’s driver to support features such as:

管理接口是通过sysfs来让用户态软件(如libvirt)进行请求和配置mdevs的,这种管理是一种硬件无关的形式。这种管理接口给底层硬件设备驱动提供了灵活的特性支持,比如:

 

mdev_bus类目录中的链接


/sys/class/mdev_bus/这个目录包含到已注册到mdev core driver设备的链接。

 

sysfs下每个物理设备的目录和文件


  1.  
    |- [parent physical device]
  2.  
    |--- Vendor-specific-attributes [optional]
  3.  
    |--- [mdev_supported_types]
  4.  
    | |--- [<type-id>]
  5.  
    | | |--- create
  6.  
    | | |--- name
  7.  
    | | |--- available_instances
  8.  
    | | |--- device_api
  9.  
    | | |--- description
  10.  
    | | |--- [devices]
  11.  
    | |--- [<type-id>]
  12.  
    | | |--- create
  13.  
    | | |--- name
  14.  
    | | |--- available_instances
  15.  
    | | |--- device_api
  16.  
    | | |--- description
  17.  
    | | |--- [devices]
  18.  
    | |--- [<type-id>]
  19.  
    | |--- create
  20.  
    | |--- name
  21.  
    | |--- available_instances
  22.  
    | |--- device_api
  23.  
    | |--- description
  24.  
    | |--- [devices]
  25.  
     

其中:

sprintf(buf, "%s-%s", dev_driver_string(parent->dev), group->name);

(或者用mdev_parent_dev(mdev)来访问core mdev代码外的parent device)

  1.  
     
  2.  
    ### sysfs下每个mdev设备的目录和文件
  3.  
     
  4.  
    ```bash
  5.  
    |- [parent phy device]
  6.  
    |--- [$MDEV_UUID]
  7.  
    |--- remove
  8.  
    |--- mdev_type {link to its type}
  9.  
    |--- vendor-specific-attributes [optional]

其中:

例子:

echo 1 > /sys/bus/mdev/devices/$mdev_UUID/remove

mdev设备的热插拔


mdev设备可以在运行时进行创建和绑定。热插拔mdev的步骤和热插拔PCI设备的步骤相同。

 

4. mdev的转换(Translation)API


以下API用于提供在VFIO驱动中从User PFN到Host PFN的转换:

  1.  
    extern int vfio_pin_pages(struct device *dev, unsigned long *user_pfn,
  2.  
    int npage, int prot, unsigned long *phys_pfn);
  3.  
     
  4.  
    extern int vfio_unpin_pages(struct device *dev, unsigned long *user_pfn,
  5.  
    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中的设备目录如下:

  1.  
    # tree /sys/devices/virtual/mtty/mtty/
  2.  
    /sys/devices/virtual/mtty/mtty/
  3.  
    |-- mdev_supported_types
  4.  
    | |-- mtty-1
  5.  
    | | |-- available_instances
  6.  
    | | |-- create
  7.  
    | | |-- device_api
  8.  
    | | |-- devices
  9.  
    | | `-- name
  10.  
    | `-- mtty-2
  11.  
    | |-- available_instances
  12.  
    | |-- create
  13.  
    | |-- device_api
  14.  
    | |-- devices
  15.  
    | `-- name
  16.  
    |-- mtty_dev
  17.  
    | `-- sample_mtty_dev
  18.  
    |-- power
  19.  
    | |-- autosuspend_delay_ms
  20.  
    | |-- control
  21.  
    | |-- runtime_active_time
  22.  
    | |-- runtime_status
  23.  
    | `-- runtime_suspended_time
  24.  
    |-- subsystem -&gt; ../../../../class/mtty
  25.  
    `-- uevent

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

  1.  
    # echo "83b8f4f2-509f-382f-3c1e-e6bfe0fa1001" &gt; \
  2.  
    /sys/devices/virtual/mtty/mtty/mdev_supported_types/mtty-2/create

Step 3 为qemu-kvm添加如下参数:

  1.  
    -device vfio-pci,\
  2.  
    sysfsdev=/sys/bus/mdev/devices/83b8f4f2-509f-382f-3c1e-e6bfe0fa1001
  3.  
     

Step 4 启动虚拟机

In the Linux guest VM, with no hardware on the host, the device appears
as follows::

在Linux为GuestOS的虚拟机中,设备会被这样显示:

  1.  
    # lspci -s 00:05.0 -xxvv
  2.  
    00:05.0 Serial controller: Device 4348:3253 (rev 10) (prog-if 02 [16550])
  3.  
    Subsystem: Device 4348:3253
  4.  
    Physical Slot: 5
  5.  
    Control: I/O+ Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr-
  6.  
    Stepping- SERR- FastB2B- DisINTx-
  7.  
    Status: Cap- 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium &gt;TAbort-
  8.  
    &lt;TAbort- &lt;MAbort- &gt;SERR- &lt;PERR- INTx-
  9.  
    Interrupt: pin A routed to IRQ 10
  10.  
    Region 0: I/O ports at c150 [size=8]
  11.  
    Region 1: I/O ports at c158 [size=8]
  12.  
    Kernel driver in use: serial
  13.  
    00: 48 43 53 32 01 00 00 02 10 02 00 07 00 00 00 00
  14.  
    10: 51 c1 00 00 59 c1 00 00 00 00 00 00 00 00 00 00
  15.  
    20: 00 00 00 00 00 00 00 00 00 00 00 00 48 43 53 32
  16.  
    30: 00 00 00 00 00 00 00 00 00 00 00 00 0a 01 00 00

在Linux为GuestOS的虚拟机中,dmesg会有如下输出:

  1.  
    serial 0000:00:05.0: PCI INT A -&gt; Link[LNKA] -&gt; GSI 10 (level, high) -&gt; IRQ 10
  2.  
    0000:00:05.0: ttyS1 at I/O 0xc150 (irq = 10) is a 16550A
  3.  
    0000:00:05.0: ttyS2 at I/O 0xc158 (irq = 10) is a 16550A

Step 5 在Linux为guestOS的虚拟机中,查看串口设备:

  1.  
    # setserial -g /dev/ttyS*
  2.  
    /dev/ttyS0, UART: 16550A, Port: 0x03f8, IRQ: 4
  3.  
    /dev/ttyS1, UART: 16550A, Port: 0xc150, IRQ: 10
  4.  
    /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

参考


评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!