vfio-ccw: 基礎架構

簡介

本文件描述了 Linux/s390 的 I/O 子通道裝置的 vfio 支援。 vfio-ccw 的動機是將子通道傳遞給虛擬機器,而 vfio 是手段。

與其他硬體架構不同,s390 定義了一種統一的 I/O 訪問方法,即所謂的通道 I/O。 它有自己的訪問模式

  • 通道程式在單獨的(協)處理器上非同步執行。

  • 通道子系統將直接訪問通道程式中呼叫者指定的任何記憶體,即不涉及 iommu。

因此,當我們為這些裝置引入 vfio 支援時,我們透過中介裝置 (mdev) 實現它。 vfio mdev 將被新增到 iommu 組,以便能夠被 vfio 框架管理。 我們為特殊的 vfio I/O 區域新增讀/寫回調,以將通道程式從中介裝置傳遞到其父裝置(真正的 I/O 子通道裝置),以進行進一步的地址轉換並執行 I/O 指令。

本文件無意詳細解釋 s390 I/O 架構。 更多資訊/參考可以在這裡找到

  • 瞭解通道 I/O 的一個好的開始:https://en.wikipedia.org/wiki/Channel_I/O

  • s390 架構:s390 操作原理手冊 (IBM Form. No. SA22-7832)

  • 現有的 QEMU 程式碼實現了一個簡單的模擬通道子系統,也可以作為一個很好的參考。 它使更容易跟蹤流程。 qemu/hw/s390x/css.c

對於 vfio 中介裝置框架:- VFIO 中介裝置

vfio-ccw 的動機

通常,在 s390 上透過 QEMU/KVM 虛擬化的訪客只能透過“基於通道 I/O 的 Virtio (virtio-ccw)”傳輸看到半虛擬化的 virtio 裝置。 這使得 virtio 裝置可以透過用於處理通道裝置的標準作業系統演算法來發現。

然而,這還不夠。 在 s390 上,對於大多數使用基於標準通道 I/O 機制的裝置,我們還需要提供將它們傳遞到 QEMU 虛擬機器的能力。 這包括沒有 virtio 對應物的裝置(例如磁帶驅動器)或具有訪客想要利用的特定特徵的裝置。

為了將裝置傳遞給訪客,我們希望使用與其他所有人相同的介面,即 vfio。 我們透過 vfio 中介裝置框架和子通道裝置驅動程式“vfio_ccw”為通道裝置實現此 vfio 支援。

CCW 裝置的訪問模式

s390 架構實現了一個所謂的通道子系統,它提供了物理連線到系統的裝置的統一檢視。 雖然 s390 硬體平臺知道各種不同的外圍附件,例如磁碟裝置(又名 DASD)、磁帶、通訊控制器等。 它們都可以透過明確定義的訪問方法訪問,並且它們以統一的方式呈現 I/O 完成:I/O 中斷。

所有 I/O 都需要使用通道命令字 (CCW)。 CCW 是對專用 I/O 通道處理器的指令。 通道程式是由 I/O 通道子系統執行的 CCW 序列。 為了向通道子系統發出通道程式,需要構建一個操作請求塊 (ORB),該塊可用於指出 CCW 的格式和其他控制資訊給系統。 作業系統向 I/O 通道子系統發出訊號,開始執行具有 SSCH(啟動子通道)指令的通道程式。 然後中央處理器可以自由地繼續執行非 I/O 指令,直到中斷。 I/O 完成結果以中斷響應塊 (IRB) 的形式被中斷處理程式接收。

回到 vfio-ccw,簡而言之

  • ORB 和通道程式在訪客核心中構建(使用訪客物理地址)。

  • ORB 和通道程式被傳遞到主機核心。

  • 主機核心將訪客物理地址轉換為真實地址,並透過發出特權通道 I/O 指令(例如 SSCH)啟動 I/O。

  • 通道程式在單獨的處理器上非同步執行。

  • I/O 完成將透過 I/O 中斷向主機發出訊號。 它將作為 IRB 複製到使用者空間,以將其傳遞迴訪客。

物理 vfio ccw 裝置及其子中介裝置

如上所述,我們透過中介裝置實現 vfio-ccw。

