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

Chaitanya Kulkarni
2022-07-13T07:34:02Z

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 --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;
--
2.29.0