mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	scsi: scsi_debug: add resp_write_scat function
Add resp_write_scat() function to support decoding WRITE SCATTERED (16 and 32). Also weave resp_write_scat() into the cdb decoding logic. Signed-off-by: Douglas Gilbert <dgilbert@interlog.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
		
							parent
							
								
									46f64e70b8
								
							
						
					
					
						commit
						481b5e5c79
					
				
					 1 changed files with 176 additions and 3 deletions
				
			
		| 
						 | 
				
			
			@ -93,6 +93,7 @@ static const char *sdebug_version_date = "20171202";
 | 
			
		|||
#define MISCOMPARE_VERIFY_ASC 0x1d
 | 
			
		||||
#define MICROCODE_CHANGED_ASCQ 0x1	/* with TARGET_CHANGED_ASC */
 | 
			
		||||
#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16
 | 
			
		||||
#define WRITE_ERROR_ASC 0xc
 | 
			
		||||
 | 
			
		||||
/* Additional Sense Code Qualifier (ASCQ) */
 | 
			
		||||
#define ACK_NAK_TO 0x3
 | 
			
		||||
| 
						 | 
				
			
			@ -331,7 +332,7 @@ enum sdeb_opcode_index {
 | 
			
		|||
	SDEB_I_MAINT_IN = 14,
 | 
			
		||||
	SDEB_I_MAINT_OUT = 15,
 | 
			
		||||
	SDEB_I_VERIFY = 16,		/* 10 only */
 | 
			
		||||
	SDEB_I_VARIABLE_LEN = 17,	/* READ(32), WRITE(32) */
 | 
			
		||||
	SDEB_I_VARIABLE_LEN = 17,	/* READ(32), WRITE(32), WR_SCAT(32) */
 | 
			
		||||
	SDEB_I_RESERVE = 18,		/* 6, 10 */
 | 
			
		||||
	SDEB_I_RELEASE = 19,		/* 6, 10 */
 | 
			
		||||
	SDEB_I_ALLOW_REMOVAL = 20,	/* PREVENT ALLOW MEDIUM REMOVAL */
 | 
			
		||||
| 
						 | 
				
			
			@ -400,6 +401,7 @@ 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_write_scat(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 *);
 | 
			
		||||
| 
						 | 
				
			
			@ -461,6 +463,9 @@ static const struct opcode_info_t vl_iarr[] = {	/* VARIABLE LENGTH */
 | 
			
		|||
	{0, 0x7f, 0xb, F_SA_HIGH | F_D_OUT | FF_MEDIA_IO, resp_write_dt0,
 | 
			
		||||
	    NULL, {32,  0xc7, 0, 0, 0, 0, 0x3f, 0x18, 0x0, 0xb, 0xfa,
 | 
			
		||||
		   0, 0xff, 0xff, 0xff, 0xff} },	/* WRITE(32) */
 | 
			
		||||
	{0, 0x7f, 0x11, F_SA_HIGH | F_D_OUT | FF_MEDIA_IO, resp_write_scat,
 | 
			
		||||
	    NULL, {32,  0xc7, 0, 0, 0, 0, 0x3f, 0x18, 0x0, 0x11, 0xf8,
 | 
			
		||||
		   0, 0xff, 0xff, 0x0, 0x0} },	/* WRITE SCATTERED(32) */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct opcode_info_t maint_in_iarr[] = {	/* MAINT IN */
 | 
			
		||||
| 
						 | 
				
			
			@ -532,8 +537,9 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = {
 | 
			
		|||
	    resp_readcap16, sa_in_16_iarr, /* SA_IN(16), READ CAPACITY(16) */
 | 
			
		||||
		{16,  0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 | 
			
		||||
		 0xff, 0xff, 0xff, 0xff, 0x1, 0xc7} },
 | 
			
		||||
	{0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* SA_OUT(16) */
 | 
			
		||||
	    {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 | 
			
		||||
	{0, 0x9f, 0x12, F_SA_LOW | F_D_OUT | FF_MEDIA_IO, resp_write_scat,
 | 
			
		||||
	    NULL, {16,  0x12, 0xf9, 0x0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0xff,
 | 
			
		||||
	    0xff, 0xff, 0xff, 0xff, 0xc7} },  /* SA_OUT(16), WRITE SCAT(16) */
 | 
			
		||||
	{ARRAY_SIZE(maint_in_iarr), 0xa3, 0xa, F_SA_LOW | F_D_IN,
 | 
			
		||||
	    resp_report_tgtpgs,	/* MAINT IN, REPORT TARGET PORT GROUPS */
 | 
			
		||||
		maint_in_iarr, {12,  0xea, 0, 0, 0, 0, 0xff, 0xff, 0xff,
 | 
			
		||||
| 
						 | 
				
			
			@ -3050,6 +3056,173 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * T10 has only specified WRITE SCATTERED(16) and WRITE SCATTERED(32).
 | 
			
		||||
 * No READ GATHERED yet (requires bidi or long cdb holding gather list).
 | 
			
		||||
 */
 | 
			
		||||
static int resp_write_scat(struct scsi_cmnd *scp,
 | 
			
		||||
			   struct sdebug_dev_info *devip)
 | 
			
		||||
{
 | 
			
		||||
	u8 *cmd = scp->cmnd;
 | 
			
		||||
	u8 *lrdp = NULL;
 | 
			
		||||
	u8 *up;
 | 
			
		||||
	u8 wrprotect;
 | 
			
		||||
	u16 lbdof, num_lrd, k;
 | 
			
		||||
	u32 num, num_by, bt_len, lbdof_blen, sg_off, cum_lb;
 | 
			
		||||
	u32 lb_size = sdebug_sector_size;
 | 
			
		||||
	u32 ei_lba;
 | 
			
		||||
	u64 lba;
 | 
			
		||||
	unsigned long iflags;
 | 
			
		||||
	int ret, res;
 | 
			
		||||
	bool is_16;
 | 
			
		||||
	static const u32 lrd_size = 32; /* + parameter list header size */
 | 
			
		||||
 | 
			
		||||
	if (cmd[0] == VARIABLE_LENGTH_CMD) {
 | 
			
		||||
		is_16 = false;
 | 
			
		||||
		wrprotect = (cmd[10] >> 5) & 0x7;
 | 
			
		||||
		lbdof = get_unaligned_be16(cmd + 12);
 | 
			
		||||
		num_lrd = get_unaligned_be16(cmd + 16);
 | 
			
		||||
		bt_len = get_unaligned_be32(cmd + 28);
 | 
			
		||||
	} else {        /* that leaves WRITE SCATTERED(16) */
 | 
			
		||||
		is_16 = true;
 | 
			
		||||
		wrprotect = (cmd[2] >> 5) & 0x7;
 | 
			
		||||
		lbdof = get_unaligned_be16(cmd + 4);
 | 
			
		||||
		num_lrd = get_unaligned_be16(cmd + 8);
 | 
			
		||||
		bt_len = get_unaligned_be32(cmd + 10);
 | 
			
		||||
		if (unlikely(have_dif_prot)) {
 | 
			
		||||
			if (sdebug_dif == T10_PI_TYPE2_PROTECTION &&
 | 
			
		||||
			    wrprotect) {
 | 
			
		||||
				mk_sense_invalid_opcode(scp);
 | 
			
		||||
				return illegal_condition_result;
 | 
			
		||||
			}
 | 
			
		||||
			if ((sdebug_dif == T10_PI_TYPE1_PROTECTION ||
 | 
			
		||||
			     sdebug_dif == T10_PI_TYPE3_PROTECTION) &&
 | 
			
		||||
			     wrprotect == 0)
 | 
			
		||||
				sdev_printk(KERN_ERR, scp->device,
 | 
			
		||||
					    "Unprotected WR to DIF device\n");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if ((num_lrd == 0) || (bt_len == 0))
 | 
			
		||||
		return 0;       /* T10 says these do-nothings are not errors */
 | 
			
		||||
	if (lbdof == 0) {
 | 
			
		||||
		if (sdebug_verbose)
 | 
			
		||||
			sdev_printk(KERN_INFO, scp->device,
 | 
			
		||||
				"%s: %s: LB Data Offset field bad\n",
 | 
			
		||||
				my_name, __func__);
 | 
			
		||||
		mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
 | 
			
		||||
		return illegal_condition_result;
 | 
			
		||||
	}
 | 
			
		||||
	lbdof_blen = lbdof * lb_size;
 | 
			
		||||
	if ((lrd_size + (num_lrd * lrd_size)) > lbdof_blen) {
 | 
			
		||||
		if (sdebug_verbose)
 | 
			
		||||
			sdev_printk(KERN_INFO, scp->device,
 | 
			
		||||
				"%s: %s: LBA range descriptors don't fit\n",
 | 
			
		||||
				my_name, __func__);
 | 
			
		||||
		mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
 | 
			
		||||
		return illegal_condition_result;
 | 
			
		||||
	}
 | 
			
		||||
	lrdp = kzalloc(lbdof_blen, GFP_ATOMIC);
 | 
			
		||||
	if (lrdp == NULL)
 | 
			
		||||
		return SCSI_MLQUEUE_HOST_BUSY;
 | 
			
		||||
	if (sdebug_verbose)
 | 
			
		||||
		sdev_printk(KERN_INFO, scp->device,
 | 
			
		||||
			"%s: %s: Fetch header+scatter_list, lbdof_blen=%u\n",
 | 
			
		||||
			my_name, __func__, lbdof_blen);
 | 
			
		||||
	res = fetch_to_dev_buffer(scp, lrdp, lbdof_blen);
 | 
			
		||||
	if (res == -1) {
 | 
			
		||||
		ret = DID_ERROR << 16;
 | 
			
		||||
		goto err_out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	write_lock_irqsave(&atomic_rw, iflags);
 | 
			
		||||
	sg_off = lbdof_blen;
 | 
			
		||||
	/* Spec says Buffer xfer Length field in number of LBs in dout */
 | 
			
		||||
	cum_lb = 0;
 | 
			
		||||
	for (k = 0, up = lrdp + lrd_size; k < num_lrd; ++k, up += lrd_size) {
 | 
			
		||||
		lba = get_unaligned_be64(up + 0);
 | 
			
		||||
		num = get_unaligned_be32(up + 8);
 | 
			
		||||
		if (sdebug_verbose)
 | 
			
		||||
			sdev_printk(KERN_INFO, scp->device,
 | 
			
		||||
				"%s: %s: k=%d  LBA=0x%llx num=%u  sg_off=%u\n",
 | 
			
		||||
				my_name, __func__, k, lba, num, sg_off);
 | 
			
		||||
		if (num == 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		ret = check_device_access_params(scp, lba, num);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			goto err_out_unlock;
 | 
			
		||||
		num_by = num * lb_size;
 | 
			
		||||
		ei_lba = is_16 ? 0 : get_unaligned_be32(up + 12);
 | 
			
		||||
 | 
			
		||||
		if ((cum_lb + num) > bt_len) {
 | 
			
		||||
			if (sdebug_verbose)
 | 
			
		||||
				sdev_printk(KERN_INFO, scp->device,
 | 
			
		||||
				    "%s: %s: sum of blocks > data provided\n",
 | 
			
		||||
				    my_name, __func__);
 | 
			
		||||
			mk_sense_buffer(scp, ILLEGAL_REQUEST, WRITE_ERROR_ASC,
 | 
			
		||||
					0);
 | 
			
		||||
			ret = illegal_condition_result;
 | 
			
		||||
			goto err_out_unlock;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* DIX + T10 DIF */
 | 
			
		||||
		if (unlikely(sdebug_dix && scsi_prot_sg_count(scp))) {
 | 
			
		||||
			int prot_ret = prot_verify_write(scp, lba, num,
 | 
			
		||||
							 ei_lba);
 | 
			
		||||
 | 
			
		||||
			if (prot_ret) {
 | 
			
		||||
				mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10,
 | 
			
		||||
						prot_ret);
 | 
			
		||||
				ret = illegal_condition_result;
 | 
			
		||||
				goto err_out_unlock;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ret = do_device_access(scp, sg_off, lba, num, true);
 | 
			
		||||
		if (unlikely(scsi_debug_lbp()))
 | 
			
		||||
			map_region(lba, num);
 | 
			
		||||
		if (unlikely(-1 == ret)) {
 | 
			
		||||
			ret = DID_ERROR << 16;
 | 
			
		||||
			goto err_out_unlock;
 | 
			
		||||
		} else if (unlikely(sdebug_verbose && (ret < num_by)))
 | 
			
		||||
			sdev_printk(KERN_INFO, scp->device,
 | 
			
		||||
			    "%s: write: cdb indicated=%u, IO sent=%d bytes\n",
 | 
			
		||||
			    my_name, num_by, ret);
 | 
			
		||||
 | 
			
		||||
		if (unlikely(sdebug_any_injecting_opt)) {
 | 
			
		||||
			struct sdebug_queued_cmd *sqcp =
 | 
			
		||||
				(struct sdebug_queued_cmd *)scp->host_scribble;
 | 
			
		||||
 | 
			
		||||
			if (sqcp) {
 | 
			
		||||
				if (sqcp->inj_recovered) {
 | 
			
		||||
					mk_sense_buffer(scp, RECOVERED_ERROR,
 | 
			
		||||
							THRESHOLD_EXCEEDED, 0);
 | 
			
		||||
					ret = illegal_condition_result;
 | 
			
		||||
					goto err_out_unlock;
 | 
			
		||||
				} else if (sqcp->inj_dif) {
 | 
			
		||||
					/* Logical block guard check failed */
 | 
			
		||||
					mk_sense_buffer(scp, ABORTED_COMMAND,
 | 
			
		||||
							0x10, 1);
 | 
			
		||||
					ret = illegal_condition_result;
 | 
			
		||||
					goto err_out_unlock;
 | 
			
		||||
				} else if (sqcp->inj_dix) {
 | 
			
		||||
					mk_sense_buffer(scp, ILLEGAL_REQUEST,
 | 
			
		||||
							0x10, 1);
 | 
			
		||||
					ret = illegal_condition_result;
 | 
			
		||||
					goto err_out_unlock;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		sg_off += num_by;
 | 
			
		||||
		cum_lb += num;
 | 
			
		||||
	}
 | 
			
		||||
	ret = 0;
 | 
			
		||||
err_out_unlock:
 | 
			
		||||
	write_unlock_irqrestore(&atomic_rw, iflags);
 | 
			
		||||
err_out:
 | 
			
		||||
	kfree(lrdp);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
 | 
			
		||||
			   u32 ei_lba, bool unmap, bool ndob)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue