On Monday 20 September 2004 12:28 pm, Alan Stern wrote:
> > And there are others.  Some of those fixes are in my patchset.
> 
> Would you care to post your fixes?

Here you go!   I think you mentioned a couple issues I didn't address
here, by the way -- haven't had a chance to look yet.

 
> > .... I'd
> > rather have USB behaving better -- notably, integrated so that it works
> > OK with system-wide suspend not just USB suspend/wake/resume --  before
> > any of that starts.
> 
> I think that should be very doable, and it shouldn't even need very much 
> work -- apart from the root hub implementations (subject of a different 
> thread).

I think I'll disagree about "much work" given the testing requirements,
but if you measure in lines of code, this patch isn't very large and it
gets a bunch of the problems solved.  OHCI was mostly behaving...

- Dave


This patch has various updates that seem to help make USB_SUSPEND
integrate better with suspend-to-disk (swsusp).  The whole thing
doesn't work right yet; khubd's ignoring the root hub after resuming
from power-off.

  PMCORE:
    - fix the state numbering problem
    - fail attempts to suspend/resume devices with a suspended parent
    - filter some superfluous suspend calls
  SWSUSP:
    - use symbolic names for device/system/suspend/??? state
  USBCORE:
    - refuse some suspend paths.
    - "legacy" PCI devices will fake pci_set_power_state() effects
    - handle "deeper" suspends in case anyone tries

Notice how many states are associated with root hubs:  the hub interface
state, the hub state, the PCI device state, the HCD state, and the driver
model power_state.  Did I miss one?

Probably all the HCDs need changes here.  I've tested with OHCI, and
have some EHCI patches too.


--- 1.14/drivers/base/power/resume.c	Wed Jun  9 23:34:24 2004
+++ edited/drivers/base/power/resume.c	Mon Sep 20 20:10:59 2004
@@ -22,6 +22,14 @@
 
 int resume_device(struct device * dev)
 {
+	if (dev->power.pm_parent
+			&& dev->power.pm_parent->power.power_state) {
+		dev_err(dev, "PM: resume from %d, parent %s still %d\n",
+			dev->power.power_state,
+			dev->power.pm_parent->bus_id,
+			dev->power.pm_parent->power.power_state);
+		return -EIO;
+	}
 	if (dev->bus && dev->bus->resume)
 		return dev->bus->resume(dev);
 	return 0;
--- 1.16/drivers/base/power/suspend.c	Wed Jun  9 23:34:24 2004
+++ edited/drivers/base/power/suspend.c	Mon Sep 20 20:10:59 2004
@@ -39,6 +39,20 @@
 {
 	int error = 0;
 
+	if (dev->power.power_state >= state) {
+		dev_dbg(dev, "PM: suspend %d-->%d\n",
+			dev->power.power_state, state);
+		return 0;
+	}
+	if (dev->power.pm_parent
+			&& dev->power.pm_parent->power.power_state) {
+		dev_err(dev,
+			"PM: suspend %d->%d, parent %s already %d\n",
+			dev->power.power_state, state,
+			dev->power.pm_parent->bus_id,
+			dev->power.pm_parent->power.power_state);
+		return -EDEADLK;
+	}
 	dev_dbg(dev, "suspending\n");
 
 	dev->power.prev_state = dev->power.power_state;
--- 1.36/drivers/usb/core/hcd-pci.c	Wed Sep 15 11:51:43 2004
+++ edited/drivers/usb/core/hcd-pci.c	Mon Sep 20 20:19:19 2004
@@ -268,6 +268,18 @@
 
 #ifdef	CONFIG_PM
 
+static char __attribute_used__ *pci_state(u32 state)
+{
+	switch (state) {
+	case 0:		return "D0";
+	case 1:		return "D1";
+	case 2:		return "D2";
+	case 3:		return "D3hot";
+	case 4:		return "D3cold";
+	}
+	return NULL;
+}
+
 /**
  * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
  * @dev: USB Host Controller being suspended
@@ -288,16 +300,34 @@
 	 * PM-sensitive HCDs may already have done this.
 	 */
 	has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
-	if (has_pci_pm)
-		dev_dbg(hcd->self.controller, "suspend D%d --> D%d\n",
-			dev->current_state, state);
+	if (state > 4)
+		state = 4;
 
 	switch (hcd->state) {
 	case USB_STATE_HALT:
 		dev_dbg (hcd->self.controller, "halted; hcd not suspended\n");
 		break;
 	case HCD_STATE_SUSPENDED:
-		dev_dbg (hcd->self.controller, "hcd already suspended\n");
+		dev_dbg (hcd->self.controller, "PCI %s --> %s\n",
+				pci_state(dev->current_state),
+				pci_state(state));
+		if (state > 3)
+			state = 3;
+
+		if (state == dev->current_state)
+			break;
+		else if (state < dev->current_state)
+			retval = -EIO;
+		else if (has_pci_pm)
+			retval = pci_set_power_state (dev, state);
+		else
+			dev->current_state = state;
+
+		if (retval == 0)
+			dev->dev.power.power_state = state;
+		else
+			dev_dbg (hcd->self.controller, 
+					"re-suspend fail, %d\n", retval);
 		break;
 	default:
 		retval = hcd->driver->suspend (hcd, state);
@@ -308,22 +338,48 @@
 		else {
 			hcd->state = HCD_STATE_SUSPENDED;
 			pci_save_state (dev, hcd->pci_state);
-#ifdef	CONFIG_USB_SUSPEND
-			pci_enable_wake (dev, state, hcd->remote_wakeup);
-			pci_enable_wake (dev, 4, hcd->remote_wakeup);
-#endif
+
 			/* no DMA or IRQs except in D0 */
 			pci_disable_device (dev);
 			free_irq (hcd->irq, hcd);
 			
-			if (has_pci_pm)
+			if (has_pci_pm) {
 				retval = pci_set_power_state (dev, state);
-			dev->dev.power.power_state = state;
+
+				/* POLICY: ignore D1/D2/D3hot differences;
+				 * we know D3hot will always work.
+				 */
+				if (retval < 0 && state < 3) {
+					retval = pci_set_power_state (dev, 3);
+					if (retval == 0)
+						state = 3;
+				}
+				if (retval == 0) {
+					dev->dev.power.power_state = state;
+#ifdef	CONFIG_USB_SUSPEND
+					pci_enable_wake (dev, state,
+							hcd->remote_wakeup);
+					pci_enable_wake (dev, 4,
+							hcd->remote_wakeup);
+#endif
+				}
+			} else {
+				if (state > 3)
+					state = 3;
+				dev->current_state = state;
+				dev->dev.power.power_state = state;
+			}
 			if (retval < 0) {
 				dev_dbg (&dev->dev,
-						"PCI suspend fail, %d\n",
+						"PCI %s suspend fail, %d\n",
+						pci_state(state),
 						retval);
 				(void) usb_hcd_pci_resume (dev);
+			} else {
+				dev_dbg(hcd->self.controller,
+					"suspended to PCI %s%s\n",
+					pci_state(state),
+					has_pci_pm ? " (PM)" : "");
 			}
 		}
 	}
@@ -345,9 +401,10 @@
 
 	hcd = pci_get_drvdata(dev);
 	has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
-	if (has_pci_pm)
-		dev_dbg(hcd->self.controller, "resume from state D%d\n",
-				dev->current_state);
+
+	/* D3cold resume isn't usually reported this way... */
+	dev_dbg(hcd->self.controller, "resume from PCI %s\n",
+			pci_state(dev->current_state));
 
 	if (hcd->state != HCD_STATE_SUSPENDED) {
 		dev_dbg (hcd->self.controller, 
@@ -358,6 +415,8 @@
 
 	if (has_pci_pm)
 		pci_set_power_state (dev, 0);
+	else
+		dev->current_state = 0;
 	dev->dev.power.power_state = 0;
 	retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ,
 				hcd->description, hcd);
--- 1.97/drivers/usb/core/hcd.c	Mon Sep 13 04:47:16 2004
+++ edited/drivers/usb/core/hcd.c	Mon Sep 20 20:10:32 2004
@@ -540,7 +540,7 @@
 		urb->actual_length = length;
 		urb->status = 0;
 		urb->hcpriv = NULL;
-	} else
+	} else if (!urb->dev->dev.power.power_state)
 		mod_timer (&hcd->rh_timer, jiffies + HZ/4);
 	spin_unlock (&hcd_data_lock);
 	spin_unlock (&urb->lock);
--- 1.138/drivers/usb/core/hub.c	Mon Sep 13 05:12:00 2004
+++ edited/drivers/usb/core/hub.c	Mon Sep 20 20:10:32 2004
@@ -1487,14 +1487,13 @@
 {
 	int	status;
 
-	/* caller owns the udev device lock */
 	if (port < 0)
 		return port;
 
-	if (state <= udev->dev.power.power_state
-			|| state < PM_SUSPEND_MEM
-			|| udev->state == USB_STATE_SUSPENDED
+	/* NOTE:  udev->serialize released on all real returns! */
+	if (udev->dev.power.power_state
 			|| udev->state == USB_STATE_NOTATTACHED) {
+		up(&udev->serialize);
 		return 0;
 	}
 
@@ -1557,7 +1556,6 @@
 	 */
 	if (state > PM_SUSPEND_MEM) {
 		dev_warn(&udev->dev, "no poweroff yet, suspending instead\n");
-		state = PM_SUSPEND_MEM;
 	}
 
 	/* "global suspend" of the HC-to-USB interface (root hub), or
@@ -1861,6 +1859,9 @@
 	struct usb_hub		*hub = usb_get_intfdata (intf);
 	unsigned		port;
 	int			status;
+
+	if (!intf->dev.power_state)
+		return 0;
 
 	for (port = 0; port < hdev->maxchild; port++) {
 		struct usb_device	*udev;
--- 1.176/drivers/usb/core/usb.c	Mon Sep 13 05:11:34 2004
+++ edited/drivers/usb/core/usb.c	Mon Sep 20 20:10:32 2004
@@ -1402,6 +1402,10 @@
 	struct usb_interface *intf;
 	struct usb_driver *driver;
 
+	/* there's only one USB suspend state */
+	if (dev->power.power_state)
+		return 0;
+
 	if (dev->driver == &usb_generic_driver)
 		return usb_suspend_device (to_usb_device(dev), state);
 
--- 1.16/include/linux/pm.h	Thu Jul  1 22:23:53 2004
+++ edited/include/linux/pm.h	Mon Sep 20 20:10:59 2004
@@ -194,11 +194,12 @@
 extern void (*pm_power_off)(void);
 
 enum {
-	PM_SUSPEND_ON,
-	PM_SUSPEND_STANDBY,
-	PM_SUSPEND_MEM,
-	PM_SUSPEND_DISK,
-	PM_SUSPEND_MAX,
+	PM_SUSPEND_ON = 0,
+	PM_SUSPEND_STANDBY = 1,
+	/* NOTE: PM_SUSPEND_MEM == PCI_D3hot */
+	PM_SUSPEND_MEM = 3,
+	PM_SUSPEND_DISK = 4,
+	PM_SUSPEND_MAX = 5,
 };
 
 enum {
--- 1.17/kernel/power/main.c	Mon Apr 12 10:54:21 2004
+++ edited/kernel/power/main.c	Mon Sep 20 20:10:59 2004
@@ -225,8 +225,8 @@
 	p = memchr(buf, '\n', n);
 	len = p ? p - buf : n;
 
-	for (s = &pm_states[state]; *s; s++, state++) {
-		if (!strncmp(buf, *s, len))
+	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
+		if (*s && !strncmp(buf, *s, len))
 			break;
 	}
 	if (*s)
--- 1.86/kernel/power/swsusp.c	Thu Jul  1 22:23:48 2004
+++ edited/kernel/power/swsusp.c	Mon Sep 20 20:10:59 2004
@@ -699,7 +699,7 @@
 	else
 #endif
 	{
-		device_suspend(3);
+		device_suspend(PM_SUSPEND_DISK);
 		device_shutdown();
 		machine_power_off();
 	}
@@ -720,7 +720,7 @@
 	mb();
 	spin_lock_irq(&suspend_pagedir_lock);	/* Done to disable interrupts */ 
 
-	device_power_down(3);
+	device_power_down(PM_SUSPEND_DISK);
 	PRINTK( "Waiting for DMAs to settle down...\n");
 	mdelay(1000);	/* We do not want some readahead with DMA to corrupt our memory, right?
 			   Do it with disabled interrupts for best effect. That way, if some
@@ -789,7 +789,7 @@
 {
 	int is_problem;
 	read_swapfiles();
-	device_power_down(3);
+	device_power_down(PM_SUSPEND_DISK);
 	is_problem = suspend_prepare_image();
 	device_power_up();
 	spin_unlock_irq(&suspend_pagedir_lock);
@@ -844,8 +844,8 @@
 		free_some_memory();
 		disable_nonboot_cpus();
 		/* Save state of all device drivers, and stop them. */
-		printk("Suspending devices... ");
-		if ((res = device_suspend(3))==0) {
+		printk("Suspending devices...\n");
+		if ((res = device_suspend(PM_SUSPEND_DISK))==0) {
 			/* If stopping device drivers worked, we proceed basically into
 			 * suspend_save_image.
 			 *
@@ -1200,7 +1200,7 @@
 		goto read_failure;
 	/* FIXME: Should we stop processes here, just to be safer? */
 	disable_nonboot_cpus();
-	device_suspend(3);
+	device_suspend(PM_SUSPEND_DISK);
 	do_magic(1);
 	panic("This never returns");
 

Reply via email to