summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Neukum <oneukum@suse.de>2012-05-10 10:19:21 +0200
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2012-05-18 15:41:39 -0700
commitf8a9e72d125f4e00ec529ba67b674321a1f3bf31 (patch)
treeec793976b925ecb2d5678ae0043d7a274b669ff6
parentc3e751e4f4754793bb52bd5ae30e9cc027edbb12 (diff)
downloadlinux-f8a9e72d125f4e00ec529ba67b674321a1f3bf31.tar.gz
linux-f8a9e72d125f4e00ec529ba67b674321a1f3bf31.tar.bz2
linux-f8a9e72d125f4e00ec529ba67b674321a1f3bf31.zip
USB: fix resource leak in xhci power loss path
Some more data structures must be freed and counters reset if an XHCI controller has lost power. The failure to do so renders some chips inoperative after a certain number of S4 cycles. This patch should be backported to kernels as old as 3.2, that contain the commits c29eea621900f18287d50519f72cb9113746d75a "xhci: Implement HS/FS/LS bandwidth checking." and commit 839c817ce67178ca3c7c7ad534c571bba1e69ebe "xhci: Implement HS/FS/LS bandwidth checking." Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: stable@vger.kernel.org
-rw-r--r--drivers/usb/host/xhci-mem.c26
1 files changed, 26 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 68eaa908ac8e..0b5ff2618de7 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1791,6 +1791,14 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
{
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
struct dev_info *dev_info, *next;
+ struct list_head *tt_list_head;
+ struct list_head *tt;
+ struct list_head *endpoints;
+ struct list_head *ep, *q;
+ struct xhci_tt_bw_info *tt_info;
+ struct xhci_interval_bw_table *bwt;
+ struct xhci_virt_ep *virt_ep;
+
unsigned long flags;
int size;
int i;
@@ -1849,8 +1857,26 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
}
spin_unlock_irqrestore(&xhci->lock, flags);
+ bwt = &xhci->rh_bw->bw_table;
+ for (i = 0; i < XHCI_MAX_INTERVAL; i++) {
+ endpoints = &bwt->interval_bw[i].endpoints;
+ list_for_each_safe(ep, q, endpoints) {
+ virt_ep = list_entry(ep, struct xhci_virt_ep, bw_endpoint_list);
+ list_del(&virt_ep->bw_endpoint_list);
+ kfree(virt_ep);
+ }
+ }
+
+ tt_list_head = &xhci->rh_bw->tts;
+ list_for_each_safe(tt, q, tt_list_head) {
+ tt_info = list_entry(tt, struct xhci_tt_bw_info, tt_list);
+ list_del(tt);
+ kfree(tt_info);
+ }
+
xhci->num_usb2_ports = 0;
xhci->num_usb3_ports = 0;
+ xhci->num_active_eps = 0;
kfree(xhci->usb2_ports);
kfree(xhci->usb3_ports);
kfree(xhci->port_array);