diff mbox series

[2/2] scsi: sd: Implement support for NDOB flag in WRITE SAME(16)

Message ID 20190228035733.19793-3-martin.petersen@oracle.com (mailing list archive)
State Changes Requested
Headers show
Series [1/2] scsi: sd: Use revalidate buffer for VPD pages | expand

Commit Message

Martin K. Petersen Feb. 28, 2019, 3:57 a.m. UTC
The NDOB flag removes the need for a zeroed logical block in the
data-out buffer when using WRITE SAME(16) to UNMAP block ranges.
Implement support for NDOB in sd.c.

The only way to detect whether a device supports NDOB is through
REPORT SUPPORTED OPERATION CODES. Since we can't safely send that
command to all devices, we only attempt this if the device implements
the Block Provisioning VPD page and sets the LBPWS flag.

If we issue a WRITE SAME(16) and UNMAP is requested, we check whether
NDOB is set for the device in question. And if so, we do not allocate
a zeroed page from the pool and simply issue the command a zero-length
payload.

Whether a device reports support for the NDOB bit is exposed in the
sysfs ndob file.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
 drivers/scsi/sd.c | 53 +++++++++++++++++++++++++++++++++++++++--------
 drivers/scsi/sd.h |  1 +
 2 files changed, 45 insertions(+), 9 deletions(-)
diff mbox series

Patch

diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index df9538e57dcb..c75b8c1b114e 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -370,6 +370,15 @@  thin_provisioning_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(thin_provisioning);
 
+static ssize_t
+ndob_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct scsi_disk *sdkp = to_scsi_disk(dev);
+
+	return sprintf(buf, "%u\n", sdkp->ndob);
+}
+static DEVICE_ATTR_RO(ndob);
+
 /* sysfs_match_string() requires dense arrays */
 static const char *lbp_mode[] = {
 	[SD_LBP_FULL]		= "full",
@@ -533,6 +542,7 @@  static struct attribute *sd_disk_attrs[] = {
 	&dev_attr_app_tag_own.attr,
 	&dev_attr_thin_provisioning.attr,
 	&dev_attr_provisioning_mode.attr,
+	&dev_attr_ndob.attr,
 	&dev_attr_zeroing_mode.attr,
 	&dev_attr_max_write_same_blocks.attr,
 	&dev_attr_max_medium_access_timeouts.attr,
@@ -852,27 +862,38 @@  static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd,
 {
 	struct scsi_device *sdp = cmd->device;
 	struct request *rq = cmd->request;
+	struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
 	u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq));
 	u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq));
 	u32 data_len = sdp->sector_size;
 
-	rq->special_vec.bv_page = mempool_alloc(sd_page_pool, GFP_ATOMIC);
-	if (!rq->special_vec.bv_page)
-		return BLK_STS_RESOURCE;
-	clear_highpage(rq->special_vec.bv_page);
+	if (unmap && sdkp->ndob) {
+		rq->special_vec.bv_page = NULL;
+		rq->special_vec.bv_len = 0;
+	} else {
+		rq->special_vec.bv_page =
+			mempool_alloc(sd_page_pool, GFP_ATOMIC);
+		if (!rq->special_vec.bv_page)
+			return BLK_STS_RESOURCE;
+		clear_highpage(rq->special_vec.bv_page);
+		rq->special_vec.bv_len = data_len;
+	}
+
 	rq->special_vec.bv_offset = 0;
-	rq->special_vec.bv_len = data_len;
 	rq->rq_flags |= RQF_SPECIAL_PAYLOAD;
 
 	cmd->cmd_len = 16;
 	cmd->cmnd[0] = WRITE_SAME_16;
-	if (unmap)
+	if (unmap) {
 		cmd->cmnd[1] = 0x8; /* UNMAP */
+		if (sdkp->ndob)
+			cmd->cmnd[1] |= 0x1; /* NDOB */
+	}
 	put_unaligned_be64(lba, &cmd->cmnd[2]);
 	put_unaligned_be32(nr_blocks, &cmd->cmnd[10]);
 
 	cmd->allowed = SD_MAX_RETRIES;
-	cmd->transfersize = data_len;
+	cmd->transfersize = rq->special_vec.bv_len;
 	rq->timeout = unmap ? SD_TIMEOUT : SD_WRITE_SAME_TIMEOUT;
 
 	return scsi_init_io(cmd);
@@ -1297,7 +1318,7 @@  static void sd_uninit_command(struct scsi_cmnd *SCpnt)
 	struct request *rq = SCpnt->request;
 	u8 *cmnd;
 
-	if (rq->rq_flags & RQF_SPECIAL_PAYLOAD)
+	if (rq->rq_flags & RQF_SPECIAL_PAYLOAD && SCpnt->transfersize)
 		mempool_free(rq->special_vec.bv_page, sd_page_pool);
 
 	if (SCpnt->cmnd != scsi_req(rq)->cmd) {
@@ -2984,7 +3005,9 @@  static void sd_read_block_characteristics(struct scsi_disk *sdkp, unsigned char
  */
 static void sd_read_block_provisioning(struct scsi_disk *sdkp, unsigned char *buffer)
 {
-	const int vpd_len = 8;
+	struct scsi_device *sdev = sdkp->device;
+	const unsigned int vpd_len = 8;
+	const unsigned int rsoc_len = 20;
 
 	if (sdkp->lbpme == 0)
 		return;
@@ -2996,6 +3019,18 @@  static void sd_read_block_provisioning(struct scsi_disk *sdkp, unsigned char *bu
 	sdkp->lbpu	= (buffer[5] >> 7) & 1;	/* UNMAP */
 	sdkp->lbpws	= (buffer[5] >> 6) & 1;	/* WRITE SAME(16) with UNMAP */
 	sdkp->lbpws10	= (buffer[5] >> 5) & 1;	/* WRITE SAME(10) with UNMAP */
+
+	if (!sdkp->lbpws)
+		return;
+	/*
+	 * We assume that if a device supports the Block Provisioning
+	 * VPD page, it is smart enough to implement Report Supported
+	 * Operation Codes. We use that operation to determine whether
+	 * the NDOB bit is supported for WRITE SAME(16).
+	 */
+	if (scsi_report_opcode(sdev, buffer, rsoc_len, WRITE_SAME_16) == 1 &&
+	    get_unaligned_be16(&buffer[2]) >= 2)
+		sdkp->ndob = buffer[5] & 1;
 }
 
 static void sd_read_write_same(struct scsi_disk *sdkp, unsigned char *buffer)
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 5796ace76225..fda6ad03e02d 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -111,6 +111,7 @@  struct scsi_disk {
 	unsigned	lbpvpd : 1;
 	unsigned	ws10 : 1;
 	unsigned	ws16 : 1;
+	unsigned	ndob : 1;
 	unsigned	rc_basis: 2;
 	unsigned	zoned: 2;
 	unsigned	urswrz : 1;