Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: global suspend and remote wakeup don't mix

The hub driver was recently changed to use "global" suspend for system
suspend transitions on non-SuperSpeed buses. This means that we don't
suspend devices individually by setting the suspend feature on the
upstream hub port; instead devices all go into suspend automatically
when the root hub stops transmitting packets. The idea was to save
time and to avoid certain kinds of wakeup races.

Now it turns out that many hubs are buggy; they don't relay wakeup
requests from a downstream port to their upstream port if the
downstream port's suspend feature is not set (depending on the speed
of the downstream port, whether or not the hub is enabled for remote
wakeup, and possibly other factors).

We can't have hubs dropping wakeup requests. Therefore this patch
goes partway back to the old policy: It sets the suspend feature for a
port if the device attached to that port or any of its descendants is
enabled for wakeup. People will still be able to benefit from the
time savings if they don't care about wakeup and leave it disabled on
all their devices.

In order to accomplish this, the patch adds a new field to the usb_hub
structure: wakeup_enabled_descendants is a count of how many devices
below a suspended hub are enabled for remote wakeup. A corresponding
new subroutine determines the number of wakeup-enabled devices at or
below an arbitrary suspended USB device.

This should be applied to the 3.10 stable kernel.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Reported-and-tested-by: Toralf Förster <toralf.foerster@gmx.de>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
e583d9db 36ff66db

+34 -8
+31 -8
drivers/usb/core/hub.c
··· 2848 2848 USB_CTRL_SET_TIMEOUT); 2849 2849 } 2850 2850 2851 + /* Count of wakeup-enabled devices at or below udev */ 2852 + static unsigned wakeup_enabled_descendants(struct usb_device *udev) 2853 + { 2854 + struct usb_hub *hub = usb_hub_to_struct_hub(udev); 2855 + 2856 + return udev->do_remote_wakeup + 2857 + (hub ? hub->wakeup_enabled_descendants : 0); 2858 + } 2859 + 2851 2860 /* 2852 2861 * usb_port_suspend - suspend a usb device's upstream port 2853 2862 * @udev: device that's no longer in active use, not a root hub ··· 2897 2888 * Linux (2.6) currently has NO mechanisms to initiate that: no khubd 2898 2889 * timer, no SRP, no requests through sysfs. 2899 2890 * 2900 - * If Runtime PM isn't enabled or used, non-SuperSpeed devices really get 2901 - * suspended only when their bus goes into global suspend (i.e., the root 2891 + * If Runtime PM isn't enabled or used, non-SuperSpeed devices may not get 2892 + * suspended until their bus goes into global suspend (i.e., the root 2902 2893 * hub is suspended). Nevertheless, we change @udev->state to 2903 2894 * USB_STATE_SUSPENDED as this is the device's "logical" state. The actual 2904 2895 * upstream port setting is stored in @udev->port_is_suspended. ··· 2969 2960 /* see 7.1.7.6 */ 2970 2961 if (hub_is_superspeed(hub->hdev)) 2971 2962 status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3); 2972 - else if (PMSG_IS_AUTO(msg)) 2973 - status = set_port_feature(hub->hdev, port1, 2974 - USB_PORT_FEAT_SUSPEND); 2963 + 2975 2964 /* 2976 2965 * For system suspend, we do not need to enable the suspend feature 2977 2966 * on individual USB-2 ports. The devices will automatically go 2978 2967 * into suspend a few ms after the root hub stops sending packets. 2979 2968 * The USB 2.0 spec calls this "global suspend". 2969 + * 2970 + * However, many USB hubs have a bug: They don't relay wakeup requests 2971 + * from a downstream port if the port's suspend feature isn't on. 2972 + * Therefore we will turn on the suspend feature if udev or any of its 2973 + * descendants is enabled for remote wakeup. 2980 2974 */ 2975 + else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0) 2976 + status = set_port_feature(hub->hdev, port1, 2977 + USB_PORT_FEAT_SUSPEND); 2981 2978 else { 2982 2979 really_suspend = false; 2983 2980 status = 0; ··· 3018 3003 if (!PMSG_IS_AUTO(msg)) 3019 3004 status = 0; 3020 3005 } else { 3021 - /* device has up to 10 msec to fully suspend */ 3022 3006 dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n", 3023 3007 (PMSG_IS_AUTO(msg) ? "auto-" : ""), 3024 3008 udev->do_remote_wakeup); 3025 - usb_set_device_state(udev, USB_STATE_SUSPENDED); 3026 3009 if (really_suspend) { 3027 3010 udev->port_is_suspended = 1; 3011 + 3012 + /* device has up to 10 msec to fully suspend */ 3028 3013 msleep(10); 3029 3014 } 3015 + usb_set_device_state(udev, USB_STATE_SUSPENDED); 3030 3016 } 3031 3017 3032 3018 /* ··· 3309 3293 unsigned port1; 3310 3294 int status; 3311 3295 3312 - /* Warn if children aren't already suspended */ 3296 + /* 3297 + * Warn if children aren't already suspended. 3298 + * Also, add up the number of wakeup-enabled descendants. 3299 + */ 3300 + hub->wakeup_enabled_descendants = 0; 3313 3301 for (port1 = 1; port1 <= hdev->maxchild; port1++) { 3314 3302 struct usb_device *udev; 3315 3303 ··· 3323 3303 if (PMSG_IS_AUTO(msg)) 3324 3304 return -EBUSY; 3325 3305 } 3306 + if (udev) 3307 + hub->wakeup_enabled_descendants += 3308 + wakeup_enabled_descendants(udev); 3326 3309 } 3327 3310 3328 3311 if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) {
+3
drivers/usb/core/hub.h
··· 59 59 struct usb_tt tt; /* Transaction Translator */ 60 60 61 61 unsigned mA_per_port; /* current for each child */ 62 + #ifdef CONFIG_PM 63 + unsigned wakeup_enabled_descendants; 64 + #endif 62 65 63 66 unsigned limited_power:1; 64 67 unsigned quiescing:1;