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);
    

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

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

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

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

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