VFIO 调解设备

版权:

© 2016, NVIDIA CORPORATION. 保留所有权利。

作者:

Neo Jia <cjia@nvidia.com>

作者:

Kirti Wankhede <kwankhede@nvidia.com>

虚拟功能 I/O (VFIO) 调解设备[1]

对不具备内置 SR_IOV 功能的 DMA 设备进行虚拟化的用例数量正在增加。以前,要虚拟化此类设备,开发人员必须创建自己的管理接口和 API,然后将其与用户空间软件集成。为了简化与用户空间软件的集成,我们确定了此类设备的通用需求和统一管理接口。

VFIO 驱动程序框架为直接设备访问提供了统一的 API。它是一个 IOMMU/设备无关的框架,用于在安全、受 IOMMU 保护的环境中向用户空间公开直接设备访问。此框架用于多种设备,例如 GPU、网络适配器和计算加速器。通过直接设备访问,虚拟机或用户空间应用程序可以直接访问物理设备。此框架被重用于调解设备。

调解核心驱动程序为调解设备管理提供了一个通用接口,可供不同设备的驱动程序使用。此模块提供了一个通用接口来执行这些操作

  • 创建和销毁调解设备

  • 将调解设备添加到调解总线驱动程序并从中删除

  • 将调解设备添加到 IOMMU 组并从中删除

调解核心驱动程序还提供了注册总线驱动程序的接口。例如,调解的 VFIO mdev 驱动程序专为调解设备设计,并支持 VFIO API。调解的总线驱动程序将调解设备添加到 VFIO 组并从中删除。

以下高级框图显示了 VFIO 调解驱动程序框架中的主要组件和接口。该图显示了 NVIDIA、Intel 和 IBM 设备作为示例,因为这些设备是第一个使用此模块的设备

+---------------+
|               |
| +-----------+ |  mdev_register_driver() +--------------+
| |           | +<------------------------+              |
| |  mdev     | |                         |              |
| |  bus      | +------------------------>+ vfio_mdev.ko |<-> VFIO user
| |  driver   | |     probe()/remove()    |              |    APIs
| |           | |                         +--------------+
| +-----------+ |
|               |
|  MDEV CORE    |
|   MODULE      |
|   mdev.ko     |
| +-----------+ |  mdev_register_parent() +--------------+
| |           | +<------------------------+              |
| |           | |                         | ccw_device.ko|<-> physical
| |           | +------------------------>+              |    device
| |           | |        callbacks        +--------------+
| | Physical  | |
| |  device   | |  mdev_register_parent() +--------------+
| | interface | |<------------------------+              |
| |           | |                         |  i915.ko     |<-> physical
| |           | +------------------------>+              |    device
| |           | |        callbacks        +--------------+
| +-----------+ |
+---------------+

注册接口

调解核心驱动程序提供以下类型的注册接口

  • 调解总线驱动程序的注册接口

  • 物理设备驱动程序接口

调解总线驱动程序的注册接口

调解设备驱动程序的注册接口提供以下结构来表示调解设备的驱动程序

/*
 * 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);
        unsigned int (*get_available)(struct mdev_type *mtype);
        ssize_t (*show_description)(struct mdev_type *mtype, char *buf);
        struct device_driver    driver;
};

mdev 的调解总线驱动程序应在函数调用中使用此结构,以向核心驱动程序注册和注销自身

  • 注册

    int mdev_register_driver(struct mdev_driver *drv);
    
  • 注销

    void mdev_unregister_driver(struct mdev_driver *drv);
    

调解的总线驱动程序的探测函数应在 mdev_device 之上创建一个 vfio_device,并将其连接到 vfio_device_ops 的适当实现。

当驱动程序想要将 GUID 创建 sysfs 添加到它已探测的现有设备时,它应调用

int mdev_register_parent(struct mdev_parent *parent, struct device *dev,
                    struct mdev_driver *mdev_driver);

这将提供 'mdev_supported_types/XX/create' 文件,然后可以使用这些文件来触发 mdev_device 的创建。创建的 mdev_device 将附加到指定的驱动程序。

当驱动程序需要删除自身时,它会调用

void mdev_unregister_parent(struct mdev_parent *parent);

这将取消绑定并销毁所有已创建的 mdev,并删除 sysfs 文件。

通过 sysfs 的调解设备管理接口

通过 sysfs 的管理接口使用户空间软件(如 libvirt)能够以硬件无关的方式查询和配置调解设备。此管理接口为底层物理设备的驱动程序提供了灵活性,以支持以下功能:

  • 调解设备热插拔

  • 单个虚拟机中的多个调解设备

  • 来自不同物理设备的多个调解设备

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

|- [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]

    当前支持的调解设备类型及其详细信息的列表。

    [<type-id>]、device_api 和 available_instances 是供应商驱动程序应提供的强制属性。

  • [<type-id>]

    [<type-id>] 名称是通过将设备驱动程序字符串作为前缀添加到供应商驱动程序提供的字符串来创建的。此名称的格式如下

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

    此属性显示正在创建哪个设备 API,例如,PCI 设备的“vfio-pci”。

  • available_instances

    此属性显示可以创建的 <type-id> 类型设备的数量。

  • [device]

    此目录包含指向已创建的 <type-id> 类型设备的链接。

  • name

    此属性显示人类可读的名称。

  • description

    此属性可以显示该类型的简要功能/描述。这是一个可选属性。

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

|- [parent phy device]
|--- [$MDEV_UUID]
       |--- remove
       |--- mdev_type {link to its type}
       |--- vendor-specific-attributes [optional]
  • remove(只写)

将“1”写入“remove”文件会销毁 mdev 设备。如果该设备处于活动状态并且供应商驱动程序不支持热插拔,则供应商驱动程序可能会使 remove() 回调失败。

示例

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

调解设备热插拔

可以在运行时创建和分配调解设备。热插拔调解设备的过程与热插拔 PCI 设备的过程相同。

调解设备的转换 API

为在 VFIO 驱动程序中将用户 pfn 转换为主机 pfn 提供了以下 API

int vfio_pin_pages(struct vfio_device *device, dma_addr_t iova,
                          int npage, int prot, struct page **pages);

void vfio_unpin_pages(struct vfio_device *device, dma_addr_t iova,
                            int npage);

这些函数通过使用 struct vfio_iommu_driver_ops[4] 的 pin_pages 和 unpin_pages 回调来回调到后端 IOMMU 模块。目前,TYPE1 IOMMU 模块支持这些回调。要为其他 IOMMU 后端模块(如 PPC64 sPAPR 模块)启用它们,它们需要提供这两个回调函数。

参考资料

  1. 有关 VFIO 的更多信息,请参阅 VFIO - “虚拟功能 I/O”

  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