適用於 S/390 和 zSeries 的 Linux

通用裝置支援 (CDS) 裝置驅動程式 I/O 支援例程

作者
  • Ingo Adlung

  • Cornelia Huck

版權所有,IBM Corp. 1999-2002

簡介

本文件介紹了 Linux/390 的通用裝置支援例程。與其他硬體架構不同,ESA/390 定義了一種統一的 I/O 訪問方法。這減輕了裝置驅動程式的負擔,因為他們不必處理不同的匯流排型別、輪詢與中斷處理、共享與非共享中斷處理、DMA 與埠 I/O (PIO) 以及其他更多硬體功能。但是,這意味著要麼每個裝置驅動程式都需要自己實現硬體 I/O 連線功能,要麼作業系統提供一種統一的方法來訪問硬體,從而提供每個裝置驅動程式都需要提供的所有功能。

本文件並不打算詳細解釋 ESA/390 硬體架構的每個細節。這些資訊可以從 ESA/390 操作原理手冊(IBM 表單編號 SA22-7201)中獲得。

為了為 ESA/390 I/O 介面構建通用裝置支援,引入了一個功能層,該層為硬體提供通用 I/O 訪問方法。

通用裝置支援層包括下面定義的 I/O 支援例程。 其中一些實現了通用的 Linux 裝置驅動程式介面,而另一些是 ESA/390 平臺特有的。

注意

為了為 S/390 編寫驅動程式,您還需要檢視 S/390 驅動程式模型介面 中描述的介面。

從 2.4 移植驅動程式的注意事項

主要變化是

  • 這些函式使用 ccw_device 而不是 irq(子通道)。

  • 所有驅動程式都必須定義一個 ccw_driver(參見 S/390 驅動程式模型介面)和關聯的函式。

  • request_irq()free_irq() 不再由驅動程式完成。

  • oper_handler 被 ccw_driver 的 probe() 和 set_online() 函式(某種程度上)取代。

  • not_oper_handler 被 ccw_driver 的 remove() 和 set_offline() 函式(某種程度上)取代。

  • 通道裝置層已消失。

  • 必須調整中斷處理程式以使用 ccw_device 作為引數。 此外,它們不返回 devstat,而是返回 irb。

  • 在啟動 io 之前,必須透過 ccw_device_set_options() 設定選項。

  • 驅動程式發出通道程式並處理中斷本身,而不是呼叫 read_dev_chars()/read_conf_data()。

ccw_device_get_ciw()

從擴充套件感知資料獲取命令。

ccw_device_start()、ccw_device_start_timeout()、ccw_device_start_key()、ccw_device_start_key_timeout()

啟動 I/O 請求。

ccw_device_resume()

恢復通道程式執行。

ccw_device_halt()

終止裝置上處理的當前 I/O 請求。

do_IRQ()

通用中斷例程。 每當向系統呈現 I/O 中斷時,中斷入口例程都會呼叫此函式。 do_IRQ() 例程確定中斷狀態,並根據使用 do_IO() 啟動 I/O 請求期間定義的規則(標誌)呼叫特定於裝置的中斷處理程式。

接下來的章節將更詳細地描述 do_IRQ() 以外的函式。 由於 do_IRQ() 僅從 Linux/390 第一級中斷處理程式呼叫,並且不包括裝置驅動程式可呼叫介面,因此未對其進行描述。 相反,do_IO() 的功能描述也描述了裝置特定中斷處理程式的輸入。

注意

所有解釋也適用於 64 位架構 s390x。

Linux/390 裝置驅動程式的通用裝置支援 (CDS)

常規資訊

以下章節描述了 Linux/390 通用裝置支援 (CDS) 提供的 I/O 相關介面例程,以便在 IBM ESA/390 硬體平臺上實現特定於裝置的驅動程式實現。 這些介面旨在提供每個裝置驅動程式實現所需的功能,以允許在 ESA/390 平臺上驅動特定的硬體裝置。 一些介面例程是 Linux/390 特有的,而另一些介面例程也可以在其他 Linux 平臺實現中找到。 各種函式原型、資料宣告和宏定義可以在架構特定的 C 標頭檔案 linux/arch/s390/include/asm/irq.h 中找到。

CDS 介面概念概述

與其他硬體平臺不同,ESA/390 架構沒有定義由特定中斷控制器管理的中斷線路和可能允許或不允許共享中斷、DMA 處理等的匯流排系統。相反,ESA/390 架構實現了一個所謂的通道子系統,該子系統提供了物理連線到系統的裝置的統一檢視。儘管 ESA/390 硬體平臺知道各種不同的外圍裝置連線,例如磁碟裝置(又名 DASD)、磁帶、通訊控制器等,但所有這些裝置都可以透過定義明確的訪問方法訪問,並且它們以統一的方式呈現 I/O 完成:I/O 中斷。每個裝置都透過所謂的子通道向系統唯一標識,其中 ESA/390 架構允許連線 64k 個裝置。

然而,Linux 最初是在 Intel PC 架構上構建的,它具有兩個級聯的 8259 可程式設計中斷控制器 (PIC),最多允許 15 條不同的中斷線路。 連線到此類系統的所有裝置共享這 15 箇中斷級別。 連線到 ISA 匯流排系統的裝置不得共享中斷級別(又名 IRQ),因為 ISA 匯流排基於邊沿觸發中斷。 MCA、EISA、PCI 和其他匯流排系統基於電平觸發中斷,因此允許共享 IRQ。 但是,如果多個裝置透過相同的(共享)IRQ 呈現其硬體狀態,則作業系統必須呼叫在此 IRQ 上註冊的每個裝置驅動程式,以確定擁有引發中斷的裝置的裝置驅動程式。

在核心 2.4 之前,Linux/390 過去透過 IRQ(子通道)提供介面。 對於通用 I/O 層的內部使用,這些仍然存在。 但是,裝置驅動程式應該僅透過 ccw_device 使用新的呼叫介面。

在其啟動期間,Linux/390 系統會檢查外圍裝置。 每個裝置都由 ESA/390 通道子系統透過所謂的子通道唯一定義。 雖然子通道號是系統生成的,但每個子通道也採用使用者定義的屬性,即所謂的裝置號。 子通道號和裝置號都不能超過 65535。在 sysfs 初始化期間,會收集有關控制單元型別和裝置型別的資訊,這些型別意味著特定的 I/O 命令(通道命令字 - CCW)以操作裝置。 裝置驅動程式可以在其初始化步驟中檢索此組硬體資訊,以使用儲存在 struct ccw_device 中的資訊識別它們支援的裝置。 此方法意味著 Linux/390 不需要探測空閒(未啟用)的中斷請求線 (IRQ) 來驅動其裝置。 在適用的情況下,裝置驅動程式可以使用發出 READ DEVICE CHARACTERISTICS ccw 來檢索其線上例程中的裝置特性。

為了便於 I/O 啟動,CDS 層提供了一個 ccw_device_start() 介面,該介面採用裝置特定的通道程式(一個或多個 CCW)作為輸入,設定所需的特定於架構的控制塊,並代表裝置驅動程式啟動 I/O 請求。 ccw_device_start() 例程允許指定它是否期望 CDS 層在每次觀察到中斷時通知裝置驅動程式,或者僅使用最終狀態通知。 有關更多詳細資訊,請參見 ccw_device_start()。 裝置驅動程式絕不能自己發出 ESA/390 I/O 命令,而必須使用 Linux/390 CDS 介面。

為了取消長時間執行的 I/O 請求,CDS 層提供了 ccw_device_halt() 函式。 有些裝置需要最初發出 HALT SUBCHANNEL (HSCH) 命令,而沒有掛起的 I/O 請求。 此函式也由 ccw_device_halt() 涵蓋。

get_ciw() - 獲取命令資訊字

此呼叫使裝置驅動程式能夠從擴充套件的 SenseID 資料中獲取有關支援的命令的資訊。

struct ciw *
ccw_device_get_ciw(struct ccw_device *cdev, __u32 cmd);

cdev

要檢索命令的 ccw_device。

cmd

要檢索的命令型別。

ccw_device_get_ciw() 返回

NULL

沒有可用的擴充套件資料、無效裝置或未找到命令。

!NULL

請求的命令。

ccw_device_start() - Initiate I/O Request

ccw_device_start() 例程是 I/O 請求前端處理器。 所有裝置驅動程式 I/O 請求都必須使用此例程發出。 裝置驅動程式絕不能自己發出 ESA/390 I/O 命令。 相反,ccw_device_start() 例程提供了驅動任意裝置所需的所有介面。

此描述還涵蓋了傳遞給裝置驅動程式中斷處理程式的狀態資訊,因為它與呼叫 ccw_device_start() 時與關聯的 I/O 請求一起定義的規則(標誌)相關。

int ccw_device_start(struct ccw_device *cdev,
                     struct ccw1 *cpa,
                     unsigned long intparm,
                     __u8 lpm,
                     unsigned long flags);
int ccw_device_start_timeout(struct ccw_device *cdev,
                             struct ccw1 *cpa,
                             unsigned long intparm,
                             __u8 lpm,
                             unsigned long flags,
                             int expires);
int ccw_device_start_key(struct ccw_device *cdev,
                         struct ccw1 *cpa,
                         unsigned long intparm,
                         __u8 lpm,
                         __u8 key,
                         unsigned long flags);
int ccw_device_start_key_timeout(struct ccw_device *cdev,
                                 struct ccw1 *cpa,
                                 unsigned long intparm,
                                 __u8 lpm,
                                 __u8 key,
                                 unsigned long flags,
                                 int expires);

cdev

ccw_device I/O 的目標

cpa

通道程式的邏輯起始地址

user_intparm

使用者特定的中斷資訊; 將返回到裝置驅動程式的中斷處理程式。 允許裝置驅動程式將中斷與特定的 I/O 請求關聯。

lpm

定義用於特定 I/O 請求的通道路徑。 值為 0 將使 cio 使用 opm。

key

用於 I/O 的儲存金鑰(對於使用儲存金鑰 != 預設金鑰的儲存進行操作非常有用)

flag

定義要對 I/O 處理執行的操作

expires

jiffies 中的超時值。 在此之後,通用 I/O 層將終止正在執行的程式,並以 ERR_PTR(-ETIMEDOUT) 作為 irb 呼叫中斷處理程式。

可能的標誌值是

DOIO_ALLOW_SUSPEND

通道程式可能會被掛起

DOIO_DENY_PREFETCH

不允許 CCW 預取; 通常,這意味著通道程式可能會被修改

DOIO_SUPPRESS_INTER

不要在中間狀態下呼叫處理程式

cpa 引數指向通道程式的第一個格式 1 CCW

struct ccw1 {
      __u8  cmd_code;/* command code */
      __u8  flags;   /* flags, like IDA addressing, etc. */
      __u16 count;   /* byte count */
      __u32 cda;     /* data address */
} __attribute__ ((packed,aligned(8)));

定義了以下 CCW 標誌值

CCW_FLAG_DC

資料鏈接

CCW_FLAG_CC

命令連結

CCW_FLAG_SLI

禁止不正確的長度

CCW_FLAG_SKIP

跳過

CCW_FLAG_PCI

PCI

CCW_FLAG_IDA

間接定址

CCW_FLAG_SUSPEND

掛起

透過 ccw_device_set_options(),裝置驅動程式可以為裝置指定以下選項

DOIO_EARLY_NOTIFICATION

允許提前中斷通知

DOIO_REPORT_ALL

報告所有中斷條件

ccw_device_start() 函式返回

0

成功完成或請求成功啟動

-EBUSY

該裝置當前正在處理之前的 I/O 請求,或者該裝置上存在掛起的狀態。

-ENODEV

cdev 無效,該裝置未執行或 ccw_device 未聯機。

當 I/O 請求完成時,CDS 第一級中斷處理程式會將狀態累積到 struct irb 中,然後呼叫裝置中斷處理程式。 intparm 欄位將包含裝置驅動程式已與特定 I/O 請求關聯的值。 如果識別出掛起的裝置狀態,則 intparm 將設定為 0(零)。 這可能在 I/O 啟動期間發生,或者由於警報狀態通知而延遲發生。 在任何情況下,此狀態都與當前(最後一個)I/O 請求無關。 在延遲狀態通知的情況下,不會顯示特殊中斷來指示 I/O 完成,因為 I/O 請求從未啟動,即使 ccw_device_start() 成功完成並返回。

