Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 4 Jun 2011 18:08:22 +0100 (BST)
From:      Gavin Atkinson <gavin@FreeBSD.org>
To:        freebsd-usb@freebsd.org
Subject:   USB suspend/resume
Message-ID:  <alpine.LNX.2.00.1106041548370.26975@ury.york.ac.uk.>

next in thread | raw e-mail | index | archive | help

Hi all,

I've been trying to get a IBM ThinkPad X60 suspend/resume working 
correctly, and discovered that with uhci loaded, the machine would hang on 
resume.  This is on head, but I believe this is aslo the cause of the 
laptop not resuming on stable/8.

It turns out that this is because we sleep in the resume path.  The resume 
path appears to run with interrupts disabled, and as a result we never 
wake up.  A comment above uhci_suspend() saggests similar:

	/* NOTE: suspend/resume is called from
	 * interrupt context and cannot sleep!

However, we do sleep.  We call usb_pause_mtx() several times during both 
tthe suspend and resume path, and on resume this is fatal.  
usb_pause_mtx() will use DELAY() when called before the machine has 
finished booting (ie cold != 0), but will call pause() once booted. To 
prove this, I've tested the following gross hack, which fixes 
suspend/resume on the ThinkPad X60:

Index: sys/dev/usb/controller/ehci_pci.c
===================================================================
--- sys/dev/usb/controller/ehci_pci.c	(revision 222417)
+++ sys/dev/usb/controller/ehci_pci.c	(working copy)
@@ -118,10 +118,16 @@
 ehci_pci_resume(device_t self)
 {
 	ehci_softc_t *sc = device_get_softc(self);
+	int mycold;
 
+	mycold = cold;
+	cold = 1;
+
 	ehci_pci_takecontroller(self);
 	ehci_resume(sc);
 
+	cold = mycold;
+
 	bus_generic_resume(self);
 
 	return (0);
Index: sys/dev/usb/controller/uhci_pci.c
===================================================================
--- sys/dev/usb/controller/uhci_pci.c	(revision 222417)
+++ sys/dev/usb/controller/uhci_pci.c	(working copy)
@@ -104,11 +104,17 @@
 uhci_pci_resume(device_t self)
 {
 	uhci_softc_t *sc = device_get_softc(self);
+	int mycold;
 
+	mycold = cold;
+	cold = 1;
+
 	pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2);
 
 	uhci_resume(sc);
 
+	cold = mycold;
+
 	bus_generic_resume(self);
 	return (0);
 }


The above hack fixes this X60 and allows suspend/resume to work, and USB 
devices work perfectly after the suspend/resume.  Other controllers look 
like they may also be affected  - at least ohci does the same.

So, I guess my question to the USB experts is:  What is the correct fix?  
I see two possibilities:

1) Have a variable somewhere called "suspres" or similar, which is set at 
   the start of the suspend, and cleared at the end of the resume.  
   usb_pause_mtx() then changes it's behaviour on (cold || suspres).  
   This may have to be a global variable, being incremented on suspend and 
   decremented on resume, as we don't pass a softc into usb_pause_mtx(), 
   however then we have issues relating to when one controller suspends 
   before the others, or resume before the others.

2) Kick off a separate task to do the resume.  Problems with this is thet 
   it doesn't fix the suspend path, but also that we may need to rely on 
   USB being back before the rest of the resume completes. (What happens 
   if we have / or another filesystem mounted from a USB device?)

I guess #1 is the simplest solution, and should have fewest side effects, 
however it does mean that every attachment would need to be modified in 
order to set and clear the variable.  It sort of feels like this should be 
handled more centrally, but I'm not sure of the best way to achieve this.

Any [other] suggestions?

Gavin



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?alpine.LNX.2.00.1106041548370.26975>