1.6.2 scsi裝置驅動體系架構
從這一層開始,整個檔案讀寫的中心将由request轉向scsi的指令結構scsi_cmnd。那麼這個指令結構到底是怎麼一回事呢,這還得從SCSI架構談起。SCSI 實作了一種客戶機/伺服器風格的通信架構,發起者向目标裝置發送指令請求。該目标處理此請求并向發起者傳回響應。發起者可以是托管計算機中的一個 SCSI 裝置,而 SCSI 目标則可以是一個磁盤、CD光牒和錄音帶裝置或特殊裝置(比如箱體裝置)。
這裡要提到一個概念——Lower Level Device(LDD):在最低層的是一組驅動器,稱為 SCSI 低層驅動器。它們是一些可與實體裝置(比如 HBA)連結的特定驅動器。LLD 提供了自公共中間層到特定于裝置的 HBA 的一種抽象。每個 LLD 都提供了到特定底層硬體的接口,但所使用的到中間層的接口卻是一組标準接口。
較低層包含大量代碼,原因是它要負責處理各種不同的 SCSI 擴充卡類型。例如,Fibre Channel 協定包含了針對 Emulex 和 QLogic 的各種擴充卡的 LLD。面向 Adaptec 和 LSI 的 SAS 擴充卡的 LLD 也包括在内。
與存儲相關的 SCSI 指令一般是在 SCSI Architecture Model (SAM)、SCSI Primary Commands (SPC) 和 SCSI Block Commands (SBC) 中定義的:
l SAM:定義SCSI 系統模型、SCSI 标準集的功能性分區,以及适用于所有 SCSI 實作和實作标準的需求。
l SPC:定義:對所有 SCSI 裝置模型通用的行為。
l SBC:定義指令集擴充,以友善操作 SCSI 直接通路塊裝置。
每個 SCSI 指令都由 Command Descriptor Block (CDB) 描述,它定義 SCSI 裝置執行的操作。SCSI 指令涉及到用于向 SCSI 裝置傳輸資料(或從中輸出資料)的資料指令,以及用于設定 SCSI 裝置的配置參數的非資料指令。
典型的CDB格式如下圖所示:
位 7 | 位 6 | 位 5 | 位 4 | 位 3 | 位 2 | 位 1 | 位 0 |
位元組 0 | Operation code = 12h | ||||||
位元組 1 | LUN | Reserved | EVPD | ||||
位元組 2 | Page code | ||||||
位元組 3 | Reserved | ||||||
位元組 4 | Allocation length | ||||||
位元組 5 | Control |
如果 EVPD 參數位(用于啟用關鍵産品資料)為 0 并且 Page Code 參數位元組為 0,那麼目标将傳回标準指令資料(如inquiry)。如果 EVPD 參數為 1,那麼目标将傳回對應 page code 字段的特定于供應商的資料。
scsi_cmnd結構中的cmnd[MAX_COMMAND_SIZE]數組就是這個CDB的内容。我們看到這個數組雖然最大可有16個元素,每個元素1位元組,但是我們僅僅用了其中6個元素,用來存放CDB,其中cmnd[0]最為重要,對應scsi操作碼。所有的scsi操作碼都有定義,在include/scsi/scsi.h中:
#define TEST_UNIT_READY 0x00 #define REZERO_UNIT 0x01 #define REQUEST_SENSE 0x03 #define FORMAT_UNIT 0x04 #define READ_BLOCK_LIMITS 0x05 #define REASSIGN_BLOCKS 0x07 #define INITIALIZE_ELEMENT_STATUS 0x07 #define READ_6 0x08 #define WRITE_6 0x0a #define SEEK_6 0x0b #define READ_REVERSE 0x0f #define WRITE_FILEMARKS 0x10 #define SPACE 0x11 #define INQUIRY 0x12 #define RECOVER_BUFFERED_DATA 0x14 #define MODE_SELECT 0x15 #define RESERVE 0x16 #define RELEASE 0x17 #define COPY 0x18 #define ERASE 0x19 #define MODE_SENSE 0x1a #define START_STOP 0x1b #define RECEIVE_DIAGNOSTIC 0x1c #define SEND_DIAGNOSTIC 0x1d #define ALLOW_MEDIUM_REMOVAL 0x1e #define SET_WINDOW 0x24 #define READ_CAPACITY 0x25 #define READ_10 0x28 #define WRITE_10 0x2a #define SEEK_10 0x2b #define POSITION_TO_ELEMENT 0x2b #define WRITE_VERIFY 0x2e #define VERIFY 0x2f #define SEARCH_HIGH 0x30 #define SEARCH_EQUAL 0x31 #define SEARCH_LOW 0x32 #define SET_LIMITS 0x33 #define PRE_FETCH 0x34 #define READ_POSITION 0x34 #define SYNCHRONIZE_CACHE 0x35 #define LOCK_UNLOCK_CACHE 0x36 #define READ_DEFECT_DATA 0x37 #define MEDIUM_SCAN 0x38 #define COMPARE 0x39 #define COPY_VERIFY 0x3a #define WRITE_BUFFER 0x3b #define READ_BUFFER 0x3c #define UPDATE_BLOCK 0x3d #define READ_LONG 0x3e #define WRITE_LONG 0x3f #define CHANGE_DEFINITION 0x40 #define WRITE_SAME 0x41 #define READ_TOC 0x43 #define LOG_SELECT 0x4c #define LOG_SENSE 0x4d #define MODE_SELECT_10 0x55 #define RESERVE_10 0x56 #define RELEASE_10 0x57 #define MODE_SENSE_10 0x5a #define PERSISTENT_RESERVE_IN 0x5e #define PERSISTENT_RESERVE_OUT 0x5f #define REPORT_LUNS 0xa0 #define MOVE_MEDIUM 0xa5 #define EXCHANGE_MEDIUM 0xa6 #define READ_12 0xa8 #define WRITE_12 0xaa #define WRITE_VERIFY_12 0xae #define SEARCH_HIGH_12 0xb0 #define SEARCH_EQUAL_12 0xb1 #define SEARCH_LOW_12 0xb2 #define READ_ELEMENT_STATUS 0xb8 #define SEND_VOLUME_TAG 0xb6 #define WRITE_LONG_2 0xea #define READ_16 0x88 #define WRITE_16 0x8a #define VERIFY_16 0x8f #define SERVICE_ACTION_IN 0x9e #define SAI_READ_CAPACITY_16 0x10 #define ATA_16 0x85 #define ATA_12 0xa1 |
我們列出最常使用的指令:
指令 | 描述 |
INQUIRY | 請求目标裝置的摘要資訊 |
TEST_UNIT_READY | 檢測目标裝置是否準備好進行傳輸 |
READ_6 | 從 SCSI 目标裝置傳輸資料 |
WRITE_6 | 向 SCSI 目标裝置傳輸資料 |
REQUEST_SENSE | 請求最後一個指令的檢測資料 |
READ_CAPACITY | 獲得存儲容量資訊 |
所有 SCSI 指令都要以操作代碼的第一個位元組為開端,以表明它所代表的操作。并且所有 SCSI 指令都要包含一個控制位元組。這個位元組通常是該指令的最後一個位元組,用于表示與供應商相關的資訊等等。
scsi_cmnd 結構就完全是SCSI記錄的抽象,不僅cmnd數組字段記錄了指令描述塊 (CDB);還有用于感測資料緩存 (SENSE BUFFER)的sense_buffer字段,以及用于存放IO 逾時時間等 SCSI 相關的資訊和 SCSI 子系統處理指令需要的一些其他資訊的字段,如回調函數等。
來自include/scsi/scsi_cmnd.h:
struct scsi_cmnd { struct scsi_device *device; struct list_head list; struct list_head eh_entry; int eh_eflags; void (*done) (struct scsi_cmnd *); unsigned long serial_number; unsigned long jiffies_at_alloc; int retries; int allowed; int timeout_per_command; unsigned char cmd_len; enum dma_data_direction sc_data_direction; #define MAX_COMMAND_SIZE 16 unsigned char cmnd[MAX_COMMAND_SIZE]; unsigned request_bufflen; struct timer_list eh_timeout; void *request_buffer; unsigned short use_sg; unsigned short sglist_len; unsigned underflow; unsigned transfersize; int resid; struct request *request; #define SCSI_SENSE_BUFFERSIZE 96 unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; void (*scsi_done) (struct scsi_cmnd *); struct scsi_pointer SCp; unsigned char *host_scribble; int result; unsigned char tag; unsigned long pid; }; |