irb 可能包含一個錯誤值,裝置驅動程式應該首先檢查此錯誤值

-ETIMEDOUT

通用 I/O 層在指定的超時值後終止了請求

-EIO

通用 I/O 層由於錯誤狀態而終止了請求

如果 irb 中擴充套件狀態字 (esw) 中的併發感知標誌已設定,則 esw 中 erw.scnt 欄位描述了擴充套件控制字 irb->scsw.ecw[] 中可用的特定於裝置的感知位元組數。 裝置驅動程式本身不需要裝置感知。

裝置中斷處理程式可以使用以下定義來調查感知位元組 0 中編碼的主要單元檢查源

SNS0_CMD_REJECT

0x80

SNS0_INTERVENTION_REQ

0x40

SNS0_BUS_OUT_CHECK

0x20

SNS0_EQUIPMENT_CHECK

0x10

SNS0_DATA_CHECK

0x08

SNS0_OVERRUN

0x04

SNS0_INCOMPL_DOMAIN

0x01

根據裝置狀態,可以將這些值中的多個一起設定。 有關詳細資訊,請參閱裝置特定的文件。

irb->scsw.cstat 欄位提供(累積的)子通道狀態

SCHN_STAT_PCI

程式控制的中斷

SCHN_STAT_INCORR_LEN

長度不正確

SCHN_STAT_PROG_CHECK

程式檢查

SCHN_STAT_PROT_CHECK

保護檢查

SCHN_STAT_CHN_DATA_CHK

通道資料檢查

SCHN_STAT_CHN_CTRL_CHK

通道控制檢查

SCHN_STAT_INTF_CTRL_CHK

介面控制檢查

SCHN_STAT_CHAIN_CHECK

連結檢查

irb->scsw.dstat 欄位提供(累積的)裝置狀態

DEV_STAT_ATTENTION

注意

DEV_STAT_STAT_MOD

狀態修改器

DEV_STAT_CU_END

控制單元結束

DEV_STAT_BUSY

DEV_STAT_CHN_END

通道結束

DEV_STAT_DEV_END

裝置結束

DEV_STAT_UNIT_CHECK

單元檢查

DEV_STAT_UNIT_EXCEP

單元異常

有關單個標誌含義的詳細資訊,請參閱 ESA/390 操作原理手冊。

使用說明

ccw_device_start() 必須在停用狀態下呼叫,並保持 ccw 裝置鎖。

裝置驅動程式可以從其中斷處理程式中發出下一個 ccw_device_start() 呼叫。 不需要安排一個 bottom-half,除非需要安排一個非確定性地長時間執行的錯誤恢復過程或類似過程。 在 I/O 處理期間,Linux/390 通用 I/O 裝置驅動程式已經獲得了 IRQ 鎖,即處理程式在呼叫 ccw_device_start() 時不得嘗試再次獲取它,否則我們將陷入死鎖狀態!

如果裝置驅動程式依賴於在啟動下一個 I/O 請求之前完成的 I/O 請求,則可以透過將 NoOp I/O 命令 CCW_CMD_NOOP 連結到提交的 CCW 鏈的末尾來減少 I/O 處理開銷。 這將強制通道結束和裝置結束狀態一起呈現,並伴隨單箇中斷。 但是,應該謹慎使用此方法,因為它意味著通道將保持忙碌,無法處理同一通道上其他裝置的 I/O 請求。 因此,例如,讀取命令絕不應使用此技術,因為結果無論如何都會透過單箇中斷呈現。

為了最大限度地減少 I/O 開銷,裝置驅動程式僅應在裝置可以報告在裝置結束之前裝置驅動程式迫切依賴的中間中斷資訊的情況下使用 DOIO_REPORT_ALL。 在這種情況下,所有 I/O 中斷都會呈現給裝置驅動程式,直到識別出最終狀態。

如果裝置能夠從非同步呈現的 I/O 錯誤中恢復,則可以使用 DOIO_EARLY_NOTIFICATION 標誌執行重疊 I/O。 雖然某些裝置始終將通道結束和裝置結束一起報告,並伴隨單箇中斷,但其他裝置在通道準備好進行下一個 I/O 請求時呈現主要狀態(通道結束),並在裝置上完成資料傳輸時呈現次要狀態(裝置結束)。

