]> git.baikalelectronics.ru Git - kernel.git/commitdiff
scsi: sd: Fix optimal I/O size for devices that change reported values
authorMartin K. Petersen <martin.petersen@oracle.com>
Tue, 24 Mar 2020 15:16:15 +0000 (11:16 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 1 Apr 2020 09:02:01 +0000 (11:02 +0200)
commit 2b7e13d33a56cd2fd792992a7d3758472ce44588 upstream.

Some USB bridge devices will return a default set of characteristics during
initialization. And then, once an attached drive has spun up, substitute
the actual parameters reported by the drive. According to the SCSI spec,
the device should return a UNIT ATTENTION in case any reported parameters
change. But in this case the change is made silently after a small window
where default values are reported.

Commit dd13fc0f321e ("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, this validation did not account for the fact that
aforementioned devices will return default values during a brief window
during spin-up. The subsequent change in reported characteristics would
invalidate the checking that had previously been performed.

Unset a previously configured optimal I/O size should the sanity checking
fail on subsequent revalidate attempts.

Link: https://lore.kernel.org/r/33fb522e-4f61-1b76-914f-c9e6a3553c9b@gmail.com
Cc: Bryan Gurney <bgurney@redhat.com>
Cc: <stable@vger.kernel.org>
Reported-by: Bernhard Sulzer <micraft.b@gmail.com>
Tested-by: Bernhard Sulzer <micraft.b@gmail.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/scsi/sd.c

index ac2e88ec11904fe76a07b20fb09a469947bd05b3..6a2f8bacfaceae9c75423e92e8e5a474b322ef79 100644 (file)
@@ -3171,9 +3171,11 @@ static int sd_revalidate_disk(struct gendisk *disk)
        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);
-       } else
+       } else {
+               q->limits.io_opt = 0;
                rw_max = min_not_zero(logical_to_sectors(sdp, dev_max),
                                      (sector_t)BLK_DEF_MAX_SECTORS);
+       }
 
        /* Do not exceed controller limit */
        rw_max = min(rw_max, queue_max_hw_sectors(q));