summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/sd.c
diff options
context:
space:
mode:
authorMartin K. Petersen <martin.petersen@oracle.com>2022-03-02 06:35:53 +0100
committerMartin K. Petersen <martin.petersen@oracle.com>2022-05-02 22:59:11 +0200
commit631669a256f96c7115c34cc19578a461c72568de (patch)
tree1c8bc1bb9d084540662ba5a6c5a701fc249dc4b2 /drivers/scsi/sd.c
parentscsi: sd: Switch to using scsi_device VPD pages (diff)
downloadlinux-631669a256f96c7115c34cc19578a461c72568de.tar.xz
linux-631669a256f96c7115c34cc19578a461c72568de.zip
scsi: sd: Optimal I/O size should be a multiple of reported granularity
Commit a83da8a4509d ("scsi: sd: Optimal I/O size should be a multiple of physical block size") validated the reported optimal I/O size against the physical block size to overcome problems with devices reporting nonsensical transfer sizes. However, some devices claim conformity to older SCSI versions that predate the physical block size being reported. Other devices do not report a physical block size at all. We need to be able to validate the optimal I/O size on those devices as well. Many devices report an OPTIMAL TRANSFER LENGTH GRANULARITY in the same VPD page as the OPTIMAL TRANSFER LENGTH. Use this value to validate the optimal I/O size. Also check that the reported granularity is a multiple of the physical block size, if supported. Link: https://lore.kernel.org/r/33fb522e-4f61-1b76-914f-c9e6a3553c9b@gmail.com Link: https://lore.kernel.org/r/20220302053559.32147-9-martin.petersen@oracle.com Reported-by: Bernhard Sulzer <micraft.b@gmail.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r--drivers/scsi/sd.c45
1 files changed, 41 insertions, 4 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 1e580e9e4a37..e1c884db7fca 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2843,7 +2843,6 @@ static void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer)
*/
static void sd_read_block_limits(struct scsi_disk *sdkp)
{
- unsigned int sector_sz = sdkp->device->sector_size;
struct scsi_vpd *vpd;
rcu_read_lock();
@@ -2852,9 +2851,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
if (!vpd || vpd->len < 16)
goto out;
- blk_queue_io_min(sdkp->disk->queue,
- get_unaligned_be16(&vpd->data[6]) * sector_sz);
-
+ sdkp->min_xfer_blocks = get_unaligned_be16(&vpd->data[6]);
sdkp->max_xfer_blocks = get_unaligned_be32(&vpd->data[8]);
sdkp->opt_xfer_blocks = get_unaligned_be32(&vpd->data[12]);
@@ -3112,6 +3109,29 @@ out:
kfree(buffer);
}
+static bool sd_validate_min_xfer_size(struct scsi_disk *sdkp)
+{
+ struct scsi_device *sdp = sdkp->device;
+ unsigned int min_xfer_bytes =
+ logical_to_bytes(sdp, sdkp->min_xfer_blocks);
+
+ if (sdkp->min_xfer_blocks == 0)
+ return false;
+
+ if (min_xfer_bytes & (sdkp->physical_block_size - 1)) {
+ sd_first_printk(KERN_WARNING, sdkp,
+ "Preferred minimum I/O size %u bytes not a " \
+ "multiple of physical block size (%u bytes)\n",
+ min_xfer_bytes, sdkp->physical_block_size);
+ sdkp->min_xfer_blocks = 0;
+ return false;
+ }
+
+ sd_first_printk(KERN_INFO, sdkp, "Preferred minimum I/O size %u bytes\n",
+ min_xfer_bytes);
+ return true;
+}
+
/*
* Determine the device's preferred I/O size for reads and writes
* unless the reported value is unreasonably small, large, not a
@@ -3123,6 +3143,8 @@ static bool sd_validate_opt_xfer_size(struct scsi_disk *sdkp,
struct scsi_device *sdp = sdkp->device;
unsigned int opt_xfer_bytes =
logical_to_bytes(sdp, sdkp->opt_xfer_blocks);
+ unsigned int min_xfer_bytes =
+ logical_to_bytes(sdp, sdkp->min_xfer_blocks);
if (sdkp->opt_xfer_blocks == 0)
return false;
@@ -3151,6 +3173,15 @@ static bool sd_validate_opt_xfer_size(struct scsi_disk *sdkp,
return false;
}
+ if (min_xfer_bytes && opt_xfer_bytes % min_xfer_bytes) {
+ sd_first_printk(KERN_WARNING, sdkp,
+ "Optimal transfer size %u bytes not a " \
+ "multiple of preferred minimum block " \
+ "size (%u bytes)\n",
+ opt_xfer_bytes, min_xfer_bytes);
+ return false;
+ }
+
if (opt_xfer_bytes & (sdkp->physical_block_size - 1)) {
sd_first_printk(KERN_WARNING, sdkp,
"Optimal transfer size %u bytes not a " \
@@ -3243,6 +3274,12 @@ static int sd_revalidate_disk(struct gendisk *disk)
dev_max = min_not_zero(dev_max, sdkp->max_xfer_blocks);
q->limits.max_dev_sectors = logical_to_sectors(sdp, dev_max);
+ if (sd_validate_min_xfer_size(sdkp))
+ blk_queue_io_min(sdkp->disk->queue,
+ logical_to_bytes(sdp, sdkp->min_xfer_blocks));
+ else
+ blk_queue_io_min(sdkp->disk->queue, 0);
+
if (sd_validate_opt_xfer_size(sdkp, dev_max)) {
q->limits.io_opt = logical_to_bytes(sdp, sdkp->opt_xfer_blocks);
rw_max = logical_to_sectors(sdp, sdkp->opt_xfer_blocks);