diff mbox series

scsi: scsi_debug: Implement support for write protect

Message ID 20190208233725.31221-1-martin.petersen@oracle.com (mailing list archive)
State Accepted
Headers show
Series scsi: scsi_debug: Implement support for write protect | expand

Commit Message

Martin K. Petersen Feb. 8, 2019, 11:37 p.m. UTC
Teach scsi_debug to honor SWP in the Control Mode Page and report the
resulting WP state in the Device-Specific Parameter field.

In check_device_access_params() verify that commands that will write
the medium are permitted to do so.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
 drivers/scsi/scsi_debug.c | 76 ++++++++++++++++-----------------------
 1 file changed, 31 insertions(+), 45 deletions(-)

Comments

Douglas Gilbert Feb. 9, 2019, 9:15 p.m. UTC | #1
On 2019-02-08 6:37 p.m., Martin K. Petersen wrote:
> Teach scsi_debug to honor SWP in the Control Mode Page and report the
> resulting WP state in the Device-Specific Parameter field.
> 
> In check_device_access_params() verify that commands that will write
> the medium are permitted to do so.
> 
> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Acked-by: Douglas Gilbert <dgilbert@interlog.com>


> ---
>   drivers/scsi/scsi_debug.c | 76 ++++++++++++++++-----------------------
>   1 file changed, 31 insertions(+), 45 deletions(-)
> 
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index e27f4df24021..19a3e9527c59 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -76,6 +76,7 @@ static const char *sdebug_version_date = "20190125";
>   #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
> @@ -673,6 +674,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 */
> @@ -2146,9 +2148,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 +2335,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 +2463,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 +2476,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;
>   }
>   
> @@ -2728,18 +2740,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))) {
> @@ -3031,19 +3034,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 +3175,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;
> @@ -3268,7 +3261,7 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
>   	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;
>   
> @@ -3440,18 +3433,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) {
> @@ -3534,7 +3518,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;
>   
> @@ -3567,7 +3551,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;
>   
> @@ -4554,6 +4538,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);
>   
> @@ -4613,6 +4598,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
>
diff mbox series

Patch

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index e27f4df24021..19a3e9527c59 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -76,6 +76,7 @@  static const char *sdebug_version_date = "20190125";
 #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
@@ -673,6 +674,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 */
@@ -2146,9 +2148,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 +2335,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 +2463,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 +2476,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;
 }
 
@@ -2728,18 +2740,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))) {
@@ -3031,19 +3034,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 +3175,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;
@@ -3268,7 +3261,7 @@  static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
 	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;
 
@@ -3440,18 +3433,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) {
@@ -3534,7 +3518,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;
 
@@ -3567,7 +3551,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;
 
@@ -4554,6 +4538,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);
 
@@ -4613,6 +4598,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