aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChao Yu <chao@kernel.org>2022-06-16 09:36:17 +0800
committerMartin K. Petersen <martin.petersen@oracle.com>2022-06-29 15:37:11 -0400
commit834e3cef205d324c66bbc7edd85541be59f1f7b6 (patch)
treea0ce29ba979082043c1ca92f7a88345f7151c933
parentc27abd81bb8991528ae5a0662e594e038bd7b84c (diff)
downloadlinux-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.c34
-rw-r--r--drivers/scsi/sd.h2
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;