libATA 開發者指南

作者:

Jeff Garzik

簡介

libATA 是 Linux 核心中使用的一個庫,用於支援 ATA 主機控制器和裝置。 libATA 提供了一個 ATA 驅動程式 API、用於 ATA 和 ATAPI 裝置的類傳輸,以及根據 T10 SAT 規範為 ATA 裝置提供的 SCSI<->ATA 轉換。

本指南記錄了 libATA 驅動程式 API、庫函式、庫內部結構,以及一些示例 ATA 底層驅動程式。

libata 驅動程式 API

struct ata_port_operations 是為每個底層 libata 硬體驅動程式定義的,它控制著底層驅動程式如何與 ATA 和 SCSI 層進行互動。

基於 FIS 的驅動程式將使用 ->qc_prep()->qc_issue() 高階鉤子接入系統。 行為類似於 PCI IDE 硬體的硬體可以使用幾個通用輔助函式,至少定義 ATA 影子暫存器塊的匯流排 I/O 地址。

struct ata_port_operations

IDENTIFY 裝置配置後

void (*dev_config) (struct ata_port *, struct ata_device *);

在向找到的每個裝置發出 IDENTIFY [PACKET] DEVICE 後呼叫。 通常用於在發出 SET FEATURES - XFER MODE 之前和操作之前應用裝置特定的修復。

此條目可以在 ata_port_operations 中指定為 NULL。

設定 PIO/DMA 模式

void (*set_piomode) (struct ata_port *, struct ata_device *);
void (*set_dmamode) (struct ata_port *, struct ata_device *);
void (*post_set_mode) (struct ata_port *);
unsigned int (*mode_filter) (struct ata_port *, struct ata_device *, unsigned int);

在發出 SET FEATURES - XFER MODE 命令之前呼叫的鉤子。 可選的 ->mode_filter() 鉤子在 libata 構建了可能模式的掩碼時被呼叫。 這被傳遞給 ->mode_filter() 函式,該函式應在過濾掉因硬體限制而不合適的模式後,返回有效模式的掩碼。 使用此介面新增模式是無效的。

當呼叫 ->set_piomode() 和呼叫 ->set_dmamode() 時,保證 dev->pio_modedev->dma_mode 有效。 此時,與電纜共享的任何其他驅動器的時序也將有效。 也就是說,庫在嘗試設定任何驅動器的模式之前,會記錄通道上每個驅動器的模式的決策。

在 SET FEATURES - XFER MODE 命令成功完成後,無條件呼叫 ->post_set_mode()

->set_piomode() 始終被呼叫(如果存在),但僅在 DMA 可行時才呼叫 ->set_dma_mode()

Taskfile 讀/寫

void (*sff_tf_load) (struct ata_port *ap, struct ata_taskfile *tf);
void (*sff_tf_read) (struct ata_port *ap, struct ata_taskfile *tf);

呼叫 ->tf_load() 將給定的 taskfile 載入到硬體暫存器/DMA 緩衝區中。 呼叫 ->tf_read() 讀取硬體暫存器/DMA 緩衝區,以獲取當前的 taskfile 暫存器值集。 大多數基於 taskfile 的硬體(PIO 或 MMIO)的驅動程式都使用 ata_sff_tf_load()ata_sff_tf_read() 作為這些鉤子。

PIO 資料讀/寫

void (*sff_data_xfer) (struct ata_device *, unsigned char *, unsigned int, int);

所有 bmdma 樣式的驅動程式都必須實現此鉤子。 這是在 PIO 資料傳輸期間實際複製資料位元組的底層操作。 通常,驅動程式將選擇 ata_sff_data_xfer()ata_sff_data_xfer32() 之一。

ATA 命令執行

void (*sff_exec_command)(struct ata_port *ap, struct ata_taskfile *tf);

使先前使用 ->tf_load() 載入的 ATA 命令在硬體中啟動。 大多數基於 taskfile 的硬體的驅動程式都使用 ata_sff_exec_command() 作為此鉤子。

每個 cmd ATAPI DMA 功能過濾器

int (*check_atapi_dma) (struct ata_queued_cmd *qc);

允許底層驅動程式過濾 ATA PACKET 命令,返回一個狀態,指示是否可以使用 DMA 來處理提供的 PACKET 命令。

此鉤子可以指定為 NULL,在這種情況下,libata 將假定可以支援 atapi dma。

讀取特定的 ATA 影子暫存器

u8   (*sff_check_status)(struct ata_port *ap);
u8   (*sff_check_altstatus)(struct ata_port *ap);

從硬體讀取 Status/AltStatus ATA 影子暫存器。 在某些硬體上,讀取 Status 暫存器具有清除中斷條件的副作用。 大多數基於 taskfile 的硬體的驅動程式都使用 ata_sff_check_status() 作為此鉤子。

寫入特定的 ATA 影子暫存器

void (*sff_set_devctl)(struct ata_port *ap, u8 ctl);

將裝置控制 ATA 影子暫存器寫入硬體。 大多數驅動程式不需要定義此項。

選擇總線上的 ATA 裝置

void (*sff_dev_select)(struct ata_port *ap, unsigned int device);

發出底層硬體命令,使 N 個硬體裝置之一被視為 ATA 總線上“已選擇”(啟用且可用)的裝置。 這通常對基於 FIS 的裝置沒有意義。

大多數基於 taskfile 的硬體的驅動程式都使用 ata_sff_dev_select() 作為此鉤子。

私有調整方法

void (*set_mode) (struct ata_port *ap);

預設情況下,libata 根據 ATA 時序規則執行驅動器和控制器調整,並且應用黑名單和電纜限制。 有些控制器需要特殊處理,並且具有自定義調整規則,通常是使用 ATA 命令但不實際執行驅動器時序的 raid 控制器。

警告

當控制器存在怪癖時,不應使用此鉤子替換標準控制器調整邏輯。 在這種情況下替換預設調整邏輯將繞過對資料可靠性可能很重要的驅動器和橋接怪癖的處理。 如果控制器需要過濾模式選擇,則應改用 mode_filter 鉤子。

控制 PCI IDE BMDMA 引擎

void (*bmdma_setup) (struct ata_queued_cmd *qc);
void (*bmdma_start) (struct ata_queued_cmd *qc);
void (*bmdma_stop) (struct ata_port *ap);
u8   (*bmdma_status) (struct ata_port *ap);

在設定 IDE BMDMA 事務時,這些鉤子會準備 (->bmdma_setup)、啟動 (->bmdma_start) 和停止 (->bmdma_stop) 硬體的 DMA 引擎。->bmdma_status 用於讀取標準 PCI IDE DMA 狀態暫存器。

這些鉤子通常在基於 FIS 的驅動程式中要麼是空操作,要麼根本沒有實現。

大多數舊版 IDE 驅動程式使用 ata_bmdma_setup() 作為 bmdma_setup() 鉤子。ata_bmdma_setup() 會將 PRD 表的指標寫入 IDE PRD 表地址暫存器,在 DMA 命令暫存器中啟用 DMA,並呼叫 exec_command() 以開始傳輸。

大多數舊版 IDE 驅動程式使用 ata_bmdma_start() 作為 bmdma_start() 鉤子。ata_bmdma_start() 會將 ATA_DMA_START 標誌寫入 DMA 命令暫存器。

許多舊版 IDE 驅動程式使用 ata_bmdma_stop() 作為 bmdma_stop() 鉤子。ata_bmdma_stop() 會清除 DMA 命令暫存器中的 ATA_DMA_START 標誌。

許多舊版 IDE 驅動程式使用 ata_bmdma_status() 作為 bmdma_status() 鉤子。

高階 taskfile 鉤子

enum ata_completion_errors (*qc_prep) (struct ata_queued_cmd *qc);
int (*qc_issue) (struct ata_queued_cmd *qc);

更高級別的鉤子,這兩個鉤子可能會取代上面的幾個 taskfile/DMA 引擎鉤子。 在緩衝區已 DMA 對映後呼叫 ->qc_prep,通常用於填充硬體的 DMA 散射/收集表。 一些驅動程式使用標準 ata_bmdma_qc_prep()ata_bmdma_dumb_qc_prep() 輔助函式,但更高階的驅動程式會推出自己的函式。

一旦準備好了硬體和 S/G 表,->qc_issue 就用於使命令處於活動狀態。 IDE BMDMA 驅動程式使用輔助函式 ata_sff_qc_issue() 進行基於 taskfile 協議的排程。 更高階的驅動程式會實現自己的 ->qc_issue

ata_sff_qc_issue() 根據需要呼叫 ->sff_tf_load()->bmdma_setup()->bmdma_start() 來啟動傳輸。

異常和探測處理 (EH)

void (*freeze) (struct ata_port *ap);
void (*thaw) (struct ata_port *ap);

當 HSM 違規或其他條件中斷埠的正常操作時,會呼叫 ata_port_freeze()。 在埠解凍之前,不允許凍結的埠執行任何操作,這通常是在成功重置之後。

可選的 ->freeze() 回撥可用於以硬體方式凍結埠(例如,遮蔽中斷並停止 DMA 引擎)。 如果無法以硬體方式凍結埠,則中斷處理程式必須無條件地確認並清除中斷,同時埠處於凍結狀態。

呼叫可選的 ->thaw() 回撥來執行與 ->freeze() 相反的操作:再次準備埠以進行正常操作。 取消遮蔽中斷、啟動 DMA 引擎等。

void (*error_handler) (struct ata_port *ap);

->error_handler() 是驅動程式連線到探測、熱插拔、恢復和其他異常情況的鉤子。 實現的主要責任是以一組 EH 鉤子作為引數呼叫 ata_do_eh()ata_bmdma_drive_eh()

在任何其他操作執行之前,在 EH 重置期間呼叫“prereset”鉤子(可能為 NULL)。

在執行 EH 重置之後呼叫“postreset”鉤子(可能為 NULL)。 基於現有條件、問題的嚴重性和硬體功能,

將呼叫“softreset”(可能為 NULL)或“hardreset”(可能為 NULL)來執行底層 EH 重置。

void (*post_internal_cmd) (struct ata_queued_cmd *qc);

透過 ata_exec_internal() 執行探測時間或 EH 時間命令後,執行完成處理所需的任何特定於硬體的操作。

硬體中斷處理

irqreturn_t (*irq_handler)(int, void *, struct pt_regs *);
void (*irq_clear) (struct ata_port *);

->irq_handler 是由 libata 向系統註冊的中斷處理例程。 在註冊中斷處理程式之前,在探測期間呼叫 ->irq_clear,以確保硬體處於靜默狀態。

第二個引數 dev_instance 應強制轉換為指向 struct ata_host_set 的指標。

大多數舊版 IDE 驅動程式使用 ata_sff_interrupt() 作為 irq_handler 鉤子,它會掃描 host_set 中的所有埠,確定哪個排隊的命令處於活動狀態(如果有),並呼叫 ata_sff_host_intr(ap,qc)。

大多數舊版 IDE 驅動程式使用 ata_sff_irq_clear() 作為 irq_clear() 鉤子,它只是清除 DMA 狀態暫存器中的中斷和錯誤標誌。

SATA phy 讀/寫

int (*scr_read) (struct ata_port *ap, unsigned int sc_reg,
         u32 *val);
int (*scr_write) (struct ata_port *ap, unsigned int sc_reg,
                   u32 val);

讀取和寫入標準 SATA phy 暫存器。 sc_reg 是 SCR_STATUS、SCR_CONTROL、SCR_ERROR 或 SCR_ACTIVE 之一。

初始化和關閉

int (*port_start) (struct ata_port *ap);
void (*port_stop) (struct ata_port *ap);
void (*host_stop) (struct ata_host_set *host_set);

在初始化每個埠的資料結構之後立即呼叫 ->port_start()。 通常,這用於分配每個埠的 DMA 緩衝區/表/環、啟用 DMA 引擎和類似的任務。 一些驅動程式還使用此入口點來為 ap->private_data 分配驅動程式私有記憶體。

許多驅動程式使用 ata_port_start() 作為此鉤子或從他們自己的 port_start() 鉤子呼叫它。ata_port_start() 會為舊版 IDE PRD 表分配空間並返回。

->host_stop() 之後呼叫 ->port_stop()。 它的唯一功能是釋放 DMA/記憶體資源,因為它們不再被主動使用。 許多驅動程式此時也會釋放埠中的驅動程式私有資料。

在所有 ->port_stop() 呼叫完成後呼叫 ->host_stop()。 此鉤子必須完成硬體關閉,釋放 DMA 和其他資源等。此鉤子可以指定為 NULL,在這種情況下不會呼叫它。

錯誤處理

本章介紹如何在 libata 下處理錯誤。 建議讀者首先閱讀 SCSI EH (SCSI EH) 和 ATA 異常文件。

命令的來源

在 libata 中,命令用 struct ata_queued_cmd 或 qc 表示。 qc 在埠初始化期間預先分配,並重複用於命令執行。 目前,每個埠僅分配一個 qc,但尚未合併的 NCQ 分支為每個標籤分配一個 qc,並將每個 qc 對映到 NCQ 標籤 1 對 1。

libata 命令可以來自兩個來源 - libata 本身和 SCSI 中間層。 libata 內部命令用於初始化和錯誤處理。 所有正常的 blk 請求和 SCSI 模擬的命令都作為 SCSI 命令透過 SCSI 主機模板的 queuecommand 回撥傳遞。

如何發出命令

內部命令

一旦分配,qc 的 taskfile 將被初始化以執行該命令。 qc 目前有兩種機制來通知完成。 一種是透過 qc->complete_fn() 回撥,另一種是完成 qc->waitingqc->complete_fn() 回撥是正常 SCSI 轉換命令使用的非同步路徑,而 qc->waiting 是內部命令使用的同步(釋出者在程序上下文中休眠)路徑。

一旦初始化完成,將獲取 host_set 鎖併發放 qc。

SCSI 命令

所有 libata 驅動程式都使用 ata_scsi_queuecmd() 作為 hostt->queuecommand 回撥。 scmd 可以模擬或轉換。 處理模擬的 scmd 不涉及任何 qc。 結果會立即計算並完成 scmd。

qc->complete_fn() 回撥用於完成通知。 ATA 命令使用 ata_scsi_qc_complete(),而 ATAPI 命令使用 atapi_qc_complete()。 當 qc 完成時,這兩個函式最終都會呼叫 qc->scsidone 來通知上層。 完成轉換後,將使用 ata_qc_issue() 發出 qc。

請注意,SCSI 中間層在持有 host_set 鎖時呼叫 hostt->queuecommand,因此以上所有操作都在持有 host_set 鎖時發生。

如何處理命令

根據使用的協議和控制器,命令的處理方式有所不同。 為了便於討論,假設使用 taskfile 介面和所有標準回撥的控制器。

目前使用了 6 種 ATA 命令協議。 根據它們的處理方式,可以將它們分為以下四個類別。

ATA 無資料或 DMA

ATA_PROT_NODATA 和 ATA_PROT_DMA 屬於此類別。 一旦發出,這些型別的命令不需要任何軟體干預。 裝置將在完成時引發中斷。

ATA PIO

ATA_PROT_PIO 屬於此類。libata 當前使用輪詢來實現 PIO。設定 ATA_NIEN 位以關閉中斷,並且 ata_wq 上的 pio_task 執行輪詢和 IO。

ATAPI NODATA 或 DMA

ATA_PROT_ATAPI_NODATA 和 ATA_PROT_ATAPI_DMA 屬於此類。packet_task 用於在發出 PACKET 命令後輪詢 BSY 位。一旦裝置關閉 BSY,packet_task 將傳輸 CDB 並將處理交給中斷處理程式。

ATAPI PIO

