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 <[email protected]>
---
 drivers/scsi/sd.c | 53 +++++++++++++++++++++++++++++++++++++++--------
 drivers/scsi/sd.h |  1 +
 2 files changed, 45 insertions(+), 9 deletions(-)

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

Reply via email to