Skip to content

Commit ff9d78b

Browse files
author
Sarah Sharp
committed
USB: Set usb_hcd->state and flags for shared roothubs.
The hcd->flags are in a sorry state. Some of them are clearly specific to the particular roothub (HCD_POLL_RH, HCD_POLL_PENDING, and HCD_WAKEUP_PENDING), but some flags are related to PCI device state (HCD_HW_ACCESSIBLE and HCD_SAW_IRQ). This is an issue when one PCI device can have two roothubs that share the same IRQ line and hardware. Make sure to set HCD_FLAG_SAW_IRQ for both roothubs when an interrupt is serviced, or an URB is unlinked without an interrupt. (We can't tell if the host actually serviced an interrupt for a particular bus, but we can tell it serviced some interrupt.) HCD_HW_ACCESSIBLE is set once by usb_add_hcd(), which is set for both roothubs as they are added, so it doesn't need to be modified. HCD_POLL_RH and HCD_POLL_PENDING are only checked by the USB core, and they are never set by the xHCI driver, since the roothub never needs to be polled. The usb_hcd's state field is a similar mess. Sometimes the state applies to the underlying hardware: HC_STATE_HALT, HC_STATE_RUNNING, and HC_STATE_QUIESCING. But sometimes the state refers to the roothub state: HC_STATE_RESUMING and HC_STATE_SUSPENDED. Alan Stern recently made the USB core not rely on the hcd->state variable. Internally, the xHCI driver still checks for HC_STATE_SUSPENDED, so leave that code in. Remove all references to HC_STATE_HALT, since the xHCI driver only sets and doesn't test those variables. We still have to set HC_STATE_RUNNING, since Alan's patch has a bug that means the roothub won't get registered if we don't set that. Alan's patch made the USB core check a different variable when trying to determine whether to suspend a roothub. The xHCI host has a split roothub, where two buses are registered for one PCI device. Each bus in the xHCI split roothub can be suspended separately, but both buses must be suspended before the PCI device can be suspended. Therefore, make sure that the USB core checks HCD_RH_RUNNING() for both roothubs before suspending the PCI host. Signed-off-by: Sarah Sharp <[email protected]>
1 parent c563543 commit ff9d78b

File tree

3 files changed

+29
-4
lines changed

3 files changed

+29
-4
lines changed

drivers/usb/core/hcd-pci.c

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,13 @@ static int check_root_hub_suspended(struct device *dev)
367367
dev_warn(dev, "Root hub is not suspended\n");
368368
return -EBUSY;
369369
}
370+
if (hcd->shared_hcd) {
371+
hcd = hcd->shared_hcd;
372+
if (HCD_RH_RUNNING(hcd)) {
373+
dev_warn(dev, "Secondary root hub is not suspended\n");
374+
return -EBUSY;
375+
}
376+
}
370377
return 0;
371378
}
372379

@@ -391,11 +398,16 @@ static int suspend_common(struct device *dev, bool do_wakeup)
391398
*/
392399
if (do_wakeup && HCD_WAKEUP_PENDING(hcd))
393400
return -EBUSY;
401+
if (do_wakeup && hcd->shared_hcd &&
402+
HCD_WAKEUP_PENDING(hcd->shared_hcd))
403+
return -EBUSY;
394404
retval = hcd->driver->pci_suspend(hcd, do_wakeup);
395405
suspend_report_result(hcd->driver->pci_suspend, retval);
396406

397407
/* Check again in case wakeup raced with pci_suspend */
398-
if (retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
408+
if ((retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) ||
409+
(retval == 0 && do_wakeup && hcd->shared_hcd &&
410+
HCD_WAKEUP_PENDING(hcd->shared_hcd))) {
399411
if (hcd->driver->pci_resume)
400412
hcd->driver->pci_resume(hcd, false);
401413
retval = -EBUSY;
@@ -426,7 +438,9 @@ static int resume_common(struct device *dev, int event)
426438
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
427439
int retval;
428440

429-
if (HCD_RH_RUNNING(hcd)) {
441+
if (HCD_RH_RUNNING(hcd) ||
442+
(hcd->shared_hcd &&
443+
HCD_RH_RUNNING(hcd->shared_hcd))) {
430444
dev_dbg(dev, "can't resume, not suspended!\n");
431445
return 0;
432446
}
@@ -440,6 +454,8 @@ static int resume_common(struct device *dev, int event)
440454
pci_set_master(pci_dev);
441455

442456
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
457+
if (hcd->shared_hcd)
458+
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags);
443459

444460
if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) {
445461
if (event != PM_EVENT_AUTO_RESUME)
@@ -449,6 +465,8 @@ static int resume_common(struct device *dev, int event)
449465
event == PM_EVENT_RESTORE);
450466
if (retval) {
451467
dev_err(dev, "PCI post-resume error %d!\n", retval);
468+
if (hcd->shared_hcd)
469+
usb_hc_died(hcd->shared_hcd);
452470
usb_hc_died(hcd);
453471
}
454472
}
@@ -474,8 +492,9 @@ static int hcd_pci_suspend_noirq(struct device *dev)
474492

475493
pci_save_state(pci_dev);
476494

477-
/* If the root hub is dead rather than suspended,
478-
* disallow remote wakeup.
495+
/* If the root hub is dead rather than suspended, disallow remote
496+
* wakeup. usb_hc_died() should ensure that both hosts are marked as
497+
* dying, so we only need to check the primary roothub.
479498
*/
480499
if (HCD_DEAD(hcd))
481500
device_set_wakeup_enable(dev, 0);

drivers/usb/core/hcd.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,8 @@ int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
11531153
dev_warn(hcd->self.controller, "Unlink after no-IRQ? "
11541154
"Controller is probably using the wrong IRQ.\n");
11551155
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
1156+
if (hcd->shared_hcd)
1157+
set_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags);
11561158
}
11571159

11581160
return 0;
@@ -2124,6 +2126,8 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
21242126
rc = IRQ_NONE;
21252127
} else {
21262128
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
2129+
if (hcd->shared_hcd)
2130+
set_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags);
21272131

21282132
if (unlikely(hcd->state == HC_STATE_HALT))
21292133
usb_hc_died(hcd);

drivers/usb/host/xhci-ring.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,6 +2181,8 @@ irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd)
21812181
irqreturn_t ret;
21822182

21832183
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
2184+
if (hcd->shared_hcd)
2185+
set_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags);
21842186

21852187
ret = xhci_irq(hcd);
21862188

0 commit comments

Comments
 (0)