diff options
author | Chao Yu <chao@kernel.org> | 2022-06-16 09:36:17 +0800 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2022-06-29 15:37:11 -0400 |
commit | 834e3cef205d324c66bbc7edd85541be59f1f7b6 (patch) | |
tree | a0ce29ba979082043c1ca92f7a88345f7151c933 | |
parent | c27abd81bb8991528ae5a0662e594e038bd7b84c (diff) | |
download | linux-5.20/discovery.tar.gz |
scsi: sd: Support multiple LBA ranges in an UNMAP command5.20/discovery
Previously we only described a single LBA range in an UNMAP command even if
the device reported it could handle multiple ranges. This restriction was
due to a limitation in the block layer which is no longer present.
Set max_discard_segments according to the Block Limits VPD of the device and
enable unmapping multiple LBA ranges in a single UNMAP command.
[mkp: Rebased on top of discard/discovery changes, minor tweaks]
Link: https://lore.kernel.org/r/20220616013617.2284341-1-chao@kernel.org
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r-- | drivers/scsi/sd.c | 34 | ||||
-rw-r--r-- | drivers/scsi/sd.h | 2 |
2 files changed, 27 insertions, 9 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index e57acf95c14213..6712c59fc51c8b 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -854,6 +854,7 @@ static void sd_config_discard(struct scsi_disk *sdkp, enum sd_lbp_mode mode) struct request_queue *q = sdkp->disk->queue; unsigned int logical_block_size = sdkp->device->sector_size; unsigned int max_blocks = 0; + unsigned int max_segments = 1; if (mode == SD_LBP_DEFAULT && !sdkp->provisioning_override) { if (sdkp->lbpme) { /* Logical Block Provisioning Enabled */ @@ -890,11 +891,14 @@ static void sd_config_discard(struct scsi_disk *sdkp, enum sd_lbp_mode mode) q->limits.discard_alignment = 0; q->limits.discard_granularity = 0; blk_queue_max_discard_sectors(q, 0); + blk_queue_max_discard_segments(q, 0); return; case SD_LBP_UNMAP: max_blocks = min_not_zero(sdkp->max_unmap_blocks, (u32)SD_MAX_WS16_BLOCKS); + max_segments = clamp(sdkp->max_unmap_descriptors, 1U, + (u32)SD_MAX_UNMAP_DESCS); break; case SD_LBP_WS16: @@ -929,6 +933,8 @@ static void sd_config_discard(struct scsi_disk *sdkp, enum sd_lbp_mode mode) max(sdkp->physical_block_size, sdkp->unmap_granularity * logical_block_size); blk_queue_max_discard_sectors(q, max_blocks * (logical_block_size >> 9)); + blk_queue_max_discard_segments(q, max_segments); + } static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd) @@ -936,9 +942,10 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd) struct scsi_device *sdp = cmd->device; struct request *rq = scsi_cmd_to_rq(cmd); struct scsi_disk *sdkp = scsi_disk(rq->q->disk); - u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq)); - u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); - unsigned int data_len = 24; + unsigned short segments = blk_rq_nr_discard_segments(rq); + unsigned int data_len = 8 + 16 * segments; + unsigned int descriptor_offset = 8; + struct bio *bio; char *buf; rq->special_vec.bv_page = mempool_alloc(sd_page_pool, GFP_ATOMIC); @@ -951,13 +958,20 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd) cmd->cmd_len = 10; cmd->cmnd[0] = UNMAP; - cmd->cmnd[8] = 24; + cmd->cmnd[8] = data_len; buf = bvec_virt(&rq->special_vec); - put_unaligned_be16(6 + 16, &buf[0]); - put_unaligned_be16(16, &buf[2]); - put_unaligned_be64(lba, &buf[8]); - put_unaligned_be32(nr_blocks, &buf[16]); + put_unaligned_be16(6 + 16 * segments, &buf[0]); + put_unaligned_be16(16 * segments, &buf[2]); + + __rq_for_each_bio(bio, rq) { + u64 lba = sectors_to_logical(sdp, bio->bi_iter.bi_sector); + u32 nr_blocks = sectors_to_logical(sdp, bio_sectors(bio)); + + put_unaligned_be64(lba, &buf[descriptor_offset]); + put_unaligned_be32(nr_blocks, &buf[descriptor_offset + 8]); + descriptor_offset += 16; + } cmd->allowed = sdkp->max_retries; cmd->transfersize = data_len; @@ -3020,8 +3034,10 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) lba_count = get_unaligned_be32(&vpd->data[20]); desc_count = get_unaligned_be32(&vpd->data[24]); - if (lba_count && desc_count) + if (lba_count && desc_count) { sdkp->max_unmap_blocks = lba_count; + sdkp->max_unmap_descriptors = desc_count; + } sdkp->unmap_granularity = get_unaligned_be32(&vpd->data[28]); diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 63129ca572cdb4..274c900badc1eb 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -49,6 +49,7 @@ enum { SD_MAX_XFER_BLOCKS = 0xffffffff, SD_MAX_WS10_BLOCKS = 0xffff, SD_MAX_WS16_BLOCKS = 0x7fffff, + SD_MAX_UNMAP_DESCS = 0xffff, }; enum sd_lbp_mode { @@ -125,6 +126,7 @@ struct scsi_disk { u32 opt_xfer_blocks; u32 max_ws_blocks; u32 max_unmap_blocks; + u32 max_unmap_descriptors; u32 unmap_granularity; u32 unmap_alignment; u32 index; |