關(guān)于linux SCSI 子系統(tǒng)
Small Computer Systems Interface (SCSI) 是一組標(biāo)準(zhǔn)集,它定義了與大量設(shè)備(主要是與存儲(chǔ)相關(guān)的設(shè)備)通信所需的接口和協(xié)議。 Linux? 提供了一種 SCSI 子系統(tǒng),用于與這些設(shè)備通信。Linux 是分層架構(gòu)的一個(gè)很好的例子,它將高層的驅(qū)動(dòng)器(比如磁盤驅(qū)動(dòng)器或光驅(qū))連接到物理接口,比如 Fibre Channel 或 Serial Attached SCSI(SAS).
scsi設(shè)備:機(jī)器外設(shè)總線是計(jì)算機(jī)內(nèi)部與外設(shè)進(jìn)行通訊的總線,分為IDE總線,SCSI總線和USB總線.IDE總線是PC機(jī)上用得最多的總線,其造價(jià)比較便 宜.SCSI總線的速度比IDE總線要快得多,不過造價(jià)比較高.IDE總線和SCSI總線一般只于硬盤,光驅(qū)和掃描儀等,而USB總線則可以用于更多的外 設(shè),且速度更快.一般來說,這三種外設(shè)總線是不可以混合使用的,但如果有總線轉(zhuǎn)換器則可以在一定程度上混合使用,如SCSI總線就可以有向IDE總線進(jìn)行 轉(zhuǎn)換的轉(zhuǎn)換器.
SCSI-3 的開發(fā)開始于 1993 年,現(xiàn)已成為了一組標(biāo)準(zhǔn)集,可以定義協(xié)議、命令集和信令方法。在 SCSI-3 中,包含一組命名為 Ultra 的并行 SCSI 標(biāo)準(zhǔn)和基于串行 SCSI 的協(xié)議,比如 IEEE 1394 (FireWire)、Fibre Channel, 、Internet SCSI (iSCSI) 和新興的 SAS。這些標(biāo)準(zhǔn)通過引入存儲(chǔ)網(wǎng)絡(luò)技術(shù)(比如 FC-AL 或 iSCSI)改變了傳統(tǒng)的存儲(chǔ)理念,將數(shù)據(jù)速率擴(kuò)展到了 1 Gbit/s,將最大的可尋址設(shè)備數(shù)增加到了 100 以上,并將最大的電纜長度擴(kuò)展到了 25 米。圖 1 展示了從 1986 至 2007 年 SCSI 的數(shù)據(jù)速率的變化 .
SCSI 傳輸所采用的協(xié)議已經(jīng)時(shí)過境遷,SCSI 命令卻保持了最初的元素。SCSI 命令是在 Command Descriptor Block (CDB) 中定義的。CDB 包含了用來定義要執(zhí)行的特定操作的操作代碼,以及大量特定于操作的參數(shù)。
SCSI 命令支持讀寫數(shù)據(jù)(各有四個(gè)變量)以及很多非數(shù)據(jù)命令,比如 test-unit-ready(設(shè)備是否已就緒)、inquiry(檢索有關(guān)目標(biāo)設(shè)備的基本信息)、read-capacity(檢索目標(biāo)設(shè)備的存儲(chǔ)容 量)等等。目標(biāo)設(shè)備支持何種命令取決于設(shè)備的類型。發(fā)起者通過 inquiry 命令識(shí)別設(shè)備類型。表 1 列出了最常用的 SCSI 命令。
表 1. 常見 SCSI 命令
命令用途Test unit readyInquiryRequest senseRead capacityReadWriteMode senseMode select
查詢?cè)O(shè)備是否已經(jīng)準(zhǔn)備好進(jìn)行傳輸 |
請(qǐng)求設(shè)備基本信息 |
請(qǐng)求之前命令的錯(cuò)誤信息 |
請(qǐng)求存儲(chǔ)容量信息 |
從設(shè)備讀取數(shù)據(jù) |
向設(shè)備寫入數(shù)據(jù) |
請(qǐng)求模式頁面(設(shè)備參數(shù)) |
在模式頁面配置設(shè)備參數(shù) |
借助大約 60 種可用命令,SCSI 可適用于許多設(shè)備(包括隨機(jī)存取設(shè)備,比如磁盤和像磁帶這樣的順序存儲(chǔ)設(shè)備)。SCSI 也提供了專門的命令以訪問箱體服務(wù)(比如存儲(chǔ)箱體內(nèi)部當(dāng)前的傳感和溫度)。
Linux 內(nèi)核中的 SCSI 架構(gòu)
圖 2 顯示了 SCSI 子系統(tǒng)在 Linux 內(nèi)核中的位置。內(nèi)核的頂部是系統(tǒng)調(diào)用接口,處理用戶空間調(diào)用到內(nèi)核中合適的目的地的路由(例如 open、read 或 write)。而虛擬文件系統(tǒng)(VFS) 是內(nèi)核中支持的大多數(shù)文件系統(tǒng)的抽象層。它負(fù)責(zé)將請(qǐng)求路由到合適的文件系統(tǒng)。大多數(shù)文件系統(tǒng)都通過緩沖區(qū)緩存來相互通信,這種緩存通過緩存最近使用的數(shù)據(jù) 來優(yōu)化對(duì)物理設(shè)備的訪問。接下來是塊設(shè)備驅(qū)動(dòng)器層,它包括針對(duì)底層設(shè)備的各種塊驅(qū)動(dòng)器。SCSI 子系統(tǒng)是這種塊設(shè)備驅(qū)動(dòng)器之一。
與 Linux 內(nèi)核中的其他主流子系統(tǒng)不同,SCSI 子系統(tǒng)是一種分層的架構(gòu),共分為三層。頂部的那層叫做較高層,代表的是內(nèi)核針對(duì) SCSI 和主要設(shè)備類型的驅(qū)動(dòng)器的最高接口。接下來的是中間層,也稱為公共層或統(tǒng)一層。在這一層包含 SCSI 堆棧的較高層和較低層的一些公共服務(wù)。最后是較低層,代表的是適用于 SCSI 的物理接口的實(shí)際驅(qū)動(dòng)器(參見圖 3)
圖 3. Linux SCSI 子系統(tǒng)的分層架構(gòu)
SCSI 較高層
SCSI 子系統(tǒng)的較高層代表的是內(nèi)核(設(shè)備級(jí))最高級(jí)別的接口。它由一組驅(qū)動(dòng)器組成,比如塊設(shè)備(SCSI 磁盤和 SCSI CD-ROM)和字符設(shè)備(SCSI 磁帶和 SCSI generic)。較高層接受來自上層(比如 VFS)的請(qǐng)求并將其轉(zhuǎn)換成 SCSI 請(qǐng)求。較高層負(fù)責(zé)完成 SCSI 命令并將狀態(tài)信息通知上層。
SCSI 磁盤驅(qū)動(dòng)器在 ./linux/drivers/scsi/sd.c 內(nèi)實(shí)現(xiàn)。SCSI 磁盤驅(qū)動(dòng)器通過調(diào)用 register_blkdev(作為塊驅(qū)動(dòng)器)進(jìn)行自初始化并通過 scsi_register_driver 提供一組函數(shù)以表示所有 SCSI 設(shè)備。其中 sd_probe 和 sd_init_command 這兩個(gè)函數(shù)很重要。只要有新的 SCSI 設(shè)備附加到系統(tǒng), SCSI 中間層就會(huì)調(diào)用 sd_probe 函數(shù)。sd_probe 函數(shù)可決定此設(shè)備是否由 SCSI 磁盤驅(qū)動(dòng)器管理,如果是,就創(chuàng)建新的 scsi_disk 結(jié)構(gòu)來表示它。sd_init_command 函數(shù)將來自文件系統(tǒng)層的請(qǐng)求轉(zhuǎn)變成 SCSI 讀或?qū)懨睿橥瓿蛇@個(gè) I/O 請(qǐng)求,sd_rw_intr 會(huì)被調(diào)用)。
SCSI 磁帶驅(qū)動(dòng)器在 ./linux/drivers/scsi/st.c 內(nèi)實(shí)現(xiàn)。磁帶驅(qū)動(dòng)器是順序存取設(shè)備,會(huì)通過 register_chrdev_region 將自身注冊(cè)為字符設(shè)備。SCSI 磁帶驅(qū)動(dòng)器還提供了一個(gè) probe 函數(shù),稱為 st_probe。該函數(shù)會(huì)創(chuàng)建一種新磁帶設(shè)備并將其添加到稱為 scsi_tapes 的向量。SCSI 磁帶驅(qū)動(dòng)器的獨(dú)特之處在于,如果可能,它可以直接從用戶空間執(zhí)行 I/O 傳輸。否則,數(shù)據(jù)會(huì)通過驅(qū)動(dòng)器緩沖被分段。
SCSI CD-ROM 驅(qū)動(dòng)器在 ./linux/drivers/scsi/sr.c 內(nèi)實(shí)現(xiàn)。CD-ROM 驅(qū)動(dòng)器是另一種塊設(shè)備并為 SCSI 磁盤驅(qū)動(dòng)器提供類似的函數(shù)集。sr_probe 函數(shù)可用來創(chuàng)建 scsi_sd 結(jié)構(gòu)以表示 CD-ROM 設(shè)備,并用 register_cdrom 注冊(cè)此 CD-ROM。SCSI 磁帶驅(qū)動(dòng)器還會(huì)導(dǎo)出 sr_init_command,以將請(qǐng)求轉(zhuǎn)換成 SCSI CD-ROM 讀或?qū)懻?qǐng)求。
SCSI generic 驅(qū)動(dòng)器在 ./linux/drivers/scsi/sg.c 內(nèi)實(shí)現(xiàn)。該驅(qū)動(dòng)器允許用戶應(yīng)用程序向設(shè)備發(fā)送 SCSI 命令(比如格式化、模式感知或診斷命令)。通過 sg3utils 包還可以從用戶空間利用 SCSI generic 驅(qū)動(dòng)器。這個(gè)用戶空間包包括多種實(shí)用工具,可用來發(fā)送 SCSI 命令和解析這些命令的響應(yīng)。
SCSI 中間層
SCSI 中間層是 SCSI 較高層和較低層的公共服務(wù)層(可以在 ./linux/drivers/scsi/scsi.c 內(nèi)部分地實(shí)現(xiàn))。它提供了很多可供較高層和較低層驅(qū)動(dòng)器使用的函數(shù),因而可以充當(dāng)這兩層間的連接層。中間層很重要,原因是它抽象化了較低層驅(qū)動(dòng)器 (LLD)的實(shí)現(xiàn),可以在 ./linux/drivers/scsi/hosts.c 中部分地實(shí)現(xiàn)。這意味著可以以同樣的方式使用帶不同接口的 Fibre Channel 主機(jī)總線適配器(HBA)。
低層驅(qū)動(dòng)器注冊(cè)和錯(cuò)誤處理都由 SCSI 中間層提供。中間層還提供了較高層和較低層間的 SCSI 命令排隊(duì)。SCSI 中間層的一個(gè)重要功能是將來自較高層的命令請(qǐng)求轉(zhuǎn)換成 SCSI 請(qǐng)求。它也負(fù)責(zé)管理特定于 SCSI 的錯(cuò)誤恢復(fù)。
中間層可以連接 SCSI 子系統(tǒng)的較高層和較低層。它接受對(duì) SCSI 事務(wù)的請(qǐng)求并對(duì)這些請(qǐng)求進(jìn)行排隊(duì)以便處理 (如 ./linux/drivers/scsi/scsi_lib.c 中所示)。當(dāng)這些命令完成后,它接受來自 LLD 的 SCSI 響應(yīng)并通知較較高層此請(qǐng)求已經(jīng)完成。
中間層最重要的職責(zé)之一是錯(cuò)誤和超時(shí)處理。如果 SCSI 命令沒有在合理的時(shí)間內(nèi)完成或者 SCSI 請(qǐng)求返回錯(cuò)誤,中間層就會(huì)管理錯(cuò)誤或重新發(fā)送此請(qǐng)求。中間層還可管理較高層恢復(fù),比如請(qǐng)求 HBA (LLD) 或 SCSI 設(shè)備重置。SCSI 錯(cuò)誤和超時(shí)處理程序在 ./linux/drivers/scsi/scsi_error.c 內(nèi)實(shí)現(xiàn)。
SCSI 較低層
在最低層的是一組驅(qū)動(dòng)器,稱為 SCSI 低層驅(qū)動(dòng)器。它們是一些可與物理設(shè)備(比如 HBA)鏈接的特定驅(qū)動(dòng)器。LLD 提供了自公共中間層到特定于設(shè)備的 HBA 的一種抽象。每個(gè) LLD 都提供了到特定底層硬件的接口,但所使用的到中間層的接口卻是一組標(biāo)準(zhǔn)接口。
較低層包含大量代碼,原因是它要負(fù)責(zé)處理各種不同的 SCSI 適配器類型。例如,F(xiàn)ibre Channel 協(xié)議包含了針對(duì) Emulex 和 QLogic 的各種適配器的 LLD。面向 Adaptec 和 LSI 的 SAS 適配器的 LLD 也包括在內(nèi)。
SCSI 客戶機(jī)/服務(wù)器模型
在主機(jī)和存儲(chǔ)介質(zhì)進(jìn)行通信期間,主機(jī)通常充當(dāng) SCSI 啟動(dòng)程序。在計(jì)算機(jī)存儲(chǔ)中,SCSI 啟動(dòng)程序是啟動(dòng) SCSI 會(huì)話的端點(diǎn),這意味著它會(huì)發(fā)送 SCSI 命令。存儲(chǔ)介質(zhì)通常充當(dāng) SCSI 目標(biāo),它接收和處理 SCSI 命令。SCSI 目標(biāo)等待啟動(dòng)程序的命令,然后提供請(qǐng)求的輸入/輸出數(shù)據(jù)轉(zhuǎn)換。
SCSI 目標(biāo)通常為啟動(dòng)程序提供一個(gè)或多個(gè)邏輯單元號(hào)(LUN)。在計(jì)算機(jī)存儲(chǔ)介質(zhì)上,LUN 僅是分配給邏輯單元的號(hào)碼。邏輯單元是一個(gè) SCSI 協(xié)議實(shí)體,實(shí)際的 I/O 操作只處理這種實(shí)體。每個(gè) SCSI 目標(biāo)可以提供一個(gè)或多個(gè)邏輯單元;它本身不執(zhí)行 I/O,但代替特定的邏輯單元執(zhí)行。
在存儲(chǔ)區(qū)域中,LUN 通常表示一個(gè)主機(jī)能夠執(zhí)行讀寫操作的 SCSI 磁盤。圖 1 顯示 SCSI 客戶機(jī)/服務(wù)器模型是如何工作的。
啟動(dòng)程序首先向目標(biāo)發(fā)送命令,然后目標(biāo)解碼命令并向啟動(dòng)程序請(qǐng)求數(shù)據(jù),或?qū)?shù)據(jù)發(fā)送給啟動(dòng)程序。在這之后,目標(biāo)將狀態(tài)發(fā)送給啟動(dòng)程序。如果狀態(tài)損壞,啟動(dòng)程序?qū)⑾蚰繕?biāo)發(fā)送一個(gè)請(qǐng)求檢測(cè)(sense)指令。目標(biāo)將返回檢測(cè)數(shù)據(jù),告知啟動(dòng)程序哪里出錯(cuò)。
現(xiàn)在我們研究與存儲(chǔ)相關(guān)的 SCSI 命令。
Linux 通用 SCSI 驅(qū)動(dòng)器
Linux 中的 SCSI 設(shè)備的命名方式能夠幫助用戶識(shí)別設(shè)備。例如,第一個(gè) SCSI CD-ROM 是 /dev/scd0。SCSI 磁盤的標(biāo)簽為 /dev/sda、/dev/sdb 和 /dev/sdc 等。當(dāng)設(shè)備初始化完成時(shí),Linux SCSI 磁盤驅(qū)動(dòng)器接口僅發(fā)送 SCSI READ 和 WRITE 命令。
這些 SCSI 設(shè)備可能具有通用的名稱和接口,比如 /dev/sg0、/dev/sg1 或 /dev/sga、/dev/sgb 等。通過這些通用的 驅(qū)動(dòng)器接口,您就可以將 SCSI 命令直接發(fā)送到 SCSI 設(shè)備,而不需要經(jīng)過在 SCSI 磁盤上創(chuàng)建(并裝載到某個(gè)目錄)的文件系統(tǒng)。在圖 2 中,您可以看到不同的應(yīng)用程序如何與 SCSI 設(shè)備通信。
圖 2. 與 SCSI 設(shè)備通信的各種方式
通過 Linux 通用驅(qū)動(dòng)器接口,您可以構(gòu)建能夠向 SCSI 設(shè)備發(fā)送更多 SCSI 命令的應(yīng)用程序。也就是說您又多了一種選擇。要確定哪個(gè) SCSI 設(shè)備表示某個(gè) sg 接口,您可以使用 sg_map 命令列出所有映射:
[root@taomaoy ~]# sg_map -i /dev/sg0 /dev/sda ATA ST3160812AS 3.AA /dev/sg1 /dev/scd0 HL-DT-ST RW/DVD GCC-4244N 1.02 |
如何使用 Red Hat 或 Fedora,則要安裝 sg3_utils。現(xiàn)在我們看看如何執(zhí)行典型的 SCSI 系統(tǒng)調(diào)用命令。
典型的 SCSI 通用驅(qū)動(dòng)器命令
對(duì)于字符設(shè)備,SCSI 通用驅(qū)動(dòng)器支持許多典型的系統(tǒng)調(diào)用,比如 open()、close()、read()、write、poll() 和 ioctl()。向特定的 SCSI 設(shè)備發(fā)送 SCSI 命令的步驟也非常簡(jiǎn)單:
打開 SCSI 通用設(shè)備文件(比如 sg1)獲取 SCSI 設(shè)備的文件描述符。
準(zhǔn)備好 SCSI 命令。
設(shè)置相關(guān)的內(nèi)存緩沖區(qū)。
調(diào)用 ioctl() 函數(shù)執(zhí)行 SCSI 命令。
關(guān)閉設(shè)備文件。
典型的 ioctl() 函數(shù)類似于:ioctl(fd,SG_IO,p_io_hdr);。
這里的 ioctl() 函數(shù)必須具有 3 個(gè)參數(shù):
fd 是設(shè)備文件的文件描述符。通過調(diào)用 open() 成功打開設(shè)備文件之后,將需要獲取這個(gè)參數(shù)。
SG_IO 表明將 sg_io_hdr 對(duì)象作為 ioctl() 函數(shù)的第三個(gè)參數(shù)提交,并且在 SCSI 命令結(jié)束時(shí)返回。
p_io_hdr 是指向 sg_io_hdr 對(duì)象的指針,該對(duì)象包含 SCSI 命令和其他設(shè)置。
SCSI 通用驅(qū)動(dòng)器的最重要數(shù)據(jù)結(jié)構(gòu)是 struct sg_io_hdr,它在 scsi/sg.h 中定義,并且包含如何使用 SCSI 命令的信息。清單 1 給出了這個(gè)結(jié)構(gòu)的定義。
清單 1. sg_io_hdr 結(jié)構(gòu)的定義
typedef struct sg_io_hdr
{
int interface_id; /* [i] 'S' (required) */
int dxfer_direction; /* [i] */
unsigned char cmd_len; /* [i] */
unsigned char mx_sb_len; /* [i] */
unsigned short iovec_count; /* [i] */
unsigned int dxfer_len; /* [i] */
void * dxferp; /* [i], [*io] */
unsigned char * cmdp; /* [i], [*i] */
unsigned char * sbp; /* [i], [*o] */
unsigned int timeout; /* [i] unit: millisecs */
unsigned int flags; /* [i] */
int pack_id; /* [i->o] */
void * usr_ptr; /* [i->o] */
unsigned char status; /* [o] */
unsigned char masked_status; /* [o] */
unsigned char msg_status; /* [o] */
unsigned char sb_len_wr; /* [o] */
unsigned short host_status; /* [o] */
unsigned short driver_status; /* [o] */
int resid; /* [o] */
unsigned int duration; /* [o] */
unsigned int info; /* [o] */
} sg_io_hdr_t; /* 64 bytes long (on i386) */
不需要用到這個(gè)結(jié)構(gòu)中的所有字段,因此這?僅列出最常用的字段:
interface_id:一般應(yīng)該設(shè)置為 S。
dxfer_direction:用于確定數(shù)據(jù)傳輸?shù)姆较颍怀3J褂靡韵轮抵唬?/p>
SG_DXFER_NONE:不需要傳輸數(shù)據(jù)。比如 SCSI Test Unit Ready 命令。
SG_DXFER_TO_DEV:將數(shù)據(jù)傳輸?shù)皆O(shè)備。使用 SCSI WRITE 命令。
SG_DXFER_FROM_DEV:從設(shè)備輸出數(shù)據(jù)。使用 SCSI READ 命令。
SG_DXFER_TO_FROM_DEV:雙向傳輸數(shù)據(jù)。
SG_DXFER_UNKNOWN:數(shù)據(jù)的傳輸方向未知。
cmd_len:指向 SCSI 命令的 cmdp 的字節(jié)長度。
mx_sb_len:當(dāng) sense_buffer 為輸出時(shí),可以寫回到 sbp 的最大大小。
dxfer_len:數(shù)據(jù)傳輸?shù)挠脩魞?nèi)存的長度。
dxferp:指向數(shù)據(jù)傳輸時(shí)長度至少為 dxfer_len 字節(jié)的用戶內(nèi)存的指針。
cmdp:指向?qū)⒁獔?zhí)行的 SCSI 命令的指針。
sbp:緩沖檢測(cè)指針。
timeout:用于使特定命令超時(shí)。
status:由 SCSI 標(biāo)準(zhǔn)定義的 SCSI 狀態(tài)字節(jié)。
總而言之,當(dāng)用這種方法傳輸數(shù)據(jù)時(shí),cmdp 必須指向其長度存儲(chǔ)在 cmd_len 中的 SCSI CDB;sbp 指向最大長度為 mx_sb_len 的用戶內(nèi)存。如果出現(xiàn)錯(cuò)誤,將把檢測(cè)數(shù)據(jù)寫回到這個(gè)位置。dxferp 指向內(nèi)存;數(shù)據(jù)將根據(jù) dxfer_direction 傳輸?shù)?SCSI 設(shè)備或從中傳輸出來。
最后,我們看看 inquiry 命令,以及如何使用通用驅(qū)動(dòng)器執(zhí)行它。
例子:執(zhí)行一個(gè) inquiry 命令
inquiry 命令是所有 SCSI 設(shè)備實(shí)現(xiàn)的最常用的 SCSI 命令。這個(gè)命令用于請(qǐng)求 SCSI 設(shè)備的基本信息,并且常常用作 ping 操作,以測(cè)試 SCSI 設(shè)備是否在線。表 2 顯示如何定義 SCSI 標(biāo)準(zhǔn)。
表 2. inquiry 命令格式定義
位 7位 6位 5位 4位 3位 2位 1位 0
字節(jié) 0 | Operation code = 12h | |||||||
字節(jié) 1 | LUN | Reserved | EVPD | |||||
字節(jié) 2 | Page code | |||||||
字節(jié) 3 | Reserved | |||||||
字節(jié) 4 | Allocation length | |||||||
字節(jié) 5 | Control |
如果 EVPD 參數(shù)位(用于啟用關(guān)鍵產(chǎn)品數(shù)據(jù))為 0 并且 Page Code 參數(shù)字節(jié)為 0,那么目標(biāo)將返回標(biāo)準(zhǔn) inquiry 數(shù)據(jù)。如果 EVPD 參數(shù)為 1,那么目標(biāo)將返回對(duì)應(yīng) page code 字段的特定于供應(yīng)商的數(shù)據(jù)。
清單 2 顯示了使用 SCSI 通用 API 的源代碼片段。我們先看看設(shè)置 sg_io_hdr 的示例。
清單 2. 設(shè)置 sg_io_hdr
struct sg_io_hdr * init_io_hdr() {
struct sg_io_hdr * p_scsi_hdr = (struct sg_io_hdr *)malloc(sizeof(struct sg_io_hdr));
memset(p_scsi_hdr, 0, sizeof(struct sg_io_hdr));
if (p_scsi_hdr) {
p_scsi_hdr->interface_id = 'S'; /* this is the only choice we */
/* this would put the LUN to 2nd byte of cdb*/
p_scsi_hdr->flags = SG_FLAG_LUN_INHIBIT;
}
return p_scsi_hdr;
}
void destroy_io_hdr(struct sg_io_hdr * p_hdr) {
if (p_hdr) {
free(p_hdr);
}
}
void set_xfer_data(struct sg_io_hdr * p_hdr, void * data, unsigned int length) {
if (p_hdr) {
p_hdr->dxferp = data;
p_hdr->dxfer_len = length;
}
}
void set_sense_data(struct sg_io_hdr * p_hdr, unsigned char * data,
unsigned int length) {
if (p_hdr) {
p_hdr->sbp = data;
p_hdr->mx_sb_len = length;
}
}
這些函數(shù)還用于設(shè)置 sg_io_hdr 對(duì)象。其中的一些字段指向用戶空間內(nèi)存;當(dāng)執(zhí)行完畢時(shí),來自 SCSI 命令的 inquiry 輸出數(shù)據(jù)將復(fù)制到 dxferp 指向的內(nèi)存。如果出現(xiàn)錯(cuò)誤并且需要檢測(cè)數(shù)據(jù),檢測(cè)數(shù)據(jù)將復(fù)制到 sbp 指向的位置。清單 3 顯示了一個(gè)向 SCSI 目標(biāo)發(fā)送 inquiry 命令的示例。
清單 3. 向 SCSI 目標(biāo)發(fā)送 inquiry 命令
int execute_Inquiry(int fd, int page_code, int evpd, struct sg_io_hdr * p_hdr) {
unsigned char cdb[6];
/* set the cdb format */
cdb[0] = 0x12; /*This is for Inquery*/
cdb[1] = evpd & 1;
cdb[2] = page_code & 0xff;
cdb[3] = 0;
cdb[4] = 0xff;
cdb[5] = 0; /*For control filed, just use 0 */
p_hdr->dxfer_direction = SG_DXFER_FROM_DEV;
p_hdr->cmdp = cdb;
p_hdr->cmd_len = 6;
int ret = ioctl(fd, SG_IO, p_hdr);
if (ret<0) {
printf("Sending SCSI Command failed.\n");
close(fd);
exit(1);
}
return p_hdr->status;
}
因此,這個(gè)函數(shù)首先根據(jù) inquiry 標(biāo)準(zhǔn)格式準(zhǔn)備 CDB,然后調(diào)用 ioctl() 函數(shù),提交文件描述符 SG_IO 和 sg_io_hdr 對(duì)象;返回的狀態(tài)存儲(chǔ)在 sg_io_hdr 對(duì)象的 status 字段中。
現(xiàn)在我們看看應(yīng)用程序如何使用這個(gè)函數(shù)執(zhí)行 inquiry 命令,如清單 4 所示:
清單 4. 應(yīng)用程序執(zhí)行 inquiry 命令
unsigned char sense_buffer[SENSE_LEN];
unsigned char data_buffer[BLOCK_LEN*256];
void test_execute_Inquiry(char * path, int evpd, int page_code) {
struct sg_io_hdr * p_hdr = init_io_hdr();
set_xfer_data(p_hdr, data_buffer, BLOCK_LEN*256);
set_sense_data(p_hdr, sense_buffer, SENSE_LEN);
int status = 0;
int fd = open(path, O_RDWR);
if (fd>0) {
status = execute_Inquiry(fd, page_code, evpd, p_hdr);
printf("the return status is %d\n", status);
if (status!=0) {
show_sense_buffer(p_hdr);
} else{
show_vendor(p_hdr);
show_product(p_hdr);
show_product_rev(p_hdr);
}
} else {
printf("failed to open sg file %s\n", path);
}
close(fd);
destroy_io_hdr(p_hdr);
}
發(fā)送 SCSI 命令的步驟非常簡(jiǎn)單。首先必須分配用戶空間數(shù)據(jù)緩沖區(qū)和檢測(cè)緩沖區(qū),并將它們指向 sg_io_hdr 對(duì)象。然后打開設(shè)備驅(qū)動(dòng)器并獲取文件描述符。有了這些參數(shù)之后,就可以將 SCSI 命令發(fā)送到目標(biāo)設(shè)備。當(dāng)這個(gè)命令完成時(shí),SCSI 目標(biāo)的輸出將被復(fù)制到用戶空間緩沖區(qū)。
清單 5. 使用參數(shù)將 SCSI 命令發(fā)送到目標(biāo)設(shè)備
void show_vendor(struct sg_io_hdr * hdr) {
unsigned char * buffer = hdr->dxferp;
int i;
printf("vendor id:");
for (i=8; i<16; ++i) {
putchar(buffer[i]);
}
putchar('\n');
}
void show_product(struct sg_io_hdr * hdr) {
unsigned char * buffer = hdr->dxferp;
int i;
printf("product id:");
for (i=16; i<32; ++i) {
putchar(buffer[i]);
}
putchar('\n');
}
void show_product_rev(struct sg_io_hdr * hdr) {
unsigned char * buffer = hdr->dxferp;
int i;
printf("product ver:");
for (i=32; i<36; ++i) {
putchar(buffer[i]);
}
putchar('\n');
}
int main(int argc, char * argv[]) {
test_execute_Inquiry(argv[1], 0, 0);
return EXIT_SUCCESS;
}
SCSI Inquiry Command(Page Code 和 EVPD 字段皆設(shè)置為 0)的標(biāo)準(zhǔn)響應(yīng)很復(fù)雜。根據(jù)標(biāo)準(zhǔn),供應(yīng)商 ID 從第 8 字節(jié)擴(kuò)展到第 15 字節(jié),產(chǎn)品 ID 從第 16 字節(jié)擴(kuò)展到第 31 字節(jié),產(chǎn)品版本從第 32 字節(jié)擴(kuò)展到第 35 字節(jié)。必須獲取這些信息,以檢查命令是否成功執(zhí)行。
SCSI command 的所有指令 指令含義
文章內(nèi)容來自
Linux SCSI 子系統(tǒng)剖析
探索 Linux 通用 SCSI 驅(qū)動(dòng)器
*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。