ATA_PROT_ATAPI 屬於此類。設定 ATA_NIEN 位,並且如 ATAPI NODATA 或 DMA 中一樣,packet_task 提交 cdb。但是,在提交 cdb 後,進一步的處理(資料傳輸)將交給 pio_task。

命令如何完成

一旦發出,所有 qc 要麼用 ata_qc_complete() 完成,要麼超時。對於由中斷處理的命令,ata_host_intr() 呼叫 ata_qc_complete(),而對於 PIO 任務,pio_task 呼叫 ata_qc_complete()。在錯誤情況下,packet_task 也可能完成命令。

ata_qc_complete() 執行以下操作。

  1. DMA 記憶體被取消對映。

  2. 從 qc->flags 中清除 ATA_QCFLAG_ACTIVE。

  3. 呼叫 qc->complete_fn 回撥。如果回撥的返回值不為零,則完成被短路並且 ata_qc_complete() 返回。

  4. 呼叫 __ata_qc_complete(),它執行

    1. qc->flags 被清除為零。

    2. ap->active_tagqc->tag 被 poisoned。

    3. qc->waiting 被清除並完成(按此順序)。

    4. 透過清除 ap->qactive 中的相應位來釋放 qc。

因此,它基本上通知上層並釋放 qc。一個例外是 #3 中的短路路徑,該路徑由 atapi_qc_complete() 使用。

對於所有非 ATAPI 命令,無論它是否失敗,幾乎都採用相同的程式碼路徑,並且幾乎不進行錯誤處理。如果 qc 成功完成,則以成功狀態完成;否則以失敗狀態完成。

但是,失敗的 ATAPI 命令需要更多的處理,因為需要 REQUEST SENSE 來獲取 sense 資料。如果 ATAPI 命令失敗,則以錯誤狀態呼叫 ata_qc_complete(),進而透過 qc->complete_fn() 回撥呼叫 atapi_qc_complete()

這使得 atapi_qc_complete()scmd->result 設定為 SAM_STAT_CHECK_CONDITION,完成 scmd 並返回 1。由於 sense 資料為空但 scmd->result 為 CHECK CONDITION,因此 SCSI 中間層將為 scmd 呼叫 EH,並且返回 1 會使 ata_qc_complete() 返回而不釋放 qc。這會將我們帶到 ata_scsi_error(),並帶有部分完成的 qc。

ata_scsi_error()

ata_scsi_error() 是 libata 當前的 transportt->eh_strategy_handler()。如上所述,這將在兩種情況下輸入 - 超時和 ATAPI 錯誤完成。此函式將檢查 qc 是否處於活動狀態且尚未失敗。這樣的 qc 將被標記為 AC_ERR_TIMEOUT,以便 EH 知道稍後處理它。然後它呼叫底層 libata 驅動程式的 error_handler() 回撥。

呼叫 error_handler() 回撥時,它會停止 BMDMA 並完成 qc。請注意,由於我們當前在 EH 中,因此無法呼叫 scsi_done。如 SCSI EH 文件中所述,恢復的 scmd 應使用 scsi_queue_insert() 重試或使用 scsi_finish_command() 完成。在這裡,我們用 scsi_finish_command() 覆蓋 qc->scsidone 並呼叫 ata_qc_complete()

如果 EH 是由於失敗的 ATAPI qc 而呼叫的,則此處的 qc 將被完成但不會被釋放。這種半完成的目的是使用 qc 作為佔位符,以使 EH 程式碼到達此位置。這有點 hackish,但它有效。

一旦控制到達此處,就會透過顯式呼叫 __ata_qc_complete() 來釋放 qc。然後,發出 REQUEST SENSE 的內部 qc。一旦獲取了 sense 資料,scmd 就會透過直接在 scmd 上呼叫 scsi_finish_command() 來完成。請注意,由於我們已經完成並釋放了與 scmd 關聯的 qc,因此我們不需要/無法再次呼叫 ata_qc_complete()

當前 EH 的問題

  • 錯誤表示過於粗糙。目前,任何和所有錯誤情況都用 ATA STATUS 和 ERROR 暫存器表示。非 ATA 裝置錯誤透過設定 ATA_ERR 位被視為 ATA 裝置錯誤。需要更好的錯誤描述符,可以正確表示 ATA 和其他錯誤/異常。

  • 處理超時時,沒有采取任何措施使裝置忘記超時的命令併為新命令做好準備。

  • 透過 ata_scsi_error() 進行 EH 處理沒有受到通常命令處理的適當保護。在 EH 進入時,裝置未處於靜止狀態。超時的命令可能隨時成功或失敗。pio_task 和 atapi_task 可能仍在執行。

  • 錯誤恢復太弱。導致 HSM 不匹配錯誤和其他錯誤的裝置/控制器通常需要重置才能返回到已知狀態。此外,需要高階錯誤處理來支援 NCQ 和熱插拔等功能。

  • ATA 錯誤直接在中斷處理程式中處理,PIO 錯誤在 pio_task 中處理。由於以下原因,這對於高階錯誤處理是有問題的。

    首先,高階錯誤處理通常需要上下文和內部 qc 執行。

    其次,即使是一個簡單的故障(例如,CRC 錯誤)也需要資訊收集,並且可能觸發複雜的錯誤處理(例如,重置和重新配置)。擁有多個程式碼路徑來收集資訊、進入 EH 和觸發操作會使生活變得痛苦。

    第三,分散的 EH 程式碼使得實現底層驅動程式變得困難。底層驅動程式覆蓋 libata 回撥。如果 EH 分散在多個位置,則每個受影響的回撥都應執行其部分的錯誤處理。這可能容易出錯且痛苦。

libata 庫

連結迭代助手

引數

struct ata_link *link

上一個連結,NULL 表示開始

struct ata_port *ap

包含要迭代的連結的 ATA 埠

enum ata_link_iter_mode mode

迭代模式,ATA_LITER_* 之一

鎖定:主機鎖或 EH 上下文。

返回

指向下一個連結的指標。

struct ata_device *ata_dev_next(struct ata_device *dev, struct ata_link *link, enum ata_dev_iter_mode mode)

裝置迭代助手

引數

struct ata_device *dev

上一個裝置,NULL 表示開始

struct ata_link *link

包含要迭代的裝置的 ATA 連結

enum ata_dev_iter_mode mode

迭代模式,ATA_DITER_* 之一

鎖定:主機鎖或 EH 上下文。

返回

指向下一個裝置的指標。

int atapi_cmd_type(u8 opcode)

從 SCSI 操作碼確定 ATAPI 命令型別

引數

u8 opcode

SCSI 操作碼

從 **opcode** 確定 ATAPI 命令型別。

鎖定:無。

返回

ATAPI_{READ|WRITE|READ_CD|PASS_THRU|MISC}

unsigned int ata_pack_xfermask(unsigned int pio_mask, unsigned int mwdma_mask, unsigned int udma_mask)

將 pio、mwdma 和 udma 掩碼打包到 xfer_mask 中

引數

unsigned int pio_mask

pio_mask

unsigned int mwdma_mask

mwdma_mask

unsigned int udma_mask

udma_mask

將 **pio_mask**、**mwdma_mask** 和 **udma_mask** 打包到單個無符號 int xfer_mask 中。

鎖定:無。

返回

打包的 xfer_mask。

u8 ata_xfer_mask2mode(unsigned int xfer_mask)

為給定的 xfer_mask 查詢匹配的 XFER_*

引數

unsigned int xfer_mask

感興趣的 xfer_mask

返回與 **xfer_mask** 匹配的 XFER_* 值。僅考慮 **xfer_mask** 的最高位。

鎖定:無。

返回

匹配的 XFER_* 值,如果未找到匹配項,則為 0xff。

unsigned int ata_xfer_mode2mask(u8 xfer_mode)

為 XFER_* 查詢匹配的 xfer_mask

引數

u8 xfer_mode

感興趣的 XFER_*

返回與 **xfer_mode** 匹配的 xfer_mask。

鎖定:無。

返回

匹配的 xfer_mask,如果未找到匹配項,則為 0。

int ata_xfer_mode2shift(u8 xfer_mode)

為 XFER_* 查詢匹配的 xfer_shift

引數

u8 xfer_mode

感興趣的 XFER_*

返回與 **xfer_mode** 匹配的 xfer_shift。

鎖定:無。

返回

匹配的 xfer_shift,如果未找到匹配項,則為 -1。

const char *ata_mode_string(unsigned int xfer_mask)

將 xfer_mask 轉換為字串

引數

unsigned int xfer_mask

支援的位掩碼;僅最高位計數。

確定表示最高速度(**modemask** 中的最高位)的字串。

鎖定:無。

返回

表示 **mode_mask** 中列出的最高速度的常量 C 字串,或常量 C 字串“<n/a>”。

unsigned int ata_dev_classify(const struct ata_taskfile *tf)

根據 ATA 規範簽名確定裝置型別

引數

const struct ata_taskfile *tf

要標識的裝置的 ATA taskfile 暫存器集

根據 taskfile 暫存器內容確定裝置是 ATA 還是 ATAPI,根據 ATA/PI 規範的“簽名和永續性”部分(第 1 卷,第 5.14 節)。

鎖定:無。

返回

裝置型別,ATA_DEV_ATAATA_DEV_ATAPIATA_DEV_PMPATA_DEV_ZACATA_DEV_UNKNOWN(如果失敗)。

void ata_id_string(const u16 *id, unsigned char *s, unsigned int ofs, unsigned int len)

將 IDENTIFY DEVICE 頁面轉換為字串

引數

const u16 *id

我們將檢查的 IDENTIFY DEVICE 結果

unsigned char *s

資料輸出到的字串

unsigned int ofs

identify device 頁面中的偏移量

unsigned int len

要返回的字串的長度。必須是偶數。

IDENTIFY DEVICE 頁面中的字串被分解為 16 位塊。遍歷字串,併線性輸出每個 8 位塊,而不管平臺如何。

鎖定:呼叫者。

void ata_id_c_string(const u16 *id, unsigned char *s, unsigned int ofs, unsigned int len)

將 IDENTIFY DEVICE 頁面轉換為 C 字串

引數

const u16 *id

我們將檢查的 IDENTIFY DEVICE 結果

unsigned char *s

資料輸出到的字串

unsigned int ofs

identify device 頁面中的偏移量

unsigned int len

要返回的字串的長度。必須是奇數。

此函式與 ata_id_string 相同,只是它會修剪尾隨空格並以 null 終止生成的字串。**len** 必須是實際最大長度(偶數)+ 1。

鎖定:呼叫者。

unsigned int ata_id_xfermask(const u16 *id)

從給定的 IDENTIFY 資料計算 xfermask

引數

const u16 *id

用於計算 xfer 掩碼的 IDENTIFY 資料

計算此裝置的 xfermask。如果必須正確考慮早期裝置,這並不像看起來那麼簡單。

FIXME: pre IDE 驅動器計時(我們在乎嗎?)。

鎖定:無。

返回

計算的 xfermask

unsigned int ata_pio_need_iordy(const struct ata_device *adev)

檢查是否需要 IORDY

引數

const struct ata_device *adev

ATA 裝置

檢查裝置的當前速度是否需要 IORDY。由各種控制器用於晶片配置。

unsigned int ata_do_dev_read_id(struct ata_device *dev, struct ata_taskfile *tf, __le16 *id)

預設 ID 讀取方法

引數

struct ata_device *dev

裝置

struct ata_taskfile *tf

建議的任務檔案

__le16 *id

資料緩衝區

發出識別任務檔案並返回包含識別資料的緩衝區。對於某些 RAID 控制器和 pre ATA 裝置,此函式由驅動程式包裝或替換

int ata_cable_40wire(struct ata_port *ap)

返回 40 線電纜型別

引數

struct ata_port *ap

用於想要硬連線 40 線電纜檢測的驅動程式的輔助方法。

int ata_cable_80wire(struct ata_port *ap)

返回 80 線電纜型別

引數

struct ata_port *ap

用於想要硬連線 80 線電纜檢測的驅動程式的輔助方法。

int ata_cable_unknown(struct ata_port *ap)

返回未知的 PATA 電纜。

引數

struct ata_port *ap

用於沒有 PATA 電纜檢測的驅動程式的輔助方法。

int ata_cable_ignore(struct ata_port *ap)

返回忽略的 PATA 電纜。

引數

struct ata_port *ap

用於不使用電纜型別來限制傳輸模式的驅動程式的輔助方法。

int ata_cable_sata(struct ata_port *ap)

返回 SATA 電纜型別

引數

struct ata_port *ap

用於具有 SATA 電纜的驅動程式的輔助方法

struct ata_device *ata_dev_pair(struct ata_device *adev)

返回電纜上的另一個裝置

引數

struct ata_device *adev

裝置

獲取同一電纜上的另一個裝置,如果沒有,則返回 NULL

int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)

程式設計定時併發出 SET FEATURES - XFER

引數

struct ata_link *link

將在其上程式設計定時的鏈路

struct ata_device **r_failed_dev

失敗裝置的輸出引數

用於調整和設定 ATA 裝置磁碟傳輸模式(PIO3、UDMA6 等)的函式的標準實現。如果 ata_dev_set_mode() 失敗,則指向失敗裝置的指標將返回到 **r_failed_dev** 中。

鎖定:PCI/etc. 匯流排探測 sem。

返回

成功時為 0,否則為負 errno

int ata_wait_after_reset(struct ata_link *link, unsigned long deadline, int (*check_ready)(struct ata_link *link))

復位後等待鏈路變為就緒

引數

struct ata_link *link

要等待的連結

unsigned long deadline

操作的截止時間 jiffies

int (*check_ready)(struct ata_link *link)

用於檢查鏈路就緒情況的回撥

等待 **link** 在重置後變為就緒狀態。

鎖定:EH 上下文。

返回

如果 **link** 在 **deadline** 之前準備就緒,則為 0;否則,為 -errno。

int ata_std_prereset(struct ata_link *link, unsigned long deadline)

準備重置

引數

struct ata_link *link

要重置的 ATA 連結

unsigned long deadline

操作的截止時間 jiffies

**link** 即將被重置。初始化它。來自 prereset 的失敗使 libata 中止整個重置序列並放棄該埠,因此 prereset 應該是盡力而為的。它盡最大努力為重置序列做準備,但如果出現問題,它應該只是抱怨,而不是失敗。

鎖定:核心執行緒上下文(可能會休眠)

返回

始終為 0。

void ata_std_postreset(struct ata_link *link, unsigned int *classes)

標準 postreset 回撥

引數

struct ata_link *link

目標 ata_link

unsigned int *classes

連線裝置的類

在成功重置後呼叫此函式。請注意,在使用不同的重置方法呼叫 postreset 之前,裝置可能已被重置多次。

鎖定:核心執行緒上下文(可能會休眠)

unsigned int ata_dev_set_feature(struct ata_device *dev, u8 subcmd, u8 action)

發出 SET FEATURES

引數

struct ata_device *dev

將向其傳送命令的裝置

u8 subcmd

要傳送的 SET FEATURES 子命令

u8 action

扇區計數表示子命令特定操作

使用扇區計數向埠 **ap** 上的裝置 **dev** 發出 SET FEATURES 命令

鎖定:PCI/etc. 匯流排探測 sem。

返回

成功時為 0,否則為 AC_ERR_* 掩碼。

int ata_std_qc_defer(struct ata_queued_cmd *qc)

檢查是否需要延遲 qc

引數

struct ata_queued_cmd *qc

有問題的 ATA 命令

非 NCQ 命令不能與任何其他命令(NCQ 或非 NCQ)一起執行。由於上層只知道佇列深度,因此我們負責維護排除。此函式檢查是否可以發出新命令 **qc**。

鎖定:spin_lock_irqsave(主機鎖)

返回

如果需要延遲,則為 ATA_DEFER_*,否則為 0。

void ata_qc_complete(struct ata_queued_cmd *qc)

