浏览代码

scsi: sd: Ignore sync cache failures when not supported

Some external hard drives don't support the sync command even though the
hard drive has write cache enabled. In this case, upon suspend request,
sync cache failures are ignored if the error code in the sense header is
ILLEGAL_REQUEST. There's not much we can do for these drives, so we
shouldn't fail to suspend for this error case. The drive may stay
powered if that's the setup for the port it's plugged into.

Signed-off-by: Derek Basehore <dbasehore@chromium.org>
Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
Reviewed-by: Ewan D. Milne <emilne@redhat.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Derek Basehore 8 年之前
父节点
当前提交
4fa8324461
共有 1 个文件被更改,包括 28 次插入12 次删除
  1. 28 12
      drivers/scsi/sd.c

+ 28 - 12
drivers/scsi/sd.c

@@ -1582,17 +1582,21 @@ out:
 	return retval;
 	return retval;
 }
 }
 
 
-static int sd_sync_cache(struct scsi_disk *sdkp)
+static int sd_sync_cache(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr)
 {
 {
 	int retries, res;
 	int retries, res;
 	struct scsi_device *sdp = sdkp->device;
 	struct scsi_device *sdp = sdkp->device;
 	const int timeout = sdp->request_queue->rq_timeout
 	const int timeout = sdp->request_queue->rq_timeout
 		* SD_FLUSH_TIMEOUT_MULTIPLIER;
 		* SD_FLUSH_TIMEOUT_MULTIPLIER;
-	struct scsi_sense_hdr sshdr;
+	struct scsi_sense_hdr my_sshdr;
 
 
 	if (!scsi_device_online(sdp))
 	if (!scsi_device_online(sdp))
 		return -ENODEV;
 		return -ENODEV;
 
 
+	/* caller might not be interested in sense, but we need it */
+	if (!sshdr)
+		sshdr = &my_sshdr;
+
 	for (retries = 3; retries > 0; --retries) {
 	for (retries = 3; retries > 0; --retries) {
 		unsigned char cmd[10] = { 0 };
 		unsigned char cmd[10] = { 0 };
 
 
@@ -1601,7 +1605,7 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
 		 * Leave the rest of the command zero to indicate
 		 * Leave the rest of the command zero to indicate
 		 * flush everything.
 		 * flush everything.
 		 */
 		 */
-		res = scsi_execute(sdp, cmd, DMA_NONE, NULL, 0, NULL, &sshdr,
+		res = scsi_execute(sdp, cmd, DMA_NONE, NULL, 0, NULL, sshdr,
 				timeout, SD_MAX_RETRIES, 0, RQF_PM, NULL);
 				timeout, SD_MAX_RETRIES, 0, RQF_PM, NULL);
 		if (res == 0)
 		if (res == 0)
 			break;
 			break;
@@ -1611,11 +1615,12 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
 		sd_print_result(sdkp, "Synchronize Cache(10) failed", res);
 		sd_print_result(sdkp, "Synchronize Cache(10) failed", res);
 
 
 		if (driver_byte(res) & DRIVER_SENSE)
 		if (driver_byte(res) & DRIVER_SENSE)
-			sd_print_sense_hdr(sdkp, &sshdr);
+			sd_print_sense_hdr(sdkp, sshdr);
+
 		/* we need to evaluate the error return  */
 		/* we need to evaluate the error return  */
-		if (scsi_sense_valid(&sshdr) &&
-			(sshdr.asc == 0x3a ||	/* medium not present */
-			 sshdr.asc == 0x20))	/* invalid command */
+		if (scsi_sense_valid(sshdr) &&
+			(sshdr->asc == 0x3a ||	/* medium not present */
+			 sshdr->asc == 0x20))	/* invalid command */
 				/* this is no error here */
 				/* this is no error here */
 				return 0;
 				return 0;
 
 
@@ -3459,7 +3464,7 @@ static void sd_shutdown(struct device *dev)
 
 
 	if (sdkp->WCE && sdkp->media_present) {
 	if (sdkp->WCE && sdkp->media_present) {
 		sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
 		sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
-		sd_sync_cache(sdkp);
+		sd_sync_cache(sdkp, NULL);
 	}
 	}
 
 
 	if (system_state != SYSTEM_RESTART && sdkp->device->manage_start_stop) {
 	if (system_state != SYSTEM_RESTART && sdkp->device->manage_start_stop) {
@@ -3471,6 +3476,7 @@ static void sd_shutdown(struct device *dev)
 static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
 static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
 {
 {
 	struct scsi_disk *sdkp = dev_get_drvdata(dev);
 	struct scsi_disk *sdkp = dev_get_drvdata(dev);
+	struct scsi_sense_hdr sshdr;
 	int ret = 0;
 	int ret = 0;
 
 
 	if (!sdkp)	/* E.g.: runtime suspend following sd_remove() */
 	if (!sdkp)	/* E.g.: runtime suspend following sd_remove() */
@@ -3478,12 +3484,23 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
 
 
 	if (sdkp->WCE && sdkp->media_present) {
 	if (sdkp->WCE && sdkp->media_present) {
 		sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
 		sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
-		ret = sd_sync_cache(sdkp);
+		ret = sd_sync_cache(sdkp, &sshdr);
+
 		if (ret) {
 		if (ret) {
 			/* ignore OFFLINE device */
 			/* ignore OFFLINE device */
 			if (ret == -ENODEV)
 			if (ret == -ENODEV)
-				ret = 0;
-			goto done;
+				return 0;
+
+			if (!scsi_sense_valid(&sshdr) ||
+			    sshdr.sense_key != ILLEGAL_REQUEST)
+				return ret;
+
+			/*
+			 * 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;
 		}
 		}
 	}
 	}
 
 
@@ -3495,7 +3512,6 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
 			ret = 0;
 			ret = 0;
 	}
 	}
 
 
-done:
 	return ret;
 	return ret;
 }
 }