Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: assign default peer ports for root hubs

Assume that the peer of a superspeed port is the port with the same id
on the shared_hcd root hub. This identification scheme is required of
external hubs by the USB3 spec [1]. However, for root hubs, tier mismatch
may be in effect [2]. Tier mismatch can only be enumerated via platform
firmware. For now, simply perform the nominal association.

A new lock 'usb_port_peer_mutex' is introduced to synchronize port
device add/remove with peer lookups. It protects peering against
changes to hcd->shared_hcd, hcd->self.root_hub, hdev->maxchild, and
port_dev->child pointers.

[1]: usb 3.1 section 10.3.3
[2]: xhci 1.1 appendix D

Cc: Alan Stern <stern@rowland.harvard.edu>
[alan: usb_port_peer_mutex locking scheme]
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Dan Williams and committed by
Greg Kroah-Hartman
d8521afe a4204ff0

+134 -27
+35 -8
drivers/usb/core/hcd.c
··· 2458 2458 mutex_init(hcd->bandwidth_mutex); 2459 2459 dev_set_drvdata(dev, hcd); 2460 2460 } else { 2461 + mutex_lock(&usb_port_peer_mutex); 2461 2462 hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex; 2462 2463 hcd->primary_hcd = primary_hcd; 2463 2464 primary_hcd->primary_hcd = primary_hcd; 2464 2465 hcd->shared_hcd = primary_hcd; 2465 2466 primary_hcd->shared_hcd = hcd; 2467 + mutex_unlock(&usb_port_peer_mutex); 2466 2468 } 2467 2469 2468 2470 kref_init(&hcd->kref); ··· 2516 2514 * deallocated. 2517 2515 * 2518 2516 * Make sure to only deallocate the bandwidth_mutex when the primary HCD is 2519 - * freed. When hcd_release() is called for the non-primary HCD, set the 2520 - * primary_hcd's shared_hcd pointer to null (since the non-primary HCD will be 2521 - * freed shortly). 2517 + * freed. When hcd_release() is called for either hcd in a peer set 2518 + * invalidate the peer's ->shared_hcd and ->primary_hcd pointers to 2519 + * block new peering attempts 2522 2520 */ 2523 - static void hcd_release (struct kref *kref) 2521 + static void hcd_release(struct kref *kref) 2524 2522 { 2525 2523 struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref); 2526 2524 2525 + mutex_lock(&usb_port_peer_mutex); 2527 2526 if (usb_hcd_is_primary_hcd(hcd)) 2528 2527 kfree(hcd->bandwidth_mutex); 2529 - else 2530 - hcd->shared_hcd->shared_hcd = NULL; 2528 + if (hcd->shared_hcd) { 2529 + struct usb_hcd *peer = hcd->shared_hcd; 2530 + 2531 + peer->shared_hcd = NULL; 2532 + if (peer->primary_hcd == hcd) 2533 + peer->primary_hcd = NULL; 2534 + } 2535 + mutex_unlock(&usb_port_peer_mutex); 2531 2536 kfree(hcd); 2532 2537 } 2533 2538 ··· 2602 2593 return 0; 2603 2594 } 2604 2595 2596 + /* 2597 + * Before we free this root hub, flush in-flight peering attempts 2598 + * and disable peer lookups 2599 + */ 2600 + static void usb_put_invalidate_rhdev(struct usb_hcd *hcd) 2601 + { 2602 + struct usb_device *rhdev; 2603 + 2604 + mutex_lock(&usb_port_peer_mutex); 2605 + rhdev = hcd->self.root_hub; 2606 + hcd->self.root_hub = NULL; 2607 + mutex_unlock(&usb_port_peer_mutex); 2608 + usb_put_dev(rhdev); 2609 + } 2610 + 2605 2611 /** 2606 2612 * usb_add_hcd - finish generic HCD structure initialization and register 2607 2613 * @hcd: the usb_hcd structure to initialize ··· 2677 2653 retval = -ENOMEM; 2678 2654 goto err_allocate_root_hub; 2679 2655 } 2656 + mutex_lock(&usb_port_peer_mutex); 2680 2657 hcd->self.root_hub = rhdev; 2658 + mutex_unlock(&usb_port_peer_mutex); 2681 2659 2682 2660 switch (hcd->speed) { 2683 2661 case HCD_USB11: ··· 2788 2762 err_request_irq: 2789 2763 err_hcd_driver_setup: 2790 2764 err_set_rh_speed: 2791 - usb_put_dev(hcd->self.root_hub); 2765 + usb_put_invalidate_rhdev(hcd); 2792 2766 err_allocate_root_hub: 2793 2767 usb_deregister_bus(&hcd->self); 2794 2768 err_register_bus: ··· 2868 2842 free_irq(hcd->irq, hcd); 2869 2843 } 2870 2844 2871 - usb_put_dev(hcd->self.root_hub); 2872 2845 usb_deregister_bus(&hcd->self); 2873 2846 hcd_buffer_destroy(hcd); 2874 2847 if (hcd->remove_phy && hcd->phy) { ··· 2875 2850 usb_put_phy(hcd->phy); 2876 2851 hcd->phy = NULL; 2877 2852 } 2853 + 2854 + usb_put_invalidate_rhdev(hcd); 2878 2855 } 2879 2856 EXPORT_SYMBOL_GPL(usb_remove_hcd); 2880 2857
+28 -14
drivers/usb/core/hub.c
··· 55 55 56 56 static struct task_struct *khubd_task; 57 57 58 + /* synchronize hub-port add/remove and peering operations */ 59 + DEFINE_MUTEX(usb_port_peer_mutex); 60 + 58 61 /* cycle leds on hubs that aren't blinking for attention */ 59 62 static bool blinkenlights = 0; 60 63 module_param (blinkenlights, bool, S_IRUGO); ··· 1326 1323 char *message = "out of memory"; 1327 1324 unsigned unit_load; 1328 1325 unsigned full_load; 1326 + unsigned maxchild; 1329 1327 1330 1328 hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL); 1331 1329 if (!hub->buffer) { ··· 1365 1361 goto fail; 1366 1362 } 1367 1363 1368 - hdev->maxchild = hub->descriptor->bNbrPorts; 1369 - dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild, 1370 - (hdev->maxchild == 1) ? "" : "s"); 1364 + maxchild = hub->descriptor->bNbrPorts; 1365 + dev_info(hub_dev, "%d port%s detected\n", maxchild, 1366 + (maxchild == 1) ? "" : "s"); 1371 1367 1372 - hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *), 1373 - GFP_KERNEL); 1368 + hub->ports = kzalloc(maxchild * sizeof(struct usb_port *), GFP_KERNEL); 1374 1369 if (!hub->ports) { 1375 1370 ret = -ENOMEM; 1376 1371 goto fail; ··· 1390 1387 int i; 1391 1388 char portstr[USB_MAXCHILDREN + 1]; 1392 1389 1393 - for (i = 0; i < hdev->maxchild; i++) 1390 + for (i = 0; i < maxchild; i++) 1394 1391 portstr[i] = hub->descriptor->u.hs.DeviceRemovable 1395 1392 [((i + 1) / 8)] & (1 << ((i + 1) % 8)) 1396 1393 ? 'F' : 'R'; 1397 - portstr[hdev->maxchild] = 0; 1394 + portstr[maxchild] = 0; 1398 1395 dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr); 1399 1396 } else 1400 1397 dev_dbg(hub_dev, "standalone hub\n"); ··· 1506 1503 if (hcd->power_budget > 0) 1507 1504 hdev->bus_mA = hcd->power_budget; 1508 1505 else 1509 - hdev->bus_mA = full_load * hdev->maxchild; 1506 + hdev->bus_mA = full_load * maxchild; 1510 1507 if (hdev->bus_mA >= full_load) 1511 1508 hub->mA_per_port = full_load; 1512 1509 else { ··· 1521 1518 hub->descriptor->bHubContrCurrent); 1522 1519 hub->limited_power = 1; 1523 1520 1524 - if (remaining < hdev->maxchild * unit_load) 1521 + if (remaining < maxchild * unit_load) 1525 1522 dev_warn(hub_dev, 1526 1523 "insufficient power available " 1527 1524 "to use all downstream ports\n"); ··· 1589 1586 if (hub->has_indicators && blinkenlights) 1590 1587 hub->indicator[0] = INDICATOR_CYCLE; 1591 1588 1592 - for (i = 0; i < hdev->maxchild; i++) { 1589 + mutex_lock(&usb_port_peer_mutex); 1590 + for (i = 0; i < maxchild; i++) { 1593 1591 ret = usb_hub_create_port_device(hub, i + 1); 1594 1592 if (ret < 0) { 1595 1593 dev_err(hub->intfdev, 1596 1594 "couldn't create port%d device.\n", i + 1); 1597 - hdev->maxchild = i; 1598 - goto fail_keep_maxchild; 1595 + break; 1599 1596 } 1600 1597 } 1598 + hdev->maxchild = i; 1599 + mutex_unlock(&usb_port_peer_mutex); 1600 + if (ret < 0) 1601 + goto fail; 1601 1602 1602 1603 usb_hub_adjust_deviceremovable(hdev, hub->descriptor); 1603 1604 ··· 1609 1602 return 0; 1610 1603 1611 1604 fail: 1612 - hdev->maxchild = 0; 1613 - fail_keep_maxchild: 1614 1605 dev_err (hub_dev, "config failed, %s (err %d)\n", 1615 1606 message, ret); 1616 1607 /* hub_disconnect() frees urb and descriptor */ ··· 1644 1639 hub->error = 0; 1645 1640 hub_quiesce(hub, HUB_DISCONNECT); 1646 1641 1642 + mutex_lock(&usb_port_peer_mutex); 1643 + 1647 1644 /* Avoid races with recursively_mark_NOTATTACHED() */ 1648 1645 spin_lock_irq(&device_state_lock); 1649 1646 port1 = hdev->maxchild; ··· 1655 1648 1656 1649 for (; port1 > 0; --port1) 1657 1650 usb_hub_remove_port_device(hub, port1); 1651 + 1652 + mutex_unlock(&usb_port_peer_mutex); 1658 1653 1659 1654 if (hub->hdev->speed == USB_SPEED_HIGH) 1660 1655 highspeed_hubs--; ··· 4617 4608 */ 4618 4609 status = 0; 4619 4610 4611 + mutex_lock(&usb_port_peer_mutex); 4612 + 4620 4613 /* We mustn't add new devices if the parent hub has 4621 4614 * been disconnected; we would race with the 4622 4615 * recursively_mark_NOTATTACHED() routine. ··· 4629 4618 else 4630 4619 port_dev->child = udev; 4631 4620 spin_unlock_irq(&device_state_lock); 4621 + mutex_unlock(&usb_port_peer_mutex); 4632 4622 4633 4623 /* Run it through the hoops (find a driver, etc) */ 4634 4624 if (!status) { 4635 4625 status = usb_new_device(udev); 4636 4626 if (status) { 4627 + mutex_lock(&usb_port_peer_mutex); 4637 4628 spin_lock_irq(&device_state_lock); 4638 4629 port_dev->child = NULL; 4639 4630 spin_unlock_irq(&device_state_lock); 4631 + mutex_unlock(&usb_port_peer_mutex); 4640 4632 } 4641 4633 } 4642 4634
+2
drivers/usb/core/hub.h
··· 82 82 * @child: usb device attached to the port 83 83 * @dev: generic device interface 84 84 * @port_owner: port's owner 85 + * @peer: related usb2 and usb3 ports (share the same connector) 85 86 * @connect_type: port's connect type 86 87 * @portnum: port index num based one 87 88 * @power_is_on: port's power state ··· 92 91 struct usb_device *child; 93 92 struct device dev; 94 93 struct usb_dev_state *port_owner; 94 + struct usb_port *peer; 95 95 enum usb_port_connect_type connect_type; 96 96 u8 portnum; 97 97 unsigned power_is_on:1;
+68 -5
drivers/usb/core/port.c
··· 157 157 .owner = THIS_MODULE, 158 158 }; 159 159 160 + static void link_peers(struct usb_port *left, struct usb_port *right) 161 + { 162 + if (left->peer == right && right->peer == left) 163 + return; 164 + 165 + if (left->peer || right->peer) { 166 + struct usb_port *lpeer = left->peer; 167 + struct usb_port *rpeer = right->peer; 168 + 169 + WARN(1, "failed to peer %s and %s (%s -> %p) (%s -> %p)\n", 170 + dev_name(&left->dev), dev_name(&right->dev), 171 + dev_name(&left->dev), lpeer, 172 + dev_name(&right->dev), rpeer); 173 + return; 174 + } 175 + 176 + left->peer = right; 177 + right->peer = left; 178 + } 179 + 180 + static void unlink_peers(struct usb_port *left, struct usb_port *right) 181 + { 182 + WARN(right->peer != left || left->peer != right, 183 + "%s and %s are not peers?\n", 184 + dev_name(&left->dev), dev_name(&right->dev)); 185 + 186 + right->peer = NULL; 187 + left->peer = NULL; 188 + } 189 + 190 + /* set the default peer port for root hubs */ 191 + static void find_and_link_peer(struct usb_hub *hub, int port1) 192 + { 193 + struct usb_port *port_dev = hub->ports[port1 - 1], *peer; 194 + struct usb_device *hdev = hub->hdev; 195 + 196 + if (!hdev->parent) { 197 + struct usb_hub *peer_hub; 198 + struct usb_device *peer_hdev; 199 + struct usb_hcd *hcd = bus_to_hcd(hdev->bus); 200 + struct usb_hcd *peer_hcd = hcd->shared_hcd; 201 + 202 + if (!peer_hcd) 203 + return; 204 + 205 + peer_hdev = peer_hcd->self.root_hub; 206 + peer_hub = usb_hub_to_struct_hub(peer_hdev); 207 + if (!peer_hub || port1 > peer_hdev->maxchild) 208 + return; 209 + 210 + peer = peer_hub->ports[port1 - 1]; 211 + 212 + if (peer) 213 + link_peers(port_dev, peer); 214 + } 215 + } 216 + 160 217 int usb_hub_create_port_device(struct usb_hub *hub, int port1) 161 218 { 162 - struct usb_port *port_dev = NULL; 219 + struct usb_port *port_dev; 163 220 int retval; 164 221 165 222 port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL); ··· 237 180 retval = device_register(&port_dev->dev); 238 181 if (retval) 239 182 goto error_register; 183 + 184 + find_and_link_peer(hub, port1); 240 185 241 186 pm_runtime_set_active(&port_dev->dev); 242 187 ··· 262 203 return retval; 263 204 } 264 205 265 - void usb_hub_remove_port_device(struct usb_hub *hub, 266 - int port1) 206 + void usb_hub_remove_port_device(struct usb_hub *hub, int port1) 267 207 { 268 - device_unregister(&hub->ports[port1 - 1]->dev); 269 - } 208 + struct usb_port *port_dev = hub->ports[port1 - 1]; 209 + struct usb_port *peer; 270 210 211 + peer = port_dev->peer; 212 + if (peer) 213 + unlink_peers(port_dev, peer); 214 + device_unregister(&port_dev->dev); 215 + }
+1
drivers/usb/core/usb.h
··· 119 119 #endif 120 120 121 121 extern struct bus_type usb_bus_type; 122 + extern struct mutex usb_port_peer_mutex; 122 123 extern struct device_type usb_device_type; 123 124 extern struct device_type usb_if_device_type; 124 125 extern struct device_type usb_ep_device_type;