Skip to content

Commit f370b99

Browse files
Andiry XuSarah Sharp
authored andcommitted
xHCI: keep track of ports being resumed and indicate in hub_status_data
This commit adds a bit-array to xhci bus_state for keeping track of which ports are undergoing a resume transition. If any of the bits are set when xhci_hub_status_data() is called, the routine will return a non-zero value even if no ports have any status changes pending. This will allow usbcore to handle races between root-hub suspend and port wakeup. This patch should be backported to kernels as old as 3.4, that contain the commit 879d38e "USB: fix race between root-hub suspend and remote wakeup". Signed-off-by: Andiry Xu <[email protected]> Signed-off-by: Sarah Sharp <[email protected]> Cc: Alan Stern <[email protected]> Cc: [email protected]
1 parent 3244560 commit f370b99

File tree

4 files changed

+25
-12
lines changed

4 files changed

+25
-12
lines changed

drivers/usb/host/xhci-hub.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
558558
xhci_dbg(xhci, "Resume USB2 port %d\n",
559559
wIndex + 1);
560560
bus_state->resume_done[wIndex] = 0;
561+
clear_bit(wIndex, &bus_state->resuming_ports);
561562
xhci_set_link_state(xhci, port_array, wIndex,
562563
XDEV_U0);
563564
xhci_dbg(xhci, "set port %d resume\n",
@@ -845,7 +846,12 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
845846
/* Initial status is no changes */
846847
retval = (max_ports + 8) / 8;
847848
memset(buf, 0, retval);
848-
status = 0;
849+
850+
/*
851+
* Inform the usbcore about resume-in-progress by returning
852+
* a non-zero value even if there are no status changes.
853+
*/
854+
status = bus_state->resuming_ports;
849855

850856
mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC;
851857

@@ -885,15 +891,11 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
885891
spin_lock_irqsave(&xhci->lock, flags);
886892

887893
if (hcd->self.root_hub->do_remote_wakeup) {
888-
port_index = max_ports;
889-
while (port_index--) {
890-
if (bus_state->resume_done[port_index] != 0) {
891-
spin_unlock_irqrestore(&xhci->lock, flags);
892-
xhci_dbg(xhci, "suspend failed because "
893-
"port %d is resuming\n",
894-
port_index + 1);
895-
return -EBUSY;
896-
}
894+
if (bus_state->resuming_ports) {
895+
spin_unlock_irqrestore(&xhci->lock, flags);
896+
xhci_dbg(xhci, "suspend failed because "
897+
"a port is resuming\n");
898+
return -EBUSY;
897899
}
898900
}
899901

drivers/usb/host/xhci-ring.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
13771377
xhci_dbg(xhci, "resume HS port %d\n", port_id);
13781378
bus_state->resume_done[faked_port_index] = jiffies +
13791379
msecs_to_jiffies(20);
1380+
set_bit(faked_port_index, &bus_state->resuming_ports);
13801381
mod_timer(&hcd->rh_timer,
13811382
bus_state->resume_done[faked_port_index]);
13821383
/* Do the rest in GetPortStatus */

drivers/usb/host/xhci.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ int xhci_reset(struct xhci_hcd *xhci)
152152
{
153153
u32 command;
154154
u32 state;
155-
int ret;
155+
int ret, i;
156156

157157
state = xhci_readl(xhci, &xhci->op_regs->status);
158158
if ((state & STS_HALT) == 0) {
@@ -175,7 +175,15 @@ int xhci_reset(struct xhci_hcd *xhci)
175175
* xHCI cannot write to any doorbells or operational registers other
176176
* than status until the "Controller Not Ready" flag is cleared.
177177
*/
178-
return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
178+
ret = handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
179+
180+
for (i = 0; i < 2; ++i) {
181+
xhci->bus_state[i].port_c_suspend = 0;
182+
xhci->bus_state[i].suspended_ports = 0;
183+
xhci->bus_state[i].resuming_ports = 0;
184+
}
185+
186+
return ret;
179187
}
180188

181189
#ifdef CONFIG_PCI

drivers/usb/host/xhci.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,6 +1362,8 @@ struct xhci_bus_state {
13621362
u32 suspended_ports;
13631363
u32 port_remote_wakeup;
13641364
unsigned long resume_done[USB_MAXCHILDREN];
1365+
/* which ports have started to resume */
1366+
unsigned long resuming_ports;
13651367
};
13661368

13671369
static inline unsigned int hcd_index(struct usb_hcd *hcd)

0 commit comments

Comments
 (0)