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;
