aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOliver Neukum <oneukum@suse.de>2012-05-10 10:19:21 +0200
committerBen Hutchings <ben@decadent.org.uk>2012-05-31 00:43:44 +0100
commit86d17bfb1a8d1b265f466604761e82e8dd2c52d5 (patch)
tree1ed6c81002f7a4554fbb19da2b2c7a942f13681d
parentf0a9bc14e9a9fda7f80484a07dadc58705a2dfec (diff)
USB: fix resource leak in xhci power loss path
commit f8a9e72d125f4e00ec529ba67b674321a1f3bf31 upstream. 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> Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
-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 01c3800fc92..a7b067659d5 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1699,6 +1699,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;
@@ -1757,8 +1765,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);