完成活動的 ATA 命令

引數

struct ata_queued_cmd *qc

要完成的命令

向中間層和上層表明 ATA 命令已完成,狀態為 ok 或 not-ok。

成功完成多個 NCQ 命令時,請避免多次呼叫此函式。應改用 ata_qc_complete_multiple(),它將正確更新 IRQ 期望狀態。

鎖定:spin_lock_irqsave(主機鎖)

u64 ata_qc_get_active(struct ata_port *ap)

獲取活動 qcs 的位掩碼

引數

struct ata_port *ap

有問題的埠

鎖定:spin_lock_irqsave(主機鎖)

返回

活動 qcs 的位掩碼

測試給定的鏈路是否線上

引數

struct ata_link *link

要測試的 ATA 鏈路

測試 **link** 是否線上。當沒有從屬鏈路時,這與 ata_phys_link_online() 相同。當存在從屬鏈路時,此函式應僅在主鏈路上呼叫,並且如果任何 M/S 鏈路線上,則將返回 true。

鎖定:無。

返回

如果埠線上狀態可用且線上,則為 True。

測試給定的鏈路是否離線

引數

struct ata_link *link

要測試的 ATA 鏈路

測試 **link** 是否離線。當沒有從屬鏈路時,這與 ata_phys_link_offline() 相同。當存在從屬鏈路時,此函式應僅在主鏈路上呼叫,並且如果 M/S 鏈路都離線,則將返回 true。

鎖定:無。

返回

如果埠離線狀態可用且離線,則為 True。

void ata_host_suspend(struct ata_host *host, pm_message_t mesg)

掛起主機

引數

struct ata_host *host

要掛起的主機

pm_message_t mesg

PM 訊息

掛起 **host**。實際操作由埠掛起執行。

void ata_host_resume(struct ata_host *host)

恢復主機

引數

struct ata_host *host

要恢復的主機

恢復 **host**。實際操作由埠恢復執行。

struct ata_port *ata_port_alloc(struct ata_host *host)

分配和初始化基本 ATA 埠資源

引數

struct ata_host *host

此已分配埠所屬的 ATA 主機

分配和初始化基本 ATA 埠資源。

返回

成功時分配 ATA 埠,失敗時分配 NULL。

鎖定:從呼叫層繼承(可能會休眠)。

struct ata_host *ata_host_alloc(struct device *dev, int n_ports)

分配和初始化基本 ATA 主機資源

引數

struct device *dev

此主機與之關聯的通用裝置

int n_ports

與此主機關聯的 ATA 埠數

分配和初始化基本 ATA 主機資源。LLD 呼叫此函式來分配主機,完全初始化它,並使用 ata_host_register() 連線它。

返回

成功時分配 ATA 主機,失敗時分配 NULL。

鎖定:從呼叫層繼承(可能會休眠)。

struct ata_host *ata_host_alloc_pinfo(struct device *dev, const struct ata_port_info *const *ppi, int n_ports)

分配主機並使用 port_info 陣列初始化

引數

struct device *dev

此主機與之關聯的通用裝置

const struct ata_port_info * const * ppi

用於初始化主機的 ATA port_info 陣列

int n_ports

連線到此主機的 ATA 埠數

分配 ATA 主機並使用來自 **ppi** 的資訊進行初始化。如果以 NULL 結尾,則 **ppi** 可能包含比 **n_ports** 更少的條目。最後一個條目將用於剩餘的埠。

返回

成功時分配 ATA 主機,失敗時分配 NULL。

鎖定:從呼叫層繼承(可能會休眠)。

int ata_host_start(struct ata_host *host)

啟動並凍結 ATA 主機的埠

引數

struct ata_host *host

要為其啟動埠的 ATA 主機

啟動並凍結 host 的埠。 啟動狀態記錄在 host->flags 中,因此可以多次呼叫此函式。 埠保證只啟動一次。 如果 host->ops 尚未初始化,則會將其設定為第一個非虛擬埠操作。

鎖定:從呼叫層繼承(可能會休眠)。

返回

如果所有埠都成功啟動,則返回 0;否則返回 -errno。

void ata_host_init(struct ata_host *host, struct device *dev, struct ata_port_operations *ops)

為 sas 初始化主機結構(ipr、libsas)

引數

struct ata_host *host

要初始化的主機

struct device *dev

主機連線到的裝置

struct ata_port_operations *ops

port_ops

int ata_host_register(struct ata_host *host, const struct scsi_host_template *sht)

註冊已初始化的 ATA 主機

引數

struct ata_host *host

要註冊的 ATA 主機

const struct scsi_host_template *sht

SCSI 主機的模板

註冊已初始化的 ATA 主機。 host 是使用 ata_host_alloc() 分配的,並由 LLD 完全初始化。 此函式啟動埠,將 host 註冊到 ATA 和 SCSI 層,並探測註冊的裝置。

鎖定:從呼叫層繼承(可能會休眠)。

返回

成功時返回 0,否則返回 -errno。

int ata_host_activate(struct ata_host *host, int irq, irq_handler_t irq_handler, unsigned long irq_flags, const struct scsi_host_template *sht)

啟動主機,請求 IRQ 並註冊它

引數

struct ata_host *host

目標 ATA 主機

int irq

要請求的 IRQ

irq_handler_t irq_handler

請求 IRQ 時使用的 irq_handler

unsigned long irq_flags

請求 IRQ 時使用的 irq_flags

const struct scsi_host_template *sht

註冊主機時要使用的 scsi_host_template

在分配 ATA 主機並初始化它之後,大多數 libata LLD 執行三個步驟來啟用主機 - 啟動主機、請求 IRQ 並註冊它。 此助手獲取必要的引數並一次性執行這三個步驟。

無效的 IRQ 會跳過 IRQ 註冊,並期望主機在埠上設定輪詢模式。 在這種情況下,irq_handler 應該為 NULL。

鎖定:從呼叫層繼承(可能會休眠)。

返回

成功時返回 0,否則返回 -errno。

void ata_host_detach(struct ata_host *host)

分離 ATA 主機的所有埠

引數

struct ata_host *host

要分離的主機

分離 host 的所有埠。

鎖定:核心執行緒上下文(可能會休眠)。

void ata_pci_remove_one(struct pci_dev *pdev)

用於裝置移除的 PCI 層回撥

引數

struct pci_dev *pdev

已移除的 PCI 裝置

PCI 層透過此掛鉤向 libata 指示已發生熱插拔或模組解除安裝事件。 分離所有埠。 資源釋放透過 devres 處理。

鎖定:繼承自 PCI 層(可能會休眠)。

void ata_platform_remove_one(struct platform_device *pdev)

用於裝置移除的平臺層回撥

引數

struct platform_device *pdev

已移除的平臺裝置

平臺層透過此掛鉤向 libata 指示已發生熱插拔或模組解除安裝事件。 分離所有埠。 資源釋放透過 devres 處理。

鎖定:繼承自平臺層(可能會休眠)。

void ata_msleep(struct ata_port *ap, unsigned int msecs)

ATA EH 所有者感知型 msleep

引數

struct ata_port *ap

要將休眠歸屬到的 ATA 埠

unsigned int msecs

以毫秒為單位的休眠持續時間

休眠 msecs。 如果當前任務是 ap 的 EH 的所有者,則在進入休眠狀態之前釋放所有權,並在休眠完成後重新獲取所有權。 換句話說,允許共享 ap->host 的其他埠在此任務休眠時擁有 EH。

鎖定:可能會休眠。

u32 ata_wait_register(struct ata_port *ap, void __iomem *reg, u32 mask, u32 val, unsigned int interval, unsigned int timeout)

等待暫存器值更改

引數

struct ata_port *ap

要等待暫存器的 ATA 埠,可以為 NULL

void __iomem *reg

IO 對映的暫存器

u32 mask

應用於讀取暫存器值的掩碼

u32 val

等待條件

unsigned int interval

以毫秒為單位的輪詢間隔

unsigned int timeout

以毫秒為單位的超時時間

等待暫存器的某些位更改是 ATA 控制器的常見操作。 此函式讀取 32 位 LE IO 對映的暫存器 reg,並測試以下條件。

(*reg & mask) != val

如果滿足條件,則返回;否則,在 interval_msec 後重復該過程,直到超時。

鎖定:核心執行緒上下文(可能會休眠)

返回

最終暫存器值。

libata 核心內部

查詢裝置的物理鏈路

引數

struct ata_device *dev

要查詢物理鏈路的 ATA 裝置

查詢 dev 連線到的物理鏈路。 請注意,這僅在 dev 位於從屬鏈路上時才與 dev->link 不同。 對於所有其他情況,它與 dev->link 相同。

鎖定:無所謂。

返回

指向找到的物理鏈路的指標。

void ata_force_cbl(struct ata_port *ap)

根據 libata.force 強制電纜型別

引數

struct ata_port *ap

感興趣的 ATA 埠

根據 libata.force 強制電纜型別並對此發出警告。 使用具有匹配埠號的最後一個條目,因此可以將其指定為裝置強制引數的一部分。 例如,“a:40c,1.00:udma4”和“1.00:40c,udma4”具有相同的效果。

鎖定:EH 上下文。

void ata_force_pflags(struct ata_port *ap)

根據 libata.force 強制埠標誌

引數

struct ata_port *ap

感興趣的 ATA 埠

根據 libata.force 強制埠標誌並對此發出警告。

鎖定:EH 上下文。

根據 libata.force 強制鏈路限制

引數

struct ata_link *link

感興趣的 ATA 鏈路

根據 libata.force 強制鏈路標誌和 SATA spd 限制並對此發出警告。 僅指定埠部分(例如 1:)時,該限制適用於連線到主機鏈路和所有透過 PMP 連線的扇出埠的所有鏈路。 如果裝置部分指定為 0(例如 1.00:),則它指定第一個扇出鏈路,而不是主機鏈路。 裝置編號 15 始終指向主機鏈路,無論是否連線 PMP。 如果控制器有從屬鏈路,則裝置編號 16 指向它。

鎖定:EH 上下文。

void ata_force_xfermask(struct ata_device *dev)

根據 libata.force 強制 xfermask

引數

struct ata_device *dev

感興趣的 ATA 裝置

根據 libata.force 強制 xfer_mask 並對此發出警告。 為了與鏈路選擇保持一致,裝置編號 15 選擇連線到主機鏈路的第一個裝置。

鎖定:EH 上下文。

void ata_force_quirks(struct ata_device *dev)

根據 libata.force 強制 quirks

引數

struct ata_device *dev

感興趣的 ATA 裝置

根據 libata.force 強制 quirks 並對此發出警告。 為了與鏈路選擇保持一致,裝置編號 15 選擇連線到主機鏈路的第一個裝置。

鎖定:EH 上下文。

bool ata_set_rwcmd_protocol(struct ata_device *dev, struct ata_taskfile *tf)

設定 taskfile r/w 命令和協議

引數

struct ata_device *dev

taskfile 的目標裝置

struct ata_taskfile *tf

要檢查和配置的 taskfile

檢查裝置配置和 tf->flags 以確定用於 tf 的正確讀取/寫入命令和協議。

鎖定:呼叫者。

u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev)

從 ATA taskfile 讀取塊地址

引數

const struct ata_taskfile *tf

感興趣的 ATA taskfile

struct ata_device *dev

tf 所屬的 ATA 裝置

鎖定:無。

tf 讀取塊地址。 此函式可以處理所有三種地址格式 - LBA、LBA48 和 CHS。 tf->protocol 和 flags 選擇要使用的地址格式。

返回

tf 讀取的塊地址。

int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, unsigned int tf_flags, int cdl, int class)

為給定的讀/寫請求構建 ATA taskfile

引數

struct ata_queued_cmd *qc

與要構建的 taskfile 關聯的元資料

u64 block

塊地址

u32 n_block

塊數

unsigned int tf_flags

RW/FUA 等...

int cdl

命令持續時間限制索引

int class

IO 優先順序類

鎖定:無。

為命令 qc 構建 ATA taskfile,用於 blockn_blocktf_flagsclass 描述的讀取/寫入請求。

返回

成功時返回 0,如果請求對於 dev 來說太大,則返回 -ERANGE,如果請求無效,則返回 -EINVAL。

void ata_unpack_xfermask(unsigned int xfer_mask, unsigned int *pio_mask, unsigned int *mwdma_mask, unsigned int *udma_mask)

將 xfer_mask 解包為 pio、mwdma 和 udma 掩碼

引數

unsigned int xfer_mask

要解包的 xfer_mask

unsigned int *pio_mask

生成的 pio_mask

unsigned int *mwdma_mask

生成的 mwdma_mask

unsigned int *udma_mask

生成的 udma_mask

xfer_mask 解包為 pio_maskmwdma_maskudma_mask。 將忽略任何 NULL 目標掩碼。

int ata_read_native_max_address(struct ata_device *dev, u64 *max_sectors)

讀取本機最大地址

引數

struct ata_device *dev

目標裝置

u64 *max_sectors

用於返回原生最大扇區地址的輸出引數

對目標裝置執行 LBA48 或 LBA28 原生大小查詢。

返回

成功時返回 0,如果命令被驅動器中止則返回 -EACCES,其他錯誤返回 -EIO。

int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors)

設定最大扇區數

引數

struct ata_device *dev

目標裝置

u64 new_sectors

要為裝置設定的新的最大扇區數值

dev 的最大扇區數設定為 new_sectors

返回

成功時返回 0,如果命令被驅動器中止或拒絕(由於先前非易失性的 SET_MAX 命令)則返回 -EACCES,其他錯誤返回 -EIO。

int ata_hpa_resize(struct ata_device *dev)

調整設定了 HPA 的裝置大小

引數

struct ata_device *dev

要調整大小的裝置

讀取具有 HPA 功能的 LBA28 或 LBA48 磁碟的大小,並在需要時將其調整為介質的完整大小。 呼叫者必須檢查驅動器是否已啟用 HPA 功能集。

返回

成功時返回 0,失敗時返回 -errno。

void ata_dump_id(struct ata_device *dev, const u16 *id)

IDENTIFY DEVICE 資訊除錯輸出

引數

struct ata_device *dev

從中獲取資訊的裝置

const u16 *id

要轉儲的 IDENTIFY DEVICE 頁面

從給定的 IDENTIFY DEVICE 頁面轉儲選定的 16 位字。

鎖定:呼叫者。

unsigned int ata_exec_internal(struct ata_device *dev, struct ata_taskfile *tf, const u8 *cdb, enum dma_data_direction dma_dir, void *buf, unsigned int buflen, unsigned int timeout)

執行 libata 內部命令

引數

struct ata_device *dev

將命令傳送到的裝置

struct ata_taskfile *tf

命令的任務檔案暫存器和結果

const u8 *cdb

資料包命令的 CDB

enum dma_data_direction dma_dir

命令的資料傳輸方向

void *buf

命令的資料緩衝區

unsigned int buflen

資料緩衝區的長度

unsigned int timeout

超時時間,以毫秒為單位(0 表示預設值)

使用超時時間執行 libata 內部命令。tf 包含輸入時的命令和返回時的結果。 超時和錯誤情況透過返回值報告。 超時後不會採取恢復措施。 呼叫者有責任在超時後進行清理。

鎖定:無。 應該在核心上下文中呼叫,可能會睡眠。

返回

成功時返回 0,失敗時返回 AC_ERR_* 掩碼

u32 ata_pio_mask_no_iordy(const struct ata_device *adev)

返回非 IORDY 掩碼

引數

const struct ata_device *adev

ATA 裝置

如果未使用 iordy,則計算可能的最高模式。 如果沒有可用的 iordy 模式,則返回 -1。

int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, unsigned int flags, u16 *id)

從指定裝置讀取 ID 資料

引數

struct ata_device *dev

目標裝置

unsigned int *p_class

指向目標裝置類別的指標(可能會更改)

unsigned int flags

ATA_READID_* 標誌

u16 *id

用於讀取 IDENTIFY 資料的緩衝區

從指定裝置讀取 ID 資料。 在 ATA 裝置上執行 ATA_CMD_ID_ATA,在 ATAPI 裝置上執行 ATA_CMD_ID_ATAPI。 此函式還會為 ATA4 之前的驅動器發出 ATA_CMD_INIT_DEV_PARAMS。

FIXME:ATA_CMD_ID_ATA 對於早期驅動器是可選的,如果遇到這種情況,我們現在會中止。

鎖定:核心執行緒上下文(可能會休眠)

返回

成功時返回 0,否則返回 -errno。

void ata_dev_power_set_standby(struct ata_device *dev)

將裝置電源模式設定為待機模式

引數

struct ata_device *dev

目標裝置

發出 STANDBY IMMEDIATE 命令以將裝置電源模式設定為待機模式。 對於 HDD 裝置,這將停止磁碟旋轉。

鎖定:核心執行緒上下文(可能會休眠)。

void ata_dev_power_set_active(struct ata_device *dev)

將裝置電源模式設定為活動模式

引數

struct ata_device *dev

目標裝置

發出 VERIFY 命令以進入確保裝置處於活動電源模式。 對於停止旋轉的 HDD(待機或空閒電源模式),VERIFY 命令將在磁碟啟動後完成。

鎖定:核心執行緒上下文(可能會休眠)。

unsigned int ata_read_log_page(struct ata_device *dev, u8 log, u8 page, void *buf, unsigned int sectors)

讀取特定的日誌頁面

引數

struct ata_device *dev

目標裝置

u8 log

要讀取的日誌

u8 page

要讀取的頁面

void *buf

用於儲存讀取頁面的緩衝區

unsigned int sectors

要讀取的扇區數

使用 READ_LOG_EXT 命令讀取日誌頁面。

鎖定:核心執行緒上下文(可能會休眠)。

返回

成功時為 0,否則為 AC_ERR_* 掩碼。

int ata_dev_configure(struct ata_device *dev)

配置指定的 ATA/ATAPI 裝置

引數

struct ata_device *dev

要配置的目標裝置

根據 dev->id 配置 dev。 還會應用通用和低級別驅動程式特定的修復。

鎖定:核心執行緒上下文(可能會休眠)

返回

成功時返回 0,否則返回 -errno

列印 SATA 鏈路狀態

引數

struct ata_link *link

要列印鏈路狀態的 SATA 鏈路

此函式列印 SATA 鏈路的鏈路速度和狀態。

鎖定:無。

u8 ata_timing_cycle2mode(unsigned int xfer_shift, int cycle)

查詢指定週期持續時間的 xfer 模式

引數

unsigned int xfer_shift

要檢查的傳輸型別的 ATA_SHIFT_* 值。

int cycle

週期持續時間,以納秒為單位

返回 cycle 的匹配 xfer 模式。 返回的模式是由 xfer_shift 指定的傳輸型別。 如果 cycle 對於 xfer_shift 而言太慢,則返回 0xff。 如果 cycle 比已知的最快模式更快,則返回最快模式。

鎖定:無。

返回

匹配的 xfer_mode,如果未找到匹配項,則返回 0xff。

int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel)

向下調整 dev xfer 掩碼

引數

struct ata_device *dev

要調整 xfer 掩碼的裝置

unsigned int sel

ATA_DNXFER_* 選擇器

向下調整 dev 的 xfer 掩碼。 請注意,此函式不會應用更改。 之後呼叫 ata_set_mode() 將應用限制。

鎖定:從呼叫方繼承。

返回

成功時返回 0,失敗時返回負 errno

int ata_wait_ready(struct ata_link *link, unsigned long deadline, int (*check_ready)(struct ata_link *link))

等待鏈路變為就緒

引數

struct ata_link *link

要等待的連結

unsigned long deadline

操作的截止時間 jiffies

int (*check_ready)(struct ata_link *link)

用於檢查鏈路就緒情況的回撥

等待 link 變為就緒。 如果 link 就緒,check_ready 應返回正數,如果未就緒,則返回 0,如果鏈路似乎未被佔用,則返回 -ENODEV,其他錯誤情況返回其他 errno。

ATA_TMOUT_FF_WAIT 允許瞬態 -ENODEV 情況。

鎖定:EH 上下文。

返回

如果 **link** 在 **deadline** 之前準備就緒,則為 0;否則,為 -errno。

int ata_dev_same_device(struct ata_device *dev, unsigned int new_class, const u16 *new_id)

確定新 ID 是否與已配置的裝置匹配

引數

struct ata_device *dev

要比較的裝置

unsigned int new_class

新裝置的類別

const u16 *new_id

新裝置的 IDENTIFY 頁面

new_classnew_iddev 進行比較,並確定 dev 是否是由 new_classnew_id 指示的裝置。

鎖定:無。

返回

如果 devnew_classnew_id 匹配,則返回 1,否則返回 0。

int ata_dev_reread_id(struct ata_device *dev, unsigned int readid_flags)

重新讀取 IDENTIFY 資料

引數

struct ata_device *dev

目標 ATA 裝置

unsigned int readid_flags

讀取 ID 標誌

重新讀取 IDENTIFY 頁面並確保 dev 仍然連線到埠。

鎖定:核心執行緒上下文(可能會休眠)

返回

成功時為 0,否則為負 errno

int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class, unsigned int readid_flags)

重新驗證 ATA 裝置

引數

struct ata_device *dev

要重新驗證的裝置

unsigned int new_class

新的類別程式碼

unsigned int readid_flags

讀取 ID 標誌

重新讀取 IDENTIFY 頁面,確保 dev 仍然連線到埠,並根據新的 IDENTIFY 頁面重新配置它。

鎖定:核心執行緒上下文(可能會休眠)

返回

成功時為 0,否則為負 errno

int ata_is_40wire(struct ata_device *dev)

檢查驅動器側檢測

引數

struct ata_device *dev

裝置

執行驅動器側檢測解碼,允許無法遵循文件的裝置供應商這樣做。

int cable_is_40wire(struct ata_port *ap)

40/80/SATA 決定器

引數

struct ata_port *ap

要考慮的埠

此函式將速度管理策略封裝在一個位置。 目前,我們不快取結果,但是當我們使用未知電纜呼叫時,有一個很好的理由將 ap->cbl 設定為結果(並弄清楚它是否會影響熱插拔)。

如果電纜似乎是 40 線,則返回 1。

void ata_dev_xfermask(struct ata_device *dev)

計算給定裝置支援的 xfermask

引數

struct ata_device *dev

要計算 xfermask 的裝置

計算 dev 支援的 xfermask 並將其儲存在 dev->*_mask 中。 此函式負責應用所有已知的限制,包括主機控制器限制、裝置特性等...

鎖定:無。

unsigned int ata_dev_set_xfermode(struct ata_device *dev)

發出 SET FEATURES - XFER MODE 命令

引數

struct ata_device *dev

將向其傳送命令的裝置

向埠 ap 上的裝置 dev 發出 SET FEATURES - XFER MODE 命令。

鎖定:PCI/etc. 匯流排探測 sem。

返回

成功時為 0,否則為 AC_ERR_* 掩碼。

unsigned int ata_dev_init_params(struct ata_device *dev, u16 heads, u16 sectors)

發出 INIT DEV PARAMS 命令

引數

struct ata_device *dev

將向其傳送命令的裝置

u16 heads

磁頭數量(任務檔案引數)

u16 sectors

扇區數量(任務檔案引數)

鎖定:核心執行緒上下文(可能會休眠)

返回

成功時為 0,否則為 AC_ERR_* 掩碼。

int atapi_check_dma(struct ata_queued_cmd *qc)

檢查是否可以支援 ATAPI DMA

引數

struct ata_queued_cmd *qc

要檢查的與任務檔案相關的元資料

允許底層驅動程式過濾 ATA PACKET 命令,返回一個狀態,指示是否可以使用 DMA 來處理提供的 PACKET 命令。

鎖定:spin_lock_irqsave(主機鎖)

返回

當可以使用 ATAPI DMA 時為 0

否則為非零值

void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg, unsigned int n_elem)

將命令與散列表關聯。

引數

struct ata_queued_cmd *qc

要關聯的命令

struct scatterlist *sg

散列表。

unsigned int n_elem

散列表中元素的數量。

初始化 queued_cmd qc 的資料相關元素,使其指向包含 n_elem 個元素的散列表 sg

鎖定:spin_lock_irqsave(主機鎖)

void ata_sg_clean(struct ata_queued_cmd *qc)

取消對映與命令關聯的 DMA 記憶體

引數

struct ata_queued_cmd *qc

包含要釋放的 DMA 記憶體的命令

取消對映與此命令關聯的所有已對映的 DMA 記憶體。

鎖定:spin_lock_irqsave(主機鎖)

int ata_sg_setup(struct ata_queued_cmd *qc)

DMA 對映與命令關聯的散列表。

引數

struct ata_queued_cmd *qc

帶有要對映的散列表的命令。

DMA 對映與 queued_cmd qc 關聯的散列表。

鎖定:spin_lock_irqsave(主機鎖)

返回

成功時為零,錯誤時為負數。

void swap_buf_le16(u16 *buf, unsigned int buf_words)

就地交換 16 位字的半部分

引數

u16 *buf

要交換的緩衝區

unsigned int buf_words

緩衝區中 16 位字的數目。

如果需要,交換 16 位字的半部分,以將小端位元組順序轉換為本機 CPU 位元組順序,反之亦然。

鎖定:從呼叫方繼承。

void ata_qc_free(struct ata_queued_cmd *qc)

釋放未使用的 ata_queued_cmd

引數

struct ata_queued_cmd *qc

要完成的命令

旨在釋放未使用的 ata_queued_cmd 物件,以防某些情況阻止使用它。

鎖定:spin_lock_irqsave(主機鎖)

void ata_qc_issue(struct ata_queued_cmd *qc)

向裝置發出任務檔案

引數

struct ata_queued_cmd *qc

要向裝置發出的命令

準備一個 ATA 命令以提交給裝置。這包括將資料對映到 DMA 可定址區域,填充 S/G 表,最後將任務檔案寫入硬體,啟動命令。

鎖定:spin_lock_irqsave(主機鎖)

測試給定的鏈路是否線上

引數

struct ata_link *link

要測試的 ATA 鏈路

測試 link 是否線上。請注意,如果無法獲得 link 的線上狀態,則此函式返回 0,因此 ata_link_online(link) != !ata_link_offline(link)。

鎖定:無。

返回

如果埠線上狀態可用且線上,則為 True。

測試給定的鏈路是否離線

引數

struct ata_link *link

要測試的 ATA 鏈路

測試 link 是否離線。請注意,如果無法獲得 link 的離線狀態,則此函式返回 0,因此 ata_link_online(link) != !ata_link_offline(link)。

鎖定:無。

返回

如果埠離線狀態可用且離線,則為 True。

void ata_dev_init(struct ata_device *dev)

初始化 ata_device 結構

引數

struct ata_device *dev

要初始化的裝置結構

初始化 dev 以準備探測。

鎖定:從呼叫方繼承。

初始化 ata_link 結構

引數

struct ata_port *ap

連結所連線的 ATA 埠

struct ata_link *link

要初始化的連結結構

int pmp

埠倍增器埠號

初始化 link

鎖定:核心執行緒上下文(可能會休眠)

初始化 link->sata_spd_limit

引數

struct ata_link *link

要為其配置 sata_spd_limit 的連結

link->[hw_]sata_spd_limit 初始化為當前配置的值。

鎖定:核心執行緒上下文(可能會休眠)。

返回

成功時返回 0,失敗時返回 -errno。

void ata_finalize_port_ops(struct ata_port_operations *ops)

完成 ata_port_operations

引數

struct ata_port_operations *ops

要完成的 ata_port_operations

ata_port_operations 可以從另一個 ops 繼承,而該 ops 又可以從另一個繼承。只要繼承鏈中沒有迴圈,這可以根據需要進行多次。

Ops 表在主機啟動時完成。 NULL 或未指定的條目從擁有該方法的最接近的祖先繼承,並且該條目使用它填充。完成之後,ops 表直接指向所有方法,並且不再需要 ->inherits 並將其清除。

使用 ATA_OP_NULL,繼承 ops 可以強制方法為 NULL。

鎖定:無。

void ata_dev_free_resources(struct ata_device *dev)

釋放裝置資源

引數

struct ata_device *dev

目標 ATA 裝置

釋放分配用於支援裝置功能的資源。

鎖定:核心執行緒上下文(可能會休眠)。

void ata_port_detach(struct ata_port *ap)

分離 ATA 埠以準備移除裝置

引數

struct ata_port *ap

要分離的 ATA 埠

分離 ap 的所有 ATA 裝置和關聯的 SCSI 裝置;然後,刪除關聯的 SCSI 主機。保證從該函式返回後,ap 處於靜止狀態。

鎖定:核心執行緒上下文(可能會休眠)。

void __ata_ehi_push_desc(struct ata_eh_info *ehi, const char *fmt, ...)

推送錯誤描述,無需新增分隔符

引數

struct ata_eh_info *ehi

目標 EHI

const char *fmt

printf 格式字串

根據 fmt 格式化字串並將其附加到 ehi->desc

鎖定:spin_lock_irqsave(主機鎖)

...

可變引數

void ata_ehi_push_desc(struct ata_eh_info *ehi, const char *fmt, ...)

推送帶分隔符的錯誤描述

引數

struct ata_eh_info *ehi

目標 EHI

const char *fmt

printf 格式字串

根據 fmt 格式化字串並將其附加到 ehi->desc。如果 ehi->desc 不為空,則在中間新增“,”。

鎖定:spin_lock_irqsave(主機鎖)

...

可變引數

void ata_ehi_clear_desc(struct ata_eh_info *ehi)

清除錯誤描述

引數

struct ata_eh_info *ehi

目標 EHI

清除 ehi->desc

鎖定:spin_lock_irqsave(主機鎖)

void ata_port_desc(struct ata_port *ap, const char *fmt, ...)

附加埠描述

引數

struct ata_port *ap

目標 ATA 埠

const char *fmt

printf 格式字串

根據 fmt 格式化字串並將其附加到埠描述。如果埠描述不為空,則在中間新增“ ”。此函式在初始化 ata_host 時使用。描述在主機註冊時列印。

鎖定:無。

...

可變引數

void ata_port_pbar_desc(struct ata_port *ap, int bar, ssize_t offset, const char *name)

附加 PCI BAR 描述

引數

struct ata_port *ap

目標 ATA 埠

int bar

目標 PCI BAR

ssize_t offset

PCI BAR 中的偏移量

const char *name

區域的名稱

如果 offset 為負數,則此函式格式化一個字串,其中包含 BAR 的名稱、地址、大小和型別,並將其附加到埠描述。如果 offset 為零或正數,則僅附加名稱和偏移地址。

鎖定:無。

unsigned int ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd)

確定內部命令的超時時間

引數

struct ata_device *dev

目標裝置

u8 cmd

要發出的內部命令

確定 dev 的內部命令 cmd 的超時時間。

鎖定:EH 上下文。

返回

確定的超時時間。

void ata_internal_cmd_timed_out(struct ata_device *dev, u8 cmd)

內部命令超時的通知

引數

struct ata_device *dev

目標裝置

u8 cmd

超時的內部命令

通知 EH,dev 的內部命令 cmd 超時。此函式應僅針對使用 ata_internal_cmd_timeout() 確定超時的命令呼叫。

鎖定:EH 上下文。

void ata_eh_acquire(struct ata_port *ap)

獲取 EH 所有權

引數

struct ata_port *ap

要為其獲取 EH 所有權的 ATA 埠

獲取 ap 的 EH 所有權。這是共享主機埠的基本互斥機制。同一主機上的只有一個埠可以宣告 EH 的所有權。

鎖定:EH 上下文。

void ata_eh_release(struct ata_port *ap)

釋放 EH 所有權

引數

struct ata_port *ap

要釋放 EH 所有權的 ATA 埠

如果呼叫者之前使用過 ata_eh_acquire() 獲取了 EH 所有權,則釋放 ap 的 EH 所有權。

鎖定:EH 上下文。

void ata_scsi_error(struct Scsi_Host *host)

SCSI 層錯誤處理回撥

引數

struct Scsi_Host *host

發生錯誤的 SCSI 主機

處理 SCSI 層丟擲的錯誤事件。

鎖定:從 SCSI 層繼承(無,可以睡眠)

返回

零。

void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, struct list_head *eh_work_q)

用於命令列表的錯誤回撥

引數

struct Scsi_Host *host

包含埠的 scsi 主機

struct ata_port *ap

主機內的 ATA 埠

struct list_head *eh_work_q

要處理的命令列表

描述

處理給定的命令列表,並將已完成的命令返回到 ap->eh_done_q。此函式是 libata 錯誤處理程式的第一部分,它處理給定的失敗命令列表。

void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap)

在命令之後恢復埠

引數

struct Scsi_Host *host

包含埠的 SCSI 主機

struct ata_port *ap

ATA 埠

描述

在所有命令都已恢復後,處理埠 ap 的恢復。

void ata_port_wait_eh(struct ata_port *ap)

等待當前掛起的 EH 完成

引數

struct ata_port *ap

要等待 EH 的埠

等待直到當前掛起的 EH 完成。

鎖定:核心執行緒上下文(可能會休眠)。

void ata_eh_set_pending(struct ata_port *ap, int fastdrain)

設定 ATA_PFLAG_EH_PENDING 並激活快速排空

引數

struct ata_port *ap

目標 ATA 埠

int fastdrain

啟用快速排空

設定 ATA_PFLAG_EH_PENDING,如果 fastdrain 非零且 EH 之前未掛起,則啟用快速排空。快速排空確保 EH 及時啟動。

鎖定:spin_lock_irqsave(主機鎖)

void ata_qc_schedule_eh(struct ata_queued_cmd *qc)

安排 qc 進行錯誤處理

引數

struct ata_queued_cmd *qc

要安排錯誤處理的命令

安排 qc 的錯誤處理。 一旦其他命令被排空,EH 將啟動。

鎖定:spin_lock_irqsave(主機鎖)

void ata_std_sched_eh(struct ata_port *ap)

非 libsas ata_ports 使用此通用例程發出 eh

引數

struct ata_port *ap

要安排 EH 的 ATA 埠

鎖定:從 ata_port_schedule_eh 繼承 spin_lock_irqsave(主機鎖)

void ata_std_end_eh(struct ata_port *ap)

非 libsas ata_ports 使用此通用例程完成 eh

引數

struct ata_port *ap

要結束 EH 的 ATA 埠

描述

在 libata 物件模型中,ata_port 到 shost 存在 1:1 的對映,因此可以在 ap->lock 下直接操作主機欄位。在 libsas 情況下,我們需要在 ha->級別保持一個鎖來協調這些事件。

鎖定:spin_lock_irqsave(主機鎖)

void ata_port_schedule_eh(struct ata_port *ap)

安排錯誤處理,無需 qc

引數

struct ata_port *ap

要安排 EH 的 ATA 埠

安排 ap 的錯誤處理。 一旦所有命令都被排空,EH 將啟動。

鎖定:spin_lock_irqsave(主機鎖)

中止連結上的所有 qc

引數

struct ata_link *link

要中止 qc 的 ATA 連結

中止在 link 上啟用的所有活動 qc,並安排 EH。

鎖定:spin_lock_irqsave(主機鎖)

返回

已中止 qc 的數量。

int ata_port_abort(struct ata_port *ap)

中止埠上的所有 qc

引數

struct ata_port *ap

要中止 qc 的 ATA 埠

中止 ap 的所有活動 qc,並安排 EH。

鎖定:spin_lock_irqsave(host_set 鎖)

返回

已中止 qc 的數量。

void __ata_port_freeze(struct ata_port *ap)

凍結埠

引數

struct ata_port *ap

要凍結的 ATA 埠

當 HSM 違規或其他一些情況中斷埠的正常操作時,會呼叫此函式。 在埠解凍之前,不允許凍結埠執行任何操作,通常在成功重置後進行解凍。

ap->ops->freeze() 回撥可用於硬體方式凍結埠(例如,遮蔽中斷並停止 DMA 引擎)。如果無法以硬體方式凍結埠,則中斷處理程式必須無條件地確認並清除中斷,同時埠被凍結。

鎖定:spin_lock_irqsave(主機鎖)

int ata_port_freeze(struct ata_port *ap)

中止並凍結埠

引數

struct ata_port *ap

要凍結的 ATA 埠

中止並凍結 ap。 必須首先呼叫凍結操作,因為某些硬體在可以訪問 taskfile 暫存器之前需要特殊操作。

鎖定:spin_lock_irqsave(主機鎖)

返回

已中止命令的數量。

void ata_eh_freeze_port(struct ata_port *ap)

凍結埠的 EH 輔助函式

引數

struct ata_port *ap

要凍結的 ATA 埠

凍結 ap

鎖定:無。

void ata_eh_thaw_port(struct ata_port *ap)

解凍埠的 EH 輔助函式

引數

struct ata_port *ap

要解凍的 ATA 埠

解凍凍結的埠 ap

鎖定:無。

void ata_eh_qc_complete(struct ata_queued_cmd *qc)

從 EH 完成活動 ATA 命令

引數

struct ata_queued_cmd *qc

要完成的命令

向中間層和上層表明 ATA 命令已完成。 從 EH 使用。

void ata_eh_qc_retry(struct ata_queued_cmd *qc)

告訴中間層在 EH 之後重試 ATA 命令

引數

struct ata_queued_cmd *qc

要重試的命令

向中間層和上層表明應該重試 ATA 命令。 從 EH 使用。

SCSI 中間層將重試次數限制為 scmd->allowed。 對於由於不相關的故障而重試的命令,scmd->allowed 會遞增 (qc->err_mask 為零)。

void ata_dev_disable(struct ata_device *dev)

停用 ATA 裝置

引數

struct ata_device *dev

要停用的 ATA 裝置

停用 dev

鎖定:EH 上下文。

void ata_eh_detach_dev(struct ata_device *dev)

分離 ATA 裝置

引數

struct ata_device *dev

要分離的 ATA 裝置

分離 dev

鎖定:無。

void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev, unsigned int action)

即將執行 eh_action

引數

struct ata_link *link

目標 ATA 連結

struct ata_device *dev

每個裝置操作的目標 ATA dev(可以為 NULL)

unsigned int action

即將執行的操作

在執行 EH 操作之前呼叫,以清除 link->eh_info 中的相關位,以便不會不必要地重複 EH 操作。

鎖定:無。

void ata_eh_done(struct ata_link *link, struct ata_device *dev, unsigned int action)

EH 操作完成

引數

struct ata_link *link

已完成 EH 操作的 ATA 連結

struct ata_device *dev

每個裝置操作的目標 ATA dev(可以為 NULL)

unsigned int action

剛完成的操作

在執行 EH 操作後立即呼叫,以清除 link->eh_context 中的相關位。

鎖定:無。

const char *ata_err_string(unsigned int err_mask)

將 err_mask 轉換為描述性字串

引數

unsigned int err_mask

要轉換為字串的錯誤掩碼

err_mask 轉換為描述性字串。 錯誤根據嚴重性進行優先順序排序,並且僅報告最嚴重的錯誤。

鎖定:無。

返回

err_mask 的描述性字串

unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key)

執行 ATAPI TEST_UNIT_READY

引數

struct ata_device *dev

目標 ATAPI 裝置

u8 *r_sense_key

sense_key 的輸出引數

執行 ATAPI TEST_UNIT_READY。

鎖定:EH 上下文(可能睡眠)。

返回

成功時為 0,失敗時為 AC_ERR_* 掩碼。

enum scsi_disposition ata_eh_decide_disposition(struct ata_queued_cmd *qc)

根據 sense 資料處置 qc

引數

struct ata_queued_cmd *qc

要檢查的 qc

對於常規 SCSI 命令,SCSI 完成回撥 (scsi_done()) 將呼叫 scsi_complete(),後者將呼叫 scsi_decide_disposition(),後者將呼叫 scsi_check_sense()。 scsi_complete() 最終呼叫 scsi_finish_command()。 這對於 SCSI 來說是可以的,因為任何最終的 sense 資料通常都在完成本身中返回(而不呼叫 SCSI EH)。 但是,對於 QC,我們總是需要使用 SCSI EH 顯式獲取 sense 資料。

透過 SCSI EH 完成的命令將改為使用 scsi_eh_flush_done_q() 完成,後者將直接呼叫 scsi_finish_command()(而從不呼叫 scsi_check_sense())。

對於透過 SCSI EH 進行的命令,SCSI EH 策略處理程式有責任呼叫 scsi_decide_disposition(),例如,請參閱 scsi_eh_get_sense() 如何為未將 sense 資料作為完成的一部分的 SCSI LLDD 呼叫 scsi_decide_disposition()。

因此,對於透過 SCSI EH 進行的 QC 命令,我們需要自己呼叫 scsi_check_sense(),類似於 scsi_eh_get_sense() 如何呼叫 scsi_decide_disposition(),後者呼叫 scsi_check_sense(),以便設定正確的 SCSI ML 位元組(如果有)。

鎖定:EH 上下文。

返回

SUCCESS 或 FAILED 或 NEEDS_RETRY 或 ADD_TO_MLQUEUE

bool ata_eh_request_sense(struct ata_queued_cmd *qc)

執行 REQUEST_SENSE_DATA_EXT

引數

struct ata_queued_cmd *qc

qc執行 REQUEST_SENSE_SENSE_DATA_EXT 到

在裝置報告 CHECK SENSE 後執行 REQUEST_SENSE_DATA_EXT。此函式是一個 EH 助手。

鎖定:核心執行緒上下文(可能會休眠)。

返回

如果可以獲取到 sense 資料,則為 true;否則為 false。

unsigned int atapi_eh_request_sense(struct ata_device *dev, u8 *sense_buf, u8 dfl_sense_key)

執行 ATAPI REQUEST_SENSE

引數

struct ata_device *dev

裝置執行 REQUEST_SENSE 到

u8 *sense_buf

結果 sense 資料緩衝區(SCSI_SENSE_BUFFERSIZE 位元組長)

u8 dfl_sense_key

要使用的預設 sense key

在裝置報告 CHECK SENSE 後執行 ATAPI REQUEST_SENSE。此函式是 EH 助手。

鎖定:核心執行緒上下文(可能會休眠)。

返回

成功時為 0,失敗時為 AC_ERR_* 掩碼

void ata_eh_analyze_serror(struct ata_link *link)

分析失敗埠的 SError

引數

struct ata_link *link

用於分析 SError 的 ATA link

如果可用,則分析 SError 並進一步確定失敗原因。

鎖定:無。

unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc)

分析失敗的 qc 的 taskfile

引數

struct ata_queued_cmd *qc

要分析的 qc

分析 qc 的 taskfile 並進一步確定失敗原因。如果可用,此函式還會請求 ATAPI sense 資料。

鎖定:核心執行緒上下文(可能會休眠)。

返回

確定的恢復操作

unsigned int ata_eh_speed_down_verdict(struct ata_device *dev)

確定降速結論

引數

struct ata_device *dev

感興趣的裝置

此函式檢查 dev 的錯誤環,並確定是否需要關閉 NCQ,是否應降低傳輸速度,或者是否需要回退到 PIO。

ECAT_ATA_BUS:任何命令的 ATA_BUS 錯誤

ECAT_TOUT_HSM任何命令的 TIMEOUT 或 HSM 違規

IO 命令

ECAT_UNK_DEV:IO 命令的未知 DEV 錯誤

ECAT_DUBIOUS_*與上述三個相同,但發生在

資料傳輸尚未驗證時。

結論是

NCQ_OFF:關閉 NCQ。

SPEED_DOWN降低傳輸速度,但不回退

到 PIO。

FALLBACK_TO_PIO:回退到 PIO。

即使返回多個結論,每次錯誤也只會採取一個操作。 由非 DUBIOUS 錯誤觸發的操作會清除 ering,而由 DUBIOUS_* 錯誤觸發的操作不會。 這是為了在最初配置裝置後立即加速降速決策。

以下是降速規則。 #1 和 #2 處理 DUBIOUS 錯誤。

  1. 如果在過去 5 分鐘內發生超過一個 DUBIOUS_ATA_BUS 或 DUBIOUS_TOUT_HSM 錯誤,則 SPEED_DOWN 和 FALLBACK_TO_PIO。

  2. 如果在過去 5 分鐘內發生超過一個 DUBIOUS_TOUT_HSM 或 DUBIOUS_UNK_DEV 錯誤,則 NCQ_OFF。

  3. 如果在過去 5 分鐘內發生超過 8 個 ATA_BUS、TOUT_HSM 或 UNK_DEV 錯誤,則 FALLBACK_TO_PIO

  4. 如果在過去 10 分鐘內發生超過 3 個 TOUT_HSM 或 UNK_DEV 錯誤,則 NCQ_OFF。

  5. 如果在過去 10 分鐘內發生超過 3 個 ATA_BUS 或 TOUT_HSM 錯誤,或者超過 6 個 UNK_DEV 錯誤,則 SPEED_DOWN。

鎖定:從呼叫方繼承。

返回

ATA_EH_SPDN_* 標誌的 OR。

unsigned int ata_eh_speed_down(struct ata_device *dev, unsigned int eflags, unsigned int err_mask)

記錄錯誤並在必要時降速

引數

struct ata_device *dev

失敗的裝置

unsigned int eflags

ATA_EFLAG_* 標誌的掩碼

unsigned int err_mask

錯誤的 err_mask

記錄錯誤並檢查錯誤歷史記錄,以確定是否需要調整傳輸速度。 如果需要進行此類調整,它還會適當地設定傳輸限制。

鎖定:核心執行緒上下文(可能會休眠)。

返回

確定的恢復操作。

int ata_eh_worth_retry(struct ata_queued_cmd *qc)

分析錯誤並決定是否重試

引數

struct ata_queued_cmd *qc

可能要重試的 qc

檢視錯誤原因,並確定重試是否有用。 我們不想重試媒體錯誤,因為驅動器本身可能已經花費了 10-30 秒進行其內部重試,然後才報告失敗。

bool ata_eh_quiet(struct ata_queued_cmd *qc)

檢查我們是否需要對命令錯誤保持沉默

引數

struct ata_queued_cmd *qc

要檢查的 qc

檢視 qc 標誌及其 scsi 命令請求標誌,以確定我們是否需要對命令失敗保持沉默。

分析錯誤並確定恢復操作

引數

struct ata_link *link

要執行檢查的主機 link

分析 link 失敗的原因並確定需要哪些恢復操作。 此函式還會設定更詳細的 AC_ERR_* 值,並填充 ATAPI CHECK SENSE 的 sense 資料。

鎖定:核心執行緒上下文(可能會休眠)。

void ata_eh_autopsy(struct ata_port *ap)

分析錯誤並確定恢復操作

引數

struct ata_port *ap

要執行檢查的主機埠

分析 ap 的所有 link,並確定它們失敗的原因以及需要哪些恢復操作。

鎖定:核心執行緒上下文(可能會休眠)。

const char *ata_get_cmd_name(u8 command)

獲取 ATA 命令的名稱

引數

