Re: [linux-usb-devel] question on flushing buffers and spinning down disk

Oliver Neukum <[email protected]>
Newsgroups gmane.linux.scsi,gmane.linux.usb.devel
Message-ID <[email protected]>
Am Dienstag 25 September 2007 schrieb Alan Stern:
> Getting all this to work will be tricky, because sd is a block device
> driven by requests issued through the block layer in atomic context,
> whereas suspend and resume require a process context.  Probably the 
> SCSI core would have to get involved in managing the synchronization.


Well,

here's a patch that seems to do what needs to be done to make it work.

	Regards
		Oliver

----

--- a/drivers/usb/storage/usb.c	2007-09-27 13:33:31.000000000 +0200
+++ b/drivers/usb/storage/usb.c	2007-09-27 13:30:21.000000000 +0200
@@ -182,33 +182,100 @@ static struct us_unusual_dev us_unusual_
 
 static int storage_suspend(struct usb_interface *iface, pm_message_t message)
 {
+	struct device *child;
 	struct us_data *us = usb_get_intfdata(iface);
+	struct Scsi_Host *host = us_to_host(us);
+	struct scsi_device *sdev, *sdev2 = NULL;
+	int err = 0;
+
+	/* In case of autosuspend we need to do extra work to flush
+	 * buffers and spin down disks. */
+	if (us->pusb_dev->auto_pm) {
+		US_DEBUGP("Autosuspend code path.\n");
+		/* block all devices against block and user space io */
+		shost_for_each_device(sdev, host) {
+			US_DEBUGP("Quiescing device.\n");
+			err = scsi_device_quiesce(sdev);
+			US_DEBUGP("Quiesced device with result %d.\n", err);
+			if (err < 0) {
+				sdev2 = sdev;
+				err = -EIO;
+				scsi_device_put(sdev);
+				goto err_unblock;
+			}
+			child = &sdev->sdev_gendev;
+			if (!child->driver || !child->driver->suspend)
+				continue;
+			US_DEBUGP("About to suspend disk.\n");
+			/* flush the buffers and spin down */
+			err = (child->driver->suspend)(child, message);
+			if (err < 0) {
+				US_DEBUGP("Could not suspend disk.\n");
+				sdev2 = sdev;
+				shost_for_each_device(sdev, host) {
+					if (sdev == sdev2) {
+						scsi_device_put(sdev);
+						break;
+					}
+					child = &sdev->sdev_gendev;
+					if (!child->driver || !child->driver->resume)
+						continue;
+					(child->driver->resume)(child);
+				}
+				err = -EIO;
+				sdev2 = NULL;
+				scsi_device_put(sdev);
+				goto err_unblock;
+			}
+		}
+	}
+
+	US_DEBUGP("%s\n", __FUNCTION__);
 
 	/* Wait until no command is running */
 	mutex_lock(&us->dev_mutex);
 
-	US_DEBUGP("%s\n", __FUNCTION__);
 	if (us->suspend_resume_hook)
 		(us->suspend_resume_hook)(us, US_SUSPEND);
 
-	/* When runtime PM is working, we'll set a flag to indicate
-	 * whether we should autoresume when a SCSI request arrives. */
-
 	mutex_unlock(&us->dev_mutex);
-	return 0;
+
+	/* In case of autosuspend device must be unblocked again */
+	if (us->pusb_dev->auto_pm) {
+err_unblock:
+		shost_for_each_device(sdev, host) {
+			if (sdev == sdev2) {
+				scsi_device_put(sdev);
+				break;
+			}
+			scsi_device_resume(sdev);
+		}
+	}
+	return err;
 }
 
 static int storage_resume(struct usb_interface *iface)
 {
 	struct us_data *us = usb_get_intfdata(iface);
+	struct Scsi_Host *host;
+	struct scsi_device *sdev;
+	struct device *child;
 
-	mutex_lock(&us->dev_mutex);
+	if (us->pusb_dev->auto_pm) {
+		host = us_to_host(us);
+		shost_for_each_device(sdev, host) {
+			child = &sdev->sdev_gendev;
+			if (!child->driver || !child->driver->resume)
+				continue;
+			(child->driver->resume)(child);
+		}
+	}
 
 	US_DEBUGP("%s\n", __FUNCTION__);
+
 	if (us->suspend_resume_hook)
 		(us->suspend_resume_hook)(us, US_RESUME);
 
-	mutex_unlock(&us->dev_mutex);
 	return 0;
 }
 
