aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2013-07-11 14:58:04 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-04 04:50:50 -0400
commit5deba4cf6db86cc902526d8b08fc5d56e9d43994 (patch)
tree4243ab0c250caff67d2a217b0ceb7f8997113e97
parent889ba7bc28e5db5da40f648c91678dcc2d2837fd (diff)
USB: global suspend and remote wakeup don't mix
commit e583d9db9960cf40e0bc8afee4946baa9d71596e upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/core/hub.c39
-rw-r--r--drivers/usb/core/hub.h3
2 files changed, 34 insertions, 8 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 868ad745a500..b93fc88c4823 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2855,6 +2855,15 @@ static int usb_disable_function_remotewakeup(struct usb_device *udev)
2855 USB_CTRL_SET_TIMEOUT); 2855 USB_CTRL_SET_TIMEOUT);
2856} 2856}
2857 2857
2858/* Count of wakeup-enabled devices at or below udev */
2859static unsigned wakeup_enabled_descendants(struct usb_device *udev)
2860{
2861 struct usb_hub *hub = usb_hub_to_struct_hub(udev);
2862
2863 return udev->do_remote_wakeup +
2864 (hub ? hub->wakeup_enabled_descendants : 0);
2865}
2866
2858/* 2867/*
2859 * usb_port_suspend - suspend a usb device's upstream port 2868 * usb_port_suspend - suspend a usb device's upstream port
2860 * @udev: device that's no longer in active use, not a root hub 2869 * @udev: device that's no longer in active use, not a root hub
@@ -2895,8 +2904,8 @@ static int usb_disable_function_remotewakeup(struct usb_device *udev)
2895 * Linux (2.6) currently has NO mechanisms to initiate that: no khubd 2904 * Linux (2.6) currently has NO mechanisms to initiate that: no khubd
2896 * timer, no SRP, no requests through sysfs. 2905 * timer, no SRP, no requests through sysfs.
2897 * 2906 *
2898 * If Runtime PM isn't enabled or used, non-SuperSpeed devices really get 2907 * If Runtime PM isn't enabled or used, non-SuperSpeed devices may not get
2899 * suspended only when their bus goes into global suspend (i.e., the root 2908 * suspended until their bus goes into global suspend (i.e., the root
2900 * hub is suspended). Nevertheless, we change @udev->state to 2909 * hub is suspended). Nevertheless, we change @udev->state to
2901 * USB_STATE_SUSPENDED as this is the device's "logical" state. The actual 2910 * USB_STATE_SUSPENDED as this is the device's "logical" state. The actual
2902 * upstream port setting is stored in @udev->port_is_suspended. 2911 * upstream port setting is stored in @udev->port_is_suspended.
@@ -2967,15 +2976,21 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
2967 /* see 7.1.7.6 */ 2976 /* see 7.1.7.6 */
2968 if (hub_is_superspeed(hub->hdev)) 2977 if (hub_is_superspeed(hub->hdev))
2969 status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3); 2978 status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3);
2970 else if (PMSG_IS_AUTO(msg)) 2979
2971 status = set_port_feature(hub->hdev, port1,
2972 USB_PORT_FEAT_SUSPEND);
2973 /* 2980 /*
2974 * For system suspend, we do not need to enable the suspend feature 2981 * For system suspend, we do not need to enable the suspend feature
2975 * on individual USB-2 ports. The devices will automatically go 2982 * on individual USB-2 ports. The devices will automatically go
2976 * into suspend a few ms after the root hub stops sending packets. 2983 * into suspend a few ms after the root hub stops sending packets.
2977 * The USB 2.0 spec calls this "global suspend". 2984 * The USB 2.0 spec calls this "global suspend".
2985 *
2986 * However, many USB hubs have a bug: They don't relay wakeup requests
2987 * from a downstream port if the port's suspend feature isn't on.
2988 * Therefore we will turn on the suspend feature if udev or any of its
2989 * descendants is enabled for remote wakeup.
2978 */ 2990 */
2991 else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0)
2992 status = set_port_feature(hub->hdev, port1,
2993 USB_PORT_FEAT_SUSPEND);
2979 else { 2994 else {
2980 really_suspend = false; 2995 really_suspend = false;
2981 status = 0; 2996 status = 0;
@@ -3010,15 +3025,16 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
3010 if (!PMSG_IS_AUTO(msg)) 3025 if (!PMSG_IS_AUTO(msg))
3011 status = 0; 3026 status = 0;
3012 } else { 3027 } else {
3013 /* device has up to 10 msec to fully suspend */
3014 dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n", 3028 dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n",
3015 (PMSG_IS_AUTO(msg) ? "auto-" : ""), 3029 (PMSG_IS_AUTO(msg) ? "auto-" : ""),
3016 udev->do_remote_wakeup); 3030 udev->do_remote_wakeup);
3017 usb_set_device_state(udev, USB_STATE_SUSPENDED);
3018 if (really_suspend) { 3031 if (really_suspend) {
3019 udev->port_is_suspended = 1; 3032 udev->port_is_suspended = 1;
3033
3034 /* device has up to 10 msec to fully suspend */
3020 msleep(10); 3035 msleep(10);
3021 } 3036 }
3037 usb_set_device_state(udev, USB_STATE_SUSPENDED);
3022 } 3038 }
3023 3039
3024 /* 3040 /*
@@ -3300,7 +3316,11 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
3300 unsigned port1; 3316 unsigned port1;
3301 int status; 3317 int status;
3302 3318
3303 /* Warn if children aren't already suspended */ 3319 /*
3320 * Warn if children aren't already suspended.
3321 * Also, add up the number of wakeup-enabled descendants.
3322 */
3323 hub->wakeup_enabled_descendants = 0;
3304 for (port1 = 1; port1 <= hdev->maxchild; port1++) { 3324 for (port1 = 1; port1 <= hdev->maxchild; port1++) {
3305 struct usb_device *udev; 3325 struct usb_device *udev;
3306 3326
@@ -3310,6 +3330,9 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
3310 if (PMSG_IS_AUTO(msg)) 3330 if (PMSG_IS_AUTO(msg))
3311 return -EBUSY; 3331 return -EBUSY;
3312 } 3332 }
3333 if (udev)
3334 hub->wakeup_enabled_descendants +=
3335 wakeup_enabled_descendants(udev);
3313 } 3336 }
3314 3337
3315 if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) { 3338 if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) {
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 80ab9ee07017..f608b39beaf0 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -59,6 +59,9 @@ struct usb_hub {
59 struct usb_tt tt; /* Transaction Translator */ 59 struct usb_tt tt; /* Transaction Translator */
60 60
61 unsigned mA_per_port; /* current for each child */ 61 unsigned mA_per_port; /* current for each child */
62#ifdef CONFIG_PM
63 unsigned wakeup_enabled_descendants;
64#endif
62 65
63 unsigned limited_power:1; 66 unsigned limited_power:1;
64 unsigned quiescing:1; 67 unsigned quiescing:1;