]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Mar 2013 22:06:55 +0000 (15:06 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Mar 2013 22:06:55 +0000 (15:06 -0700)
added patches:
usb-don-t-use-ehci-port-sempahore-for-usb-3.0-hubs.patch
usb-prepare-for-refactoring-by-adding-extra-udev-checks.patch
usb-rip-out-recursive-call-on-warm-port-reset.patch

queue-3.4/series
queue-3.4/usb-don-t-use-ehci-port-sempahore-for-usb-3.0-hubs.patch [new file with mode: 0644]
queue-3.4/usb-prepare-for-refactoring-by-adding-extra-udev-checks.patch [new file with mode: 0644]
queue-3.4/usb-rip-out-recursive-call-on-warm-port-reset.patch [new file with mode: 0644]

index a564edc944c72f4bd4e22f7d7030d9cfba35990f..9743dc8666bec79c9d3be42fb41e7b7d1fce2c7a 100644 (file)
@@ -35,3 +35,6 @@ fix-memory-leak-in-cpufreq-stats.patch
 ftrace-update-the-kconfig-for-dynamic_ftrace.patch
 decnet-fix-disappearing-sysctl-entries.patch
 dmi_scan-fix-missing-check-for-_dmi_-signature-in-smbios_present.patch
+usb-don-t-use-ehci-port-sempahore-for-usb-3.0-hubs.patch
+usb-prepare-for-refactoring-by-adding-extra-udev-checks.patch
+usb-rip-out-recursive-call-on-warm-port-reset.patch
diff --git a/queue-3.4/usb-don-t-use-ehci-port-sempahore-for-usb-3.0-hubs.patch b/queue-3.4/usb-don-t-use-ehci-port-sempahore-for-usb-3.0-hubs.patch
new file mode 100644 (file)
index 0000000..42b931a
--- /dev/null
@@ -0,0 +1,66 @@
+From sarah.a.sharp@linux.intel.com  Tue Mar 12 15:00:01 2013
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Thu, 7 Mar 2013 16:24:19 -0800
+Subject: USB: Don't use EHCI port sempahore for USB 3.0 hubs.
+To: Greg KH <gregkh@linuxfoundation.org>
+Cc: stable@vger.kernel.org
+Message-ID: <20130308002419.GA13701@xanatos>
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+[This is upstream commit 0fe51aa5eee51db7c7ecd201d42a977ad79c58b6.
+It needs to be backported to kernels as old as 3.2, because it fixes the
+buggy commit 9dbcaec830cd97f44a0b91b315844e0d7144746b "USB: Handle warm
+reset failure on empty port."]
+
+The EHCI host controller needs to prevent EHCI initialization when the
+UHCI or OHCI companion controller is in the middle of a port reset.  It
+uses ehci_cf_port_reset_rwsem to do this.  USB 3.0 hubs can't be under
+an EHCI host controller, so it makes no sense to down the semaphore for
+USB 3.0 hubs.  It also makes the warm port reset code more complex.
+
+Don't down ehci_cf_port_reset_rwsem for USB 3.0 hubs.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/core/hub.c |   15 +++++++--------
+ 1 file changed, 7 insertions(+), 8 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2330,17 +2330,16 @@ static int hub_port_reset(struct usb_hub
+ {
+       int i, status;
+-      if (!warm) {
+-              /* Block EHCI CF initialization during the port reset.
+-               * Some companion controllers don't like it when they mix.
+-               */
+-              down_read(&ehci_cf_port_reset_rwsem);
+-      } else {
+-              if (!hub_is_superspeed(hub->hdev)) {
++      if (!hub_is_superspeed(hub->hdev)) {
++              if (warm) {
+                       dev_err(hub->intfdev, "only USB3 hub support "
+                                               "warm reset\n");
+                       return -EINVAL;
+               }
++              /* Block EHCI CF initialization during the port reset.
++               * Some companion controllers don't like it when they mix.
++               */
++              down_read(&ehci_cf_port_reset_rwsem);
+       }
+       /* Reset the port */
+@@ -2378,7 +2377,7 @@ static int hub_port_reset(struct usb_hub
+               port1);
+ done:
+-      if (!warm)
++      if (!hub_is_superspeed(hub->hdev))
+               up_read(&ehci_cf_port_reset_rwsem);
+       return status;
diff --git a/queue-3.4/usb-prepare-for-refactoring-by-adding-extra-udev-checks.patch b/queue-3.4/usb-prepare-for-refactoring-by-adding-extra-udev-checks.patch
new file mode 100644 (file)
index 0000000..2ee836b
--- /dev/null
@@ -0,0 +1,75 @@
+From sarah.a.sharp@linux.intel.com  Tue Mar 12 15:00:24 2013
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Thu, 7 Mar 2013 16:24:22 -0800
+Subject: USB: Prepare for refactoring by adding extra udev checks.
+To: Greg KH <gregkh@linuxfoundation.org>
+Cc: stable@vger.kernel.org
+Message-ID: <20130308002422.GA13722@xanatos>
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+[This is upstream commit 2d4fa940f99663c82ba55b2244638833b388e4e2.
+It needs to be backported to kernels as old as 3.2, because it fixes the
+buggy commit 9dbcaec830cd97f44a0b91b315844e0d7144746b "USB: Handle warm
+reset failure on empty port."]
+
+The next patch will refactor the hub port code to rip out the recursive
+call to hub_port_reset on a failed hot reset.  In preparation for that,
+make sure all code paths can deal with being called with a NULL udev.
+The usb_device will not be valid if warm reset was issued because a port
+transitioned to the Inactive or Compliance Mode on a device connect.
+
+This patch should have no effect on current behavior.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/core/hub.c |   21 +++++++++++++--------
+ 1 file changed, 13 insertions(+), 8 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2253,6 +2253,9 @@ static int hub_port_wait_reset(struct us
+                               return -ENOTCONN;
+                       if ((portstatus & USB_PORT_STAT_ENABLE)) {
++                              if (!udev)
++                                      return 0;
++
+                               if (hub_is_wusb(hub))
+                                       udev->speed = USB_SPEED_WIRELESS;
+                               else if (hub_is_superspeed(hub->hdev))
+@@ -2296,13 +2299,15 @@ static void hub_port_finish_reset(struct
+                       struct usb_hcd *hcd;
+                       /* TRSTRCY = 10 ms; plus some extra */
+                       msleep(10 + 40);
+-                      update_devnum(udev, 0);
+-                      hcd = bus_to_hcd(udev->bus);
+-                      /* The xHC may think the device is already reset,
+-                       * so ignore the status.
+-                       */
+-                      if (hcd->driver->reset_device)
+-                              hcd->driver->reset_device(hcd, udev);
++                      if (udev) {
++                              update_devnum(udev, 0);
++                              hcd = bus_to_hcd(udev->bus);
++                              /* The xHC may think the device is already
++                               * reset, so ignore the status.
++                               */
++                              if (hcd->driver->reset_device)
++                                      hcd->driver->reset_device(hcd, udev);
++                      }
+               }
+               /* FALL THROUGH */
+       case -ENOTCONN:
+@@ -2316,7 +2321,7 @@ static void hub_port_finish_reset(struct
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
+               }
+-              if (!warm)
++              if (!warm && udev)
+                       usb_set_device_state(udev, *status
+                                       ? USB_STATE_NOTATTACHED
+                                       : USB_STATE_DEFAULT);
diff --git a/queue-3.4/usb-rip-out-recursive-call-on-warm-port-reset.patch b/queue-3.4/usb-rip-out-recursive-call-on-warm-port-reset.patch
new file mode 100644 (file)
index 0000000..06bee7b
--- /dev/null
@@ -0,0 +1,280 @@
+From sarah.a.sharp@linux.intel.com  Tue Mar 12 15:02:01 2013
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Thu, 7 Mar 2013 16:24:24 -0800
+Subject: USB: Rip out recursive call on warm port reset.
+To: Greg KH <gregkh@linuxfoundation.org>
+Cc: stable@vger.kernel.org
+Message-ID: <20130308002424.GA13738@xanatos>
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+[This is upstream commit 24a6078754f28528bc91e7e7b3e6ae86bd936d8.
+It needs to be backported to kernels as old as 3.2, because it fixes the
+buggy commit 9dbcaec830cd97f44a0b91b315844e0d7144746b "USB: Handle warm
+reset failure on empty port."]
+
+When a hot reset fails on a USB 3.0 port, the current port reset code
+recursively calls hub_port_reset inside hub_port_wait_reset.  This isn't
+ideal, since we should avoid recursive calls in the kernel, and it also
+doesn't allow us to issue multiple warm resets on reset failures.
+
+Rip out the recursive call.  Instead, add code to hub_port_reset to
+issue a warm reset if the hot reset fails, and try multiple warm resets
+before giving up on the port.
+
+In hub_port_wait_reset, remove the recursive call and re-indent.  The
+code is basically the same, except:
+
+1. It bails out early if the port has transitioned to Inactive or
+Compliance Mode after the reset completed.
+
+2. It doesn't consider a connect status change to be a failed reset.  If
+multiple warm resets needed to be issued, the connect status may have
+changed, so we need to ignore that and look at the port link state
+instead.  hub_port_reset will now do that.
+
+3. It unconditionally sets udev->speed on all types of successful
+resets.  The old recursive code would set the port speed when the second
+hub_port_reset returned.
+
+The old code did not handle connected devices needing a warm reset well.
+There were only two situations that the old code handled correctly: an
+empty port needing a warm reset, and a hot reset that migrated to a warm
+reset.
+
+When an empty port needed a warm reset, hub_port_reset was called with
+the warm variable set.  The code in hub_port_finish_reset would skip
+telling the USB core and the xHC host that the device was reset, because
+otherwise that would result in a NULL pointer dereference.
+
+When a USB 3.0 device reset migrated to a warm reset, the recursive call
+made the call stack look like this:
+
+hub_port_reset(warm = false)
+        hub_wait_port_reset(warm = false)
+                hub_port_reset(warm = true)
+                        hub_wait_port_reset(warm = true)
+                        hub_port_finish_reset(warm = true)
+                        (return up the call stack to the first wait)
+
+        hub_port_finish_reset(warm = false)
+
+The old code didn't want to notify the USB core or the xHC host of device reset
+twice, so it only did it in the second call to hub_port_finish_reset,
+when warm was set to false.  This was necessary because
+before patch two ("USB: Ignore xHCI Reset Device status."), the USB core
+would pay attention to the xHC Reset Device command error status, and
+the second call would always fail.
+
+Now that we no longer have the recursive call, and warm can change from
+false to true in hub_port_reset, we need to have hub_port_finish_reset
+unconditionally notify the USB core and the xHC of the device reset.
+
+In hub_port_finish_reset, unconditionally clear the connect status
+change (CSC) bit for USB 3.0 hubs when the port reset is done.  If we
+had to issue multiple warm resets for a device, that bit may have been
+set if the device went into SS.Inactive and then was successfully warm
+reset.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/core/hub.c |  148 ++++++++++++++++++++++---------------------------
+ 1 file changed, 67 insertions(+), 81 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2207,73 +2207,35 @@ static int hub_port_wait_reset(struct us
+               if ((portstatus & USB_PORT_STAT_RESET))
+                       goto delay;
+-              /*
+-               * Some buggy devices require a warm reset to be issued even
+-               * when the port appears not to be connected.
++              if (hub_port_warm_reset_required(hub, portstatus))
++                      return -ENOTCONN;
++
++              /* Device went away? */
++              if (!(portstatus & USB_PORT_STAT_CONNECTION))
++                      return -ENOTCONN;
++
++              /* bomb out completely if the connection bounced.  A USB 3.0
++               * connection may bounce if multiple warm resets were issued,
++               * but the device may have successfully re-connected. Ignore it.
+                */
+-              if (!warm) {
+-                      /*
+-                       * Some buggy devices can cause an NEC host controller
+-                       * to transition to the "Error" state after a hot port
+-                       * reset.  This will show up as the port state in
+-                       * "Inactive", and the port may also report a
+-                       * disconnect.  Forcing a warm port reset seems to make
+-                       * the device work.
+-                       *
+-                       * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
+-                       */
+-                      if (hub_port_warm_reset_required(hub, portstatus)) {
+-                              int ret;
++              if (!hub_is_superspeed(hub->hdev) &&
++                              (portchange & USB_PORT_STAT_C_CONNECTION))
++                      return -ENOTCONN;
+-                              if ((portchange & USB_PORT_STAT_C_CONNECTION))
+-                                      clear_port_feature(hub->hdev, port1,
+-                                                      USB_PORT_FEAT_C_CONNECTION);
+-                              if (portchange & USB_PORT_STAT_C_LINK_STATE)
+-                                      clear_port_feature(hub->hdev, port1,
+-                                                      USB_PORT_FEAT_C_PORT_LINK_STATE);
+-                              if (portchange & USB_PORT_STAT_C_RESET)
+-                                      clear_port_feature(hub->hdev, port1,
+-                                                      USB_PORT_FEAT_C_RESET);
+-                              dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
+-                                              port1);
+-                              ret = hub_port_reset(hub, port1,
+-                                              udev, HUB_BH_RESET_TIME,
+-                                              true);
+-                              if ((portchange & USB_PORT_STAT_C_CONNECTION))
+-                                      clear_port_feature(hub->hdev, port1,
+-                                                      USB_PORT_FEAT_C_CONNECTION);
+-                              return ret;
+-                      }
+-                      /* Device went away? */
+-                      if (!(portstatus & USB_PORT_STAT_CONNECTION))
+-                              return -ENOTCONN;
+-
+-                      /* bomb out completely if the connection bounced */
+-                      if ((portchange & USB_PORT_STAT_C_CONNECTION))
+-                              return -ENOTCONN;
+-
+-                      if ((portstatus & USB_PORT_STAT_ENABLE)) {
+-                              if (!udev)
+-                                      return 0;
+-
+-                              if (hub_is_wusb(hub))
+-                                      udev->speed = USB_SPEED_WIRELESS;
+-                              else if (hub_is_superspeed(hub->hdev))
+-                                      udev->speed = USB_SPEED_SUPER;
+-                              else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+-                                      udev->speed = USB_SPEED_HIGH;
+-                              else if (portstatus & USB_PORT_STAT_LOW_SPEED)
+-                                      udev->speed = USB_SPEED_LOW;
+-                              else
+-                                      udev->speed = USB_SPEED_FULL;
++              if ((portstatus & USB_PORT_STAT_ENABLE)) {
++                      if (!udev)
+                               return 0;
+-                      }
+-              } else {
+-                      if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+-                                      hub_port_warm_reset_required(hub,
+-                                              portstatus))
+-                              return -ENOTCONN;
++                      if (hub_is_wusb(hub))
++                              udev->speed = USB_SPEED_WIRELESS;
++                      else if (hub_is_superspeed(hub->hdev))
++                              udev->speed = USB_SPEED_SUPER;
++                      else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
++                              udev->speed = USB_SPEED_HIGH;
++                      else if (portstatus & USB_PORT_STAT_LOW_SPEED)
++                              udev->speed = USB_SPEED_LOW;
++                      else
++                              udev->speed = USB_SPEED_FULL;
+                       return 0;
+               }
+@@ -2291,23 +2253,21 @@ delay:
+ }
+ static void hub_port_finish_reset(struct usb_hub *hub, int port1,
+-                      struct usb_device *udev, int *status, bool warm)
++                      struct usb_device *udev, int *status)
+ {
+       switch (*status) {
+       case 0:
+-              if (!warm) {
+-                      struct usb_hcd *hcd;
+-                      /* TRSTRCY = 10 ms; plus some extra */
+-                      msleep(10 + 40);
+-                      if (udev) {
+-                              update_devnum(udev, 0);
+-                              hcd = bus_to_hcd(udev->bus);
+-                              /* The xHC may think the device is already
+-                               * reset, so ignore the status.
+-                               */
+-                              if (hcd->driver->reset_device)
+-                                      hcd->driver->reset_device(hcd, udev);
+-                      }
++              /* TRSTRCY = 10 ms; plus some extra */
++              msleep(10 + 40);
++              if (udev) {
++                      struct usb_hcd *hcd = bus_to_hcd(udev->bus);
++
++                      update_devnum(udev, 0);
++                      /* The xHC may think the device is already reset,
++                       * so ignore the status.
++                       */
++                      if (hcd->driver->reset_device)
++                              hcd->driver->reset_device(hcd, udev);
+               }
+               /* FALL THROUGH */
+       case -ENOTCONN:
+@@ -2320,8 +2280,10 @@ static void hub_port_finish_reset(struct
+                                       USB_PORT_FEAT_C_BH_PORT_RESET);
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
++                      clear_port_feature(hub->hdev, port1,
++                                      USB_PORT_FEAT_C_CONNECTION);
+               }
+-              if (!warm && udev)
++              if (udev)
+                       usb_set_device_state(udev, *status
+                                       ? USB_STATE_NOTATTACHED
+                                       : USB_STATE_DEFAULT);
+@@ -2334,6 +2296,7 @@ static int hub_port_reset(struct usb_hub
+                       struct usb_device *udev, unsigned int delay, bool warm)
+ {
+       int i, status;
++      u16 portchange, portstatus;
+       if (!hub_is_superspeed(hub->hdev)) {
+               if (warm) {
+@@ -2365,10 +2328,33 @@ static int hub_port_reset(struct usb_hub
+                                               status);
+               }
+-              /* return on disconnect or reset */
++              /* Check for disconnect or reset */
+               if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
+-                      hub_port_finish_reset(hub, port1, udev, &status, warm);
+-                      goto done;
++                      hub_port_finish_reset(hub, port1, udev, &status);
++
++                      if (!hub_is_superspeed(hub->hdev))
++                              goto done;
++
++                      /*
++                       * If a USB 3.0 device migrates from reset to an error
++                       * state, re-issue the warm reset.
++                       */
++                      if (hub_port_status(hub, port1,
++                                      &portstatus, &portchange) < 0)
++                              goto done;
++
++                      if (!hub_port_warm_reset_required(hub, portstatus))
++                              goto done;
++
++                      /*
++                       * If the port is in SS.Inactive or Compliance Mode, the
++                       * hot or warm reset failed.  Try another warm reset.
++                       */
++                      if (!warm) {
++                              dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
++                                              port1);
++                              warm = true;
++                      }
+               }
+               dev_dbg (hub->intfdev,