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