@@ -306,6 +373,7 @@ static int usb_stor_control_thread(void 
 {
 	struct us_data *us = (struct us_data *)__us;
 	struct Scsi_Host *host = us_to_host(us);
+	int autopm_rc;
 
 	for(;;) {
 		US_DEBUGP("*** thread sleeping.\n");
@@ -314,6 +382,9 @@ static int usb_stor_control_thread(void 
 			
 		US_DEBUGP("*** thread awakened.\n");
 
+		/* Autoresume the device */
+		autopm_rc = usb_autopm_get_interface(us->pusb_intf);
+
 		/* lock the device pointers */
 		mutex_lock(&(us->dev_mutex));
 
@@ -372,6 +443,12 @@ static int usb_stor_control_thread(void 
 			us->srb->result = SAM_STAT_GOOD;
 		}
 
+		/* Did the autoresume fail? */
+		else if (autopm_rc < 0) {
+			US_DEBUGP("Could not wake device\n");
+			us->srb->result = DID_ERROR << 16;
+		}
+
 		/* we've got a command, let's do it! */
 		else {
 			US_DEBUG(usb_stor_show_command(us->srb));
@@ -414,6 +491,10 @@ SkipForAbort:
 
 		/* unlock the device pointers */
 		mutex_unlock(&us->dev_mutex);
+
+		/* Start an autosuspend */
+		if (autopm_rc == 0)
+			usb_autopm_put_interface(us->pusb_intf);
 	} /* for (;;) */
 
 	/* Wait until we are told to stop */
@@ -931,6 +1012,7 @@ retry:
 		/* Should we unbind if no devices were detected? */
 	}
 
+	usb_autopm_put_interface(us->pusb_intf);
 	complete_and_exit(&us->scanning_done, 0);
 }
 
@@ -1016,6 +1098,7 @@ static int storage_probe(struct usb_inte
 		goto BadDevice;
 	}
 
+	usb_autopm_get_interface(intf); /* dropped in the scanning thread */
 	wake_up_process(th);
 
 	return 0;
@@ -1053,6 +1136,7 @@ static struct usb_driver usb_storage_dri
 	.pre_reset =	storage_pre_reset,
 	.post_reset =	storage_post_reset,
 	.id_table =	storage_usb_ids,
+	.supports_autosuspend = 1,
 };
 
 static int __init usb_stor_init(void)
--- a/drivers/usb/storage/scsiglue.c	2007-09-27 13:33:31.000000000 +0200
+++ b/drivers/usb/storage/scsiglue.c	2007-09-25 13:24:20.000000000 +0200
@@ -285,10 +285,15 @@ static int device_reset(struct scsi_cmnd
 
 	US_DEBUGP("%s called\n", __FUNCTION__);
 
-	/* lock the device pointers and do the reset */
-	mutex_lock(&(us->dev_mutex));
-	result = us->transport_reset(us);
-	mutex_unlock(&us->dev_mutex);
+	result = usb_autopm_get_interface(us->pusb_intf);
+	if (result == 0) {
+
+		/* lock the device pointers and do the reset */
+		mutex_lock(&(us->dev_mutex));
+		result = us->transport_reset(us);
+		mutex_unlock(&us->dev_mutex);
+		usb_autopm_put_interface(us->pusb_intf);
+	}
 
 	return result < 0 ? FAILED : SUCCESS;
 }
-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.