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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html