Commit c52804a472649b2e5005342308739434cbd51119

Authored by Sarah Sharp
1 parent 65bdac5eff

xhci: Avoid "dead ports", add roothub port polling.

The USB core hub thread (khubd) is designed with external USB hubs in
mind.  It expects that if a port status change bit is set, the hub will
continue to send a notification through the hub status data transfer.
Basically, it expects hub notifications to be level-triggered.

The xHCI host controller is designed to be edge-triggered on the logical
'OR' of all the port status change bits.  When all port status change
bits are clear, and a new change bit is set, the xHC will generate a
Port Status Change Event.  If another change bit is set in the same port
status register before the first bit is cleared, it will not send
another event.

This means that the hub code may lose port status changes because of
race conditions between clearing change bits.  The user sees this as a
"dead port" that doesn't react to device connects.

The fix is to turn on port polling whenever a new change bit is set.
Once the USB core issues a hub status request that shows that no change
bits are set in any USB ports, turn off port polling.

We can't allow the USB core to poll the roothub for port events during
host suspend because if the PCI host is in D3cold, the port registers
will be all f's.  Instead, stop the port polling timer, and
unconditionally restart it when the host resumes.  If there are no port
change bits set after the resume, the first call to hub_status_data will
disable polling.

This patch should be backported to stable kernels with the first xHCI
support, 2.6.31 and newer, that include the commit
0f2a79300a1471cf92ab43af165ea13555c8b0a5 "USB: xhci: Root hub support."
There will be merge conflicts because the check for HC_STATE_SUSPENDED
was moved into xhci_suspend in 3.8.

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Cc: stable@vger.kernel.org

Showing 3 changed files with 26 additions and 0 deletions Side-by-side Diff

drivers/usb/host/xhci-hub.c
... ... @@ -984,6 +984,7 @@
984 984 int max_ports;
985 985 __le32 __iomem **port_array;
986 986 struct xhci_bus_state *bus_state;
  987 + bool reset_change = false;
987 988  
988 989 max_ports = xhci_get_ports(hcd, &port_array);
989 990 bus_state = &xhci->bus_state[hcd_index(hcd)];
... ... @@ -1015,6 +1016,12 @@
1015 1016 buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
1016 1017 status = 1;
1017 1018 }
  1019 + if ((temp & PORT_RC))
  1020 + reset_change = true;
  1021 + }
  1022 + if (!status && !reset_change) {
  1023 + xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
  1024 + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
1018 1025 }
1019 1026 spin_unlock_irqrestore(&xhci->lock, flags);
1020 1027 return status ? retval : 0;
drivers/usb/host/xhci-ring.c
... ... @@ -1725,6 +1725,15 @@
1725 1725 if (bogus_port_status)
1726 1726 return;
1727 1727  
  1728 + /*
  1729 + * xHCI port-status-change events occur when the "or" of all the
  1730 + * status-change bits in the portsc register changes from 0 to 1.
  1731 + * New status changes won't cause an event if any other change
  1732 + * bits are still set. When an event occurs, switch over to
  1733 + * polling to avoid losing status changes.
  1734 + */
  1735 + xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
  1736 + set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
1728 1737 spin_unlock(&xhci->lock);
1729 1738 /* Pass this up to the core */
1730 1739 usb_hcd_poll_rh_status(hcd);
drivers/usb/host/xhci.c
... ... @@ -884,6 +884,11 @@
884 884 xhci->shared_hcd->state != HC_STATE_SUSPENDED)
885 885 return -EINVAL;
886 886  
  887 + /* Don't poll the roothubs on bus suspend. */
  888 + xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
  889 + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
  890 + del_timer_sync(&hcd->rh_timer);
  891 +
887 892 spin_lock_irq(&xhci->lock);
888 893 clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
889 894 clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
... ... @@ -1068,6 +1073,11 @@
1068 1073 */
1069 1074 if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
1070 1075 compliance_mode_recovery_timer_init(xhci);
  1076 +
  1077 + /* Re-enable port polling. */
  1078 + xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
  1079 + set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
  1080 + usb_hcd_poll_rh_status(hcd);
1071 1081  
1072 1082 return retval;
1073 1083 }