|
@@ -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);
|