Skip to content

Commit

Permalink
scsi: sd: Fix sshdr use in sd_suspend_common()
Browse files Browse the repository at this point in the history
If scsi_execute_cmd() returns < 0, it doesn't initialize the sshdr, so we
shouldn't access the sshdr. If it returns 0, then the cmd executed
successfully, so there is no need to check the sshdr. sd_sync_cache() will
only access the sshdr if it's been setup because it calls
scsi_status_is_check_condition() before accessing it. However, the
sd_sync_cache() caller, sd_suspend_common(), does not check.

sd_suspend_common() is only checking for ILLEGAL_REQUEST which it's using
to determine if the command is supported. If it's not it just ignores the
error. So to fix its sshdr use this patch just moves that check to
sd_sync_cache() where it converts ILLEGAL_REQUEST to success/0.
sd_suspend_common() was ignoring that error and sd_shutdown() doesn't check
for errors so there will be no behavior changes.

Signed-off-by: Mike Christie <michael.christie@oracle.com>
Link: https://lore.kernel.org/r/20231106231304.5694-2-michael.christie@oracle.com
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
  • Loading branch information
Mike Christie authored and Martin K. Petersen committed Nov 9, 2023
1 parent 037fbd3 commit 3b83486
Showing 1 changed file with 23 additions and 30 deletions.
53 changes: 23 additions & 30 deletions drivers/scsi/sd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1565,24 +1565,21 @@ static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing)
return disk_changed ? DISK_EVENT_MEDIA_CHANGE : 0;
}

static int sd_sync_cache(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr)
static int sd_sync_cache(struct scsi_disk *sdkp)
{
int retries, res;
struct scsi_device *sdp = sdkp->device;
const int timeout = sdp->request_queue->rq_timeout
* SD_FLUSH_TIMEOUT_MULTIPLIER;
struct scsi_sense_hdr my_sshdr;
struct scsi_sense_hdr sshdr;
const struct scsi_exec_args exec_args = {
.req_flags = BLK_MQ_REQ_PM,
/* caller might not be interested in sense, but we need it */
.sshdr = sshdr ? : &my_sshdr,
.sshdr = &sshdr,
};

if (!scsi_device_online(sdp))
return -ENODEV;

sshdr = exec_args.sshdr;

for (retries = 3; retries > 0; --retries) {
unsigned char cmd[16] = { 0 };

Expand All @@ -1607,15 +1604,23 @@ static int sd_sync_cache(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr)
return res;

if (scsi_status_is_check_condition(res) &&
scsi_sense_valid(sshdr)) {
sd_print_sense_hdr(sdkp, sshdr);
scsi_sense_valid(&sshdr)) {
sd_print_sense_hdr(sdkp, &sshdr);

/* we need to evaluate the error return */
if (sshdr->asc == 0x3a || /* medium not present */
sshdr->asc == 0x20 || /* invalid command */
(sshdr->asc == 0x74 && sshdr->ascq == 0x71)) /* drive is password locked */
if (sshdr.asc == 0x3a || /* medium not present */
sshdr.asc == 0x20 || /* invalid command */
(sshdr.asc == 0x74 && sshdr.ascq == 0x71)) /* drive is password locked */
/* this is no error here */
return 0;
/*
* This drive doesn't support sync and there's not much
* we can do because this is called during shutdown
* or suspend so just return success so those operations
* can proceed.
*/
if (sshdr.sense_key == ILLEGAL_REQUEST)
return 0;
}

switch (host_byte(res)) {
Expand Down Expand Up @@ -3774,7 +3779,7 @@ static void sd_shutdown(struct device *dev)

if (sdkp->WCE && sdkp->media_present) {
sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
sd_sync_cache(sdkp, NULL);
sd_sync_cache(sdkp);
}

if (system_state != SYSTEM_RESTART && sdkp->device->manage_start_stop) {
Expand All @@ -3786,7 +3791,6 @@ static void sd_shutdown(struct device *dev)
static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
{
struct scsi_disk *sdkp = dev_get_drvdata(dev);
struct scsi_sense_hdr sshdr;
int ret = 0;

if (!sdkp) /* E.g.: runtime suspend following sd_remove() */
Expand All @@ -3795,24 +3799,13 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
if (sdkp->WCE && sdkp->media_present) {
if (!sdkp->device->silence_suspend)
sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
ret = sd_sync_cache(sdkp, &sshdr);

if (ret) {
/* ignore OFFLINE device */
if (ret == -ENODEV)
return 0;

if (!scsi_sense_valid(&sshdr) ||
sshdr.sense_key != ILLEGAL_REQUEST)
return ret;
ret = sd_sync_cache(sdkp);
/* ignore OFFLINE device */
if (ret == -ENODEV)
return 0;

/*
* sshdr.sense_key == ILLEGAL_REQUEST means this drive
* doesn't support sync. There's not much to do and
* suspend shouldn't fail.
*/
ret = 0;
}
if (ret)
return ret;
}

if (sdkp->device->manage_start_stop) {
Expand Down

0 comments on commit 3b83486

Please sign in to comment.