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

USB: Support for bandwidth allocation.

Originally, the USB core had no support for allocating bandwidth when a
particular configuration or alternate setting for an interface was
selected. Instead, the device driver's URB submission would fail if
there was not enough bandwidth for a periodic endpoint. Drivers could
work around this, by using the scatter-gather list API to guarantee
bandwidth.

This patch adds host controller API to allow the USB core to allocate or
deallocate bandwidth for an endpoint. Endpoints are added to or dropped
from a copy of the current schedule by calling add_endpoint() or
drop_endpoint(), and then the schedule is atomically evaluated with a
call to check_bandwidth(). This allows all the endpoints for a new
configuration or alternate setting to be added at the same time that the
endpoints from the old configuration or alt setting are dropped.

Endpoints must be added to the schedule before any URBs are submitted to
them. The HCD must be allowed to reject a new configuration or alt
setting before the control transfer is sent to the device requesting the
change. It may reject the change because there is not enough bandwidth,
not enough internal resources (such as memory on an embedded host
controller), or perhaps even for security reasons in a virtualized
environment.

If the call to check_bandwidth() fails, the USB core must call
reset_bandwidth(). This causes the schedule to be reverted back to the
state it was in just after the last successful check_bandwidth() call.

If the call succeeds, the host controller driver (and hardware) will have
changed its internal state to match the new configuration or alternate
setting. The USB core can then issue a control transfer to the device to
change the configuration or alt setting. This allows the core to test new
configurations or alternate settings before unbinding drivers bound to
interfaces in the old configuration.

WIP:

The USB core must add endpoints from all interfaces in a configuration
to the schedule, because a driver may claim that interface at any time.
A slight optimization might be to add the endpoints to the schedule once
a driver claims that interface. FIXME

This patch does not cover changing alternate settings, but it does
handle a configuration change or de-configuration. FIXME

The code for managing the schedule is currently HCD specific. A generic
scheduling algorithm could be added for host controllers without
built-in scheduling support. For now, if a host controller does not
define the check_bandwidth() function, the call to
usb_hcd_check_bandwidth() will always succeed.

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Sarah Sharp and committed by
Greg Kroah-Hartman
79abb1ab 663c30d0