通道 I/O 沒有 IOMMU 硬體支援,因此物理 vfio-ccw 裝置沒有 IOMMU 級別的轉換或隔離。

子通道 I/O 指令都是特權指令。 在處理 I/O 指令攔截時,vfio-ccw 具有軟體策略和轉換,通道程式在傳送到硬體之前是如何程式設計的。

在此實現中,我們有兩個驅動程式用於兩種型別的裝置

  • 物理子通道裝置的 vfio_ccw 驅動程式。 這是真實子通道裝置的 I/O 子通道驅動程式。 它實現了一組回撥並註冊到中介裝置框架作為父(物理)裝置。 因此,中介裝置為 vfio_ccw 提供了一個通用介面 (sysfs) 來建立中介裝置。 然後可以透過 vfio_ccw 建立 vfio 中介裝置並新增到中介匯流排。 它是新增到 IOMMU 組和 vfio 組的 vfio 裝置。 vfio_ccw 還提供了一個 I/O 區域來接受來自使用者空間的通道程式請求,並存儲 I/O 中斷結果供使用者空間檢索。 為了通知使用者空間 I/O 完成,它提供了一個介面來設定 eventfd fd 以進行非同步訊號傳遞。

  • 中介 vfio ccw 裝置的 vfio_mdev 驅動程式。 這是由中介裝置框架提供的。 它是 vfio_ccw 建立的中介裝置的 vfio 裝置驅動程式。 它實現了一組 vfio 裝置驅動程式回撥,將其自身新增到 vfio 組,並將其自身註冊到中介裝置框架作為中介裝置驅動程式。 它使用 vfio iommu 後端,該後端使用現有的 map 和 unmap ioctl,但不是將它們程式設計到裝置的 IOMMU 中,而是簡單地儲存轉換以供以後的請求使用。 這意味著在 VM 中使用訪客物理地址程式設計的裝置可以使 vfio 核心將該地址轉換為程序虛擬地址,固定頁面並在一個步驟中用主機物理地址程式設計硬體。 對於中介裝置,vfio iommu 後端不會在 VFIO_IOMMU_MAP_DMA ioctl 期間固定頁面。 中介裝置框架將僅在此操作中維護 iova<->vaddr 對映的資料庫。 它們從 vfio iommu 後端匯出一個 vfio_pin_pages 和一個 vfio_unpin_pages 介面,供物理裝置按需固定和取消固定頁面。

以下是高階框圖

+-------------+
|             |
| +---------+ | mdev_register_driver() +--------------+
| |  Mdev   | +<-----------------------+              |
| |  bus    | |                        | vfio_mdev.ko |
| | driver  | +----------------------->+              |<-> VFIO user
| +---------+ |    probe()/remove()    +--------------+    APIs
|             |
|  MDEV CORE  |
|   MODULE    |
|   mdev.ko   |
| +---------+ | mdev_register_parent() +--------------+
| |Physical | +<-----------------------+              |
| | device  | |                        |  vfio_ccw.ko |<-> subchannel
| |interface| +----------------------->+              |     device
| +---------+ |       callback         +--------------+
+-------------+

這些如何協同工作的過程。

  1. vfio_ccw.ko 驅動物理 I/O 子通道,並將物理裝置(具有回撥)註冊到中介裝置框架。 當 vfio_ccw 探測子通道裝置時,它將裝置指標和回撥註冊到中介裝置框架。 將在 sysfs 中裝置節點下為子通道裝置建立與中介裝置相關的檔案節點,即“mdev_create”、“mdev_destroy”和“mdev_supported_types”。

  2. 建立一箇中介 vfio ccw 裝置。 使用“mdev_create”sysfs 檔案,我們需要手動建立一個(並且在我們這裡只有一個)中介裝置。

  3. vfio_mdev.ko 驅動中介 ccw 裝置。 vfio_mdev 也是 vfio 裝置驅動程式。 它將探測中介裝置並將其新增到 iommu_group 和 vfio_group。 然後我們可以將中介裝置傳遞給訪客。

VFIO-CCW 區域

vfio-ccw 驅動程式公開 MMIO 區域以接受來自使用者空間的請求並將結果返回給使用者空間。

