diff options
Diffstat (limited to 'drivers/scsi/scsi_debug.c')
-rw-r--r-- | drivers/scsi/scsi_debug.c | 227 |
1 files changed, 73 insertions, 154 deletions
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 661512bec3ac..2740a90501a0 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -62,7 +62,7 @@ /* make sure inq_product_rev string corresponds to this version */ #define SDEBUG_VERSION "0188" /* format to fit INQUIRY revision field */ -static const char *sdebug_version_date = "20180128"; +static const char *sdebug_version_date = "20190125"; #define MY_NAME "scsi_debug" @@ -76,6 +76,7 @@ static const char *sdebug_version_date = "20180128"; #define LBA_OUT_OF_RANGE 0x21 #define INVALID_FIELD_IN_CDB 0x24 #define INVALID_FIELD_IN_PARAM_LIST 0x26 +#define WRITE_PROTECTED 0x27 #define UA_RESET_ASC 0x29 #define UA_CHANGED_ASC 0x2a #define TARGET_CHANGED_ASC 0x3f @@ -351,12 +352,11 @@ enum sdeb_opcode_index { 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, 16 */ - SDEB_I_COMP_WRITE = 29, - SDEB_I_LAST_ELEMENT = 30, /* keep this last (previous + 1) */ + SDEB_I_WRITE_BUFFER = 25, + SDEB_I_WRITE_SAME = 26, /* 10, 16 */ + SDEB_I_SYNC_CACHE = 27, /* 10, 16 */ + SDEB_I_COMP_WRITE = 28, + SDEB_I_LAST_ELEMENT = 29, /* keep this last (previous + 1) */ }; @@ -377,7 +377,7 @@ static const unsigned char opcode_ind_arr[256] = { /* 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, + 0, 0, 0, 0, 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, 0x7e is "extended cdb" */ @@ -430,7 +430,6 @@ static int resp_rsup_opcodes(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_rsup_tmfs(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 *); static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_write_buffer(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_sync_cache(struct scsi_cmnd *, struct sdebug_dev_info *); @@ -600,9 +599,6 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = { {0, 0x42, 0, F_D_OUT | FF_MEDIA_IO, resp_unmap, NULL, /* UNMAP */ {10, 0x1, 0, 0, 0, 0, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, /* 25 */ - {0, 0x53, 0, F_D_IN | F_D_OUT | FF_MEDIA_IO, resp_xdwriteread_10, - NULL, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, - 0, 0, 0, 0, 0, 0} }, /* XDWRITEREAD(10) */ {0, 0x3b, 0, F_D_OUT_MAYBE, resp_write_buffer, NULL, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, /* WRITE_BUFFER */ @@ -618,7 +614,7 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = { {16, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0xff, 0x3f, 0xc7} }, /* COMPARE AND WRITE */ -/* 30 */ +/* 29 */ {0xff, 0, 0, 0, NULL, NULL, /* terminating element */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, }; @@ -673,6 +669,7 @@ static bool sdebug_verbose; static bool have_dif_prot; static bool write_since_sync; static bool sdebug_statistics = DEF_STATISTICS; +static bool sdebug_wp; static unsigned int sdebug_store_sectors; static sector_t sdebug_capacity; /* in sectors */ @@ -735,7 +732,7 @@ static inline bool scsi_debug_lbp(void) (sdebug_lbpu || sdebug_lbpws || sdebug_lbpws10); } -static void *fake_store(unsigned long long lba) +static void *lba2fake_store(unsigned long long lba) { lba = do_div(lba, sdebug_store_sectors); @@ -836,7 +833,8 @@ static void mk_sense_invalid_opcode(struct scsi_cmnd *scp) mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_OPCODE, 0); } -static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg) +static int scsi_debug_ioctl(struct scsi_device *dev, unsigned int cmd, + void __user *arg) { if (sdebug_verbose) { if (0x1261 == cmd) @@ -1010,16 +1008,16 @@ static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, int arr_len) { int act_len; - struct scsi_data_buffer *sdb = scsi_in(scp); + struct scsi_data_buffer *sdb = &scp->sdb; if (!sdb->length) return 0; - if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE)) + if (scp->sc_data_direction != DMA_FROM_DEVICE) return DID_ERROR << 16; act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents, arr, arr_len); - sdb->resid = scsi_bufflen(scp) - act_len; + scsi_set_resid(scp, scsi_bufflen(scp) - act_len); return 0; } @@ -1033,20 +1031,21 @@ static int p_fill_from_dev_buffer(struct scsi_cmnd *scp, const void *arr, int arr_len, unsigned int off_dst) { int act_len, n; - struct scsi_data_buffer *sdb = scsi_in(scp); + struct scsi_data_buffer *sdb = &scp->sdb; off_t skip = off_dst; if (sdb->length <= off_dst) return 0; - if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE)) + if (scp->sc_data_direction != DMA_FROM_DEVICE) return DID_ERROR << 16; act_len = sg_pcopy_from_buffer(sdb->table.sgl, sdb->table.nents, arr, arr_len, skip); pr_debug("%s: off_dst=%u, scsi_bufflen=%u, act_len=%u, resid=%d\n", - __func__, off_dst, scsi_bufflen(scp), act_len, sdb->resid); + __func__, off_dst, scsi_bufflen(scp), act_len, + scsi_get_resid(scp)); n = (int)scsi_bufflen(scp) - ((int)off_dst + act_len); - sdb->resid = min(sdb->resid, n); + scsi_set_resid(scp, min(scsi_get_resid(scp), n)); return 0; } @@ -1058,7 +1057,7 @@ static int fetch_to_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, { if (!scsi_bufflen(scp)) return 0; - if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_TO_DEVICE)) + if (scp->sc_data_direction != DMA_TO_DEVICE) return -1; return scsi_sg_copy_to_buffer(scp, arr, arr_len); @@ -2146,9 +2145,11 @@ static int resp_mode_sense(struct scsi_cmnd *scp, target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) + (devip->target * 1000) - 3; /* for disks set DPOFUA bit and clear write protect (WP) bit */ - if (is_disk) + if (is_disk) { dev_spec = 0x10; /* =0x90 if WP=1 implies read-only */ - else + if (sdebug_wp) + dev_spec |= 0x80; + } else dev_spec = 0x0; if (msense_6) { arr[2] = dev_spec; @@ -2331,6 +2332,10 @@ static int resp_mode_select(struct scsi_cmnd *scp, if (ctrl_m_pg[1] == arr[off + 1]) { memcpy(ctrl_m_pg + 2, arr + off + 2, sizeof(ctrl_m_pg) - 2); + if (ctrl_m_pg[4] & 0x8) + sdebug_wp = true; + else + sdebug_wp = false; sdebug_dsense = !!(ctrl_m_pg[2] & 0x4); goto set_mode_changed_ua; } @@ -2455,8 +2460,8 @@ static int resp_log_sense(struct scsi_cmnd *scp, min(len, SDEBUG_MAX_INQ_ARR_SZ)); } -static int check_device_access_params(struct scsi_cmnd *scp, - unsigned long long lba, unsigned int num) +static inline int check_device_access_params(struct scsi_cmnd *scp, + unsigned long long lba, unsigned int num, bool write) { if (lba + num > sdebug_capacity) { mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); @@ -2468,6 +2473,10 @@ static int check_device_access_params(struct scsi_cmnd *scp, mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } + if (write && unlikely(sdebug_wp)) { + mk_sense_buffer(scp, DATA_PROTECT, WRITE_PROTECTED, 0x2); + return check_condition_result; + } return 0; } @@ -2477,21 +2486,19 @@ static int do_device_access(struct scsi_cmnd *scmd, u32 sg_skip, u64 lba, { int ret; u64 block, rest = 0; - struct scsi_data_buffer *sdb; + struct scsi_data_buffer *sdb = &scmd->sdb; enum dma_data_direction dir; if (do_write) { - sdb = scsi_out(scmd); dir = DMA_TO_DEVICE; write_since_sync = true; } else { - sdb = scsi_in(scmd); dir = DMA_FROM_DEVICE; } if (!sdb->length) return 0; - if (!(scsi_bidi_cmnd(scmd) || scmd->sc_data_direction == dir)) + if (scmd->sc_data_direction != dir) return -1; block = do_div(lba, sdebug_store_sectors); @@ -2514,8 +2521,8 @@ static int do_device_access(struct scsi_cmnd *scmd, u32 sg_skip, u64 lba, return ret; } -/* If fake_store(lba,num) compares equal to arr(num), then copy top half of - * arr into fake_store(lba,num) and return true. If comparison fails then +/* If lba2fake_store(lba,num) compares equal to arr(num), then copy top half of + * arr into lba2fake_store(lba,num) and return true. If comparison fails then * return false. */ static bool comp_write_worker(u64 lba, u32 num, const u8 *arr) { @@ -2643,7 +2650,7 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, if (sdt->app_tag == cpu_to_be16(0xffff)) continue; - ret = dif_verify(sdt, fake_store(sector), sector, ei_lba); + ret = dif_verify(sdt, lba2fake_store(sector), sector, ei_lba); if (ret) { dif_errors++; return ret; @@ -2728,18 +2735,9 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) } else sqcp = NULL; - /* inline check_device_access_params() */ - if (unlikely(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 (unlikely(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; - } - + ret = check_device_access_params(scp, lba, num, false); + if (ret) + return ret; if (unlikely((SDEBUG_OPT_MEDIUM_ERR & sdebug_opts) && (lba <= (sdebug_medium_error_start + sdebug_medium_error_count - 1)) && ((lba + num) > sdebug_medium_error_start))) { @@ -2774,7 +2772,7 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) if (unlikely(ret == -1)) return DID_ERROR << 16; - scsi_in(scp)->resid = scsi_bufflen(scp) - ret; + scsi_set_resid(scp, scsi_bufflen(scp) - ret); if (unlikely(sqcp)) { if (sqcp->inj_recovered) { @@ -3031,19 +3029,9 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) sdev_printk(KERN_ERR, scp->device, "Unprotected WR " "to DIF device\n"); } - - /* inline check_device_access_params() */ - if (unlikely(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 (unlikely(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; - } - + ret = check_device_access_params(scp, lba, num, true); + if (ret) + return ret; write_lock_irqsave(&atomic_rw, iflags); /* DIX + T10 DIF */ @@ -3182,7 +3170,7 @@ static int resp_write_scat(struct scsi_cmnd *scp, my_name, __func__, k, lba, num, sg_off); if (num == 0) continue; - ret = check_device_access_params(scp, lba, num); + ret = check_device_access_params(scp, lba, num, true); if (ret) goto err_out_unlock; num_by = num * lb_size; @@ -3261,12 +3249,14 @@ err_out: static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num, u32 ei_lba, bool unmap, bool ndob) { + int ret; unsigned long iflags; unsigned long long i; - int ret; - u64 lba_off; + u32 lb_size = sdebug_sector_size; + u64 block, lbaa; + u8 *fs1p; - ret = check_device_access_params(scp, lba, num); + ret = check_device_access_params(scp, lba, num, true); if (ret) return ret; @@ -3276,31 +3266,30 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num, unmap_region(lba, num); goto out; } - - lba_off = lba * sdebug_sector_size; + lbaa = lba; + block = do_div(lbaa, sdebug_store_sectors); /* if ndob then zero 1 logical block, else fetch 1 logical block */ + fs1p = fake_storep + (block * lb_size); if (ndob) { - memset(fake_storep + lba_off, 0, sdebug_sector_size); + memset(fs1p, 0, lb_size); ret = 0; } else - ret = fetch_to_dev_buffer(scp, fake_storep + lba_off, - sdebug_sector_size); + ret = fetch_to_dev_buffer(scp, fs1p, lb_size); if (-1 == ret) { write_unlock_irqrestore(&atomic_rw, iflags); return DID_ERROR << 16; - } else if (sdebug_verbose && !ndob && (ret < sdebug_sector_size)) + } else if (sdebug_verbose && !ndob && (ret < lb_size)) sdev_printk(KERN_INFO, scp->device, "%s: %s: lb size=%u, IO sent=%d bytes\n", - my_name, "write same", - sdebug_sector_size, ret); + my_name, "write same", lb_size, ret); /* Copy first sector to remaining blocks */ - for (i = 1 ; i < num ; i++) - memcpy(fake_storep + ((lba + i) * sdebug_sector_size), - fake_storep + lba_off, - sdebug_sector_size); - + for (i = 1 ; i < num ; i++) { + lbaa = lba + i; + block = do_div(lbaa, sdebug_store_sectors); + memmove(fake_storep + (block * lb_size), fs1p, lb_size); + } if (scsi_debug_lbp()) map_region(lba, num); out: @@ -3439,18 +3428,9 @@ static int resp_comp_write(struct scsi_cmnd *scp, (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; - } + ret = check_device_access_params(scp, lba, num, false); + if (ret) + return ret; dnum = 2 * num; arr = kcalloc(lb_size, dnum, GFP_ATOMIC); if (NULL == arr) { @@ -3533,7 +3513,7 @@ static int resp_unmap(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) unsigned long long lba = get_unaligned_be64(&desc[i].lba); unsigned int num = get_unaligned_be32(&desc[i].blocks); - ret = check_device_access_params(scp, lba, num); + ret = check_device_access_params(scp, lba, num, true); if (ret) goto out; @@ -3566,7 +3546,7 @@ static int resp_get_lba_status(struct scsi_cmnd *scp, if (alloc_len < 24) return 0; - ret = check_device_access_params(scp, lba, 1); + ret = check_device_access_params(scp, lba, 1, false); if (ret) return ret; @@ -3718,68 +3698,6 @@ static int resp_report_luns(struct scsi_cmnd *scp, return res; } -static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba, - unsigned int num, struct sdebug_dev_info *devip) -{ - int j; - unsigned char *kaddr, *buf; - unsigned int offset; - struct scsi_data_buffer *sdb = scsi_in(scp); - struct sg_mapping_iter miter; - - /* better not to use temporary buffer. */ - buf = kzalloc(scsi_bufflen(scp), GFP_ATOMIC); - if (!buf) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, - INSUFF_RES_ASCQ); - return check_condition_result; - } - - scsi_sg_copy_to_buffer(scp, buf, scsi_bufflen(scp)); - - offset = 0; - sg_miter_start(&miter, sdb->table.sgl, sdb->table.nents, - SG_MITER_ATOMIC | SG_MITER_TO_SG); - - while (sg_miter_next(&miter)) { - kaddr = miter.addr; - for (j = 0; j < miter.length; j++) - *(kaddr + j) ^= *(buf + offset + j); - - offset += miter.length; - } - sg_miter_stop(&miter); - kfree(buf); - - return 0; -} - -static int resp_xdwriteread_10(struct scsi_cmnd *scp, - struct sdebug_dev_info *devip) -{ - u8 *cmd = scp->cmnd; - u64 lba; - u32 num; - int errsts; - - if (!scsi_bidi_cmnd(scp)) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, - INSUFF_RES_ASCQ); - return check_condition_result; - } - errsts = resp_read_dt0(scp, devip); - if (errsts) - return errsts; - if (!(cmd[1] & 0x4)) { /* DISABLE_WRITE is not set */ - errsts = resp_write_dt0(scp, devip); - if (errsts) - return errsts; - } - lba = get_unaligned_be32(cmd + 2); - num = get_unaligned_be16(cmd + 7); - return resp_xdwriteread(scp, lba, num, devip); -} - static struct sdebug_queue *get_queue(struct scsi_cmnd *cmnd) { u32 tag = blk_mq_unique_tag(cmnd->request); @@ -3953,7 +3871,6 @@ static int scsi_debug_slave_alloc(struct scsi_device *sdp) if (sdebug_verbose) pr_info("slave_alloc <%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); - blk_queue_flag_set(QUEUE_FLAG_BIDI, sdp->request_queue); return 0; } @@ -4553,6 +4470,7 @@ module_param_named(virtual_gb, sdebug_virtual_gb, int, S_IRUGO | S_IWUSR); module_param_named(uuid_ctl, sdebug_uuid_ctl, int, S_IRUGO); module_param_named(vpd_use_hostno, sdebug_vpd_use_hostno, int, S_IRUGO | S_IWUSR); +module_param_named(wp, sdebug_wp, bool, S_IRUGO | S_IWUSR); module_param_named(write_same_length, sdebug_write_same_length, int, S_IRUGO | S_IWUSR); @@ -4612,6 +4530,7 @@ MODULE_PARM_DESC(uuid_ctl, "1->use uuid for lu name, 0->don't, 2->all use same (def=0)"); MODULE_PARM_DESC(virtual_gb, "virtual gigabyte (GiB) size (def=0 -> use dev_size_mb)"); MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)"); +MODULE_PARM_DESC(wp, "Write Protect (def=0)"); MODULE_PARM_DESC(write_same_length, "Maximum blocks per WRITE SAME cmd (def=0xffff)"); #define SDEBUG_INFO_LEN 256 |