+144
+86
drivers/usb/core/hcd.c
··· 1560 1560 } 1561 1561 } 1562 1562 1563 + /* Check whether a new configuration or alt setting for an interface 1564 + * will exceed the bandwidth for the bus (or the host controller resources). 1565 + * Only pass in a non-NULL config or interface, not both! 1566 + * Passing NULL for both new_config and new_intf means the device will be 1567 + * de-configured by issuing a set configuration 0 command. 1568 + */ 1569 + int usb_hcd_check_bandwidth(struct usb_device *udev, 1570 + struct usb_host_config *new_config, 1571 + struct usb_interface *new_intf) 1572 + { 1573 + int num_intfs, i, j; 1574 + struct usb_interface_cache *intf_cache; 1575 + struct usb_host_interface *alt = 0; 1576 + int ret = 0; 1577 + struct usb_hcd *hcd; 1578 + struct usb_host_endpoint *ep; 1579 + 1580 + hcd = bus_to_hcd(udev->bus); 1581 + if (!hcd->driver->check_bandwidth) 1582 + return 0; 1583 + 1584 + /* Configuration is being removed - set configuration 0 */ 1585 + if (!new_config && !new_intf) { 1586 + for (i = 1; i < 16; ++i) { 1587 + ep = udev->ep_out[i]; 1588 + if (ep) 1589 + hcd->driver->drop_endpoint(hcd, udev, ep); 1590 + ep = udev->ep_in[i]; 1591 + if (ep) 1592 + hcd->driver->drop_endpoint(hcd, udev, ep); 1593 + } 1594 + hcd->driver->check_bandwidth(hcd, udev); 1595 + return 0; 1596 + } 1597 + /* Check if the HCD says there's enough bandwidth. Enable all endpoints 1598 + * each interface's alt setting 0 and ask the HCD to check the bandwidth 1599 + * of the bus. There will always be bandwidth for endpoint 0, so it's 1600 + * ok to exclude it. 1601 + */ 1602 + if (new_config) { 1603 + num_intfs = new_config->desc.bNumInterfaces; 1604 + /* Remove endpoints (except endpoint 0, which is always on the 1605 + * schedule) from the old config from the schedule 1606 + */ 1607 + for (i = 1; i < 16; ++i) { 1608 + ep = udev->ep_out[i]; 1609 + if (ep) { 1610 + ret = hcd->driver->drop_endpoint(hcd, udev, ep); 1611 + if (ret < 0) 1612 + goto reset; 1613 + } 1614 + ep = udev->ep_in[i]; 1615 + if (ep) { 1616 + ret = hcd->driver->drop_endpoint(hcd, udev, ep); 1617 + if (ret < 0) 1618 + goto reset; 1619 + } 1620 + } 1621 + for (i = 0; i < num_intfs; ++i) { 1622 + 1623 + /* Dig the endpoints for alt setting 0 out of the 1624 + * interface cache for this interface 1625 + */ 1626 + intf_cache = new_config->intf_cache[i]; 1627 + for (j = 0; j < intf_cache->num_altsetting; j++) { 1628 + if (intf_cache->altsetting[j].desc.bAlternateSetting == 0) 1629 + alt = &intf_cache->altsetting[j]; 1630 + } 1631 + if (!alt) { 1632 + printk(KERN_DEBUG "Did not find alt setting 0 for intf %d\n", i); 1633 + continue; 1634 + } 1635 + for (j = 0; j < alt->desc.bNumEndpoints; j++) { 1636 + ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]); 1637 + if (ret < 0) 1638 + goto reset; 1639 + } 1640 + } 1641 + } 1642 + ret = hcd->driver->check_bandwidth(hcd, udev); 1643 + reset: 1644 + if (ret < 0) 1645 + hcd->driver->reset_bandwidth(hcd, udev); 1646 + return ret; 1647 + } 1648 + 1563 1649 /* Disables the endpoint: synchronizes with the hcd to make sure all 1564 1650 * endpoint state is gone from hardware. usb_hcd_flush_endpoint() must 1565 1651 * have been called previously. Use for set_configuration, set_interface,
+32
drivers/usb/core/hcd.h
··· 232 232 int (*alloc_dev)(struct usb_hcd *, struct usb_device *); 233 233 /* Called by usb_release_dev to free HC device structures */ 234 234 void (*free_dev)(struct usb_hcd *, struct usb_device *); 235 + 236 + /* Bandwidth computation functions */ 237 + /* Note that add_endpoint() can only be called once per endpoint before 238 + * check_bandwidth() or reset_bandwidth() must be called. 239 + * drop_endpoint() can only be called once per endpoint also. 240 + * A call to xhci_drop_endpoint() followed by a call to xhci_add_endpoint() will 241 + * add the endpoint to the schedule with possibly new parameters denoted by a 242 + * different endpoint descriptor in usb_host_endpoint. 243 + * A call to xhci_add_endpoint() followed by a call to xhci_drop_endpoint() is 244 + * not allowed. 245 + */ 246 + /* Allocate endpoint resources and add them to a new schedule */ 247 + int (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *); 248 + /* Drop an endpoint from a new schedule */ 249 + int (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *); 250 + /* Check that a new hardware configuration, set using 251 + * endpoint_enable and endpoint_disable, does not exceed bus 252 + * bandwidth. This must be called before any set configuration 253 + * or set interface requests are sent to the device. 254 + */ 255 + int (*check_bandwidth)(struct usb_hcd *, struct usb_device *); 256 + /* Reset the device schedule to the last known good schedule, 257 + * which was set from a previous successful call to 258 + * check_bandwidth(). This reverts any add_endpoint() and 259 + * drop_endpoint() calls since that last successful call. 260 + * Used for when a check_bandwidth() call fails due to resource 261 + * or bandwidth constraints. 262 + */ 263 + void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *); 235 264 /* Returns the hardware-chosen device address */ 236 265 int (*address_device)(struct usb_hcd *, struct usb_device *udev); 237 266 }; ··· 281 252 extern void usb_hcd_reset_endpoint(struct usb_device *udev, 282 253 struct usb_host_endpoint *ep); 283 254 extern void usb_hcd_synchronize_unlinks(struct usb_device *udev); 255 + extern int usb_hcd_check_bandwidth(struct usb_device *udev, 256 + struct usb_host_config *new_config, 257 + struct usb_interface *new_intf); 284 258 extern int usb_hcd_get_frame_number(struct usb_device *udev); 285 259 286 260 extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
+20
drivers/usb/core/message.c
··· 510 510 * could be transferred. That capability is less useful for low or full 511 511 * speed interrupt endpoints, which allow at most one packet per millisecond, 512 512 * of at most 8 or 64 bytes (respectively). 513 + * 514 + * It is not necessary to call this function to reserve bandwidth for devices 515 + * under an xHCI host controller, as the bandwidth is reserved when the 516 + * configuration or interface alt setting is selected. 513 517 */ 514 518 void usb_sg_wait(struct usb_sg_request *io) 515 519 { ··· 1657 1653 if (ret) 1658 1654 goto free_interfaces; 1659 1655 1656 + /* Make sure we have bandwidth (and available HCD resources) for this 1657 + * configuration. Remove endpoints from the schedule if we're dropping 1658 + * this configuration to set configuration 0. After this point, the 1659 + * host controller will not allow submissions to dropped endpoints. If 1660 + * this call fails, the device state is unchanged. 1661 + */ 1662 + if (cp) 1663 + ret = usb_hcd_check_bandwidth(dev, cp, NULL); 1664 + else 1665 + ret = usb_hcd_check_bandwidth(dev, NULL, NULL); 1666 + if (ret < 0) { 1667 + usb_autosuspend_device(dev); 1668 + goto free_interfaces; 1669 + } 1670 + 1660 1671 /* if it's already configured, clear out old state first. 1661 1672 * getting rid of old interfaces means unbinding their drivers. 1662 1673 */ ··· 1694 1675 dev->actconfig = cp; 1695 1676 if (!cp) { 1696 1677 usb_set_device_state(dev, USB_STATE_ADDRESS); 1678 + usb_hcd_check_bandwidth(dev, NULL, NULL); 1697 1679 usb_autosuspend_device(dev); 1698 1680 goto free_interfaces; 1699 1681 }
+6
drivers/usb/core/urb.c
··· 241 241 * If the USB subsystem can't allocate sufficient bandwidth to perform 242 242 * the periodic request, submitting such a periodic request should fail. 243 243 * 244 + * For devices under xHCI, the bandwidth is reserved at configuration time, or 245 + * when the alt setting is selected. If there is not enough bus bandwidth, the 246 + * configuration/alt setting request will fail. Therefore, submissions to 247 + * periodic endpoints on devices under xHCI should never fail due to bandwidth 248 + * constraints. 249 + * 244 250 * Device drivers must explicitly request that repetition, by ensuring that 245 251 * some URB is always on the endpoint's queue (except possibly for short 246 252 * periods during completion callacks). When there is no longer an urb