vfio-ccw I/O 區域

I/O 區域用於接受來自使用者空間的通道程式請求,並存儲 I/O 中斷結果供使用者空間檢索。 區域的定義是

struct ccw_io_region {
#define ORB_AREA_SIZE 12
        __u8    orb_area[ORB_AREA_SIZE];
#define SCSW_AREA_SIZE 12
        __u8    scsw_area[SCSW_AREA_SIZE];
#define IRB_AREA_SIZE 96
        __u8    irb_area[IRB_AREA_SIZE];
        __u32   ret_code;
} __packed;

此區域始終可用。

在啟動 I/O 請求時,orb_area 應該填充訪客 ORB,scsw_area 應該填充虛擬子通道的 SCSW。

irb_area 儲存 I/O 結果。

ret_code 儲存區域每次訪問的返回程式碼。 可能出現以下值

0

操作成功。

-EOPNOTSUPP

ORB 指定的傳輸模式或 SCSW 指定的函式不是啟動函式。

-EIO

當裝置未處於接受請求的就緒狀態時發出請求,或者發生內部錯誤。

-EBUSY

子通道狀態掛起或忙碌,或者請求已處於活動狀態。

-EAGAIN

請求正在處理中,呼叫者應該重試。

-EACCES

發現用於 I/O 的通道路徑無法執行。

-ENODEV

發現裝置無法執行。

-EINVAL

orb 指定的鏈長於 255 個 ccw,或者發生內部錯誤。

vfio-ccw cmd 區域

vfio-ccw cmd 區域用於接受來自使用者空間的非同步指令

#define VFIO_CCW_ASYNC_CMD_HSCH (1 << 0)
#define VFIO_CCW_ASYNC_CMD_CSCH (1 << 1)
struct ccw_cmd_region {
       __u32 command;
       __u32 ret_code;
} __packed;

此區域透過區域型別 VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD 公開。

目前,CLEAR SUBCHANNEL 和 HALT SUBCHANNEL 使用此區域。

command 指定要發出的命令; ret_code 儲存區域每次訪問的返回程式碼。 可能出現以下值

0

操作成功。

-ENODEV

發現裝置無法執行。

-EINVAL

指定的命令不是 halt 或 clear。

-EIO

當裝置未處於接受請求的就緒狀態時發出請求。

-EAGAIN

請求正在處理中,呼叫者應該重試。

-EBUSY

在處理 halt 請求時,子通道狀態掛起或忙碌。

vfio-ccw schib 區域

vfio-ccw schib 區域用於將子通道資訊塊 (SCHIB) 資料返回給使用者空間

struct ccw_schib_region {
#define SCHIB_AREA_SIZE 52
       __u8 schib_area[SCHIB_AREA_SIZE];
} __packed;

此區域透過區域型別 VFIO_REGION_SUBTYPE_CCW_SCHIB 公開。

讀取此區域會觸發 STORE SUBCHANNEL 以發出到關聯的硬體。

vfio-ccw crw 區域

vfio-ccw crw 區域用於將通道報告字 (CRW) 資料返回給使用者空間

struct ccw_crw_region {
       __u32 crw;
       __u32 pad;
} __packed;

此區域透過區域型別 VFIO_REGION_SUBTYPE_CCW_CRW 公開。

如果存在與此子通道相關的 CRW(例如,報告通道路徑狀態變化的 CRW),則讀取此區域將返回 CRW,如果不是,則返回全零。 如果有多個 CRW 掛起(包括可能連結的 CRW),則再次讀取此區域將返回下一個 CRW,直到沒有更多 CRW 掛起並返回零。 這類似於 STORE CHANNEL REPORT WORD 的工作方式。

vfio-ccw 操作細節

