summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorDmitry Torokhov <dtor@vmware.com>2011-04-12 23:06:28 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2011-04-21 14:32:57 -0700
commit994133a056515517a16bfac485e9741188a71912 (patch)
tree2f6401d1461eb7d10baebe4de913ee97b74bf8ed /drivers/usb
parentb9d75083e022102b4f24dd0f8ebaa14ab4568bbf (diff)
downloadlwn-994133a056515517a16bfac485e9741188a71912.tar.gz
lwn-994133a056515517a16bfac485e9741188a71912.zip
USB: xhci - also free streams when resetting devices
commit 2dea75d96ade3c7cd2bfe73f99c7b3291dc3d03a upstream. Currently, when resetting a device, xHCI driver disables all but one endpoints and frees their rings, but leaves alone any streams that might have been allocated. Later, when users try to free allocated streams, we oops in xhci_setup_no_streams_ep_input_ctx() because ep->ring is NULL. Let's free not only rings but also stream data as well, so that calling free_streams() on a device that was reset will be safe. This should be queued for stable trees back to 2.6.35. Reviewed-by: Micah Elizabeth Scott <micah@vmware.com> Signed-off-by: Dmitry Torokhov <dtor@vmware.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/xhci.c16
1 files changed, 12 insertions, 4 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 2083fc2179b2..150349d1fdac 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -2335,10 +2335,18 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
/* Everything but endpoint 0 is disabled, so free or cache the rings. */
last_freed_endpoint = 1;
for (i = 1; i < 31; ++i) {
- if (!virt_dev->eps[i].ring)
- continue;
- xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
- last_freed_endpoint = i;
+ struct xhci_virt_ep *ep = &virt_dev->eps[i];
+
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ xhci_free_stream_info(xhci, ep->stream_info);
+ ep->stream_info = NULL;
+ ep->ep_state &= ~EP_HAS_STREAMS;
+ }
+
+ if (ep->ring) {
+ xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
+ last_freed_endpoint = i;
+ }
}
xhci_dbg(xhci, "Output context after successful reset device cmd:\n");
xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint);