diff mbox series

[V2,6/6] scsi: sd: add support for REQ_OP_VERIFY

Message ID 20220713072019.5885-7-kch@nvidia.com (mailing list archive)
State New, archived
Headers show
Series block: add support for REQ_OP_VERIFY | expand

Commit Message

Chaitanya Kulkarni July 13, 2022, 7:20 a.m. UTC
Add support to handle REQ_OP_VERIFY req_op and map it on VERIFY (16)
or VERIFY (10) in the sd driver. In case SCSI command VERIFY (16) is not
supported use SCSI command VERIFY (10). Tested with scsi_debug.

Signed-off-by: Chaitanya Kulkarni <kch@nvidia.com>
---
 drivers/scsi/sd.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/sd.h |   5 ++
 2 files changed, 129 insertions(+)
diff mbox series

Patch

diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index eb02d939dd44..8ba8bdd78ebd 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -101,6 +101,7 @@  MODULE_ALIAS_SCSI_DEVICE(TYPE_ZBC);
 
 static void sd_config_discard(struct scsi_disk *, unsigned int);
 static void sd_config_write_same(struct scsi_disk *);
+static void sd_config_verify(struct scsi_disk *sdkp);
 static int  sd_revalidate_disk(struct gendisk *);
 static void sd_unlock_native_capacity(struct gendisk *disk);
 static int  sd_probe(struct device *);
@@ -519,6 +520,43 @@  max_write_same_blocks_store(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RW(max_write_same_blocks);
 
+static ssize_t
+max_verify_blocks_show(struct device *dev, struct device_attribute *attr,
+		       char *buf)
+{
+	struct scsi_disk *sdkp = to_scsi_disk(dev);
+
+	return sprintf(buf, "%u\n", sdkp->max_verify_blocks);
+}
+
+static ssize_t
+max_verify_blocks_store(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct scsi_disk *sdkp = to_scsi_disk(dev);
+	struct scsi_device *sdp = sdkp->device;
+	unsigned long max;
+	int err;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC)
+		return -EINVAL;
+
+	err = kstrtoul(buf, 10, &max);
+
+	if (err)
+		return err;
+
+	sdkp->max_verify_blocks = max;
+
+	sd_config_verify(sdkp);
+
+	return count;
+}
+static DEVICE_ATTR_RW(max_verify_blocks);
+
 static ssize_t
 zoned_cap_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
@@ -579,6 +617,7 @@  static struct attribute *sd_disk_attrs[] = {
 	&dev_attr_provisioning_mode.attr,
 	&dev_attr_zeroing_mode.attr,
 	&dev_attr_max_write_same_blocks.attr,
+	&dev_attr_max_verify_blocks.attr,
 	&dev_attr_max_medium_access_timeouts.attr,
 	&dev_attr_zoned_cap.attr,
 	&dev_attr_max_retries.attr,
@@ -1018,6 +1057,68 @@  static void sd_config_write_same(struct scsi_disk *sdkp)
 					 (logical_block_size >> 9));
 }
 
+static blk_status_t sd_setup_verify16_cmnd(struct scsi_cmnd *cmd, u64 lba,
+					   u32 nr_blocks)
+{
+	cmd->cmd_len = 16;
+	cmd->cmnd[0] = VERIFY_16;
+	put_unaligned_be64(lba, &cmd->cmnd[2]);
+	put_unaligned_be32(nr_blocks, &cmd->cmnd[10]);
+	cmd->cmnd[14] = 0;
+	cmd->cmnd[15] = 0;
+
+	return BLK_STS_OK;
+}
+
+static blk_status_t sd_setup_verify10_cmnd(struct scsi_cmnd *cmd, u64 lba,
+					   u32 nr_blocks)
+{
+	if (lba > 0xffffffff && nr_blocks > 0xffff)
+		return BLK_STS_NOTSUPP;
+
+	cmd->cmd_len = 10;
+	cmd->cmnd[0] = VERIFY;
+	put_unaligned_be32((u32)lba, &cmd->cmnd[2]);
+	put_unaligned_be16((u16)nr_blocks, &cmd->cmnd[6]);
+	cmd->cmnd[9] = 0;
+
+	return BLK_STS_OK;
+}
+
+static blk_status_t sd_setup_verify_cmnd(struct scsi_cmnd *cmd)
+{
+	struct request *rq = scsi_cmd_to_rq(cmd);
+	struct scsi_disk *sdkp = scsi_disk(rq->q->disk);
+	struct scsi_device *sdp = cmd->device;
+	u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq));
+	u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq));
+
+	if (!sdkp->verify16 && !sdkp->verify10)
+		goto out;
+
+	cmd->allowed = SD_MAX_RETRIES;
+	cmd->sc_data_direction = DMA_NONE;
+	cmd->transfersize = 0;
+	/* skip veprotect / dpo / bytchk */
+	cmd->cmnd[1] = 0;
+
+	if (sdkp->verify16)
+		return sd_setup_verify16_cmnd(cmd, lba, nr_blocks);
+	if (sdkp->verify10)
+		return sd_setup_verify10_cmnd(cmd, lba, nr_blocks);
+out:
+	return BLK_STS_TARGET;
+}
+
+static void sd_config_verify(struct scsi_disk *sdkp)
+{
+	unsigned int max_verify_sectors = sdkp->max_verify_blocks;
+	unsigned int logical_bs = sdkp->device->sector_size;
+	struct request_queue *q = sdkp->disk->queue;
+
+	blk_queue_max_verify_sectors(q, max_verify_sectors * (logical_bs >> 9));
+}
+
 static blk_status_t sd_setup_flush_cmnd(struct scsi_cmnd *cmd)
 {
 	struct request *rq = scsi_cmd_to_rq(cmd);
@@ -1244,6 +1345,8 @@  static blk_status_t sd_init_command(struct scsi_cmnd *cmd)
 		}
 	case REQ_OP_WRITE_ZEROES:
 		return sd_setup_write_zeroes_cmnd(cmd);
+	case REQ_OP_VERIFY:
+		return sd_setup_verify_cmnd(cmd);
 	case REQ_OP_FLUSH:
 		return sd_setup_flush_cmnd(cmd);
 	case REQ_OP_READ:
@@ -1935,6 +2038,7 @@  static int sd_done(struct scsi_cmnd *SCpnt)
 	switch (req_op(req)) {
 	case REQ_OP_DISCARD:
 	case REQ_OP_WRITE_ZEROES:
+	case REQ_OP_VERIFY:
 	case REQ_OP_ZONE_RESET:
 	case REQ_OP_ZONE_RESET_ALL:
 	case REQ_OP_ZONE_OPEN:
@@ -3021,6 +3125,24 @@  static void sd_read_write_same(struct scsi_disk *sdkp, unsigned char *buffer)
 		sdkp->ws10 = 1;
 }
 
+static void sd_read_verify(struct scsi_disk *sdkp, unsigned char *buffer)
+{
+	struct scsi_device *sdev = sdkp->device;
+
+	if (scsi_report_opcode(sdev, buffer, SD_BUF_SIZE, VERIFY_16)) {
+		sd_printk(KERN_DEBUG, sdkp, "VERIFY16 supported\n");
+		sdkp->verify16 = 1;
+		sdkp->max_verify_blocks = SD_MAX_VERIFY16_BLOCKS;
+		return;
+	}
+
+	if (scsi_report_opcode(sdev, buffer, SD_BUF_SIZE, VERIFY)) {
+		sd_printk(KERN_DEBUG, sdkp, "VERIFY10 supported\n");
+		sdkp->verify10 = 1;
+		sdkp->max_verify_blocks = SD_MAX_VERIFY10_BLOCKS;
+	}
+}
+
 static void sd_read_security(struct scsi_disk *sdkp, unsigned char *buffer)
 {
 	struct scsi_device *sdev = sdkp->device;
@@ -3264,6 +3386,7 @@  static int sd_revalidate_disk(struct gendisk *disk)
 		sd_read_cache_type(sdkp, buffer);
 		sd_read_app_tag_own(sdkp, buffer);
 		sd_read_write_same(sdkp, buffer);
+		sd_read_verify(sdkp, buffer);
 		sd_read_security(sdkp, buffer);
 		sd_config_protection(sdkp);
 	}
@@ -3312,6 +3435,7 @@  static int sd_revalidate_disk(struct gendisk *disk)
 
 	set_capacity_and_notify(disk, logical_to_sectors(sdp, sdkp->capacity));
 	sd_config_write_same(sdkp);
+	sd_config_verify(sdkp);
 	kfree(buffer);
 
 	/*
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 5eea762f84d1..249100e2ea1f 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -49,6 +49,8 @@  enum {
 	SD_MAX_XFER_BLOCKS = 0xffffffff,
 	SD_MAX_WS10_BLOCKS = 0xffff,
 	SD_MAX_WS16_BLOCKS = 0x7fffff,
+	SD_MAX_VERIFY10_BLOCKS = 0xffff,
+	SD_MAX_VERIFY16_BLOCKS = 0xffffff,
 };
 
 enum {
@@ -118,6 +120,7 @@  struct scsi_disk {
 	u32		max_xfer_blocks;
 	u32		opt_xfer_blocks;
 	u32		max_ws_blocks;
+	u32		max_verify_blocks;
 	u32		max_unmap_blocks;
 	u32		unmap_granularity;
 	u32		unmap_alignment;
@@ -145,6 +148,8 @@  struct scsi_disk {
 	unsigned	lbpvpd : 1;
 	unsigned	ws10 : 1;
 	unsigned	ws16 : 1;
+	unsigned        verify10 : 1;
+	unsigned        verify16 : 1;
 	unsigned	rc_basis: 2;
 	unsigned	zoned: 2;
 	unsigned	urswrz : 1;