Commit 4900769d authored by Douglas Gilbert's avatar Douglas Gilbert Committed by Christoph Hellwig

[PATCH] scsi_debug version 1.74

To check descriptor format sense data handling we need a source.
Clean up some other problems and improve support for st.

Changes:
   - add 'dsense' option to generate descriptor sense data
     format (default still fixed format)
   - correct unit attention generation (only INQUIRY, REQUEST_SENSE
     and REPORT_LUNS ignore it)
   - better information sent to log in "noisy" mode (i.e. opts=1)
   - correct and expand standard INQUIRY response, include version
     descriptors
   - filter MODE_SENSE command so that subpage!=0 generates error
   - add REWIND (SSC) command support (NOP)
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 946b68fb
...@@ -55,8 +55,8 @@ ...@@ -55,8 +55,8 @@
#include "scsi_logging.h" #include "scsi_logging.h"
#include "scsi_debug.h" #include "scsi_debug.h"
#define SCSI_DEBUG_VERSION "1.73" #define SCSI_DEBUG_VERSION "1.74"
static const char * scsi_debug_version_date = "20040518"; static const char * scsi_debug_version_date = "20040829";
/* Additional Sense Code (ASC) used */ /* Additional Sense Code (ASC) used */
#define NO_ADDED_SENSE 0x0 #define NO_ADDED_SENSE 0x0
...@@ -82,8 +82,9 @@ static const char * scsi_debug_version_date = "20040518"; ...@@ -82,8 +82,9 @@ static const char * scsi_debug_version_date = "20040518";
#define DEF_EVERY_NTH 0 #define DEF_EVERY_NTH 0
#define DEF_NUM_PARTS 0 #define DEF_NUM_PARTS 0
#define DEF_OPTS 0 #define DEF_OPTS 0
#define DEF_SCSI_LEVEL 3 #define DEF_SCSI_LEVEL 4 /* SPC-2 */
#define DEF_PTYPE 0 #define DEF_PTYPE 0
#define DEF_D_SENSE 0
/* bit mask values for scsi_debug_opts */ /* bit mask values for scsi_debug_opts */
#define SCSI_DEBUG_OPT_NOISE 1 #define SCSI_DEBUG_OPT_NOISE 1
...@@ -114,6 +115,7 @@ static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */ ...@@ -114,6 +115,7 @@ static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */
static int scsi_debug_opts = DEF_OPTS; static int scsi_debug_opts = DEF_OPTS;
static int scsi_debug_scsi_level = DEF_SCSI_LEVEL; static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;
static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */ static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */
static int scsi_debug_dsense = DEF_D_SENSE;
static int scsi_debug_cmnd_count = 0; static int scsi_debug_cmnd_count = 0;
...@@ -277,7 +279,7 @@ static ...@@ -277,7 +279,7 @@ static
int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
{ {
unsigned char *cmd = (unsigned char *) SCpnt->cmnd; unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
int block, upper_blk, num; int block, upper_blk, num, k;
unsigned char *buff; unsigned char *buff;
int errsts = 0; int errsts = 0;
int target = SCpnt->device->id; int target = SCpnt->device->id;
...@@ -305,7 +307,12 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) ...@@ -305,7 +307,12 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
bufflen = SDEBUG_SENSE_LEN; bufflen = SDEBUG_SENSE_LEN;
} }
if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
printk(KERN_INFO "scsi_debug: cmd ");
for (k = 0, num = SCpnt->cmd_len; k < num; ++k)
printk("%02x ", (int)cmd[k]);
printk("\n");
}
if(target == sdebug_driver_template.this_id) { if(target == sdebug_driver_template.this_id) {
printk(KERN_INFO "scsi_debug: initiator's id used as " printk(KERN_INFO "scsi_debug: initiator's id used as "
"target!\n"); "target!\n");
...@@ -331,14 +338,10 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) ...@@ -331,14 +338,10 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
} }
switch (*cmd) { switch (*cmd) {
case INQUIRY: /* mandatory */ case INQUIRY: /* mandatory, ignore unit attention */
errsts = resp_inquiry(cmd, target, buff, bufflen, devip); errsts = resp_inquiry(cmd, target, buff, bufflen, devip);
break; break;
case REQUEST_SENSE: /* mandatory */ case REQUEST_SENSE: /* mandatory, ignore unit attention */
/* Since this driver indicates autosense by placing the
* sense buffer in the scsi_cmnd structure in the response
* (when SAM_STAT_CHECK_CONDITION is set), the mid level
* shouldn't need to call REQUEST_SENSE */
if (devip) { if (devip) {
sbuff = devip->sense_buff; sbuff = devip->sense_buff;
memcpy(buff, sbuff, (bufflen < SDEBUG_SENSE_LEN) ? memcpy(buff, sbuff, (bufflen < SDEBUG_SENSE_LEN) ?
...@@ -349,20 +352,24 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) ...@@ -349,20 +352,24 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
buff[0] = 0x70; buff[0] = 0x70;
} }
break; break;
case REZERO_UNIT: /* actually this is REWIND for SSC */
case START_STOP: case START_STOP:
errsts = check_reset(SCpnt, devip); errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break; break;
case ALLOW_MEDIUM_REMOVAL: case ALLOW_MEDIUM_REMOVAL:
if ((errsts = check_reset(SCpnt, devip))) if ((errsts = check_reset(SCpnt, devip)))
break; break;
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk("\tMedium removal %s\n", printk(KERN_INFO "scsi_debug: Medium removal %s\n",
cmd[4] ? "inhibited" : "enabled"); cmd[4] ? "inhibited" : "enabled");
break; break;
case SEND_DIAGNOSTIC: /* mandatory */ case SEND_DIAGNOSTIC: /* mandatory */
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen); memset(buff, 0, bufflen);
break; break;
case TEST_UNIT_READY: /* mandatory */ case TEST_UNIT_READY: /* mandatory */
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen); memset(buff, 0, bufflen);
break; break;
case RESERVE: case RESERVE:
...@@ -429,7 +436,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) ...@@ -429,7 +436,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
errsts = check_condition_result; errsts = check_condition_result;
} }
break; break;
case REPORT_LUNS: case REPORT_LUNS: /* mandatory, ignore unit attention */
errsts = resp_report_luns(cmd, buff, bufflen, devip); errsts = resp_report_luns(cmd, buff, bufflen, devip);
break; break;
case WRITE_16: case WRITE_16:
...@@ -469,14 +476,20 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) ...@@ -469,14 +476,20 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
break; break;
case MODE_SENSE: case MODE_SENSE:
case MODE_SENSE_10: case MODE_SENSE_10:
if ((errsts = check_reset(SCpnt, devip)))
break;
errsts = resp_mode_sense(cmd, target, buff, bufflen, devip); errsts = resp_mode_sense(cmd, target, buff, bufflen, devip);
break; break;
case SYNCHRONIZE_CACHE: case SYNCHRONIZE_CACHE:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen); memset(buff, 0, bufflen);
break; break;
default: default:
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
"supported\n", *cmd);
if ((errsts = check_reset(SCpnt, devip))) if ((errsts = check_reset(SCpnt, devip)))
break; break; /* Unit attention takes precedence */
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0, 18); mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0, 18);
errsts = check_condition_result; errsts = check_condition_result;
break; break;
...@@ -496,6 +509,9 @@ static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg) ...@@ -496,6 +509,9 @@ static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip) static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip)
{ {
if (devip->reset) { if (devip->reset) {
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk(KERN_INFO "scsi_debug: Reporting Unit "
"attention: power on reset\n");
devip->reset = 0; devip->reset = 0;
mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0, 18); mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0, 18);
return check_condition_result; return check_condition_result;
...@@ -503,7 +519,7 @@ static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip) ...@@ -503,7 +519,7 @@ static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip)
return 0; return 0;
} }
#define SDEBUG_LONG_INQ_SZ 58 #define SDEBUG_LONG_INQ_SZ 96
#define SDEBUG_MAX_INQ_ARR_SZ 128 #define SDEBUG_MAX_INQ_ARR_SZ 128
static const char * vendor_id = "Linux "; static const char * vendor_id = "Linux ";
...@@ -593,11 +609,21 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, ...@@ -593,11 +609,21 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,
/* drops through here for a standard inquiry */ /* drops through here for a standard inquiry */
arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */ arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */
arr[2] = scsi_debug_scsi_level; arr[2] = scsi_debug_scsi_level;
arr[3] = 2; /* response_data_format==2 */
arr[4] = SDEBUG_LONG_INQ_SZ - 5; arr[4] = SDEBUG_LONG_INQ_SZ - 5;
arr[6] = 0x1; /* claim: ADDR16 */
arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */ arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */
memcpy(&arr[8], vendor_id, 8); memcpy(&arr[8], vendor_id, 8);
memcpy(&arr[16], product_id, 16); memcpy(&arr[16], product_id, 16);
memcpy(&arr[32], product_rev, 4); memcpy(&arr[32], product_rev, 4);
/* version descriptors (2 bytes each) follow */
arr[58] = 0x0; arr[59] = 0x40; /* SAM-2 */
arr[60] = 0x2; arr[61] = 0x60; /* SPC-2 */
if (scsi_debug_ptype == 0) {
arr[62] = 0x1; arr[63] = 0x80; /* SBC */
} else if (scsi_debug_ptype == 1) {
arr[62] = 0x2; arr[63] = 0x00; /* SSC */
}
memcpy(buff, arr, min_len); memcpy(buff, arr, min_len);
return 0; return 0;
} }
...@@ -660,6 +686,8 @@ static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target) ...@@ -660,6 +686,8 @@ static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0, unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
0, 0, 0x2, 0x4b}; 0, 0, 0x2, 0x4b};
if (scsi_debug_dsense)
ctrl_m_pg[2] |= 0x4;
memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg)); memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
if (1 == pcontrol) if (1 == pcontrol)
memset(p + 2, 0, sizeof(ctrl_m_pg) - 2); memset(p + 2, 0, sizeof(ctrl_m_pg) - 2);
...@@ -683,7 +711,7 @@ static int resp_mode_sense(unsigned char * cmd, int target, ...@@ -683,7 +711,7 @@ static int resp_mode_sense(unsigned char * cmd, int target,
struct sdebug_dev_info * devip) struct sdebug_dev_info * devip)
{ {
unsigned char dbd; unsigned char dbd;
int pcontrol, pcode; int pcontrol, pcode, subpcode;
unsigned char dev_spec; unsigned char dev_spec;
int alloc_len, msense_6, offset, len; int alloc_len, msense_6, offset, len;
unsigned char * ap; unsigned char * ap;
...@@ -695,11 +723,9 @@ static int resp_mode_sense(unsigned char * cmd, int target, ...@@ -695,11 +723,9 @@ static int resp_mode_sense(unsigned char * cmd, int target,
dbd = cmd[1] & 0x8; dbd = cmd[1] & 0x8;
pcontrol = (cmd[2] & 0xc0) >> 6; pcontrol = (cmd[2] & 0xc0) >> 6;
pcode = cmd[2] & 0x3f; pcode = cmd[2] & 0x3f;
subpcode = cmd[3];
msense_6 = (MODE_SENSE == cmd[0]); msense_6 = (MODE_SENSE == cmd[0]);
alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]); alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
/* printk(KERN_INFO "msense: dbd=%d pcontrol=%d pcode=%d "
"msense_6=%d alloc_len=%d\n", dbd, pcontrol, pcode, "
"msense_6, alloc_len); */
if (bufflen < alloc_len) if (bufflen < alloc_len)
printk(KERN_INFO "scsi_debug: mode_sense: bufflen=%d " printk(KERN_INFO "scsi_debug: mode_sense: bufflen=%d "
"< alloc_length=%d\n", bufflen, alloc_len); "< alloc_length=%d\n", bufflen, alloc_len);
...@@ -720,6 +746,11 @@ static int resp_mode_sense(unsigned char * cmd, int target, ...@@ -720,6 +746,11 @@ static int resp_mode_sense(unsigned char * cmd, int target,
} }
ap = arr + offset; ap = arr + offset;
if (0 != subpcode) { /* TODO: Control Extension page */
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
0, 18);
return check_condition_result;
}
switch (pcode) { switch (pcode) {
case 0x1: /* Read-Write error recovery page, direct access */ case 0x1: /* Read-Write error recovery page, direct access */
len = resp_err_recov_pg(ap, pcontrol, target); len = resp_err_recov_pg(ap, pcontrol, target);
...@@ -1023,13 +1054,23 @@ static void mk_sense_buffer(struct sdebug_dev_info * devip, int key, ...@@ -1023,13 +1054,23 @@ static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
sbuff = devip->sense_buff; sbuff = devip->sense_buff;
memset(sbuff, 0, SDEBUG_SENSE_LEN); memset(sbuff, 0, SDEBUG_SENSE_LEN);
if (scsi_debug_dsense) {
sbuff[0] = 0x72; /* descriptor, current */
sbuff[1] = key;
sbuff[2] = asc;
sbuff[3] = asq;
} else {
if (inbandLen > SDEBUG_SENSE_LEN) if (inbandLen > SDEBUG_SENSE_LEN)
inbandLen = SDEBUG_SENSE_LEN; inbandLen = SDEBUG_SENSE_LEN;
sbuff[0] = 0x70; sbuff[0] = 0x70; /* fixed, current */
sbuff[2] = key; sbuff[2] = key;
sbuff[7] = (inbandLen > 7) ? (inbandLen - 8) : 0; sbuff[7] = (inbandLen > 7) ? (inbandLen - 8) : 0;
sbuff[12] = asc; sbuff[12] = asc;
sbuff[13] = asq; sbuff[13] = 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);
} }
static int scsi_debug_abort(struct scsi_cmnd * SCpnt) static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
...@@ -1230,17 +1271,11 @@ static int schedule_resp(struct scsi_cmnd * cmnd, ...@@ -1230,17 +1271,11 @@ static int schedule_resp(struct scsi_cmnd * cmnd,
struct sdebug_dev_info * devip, struct sdebug_dev_info * devip,
done_funct_t done, int scsi_result, int delta_jiff) done_funct_t done, int scsi_result, int delta_jiff)
{ {
int k, num;
if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) { if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) {
printk(KERN_INFO "scsi_debug: cmd ");
for (k = 0, num = cmnd->cmd_len; k < num; ++k)
printk("%02x ", (int)cmnd->cmnd[k]);
printk("\n");
if (scsi_result) { if (scsi_result) {
struct scsi_device * sdp = cmnd->device; struct scsi_device * sdp = cmnd->device;
printk(KERN_INFO "scsi_debug: ... <%u %u %u %u> " printk(KERN_INFO "scsi_debug: <%u %u %u %u> "
"non-zero result=0x%x\n", sdp->host->host_no, "non-zero result=0x%x\n", sdp->host->host_no,
sdp->channel, sdp->id, sdp->lun, scsi_result); sdp->channel, sdp->id, sdp->lun, scsi_result);
} }
...@@ -1296,6 +1331,7 @@ static int schedule_resp(struct scsi_cmnd * cmnd, ...@@ -1296,6 +1331,7 @@ static int schedule_resp(struct scsi_cmnd * cmnd,
module_param_named(add_host, scsi_debug_add_host, int, 0); /* perm=0644 */ module_param_named(add_host, scsi_debug_add_host, int, 0); /* perm=0644 */
module_param_named(delay, scsi_debug_delay, int, 0); /* perm=0644 */ module_param_named(delay, scsi_debug_delay, int, 0); /* perm=0644 */
module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, 0); module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, 0);
module_param_named(dsense, scsi_debug_dsense, int, 0);
module_param_named(every_nth, scsi_debug_every_nth, int, 0); module_param_named(every_nth, scsi_debug_every_nth, int, 0);
module_param_named(max_luns, scsi_debug_max_luns, int, 0); module_param_named(max_luns, scsi_debug_max_luns, int, 0);
module_param_named(num_parts, scsi_debug_num_parts, int, 0); module_param_named(num_parts, scsi_debug_num_parts, int, 0);
...@@ -1312,13 +1348,14 @@ MODULE_VERSION(SCSI_DEBUG_VERSION); ...@@ -1312,13 +1348,14 @@ MODULE_VERSION(SCSI_DEBUG_VERSION);
MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)"); MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)"); MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs"); MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs");
MODULE_PARM_DESC(dsense, "use descriptor sense format(def: fixed)");
MODULE_PARM_DESC(every_nth, "timeout every nth command(def=100)"); MODULE_PARM_DESC(every_nth, "timeout every nth command(def=100)");
MODULE_PARM_DESC(max_luns, "number of SCSI LUNs per target to simulate"); MODULE_PARM_DESC(max_luns, "number of SCSI LUNs per target to simulate");
MODULE_PARM_DESC(num_parts, "number of partitions(def=0)"); MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
MODULE_PARM_DESC(num_tgts, "number of SCSI targets per host to simulate"); MODULE_PARM_DESC(num_tgts, "number of SCSI targets per host to simulate");
MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->..."); MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->...");
MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])"); MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
MODULE_PARM_DESC(scsi_level, "SCSI level to simulate"); MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=4[SPC-2])");
static char sdebug_info[256]; static char sdebug_info[256];
...@@ -1452,6 +1489,24 @@ static ssize_t sdebug_ptype_store(struct device_driver * ddp, ...@@ -1452,6 +1489,24 @@ static ssize_t sdebug_ptype_store(struct device_driver * ddp,
} }
DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store); DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
{
return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
}
static ssize_t sdebug_dsense_store(struct device_driver * ddp,
const char * buf, size_t count)
{
int n;
if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
scsi_debug_dsense = n;
return count;
}
return -EINVAL;
}
DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
sdebug_dsense_store);
static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf) static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
{ {
return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts); return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment