scsi设备驱动体系架构

    技术2024-07-27  64

    1.6.2 scsi设备驱动体系架构

    从这一层开始,整个文件读写的中心将由request转向scsi的命令结构scsi_cmnd。那么这个命令结构到底是怎么一回事呢,这还得从SCSI架构谈起。SCSI 实现了一种客户机/服务器风格的通信架构,发起者向目标设备发送命令请求。该目标处理此请求并向发起者返回响应。发起者可以是托管计算机中的一个 SCSI 设备,而 SCSI 目标则可以是一个磁盘、光盘和磁带设备或特殊设备(比如箱体设备)。

     

    这里要提到一个概念——Lower Level DeviceLDD):在最低层的是一组驱动器,称为 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

    /* values for service action in */

    #define    SAI_READ_CAPACITY_16  0x10

     

    /* Values for T10/04-262r7 */

    #define    ATA_16                0x85    /* 16-byte pass-thru */

    #define    ATA_12                0xa1    /* 12-byte pass-thru */

     

    我们列出最常使用的命令:

     

    命令

    描述

    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;  /* scsi_cmnd participates in queue lists */

           struct list_head eh_entry; /* entry for the host eh_cmd_q */

           int eh_eflags;         /* Used by error handlr */

           void (*done) (struct scsi_cmnd *);       /* Mid-level done function */

     

           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;

     

           /* These elements define the operation we are about to perform */

    #define MAX_COMMAND_SIZE       16

           unsigned char cmnd[MAX_COMMAND_SIZE];

           unsigned request_bufflen;      /* Actual request size */

     

           struct timer_list eh_timeout;  /* Used to time out the command. */

           void *request_buffer;            /* Actual requested buffer */

     

           /* These elements define the operation we ultimately want to perform */

           unsigned short use_sg;   /* Number of pieces of scatter-gather */

           unsigned short sglist_len;      /* size of malloc'd scatter-gather list */

     

           unsigned underflow;     

     

           unsigned transfersize;    

     

           int resid;        

     

           struct request *request;

     

    #define SCSI_SENSE_BUFFERSIZE        96

           unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];

                                /* obtained by REQUEST SENSE when

                                 * CHECK CONDITION is received on original

                                 * command (auto-sense) */

     

           void (*scsi_done) (struct scsi_cmnd *);

     

           struct scsi_pointer SCp;  /* Scratchpad used by some host adapters */

     

           unsigned char *host_scribble;      

     

           int result;        /* Status code from lower level driver */

     

           unsigned char tag;  /* SCSI-II queued command tag */

           unsigned long pid;  /* Process ID, starts at 0. Unique per host. */

    };

     

    最新回复(0)