kernel: revert an upstream linux-stable commit that is causing usb regressions on...
authorFelix Fietkau <nbd@openwrt.org>
Wed, 9 Jul 2014 10:11:23 +0000 (10:11 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Wed, 9 Jul 2014 10:11:23 +0000 (10:11 +0000)
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
SVN-Revision: 41554

target/linux/generic/patches-3.10/140-revert_usb_unbind_interfaces.patch [new file with mode: 0644]

diff --git a/target/linux/generic/patches-3.10/140-revert_usb_unbind_interfaces.patch b/target/linux/generic/patches-3.10/140-revert_usb_unbind_interfaces.patch
new file mode 100644 (file)
index 0000000..aa1ccb3
--- /dev/null
@@ -0,0 +1,206 @@
+commit 032f44791457d9aa50c6a194a2d475f07e311afd
+Author: Felix Fietkau <nbd@openwrt.org>
+Date:   Wed Jul 9 12:08:23 2014 +0200
+
+    Revert "USB: unbind all interfaces before rebinding any"
+    
+    This reverts commit 59f0103d74e4a32cbaa054d5011ea287fcfb83e4.
+    The commit has been found to cause USB regressions on AR933x and
+    BCM4705.
+
+--- a/drivers/usb/core/driver.c
++++ b/drivers/usb/core/driver.c
+@@ -953,7 +953,8 @@ EXPORT_SYMBOL_GPL(usb_deregister);
+  * it doesn't support pre_reset/post_reset/reset_resume or
+  * because it doesn't support suspend/resume.
+  *
+- * The caller must hold @intf's device's lock, but not @intf's lock.
++ * The caller must hold @intf's device's lock, but not its pm_mutex
++ * and not @intf->dev.sem.
+  */
+ void usb_forced_unbind_intf(struct usb_interface *intf)
+ {
+@@ -966,37 +967,16 @@ void usb_forced_unbind_intf(struct usb_i
+       intf->needs_binding = 1;
+ }
+-/*
+- * Unbind drivers for @udev's marked interfaces.  These interfaces have
+- * the needs_binding flag set, for example by usb_resume_interface().
+- *
+- * The caller must hold @udev's device lock.
+- */
+-static void unbind_marked_interfaces(struct usb_device *udev)
+-{
+-      struct usb_host_config  *config;
+-      int                     i;
+-      struct usb_interface    *intf;
+-
+-      config = udev->actconfig;
+-      if (config) {
+-              for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+-                      intf = config->interface[i];
+-                      if (intf->dev.driver && intf->needs_binding)
+-                              usb_forced_unbind_intf(intf);
+-              }
+-      }
+-}
+-
+ /* Delayed forced unbinding of a USB interface driver and scan
+  * for rebinding.
+  *
+- * The caller must hold @intf's device's lock, but not @intf's lock.
++ * The caller must hold @intf's device's lock, but not its pm_mutex
++ * and not @intf->dev.sem.
+  *
+  * Note: Rebinds will be skipped if a system sleep transition is in
+  * progress and the PM "complete" callback hasn't occurred yet.
+  */
+-static void usb_rebind_intf(struct usb_interface *intf)
++void usb_rebind_intf(struct usb_interface *intf)
+ {
+       int rc;
+@@ -1013,66 +993,68 @@ static void usb_rebind_intf(struct usb_i
+       }
+ }
+-/*
+- * Rebind drivers to @udev's marked interfaces.  These interfaces have
+- * the needs_binding flag set.
++#ifdef CONFIG_PM
++
++/* Unbind drivers for @udev's interfaces that don't support suspend/resume
++ * There is no check for reset_resume here because it can be determined
++ * only during resume whether reset_resume is needed.
+  *
+  * The caller must hold @udev's device lock.
+  */
+-static void rebind_marked_interfaces(struct usb_device *udev)
++static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
+ {
+       struct usb_host_config  *config;
+       int                     i;
+       struct usb_interface    *intf;
++      struct usb_driver       *drv;
+       config = udev->actconfig;
+       if (config) {
+               for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+                       intf = config->interface[i];
+-                      if (intf->needs_binding)
+-                              usb_rebind_intf(intf);
++
++                      if (intf->dev.driver) {
++                              drv = to_usb_driver(intf->dev.driver);
++                              if (!drv->suspend || !drv->resume)
++                                      usb_forced_unbind_intf(intf);
++                      }
+               }
+       }
+ }
+-/*
+- * Unbind all of @udev's marked interfaces and then rebind all of them.
+- * This ordering is necessary because some drivers claim several interfaces
+- * when they are first probed.
++/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
++ * These interfaces have the needs_binding flag set by usb_resume_interface().
+  *
+  * The caller must hold @udev's device lock.
+  */
+-void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev)
++static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
+ {
+-      unbind_marked_interfaces(udev);
+-      rebind_marked_interfaces(udev);
+-}
++      struct usb_host_config  *config;
++      int                     i;
++      struct usb_interface    *intf;
+-#ifdef CONFIG_PM
++      config = udev->actconfig;
++      if (config) {
++              for (i = 0; i < config->desc.bNumInterfaces; ++i) {
++                      intf = config->interface[i];
++                      if (intf->dev.driver && intf->needs_binding)
++                              usb_forced_unbind_intf(intf);
++              }
++      }
++}
+-/* Unbind drivers for @udev's interfaces that don't support suspend/resume
+- * There is no check for reset_resume here because it can be determined
+- * only during resume whether reset_resume is needed.
+- *
+- * The caller must hold @udev's device lock.
+- */
+-static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
++static void do_rebind_interfaces(struct usb_device *udev)
+ {
+       struct usb_host_config  *config;
+       int                     i;
+       struct usb_interface    *intf;
+-      struct usb_driver       *drv;
+       config = udev->actconfig;
+       if (config) {
+               for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+                       intf = config->interface[i];
+-
+-                      if (intf->dev.driver) {
+-                              drv = to_usb_driver(intf->dev.driver);
+-                              if (!drv->suspend || !drv->resume)
+-                                      usb_forced_unbind_intf(intf);
+-                      }
++                      if (intf->needs_binding)
++                              usb_rebind_intf(intf);
+               }
+       }
+ }
+@@ -1397,7 +1379,7 @@ int usb_resume_complete(struct device *d
+        * whose needs_binding flag is set
+        */
+       if (udev->state != USB_STATE_NOTATTACHED)
+-              rebind_marked_interfaces(udev);
++              do_rebind_interfaces(udev);
+       return 0;
+ }
+@@ -1419,7 +1401,7 @@ int usb_resume(struct device *dev, pm_me
+               pm_runtime_disable(dev);
+               pm_runtime_set_active(dev);
+               pm_runtime_enable(dev);
+-              unbind_marked_interfaces(udev);
++              unbind_no_reset_resume_drivers_interfaces(udev);
+       }
+       /* Avoid PM error messages for devices disconnected while suspended
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -5263,11 +5263,10 @@ int usb_reset_device(struct usb_device *
+                               else if (cintf->condition ==
+                                               USB_INTERFACE_BOUND)
+                                       rebind = 1;
+-                              if (rebind)
+-                                      cintf->needs_binding = 1;
+                       }
++                      if (ret == 0 && rebind)
++                              usb_rebind_intf(cintf);
+               }
+-              usb_unbind_and_rebind_marked_interfaces(udev);
+       }
+       usb_autosuspend_device(udev);
+--- a/drivers/usb/core/usb.h
++++ b/drivers/usb/core/usb.h
+@@ -55,7 +55,7 @@ extern int usb_match_one_id_intf(struct 
+ extern int usb_match_device(struct usb_device *dev,
+                           const struct usb_device_id *id);
+ extern void usb_forced_unbind_intf(struct usb_interface *intf);
+-extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev);
++extern void usb_rebind_intf(struct usb_interface *intf);
+ extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port,
+               struct dev_state *owner);