SAS 層¶
SAS 層是一個管理基礎架構,用於管理 SAS LLDD。它位於 SCSI Core 和 SAS LLDD 之間。佈局如下:SCSI Core 關注 SAM/SPC 問題,SAS LLDD+Sequencer 關注物理層/OOB/鏈路管理,而 SAS 層則關注
SAS Phy/Port/HA 事件管理(LLDD 生成,SAS 層處理),
SAS 埠管理(建立/銷燬),
SAS 域發現和重新驗證,
SAS 域裝置管理,
SCSI 主機註冊/取消註冊,
向 SCSI Core (SAS) 或 libata (SATA) 註冊裝置,以及
擴充套件器管理以及將擴充套件器控制匯出到使用者空間。
SAS LLDD 是一個 PCI 裝置驅動程式。它關注物理層/OOB 管理和供應商特定任務,並向 SAS 層生成事件。
SAS 層執行 SAS 1.1 規範中概述的大部分 SAS 任務。
sas_ha_struct 描述了 SAS LLDD 到 SAS 層。它的大部分由 SAS 層使用,但一些欄位需要由 LLDD 初始化。
初始化硬體後,從 probe() 函式中呼叫 sas_register_ha()。它會將您的 LLDD 註冊到 SCSI 子系統,建立一個 SCSI 主機,並將您的 SAS 驅動程式註冊到它建立的 sysfs SAS 樹。然後它將返回。然後啟用您的物理層以實際啟動 OOB(此時您的驅動程式將開始呼叫 notify_* 事件回撥)。
結構體描述¶
struct sas_phy¶
通常,這會靜態嵌入到驅動程式的 phy 結構中
struct my_phy {
blah;
struct sas_phy sas_phy;
bleh;
};
然後所有的物理層都是 HA 結構中的 my_phy 陣列(如下所示)。
然後,隨著您的進行並初始化您的物理層,您還會初始化 sas_phy 結構體,以及您自己的物理層結構。
一般來說,物理層由 LLDD 管理,埠由 SAS 層管理。因此,物理層由 LLDD 初始化和更新,埠由 SAS 層初始化和更新。
有一種方案,LLDD 可以讀寫某些欄位,而 SAS 層只能讀取這些欄位,反之亦然。這樣做的目的是避免不必要的鎖定。
- enabled
必須設定 (0/1)
- id
必須設定 [0,MAX_PHYS)]
- class, proto, type, role, oob_mode, linkrate
必須設定
- oob_mode
您在 OOB 完成後設定此項,然後通知 SAS 層。
- sas_addr
這通常指向一個數組,該陣列儲存物理層的 SAS 地址,可能在您的 my_phy 結構中的某個位置。
- attached_sas_addr
當您 (LLDD) 收到 IDENTIFY 幀或 FIS 幀時,在通知 SAS 層 _之前_ 設定此項。這樣做的想法是,有時 LLDD 可能希望偽造或在該物理層/埠上提供不同的 SAS 地址,這允許它這樣做。最好的情況是,您應該從 IDENTIFY 幀複製 SAS 地址,或者可能為直接連線的 SATA 裝置生成 SAS 地址。Discover 過程可能會稍後更改此地址。
- frame_rcvd
這是您在收到 IDENTIFY/FIS 幀時複製幀的地方;您鎖定、複製、設定 frame_rcvd_size 並釋放鎖定,然後呼叫事件。它是一個指標,因為無法 _完全_ 知道您的硬體幀大小,因此您在 phy 結構中定義實際陣列,並讓此指標指向它。您將幀從 DMAable 記憶體複製到儲存鎖定的區域。
- sas_prim
這是接收到基元時基元的去向。請參閱 sas.h。獲取鎖定,設定基元,釋放鎖定,通知。
- port
如果物理層屬於埠,則這指向 sas_port -- LLDD 僅讀取此項。它指向此物理層所屬的 sas_port。由 SAS 層設定。
- ha
可以設定;SAS 層無論如何都會設定它。
- lldd_phy
您應該將其設定為指向您的 phy,以便在 SAS 層呼叫您的回撥之一併將 phy 傳遞給您時,您可以更快地找到您的 phy。如果嵌入了 sas_phy,您也可以使用 container_of -- 無論您喜歡什麼。
struct sas_port¶
LLDD 不設定此結構體的任何欄位 -- 它只讀取它們。它們應該是自我解釋的。
phy_mask 是 32 位,這現在應該足夠了,因為我還沒有聽說過 HA 擁有超過 8 個物理層。
- lldd_port
我還沒有找到它的用途 -- 也許其他希望擁有內部埠表示的 LLDD 可以使用它。
struct sas_ha_struct¶
它通常在您自己的 LLDD 結構中靜態宣告,用於描述您的介面卡
struct my_sas_ha {
blah;
struct sas_ha_struct sas_ha;
struct my_phy phys[MAX_PHYS];
struct sas_port sas_ports[MAX_PHYS]; /* (1) */
bleh;
};
(1) If your LLDD doesn't have its own port representation.
需要初始化什麼(下面給出了示例函式)。
pcidev¶
- sas_addr
由於 SAS 層不想弄亂記憶體分配等,因此這指向靜態分配的陣列中的某個位置(例如,在您的主機介面卡結構中),並儲存您或製造商等給出的主機介面卡的 SAS 地址。
sas_port¶
- sas_phy
指向結構體的指標陣列。(請參閱上面關於 sas_addr 的註釋)。這些必須設定。請參閱下面的更多註釋。
- num_phys
sas_phy 陣列中存在的物理層數量和 sas_port 陣列中存在的埠數量。最多可以有 num_phys 個埠(每個埠一個),因此我們刪除 num_ports,僅使用 num_phys。
事件介面
/* LLDD calls these to notify the class of an event. */
void sas_notify_port_event(struct sas_phy *, enum port_event, gfp_t);
void sas_notify_phy_event(struct sas_phy *, enum phy_event, gfp_t);
埠通知
/* The class calls these to notify the LLDD of an event. */
void (*lldd_port_formed)(struct sas_phy *);
void (*lldd_port_deformed)(struct sas_phy *);
如果 LLDD 希望在形成或變形埠時收到通知,它會將這些設定為滿足型別的函式。
SAS LLDD 還應至少實現 SAM 中描述的一項任務管理功能 (TMF)
/* Task Management Functions. Must be called from process context. */
int (*lldd_abort_task)(struct sas_task *);
int (*lldd_abort_task_set)(struct domain_device *, u8 *lun);
int (*lldd_clear_task_set)(struct domain_device *, u8 *lun);
int (*lldd_I_T_nexus_reset)(struct domain_device *);
int (*lldd_lu_reset)(struct domain_device *, u8 *lun);
int (*lldd_query_task)(struct sas_task *);
有關更多資訊,請閱讀 T10.org 上的 SAM。
埠和介面卡管理
/* Port and Adapter management */
int (*lldd_clear_nexus_port)(struct sas_port *);
int (*lldd_clear_nexus_ha)(struct sas_ha_struct *);
SAS LLDD 應至少實現其中一項。
物理層管理
/* Phy management */
int (*lldd_control_phy)(struct sas_phy *, enum phy_func);
- lldd_ha
將其設定為指向您的 HA 結構。如果如上所示嵌入它,您也可以使用 container_of。
示例初始化和註冊函式可以如下所示(從 probe() 中最後呼叫)但是 在您啟用物理層以執行 OOB 之前
static int register_sas_ha(struct my_sas_ha *my_ha)
{
int i;
static struct sas_phy *sas_phys[MAX_PHYS];
static struct sas_port *sas_ports[MAX_PHYS];
my_ha->sas_ha.sas_addr = &my_ha->sas_addr[0];
for (i = 0; i < MAX_PHYS; i++) {
sas_phys[i] = &my_ha->phys[i].sas_phy;
sas_ports[i] = &my_ha->sas_ports[i];
}
my_ha->sas_ha.sas_phy = sas_phys;
my_ha->sas_ha.sas_port = sas_ports;
my_ha->sas_ha.num_phys = MAX_PHYS;
my_ha->sas_ha.lldd_port_formed = my_port_formed;
my_ha->sas_ha.lldd_dev_found = my_dev_found;
my_ha->sas_ha.lldd_dev_gone = my_dev_gone;
my_ha->sas_ha.lldd_execute_task = my_execute_task;
my_ha->sas_ha.lldd_abort_task = my_abort_task;
my_ha->sas_ha.lldd_abort_task_set = my_abort_task_set;
my_ha->sas_ha.lldd_clear_task_set = my_clear_task_set;
my_ha->sas_ha.lldd_I_T_nexus_reset= NULL; (2)
my_ha->sas_ha.lldd_lu_reset = my_lu_reset;
my_ha->sas_ha.lldd_query_task = my_query_task;
my_ha->sas_ha.lldd_clear_nexus_port = my_clear_nexus_port;
my_ha->sas_ha.lldd_clear_nexus_ha = my_clear_nexus_ha;
my_ha->sas_ha.lldd_control_phy = my_control_phy;
return sas_register_ha(&my_ha->sas_ha);
}
SAS 1.1 未定義 I_T Nexus Reset TMF。
事件¶
事件是 SAS LLDD 通知 SAS 層任何事情的 唯一方式。沒有其他方法或方式 LLDD 告訴 SAS 層內部或 SAS 域中發生的任何事情。
物理層事件
PHYE_LOSS_OF_SIGNAL, (C)
PHYE_OOB_DONE,
PHYE_OOB_ERROR, (C)
PHYE_SPINUP_HOLD.
埠事件,在 _phy_ 上傳遞
PORTE_BYTES_DMAED, (M)
PORTE_BROADCAST_RCVD, (E)
PORTE_LINK_RESET_ERR, (C)
PORTE_TIMER_EVENT, (C)
PORTE_HARD_RESET.
- 主機介面卡事件
HAE_RESET
SAS LLDD 應該能夠生成
來自 C 組的至少一個事件(選擇),
標記為 M(強制)的事件是強制性的(只有一個),
如果它希望 SAS 層處理域重新驗證,則標記為 E(擴充套件器)的事件(只有一個)。
未標記的事件是可選的。
含義
- HAE_RESET
當您的 HA 出現內部錯誤並重置時。
- PORTE_BYTES_DMAED
在接收到 IDENTIFY/FIS 幀時
- PORTE_BROADCAST_RCVD
在接收到基元時
- PORTE_LINK_RESET_ERR
計時器到期、訊號丟失、DWS 丟失等。[1]
- PORTE_TIMER_EVENT
DWS 重置超時計時器到期[1]
- PORTE_HARD_RESET
接收到硬重置基元。
- PHYE_LOSS_OF_SIGNAL
裝置已消失[1]
- PHYE_OOB_DONE
OOB 執行良好,並且 oob_mode 有效
- PHYE_OOB_ERROR
執行 OOB 時出錯,裝置可能已斷開連線。[1]
- PHYE_SPINUP_HOLD
SATA 存在,未傳送 COMWAKE。
執行命令 SCSI RPC
int (*lldd_execute_task)(struct sas_task *, gfp_t gfp_flags);
用於將任務排隊到 SAS LLDD。@task 是要執行的任務。@gfp_mask 是定義呼叫者上下文的 gfp_mask。
此函式應實現執行命令 SCSI RPC,
也就是說,當呼叫 lldd_execute_task() 時,命令會 立即 在傳輸層上發出。在 SAS LLDD 中 沒有 任何型別的排隊,也沒有任何級別。
返回值
-SAS_QUEUE_FULL, -ENOMEM, 未排隊任何內容;
0,任務已排隊。
struct sas_task {
dev -- the device this task is destined to
task_proto -- _one_ of enum sas_proto
scatter -- pointer to scatter gather list array
num_scatter -- number of elements in scatter
total_xfer_len -- total number of bytes expected to be transferred
data_dir -- PCI_DMA_...
task_done -- callback when the task has finished execution
};
發現¶
sysfs 樹具有以下目的
它向您顯示當前 SAS 域的物理佈局,即域在物理世界中的當前外觀。
顯示一些 _在_發現_時_ 的裝置引數。
這是一個指向 tree(1) 程式的連結,它在檢視 SAS 域時非常有用:ftp://mama.indstate.edu/linux/tree/
我希望使用者空間應用程式實際建立此圖形介面。
也就是說,sysfs 域樹不會顯示或保持狀態,例如,如果您更改 READY LED MEANING 設定的含義,但它會向您顯示域裝置的當前連線狀態。
保持內部裝置狀態更改是上層(命令集驅動程式)和使用者空間的責任。
當一個或多個裝置從域中拔出時,這會立即反映在 sysfs 樹中,並且裝置將從系統中刪除。
結構體 domain_device 描述了 SAS 域中的任何裝置。它完全由 SAS 層管理。任務指向域裝置,這是 SAS LLDD 知道將任務傳送到哪裡。SAS LLDD 僅讀取 domain_device 結構的內容,但它永遠不會建立或銷燬一個。
來自使用者空間的擴充套件器管理¶
在 sysfs 的每個擴充套件器目錄中,都有一個名為“smp_portal”的檔案。它是一個二進位制 sysfs 屬性檔案,它實現了一個 SMP 門戶(注意:這 不是 SMP 埠),使用者空間應用程式可以向其傳送 SMP 請求並接收 SMP 響應。
功能非常簡單
構建您要傳送的 SMP 幀。格式和佈局在 SAS 規範中描述。將 CRC 欄位設定為 0。
open(2)
以 RW 模式開啟擴充套件器的 SMP 門戶 sysfs 檔案。
write(2)
寫入您在 1 中構建的幀。
read(2)
讀取您期望接收到的幀的資料量。如果您收到與您期望接收到的資料量不同的資料,則出現某種錯誤。
close(2)
所有這些過程都在檔案“expander_conf.c”中的函式 do_smp_func() 及其呼叫者中詳細顯示。
核心功能在檔案“sas_expander.c”中實現。
程式“expander_conf.c”實現了這一點。它接受一個引數,即擴充套件器的 SMP 門戶的 sysfs 檔名,並提供擴充套件器資訊,包括路由表。
SMP 門戶讓您完全控制擴充套件器,因此請小心。