u8 command

要獲取名稱的 ATA 命令程式碼

返回給定命令的文字名稱或“unknown”

鎖定:無

向用戶報告錯誤處理

引數

struct ata_link *link

正在進行的 ATA link EH

向用戶報告 EH。

鎖定:無。

void ata_eh_report(struct ata_port *ap)

向用戶報告錯誤處理

引數

struct ata_port *ap

要報告 EH 的 ATA 埠

向用戶報告 EH。

鎖定:無。

int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)

程式設計定時併發出 SET FEATURES - XFER

引數

struct ata_link *link

將在其上程式設計定時的鏈路

struct ata_device **r_failed_dev

失敗裝置的輸出引數

設定 ATA 裝置磁碟傳輸模式(PIO3、UDMA6 等)。 如果 ata_set_mode() 失敗,則指向失敗裝置的指標將在 r_failed_dev 中返回。

鎖定:PCI/etc. 匯流排探測 sem。

返回

成功時為 0,否則為負 errno

int atapi_eh_clear_ua(struct ata_device *dev)

在重置後清除 ATAPI UNIT ATTENTION

引數

struct ata_device *dev

要清除 UA 的 ATAPI 裝置

重置和其他操作可能會使 ATAPI 裝置引發 UNIT ATTENTION,這會導致下一個操作失敗。 此函式清除 UA。

鎖定:EH 上下文(可能睡眠)。

返回

成功時返回 0,失敗時返回 -errno。

int ata_eh_maybe_retry_flush(struct ata_device *dev)

必要時重試 FLUSH

引數

struct ata_device *dev

可能需要 FLUSH 重試的 ATA 裝置

如果 dev FLUSH 失敗,則需要立即向上傳遞報告,因為它意味著 dev 無法重新對映,並且至少已經丟失了一個扇區,並且進一步的 FLUSH 重試對丟失的扇區沒有任何影響。 但是,如果 FLUSH 由於其他原因(例如傳輸錯誤)失敗,則需要重試 FLUSH。

此函式確定是否需要 FLUSH 失敗重試,並在必要時執行它。

返回

如果 EH 可以繼續,則為 0;如果 EH 需要重複,則為 -errno。

int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, struct ata_device **r_failed_dev)

配置 SATA 介面電源管理

引數

struct ata_link *link

用於配置電源管理的 link

enum ata_lpm_policy policy

link 電源管理策略

struct ata_device **r_failed_dev

失敗裝置的輸出引數

啟用 SATA 介面電源管理。 這將為 min_power 和 medium_power_with_dipm 策略啟用裝置介面電源管理 (DIPM),然後呼叫驅動程式特定的回撥以啟用主機發起的電源管理。

鎖定:EH 上下文。

返回

成功時返回 0,失敗時返回 -errno。

int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, ata_reset_fn_t softreset, ata_reset_fn_t hardreset, ata_postreset_fn_t postreset, struct ata_link **r_failed_link)

在錯誤後恢復主機埠

引數

struct ata_port *ap

要恢復的主機埠

ata_prereset_fn_t prereset

prereset 方法(可以為 NULL)

ata_reset_fn_t softreset

softreset 方法(可以為 NULL)

ata_reset_fn_t hardreset

hardreset 方法(可以為 NULL)

ata_postreset_fn_t postreset

postreset 方法(可以為 NULL)

struct ata_link **r_failed_link

失敗 link 的輸出引數

這是 libata 異常處理的 alpha 和 omega、eum 和 yang、核心和靈魂。 在進入時,恢復每個 link 所需的操作和熱插拔請求記錄在 link 的 eh_context 中。 此函式執行所有操作,並進行適當的重試和回退,以恢復失敗的裝置、分離損壞的裝置並迎接新裝置。

鎖定:核心執行緒上下文(可能會休眠)。

返回

成功時返回 0,失敗時返回 -errno。

void ata_eh_finish(struct ata_port *ap)

完成 EH

引數

struct ata_port *ap

要完成 EH 的主機埠

恢復已完成。 清理 EH 狀態並重試或完成失敗的 qcs。

鎖定:無。

void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset, ata_reset_fn_t softreset, ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)

執行標準錯誤處理

引數

struct ata_port *ap

要處理錯誤的主機埠

ata_prereset_fn_t prereset

prereset 方法(可以為 NULL)

ata_reset_fn_t softreset

softreset 方法(可以為 NULL)

ata_reset_fn_t hardreset

hardreset 方法(可以為 NULL)

ata_postreset_fn_t postreset

postreset 方法(可以為 NULL)

執行標準錯誤處理序列。

鎖定:核心執行緒上下文(可能會休眠)。

void ata_std_error_handler(struct ata_port *ap)

標準錯誤處理程式

引數

struct ata_port *ap

要處理錯誤的主機埠

標準錯誤處理程式

鎖定:核心執行緒上下文(可能會休眠)。

void ata_eh_handle_port_suspend(struct ata_port *ap)

執行埠掛起操作

引數

struct ata_port *ap

要掛起的埠

掛起 ap

鎖定:核心執行緒上下文(可能會休眠)。

void ata_eh_handle_port_resume(struct ata_port *ap)

執行埠恢復操作

引數

struct ata_port *ap

要恢復的埠

恢復 ap

鎖定:核心執行緒上下文(可能會休眠)。

libata SCSI 轉換/模擬

int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])

sd 使用的通用 bios 磁頭/扇區/柱面計算器。

引數

struct scsi_device *sdev

要確定 BIOS 幾何結構的 SCSI 裝置

struct block_device *bdev

sdev 關聯的塊裝置

sector_t capacity

SCSI 裝置的容量

int geom[]

幾何結構將要輸出到的位置

sd 使用的通用 bios 磁頭/扇區/柱面計算器。現在大多數 BIOS 都期望 XXX/255/16 (CHS) 對映。如果未使用此對映,則某些情況下磁碟可能無法啟動。

LOCKING: 由 SCSI 層定義。我們並不真正在意。

返回

零。

void ata_scsi_unlock_native_capacity(struct scsi_device *sdev)

解鎖原生容量

引數

struct scsi_device *sdev

要調整裝置容量的 SCSI 裝置

如果 sdev 上的分割槽超出裝置末尾,則呼叫此函式。它請求 EH 解鎖 HPA。

LOCKING: 由 SCSI 層定義。可能會睡眠。

bool ata_scsi_dma_need_drain(struct request *rq)

檢查資料傳輸是否可能溢位

引數

struct request *rq

要檢查的請求

由於應用程式錯誤或硬體錯誤,將可變長度資料傳輸到主機的 ATAPI 命令可能會溢位。此函式檢查是否應為 request 耗盡並忽略溢位。

鎖定:無。

返回

如果是,則為 1;否則為 0。

int ata_scsi_sdev_init(struct scsi_device *sdev)

SCSI 裝置的早期設定

引數

struct scsi_device *sdev

要檢查的 SCSI 裝置

當與 ATA 裝置關聯的 SCSI 裝置在埠上掃描時,從 scsi_alloc_sdev() 呼叫此函式。

LOCKING: 由 SCSI 層定義。我們並不真正在意。

int ata_scsi_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)

設定 SCSI 裝置屬性

引數

struct scsi_device *sdev

要檢查的 SCSI 裝置

struct queue_limits *lim

佇列限制

這在我們實際開始讀取和寫入裝置之前呼叫,以配置某些 SCSI 中間層行為。

LOCKING: 由 SCSI 層定義。我們並不真正在意。

void ata_scsi_sdev_destroy(struct scsi_device *sdev)

SCSI 裝置即將被銷燬

引數

struct scsi_device *sdev

要銷燬的 SCSI 裝置

sdev 即將被銷燬以進行熱插拔/熱拔插。如果此拔插是由 libata 發起的,如 NULL dev->sdev 所示,則此函式不必執行任何操作。否則,SCSI 層發起的暖拔插正在進行中。清除 dev->sdev,安排裝置進行 ATA 分離並呼叫 EH。

LOCKING: 由 SCSI 層定義。我們並不真正在意。

int ata_scsi_queuecmd(struct Scsi_Host *shost, struct scsi_cmnd *cmd)

向 libata 管理的裝置發出 SCSI cdb

引數

struct Scsi_Host *shost

要傳送的命令的 SCSI 主機

struct scsi_cmnd *cmd

要傳送的 SCSI 命令

在某些情況下,此函式將 SCSI 命令轉換為 ATA 任務檔案,並將任務檔案排隊以傳送到硬體。在其他情況下,此函式透過評估和響應某些 SCSI 命令來模擬 SCSI 裝置。這建立了 ATA 和 ATAPI 裝置顯示為 SCSI 裝置的整體效果。

LOCKING: ATA 主機鎖

返回

如果可以對 cmd 進行排隊,則從 __ata_scsi_queuecmd() 返回值,否則為 0。

void ata_scsi_set_passthru_sense_fields(struct ata_queued_cmd *qc)

在 sense 緩衝區中設定 ATA 欄位

引數

struct ata_queued_cmd *qc

ATA PASS-THROUGH 命令。

使用 ATA 任務檔案欄位填充“ATA 狀態返回 sense 資料描述符”/“固定格式 sense 資料”。

鎖定:無。

int ata_get_identity(struct ata_port *ap, struct scsi_device *sdev, void __user *arg)

HDIO_GET_IDENTITY ioctl 的處理程式

引數

struct ata_port *ap

目標埠

struct scsi_device *sdev

要獲取標識資料的 SCSI 裝置

void __user *arg

標識資料的使用者緩衝區區域

LOCKING: 由 SCSI 層定義。我們並不真正在意。

返回

成功時為零,出錯時為負 errno。

int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg)

HDIO_DRIVE_CMD ioctl 的處理程式

引數

struct scsi_device *scsidev

我們要向其發出命令的裝置

void __user *arg

使用者提供的用於發出命令的資料

LOCKING: 由 SCSI 層定義。我們並不真正在意。

返回

成功時為零,出錯時為負 errno。

int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg)

HDIO_DRIVE_TASK ioctl 的處理程式

引數

struct scsi_device *scsidev

我們要向其發出命令的裝置

void __user *arg

使用者提供的用於發出命令的資料

LOCKING: 由 SCSI 層定義。我們並不真正在意。

返回

成功時為零,出錯時為負 errno。

struct ata_queued_cmd *ata_scsi_qc_new(struct ata_device *dev, struct scsi_cmnd *cmd)

獲取新的 ata_queued_cmd 引用

引數

struct ata_device *dev

新命令所附加到的 ATA 裝置

struct scsi_cmnd *cmd

發起此 ATA 命令的 SCSI 命令

獲取對未使用 ata_queued_cmd 結構的引用,該結構是表示傳送到硬體的單個 ATA 命令的基本 libata 結構。

如果命令可用,請使用當前命令的資訊填充結構的 SCSI 特定部分。

鎖定:spin_lock_irqsave(主機鎖)

返回

已分配命令,如果無可用命令,則為 NULL

void ata_to_sense_error(u8 drv_stat, u8 drv_err, u8 *sk, u8 *asc, u8 *ascq)

將 ATA 錯誤轉換為 SCSI 錯誤

引數

u8 drv_stat

ATA 狀態暫存器中包含的值

u8 drv_err

ATA 錯誤暫存器中包含的值

u8 *sk

我們將要填充的 sense key

u8 *asc

我們將要填充的附加 sense code

u8 *ascq

我們將要填充的附加 sense code qualifier

將 ATA 錯誤轉換為 SCSI 錯誤。填充 SK、ASC 和 ASCQ 位元組的指標,以便稍後在固定或描述符格式 sense 塊中使用。

鎖定:spin_lock_irqsave(主機鎖)

void ata_gen_ata_sense(struct ata_queued_cmd *qc)

生成 SCSI 固定 sense 塊

引數

struct ata_queued_cmd *qc

我們出錯的命令

為失敗的 ATA 命令 qc 生成 sense 塊。

鎖定:無。

unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc)

轉換 SCSI START STOP UNIT 命令

引數

struct ata_queued_cmd *qc

已轉換 ATA 任務檔案的儲存

設定 ATA 任務檔案以發出 STANDBY(停止)或 READ VERIFY(啟動)。也許這些命令應該先執行 CHECK POWER MODE 來檢視裝置當前處於哪種電源模式。[請參閱 www.t10.org 上的 SAT 修訂版 5]

鎖定:spin_lock_irqsave(主機鎖)

返回

成功時為零,出錯時為非零。

unsigned int ata_scsi_flush_xlat(struct ata_queued_cmd *qc)

轉換 SCSI SYNCHRONIZE CACHE 命令

引數

struct ata_queued_cmd *qc

已轉換 ATA 任務檔案的儲存

設定 ATA 任務檔案以發出 FLUSH CACHE 或 FLUSH CACHE EXT。

鎖定:spin_lock_irqsave(主機鎖)

返回

成功時為零,出錯時為非零。

void scsi_6_lba_len(const u8 *cdb, u64 *plba, u32 *plen)

獲取 LBA 和傳輸長度

引數

const u8 *cdb

要轉換的 SCSI 命令

計算 6 位元組命令的 LBA 和傳輸長度。

u64 *plba

LBA

u32 *plen

傳輸長度

void scsi_10_lba_len(const u8 *cdb, u64 *plba, u32 *plen)

獲取 LBA 和傳輸長度

引數

const u8 *cdb

要轉換的 SCSI 命令

計算 10 位元組命令的 LBA 和傳輸長度。

u64 *plba

LBA

u32 *plen

傳輸長度

void scsi_16_lba_len(const u8 *cdb, u64 *plba, u32 *plen)

獲取 LBA 和傳輸長度

引數

const u8 *cdb

要轉換的 SCSI 命令

計算 16 位元組命令的 LBA 和傳輸長度。

u64 *plba

LBA

u32 *plen

傳輸長度

int scsi_dld(const u8 *cdb)

獲取持續時間限制描述符索引

引數

const u8 *cdb

要轉換的 SCSI 命令

返回指示命令持續時間限制描述符索引的 dld 位。

unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc)

將 SCSI VERIFY 命令轉換為 ATA 命令

引數

struct ata_queued_cmd *qc

已轉換 ATA 任務檔案的儲存

將 SCSI VERIFY 命令轉換為 ATA READ VERIFY 命令。

鎖定:spin_lock_irqsave(主機鎖)

返回

成功時為零,出錯時為非零。

unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)

將 SCSI r/w 命令轉換為 ATA 命令

引數

struct ata_queued_cmd *qc

已轉換 ATA 任務檔案的儲存

將六個 SCSI 讀/寫命令中的任何一個轉換為 ATA 對應命令,包括起始扇區 (LBA)、扇區計數,並考慮裝置的 LBA48 支援。

當前支援命令 READ_6READ_10READ_16WRITE_6WRITE_10WRITE_16

鎖定:spin_lock_irqsave(主機鎖)

返回

成功時為零,出錯時為非零。

int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd, ata_xlat_func_t xlat_func)

轉換 SCSI 命令,然後傳送到 ATA 裝置

引數

struct ata_device *dev

命令所定址的 ATA 裝置

struct scsi_cmnd *cmd

要執行的 SCSI 命令

ata_xlat_func_t xlat_func

cmd 轉換為 ATA 任務檔案的執行者

我們的 ->queuecommand() 函式已經確定,發出的 SCSI 命令可以直接轉換為 ATA 命令,而不是在內部處理。

此函式為 SCSI 命令設定 ata_queued_cmd 結構,並將該 ata_queued_cmd 傳送到硬體。

如果準備好執行 ATA 命令,則 xlat_func 引數(執行者)返回 0,否則返回 1 以完成轉換。如果返回 1,則假定 cmd->result(可能還有 cmd->sense_buffer)被設定為反映錯誤情況或乾淨的(早期)終止。

鎖定:spin_lock_irqsave(主機鎖)

