diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2014-11-24 20:46:29 -0500 |
---|---|---|
committer | Christoph Hellwig <hch@lst.de> | 2014-11-25 15:42:57 +0100 |
commit | c2248fc974df7be55a5f6db6b6f99a90b749581b (patch) | |
tree | 73117721a14603545743fecc6294ef65321958a5 /drivers/scsi | |
parent | 0d01c5df5cd470515a88a454ba69126f4b7abdab (diff) |
scsi_debug: change SCSI command parser to table driven
The existing 'big switch' parser in queuecommand() is changed to
a table driven parser. The old and new queuecommand() were moved
in the source so diff would not shuffle them. Apart from the new
tables most other changes are refactoring existing response code
to be more easily called out of the table parser. The 'strict'
parameter is added so that cdb_s can be checked for non-zero
values in parts of the cdb that are reserved. Some other changes
include: tweak request sense response when D_SENSE differs; support
NDOB in Write Same(16); and fix crash in Get LBA Status when LBP
was inactive.
Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/scsi_debug.c | 1391 |
1 files changed, 833 insertions, 558 deletions
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index ee99aca92bca..2181427a1ea5 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -71,10 +71,10 @@ static const char *scsi_debug_version_date = "20141022"; /* Additional Sense Code (ASC) */ #define NO_ADDITIONAL_SENSE 0x0 #define LOGICAL_UNIT_NOT_READY 0x4 +#define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8 #define UNRECOVERED_READ_ERR 0x11 #define PARAMETER_LIST_LENGTH_ERR 0x1a #define INVALID_OPCODE 0x20 -#define INVALID_COMMAND_OPCODE 0x20 #define LBA_OUT_OF_RANGE 0x21 #define INVALID_FIELD_IN_CDB 0x24 #define INVALID_FIELD_IN_PARAM_LIST 0x26 @@ -136,6 +136,7 @@ static const char *scsi_debug_version_date = "20141022"; #define DEF_VIRTUAL_GB 0 #define DEF_VPD_USE_HOSTNO 1 #define DEF_WRITESAME_LENGTH 0xFFFF +#define DEF_STRICT 0 #define DELAY_OVERRIDDEN -9999 /* bit mask values for scsi_debug_opts */ @@ -183,8 +184,8 @@ static const char *scsi_debug_version_date = "20141022"; #define SDEBUG_NUM_UAS 4 /* for check_readiness() */ -#define UAS_ONLY 1 -#define UAS_TUR 0 +#define UAS_ONLY 1 /* check for UAs only */ +#define UAS_TUR 0 /* if no UAs then check if media access possible */ /* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this * sector on read commands: */ @@ -210,6 +211,291 @@ static const char *scsi_debug_version_date = "20141022"; #warning "Expect DEF_CMD_PER_LUN <= SCSI_DEBUG_CANQUEUE" #endif +/* SCSI opcodes (first byte of cdb) mapped onto these indexes */ +enum sdeb_opcode_index { + SDEB_I_INVALID_OPCODE = 0, + SDEB_I_INQUIRY = 1, + SDEB_I_REPORT_LUNS = 2, + SDEB_I_REQUEST_SENSE = 3, + SDEB_I_TEST_UNIT_READY = 4, + SDEB_I_MODE_SENSE = 5, /* 6, 10 */ + SDEB_I_MODE_SELECT = 6, /* 6, 10 */ + SDEB_I_LOG_SENSE = 7, + SDEB_I_READ_CAPACITY = 8, /* 10; 16 is in SA_IN(16) */ + SDEB_I_READ = 9, /* 6, 10, 12, 16 */ + SDEB_I_WRITE = 10, /* 6, 10, 12, 16 */ + SDEB_I_START_STOP = 11, + SDEB_I_SERV_ACT_IN = 12, /* 12, 16 */ + SDEB_I_SERV_ACT_OUT = 13, /* 12, 16 */ + SDEB_I_MAINT_IN = 14, + SDEB_I_MAINT_OUT = 15, + SDEB_I_VERIFY = 16, /* 10 only */ + SDEB_I_VARIABLE_LEN = 17, + SDEB_I_RESERVE = 18, /* 6, 10 */ + SDEB_I_RELEASE = 19, /* 6, 10 */ + SDEB_I_ALLOW_REMOVAL = 20, /* PREVENT ALLOW MEDIUM REMOVAL */ + SDEB_I_REZERO_UNIT = 21, /* REWIND in SSC */ + SDEB_I_ATA_PT = 22, /* 12, 16 */ + SDEB_I_SEND_DIAG = 23, + SDEB_I_UNMAP = 24, + SDEB_I_XDWRITEREAD = 25, /* 10 only */ + SDEB_I_WRITE_BUFFER = 26, + SDEB_I_WRITE_SAME = 27, /* 10, 16 */ + SDEB_I_SYNC_CACHE = 28, /* 10 only */ + SDEB_I_COMP_WRITE = 29, + SDEB_I_LAST_ELEMENT = 30, /* keep this last */ +}; + +static const unsigned char opcode_ind_arr[256] = { +/* 0x0; 0x0->0x1f: 6 byte cdbs */ + SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE, + 0, 0, 0, 0, + SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0, + 0, 0, SDEB_I_INQUIRY, 0, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE, + SDEB_I_RELEASE, + 0, 0, SDEB_I_MODE_SENSE, SDEB_I_START_STOP, 0, SDEB_I_SEND_DIAG, + SDEB_I_ALLOW_REMOVAL, 0, +/* 0x20; 0x20->0x3f: 10 byte cdbs */ + 0, 0, 0, 0, 0, SDEB_I_READ_CAPACITY, 0, 0, + SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, SDEB_I_VERIFY, + 0, 0, 0, 0, 0, SDEB_I_SYNC_CACHE, 0, 0, + 0, 0, 0, SDEB_I_WRITE_BUFFER, 0, 0, 0, 0, +/* 0x40; 0x40->0x5f: 10 byte cdbs */ + 0, SDEB_I_WRITE_SAME, SDEB_I_UNMAP, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, SDEB_I_LOG_SENSE, 0, 0, + 0, 0, 0, SDEB_I_XDWRITEREAD, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE, + SDEB_I_RELEASE, + 0, 0, SDEB_I_MODE_SENSE, 0, 0, 0, 0, 0, +/* 0x60; 0x60->0x7d are reserved */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, SDEB_I_VARIABLE_LEN, +/* 0x80; 0x80->0x9f: 16 byte cdbs */ + 0, 0, 0, 0, 0, SDEB_I_ATA_PT, 0, 0, + SDEB_I_READ, SDEB_I_COMP_WRITE, SDEB_I_WRITE, 0, 0, 0, 0, 0, + 0, 0, 0, SDEB_I_WRITE_SAME, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, SDEB_I_SERV_ACT_IN, SDEB_I_SERV_ACT_OUT, +/* 0xa0; 0xa0->0xbf: 12 byte cdbs */ + SDEB_I_REPORT_LUNS, SDEB_I_ATA_PT, 0, SDEB_I_MAINT_IN, + SDEB_I_MAINT_OUT, 0, 0, 0, + SDEB_I_READ, SDEB_I_SERV_ACT_OUT, SDEB_I_WRITE, SDEB_I_SERV_ACT_IN, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +/* 0xc0; 0xc0->0xff: vendor specific */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +#define F_D_IN 1 +#define F_D_OUT 2 +#define F_D_OUT_MAYBE 4 /* WRITE SAME, NDOB bit */ +#define F_D_UNKN 8 +#define F_RL_WLUN_OK 0x10 +#define F_SKIP_UA 0x20 +#define F_DELAY_OVERR 0x40 +#define F_SA_LOW 0x80 /* cdb byte 1, bits 4 to 0 */ +#define F_SA_HIGH 0x100 /* as used by variable length cdbs */ +#define F_INV_OP 0x200 +#define F_FAKE_RW 0x400 +#define F_M_ACCESS 0x800 /* media access */ + +#define FF_RESPOND (F_RL_WLUN_OK | F_SKIP_UA | F_DELAY_OVERR) +#define FF_DIRECT_IO (F_M_ACCESS | F_FAKE_RW) +#define FF_SA (F_SA_HIGH | F_SA_LOW) + +struct sdebug_dev_info; +static int scsi_debug_queuecommand(struct scsi_cmnd *scp); +static int resp_inquiry(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_report_luns(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_requests(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_mode_sense(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_mode_select(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_log_sense(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_readcap(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_read_dt0(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_write_dt0(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_start_stop(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_readcap16(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_get_lba_status(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_report_tgtpgs(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_unmap(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_write_same_10(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_write_same_16(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_xdwriteread_10(struct scsi_cmnd *, struct sdebug_dev_info *); + +struct opcode_info_t { + u8 num_attached; /* 0 if this is it (i.e. a leaf); use 0xff + * for terminating element */ + u8 opcode; /* if num_attached > 0, preferred */ + u16 sa; /* service action */ + u32 flags; /* OR-ed set of SDEB_F_* */ + int (*pfp)(struct scsi_cmnd *, struct sdebug_dev_info *); + const struct opcode_info_t *arrp; /* num_attached elements or NULL */ + u8 len_mask[16]; /* len=len_mask[0], then mask for cdb[1]... */ + /* ignore cdb bytes after position 15 */ +}; + +static const struct opcode_info_t msense_iarr[1] = { + {0, 0x1a, 0, F_D_IN, NULL, NULL, + {6, 0xe8, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + +static const struct opcode_info_t mselect_iarr[1] = { + {0, 0x15, 0, F_D_OUT, NULL, NULL, + {6, 0xf1, 0, 0, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + +static const struct opcode_info_t read_iarr[3] = { + {0, 0x28, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, NULL,/* READ(10) */ + {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0, + 0, 0, 0, 0} }, + {0, 0x8, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, NULL, /* READ(6) */ + {6, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0xa8, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, NULL,/* READ(12) */ + {12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, + 0xc7, 0, 0, 0, 0} }, +}; + +static const struct opcode_info_t write_iarr[3] = { + {0, 0x2a, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, NULL, /* 10 */ + {10, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0, + 0, 0, 0, 0} }, + {0, 0xa, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, NULL, /* 6 */ + {6, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0xaa, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, NULL, /* 12 */ + {12, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, + 0xc7, 0, 0, 0, 0} }, +}; + +static const struct opcode_info_t sa_in_iarr[1] = { + {0, 0x9e, 0x12, F_SA_LOW | F_D_IN, resp_get_lba_status, NULL, + {16, 0x12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0, 0xc7} }, +}; + +static const struct opcode_info_t vl_iarr[1] = { /* VARIABLE LENGTH */ + {0, 0x7f, 0xb, F_SA_HIGH | F_D_OUT | FF_DIRECT_IO, resp_write_dt0, + NULL, {32, 0xc7, 0, 0, 0, 0, 0x1f, 0x18, 0x0, 0xb, 0xfa, + 0, 0xff, 0xff, 0xff, 0xff} }, /* WRITE(32) */ +}; + +static const struct opcode_info_t maint_in_iarr[2] = { + {0, 0xa3, 0xc, F_SA_LOW | F_D_IN, NULL, NULL, + {12, 0xc, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, + 0xc7, 0, 0, 0, 0} }, + {0, 0xa3, 0xd, F_SA_LOW | F_D_IN, NULL, NULL, + {12, 0xd, 0x80, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, + 0, 0} }, +}; + +static const struct opcode_info_t write_same_iarr[1] = { + {0, 0x93, 0, F_D_OUT_MAYBE | FF_DIRECT_IO, resp_write_same_16, NULL, + {16, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x1f, 0xc7} }, +}; + +static const struct opcode_info_t reserve_iarr[1] = { + {0, 0x16, 0, F_D_OUT, NULL, NULL, /* RESERVE(6) */ + {6, 0x1f, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + +static const struct opcode_info_t release_iarr[1] = { + {0, 0x17, 0, F_D_OUT, NULL, NULL, /* RELEASE(6) */ + {6, 0x1f, 0xff, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + + +/* This array is accessed via SDEB_I_* values. Make sure all are mapped, + * plus the terminating elements for logic that scans this table such as + * REPORT SUPPORTED OPERATION CODES. */ +static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = { +/* 0 */ + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x12, 0, FF_RESPOND | F_D_IN, resp_inquiry, NULL, + {6, 0xe3, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0xa0, 0, FF_RESPOND | F_D_IN, resp_report_luns, NULL, + {12, 0xe3, 0xff, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, + 0, 0} }, + {0, 0x3, 0, FF_RESPOND | F_D_IN, resp_requests, NULL, + {6, 0xe1, 0, 0, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x0, 0, F_M_ACCESS | F_RL_WLUN_OK, NULL, NULL,/* TEST UNIT READY */ + {6, 0, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {1, 0x5a, 0, F_D_IN, resp_mode_sense, msense_iarr, + {10, 0xf8, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, + 0} }, + {1, 0x55, 0, F_D_OUT, resp_mode_select, mselect_iarr, + {10, 0xf1, 0, 0, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, + {0, 0x4d, 0, F_D_IN, resp_log_sense, NULL, + {10, 0xe3, 0xff, 0xff, 0, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, + 0, 0, 0} }, + {0, 0x25, 0, F_D_IN, resp_readcap, NULL, + {10, 0xe1, 0xff, 0xff, 0xff, 0xff, 0, 0, 0x1, 0xc7, 0, 0, 0, 0, + 0, 0} }, + {3, 0x88, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, read_iarr, + {16, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x9f, 0xc7} }, /* READ(16) */ +/* 10 */ + {3, 0x8a, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, write_iarr, + {16, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x9f, 0xc7} }, /* WRITE(16) */ + {0, 0x1b, 0, 0, resp_start_stop, NULL, /* START STOP UNIT */ + {6, 0x1, 0, 0xf, 0xf7, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {1, 0x9e, 0x10, F_SA_LOW | F_D_IN, resp_readcap16, sa_in_iarr, + {16, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x1, 0xc7} }, /* READ CAPACITY(16) */ + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* SA OUT */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {2, 0xa3, 0xa, F_SA_LOW | F_D_IN, resp_report_tgtpgs, maint_in_iarr, + {12, 0xea, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0, + 0} }, + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* MAINT OUT */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* VERIFY */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {1, 0x7f, 0x9, F_SA_HIGH | F_D_IN | FF_DIRECT_IO, resp_read_dt0, + vl_iarr, {32, 0xc7, 0, 0, 0, 0, 0x1f, 0x18, 0x0, 0x9, 0xfe, 0, + 0xff, 0xff, 0xff, 0xff} },/* VARIABLE LENGTH, READ(32) */ + {1, 0x56, 0, F_D_OUT, NULL, reserve_iarr, /* RESERVE(10) */ + {10, 0xff, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, + 0} }, + {1, 0x57, 0, F_D_OUT, NULL, release_iarr, /* RELEASE(10) */ + {10, 0x13, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, + 0} }, +/* 20 */ + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ALLOW REMOVAL */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x1, 0, 0, resp_start_stop, NULL, /* REWIND ?? */ + {6, 0x1, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ATA_PT */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x1d, F_D_OUT, 0, NULL, NULL, /* SEND DIAGNOSTIC */ + {6, 0xf7, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x42, 0, F_D_OUT | FF_DIRECT_IO, resp_unmap, NULL, /* UNMAP */ + {10, 0x1, 0, 0, 0, 0, 0x1f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, + {0, 0x53, 0, F_D_IN | F_D_OUT | FF_DIRECT_IO, resp_xdwriteread_10, + NULL, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, + 0, 0, 0, 0, 0, 0} }, + {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* WRITE_BUFFER */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {1, 0x41, 0, F_D_OUT_MAYBE | FF_DIRECT_IO, resp_write_same_10, + write_same_iarr, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, + 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, + {0, 0x35, 0, F_DELAY_OVERR | FF_DIRECT_IO, NULL, NULL, /* SYNC_CACHE */ + {10, 0x7, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0, + 0, 0, 0, 0} }, + {0, 0x89, 0, F_D_OUT | FF_DIRECT_IO, NULL, NULL, + {16, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, + 0, 0xff, 0x1f, 0xc7} }, /* COMPARE AND WRITE */ + +/* 30 */ + {0xff, 0, 0, 0, NULL, NULL, /* terminating element */ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + struct sdebug_scmd_extra_t { bool inj_recovered; bool inj_transport; @@ -257,6 +543,7 @@ static unsigned int scsi_debug_write_same_length = DEF_WRITESAME_LENGTH; static bool scsi_debug_removable = DEF_REMOVABLE; static bool scsi_debug_clustering; static bool scsi_debug_host_lock = DEF_HOST_LOCK; +static bool scsi_debug_strict = DEF_STRICT; static bool sdebug_any_injecting_opt; static atomic_t sdebug_cmnd_count; @@ -290,11 +577,10 @@ struct sdebug_dev_info { unsigned int target; u64 lun; struct sdebug_host_info *sdbg_host; - u64 wlun; unsigned long uas_bm[1]; atomic_t num_in_q; - char stopped; - char used; + char stopped; /* TODO: should be atomic */ + bool used; }; struct sdebug_host_info { @@ -477,65 +763,6 @@ mk_sense_invalid_opcode(struct scsi_cmnd *scp) mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_OPCODE, 0); } -static void get_data_transfer_info(unsigned char *cmd, - unsigned long long *lba, unsigned int *num, - u32 *ei_lba) -{ - *ei_lba = 0; - - switch (*cmd) { - case VARIABLE_LENGTH_CMD: - *lba = (u64)cmd[19] | (u64)cmd[18] << 8 | - (u64)cmd[17] << 16 | (u64)cmd[16] << 24 | - (u64)cmd[15] << 32 | (u64)cmd[14] << 40 | - (u64)cmd[13] << 48 | (u64)cmd[12] << 56; - - *ei_lba = (u32)cmd[23] | (u32)cmd[22] << 8 | - (u32)cmd[21] << 16 | (u32)cmd[20] << 24; - - *num = (u32)cmd[31] | (u32)cmd[30] << 8 | (u32)cmd[29] << 16 | - (u32)cmd[28] << 24; - break; - - case WRITE_SAME_16: - case WRITE_16: - case READ_16: - *lba = (u64)cmd[9] | (u64)cmd[8] << 8 | - (u64)cmd[7] << 16 | (u64)cmd[6] << 24 | - (u64)cmd[5] << 32 | (u64)cmd[4] << 40 | - (u64)cmd[3] << 48 | (u64)cmd[2] << 56; - - *num = (u32)cmd[13] | (u32)cmd[12] << 8 | (u32)cmd[11] << 16 | - (u32)cmd[10] << 24; - break; - case WRITE_12: - case READ_12: - *lba = (u32)cmd[5] | (u32)cmd[4] << 8 | (u32)cmd[3] << 16 | - (u32)cmd[2] << 24; - - *num = (u32)cmd[9] | (u32)cmd[8] << 8 | (u32)cmd[7] << 16 | - (u32)cmd[6] << 24; - break; - case WRITE_SAME: - case WRITE_10: - case READ_10: - case XDWRITEREAD_10: - *lba = (u32)cmd[5] | (u32)cmd[4] << 8 | (u32)cmd[3] << 16 | - (u32)cmd[2] << 24; - - *num = (u32)cmd[8] | (u32)cmd[7] << 8; - break; - case WRITE_6: - case READ_6: - *lba = (u32)cmd[3] | (u32)cmd[2] << 8 | - (u32)(cmd[1] & 0x1f) << 16; - *num = (0 == cmd[4]) ? 256 : cmd[4]; - break; - default: - break; - } -} - static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { @@ -992,19 +1219,20 @@ static int inquiry_evpd_b2(unsigned char *arr) #define SDEBUG_LONG_INQ_SZ 96 #define SDEBUG_MAX_INQ_ARR_SZ 584 -static int resp_inquiry(struct scsi_cmnd *scp, int target, - struct sdebug_dev_info * devip) +static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { unsigned char pq_pdt; unsigned char * arr; unsigned char *cmd = scp->cmnd; int alloc_len, n, ret; + bool have_wlun; alloc_len = (cmd[3] << 8) + cmd[4]; arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC); if (! arr) return DID_REQUEUE << 16; - if (devip->wlun) + have_wlun = (scp->device->lun == SAM2_WLUN_REPORT_LUNS); + if (have_wlun) pq_pdt = 0x1e; /* present, wlun */ else if (scsi_debug_no_lun_0 && (0 == devip->lun)) pq_pdt = 0x7f; /* not present, no device type */ @@ -1024,7 +1252,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, int target, (devip->channel & 0x7f); if (0 == scsi_debug_vpd_use_hostno) host_no = 0; - lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) + + lu_id_num = have_wlun ? -1 : (((host_no + 1) * 2000) + (devip->target * 1000) + devip->lun); target_dev_id = ((host_no + 1) * 2000) + (devip->target * 1000) - 3; @@ -1142,18 +1370,20 @@ static int resp_requests(struct scsi_cmnd * scp, unsigned char * sbuff; unsigned char *cmd = scp->cmnd; unsigned char arr[SCSI_SENSE_BUFFERSIZE]; - int want_dsense; + bool dsense, want_dsense; int len = 18; memset(arr, 0, sizeof(arr)); - want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense; + dsense = !!(cmd[1] & 1); + want_dsense = dsense || scsi_debug_dsense; sbuff = scp->sense_buffer; if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) { - if (want_dsense) { + if (dsense) { arr[0] = 0x72; arr[1] = 0x0; /* NO_SENSE in sense_key */ arr[2] = THRESHOLD_EXCEEDED; arr[3] = 0xff; /* TEST set and MRIE==6 */ + len = 8; } else { arr[0] = 0x70; arr[2] = 0x0; /* NO_SENSE in sense_key */ @@ -1163,15 +1393,34 @@ static int resp_requests(struct scsi_cmnd * scp, } } else { memcpy(arr, sbuff, SCSI_SENSE_BUFFERSIZE); - if ((cmd[1] & 1) && (! scsi_debug_dsense)) { - /* DESC bit set and sense_buff in fixed format */ - memset(arr, 0, sizeof(arr)); + if (arr[0] >= 0x70 && dsense == scsi_debug_dsense) + ; /* have sense and formats match */ + else if (arr[0] <= 0x70) { + if (dsense) { + memset(arr, 0, 8); + arr[0] = 0x72; + len = 8; + } else { + memset(arr, 0, 18); + arr[0] = 0x70; + arr[7] = 0xa; + } + } else if (dsense) { + memset(arr, 0, 8); arr[0] = 0x72; arr[1] = sbuff[2]; /* sense key */ arr[2] = sbuff[12]; /* asc */ arr[3] = sbuff[13]; /* ascq */ len = 8; + } else { + memset(arr, 0, 18); + arr[0] = 0x70; + arr[2] = sbuff[1]; + arr[7] = 0xa; + arr[12] = sbuff[1]; + arr[13] = sbuff[3]; } + } mk_sense_buffer(scp, 0, NO_ADDITIONAL_SENSE, 0); return fill_from_dev_buffer(scp, arr, len); @@ -1181,11 +1430,8 @@ static int resp_start_stop(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { unsigned char *cmd = scp->cmnd; - int power_cond, errsts, start; + int power_cond, start; - errsts = check_readiness(scp, UAS_ONLY, devip); - if (errsts) - return errsts; power_cond = (cmd[4] & 0xf0) >> 4; if (power_cond) { mk_sense_invalid_fld(scp, SDEB_IN_CDB, 4, 7); @@ -1212,11 +1458,7 @@ static int resp_readcap(struct scsi_cmnd * scp, { unsigned char arr[SDEBUG_READCAP_ARR_SZ]; unsigned int capac; - int errsts; - errsts = check_readiness(scp, UAS_ONLY, devip); - if (errsts) - return errsts; /* following just in case virtual_gb changed */ sdebug_capacity = get_sdebug_capacity(); memset(arr, 0, SDEBUG_READCAP_ARR_SZ); @@ -1244,11 +1486,8 @@ static int resp_readcap16(struct scsi_cmnd * scp, unsigned char *cmd = scp->cmnd; unsigned char arr[SDEBUG_READCAP16_ARR_SZ]; unsigned long long capac; - int errsts, k, alloc_len; + int k, alloc_len; - errsts = check_readiness(scp, UAS_ONLY, devip); - if (errsts) - return errsts; alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8) + cmd[13]); /* following just in case virtual_gb changed */ @@ -1523,20 +1762,18 @@ static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol) #define SDEBUG_MAX_MSENSE_SZ 256 -static int resp_mode_sense(struct scsi_cmnd * scp, int target, - struct sdebug_dev_info * devip) +static int +resp_mode_sense(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { unsigned char dbd, llbaa; int pcontrol, pcode, subpcode, bd_len; unsigned char dev_spec; - int k, alloc_len, msense_6, offset, len, errsts, target_dev_id; + int k, alloc_len, msense_6, offset, len, target_dev_id; + int target = scp->device->id; unsigned char * ap; unsigned char arr[SDEBUG_MAX_MSENSE_SZ]; unsigned char *cmd = scp->cmnd; - errsts = check_readiness(scp, UAS_ONLY, devip); - if (errsts) - return errsts; dbd = !!(cmd[1] & 0x8); pcontrol = (cmd[2] & 0xc0) >> 6; pcode = cmd[2] & 0x3f; @@ -1684,17 +1921,15 @@ static int resp_mode_sense(struct scsi_cmnd * scp, int target, #define SDEBUG_MAX_MSELECT_SZ 512 -static int resp_mode_select(struct scsi_cmnd * scp, int mselect6, - struct sdebug_dev_info * devip) +static int +resp_mode_select(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { int pf, sp, ps, md_len, bd_len, off, spf, pg_len; - int param_len, res, errsts, mpage; + int param_len, res, mpage; unsigned char arr[SDEBUG_MAX_MSELECT_SZ]; unsigned char *cmd = scp->cmnd; + int mselect6 = (MODE_SELECT == cmd[0]); - errsts = check_readiness(scp, UAS_ONLY, devip); - if (errsts) - return errsts; memset(arr, 0, sizeof(arr)); pf = cmd[1] & 0x10; sp = cmd[1] & 0x1; @@ -1793,13 +2028,10 @@ static int resp_ie_l_pg(unsigned char * arr) static int resp_log_sense(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { - int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n; + int ppc, sp, pcontrol, pcode, subpcode, alloc_len, len, n; unsigned char arr[SDEBUG_MAX_LSENSE_SZ]; unsigned char *cmd = scp->cmnd; - errsts = check_readiness(scp, UAS_ONLY, devip); - if (errsts) - return errsts; memset(arr, 0, sizeof(arr)); ppc = cmd[1] & 0x2; sp = cmd[1] & 0x1; @@ -1889,17 +2121,17 @@ static int check_device_access_params(struct scsi_cmnd *scp, } /* Returns number of bytes copied or -1 if error. */ -static int do_device_access(struct scsi_cmnd *scmd, - unsigned long long lba, unsigned int num, int write) +static int +do_device_access(struct scsi_cmnd *scmd, u64 lba, u32 num, bool do_write) { int ret; - unsigned long long block, rest = 0; + u64 block, rest = 0; struct scsi_data_buffer *sdb; enum dma_data_direction dir; size_t (*func)(struct scatterlist *, unsigned int, void *, size_t, off_t); - if (write) { + if (do_write) { sdb = scsi_out(scmd); dir = DMA_TO_DEVICE; func = sg_pcopy_to_buffer; @@ -2045,55 +2277,143 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, return 0; } -static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba, - unsigned int num, u32 ei_lba) +static int +resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { + u8 *cmd = scp->cmnd; + u64 lba; + u32 num; + u32 ei_lba; unsigned long iflags; int ret; + bool check_prot; - ret = check_device_access_params(SCpnt, lba, num); - if (ret) - return ret; + switch (cmd[0]) { + case READ_16: + ei_lba = 0; + lba = get_unaligned_be64(cmd + 2); + num = get_unaligned_be32(cmd + 10); + check_prot = true; + break; + case READ_10: + ei_lba = 0; + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be16(cmd + 7); + check_prot = true; + break; + case READ_6: + ei_lba = 0; + lba = (u32)cmd[3] | (u32)cmd[2] << 8 | + (u32)(cmd[1] & 0x1f) << 16; + num = (0 == cmd[4]) ? 256 : cmd[4]; + check_prot = true; + break; + case READ_12: + ei_lba = 0; + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be32(cmd + 6); + check_prot = true; + break; + case XDWRITEREAD_10: + ei_lba = 0; + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be16(cmd + 7); + check_prot = false; + break; + default: /* assume READ(32) */ + lba = get_unaligned_be64(cmd + 12); + ei_lba = get_unaligned_be32(cmd + 20); + num = get_unaligned_be32(cmd + 28); + check_prot = false; + break; + } + if (check_prot) { + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + (cmd[1] & 0xe0)) { + mk_sense_invalid_opcode(scp); + return check_condition_result; + } + if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION || + scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) && + (cmd[1] & 0xe0) == 0) + sdev_printk(KERN_ERR, scp->device, "Unprotected RD " + "to DIF device\n"); + } + if (sdebug_any_injecting_opt) { + struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp); + + if (ep->inj_short) + num /= 2; + } + + /* inline check_device_access_params() */ + if (lba + num > sdebug_capacity) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); + return check_condition_result; + } + /* transfer length excessive (tie in to block limits VPD page) */ + if (num > sdebug_store_sectors) { + /* needs work to find which cdb byte 'num' comes from */ + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); + return check_condition_result; + } if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) && (lba <= (OPT_MEDIUM_ERR_ADDR + OPT_MEDIUM_ERR_NUM - 1)) && ((lba + num) > OPT_MEDIUM_ERR_ADDR)) { /* claim unrecoverable read error */ - mk_sense_buffer(SCpnt, MEDIUM_ERROR, UNRECOVERED_READ_ERR, 0); + mk_sense_buffer(scp, MEDIUM_ERROR, UNRECOVERED_READ_ERR, 0); /* set info field and valid bit for fixed descriptor */ - if (0x70 == (SCpnt->sense_buffer[0] & 0x7f)) { - SCpnt->sense_buffer[0] |= 0x80; /* Valid bit */ + if (0x70 == (scp->sense_buffer[0] & 0x7f)) { + scp->sense_buffer[0] |= 0x80; /* Valid bit */ ret = (lba < OPT_MEDIUM_ERR_ADDR) ? OPT_MEDIUM_ERR_ADDR : (int)lba; - SCpnt->sense_buffer[3] = (ret >> 24) & 0xff; - SCpnt->sense_buffer[4] = (ret >> 16) & 0xff; - SCpnt->sense_buffer[5] = (ret >> 8) & 0xff; - SCpnt->sense_buffer[6] = ret & 0xff; + put_unaligned_be32(ret, scp->sense_buffer + 3); } - scsi_set_resid(SCpnt, scsi_bufflen(SCpnt)); + scsi_set_resid(scp, scsi_bufflen(scp)); return check_condition_result; } read_lock_irqsave(&atomic_rw, iflags); /* DIX + T10 DIF */ - if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) { - int prot_ret = prot_verify_read(SCpnt, lba, num, ei_lba); + if (scsi_debug_dix && scsi_prot_sg_count(scp)) { + int prot_ret = prot_verify_read(scp, lba, num, ei_lba); if (prot_ret) { read_unlock_irqrestore(&atomic_rw, iflags); - mk_sense_buffer(SCpnt, ABORTED_COMMAND, 0x10, prot_ret); + mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, prot_ret); return illegal_condition_result; } } - ret = do_device_access(SCpnt, lba, num, 0); + ret = do_device_access(scp, lba, num, false); read_unlock_irqrestore(&atomic_rw, iflags); if (ret == -1) return DID_ERROR << 16; - scsi_in(SCpnt)->resid = scsi_bufflen(SCpnt) - ret; + scsi_in(scp)->resid = scsi_bufflen(scp) - ret; + + if (sdebug_any_injecting_opt) { + struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp); + if (ep->inj_recovered) { + mk_sense_buffer(scp, RECOVERED_ERROR, + THRESHOLD_EXCEEDED, 0); + return check_condition_result; + } else if (ep->inj_transport) { + mk_sense_buffer(scp, ABORTED_COMMAND, + TRANSPORT_PROBLEM, ACK_NAK_TO); + return check_condition_result; + } else if (ep->inj_dif) { + /* Logical block guard check failed */ + mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 1); + return illegal_condition_result; + } else if (ep->inj_dix) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 1); + return illegal_condition_result; + } + } return 0; } @@ -2276,31 +2596,95 @@ static void unmap_region(sector_t lba, unsigned int len) } } -static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba, - unsigned int num, u32 ei_lba) +static int +resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { + u8 *cmd = scp->cmnd; + u64 lba; + u32 num; + u32 ei_lba; unsigned long iflags; int ret; + bool check_prot; - ret = check_device_access_params(SCpnt, lba, num); - if (ret) - return ret; + switch (cmd[0]) { + case WRITE_16: + ei_lba = 0; + lba = get_unaligned_be64(cmd + 2); + num = get_unaligned_be32(cmd + 10); + check_prot = true; + break; + case WRITE_10: + ei_lba = 0; + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be16(cmd + 7); + check_prot = true; + break; + case WRITE_6: + ei_lba = 0; + lba = (u32)cmd[3] | (u32)cmd[2] << 8 | + (u32)(cmd[1] & 0x1f) << 16; + num = (0 == cmd[4]) ? 256 : cmd[4]; + check_prot = true; + break; + case WRITE_12: + ei_lba = 0; + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be32(cmd + 6); + check_prot = true; + break; + case 0x53: /* XDWRITEREAD(10) */ + ei_lba = 0; + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be16(cmd + 7); + check_prot = false; + break; + default: /* assume WRITE(32) */ + lba = get_unaligned_be64(cmd + 12); + ei_lba = get_unaligned_be32(cmd + 20); + num = get_unaligned_be32(cmd + 28); + check_prot = false; + break; + } + if (check_prot) { + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + (cmd[1] & 0xe0)) { + mk_sense_invalid_opcode(scp); + return check_condition_result; + } + if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION || + scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) && + (cmd[1] & 0xe0) == 0) + sdev_printk(KERN_ERR, scp->device, "Unprotected WR " + "to DIF device\n"); + } + + /* inline check_device_access_params() */ + if (lba + num > sdebug_capacity) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); + return check_condition_result; + } + /* transfer length excessive (tie in to block limits VPD page) */ + if (num > sdebug_store_sectors) { + /* needs work to find which cdb byte 'num' comes from */ + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); + return check_condition_result; + } write_lock_irqsave(&atomic_rw, iflags); /* DIX + T10 DIF */ - if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) { - int prot_ret = prot_verify_write(SCpnt, lba, num, ei_lba); + if (scsi_debug_dix && scsi_prot_sg_count(scp)) { + int prot_ret = prot_verify_write(scp, lba, num, ei_lba); if (prot_ret) { write_unlock_irqrestore(&atomic_rw, iflags); - mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, 0x10, - prot_ret); + mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, prot_ret); return illegal_condition_result; } } - ret = do_device_access(SCpnt, lba, num, 1); + ret = do_device_access(scp, lba, num, true); if (scsi_debug_lbp()) map_region(lba, num); write_unlock_irqrestore(&atomic_rw, iflags); @@ -2308,30 +2692,41 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba, return (DID_ERROR << 16); else if ((ret < (num * scsi_debug_sector_size)) && (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) - sdev_printk(KERN_INFO, SCpnt->device, + sdev_printk(KERN_INFO, scp->device, "%s: write: cdb indicated=%u, IO sent=%d bytes\n", my_name, num * scsi_debug_sector_size, ret); + if (sdebug_any_injecting_opt) { + struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp); + + if (ep->inj_recovered) { + mk_sense_buffer(scp, RECOVERED_ERROR, + THRESHOLD_EXCEEDED, 0); + return check_condition_result; + } else if (ep->inj_dif) { + /* Logical block guard check failed */ + mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 1); + return illegal_condition_result; + } else if (ep->inj_dix) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 1); + return illegal_condition_result; + } + } return 0; } -static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba, - unsigned int num, u32 ei_lba, unsigned int unmap) +static int +resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num, u32 ei_lba, + bool unmap, bool ndob) { unsigned long iflags; unsigned long long i; int ret; - ret = check_device_access_params(scmd, lba, num); + ret = check_device_access_params(scp, lba, num); if (ret) return ret; - if (num > scsi_debug_write_same_length) { - mk_sense_buffer(scmd, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, - 0); - return check_condition_result; - } - write_lock_irqsave(&atomic_rw, iflags); if (unmap && scsi_debug_lbp()) { @@ -2339,17 +2734,22 @@ static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba, goto out; } - /* Else fetch one logical block */ - ret = fetch_to_dev_buffer(scmd, - fake_storep + (lba * scsi_debug_sector_size), - scsi_debug_sector_size); + /* if ndob then zero 1 logical block, else fetch 1 logical block */ + if (ndob) { + memset(fake_storep + (lba * scsi_debug_sector_size), 0, + scsi_debug_sector_size); + ret = 0; + } else + ret = fetch_to_dev_buffer(scp, fake_storep + + (lba * scsi_debug_sector_size), + scsi_debug_sector_size); if (-1 == ret) { write_unlock_irqrestore(&atomic_rw, iflags); return (DID_ERROR << 16); } else if ((ret < (num * scsi_debug_sector_size)) && (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) - sdev_printk(KERN_INFO, scmd->device, + sdev_printk(KERN_INFO, scp->device, "%s: %s: cdb indicated=%u, IO sent=%d bytes\n", my_name, "write same", num * scsi_debug_sector_size, ret); @@ -2368,13 +2768,67 @@ out: return 0; } +static int +resp_write_same_10(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) +{ + u8 *cmd = scp->cmnd; + u32 lba; + u16 num; + u32 ei_lba = 0; + bool unmap = false; + + if (cmd[1] & 0x8) { + if (scsi_debug_lbpws10 == 0) { + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 3); + return check_condition_result; + } else + unmap = true; + } + lba = get_unaligned_be32(cmd + 2); + num = get_unaligned_be16(cmd + 7); + if (num > scsi_debug_write_same_length) { + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 7, -1); + return check_condition_result; + } + return resp_write_same(scp, lba, num, ei_lba, unmap, false); |