Linux內核:VFIO Mediated Device(vfio-mdev)內核文檔(翻譯)【轉】

轉自: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 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設備,因爲這些設備是首先使用這些模塊的。

  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總線驅動設計的註冊接口提供瞭如下接口來表示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結構中的數據結構如下:

  • dev_attr_groups: parent device的屬性
  • mdev_attr_groups: mediated device的屬性
  • supported_config: 定義所支持配置的屬性

The functions in the mdev_parent_ops structure are as follows:

mdev_parent_ops結構中的函數如下:

  • create: 在driver中爲一個mdev分配基本的資源
  • remove: 當一個mdev被銷燬時在driver中free掉相關資源

(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結構中的回調如下:

  • open: 打開mdev的回調
  • close: 關閉mdev的回調
  • ioctl: mdev的ioctl回調 callback of mediated device
  • read : read模擬回調
  • write: write模擬回調
  • mmap: mmap模擬回調

一個驅動應該用在註冊到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的熱插拔
  • 多個mdev在一個虛擬機中
  • 來自不同物理設備的多個mdev

 

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.  
     

其中:

  • [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:展示簡單說明的類型特性、描述,此項可選
  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]

其中:

  • 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的轉換:

  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

參考


  • [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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章