Commit 2cf95c18 authored by Sarah Sharp's avatar Sarah Sharp

Intel xhci: Limit number of active endpoints to 64.

The Panther Point chipset has an xHCI host controller that has a limit to
the number of active endpoints it can handle.  Ideally, it would signal
that it can't handle anymore endpoints by returning a Resource Error for
the Configure Endpoint command, but they don't.  Instead it needs software
to keep track of the number of active endpoints, across configure endpoint
commands, reset device commands, disable slot commands, and address device
commands.

Add a new endpoint context counter, xhci_hcd->num_active_eps, and use it
to track the number of endpoints the xHC has active.  This gets a little
tricky, because commands to change the number of active endpoints can
fail.  This patch adds a new xHCI quirk for these Intel hosts, and the new
code should not have any effect on other xHCI host controllers.

Fail a new device allocation if we don't have room for the new default
control endpoint.  Use the endpoint ring pointers to determine what
endpoints were active before a Reset Device command or a Disable Slot
command, and drop those once the command completes.

Fail a configure endpoint command if it would add too many new endpoints.
We have to be a bit over zealous here, and only count the number of new
endpoints to be added, without subtracting the number of dropped
endpoints.  That's because a second configure endpoint command for a
different device could sneak in before we know if the first command is
completed.  If the first command dropped resources, the host controller
fails the command for some reason, and we're nearing the limit of
endpoints, we could end up oversubscribing the host.

To fix this race condition, when evaluating whether a configure endpoint
command will fix in our bandwidth budget, only add the new endpoints to
xhci->num_active_eps, and don't subtract the dropped endpoints.  Ignore
changed endpoints (ones that are dropped and then re-added), as that
shouldn't effect the host's endpoint resources.  When the configure
endpoint command completes, subtract off the dropped endpoints.

This may mean some configuration changes may temporarily fail, but it's
always better to under-subscribe than over-subscribe resources.

(Originally my plan had been to push the resource allocation down into the
ring allocation functions.  However, that would cause us to allocate
unnecessary resources when endpoints were changed, because the xHCI driver
allocates a new ring for the changed endpoint, and only deletes the old
ring once the Configure Endpoint command succeeds.  A further complication
would have been dealing with the per-device endpoint ring cache.)
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
parent ad808333
...@@ -121,6 +121,8 @@ static int xhci_pci_setup(struct usb_hcd *hcd) ...@@ -121,6 +121,8 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
if (pdev->vendor == PCI_VENDOR_ID_INTEL && if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI) { pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI) {
xhci->quirks |= XHCI_SPURIOUS_SUCCESS; xhci->quirks |= XHCI_SPURIOUS_SUCCESS;
xhci->quirks |= XHCI_EP_LIMIT_QUIRK;
xhci->limit_active_eps = 64;
} }
/* Make sure the HC is halted. */ /* Make sure the HC is halted. */
......
...@@ -1081,8 +1081,13 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, ...@@ -1081,8 +1081,13 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
complete(&xhci->addr_dev); complete(&xhci->addr_dev);
break; break;
case TRB_TYPE(TRB_DISABLE_SLOT): case TRB_TYPE(TRB_DISABLE_SLOT):
if (xhci->devs[slot_id]) if (xhci->devs[slot_id]) {
if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
/* Delete default control endpoint resources */
xhci_free_device_endpoint_resources(xhci,
xhci->devs[slot_id], true);
xhci_free_virt_device(xhci, slot_id); xhci_free_virt_device(xhci, slot_id);
}
break; break;
case TRB_TYPE(TRB_CONFIG_EP): case TRB_TYPE(TRB_CONFIG_EP):
virt_dev = xhci->devs[slot_id]; virt_dev = xhci->devs[slot_id];
......
This diff is collapsed.
...@@ -1292,6 +1292,18 @@ struct xhci_hcd { ...@@ -1292,6 +1292,18 @@ struct xhci_hcd {
#define XHCI_NEC_HOST (1 << 2) #define XHCI_NEC_HOST (1 << 2)
#define XHCI_AMD_PLL_FIX (1 << 3) #define XHCI_AMD_PLL_FIX (1 << 3)
#define XHCI_SPURIOUS_SUCCESS (1 << 4) #define XHCI_SPURIOUS_SUCCESS (1 << 4)
/*
* Certain Intel host controllers have a limit to the number of endpoint
* contexts they can handle. Ideally, they would signal that they can't handle
* anymore endpoint contexts by returning a Resource Error for the Configure
* Endpoint command, but they don't. Instead they expect software to keep track
* of the number of active endpoints for them, across configure endpoint
* commands, reset device commands, disable slot commands, and address device
* commands.
*/
#define XHCI_EP_LIMIT_QUIRK (1 << 5)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */ /* There are two roothubs to keep track of bus suspend info for */
struct xhci_bus_state bus_state[2]; struct xhci_bus_state bus_state[2];
/* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */ /* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */
...@@ -1435,6 +1447,8 @@ void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci, ...@@ -1435,6 +1447,8 @@ void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci,
void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci, void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci,
struct xhci_ep_ctx *ep_ctx, struct xhci_ep_ctx *ep_ctx,
struct xhci_virt_ep *ep); struct xhci_virt_ep *ep);
void xhci_free_device_endpoint_resources(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev, bool drop_control_ep);
struct xhci_ring *xhci_dma_to_transfer_ring( struct xhci_ring *xhci_dma_to_transfer_ring(
struct xhci_virt_ep *ep, struct xhci_virt_ep *ep,
u64 address); u64 address);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment