2006-09-05 15:26:53

by Alan Stern

[permalink] [raw]
Subject: [RFC] USB device persistence across suspend-to-disk

Lots of people have asked why USB mass-storage devices don't survive
suspend-to-disk (swsusp). The answer is technical and not too
interesting; what matters is that people have been forced to unmount all
filesystems on USB devices before doing suspend-to-disk. On some systems
this is necessary even for suspend-to-RAM.

This patch adds the capability for USB devices to persist across such
suspends. You can even suspend with your root fs on a USB flash device!
(I think -- I haven't actually tried it. But it _should_ work.)

It's all explained in Documentation/usb/persist.txt, added by the patch.
The "persist" mode is turned off by default because it can be dangerous.

The patch is based on 2.6.18-rc5-mm1. If people like it, I'll submit it
to Greg KH for inclusion in the USB development tree.

Alan Stern


Index: mm/drivers/usb/core/generic.c
===================================================================
--- mm.orig/drivers/usb/core/generic.c
+++ mm/drivers/usb/core/generic.c
@@ -194,6 +194,8 @@ static int generic_suspend(struct usb_de

static int generic_resume(struct usb_device *udev)
{
+ if (udev->do_persist)
+ return usb_reset_persistent_device(udev);
return usb_port_resume(udev);
}

Index: mm/drivers/usb/core/hub.c
===================================================================
--- mm.orig/drivers/usb/core/hub.c
+++ mm/drivers/usb/core/hub.c
@@ -61,6 +61,18 @@ module_param (blinkenlights, bool, S_IRU
MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");

/*
+ * Support persistence of devices across power-loss during suspend.
+ *
+ * WARNING: This option can corrupt filesystems and crash the kernel
+ * if a mass-storage device or its media are switched while the system
+ * is asleep and the controller is unpowered.
+ */
+static int persist;
+module_param(persist, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(persist, "Make devices persist across suspend-to-disk "
+ "(USE AT YOUR OWN RISK)");
+
+/*
* As of 2.6.10 we introduce a new USB device initialization scheme which
* closely resembles the way Windows works. Hopefully it will be compatible
* with a wider range of devices than the old scheme. However some previously
@@ -540,6 +552,12 @@ static void hub_pre_reset(struct usb_int
struct usb_device *hdev = hub->hdev;
int port1;

+ /* If this is part of a "persistent-device" reset then we should
+ * do nothing, especially not disconnect the children.
+ */
+ if (hdev->do_persist)
+ return;
+
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
if (hdev->children[port1 - 1]) {
usb_disconnect(&hdev->children[port1 - 1]);
@@ -1056,6 +1074,9 @@ void usb_set_device_state(struct usb_dev
* is resumed and Vbus power has been interrupted or the controller
* has been reset. The routine marks all the children of the root hub
* as NOTATTACHED and marks logical connect-change events on their ports.
+ *
+ * But if the "persist" module parameter is set, instead the routine
+ * begins the power-session recovery procedure at the root hub.
*/
void usb_root_hub_lost_power(struct usb_device *rhdev)
{
@@ -1064,6 +1085,13 @@ void usb_root_hub_lost_power(struct usb_
unsigned long flags;

dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
+
+ /* Start the "persistent device" mechanism */
+ if (persist) {
+ usb_reset_persistent_device(rhdev);
+ return;
+ }
+
spin_lock_irqsave(&device_state_lock, flags);
hub = hdev_to_hub(rhdev);
for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
@@ -1077,6 +1105,90 @@ void usb_root_hub_lost_power(struct usb_
}
EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);

+/**
+ * usb_reset_persistent_device - reset device following power-session loss
+ * @udev: device to be reset instead of resumed
+ *
+ * If a host controller doesn't maintain VBUS suspend current during a
+ * system sleep or is reset when the system wakes up, all the USB
+ * power sessions below it will be broken. This is especially troublesome
+ * for mass-storage devices containing mounted filesystems, since the
+ * device will appear to have disconnected and all the memory mappings
+ * to it will be lost.
+ *
+ * As an alternative, this routine attempts to recover power sessions for
+ * devices that are still present by resetting them instead of resuming
+ * them. If all goes well, the devices will appear to persist across the
+ * the interruption of the power sessions.
+ *
+ * This facility is inherently dangerous. Although usb_reset_device()
+ * makes every effort to insure that the same device is present after the
+ * reset as before, it cannot provide a 100% guarantee. Furthermore it's
+ * quite possible for a device to remain unaltered but its media to be
+ * changed. If the user replaces a flash memory card while the system is
+ * asleep, he will have only himself to blame when the filesystem on the
+ * new card is corrupted and the system crashes.
+ */
+int usb_reset_persistent_device(struct usb_device *udev)
+{
+ struct usb_host_config *config;
+ int i;
+
+ /* Root hubs don't need to be (and can't be) reset */
+ if (!udev->parent)
+ goto mark_children;
+
+ /* The parent hub's port connect-change status might be set */
+ clear_port_feature(udev->parent, udev->portnum,
+ USB_PORT_FEAT_C_CONNECTION);
+
+ /* Can't reset it if it's marked as suspended */
+ usb_set_device_state(udev, USB_STATE_ADDRESS);
+ i = usb_reset_device(udev);
+ if (i < 0)
+ return i;
+
+ /* Let the interface drivers know the device has been reset.
+ * The call to the post_reset() method essentially replaces the
+ * call to resume().
+ */
+ config = udev->actconfig;
+ if (config) {
+ struct usb_interface *cintf;
+ struct usb_driver *drv;
+
+ for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+ cintf = config->interface[i];
+
+ /* We can't lock the interface because we already
+ * hold the pm_mutex. Luckily it doesn't matter
+ * since no other tasks will be running during a
+ * system resume.
+ */
+ if (device_is_registered(&cintf->dev) &&
+ cintf->dev.driver &&
+ !is_active(cintf)) {
+ drv = to_usb_driver(cintf->dev.driver);
+ if (drv->pre_reset && drv->post_reset) {
+ (drv->pre_reset)(cintf);
+ (drv->post_reset)(cintf);
+ mark_active(cintf);
+ }
+ }
+ }
+ }
+
+ /* Mark the child devices for reset */
+mark_children:
+ for (i = 0; i < udev->maxchild; ++i) {
+ if (udev->children[i])
+ udev->children[i]->do_persist = 1;
+ }
+
+ udev->do_persist = 0;
+ return 0;
+}
+
#endif /* CONFIG_PM */

static void choose_address(struct usb_device *udev)
@@ -2845,6 +2957,8 @@ static int config_descriptors_changed(st
unsigned len = 0;
struct usb_config_descriptor *buf;

+ /* FIXME: Compare the Vendor, Product, and Serial string descriptors */
+
for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
if (len < le16_to_cpu(udev->config[index].desc.wTotalLength))
len = le16_to_cpu(udev->config[index].desc.wTotalLength);
Index: mm/drivers/usb/core/usb.h
===================================================================
--- mm.orig/drivers/usb/core/usb.h
+++ mm/drivers/usb/core/usb.h
@@ -36,6 +36,7 @@ extern int usb_suspend_both(struct usb_d
extern int usb_resume_both(struct usb_device *udev);
extern int usb_port_suspend(struct usb_device *dev);
extern int usb_port_resume(struct usb_device *dev);
+extern int usb_reset_persistent_device(struct usb_device *udev);

#else

Index: mm/include/linux/usb.h
===================================================================
--- mm.orig/include/linux/usb.h
+++ mm/include/linux/usb.h
@@ -355,7 +355,8 @@ struct usb_device {
unsigned short bus_mA; /* Current available from the bus */
u8 portnum; /* Parent port number (origin 1) */

- int have_langid; /* whether string_langid is valid */
+ unsigned do_persist:1; /* Needs persistent-device reset */
+ unsigned have_langid:1; /* whether string_langid is valid */
int string_langid; /* language ID for strings */

/* static strings from the device */
Index: mm/Documentation/kernel-parameters.txt
===================================================================
--- mm.orig/Documentation/kernel-parameters.txt
+++ mm/Documentation/kernel-parameters.txt
@@ -1235,6 +1235,16 @@ and is between 256 and 4096 characters.
Format: { 0 | 1 }
See arch/parisc/kernel/pdc_chassis.c

+ persist= [USB] Enable USB device persistence across power loss
+ during system suspend.
+ Format: { 0 | 1 }
+ See also Documentation/usb/persist.txt
+ WARNING!! If a USB mass-storage device or its media
+ are changed while the system is suspended, the kernel
+ may not realize what has happened. If this option is
+ enabled then filesystem corruption and a system crash
+ may result.
+
pf. [PARIDE]
See Documentation/paride.txt.

Index: mm/Documentation/usb/persist.txt
===================================================================
--- /dev/null
+++ mm/Documentation/usb/persist.txt
@@ -0,0 +1,154 @@
+ USB device persistence during system suspend
+
+ Alan Stern <[email protected]>
+
+ September 2, 2006
+
+
+
+ What is the problem?
+
+According to the USB specification, when a USB bus is suspended the
+bus must continue to supply suspend current (around 1-5 mA). This
+is so that devices can maintain their internal state and hubs can
+detect connect-change events (devices being plugged in or unplugged).
+The technical term is "power session".
+
+If a USB device's power session is interrupted then the system is
+required to behave as though the device has been unplugged. It's a
+conservative approach; in the absence of suspend current the computer
+has no way to know what has actually happened. Perhaps the same
+device is still attached or perhaps it was removed and a different
+device plugged into the port. The system must assume the worst.
+
+By default, Linux behaves according to the spec. If a USB host
+controller loses power during a system suspend, then when the system
+wakes up all the devices attached to that controller are treated as
+though they had disconnected. This is always safe and it is the
+"officially correct" thing to do.
+
+For many sorts of devices this behavior doesn't matter in the least.
+If the kernel wants to believe that your USB keyboard was unplugged
+while the system was asleep and a new keyboard was plugged in when the
+system woke up, who cares? It'll still work the same when you type on
+it.
+
+Unfortunately problems _can_ arise, particularly with mass-storage
+devices. The effect is exactly the same as if the device really had
+been unplugged while the system was suspended. If you had a mounted
+filesystem on the device, you're out of luck -- everything in that
+filesystem is now inaccessible. This is especially annoying if your
+root filesystem was located on the device, since your system will
+instantly crash. :-(
+
+Loss of power isn't the only mechanism to worry about. Anything that
+interrupts a power session will have the same effect. For example,
+even though suspend current may have been maintained while the system
+was asleep, on many systems during the initial stages of wakeup the
+firmware (i.e., the BIOS) resets the motherboard's USB host
+controllers. Result: all the power sessions are destroyed and again
+it's as though you had unplugged all the USB devices. Yes, it's
+entirely the BIOS's fault, but that doesn't do _you_ any good unless
+you can convince the BIOS supplier to fix the problem (lots of luck!).
+
+On many systems the USB host controllers will get reset after a
+suspend-to-RAM. On almost all systems, no suspend current is
+available during suspend-to-disk (also known as swsusp). You can
+check the kernel log after resuming to see if either of these has
+happened; look for lines saying "root hub lost power or was reset".
+
+In practice, people are forced to unmount any filesystems on a USB
+device before suspending. If the root filesystem is on a USB device,
+the system can't be suspended at all. (All right, it _can_ be
+suspended -- but it will crash as soon as it wakes up, which isn't
+much better.)
+
+
+ What is the solution?
+
+Setting the "persist=y" module parameter for usbcore will cause the
+kernel to work around these issues. If usbcore is build into the
+main kernel instead of as a separate module, you can put
+"usbcore.persist=1" on the boot command line. You can also change the
+kernel's behavior on the fly using sysfs: Type
+
+ echo y >/sys/module/usbcore/parameters/persist
+
+to turn the option on, and replace the 'y' with an 'n' to turn it off.
+
+The "persist" option enables a mode in which the core USB device data
+structures are allowed to persist across a power-session disruption.
+It works like this. If the kernel sees that a USB host controller is
+not in the expected state during resume (i.e., if the controller was
+reset or otherwise had lost power) then it applies a persistence check
+to each of the USB devices below that controller. It doesn't try to
+resume the device; that can't work once the power session is gone.
+Instead it issues a USB port reset followed by a re-enumeration.
+(This is exactly the same thing that happens whenever a USB device is
+reset.) If the re-enumeration shows that the device now attached to
+that port has the same descriptors as before, including the Vendor and
+Product IDs, then the kernel continues to use the same device
+structure. In effect, the kernel treats the device as though it had
+merely been reset instead of unplugged.
+
+If no device is now attached to the port, or if the descriptors are
+different from what the kernel remembers, then the treatment is what
+you would expect. The kernel destroys the old device structure and
+behaves as though the old device had been unplugged and a new device
+plugged in, just as it would without the "persist=y" option.
+
+The end result is that the USB device remains available and usable.
+Mounts and memory mappings are unaffected, and the world is now a good
+and happy place.
+
+
+ Is this the best solution?
+
+Perhaps not. Arguably, keeping track of mounted filesystems and
+memory mappings across device disconnects should be handled by a
+centralized Logical Volume Manager. Such a solution would allow you
+to plug in a USB flash device, create a persistent volume associated
+with it, unplug the flash device, plug it back in later, and still
+have the same persistent volume associated with the device. As such
+it would be more far-reaching than usbcore's "persist=y" option.
+
+On the other hand, writing a persistent volume manager would be a big
+job and using it would require significant input from the user. This
+solution is much quicker and easier -- and it exists now, a giant
+point in its favor!
+
+Furthermore, the "persist" option applies to _all_ USB devices, not
+just mass-storage devices. It might turn out to be equally useful for
+other device types, such as network interfaces.
+
+
+ WARNING: Using "persist=y" can be dangerous!!
+
+When recovering an interrupted power session the kernel does its best
+to make sure the USB device hasn't been changed; that is, the same
+device is still plugged into the port as before. But the checks
+aren't guaranteed to be 100% accurate.
+
+If you replace one USB device with another of the same type (same
+manufacturer, same IDs, and so on) there's an excellent chance the
+kernel won't detect the change. Serial numbers and other strings are
+not compared. In many cases it wouldn't help if they were, because
+manufacturers frequently omit serial numbers entirely in their
+devices.
+
+Furthermore it's quite possible to leave a USB device exactly the
+same while changing its media. If you replace the flash memory card
+in a USB card reader while the system is asleep, the kernel will have
+no way to know you did it. The kernel will assume that nothing has
+happened and will continue to use the partition tables and memory
+mappings for the old card.
+
+If the kernel gets fooled in this way, it's almost certain to cause
+filesystem corruption and to crash your system. You'll have no one to
+blame but yourself.
+
+YOU HAVE BEEN WARNED! USE AT YOUR OWN RISK!
+
+That having been said, most of the time there shouldn't be any trouble
+at all. The "persist" feature can be extremely useful. Make the most
+of it.
Index: mm/drivers/usb/core/message.c
===================================================================
--- mm.orig/drivers/usb/core/message.c
+++ mm/drivers/usb/core/message.c
@@ -764,7 +764,7 @@ int usb_string(struct usb_device *dev, i
err = -EINVAL;
goto errout;
} else {
- dev->have_langid = -1;
+ dev->have_langid = 1;
dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
/* always use the first langid listed */
dev_dbg (&dev->dev, "default language 0x%04x\n",
Index: mm/Documentation/power/swsusp.txt
===================================================================
--- mm.orig/Documentation/power/swsusp.txt
+++ mm/Documentation/power/swsusp.txt
@@ -405,6 +405,9 @@ safest thing is to unmount all filesyste
Firewire, CompactFlash, MMC, external SATA, or even IDE hotplug bays)
before suspending; then remount them after resuming.

+There is a work-around for this problem. For more information, see
+Documentation/usb/persist.txt.
+
Q: I upgraded the kernel from 2.6.15 to 2.6.16. Both kernels were
compiled with the similar configuration files. Anyway I found that
suspend to disk (and resume) is much slower on 2.6.16 compared to


2006-09-05 16:09:47

by Randy Dunlap

[permalink] [raw]
Subject: Re: [linux-usb-devel] [RFC] USB device persistence across suspend-to-disk

On Tue, 5 Sep 2006 11:26:48 -0400 (EDT) Alan Stern wrote:

> Index: mm/Documentation/usb/persist.txt
> ===================================================================
> --- /dev/null
> +++ mm/Documentation/usb/persist.txt
> @@ -0,0 +1,154 @@
> +
> + What is the solution?
> +
> +Setting the "persist=y" module parameter for usbcore will cause the

persist=1 ??

> +kernel to work around these issues. If usbcore is build into the

s/build/built/

> +main kernel instead of as a separate module, you can put
> +"usbcore.persist=1" on the boot command line. You can also change the
> +kernel's behavior on the fly using sysfs: Type
> +
> + echo y >/sys/module/usbcore/parameters/persist

Does sysfs treat 'y' as '1'?
Anyway, it would be Good to be consistent.

> +to turn the option on, and replace the 'y' with an 'n' to turn it off.
> +
> +The "persist" option enables a mode in which the core USB device data
> +structures are allowed to persist across a power-session disruption.
> +It works like this. If the kernel sees that a USB host controller is
> +not in the expected state during resume (i.e., if the controller was
> +reset or otherwise had lost power) then it applies a persistence check
> +to each of the USB devices below that controller. It doesn't try to
> +resume the device; that can't work once the power session is gone.
> +Instead it issues a USB port reset followed by a re-enumeration.
> +(This is exactly the same thing that happens whenever a USB device is
> +reset.) If the re-enumeration shows that the device now attached to
> +that port has the same descriptors as before, including the Vendor and
> +Product IDs, then the kernel continues to use the same device
> +structure. In effect, the kernel treats the device as though it had
> +merely been reset instead of unplugged.

so does the USB device also retain its same USB address?


> Index: mm/drivers/usb/core/message.c
> ===================================================================
> --- mm.orig/drivers/usb/core/message.c
> +++ mm/drivers/usb/core/message.c
> @@ -764,7 +764,7 @@ int usb_string(struct usb_device *dev, i
> err = -EINVAL;
> goto errout;
> } else {
> - dev->have_langid = -1;
> + dev->have_langid = 1;
> dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
> /* always use the first langid listed */
> dev_dbg (&dev->dev, "default language 0x%04x\n",

Different patch (?).

---
~Randy

2006-09-05 16:59:49

by Alan Stern

[permalink] [raw]
Subject: Re: [linux-usb-devel] [RFC] USB device persistence across suspend-to-disk

On Tue, 5 Sep 2006, Randy.Dunlap wrote:

> > +Setting the "persist=y" module parameter for usbcore will cause the
>
> persist=1 ??

Either will work.

> > +kernel to work around these issues. If usbcore is build into the
>
> s/build/built/

Got it, thanks.

> > +main kernel instead of as a separate module, you can put
> > +"usbcore.persist=1" on the boot command line. You can also change the
> > +kernel's behavior on the fly using sysfs: Type
> > +
> > + echo y >/sys/module/usbcore/parameters/persist
>
> Does sysfs treat 'y' as '1'?
> Anyway, it would be Good to be consistent.

Yes; I'll change everything to 'y'.

> > +structure. In effect, the kernel treats the device as though it had
> > +merely been reset instead of unplugged.
>
> so does the USB device also retain its same USB address?

It does. It didn't seem worthwhile to mention that point, however.

> > - dev->have_langid = -1;
> > + dev->have_langid = 1;

> Different patch (?).

When this is submitted for inclusion, that change will be broken out into
a separate patch.

Alan Stern

2006-09-06 14:32:34

by Pavel Machek

[permalink] [raw]
Subject: Re: [linux-pm] [linux-usb-devel] [RFC] USB device persistence across suspend-to-disk

Hi!

> > > +main kernel instead of as a separate module, you can put
> > > +"usbcore.persist=1" on the boot command line. You can also change the
> > > +kernel's behavior on the fly using sysfs: Type
> > > +
> > > + echo y >/sys/module/usbcore/parameters/persist
> >
> > Does sysfs treat 'y' as '1'?
> > Anyway, it would be Good to be consistent.
>
> Yes; I'll change everything to 'y'.

Actually I'd prefer 0/1... that's what other parts of kernel use IIRC.

Otherwise it looks good to me.
Pavel
--
Thanks for all the (sleeping) penguins.

2006-09-06 15:10:31

by Greg KH

[permalink] [raw]
Subject: Re: [linux-pm] [linux-usb-devel] [RFC] USB device persistence across suspend-to-disk

On Wed, Sep 06, 2006 at 05:36:38AM +0000, Pavel Machek wrote:
> Hi!
>
> > > > +main kernel instead of as a separate module, you can put
> > > > +"usbcore.persist=1" on the boot command line. You can also change the
> > > > +kernel's behavior on the fly using sysfs: Type
> > > > +
> > > > + echo y >/sys/module/usbcore/parameters/persist
> > >
> > > Does sysfs treat 'y' as '1'?
> > > Anyway, it would be Good to be consistent.
> >
> > Yes; I'll change everything to 'y'.
>
> Actually I'd prefer 0/1... that's what other parts of kernel use IIRC.

The module paramater parsing code in the kernel will accept either, so
it really isn't a big deal.

thanks,

greg k-h

2006-09-19 18:11:31

by David Brownell

[permalink] [raw]
Subject: Re: [linux-usb-devel] [RFC] USB device persistence across suspend-to-disk

On Tuesday 05 September 2006 8:26 am, Alan Stern wrote:
> Lots of people have asked why USB mass-storage devices don't survive
> suspend-to-disk (swsusp). The answer is technical and not too
> interesting;

Not that technical: "Unlike most older busses, USB monitors connection
state during suspend states. It does this by using an active electical
circuit, which is broken by swsusp. USB detects that broken circuit
and thus reports the device's disconnection."

And why does that matter? For stateless devices like most mice and
keyboards, it doesn't. For a disk with a mounted file system, it does
since (a) there's no trustworthy way to tell if a disk that's there on
resume is the same one that was there on suspend, while (b) wrongly
assuming it's the same *WILL* quickly lead to data corruption.


> what matters is that people have been forced to unmount all
> filesystems on USB devices before doing suspend-to-disk. On some systems
> this is necessary even for suspend-to-RAM.

Most PCs maintain the USB connections during suspend-to-RAM; ISTR seeing
some hardware design guidelines from SFT saying to do it that way, so that
remote wakeup works. (E.g. on systems with only USB keyboards and mice,
you want wakeup to be natural.)

Some laptops (to eke out a bit more battery life) and embedded systems
(where STR is the deepest suspend state) do drop USB power during STR.
In my observation, hardly any PCs drop USB power like that.


> This patch adds the capability for USB devices to persist across such
> suspends. You can even suspend with your root fs on a USB flash device!
> (I think -- I haven't actually tried it. But it _should_ work.)
>
> It's all explained in Documentation/usb/persist.txt, added by the patch.
> The "persist" mode is turned off by default because it can be dangerous.
>
> The patch is based on 2.6.18-rc5-mm1. If people like it, I'll submit it
> to Greg KH for inclusion in the USB development tree.

A mechanism that's this dangerous deserves to be discouraged much more
strongly, if it's even merged. (I'm not a big fan of giving people
quite that much rope.) Having it depend on CONFIG_EMBEDDED as well as
CONFIG_EXPERIMENTAL (or better yet CONFIG_DANGEROUS) seems a better route...



> + * This facility is inherently dangerous. Although usb_reset_device()
> + * makes every effort to insure that the same device is present after the
> + * reset as before, it cannot provide a 100% guarantee. Furthermore it's
> + * quite possible for a device to remain unaltered but its media to be
> + * changed. If the user replaces a flash memory card while the system is
> + * asleep, he will have only himself to blame when the filesystem on the
> + * new card is corrupted and the system crashes.

I'll disagree on the "only himself to blame". If this mechanism is trivial
to kick in, it will be kicked in even by (or on behalf of) users that have
no reason to understand how dangerous this is.



> --- mm.orig/Documentation/kernel-parameters.txt
> +++ mm/Documentation/kernel-parameters.txt
> @@ -1235,6 +1235,16 @@ and is between 256 and 4096 characters.
> Format: { 0 | 1 }
> See arch/parisc/kernel/pdc_chassis.c
>
> + persist= [USB] Enable USB device persistence across power loss
> + during system suspend.
> + Format: { 0 | 1 }
> + See also Documentation/usb/persist.txt
> + WARNING!! If a USB mass-storage device or its media
> + are changed while the system is suspended, the kernel
> + may not realize what has happened. If this option is
> + enabled then filesystem corruption and a system crash
> + may result.
^^^
File system corruption ** WILL ** result...

> +
> pf. [PARIDE]
> See Documentation/paride.txt.
>
> Index: mm/Documentation/usb/persist.txt
> ===================================================================
> --- /dev/null
> +++ mm/Documentation/usb/persist.txt
> @@ -0,0 +1,154 @@
> + USB device persistence during system suspend
> +
> + Alan Stern <[email protected]>
> +
> + September 2, 2006
> +
> +
> +
> + What is the problem?
> +
> +According to the USB specification, when a USB bus is suspended the
> +bus must continue to supply suspend current (around 1-5 mA). This

No ... it's normally 500 uA, or for high powered devices up to 2500 uA.
The specification is a per-device average over 1 second, not per bus.

Some root hubs ("busses") support ten directly-connected devices, which
implies 25 mA would be needed on top of whatever the controller itself
is consuming. (You'd think it only needs some comparators and trivial
unclocked logic, but for some reason that seems uncommon.)



> +Loss of power isn't the only mechanism to worry about. Anything that
> +interrupts a power session will have the same effect. For example,
> +even though suspend current may have been maintained while the system
> +was asleep, on many systems during the initial stages of wakeup the
> +firmware (i.e., the BIOS) resets the motherboard's USB host
> +controllers.

That's called a BUG ... firmware shouldn't have reset any device
that the OS was managing. And in fact during true system suspend
states, I've not heard any reports of a BIOS resetting a USB host
controller. Do you have examples of these "many" systems??

(Any swsusp case is not one of these, since that's not a true
suspend mechanism. The hardware really did get reset, and BIOS
init can't be bypassed if it's going to let users do basics like
using a usb keyboard to choose which OS to run.)


> +On many systems the USB host controllers will get reset after a
^^^^
"some" ... by numbers, not very many since ISTR it's against
the hardware design guidelines from MSFT.

> +suspend-to-RAM. On almost all systems, no suspend current is
> +available during suspend-to-disk (also known as swsusp). You can
> +check the kernel log after resuming to see if either of these has
> +happened; look for lines saying "root hub lost power or was reset".
> +
> +In practice, people are forced to unmount any filesystems on a USB
> +device before suspending.

Just like for ** ALL ** other kinds of removable media, on any system
where userspace isn't facilitating data corruption.


> + WARNING: Using "persist=y" can be dangerous!!

You're understating this. I really think that such a mechanism would
need to be explicitly configured into the kernel. Distros that want
to cope with all the end-user "your kernel trashed my disk!!" service
calls could do so ... others would keep that option off, and save lots
of customer support and end user pain.



> +YOU HAVE BEEN WARNED! USE AT YOUR OWN RISK!

Actually, most end users would never read this information, so they
will never get such a warning.

- Dave

2006-09-19 20:25:21

by Alan Stern

[permalink] [raw]
Subject: Re: [linux-usb-devel] [RFC] USB device persistence across suspend-to-disk

On Tue, 19 Sep 2006, David Brownell wrote:

> > what matters is that people have been forced to unmount all
> > filesystems on USB devices before doing suspend-to-disk. On some systems
> > this is necessary even for suspend-to-RAM.
>
> Most PCs maintain the USB connections during suspend-to-RAM; ISTR seeing
> some hardware design guidelines from SFT saying to do it that way, so that
> remote wakeup works. (E.g. on systems with only USB keyboards and mice,
> you want wakeup to be natural.)

I also saw some guidelines from Microsoft, perhaps the very same ones.
They described the tasks the BIOS was supposed to undertake with regard to
USB controllers. However they didn't address the (IMO) really important
issues. And the vendor in question at the time interpreted the document
to mean exactly the opposite of what you're saying! (Of course, that's
not saying much...)

> A mechanism that's this dangerous deserves to be discouraged much more
> strongly, if it's even merged. (I'm not a big fan of giving people
> quite that much rope.) Having it depend on CONFIG_EMBEDDED as well as
> CONFIG_EXPERIMENTAL (or better yet CONFIG_DANGEROUS) seems a better route...

I'll be happy to change the enabling mechanism from a module parameter to
a config option -- or even both.

How dangerous would this turn out to be in the real world? It's hard to
say. No doubt there would be occasional instances of crashes, but just
how occasional is impossible to estimate. My guess is pretty infrequent.

> > + * changed. If the user replaces a flash memory card while the system is
> > + * asleep, he will have only himself to blame when the filesystem on the
> > + * new card is corrupted and the system crashes.
>
> I'll disagree on the "only himself to blame". If this mechanism is trivial
> to kick in, it will be kicked in even by (or on behalf of) users that have
> no reason to understand how dangerous this is.

This is the flip side of the argument. How easy should it be to invoke
this feature? Most users at the level of ignorance you're assuming don't
ever set up nontrivial module parameters. Even fewer users bother to
recompile kernels with config options different from those used by their
distribution.

> > + WARNING!! If a USB mass-storage device or its media
> > + are changed while the system is suspended, the kernel
> > + may not realize what has happened. If this option is
> > + enabled then filesystem corruption and a system crash
> > + may result.
> ^^^
> File system corruption ** WILL ** result...

Not necessarily. The example that provoked this was an embedded system
with its root fs on a non-accessible USB flash device.

> > +According to the USB specification, when a USB bus is suspended the
> > +bus must continue to supply suspend current (around 1-5 mA). This
>
> No ... it's normally 500 uA, or for high powered devices up to 2500 uA.
> The specification is a per-device average over 1 second, not per bus.

Okay, so I wrote that from memory and I was off by a factor of 2 (it does
say "around"!). Since I didn't say whether the figure was per-device or
per-bus, you can't criticize that aspect. :-)

> > +Loss of power isn't the only mechanism to worry about. Anything that
> > +interrupts a power session will have the same effect. For example,
> > +even though suspend current may have been maintained while the system
> > +was asleep, on many systems during the initial stages of wakeup the
> > +firmware (i.e., the BIOS) resets the motherboard's USB host
> > +controllers.
>
> That's called a BUG ... firmware shouldn't have reset any device
> that the OS was managing. And in fact during true system suspend
> states, I've not heard any reports of a BIOS resetting a USB host
> controller. Do you have examples of these "many" systems??

I don't have statistics. But I have run across quite a few examples of
systems where this happens. Lots of bug reports, plus at least one of my
own machines. (I think -- haven't checked it recently.)

> > +On many systems the USB host controllers will get reset after a
> ^^^^
> "some" ... by numbers, not very many since ISTR it's against
> the hardware design guidelines from MSFT.

I wouldn't go so far as to say that, unless you can actually produce the
relevant quite from the guidelines document. In any case, even if the
percentage of machines where this happens is relatively low it can still
amount to a lot of systems.

> > +suspend-to-RAM. On almost all systems, no suspend current is
> > +available during suspend-to-disk (also known as swsusp). You can
> > +check the kernel log after resuming to see if either of these has
> > +happened; look for lines saying "root hub lost power or was reset".
> > +
> > +In practice, people are forced to unmount any filesystems on a USB
> > +device before suspending.
>
> Just like for ** ALL ** other kinds of removable media, on any system
> where userspace isn't facilitating data corruption.

What about floppy disks?

> > + WARNING: Using "persist=y" can be dangerous!!
>
> You're understating this. I really think that such a mechanism would
> need to be explicitly configured into the kernel. Distros that want
> to cope with all the end-user "your kernel trashed my disk!!" service
> calls could do so ... others would keep that option off, and save lots
> of customer support and end user pain.

Would you prefer just a config option or a config option plus a module
parameter (both must be set to enable the feature)?

Alan Stern