返回

成功時返回 0,如果需要延遲命令,則返回 SCSI_ML_QUEUE_DEVICE_BUSY。

void ata_scsi_rbuf_fill(struct ata_device *dev, struct scsi_cmnd *cmd, unsigned int (*actor)(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf))

SCSI 命令模擬器的包裝器

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

unsigned int (*actor)(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

所需 SCSI 命令模擬器的回撥鉤子

負責模擬 SCSI 命令的繁重工作...對映響應緩衝區,呼叫命令的處理程式,並處理處理程式的返回值。此返回值指示處理程式是否希望 SCSI 命令成功完成 (0),否則不成功(在這種情況下,假定 cmd->result 和 sense 緩衝區已設定)。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsiop_inq_std(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬標準 INQUIRY 命令

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

返回與非 VPD INQUIRY 命令輸出關聯的標準裝置標識資料。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsiop_inq_00(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 INQUIRY VPD 頁面 0,頁面列表

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

返回可用的 inquiry VPD 頁面列表。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsiop_inq_80(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 INQUIRY VPD 頁面 80,裝置序列號

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

返回 ATA 裝置序列號。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsiop_inq_83(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 INQUIRY VPD 頁面 83,裝置標識

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

產生兩個邏輯單元裝置識別符號
  • 包含 ATA 序列號的供應商特定 ASCII

  • SAT 定義的“基於 t10 供應商 id”包含 ASCII 供應商名稱 (“ATA “)、型號和序列號。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsiop_inq_89(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 INQUIRY VPD 頁面 89,ATA 資訊

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

產生 SAT 指定的 ATA VPD 頁面。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsiop_inq_b0(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 INQUIRY VPD 頁面 B0,塊限制

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

返回 VPD 頁面 B0h(塊限制)的資料。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsiop_inq_b1(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 INQUIRY VPD 頁面 B1,塊裝置特性

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

返回 VPD 頁面 B1h(塊裝置特性)的資料。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsiop_inq_b2(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 INQUIRY VPD 頁面 B2,邏輯塊配置

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

返回 VPD 頁面 B2h(邏輯塊配置)的資料。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsiop_inq_b6(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 INQUIRY VPD 頁面 B6,分割槽塊裝置特性

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

返回 VPD 頁面 B2h(分割槽塊裝置特性)的資料。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsiop_inq_b9(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 INQUIRY VPD 頁面 B9,併發定位範圍

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

返回 VPD 頁面 B9h(併發定位範圍)的資料。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsiop_inquiry(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 INQUIRY 命令

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

返回與 INQUIRY 命令輸出關聯的資料。

鎖定:spin_lock_irqsave(主機鎖)

void modecpy(u8 *dest, const u8 *src, int n, bool changeable)

準備 MODE SENSE 的響應

引數

u8 *dest

輸出緩衝區

const u8 *src

正在複製的資料

int n

模式頁面的長度

bool changeable

是否請求可更改的引數

為當前或可更改的引數生成一個通用的 MODE SENSE 頁面。

鎖定:無。

unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable)

模擬 MODE SENSE 快取資訊頁面

引數

u16 *id

裝置 IDENTIFY 資料

u8 *buf

輸出緩衝區

bool changeable

是否請求可更改的引數

生成一個快取資訊頁面,該頁面有條件地向 SCSI 層指示寫快取,具體取決於裝置功能。

鎖定:無。

unsigned int ata_msense_control(struct ata_device *dev, u8 *buf, u8 spg, bool changeable)

模擬 MODE SENSE 控制模式頁面

引數

struct ata_device *dev

感興趣的 ATA 裝置

u8 *buf

輸出緩衝區

u8 spg

子頁面程式碼

bool changeable

是否請求可更改的引數

生成一個通用的 MODE SENSE 控制模式頁面。

鎖定:無。

unsigned int ata_msense_rw_recovery(u8 *buf, bool changeable)

模擬 MODE SENSE r/w 錯誤恢復頁面

引數

u8 *buf

輸出緩衝區

bool changeable

是否請求可更改的引數

生成一個通用的 MODE SENSE r/w 錯誤恢復頁面。

鎖定:無。

unsigned int ata_scsiop_mode_sense(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 MODE SENSE 6, 10 命令

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

模擬 MODE SENSE 命令。假定這是為直接訪問裝置(例如磁碟)呼叫的。其他裝置型別不應有塊描述符。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsiop_read_cap(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 READ CAPACITY[ 16] 命令

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

模擬 READ CAPACITY 命令。

鎖定:無。

unsigned int ata_scsiop_report_luns(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 REPORT LUNS 命令

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

模擬 REPORT LUNS 命令。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int atapi_xlat(struct ata_queued_cmd *qc)

初始化 PACKET taskfile

引數

struct ata_queued_cmd *qc

要初始化的命令結構

鎖定:spin_lock_irqsave(主機鎖)

返回

成功返回零,失敗返回非零。

struct ata_device *ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev)

從 scsi_cmnd 查詢 ata_device

引數

struct ata_port *ap

裝置連線到的 ATA 埠

const struct scsi_device *scsidev

從中派生 ATA 裝置的 SCSI 裝置

給定 struct scsi_cmnd 中提供的各種資訊,將其對映到 ATA 總線上,並使用該對映確定哪個 ata_device 與要傳送的 SCSI 命令相關聯。

鎖定:spin_lock_irqsave(主機鎖)

返回

關聯的 ATA 裝置,如果未找到,則為 NULL

unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)

將 ATA 直通 CDB 轉換為 taskfile

引數

struct ata_queued_cmd *qc

要初始化的命令結構

處理 CDB 的 12 位元組、16 位元組或 32 位元組版本。

返回

成功返回零,失敗返回非零。

size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 trmax, u64 sector, u32 count)

SATL Write Same 到 DSM Trim

引數

struct scsi_cmnd *cmd

正在轉換的 SCSI 命令

u32 trmax

可以容納在 sector_size 位元組中的最大條目數。

u64 sector

起始扇區

u32 count

請求的總範圍(以邏輯扇區為單位)

描述

重寫 WRITE SAME 描述符,使其成為 DSM TRIM 小端格式描述符。

最多 64 個格式的條目

63:48 範圍長度 47:0 LBA

範圍長度為 0 時將被忽略。LBA 應按排序順序排列,並且不應重疊。

注意

這與 ADD LBA(S) TO NV CACHE PINNED SET 的格式相同

返回

複製到 sglist 中的位元組數。

unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)

SATL Write Same 到 ATA SCT Write Same

引數

struct ata_queued_cmd *qc

要轉換的命令

描述

將 SCSI WRITE SAME 命令轉換為 DSM TRIM 命令或 SCT Write Same 命令。基於 WRITE SAME 具有 UNMAP 標誌

  • 設定後轉換為 DSM TRIM

  • 清除後轉換為 SCT Write Same

unsigned int ata_scsiop_maint_in(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模擬 MAINTENANCE_IN 的子集

引數

struct ata_device *dev

目標裝置。

struct scsi_cmnd *cmd

感興趣的 SCSI 命令。

u8 *rbuf

響應緩衝區,模擬的 SCSI cmd 輸出將傳送到該緩衝區。

產生一個子集以滿足 scsi_report_opcode()

鎖定:spin_lock_irqsave(主機鎖)

void ata_scsi_report_zones_complete(struct ata_queued_cmd *qc)

轉換 ATA 輸出

引數

struct ata_queued_cmd *qc

返回資料的命令結構

將 T-13 小端欄位表示形式轉換為 T-10 大端欄位表示形式。真是一團糟。

int ata_mselect_caching(struct ata_queued_cmd *qc, const u8 *buf, int len, u16 *fp)

模擬用於快取資訊頁的 MODE SELECT

引數

struct ata_queued_cmd *qc

已轉換 ATA 任務檔案的儲存

const u8 *buf

輸入緩衝區

int len

輸入緩衝區中的有效位元組數

u16 *fp

發生錯誤時,用於指示失敗欄位的輸出引數

準備一個 taskfile 以修改裝置的快取資訊。

鎖定:無。

int ata_mselect_control(struct ata_queued_cmd *qc, u8 spg, const u8 *buf, int len, u16 *fp)

模擬用於控制頁的 MODE SELECT

引數

struct ata_queued_cmd *qc

已轉換 ATA 任務檔案的儲存

u8 spg

控制頁的目標子頁

const u8 *buf

輸入緩衝區

int len

輸入緩衝區中的有效位元組數

u16 *fp

發生錯誤時,用於指示失敗欄位的輸出引數

準備一個 taskfile 以修改裝置的快取資訊。

鎖定:無。

unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)

模擬 MODE SELECT 6、10 命令

引數

struct ata_queued_cmd *qc

已轉換 ATA 任務檔案的儲存

將 MODE SELECT 命令轉換為 ATA SET FEATURES taskfile。假定僅為直接訪問裝置(例如磁碟)呼叫此命令。對於其他裝置型別,應該沒有塊描述符。

鎖定:spin_lock_irqsave(主機鎖)

unsigned int ata_scsi_var_len_cdb_xlat(struct ata_queued_cmd *qc)

SATL 可變長度 CDB 到處理程式

引數

struct ata_queued_cmd *qc

要轉換的命令

將 SCSI 可變長度 CDB 轉換為指定命令。它檢查 CDB 中的服務操作值以呼叫相應的處理程式。

返回

成功返回零,失敗返回非零

ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd)

檢查是否可以進行 SCSI 到 ATA 的轉換

引數

struct ata_device *dev

ATA 裝置

u8 cmd

要考慮的 SCSI 命令操作碼

查詢給定的 SCSI 命令,並確定是要翻譯還是模擬 SCSI 命令。

返回

如果可以,則指向轉換函式的指標,否則為 NULL

void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)

在 ATA 裝置上模擬 SCSI 命令

引數

struct ata_device *dev

目標裝置

struct scsi_cmnd *cmd

傳送到裝置的 SCSI 命令。

解釋並直接執行可以選擇性處理的 SCSI 命令列表。

鎖定:spin_lock_irqsave(主機鎖)

int ata_scsi_offline_dev(struct ata_device *dev)

離線連線的 SCSI 裝置

引數

struct ata_device *dev

要離線連線 SCSI 裝置的 ATA 裝置

此函式從 ata_eh_hotplug() 呼叫,負責使連線到 dev 的 SCSI 裝置離線。呼叫此函式時會使用主機鎖,該鎖保護 dev->sdev 免於清除。

鎖定:spin_lock_irqsave(主機鎖)

返回

如果存在連線的 SCSI 裝置,則為 1,否則為 0。

void ata_scsi_remove_dev(struct ata_device *dev)

移除連線的 SCSI 裝置

引數

struct ata_device *dev

要移除連線的 SCSI 裝置的 ATA 裝置

此函式從 ata_eh_scsi_hotplug() 呼叫,負責移除連線到 dev 的 SCSI 裝置。

鎖定:核心執行緒上下文(可能會休眠)。

void ata_scsi_media_change_notify(struct ata_device *dev)

傳送介質更改事件

引數

struct ata_device *dev

指向具有介質更改事件的磁碟裝置的指標

告訴塊層傳送介質更改通知事件。

鎖定:spin_lock_irqsave(主機鎖)

void ata_scsi_hotplug(struct work_struct *work)

熱插拔的 SCSI 部分

引數

struct work_struct *work

指向要在其上執行 SCSI 熱插拔的 ATA 埠的指標

執行熱插拔的 SCSI 部分。它在 EH 完成後從單獨的 workqueue 執行。這是必需的,因為 SCSI 熱插拔需要正常工作的 EH,並且熱拔與使用互斥鎖的熱插拔同步。

鎖定:核心執行緒上下文(可能會休眠)。

int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, unsigned int id, u64 lun)

使用者啟動的匯流排掃描的指示

引數

struct Scsi_Host *shost

要掃描的 SCSI 主機

unsigned int channel

要掃描的通道

unsigned int id

要掃描的 ID

u64 lun

要掃描的 LUN

當用戶顯式請求匯流排掃描時,將呼叫此函式。設定探測掛起標誌並呼叫 EH。

鎖定:SCSI 層(我們不在乎)

返回

零。

void ata_scsi_dev_rescan(struct work_struct *work)

啟動 scsi_rescan_device()

引數

struct work_struct *work

指向要執行 scsi_rescan_device() 的 ATA 埠的指標

成功執行 ATA 直通 (SAT) 命令後,libata 需要將更改傳播到 SCSI 層。

鎖定:核心執行緒上下文(可能會休眠)。

ATA 錯誤和異常

本章嘗試識別 ATA/ATAPI 裝置的錯誤/異常條件,並描述應如何以與實現無關的方式處理它們。

術語“錯誤”用於描述從裝置報告顯式錯誤條件或命令超時的條件。

術語“異常”用於描述不是錯誤的異常情況(例如,電源或熱插拔事件),或描述錯誤和非錯誤異常情況。在需要明確區分錯誤和異常的情況下,使用術語“非錯誤異常”。

異常類別

異常主要根據傳統 taskfile + 匯流排主控 IDE 介面進行描述。如果控制器為錯誤報告提供了其他更好的機制,則將這些對映到下面描述的類別應該不困難。

在以下各節中,提到了兩個恢復操作 - 重置和重新配置傳輸。這些將在 EH 恢復操作 中進一步描述。

HSM 違規

當 STATUS 值在發出或執行任何 ATA/ATAPI 命令期間與 HSM 要求不匹配時,會指示此錯誤。

  • 嘗試發出命令時,ATA_STATUS 不包含 !BSY && DRDY && !DRQ。

  • PIO 資料傳輸期間的 !BSY && !DRQ。

  • 命令完成時的 DRQ。

  • CDB 傳輸開始後但在 CDB 的最後一個位元組傳輸之前,出現 !BSY && ERR。ATA/ATAPI 標準規定,“在寫入命令包的最後一個位元組之前,裝置不得終止 PACKET 命令並出現錯誤”,並且狀態圖不包括此類轉換。

在這些情況下,HSM 遭到破壞,並且無法從 STATUS 或 ERROR 暫存器獲取有關該錯誤的更多資訊。換句話說,此錯誤可能是任何原因引起的 - 驅動程式錯誤、裝置故障、控制器和/或電纜故障。

由於 HSM 遭到破壞,因此必須重置以恢復已知狀態。降低傳輸速度的重新配置傳輸也可能有所幫助,因為傳輸錯誤有時會導致此類錯誤。

ATA/ATAPI 裝置錯誤(非 NCQ / 非 CHECK CONDITION)

這些錯誤由 ATA/ATAPI 裝置檢測和報告,指示裝置存在問題。對於此類錯誤,STATUS 和 ERROR 暫存器值有效,並描述了錯誤情況。請注意,某些 ATA 匯流排錯誤由 ATA/ATAPI 裝置檢測到,並使用與裝置錯誤相同的機制進行報告。這些情況將在本節後面描述。

對於 ATA 命令,此類錯誤透過在命令執行期間和完成時出現 !BSY && ERR 來指示。

對於 ATAPI 命令,

  • 發出 PACKET 後立即出現 !BSY && ERR && ABRT 表示不支援 PACKET 命令,並且屬於此類別。

  • CDB 的最後一個位元組傳輸後出現 !BSY && ERR(==CHK) && !ABRT 表示 CHECK CONDITION,並且不屬於此類別。

  • CDB 的最後一個位元組傳輸後出現 !BSY && ERR(==CHK) && ABRT *可能* 表示 CHECK CONDITION,並且不屬於此類別。

對於如上檢測到的錯誤,以下錯誤不是ATA/ATAPI裝置錯誤,而是ATA匯流排錯誤,應根據ATA匯流排錯誤進行處理。

資料傳輸期間發生CRC錯誤

這由ERROR暫存器中的ICRC位指示,表示資料傳輸期間發生了損壞。在ATA/ATAPI-7之前,標準規定此位僅適用於UDMA傳輸,但ATA/ATAPI-8草案修訂版1f表示該位可能適用於多字DMA和PIO。

資料傳輸期間或完成時發生ABRT錯誤

在ATA/ATAPI-7之前,標準規定可以在ICRC錯誤和裝置無法完成命令的情況下設定ABRT。結合MWDMA和PIO傳輸錯誤在ATA/ATAPI-7之前不允許使用ICRC位的事實,這似乎暗示ABRT位本身可以指示傳輸錯誤。

但是,ATA/ATAPI-8草案修訂版1f刪除了ICRC錯誤可以開啟ABRT的部分。所以,這是一個灰色地帶。這裡需要一些啟發式方法。

ATA/ATAPI裝置錯誤可以進一步分類如下。

介質錯誤

這由ERROR暫存器中的UNC位指示。ATA裝置僅在一定數量的重試無法恢復資料後才報告UNC錯誤,因此除了通知上層之外,沒有其他太多可以做的事情。

READ和WRITE命令報告第一個失敗扇區的CHS或LBA,但ATA/ATAPI標準規定錯誤完成時傳輸的資料量是不確定的,因此我們不能假定失敗扇區之前的扇區已被傳輸,因此無法像SCSI那樣成功完成這些扇區。

介質已更改/請求介質更改錯誤

<<TODO: 在這裡填寫>>

地址錯誤

這由ERROR暫存器中的IDNF位指示。向上層報告。

其他錯誤

這可能是無效命令或引數(由ABRT ERROR位指示)或一些其他錯誤條件。請注意,ABRT位可以指示很多內容,包括ICRC和地址錯誤。需要啟發式方法。

並非所有STATUS/ERROR位都適用於某些命令。這些不適用的位在輸出描述中標記為“na”,但在ATA/ATAPI-7之前,找不到“na”的定義。但是,ATA/ATAPI-8草案修訂版1f將“N/A”描述如下。

3.2.3.3a N/A

一個關鍵字,表示該欄位在本標準中沒有定義的值,不應由主機或裝置檢查。N/A欄位應清除為零。

因此,假設裝置將“na”位清除為零,因此無需顯式遮蔽,這似乎是合理的。

ATAPI 裝置 CHECK CONDITION

ATAPI 裝置 CHECK CONDITION 錯誤由 PACKET 命令傳輸 CDB 的最後一個位元組後,STATUS 暫存器中的 CHK 位(ERR 位)設定指示。對於此類錯誤,應獲取感知資料以收集有關錯誤的資訊。應使用 REQUEST SENSE 資料包命令來獲取感知資料。

一旦獲取了感知資料,此類錯誤的處理方式可以與其他 SCSI 錯誤類似。請注意,感知資料可能指示 ATA 匯流排錯誤(例如,感知鍵 04h HARDWARE ERROR && ASC/ASCQ 47h/00h SCSI PARITY ERROR)。在這種情況下,該錯誤應被視為 ATA 匯流排錯誤,並根據ATA 匯流排錯誤進行處理。

ATA 裝置錯誤 (NCQ)

NCQ 命令錯誤由 NCQ 命令階段(一個或多個未完成的 NCQ 命令)期間清除 BSY 並設定 ERR 位指示。雖然 STATUS 和 ERROR 暫存器將包含描述錯誤的有效值,但需要 READ LOG EXT 來清除錯誤條件,確定哪個命令失敗並獲取更多資訊。

READ LOG EXT 日誌頁面 10h 報告哪個標籤失敗以及描述錯誤的任務檔案暫存器值。使用此資訊,失敗的命令可以作為正常的 ATA 命令錯誤進行處理,如ATA/ATAPI 裝置錯誤(非 NCQ/非 CHECK CONDITION)中所述,所有其他正在執行的命令必須重試。請注意,此重試不應計算在內 - 如果沒有失敗的命令,以這種方式重試的命令很可能已經正常完成。

請注意,ATA 匯流排錯誤可以報告為 ATA 裝置 NCQ 錯誤。應按照ATA 匯流排錯誤中的描述進行處理。

如果 READ LOG EXT 日誌頁面 10h 失敗或報告 NQ,我們就徹底完蛋了。應根據HSM 違規處理此條件。

ATA 匯流排錯誤

ATA 匯流排錯誤表示資料在透過 ATA 匯流排(SATA 或 PATA)傳輸期間發生損壞。此型別的錯誤可以透過以下方式指示:

  • ATA/ATAPI 裝置錯誤(非 NCQ/非 CHECK CONDITION)中所述的 ICRC 或 ABRT 錯誤。

  • 具有指示傳輸錯誤的錯誤資訊的特定於控制器的錯誤完成。

  • 在某些控制器上,命令超時。在這種情況下,可能存在一種機制來確定超時是由於傳輸錯誤造成的。

  • 未知/隨機錯誤、超時和各種怪異現象。

如上所述,傳輸錯誤可能導致各種症狀,從裝置 ICRC 錯誤到隨機裝置鎖定,並且在許多情況下,無法判斷錯誤條件是否由於傳輸錯誤造成的;因此,在處理錯誤和超時時,有必要採用某種啟發式方法。例如,已知支援的命令重複出現 ABRT 錯誤很可能表明 ATA 匯流排錯誤。

一旦確定可能發生了 ATA 匯流排錯誤,降低 ATA 匯流排傳輸速度是可以緩解問題的措施之一。有關更多資訊,請參見重新配置傳輸

PCI 匯流排錯誤

在透過 PCI(或其他系統匯流排)傳輸期間發生的資料損壞或其他故障。對於標準 BMDMA,這由 BMDMA 狀態暫存器中的 Error 位指示。必須記錄此型別的錯誤,因為它表明系統存在非常嚴重的問題。建議重置主機控制器。

延遲完成

當發生超時並且超時處理程式發現超時的命令已成功完成或出現錯誤時,會發生這種情況。這通常是由中斷丟失引起的。必須記錄此型別的錯誤。建議重置主機控制器。

未知錯誤(超時)

這是當發生超時並且命令仍在處理或主機和裝置處於未知狀態時發生的情況。發生這種情況時,HSM 可能處於任何有效或無效狀態。為了使裝置進入已知狀態並使其忘記超時的命令,必須進行重置。可以重試超時的命令。

超時也可能是由傳輸錯誤引起的。有關更多詳細資訊,請參閱ATA 匯流排錯誤

熱插拔和電源管理異常

<<TODO: 在這裡填寫>>

EH 恢復操作

本節討論了幾種重要的恢復操作。

清除錯誤條件

許多控制器要求錯誤處理程式清除其錯誤暫存器。不同的控制器可能有不同的要求。

對於 SATA,強烈建議在錯誤處理期間至少清除 SError 暫存器。

重置

在 EH 期間,在以下情況下必須進行重置。

  • HSM 處於未知或無效狀態

  • HBA 處於未知或無效狀態

  • EH 需要使 HBA/裝置忘記正在執行的命令

  • HBA/裝置的表現很奇怪

無論錯誤條件如何,在 EH 期間重置可能是一個好主意,以提高 EH 的魯棒性。是否重置 HBA 和裝置中的一個或兩個取決於具體情況,但建議採用以下方案。

  • 當已知 HBA 處於就緒狀態但 ATA/ATAPI 裝置處於未知狀態時,僅重置裝置。

  • 如果 HBA 處於未知狀態,則重置 HBA 和裝置。

HBA 重置是特定於實現的。對於符合 taskfile/BMDMA PCI IDE 的控制器,如果 BMDMA 狀態是唯一的 HBA 上下文,則停止活動 DMA 事務可能就足夠了。但是,即使主要是 taskfile/BMDMA PCI IDE 的控制器也可能具有特定於實現的重置自身的要求和機制。這必須由特定的驅動程式來解決。

另一方面,ATA/ATAPI 標準詳細描述了重置 ATA/ATAPI 裝置的方法。

PATA 硬體重置

這是硬體發起的裝置重置,透過斷言 PATA RESET- 訊號來指示。雖然某些硬體提供了允許驅動程式直接調整 RESET- 訊號的暫存器,但沒有從軟體啟動硬體重置的標準方法。

軟體重置

這是透過將 CONTROL SRST 位開啟至少 5 微秒來實現的。PATA 和 SATA 都支援它,但在 SATA 的情況下,這可能需要特定於控制器的支援,因為在 BSY 位仍然設定的情況下,應該傳輸第二個 Register FIS 以清除 SRST。請注意,在 PATA 上,這將重置通道上的主裝置和從裝置。

EXECUTE DEVICE DIAGNOSTIC 命令

雖然 ATA/ATAPI 標準沒有明確描述,但 EDD 意味著某種程度的重置,可能與軟體重置的級別相似。主機端 EDD 協議可以使用正常的命令處理來處理,並且大多數 SATA 控制器應該能夠像處理其他命令一樣處理 EDD。與軟體重置一樣,EDD 會影響 PATA 總線上的兩個裝置。

雖然 EDD 會重置裝置,但這不適合錯誤處理,因為在設定 BSY 時無法發出 EDD,並且不清楚當裝置處於未知/怪異狀態時它將如何執行。

ATAPI DEVICE RESET 命令

這與軟體重置非常相似,只是重置可以限制為所選裝置,而不會影響共享電纜的其他裝置。

SATA PHY 重置

這是重置 SATA 裝置的首選方法。實際上,它與 PATA 硬體重置相同。請注意,這可以使用標準 SCR 控制暫存器來完成。因此,它通常比軟體重置更容易實現。

重置裝置時要考慮的另一件事是,重置會清除某些配置引數,並且需要在重置後將它們設定為其先前或新調整的值。

受影響的引數是。

  • 使用 INITIALIZE DEVICE PARAMETERS 設定的 CHS(很少使用)

  • 使用 SET FEATURES 設定的引數,包括傳輸模式設定

  • 使用 SET MULTIPLE MODE 設定的塊計數

  • 其他引數 (SET MAX, MEDIA LOCK...)

ATA/ATAPI 標準規定某些引數必須在硬體或軟體重置期間保持不變,但沒有嚴格規定所有引數。始終在重置後重新配置所需的引數對於魯棒性是必需的。請注意,這也適用於從深度睡眠(斷電)恢復時。

此外,ATA/ATAPI 標準要求在更新任何配置引數或進行硬體重置後發出 IDENTIFY DEVICE/IDENTIFY PACKET DEVICE,並且結果用於進一步操作。OS 驅動程式需要實現重新驗證機制以支援此功能。

重新配置傳輸

對於 PATA 和 SATA,許多角落都為了廉價的聯結器、電纜或控制器而被削減,並且看到高傳輸錯誤率是很常見的。可以透過降低傳輸速度來緩解這種情況。

以下是 Jeff Garzik 建議的一種可能的方案。

如果在 15 分鐘內發生超過 $N (3?) 個傳輸錯誤,

  • 如果是 SATA,則降低 SATA PHY 速度。如果無法降低速度,

  • 則降低 UDMA 傳輸速度。如果在 UDMA0,則切換到 PIO4,

  • 降低 PIO 傳輸速度。如果在 PIO3,則抱怨,但繼續

ata_piix 內部

int ich_pata_cable_detect(struct ata_port *ap)

探測主機控制器電纜檢測資訊

引數

struct ata_port *ap

需要電纜檢測資訊的埠

從 ATA PCI 裝置的 PCI 配置暫存器中讀取 80c 電纜指示器。此暫存器通常由韌體 (BIOS) 設定。

鎖定:無(從呼叫者繼承)。

int piix_pata_prereset(struct ata_link *link, unsigned long deadline)

PATA 主機控制器的 prereset

引數

struct ata_link *link

目標連結

unsigned long deadline

操作的截止時間 jiffies

鎖定:無(從呼叫者繼承)。

void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)

初始化主機控制器 PATA PIO 時序

引數

struct ata_port *ap

我們要配置時序的埠

struct ata_device *adev

有問題的驅動器

在主機控制器 PCI 配置空間中,為裝置設定 PIO 模式。

鎖定:無(從呼叫者繼承)。

void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, int isich)

初始化主機控制器 PATA PIO 時序

引數

struct ata_port *ap

我們要配置時序的埠

struct ata_device *adev

有問題的驅動器

int isich

如果晶片是 ICH 裝置,則設定

在主機控制器 PCI 配置空間中,為裝置設定 UDMA 模式。

鎖定:無(從呼叫者繼承)。

void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev)

初始化主機控制器 PATA DMA 時序

引數

struct ata_port *ap

我們要配置時序的埠

struct ata_device *adev

um

在主機控制器 PCI 配置空間中,為裝置設定 MW/UDMA 模式。

鎖定:無(從呼叫者繼承)。

void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev)

初始化主機控制器 PATA DMA 時序

引數

struct ata_port *ap

我們要配置時序的埠

struct ata_device *adev

um

在主機控制器 PCI 配置空間中,為裝置設定 MW/UDMA 模式。

鎖定:無(從呼叫者繼承)。

int piix_check_450nx_errata(struct pci_dev *ata_dev)

檢查問題 450NX 設定

引數

struct pci_dev *ata_dev

要檢查的 PCI 裝置

檢查是否存在 450NX 勘誤 #19 和勘誤 #25。如果找到它們,則返回一個錯誤程式碼,以便我們可以關閉 DMA

int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)

向核心服務註冊 PIIX ATA PCI 裝置

引數

struct pci_dev *pdev

要註冊的 PCI 裝置

const struct pci_device_id *ent

pdev 匹配的 piix_pci_tbl 中的條目

從核心 PCI 層呼叫。我們探測組合模式(嘆息),然後將控制權交給 libata,讓它完成其餘的工作。

鎖定:繼承自 PCI 層(可能會休眠)。

返回

成功時為零,或 -ERRNO 值。

sata_sil 內部

int sil_set_mode(struct ata_link *link, struct ata_device **r_failed)

包裝 set_mode 函式

引數

struct ata_link *link

要設定的連結

struct ata_device **r_failed

我們失敗時返回的裝置

包裝 libata 裝置設定方法,因為在設定後,我們需要檢查結果並進行一些配置工作

void sil_dev_config(struct ata_device *dev)

應用裝置/主機特定的勘誤修復

引數

struct ata_device *dev

要檢查的裝置

在 IDENTIFY [PACKET] DEVICE 步驟完成後,並且已知裝置存在後,將呼叫此函式。我們應用兩個特定於 Silicon Image 的勘誤修復、Seagate 和 Maxtor 修復。

對於某些 Seagate 裝置,我們必須將最大扇區數限制在 8K 以下。

對於某些 Maxtor 裝置,我們不得將驅動器程式設計為超過 udma5。

這兩個修復都非常悲觀。一旦我獲得有關這些勘誤的更多資訊,我將建立一個更詳盡的列表,並將修復僅應用於需要它的特定裝置/主機/韌體。

20040111 - 受 Mod15Write 錯誤影響的 Seagate 驅動器是怪異的。Maxtor 怪異現象位於 sil_quirks 中,但我保留了原始的悲觀修復,原因如下... - 似乎關於它的資訊較少,僅從 Windows 驅動程式中收集到一個裝置,可能僅影響一個裝置。如果能提供更多資訊,將不勝感激。 - 但話說回來,UDMA5 也沒什麼好抱怨的

謝謝

大部分 ATA 知識都來自與 Andre Hedrick (www.linux-ide.org) 的長時間對話,以及長時間思考 ATA 和 SCSI 規範。

感謝 Alan Cox 指出 SATA 和 SCSI 之間的相似之處,以及通常激勵我們破解 libata。

libata 的裝置檢測方法 ata_pio_devchk,以及通常所有早期探測都基於對 Hale Landis 的 ATADRVR 驅動程式 (www.ata-atapi.com) 中探測/重置程式碼的廣泛研究。