上面的標誌允許利用此功能,例如,對於可以處理網路上丟失的資料的通訊裝置,以允許增強的 I/O 處理。

除非通道子系統隨時呈現輔助狀態中斷,否則利用此功能將導致僅將主要狀態中斷呈現給裝置驅動程式,同時執行重疊 I/O。 當呈現沒有錯誤的輔助狀態(警報狀態)時,這表示自上次輔助(最終)狀態以來發出的所有重疊 ccw_device_start() 請求已成功完成。

打算在通道命令字 (CCW) 上設定掛起標誌的通道程式必須使用 DOIO_ALLOW_SUSPEND 選項啟動 I/O 操作,否則掛起標誌將導致通道程式檢查。 在通道程式被掛起時,通道子系統將生成一箇中間中斷。

ccw_device_resume() - 恢復通道程式執行

如果裝置驅動程式選擇透過在特定的 CCW 上設定 CCW 掛起標誌來掛起當前通道程式的執行,則通道程式執行將被掛起。為了恢復通道程式執行,CIO 層提供了 ccw_device_resume() 例程。

int ccw_device_resume(struct ccw_device *cdev);

cdev

請求恢復操作的 ccw_device

ccw_device_resume() 函式返回

0

掛起的通道程式已恢復

-EBUSY

狀態掛起

-ENODEV

cdev 無效或非操作子通道

-EINVAL

恢復函式不適用

-ENOTCONN

沒有 I/O 請求等待完成

使用說明

請檢視 ccw_device_start() 用法說明,瞭解有關掛起通道程式的更多詳細資訊。

ccw_device_halt() - 停止 I/O 請求處理

有時,裝置驅動程式可能需要停止長時間執行的通道程式處理,或者裝置可能需要最初發出停止子通道 (HSCH) I/O 命令。為此,提供了 ccw_device_halt() 命令。

必須在停用狀態下呼叫 ccw_device_halt(),並且持有 ccw 裝置鎖。

int ccw_device_halt(struct ccw_device *cdev,
                    unsigned long intparm);

cdev

請求停止操作的 ccw_device

intparm

中斷引數;該值僅在沒有未完成的 I/O 時使用,否則返回與 I/O 請求關聯的 intparm

ccw_device_halt() 函式返回

0

請求已成功啟動

-EBUSY

裝置當前正忙,或狀態掛起。

-ENODEV

cdev 無效。

-EINVAL

裝置未執行或 ccw 裝置未聯機。

使用說明

裝置驅動程式可以透過編寫在其末尾透過通道內轉移 (TIC) 命令 (CCW_CMD_TIC) 迴圈回到其開頭的通道程式來編寫永無止境的通道程式。通常,這是由網路裝置驅動程式透過設定 PCI CCW 標誌 (CCW_FLAG_PCI) 來執行的。一旦執行此 CCW,就會生成程式控制中斷 (PCI)。然後,裝置驅動程式可以執行適當的操作。在中斷對網路裝置的未完成讀取(無論是否帶有 PCI 標誌)之前,需要 ccw_device_halt() 結束未完成的操作。

ccw_device_clear() - Terminage I/O Request Processing

為了終止子通道上的所有 I/O 處理,使用了清除子通道 (CSCH) 命令。可以透過 ccw_device_clear() 發出。

必須在停用狀態下呼叫 ccw_device_clear(),並且持有 ccw 裝置鎖。

int ccw_device_clear(struct ccw_device *cdev, unsigned long intparm);

cdev

請求清除操作的 ccw_device

intparm

中斷引數(請參閱 ccw_device_halt()

ccw_device_clear() 函式返回

0

請求已成功啟動

-ENODEV

cdev 無效

-EINVAL

裝置未執行或 ccw 裝置未聯機。

其他支援例程

本章介紹 Linux/390 裝置驅動程式程式設計環境中要使用的各種例程。

get_ccwdev_lock()

獲取裝置特定鎖的地址。然後將其用於 spin_lock() / spin_unlock() 呼叫中。

__u8 ccw_device_get_path_mask(struct ccw_device *cdev);

獲取當前可用於 cdev 的路徑的掩碼。