3.3.1 INQUIRY命令
参照SPC-3 revision23(以下简称SPC-3)标准的第5.2.3小节,INQUIRY指令是客户应用获取LUN信息的指令,scsi设备可以返回包括设备类型、版本号等信息,也可以包含厂商ID、产品型号等其他信息。该命令的定义格式如图9所示:
图9 INQUIRY命令格式
针对INQUIRY命令其中的EVPD位如果为0,则返回标准INQUIRY信息,否则scsi设备需要返回设备的VPD(Vital product data)信息。从如下代码可以看出scsi_debug对于INQUIRY命令的处理交由resp_inquiry函数完成,
switch (*cmd) {
case INQUIRY: /* mandatory, ignore unit attention */
delay_override = 1;
errsts = resp_inquiry(SCpnt, target, devip);
break;
该函数定义如下:
static int resp_inquiry(struct scsi_cmnd * scp, int target,
struct sdebug_dev_info * devip)
{
unsigned char pq_pdt;
unsigned char * arr;
unsigned char *cmd = (unsigned char *)scp->cmnd;
int alloc_len, n, ret;
alloc_len = (cmd[3] << 8) + cmd[4];
arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC);
if (! arr)
return << 16;
if (devip->wlun)
pq_pdt = 0x1e; /* present, wlun */
else if (scsi_debug_no_lun_0 && (0 == devip->lun))
pq_pdt = 0x7f; /* not present, no device type */
else
pq_pdt = (scsi_debug_ptype & 0x1f);
arr[0] = pq_pdt;
对应代码,可见程序首先获得了INQUIRY命令中下标为3,4中代表的allocation length,之后按照SDEBUG_MAX_INQ_ARR_SZ调用kzalloc分配空间,如果分配失败则通知上层,返回DID_REQUEUE错误。由标准可知VPD的第一个字节是由(高3位),PERIPHERAL DEVICE TYPE(低5位组成),定义分别如图10和图11,参照图中内容,0x1e代表本LUN与其他LUN的设备类型相同(高3位为000,低5位为1e),0x7f代表该device无法支持,0x7f代表这个LUN无法被使用(高3位为011,低5位为1f)。
图10 PERIPHERAL QUALIFIER定义
图11 PERIPHERAL DEVICE TYPE定义
if (0x2 & cmd[1]) { /* CMDDT bit set */
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
0);
kfree(arr);
return check_condition_result;
cmd[1]&0x2判断了cmd[1]的第2个bit是否为1,如果为1,则返回LLEGAL_REQUEST,因为标准中已经规定了这个选项是过时的。
} else if (0x1 & cmd[1]) { /* EVPD bit set */
否则判断EVPD是否设置,如果设置则返回VPD,接下来的代码就是在设置VPD的各种信息:
…
if (0 == cmd[2]) { /* supported vital product data pages */
…
} else if (0x80 == cmd[2]) { /* unit serial number */
arr[1] = cmd[2]; /*sanity */
arr[3] = len;
memcpy(&arr[4], lu_id_str, len);
} else if (0x83 == cmd[2]) { /* device identification */
arr[1] = cmd[2]; /*sanity */
arr[3] = inquiry_evpd_83(&arr[4], port_group_id,target_dev_id, lu_id_num,
lu_id_str, len);
} else if (0x84 == cmd[2]) { /* Software interface ident. */
arr[1] = cmd[2]; /*sanity */
arr[3] = inquiry_evpd_84(&arr[4]);
} else if (0x85 == cmd[2]) { /* Management network addresses */
arr[1] = cmd[2]; /*sanity */
arr[3] = inquiry_evpd_85(&arr[4]);
} else if (0x86 == cmd[2]) { /* extended inquiry */
arr[1] = cmd[2]; /*sanity */
arr[3] = 0x3c; /* number of following entries */
if (scsi_debug_dif == SD_DIF_TYPE3_PROTECTION)
arr[4] = 0x4; /* SPT: GRD_CHK:1 */
else if (scsi_debug_dif)
arr[4] = 0x5; /* SPT: GRD_CHK:1, REF_CHK:1 */
else
arr[4] = 0x0; /* no protection stuff */
arr[5] = 0x7; /* head of q, ordered + simple q's */
} else if (0x87 == cmd[2]) { /* mode page policy */
arr[1] = cmd[2]; /*sanity */
arr[3] = 0x8; /* number of following entries */
arr[4] = 0x2; /* disconnect-reconnect mp */
arr[6] = 0x80; /* mlus, shared */
arr[8] = 0x18; /* protocol specific lu */
arr[10] = 0x82; /* mlus, per initiator port */
} else if (0x88 == cmd[2]) { /* SCSI Ports */
arr[1] = cmd[2]; /*sanity */
arr[3] = inquiry_evpd_88(&arr[4], target_dev_id);
} else if (0x89 == cmd[2]) { /* ATA information */
arr[1] = cmd[2]; /*sanity */
n = inquiry_evpd_89(&arr[4]);
arr[2] = (n >> 8);
arr[3] = (n & 0xff);
} else if (0xb0 == cmd[2]) { /* Block limits (SBC) */
arr[1] = cmd[2]; /*sanity */
arr[3] = inquiry_evpd_b0(&arr[4]);
} else if (0xb1 == cmd[2]) { /* Block characteristics (SBC) */
arr[1] = cmd[2]; /*sanity */
arr[3] = inquiry_evpd_b1(&arr[4]);
} else if (0xb2 == cmd[2]) { /* Logical Block Prov. (SBC) */
arr[1] = cmd[2]; /*sanity */
arr[3] = inquiry_evpd_b2(&arr[4]);
} else {
/* Illegal request, invalid field in cdb */
mk_sense_buffer(devip, ILLEGAL_REQUEST,
INVALID_FIELD_IN_CDB, 0);
kfree(arr);
return check_condition_result;
}
这段代码中对于VPD的设置参考SPC-3的第7.6小节,针对INQUIRY的下标为2的page code进行判断继而填充相关信息,page code种类如图12所示。若page code为0,则需要返回所有支持的page code,由于scsi_debug是个虚拟的设备,所以支持比较全面,图12中的page code都支持,返回数据格式定义参见SPC-3第7.6.9小节。设置完成page code之后需要在返回数据下标为3的位置中填充page length。若page code为其他类型,scsi_debug中对调用的函数进行了细化,正如代码中的inquiry_evpd_83、inquiry_evpd_84、inquiry_evpd_b2等等所示。
图12 INQUIRY命令page code种类
此处以inquiry_evpd_84为例,从图12中可以看出应该返回Software Interface Indentification信息,该函数定义如下,SPC-3标准的7.6.8小节中规定每个Software Interface identifier应该返回6个字节长度的LUN信息,Software Interface identifier的内容需要遵循EUI-48格式(参见http://standards.ieee.org/develop/regauth/tut/eui48.pdf)。
static unsigned char vpd84_data[] = {
/* from 4th byte */ 0x22,0x22,0x22,0x0,0xbb,0x0,
0x22,0x22,0x22,0x0,0xbb,0x1,
0x22,0x22,0x22,0x0,0xbb,0x2,
};
static int inquiry_evpd_84(unsigned char * arr)
{
memcpy(arr, vpd84_data, sizeof(vpd84_data));
return sizeof(vpd84_data);
}
如果没有找到正确的page code则生成sense buffer并返回错误,接下来的代码:
len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
ret = fill_from_dev_buffer(scp, arr, min(len, SDEBUG_MAX_INQ_ARR_SZ));
kfree(arr);
return ret;
此处针对已经填充的信息获取真实的返回数据的长度,然后调用fill_from_dev_buffer,该函数定义如下: