diff options
-rw-r--r-- | Documentation/usb/persist.txt | 35 | ||||
-rw-r--r-- | drivers/usb/core/Kconfig | 25 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 27 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 12 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 22 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 11 | ||||
-rw-r--r-- | include/linux/usb.h | 2 |
7 files changed, 53 insertions, 81 deletions
diff --git a/Documentation/usb/persist.txt b/Documentation/usb/persist.txt index df54d64..bea58db 100644 --- a/Documentation/usb/persist.txt +++ b/Documentation/usb/persist.txt @@ -2,7 +2,7 @@ Alan Stern <stern@rowland.harvard.edu> - September 2, 2006 (Updated May 29, 2007) + September 2, 2006 (Updated February 25, 2008) What is the problem? @@ -65,9 +65,10 @@ much better.) What is the solution? -Setting CONFIG_USB_PERSIST will cause the kernel to work around these -issues. It enables a mode in which the core USB device data -structures are allowed to persist across a power-session disruption. +The kernel includes a feature called USB-persist. It tries to work +around these issues by allowing the core USB device data structures 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 @@ -80,28 +81,30 @@ 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. +unplugged. The same thing happens if the host controller is in the +expected state but a USB device was unplugged and then replugged. 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 CONFIG_USB_PERSIST option. +plugged in. The end result is that the USB device remains available and usable. Filesystem mounts and memory mappings are unaffected, and the world is now a good and happy place. -Note that even when CONFIG_USB_PERSIST is set, the "persist" feature -will be applied only to those devices for which it is enabled. You -can enable the feature by doing (as root): +Note that the "USB-persist" feature will be applied only to those +devices for which it is enabled. You can enable the feature by doing +(as root): echo 1 >/sys/bus/usb/devices/.../power/persist where the "..." should be filled in the with the device's ID. Disable the feature by writing 0 instead of 1. For hubs the feature is -automatically and permanently enabled, so you only have to worry about -setting it for devices where it really matters. +automatically and permanently enabled and the power/persist file +doesn't even exist, so you only have to worry about setting it for +devices where it really matters. Is this the best solution? @@ -112,19 +115,19 @@ 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 CONFIG_USB_PERSIST. +it would be more far-reaching than USB-persist. 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 USB_PERSIST option applies to _all_ USB devices, not +Furthermore, the USB-persist feature 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 CONFIG_USB_PERSIST can be dangerous!! + WARNING: USB-persist 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 @@ -152,5 +155,5 @@ 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. +at all. The USB-persist feature can be extremely useful. Make the +most of it. diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index a2b0aa4..c15621d 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -102,31 +102,6 @@ config USB_SUSPEND If you are unsure about this, say N here. -config USB_PERSIST - bool "USB device persistence during system suspend (DANGEROUS)" - depends on USB && PM && EXPERIMENTAL - default n - help - - If you say Y here and enable the "power/persist" attribute - for a USB device, the device's data structures will remain - persistent across system suspend, even if the USB bus loses - power. (This includes hibernation, also known as swsusp or - suspend-to-disk.) The devices will reappear as if by magic - when the system wakes up, with no need to unmount USB - filesystems, rmmod host-controller drivers, or do anything - else. - - WARNING: This option can be dangerous! - - If a USB device is replaced by another of the same type while - the system is asleep, there's a good chance the kernel won't - detect the change. Likewise if the media in a USB storage - device is replaced. When this happens it's almost certain to - cause data corruption and maybe even crash your system. - - If you are unsure, say N here. - config USB_OTG bool depends on USB && EXPERIMENTAL diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index df68e25..6dc5899 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -30,12 +30,6 @@ #include "hcd.h" #include "hub.h" -#ifdef CONFIG_USB_PERSIST -#define USB_PERSIST 1 -#else -#define USB_PERSIST 0 -#endif - /* if we are in debug mode, always announce new devices */ #ifdef DEBUG #ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES @@ -695,7 +689,7 @@ static void hub_restart(struct usb_hub *hub, int type) * turn off the various status changes to prevent * khubd from disconnecting it later. */ - if (USB_PERSIST && udev->persist_enabled && status == 0 && + if (udev->persist_enabled && status == 0 && !(portstatus & USB_PORT_STAT_ENABLE)) { if (portchange & USB_PORT_STAT_C_ENABLE) clear_port_feature(hub->hdev, port1, @@ -1923,9 +1917,8 @@ static int finish_port_resume(struct usb_device *udev) * the host and the device is the same as it was when the device * suspended. * - * If CONFIG_USB_PERSIST and @udev->reset_resume are both set then this - * routine won't check that the port is still enabled. Furthermore, - * if @udev->reset_resume is set then finish_port_resume() above will + * If @udev->reset_resume is set then this routine won't check that the + * port is still enabled. Furthermore, finish_port_resume() above will * reset @udev. The end result is that a broken power session can be * recovered and @udev will appear to persist across a loss of VBUS power. * @@ -1937,8 +1930,8 @@ static int finish_port_resume(struct usb_device *udev) * to it will be lost. Using the USB_PERSIST facility, the device can be * made to appear as if it had not disconnected. * - * This facility is inherently dangerous. Although usb_reset_device() - * makes every effort to insure that the same device is present after the + * This facility can be 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 @@ -1983,7 +1976,7 @@ int usb_port_resume(struct usb_device *udev) status = hub_port_status(hub, port1, &portstatus, &portchange); SuspendCleared: - if (USB_PERSIST && udev->reset_resume) + if (udev->reset_resume) want_flags = USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION; else @@ -2113,10 +2106,10 @@ static int hub_reset_resume(struct usb_interface *intf) * * The USB host controller driver calls this function when its root hub * is resumed and Vbus power has been interrupted or the controller - * has been reset. The routine marks @rhdev as having lost power. When - * the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST - * is enabled then it will carry out power-session recovery, otherwise - * it will disconnect all the child devices. + * has been reset. The routine marks @rhdev as having lost power. + * When the hub driver is resumed it will take notice and carry out + * power-session recovery for all the "USB-PERSIST"-enabled child devices; + * the others will be disconnected. */ void usb_root_hub_lost_power(struct usb_device *rhdev) { diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index dfc5418e..f384edf 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -97,4 +97,16 @@ void usb_detect_quirks(struct usb_device *udev) if (udev->descriptor.bDeviceClass != USB_CLASS_HUB) udev->autosuspend_disabled = 1; #endif + +#ifdef CONFIG_PM + /* Hubs are automatically enabled for USB-PERSIST */ + if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) + udev->persist_enabled = 1; +#else + /* In the absense of PM, we can safely enable USB-PERSIST + * for all devices. It will affect things like hub resets + * and EMF-related port disables. + */ + udev->persist_enabled = 1; +#endif /* CONFIG_PM */ } diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index a37ccbd..5b20a60 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -180,11 +180,9 @@ show_urbnum(struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL); -#if defined(CONFIG_USB_PERSIST) || defined(CONFIG_USB_SUSPEND) -static const char power_group[] = "power"; -#endif +#ifdef CONFIG_PM -#ifdef CONFIG_USB_PERSIST +static const char power_group[] = "power"; static ssize_t show_persist(struct device *dev, struct device_attribute *attr, char *buf) @@ -222,12 +220,13 @@ static int add_persist_attributes(struct device *dev) if (is_usb_device(dev)) { struct usb_device *udev = to_usb_device(dev); - /* Hubs are automatically enabled for USB_PERSIST */ - if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) - udev->persist_enabled = 1; - rc = sysfs_add_file_to_group(&dev->kobj, - &dev_attr_persist.attr, - power_group); + /* Hubs are automatically enabled for USB_PERSIST, + * no point in creating the attribute file. + */ + if (udev->descriptor.bDeviceClass != USB_CLASS_HUB) + rc = sysfs_add_file_to_group(&dev->kobj, + &dev_attr_persist.attr, + power_group); } return rc; } @@ -238,13 +237,12 @@ static void remove_persist_attributes(struct device *dev) &dev_attr_persist.attr, power_group); } - #else #define add_persist_attributes(dev) 0 #define remove_persist_attributes(dev) do {} while (0) -#endif /* CONFIG_USB_PERSIST */ +#endif /* CONFIG_PM */ #ifdef CONFIG_USB_SUSPEND diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 8d513a1..fea9e47 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -28,7 +28,7 @@ /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_USB_PERSIST +#ifdef CONFIG_PM static int ehci_hub_control( struct usb_hcd *hcd, @@ -104,15 +104,6 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) ehci->owned_ports = 0; } -#else /* CONFIG_USB_PERSIST */ - -static inline void ehci_handover_companion_ports(struct ehci_hcd *ehci) -{ } - -#endif - -#ifdef CONFIG_PM - static int ehci_bus_suspend (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); diff --git a/include/linux/usb.h b/include/linux/usb.h index 583e048..7e31cac 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -387,6 +387,7 @@ struct usb_device { unsigned can_submit:1; /* URBs may be submitted */ unsigned discon_suspended:1; /* Disconnected while suspended */ + unsigned persist_enabled:1; /* USB_PERSIST enabled for this dev */ unsigned have_langid:1; /* whether string_langid is valid */ unsigned authorized:1; /* Policy has said we can use it */ unsigned wusb:1; /* Device is Wireless USB */ @@ -433,7 +434,6 @@ struct usb_device { unsigned auto_pm:1; /* autosuspend/resume in progress */ unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ unsigned reset_resume:1; /* needs reset instead of resume */ - unsigned persist_enabled:1; /* USB_PERSIST enabled for this dev */ unsigned autosuspend_disabled:1; /* autosuspend and autoresume */ unsigned autoresume_disabled:1; /* disabled by the user */ unsigned skip_sys_resume:1; /* skip the next system resume */ |