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@xxxxxxxxxx> --- 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