Commit 321da3dc authored by Martin K. Petersen's avatar Martin K. Petersen

scsi: sd: usb_storage: uas: Access media prior to querying device properties

It has been observed that some USB/UAS devices return generic properties
hardcoded in firmware for mode pages for a period of time after a device
has been discovered. The reported properties are either garbage or they do
not accurately reflect the characteristics of the physical storage device
attached in the case of a bridge.

Prior to commit 1e029397 ("scsi: sd: Reorganize DIF/DIX code to
avoid calling revalidate twice") we would call revalidate several
times during device discovery. As a result, incorrect values would
eventually get replaced with ones accurately describing the attached
storage. When we did away with the redundant revalidate pass, several
cases were reported where devices reported nonsensical values or would
end up in write-protected state.

An initial attempt at addressing this issue involved introducing a
delayed second revalidate invocation. However, this approach still
left some devices reporting incorrect characteristics.

Tasos Sahanidis debugged the problem further and identified that
introducing a READ operation prior to MODE SENSE fixed the problem and that
it wasn't a timing issue. Issuing a READ appears to cause the devices to
update their state to reflect the actual properties of the storage
media. Device properties like vendor, model, and storage capacity appear to
be correctly reported from the get-go. It is unclear why these devices
defer populating the remaining characteristics.

Match the behavior of a well known commercial operating system and
trigger a READ operation prior to querying device characteristics to
force the device to populate the mode pages.

The additional READ is triggered by a flag set in the USB storage and
UAS drivers. We avoid issuing the READ for other transport classes
since some storage devices identify Linux through our particular
discovery command sequence.

Link: https://lore.kernel.org/r/20240213143306.2194237-1-martin.petersen@oracle.com
Fixes: 1e029397 ("scsi: sd: Reorganize DIF/DIX code to avoid calling revalidate twice")
Cc: stable@vger.kernel.org
Reported-by: default avatarTasos Sahanidis <tasos@tasossah.com>
Reviewed-by: default avatarEwan D. Milne <emilne@redhat.com>
Reviewed-by: default avatarBart Van Assche <bvanassche@acm.org>
Tested-by: default avatarTasos Sahanidis <tasos@tasossah.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 379a58ca
...@@ -3407,6 +3407,24 @@ static bool sd_validate_opt_xfer_size(struct scsi_disk *sdkp, ...@@ -3407,6 +3407,24 @@ static bool sd_validate_opt_xfer_size(struct scsi_disk *sdkp,
return true; return true;
} }
static void sd_read_block_zero(struct scsi_disk *sdkp)
{
unsigned int buf_len = sdkp->device->sector_size;
char *buffer, cmd[10] = { };
buffer = kmalloc(buf_len, GFP_KERNEL);
if (!buffer)
return;
cmd[0] = READ_10;
put_unaligned_be32(0, &cmd[2]); /* Logical block address 0 */
put_unaligned_be16(1, &cmd[7]); /* Transfer 1 logical block */
scsi_execute_cmd(sdkp->device, cmd, REQ_OP_DRV_IN, buffer, buf_len,
SD_TIMEOUT, sdkp->max_retries, NULL);
kfree(buffer);
}
/** /**
* sd_revalidate_disk - called the first time a new disk is seen, * sd_revalidate_disk - called the first time a new disk is seen,
* performs disk spin up, read_capacity, etc. * performs disk spin up, read_capacity, etc.
...@@ -3446,7 +3464,13 @@ static int sd_revalidate_disk(struct gendisk *disk) ...@@ -3446,7 +3464,13 @@ static int sd_revalidate_disk(struct gendisk *disk)
*/ */
if (sdkp->media_present) { if (sdkp->media_present) {
sd_read_capacity(sdkp, buffer); sd_read_capacity(sdkp, buffer);
/*
* Some USB/UAS devices return generic values for mode pages
* until the media has been accessed. Trigger a READ operation
* to force the device to populate mode pages.
*/
if (sdp->read_before_ms)
sd_read_block_zero(sdkp);
/* /*
* set the default to rotational. All non-rotational devices * set the default to rotational. All non-rotational devices
* support the block characteristics VPD page, which will * support the block characteristics VPD page, which will
......
...@@ -179,6 +179,13 @@ static int slave_configure(struct scsi_device *sdev) ...@@ -179,6 +179,13 @@ static int slave_configure(struct scsi_device *sdev)
*/ */
sdev->use_192_bytes_for_3f = 1; sdev->use_192_bytes_for_3f = 1;
/*
* Some devices report generic values until the media has been
* accessed. Force a READ(10) prior to querying device
* characteristics.
*/
sdev->read_before_ms = 1;
/* /*
* Some devices don't like MODE SENSE with page=0x3f, * Some devices don't like MODE SENSE with page=0x3f,
* which is the command used for checking if a device * which is the command used for checking if a device
......
...@@ -878,6 +878,13 @@ static int uas_slave_configure(struct scsi_device *sdev) ...@@ -878,6 +878,13 @@ static int uas_slave_configure(struct scsi_device *sdev)
if (devinfo->flags & US_FL_CAPACITY_HEURISTICS) if (devinfo->flags & US_FL_CAPACITY_HEURISTICS)
sdev->guess_capacity = 1; sdev->guess_capacity = 1;
/*
* Some devices report generic values until the media has been
* accessed. Force a READ(10) prior to querying device
* characteristics.
*/
sdev->read_before_ms = 1;
/* /*
* Some devices don't like MODE SENSE with page=0x3f, * Some devices don't like MODE SENSE with page=0x3f,
* which is the command used for checking if a device * which is the command used for checking if a device
......
...@@ -208,6 +208,7 @@ struct scsi_device { ...@@ -208,6 +208,7 @@ struct scsi_device {
unsigned use_10_for_rw:1; /* first try 10-byte read / write */ unsigned use_10_for_rw:1; /* first try 10-byte read / write */
unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */ unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */
unsigned set_dbd_for_ms:1; /* Set "DBD" field in mode sense */ unsigned set_dbd_for_ms:1; /* Set "DBD" field in mode sense */
unsigned read_before_ms:1; /* perform a READ before MODE SENSE */
unsigned no_report_opcodes:1; /* no REPORT SUPPORTED OPERATION CODES */ unsigned no_report_opcodes:1; /* no REPORT SUPPORTED OPERATION CODES */
unsigned no_write_same:1; /* no WRITE SAME command */ unsigned no_write_same:1; /* no WRITE SAME command */
unsigned use_16_for_rw:1; /* Use read/write(16) over read/write(10) */ unsigned use_16_for_rw:1; /* Use read/write(16) over read/write(10) */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment