[SCSI] sd: Add error handling during flushing caches
authorOliver Neukum <oneukum@suse.de>
Mon, 16 Sep 2013 11:28:15 +0000 (13:28 +0200)
committerJames Bottomley <JBottomley@Parallels.com>
Fri, 25 Oct 2013 08:58:13 +0000 (09:58 +0100)
It makes no sense to flush the cache of a device without medium.
Errors during suspend must be handled according to their causes.
Errors due to missing media or unplugged devices must be ignored.
Errors due to devices being offlined must also be ignored.
The error returns must be modified so that the generic layer
understands them.

[jejb: fix up whitespace and other formatting problems]
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/scsi_pm.c
drivers/scsi/sd.c

index 4c5aabe..af4c050 100644 (file)
@@ -54,7 +54,8 @@ scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *))
                /*
                 * All the high-level SCSI drivers that implement runtime
                 * PM treat runtime suspend, system suspend, and system
-                * hibernate identically.
+                * hibernate nearly identically. In all cases the requirements
+                * for runtime suspension are stricter.
                 */
                if (pm_runtime_suspended(dev))
                        return 0;
index ab96b79..3824e75 100644 (file)
@@ -105,7 +105,8 @@ static void sd_unlock_native_capacity(struct gendisk *disk);
 static int  sd_probe(struct device *);
 static int  sd_remove(struct device *);
 static void sd_shutdown(struct device *);
-static int sd_suspend(struct device *);
+static int sd_suspend_system(struct device *);
+static int sd_suspend_runtime(struct device *);
 static int sd_resume(struct device *);
 static void sd_rescan(struct device *);
 static int sd_done(struct scsi_cmnd *);
@@ -484,11 +485,11 @@ static struct class sd_disk_class = {
 };
 
 static const struct dev_pm_ops sd_pm_ops = {
-       .suspend                = sd_suspend,
+       .suspend                = sd_suspend_system,
        .resume                 = sd_resume,
-       .poweroff               = sd_suspend,
+       .poweroff               = sd_suspend_system,
        .restore                = sd_resume,
-       .runtime_suspend        = sd_suspend,
+       .runtime_suspend        = sd_suspend_runtime,
        .runtime_resume         = sd_resume,
 };
 
@@ -1438,7 +1439,6 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
        if (!scsi_device_online(sdp))
                return -ENODEV;
 
-
        for (retries = 3; retries > 0; --retries) {
                unsigned char cmd[10] = { 0 };
 
@@ -1456,12 +1456,31 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
 
        if (res) {
                sd_print_result(sdkp, res);
+
                if (driver_byte(res) & DRIVER_SENSE)
                        sd_print_sense_hdr(sdkp, &sshdr);
+               /* we need to evaluate the error return  */
+               if (scsi_sense_valid(&sshdr) &&
+                       /* 0x3a is medium not present */
+                       sshdr.asc == 0x3a)
+                               /* this is no error here */
+                               return 0;
+
+               switch (host_byte(res)) {
+               /* ignore errors due to racing a disconnection */
+               case DID_BAD_TARGET:
+               case DID_NO_CONNECT:
+                       return 0;
+               /* signal the upper layer it might try again */
+               case DID_BUS_BUSY:
+               case DID_IMM_RETRY:
+               case DID_REQUEUE:
+               case DID_SOFT_ERROR:
+                       return -EBUSY;
+               default:
+                       return -EIO;
+               }
        }
-
-       if (res)
-               return -EIO;
        return 0;
 }
 
@@ -3061,9 +3080,17 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start)
                sd_print_result(sdkp, res);
                if (driver_byte(res) & DRIVER_SENSE)
                        sd_print_sense_hdr(sdkp, &sshdr);
+               if (scsi_sense_valid(&sshdr) &&
+                       /* 0x3a is medium not present */
+                       sshdr.asc == 0x3a)
+                       res = 0;
        }
 
-       return res;
+       /* SCSI error codes must not go to the generic layer */
+       if (res)
+               return -EIO;
+
+       return 0;
 }
 
 /*
@@ -3081,7 +3108,7 @@ static void sd_shutdown(struct device *dev)
        if (pm_runtime_suspended(dev))
                goto exit;
 
-       if (sdkp->WCE) {
+       if (sdkp->WCE && sdkp->media_present) {
                sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
                sd_sync_cache(sdkp);
        }
@@ -3095,7 +3122,7 @@ exit:
        scsi_disk_put(sdkp);
 }
 
-static int sd_suspend(struct device *dev)
+static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
 {
        struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);
        int ret = 0;
@@ -3103,16 +3130,23 @@ static int sd_suspend(struct device *dev)
        if (!sdkp)
                return 0;       /* this can happen */
 
-       if (sdkp->WCE) {
+       if (sdkp->WCE && sdkp->media_present) {
                sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
                ret = sd_sync_cache(sdkp);
-               if (ret)
+               if (ret) {
+                       /* ignore OFFLINE device */
+                       if (ret == -ENODEV)
+                               ret = 0;
                        goto done;
+               }
        }
 
        if (sdkp->device->manage_start_stop) {
                sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
+               /* an error is not worth aborting a system sleep */
                ret = sd_start_stop_device(sdkp, 0);
+               if (ignore_stop_errors)
+                       ret = 0;
        }
 
 done:
@@ -3120,6 +3154,16 @@ done:
        return ret;
 }
 
+static int sd_suspend_system(struct device *dev)
+{
+       return sd_suspend_common(dev, true);
+}
+
+static int sd_suspend_runtime(struct device *dev)
+{
+       return sd_suspend_common(dev, false);
+}
+
 static int sd_resume(struct device *dev)
 {
        struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);