3.3 对于SCSI命令的模拟和处理
在3.2小节中讲到scsi_debug中最主要的工作函数是scsi_debug_queuecommand_lck,该函数篇幅较长,以下对该函数的内容分部分进行解析。
static
int scsi_debug_queuecommand_lck(struct scsi_cmnd *SCpnt, done_funct_t done)
{
…
if (target == SCpnt->device->host->hostt->this_id) {
printk(KERN_INFO "scsi_debug: initiator's id used as "
"target!\n");
return schedule_resp(SCpnt, NULL, done,
DID_NO_CONNECT << 16, 0);
}
if ((SCpnt->device->lun >= scsi_debug_max_luns) &&
(SCpnt->device->lun != SAM2_WLUN_REPORT_LUNS))
return schedule_resp(SCpnt, NULL, done,
DID_NO_CONNECT << 16, 0);
devip = devInfoReg(SCpnt->device);
if (NULL == devip)
return schedule_resp(SCpnt, NULL, done,
DID_NO_CONNECT << 16, 0);
如果initiator id和target id相同,或者scsi device的lun数目超出了允许的最大值,或者未找到相应的设备信息,则调用schedule_resp执行done回调函数并返回错误。
if ((scsi_debug_every_nth != 0) &&
(++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
scsi_debug_cmnd_count = 0;
if (scsi_debug_every_nth < -1)
scsi_debug_every_nth = -1;
if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
return 0; /* ignore command causing timeout */
else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
inj_recovered = 1; /* to reads and writes below */
else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts)
inj_transport = 1; /* to reads and writes below */
else if (SCSI_DEBUG_OPT_DIF_ERR & scsi_debug_opts)
inj_dif = 1; /* to reads and writes below */
else if (SCSI_DEBUG_OPT_DIX_ERR & scsi_debug_opts)
inj_dix = 1; /* to reads and writes below */
}
接下来是scsi_debug的fault injection机制,判断scsi_debug_every_nth并设置相应的injection标志位。
if (devip->wlun) {
switch (*cmd) {
case INQUIRY:
case REQUEST_SENSE:
case TEST_UNIT_READY:
case REPORT_LUNS:
break; /* only allowable wlun commands */
default:
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk(KERN_INFO "scsi_debug: Opcode: 0x%x "
"not supported for wlun\n", *cmd);
mk_sense_buffer(devip, ILLEGAL_REQUEST,
INVALID_OPCODE, 0);
errsts = check_condition_result;
return schedule_resp(SCpnt, devip, done, errsts,
0);
}
}
此处检查wlun存在的情况下cmd是否合法,如果不合法则调用mk_sense_buffer构造sense buffer,之后返回。该函数定义如下:
static void mk_sense_buffer(struct sdebug_dev_info *devip, int key, int asc, int asq)
{
unsigned char *sbuff;
sbuff = devip->sense_buff;
memset(sbuff, 0, SDEBUG_SENSE_LEN);
scsi_build_sense_buffer(scsi_debug_dsense, sbuff, key, asc, asq);
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk(KERN_INFO "scsi_debug: [sense_key,asc,ascq]: "
"[0x%x,0x%x,0x%x]\n", key, asc, asq);
}
调用了scsi_build_sense_buffer,该函数定义在drivers/scsi/scsi_error.c中,使用传入的asc和ascq构造出sense buffer,此处传入的asc和ascq分别为ILLEGAL_REQUEST和INVALID_OPCODE。Sense data在SCSI Primary Commands标准中定义为对于REQUEST SENSE,在返回CHECK CONDITION状态码下的参数数据。
switch (*cmd) {
case INQUIRY: /* mandatory, ignore unit attention */
delay_override = 1;
errsts = resp_inquiry(SCpnt, target, devip);
break;
case REQUEST_SENSE: /* mandatory, ignore unit attention */
delay_override = 1;
errsts = resp_requests(SCpnt, devip);
break;
case REZERO_UNIT: /* actually this is REWIND for SSC */
case START_STOP:
errsts = resp_start_stop(SCpnt, devip);
break;
…
read:
errsts = check_readiness(SCpnt, 0, devip);
if (errsts)
break;
if (scsi_debug_fake_rw)
break;
get_data_transfer_info(cmd, &lba, &num, &ei_lba);
errsts = resp_read(SCpnt, lba, num, devip, ei_lba);
if (inj_recovered && (0 == errsts)) {
mk_sense_buffer(devip, RECOVERED_ERROR,
THRESHOLD_EXCEEDED, 0);
errsts = check_condition_result;
} else if (inj_transport && (0 == errsts)) {
mk_sense_buffer(devip, ABORTED_COMMAND,
TRANSPORT_PROBLEM, ACK_NAK_TO);
errsts = check_condition_result;
} else if (inj_dif && (0 == errsts)) {
mk_sense_buffer(devip, ABORTED_COMMAND, 0x10, 1);
errsts = illegal_condition_result;
} else if (inj_dix && (0 == errsts)) {
…
write:
errsts = check_readiness(SCpnt, 0, devip);
if (errsts)
break;
if (scsi_debug_fake_rw)
break;
get_data_transfer_info(cmd, &lba, &num, &ei_lba);
errsts = resp_write(SCpnt, lba, num, devip, ei_lba);
if (inj_recovered && (0 == errsts)) {
mk_sense_buffer(devip, RECOVERED_ERROR,
THRESHOLD_EXCEEDED, 0);
errsts = check_condition_result;
} else if (inj_dif && (0 == errsts)) {
mk_sense_buffer(devip, ABORTED_COMMAND, 0x10, 1);
errsts = illegal_condition_result;
} else if (inj_dix && (0 == errsts)) {
mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x10, 1);
errsts = illegal_condition_result;
}
break;
case WRITE_SAME_16:
…
default:
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
"supported\n", *cmd);
errsts = check_readiness(SCpnt, 1, devip);
if (errsts)
break; /* Unit attention takes precedence */
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
errsts = check_condition_result;
break;
}
return schedule_resp(SCpnt, devip, done, errsts,
(delay_override ? 0 : scsi_debug_delay));
}
可见,对于scsi command所有的处理都在此进行,此处选取INQUIRY和WRITE(12)命令,参照spc和sbc标准对其进行详细解释,其他命令字段读者可以参照SCSI相关标准,自行推导。