vfio-ccw 遵循 vfio-pci 在 s390 平臺上所做的工作,並使用 vfio-iommu-type1 作為 vfio iommu 後端。

  • CCW 轉換 API 一組 API(以 cp_ 開頭)用於進行 CCW 轉換。 使用者空間程式傳入的 CCW 使用其訪客物理記憶體地址進行組織。 這些 API 將 CCW 複製到核心空間,並透過使用相應的主機物理地址更新訪客物理地址來組裝一個可執行的核心通道程式。 請注意,即使對於直接訪問 CCW,我們也必須使用 IDAL,因為引用的記憶體可以位於任何位置,包括 2G 以上。

  • vfio_ccw 裝置驅動程式 此驅動程式利用 CCW 轉換 API 並引入 vfio_ccw,它是您想要傳遞的 I/O 子通道裝置的驅動程式。 vfio_ccw 實現以下 vfio ioctl

    VFIO_DEVICE_GET_INFO
    VFIO_DEVICE_GET_IRQ_INFO
    VFIO_DEVICE_GET_REGION_INFO
    VFIO_DEVICE_RESET
    VFIO_DEVICE_SET_IRQS
    

    這提供了一個 I/O 區域,以便使用者空間程式可以將通道程式傳遞給核心,以便在將它們發出到真實裝置之前進行進一步的 CCW 轉換。 這還提供了 SET_IRQ ioctl 來設定事件通知器,以非同步方式通知使用者空間程式 I/O 完成。

vfio-ccw 的使用不限於 QEMU,而 QEMU 絕對是瞭解這些補丁如何工作的一個好例子。 這裡稍微詳細介紹一下由 QEMU 訪客觸發的 I/O 請求將如何處理(沒有錯誤處理)。

說明

  • Q1-Q7:QEMU 側過程。

  • K1-K5:核心側過程。

Q1。

在初始化期間獲取 I/O 區域資訊。

Q2。

設定事件通知器和處理程式以處理 I/O 完成。

... ...

Q3。

攔截 ssch 指令。

Q4。

將訪客通道程式和 ORB 寫入 I/O 區域。

K1。

從訪客複製到核心。

K2。

將訪客通道程式轉換為主機核心空間通道程式,該程式對於真實裝置變得可執行。

K3。

使用 QEMU 傳入的 orb 中包含的必要資訊,將 ccwchain 發出到裝置。

K4。

返回 ssch CC 程式碼。

Q5。

將 CC 程式碼返回給訪客。

... ...

K5。

中斷處理程式獲取 I/O 結果並將結果寫入 I/O 區域。

K6。

向 QEMU 發出訊號以檢索結果。

Q6。

獲取訊號並且事件處理程式從 I/O 區域讀取結果。

Q7。

更新訪客的 irb。

侷限性

當前的 vfio-ccw 實現側重於支援實現 DASD/ECKD 裝置的塊裝置功能(讀/寫)所需的基本命令。 將來某些命令可能需要特殊處理,例如,任何與路徑分組相關的內容。

DASD 是一種儲存裝置。 而 ECKD 是一種資料記錄格式。 有關 DASD 和 ECKD 的更多資訊可以在這裡找到:https://en.wikipedia.org/wiki/Direct-access_storage_device https://en.wikipedia.org/wiki/Count_key_data

與 QEMU 中的相應工作相結合,我們現在可以將傳遞的 DASD/ECKD 裝置線上引入訪客,並將其用作塊裝置。

當前程式碼允許訪客透過 START SUBCHANNEL 啟動通道程式,併發出 HALT SUBCHANNEL、CLEAR SUBCHANNEL 和 STORE SUBCHANNEL。

目前,所有通道程式都是預取的,無論 ORB 中 p-bit 的設定如何。 因此,不支援自修改通道程式。 因此,IPL 必須由使用者空間/訪客程式作為特殊情況處理; 這已在 QEMU 4.1 的 QEMU 的 s390-ccw bios 中實現。

vfio-ccw 僅支援經典(命令模式)通道 I/O。 不支援傳輸模式 (HPF)。

目前不支援 QDIO 子通道。 除了 DASD/ECKD 之外的經典裝置可能可以工作,但尚未經過測試。

參考

  1. ESA/s390 操作原理手冊 (IBM Form. No. SA22-7832)

  2. ESA/390 通用 I/O 裝置命令手冊 (IBM Form. No. SA22-7204)

  3. https://en.wikipedia.org/wiki/Channel_I/O

  4. Linux for S/390 和 zSeries

  5. VFIO - “虛擬功能 I/O”

  6. VFIO 中介裝置