diff options
author | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2011-05-16 16:09:08 -0400 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2011-05-16 20:59:11 -0400 |
commit | 30f89ca021c3e584b61bc5a14eede89f74b2e826 (patch) | |
tree | a0499d96cacd5ca86117e45898d53f241ab5c6f3 | |
parent | b513d44751bfb609a3c20463f764c8ce822d63e9 (diff) |
xhci: Fix memory leak in ring cache deallocation.
When an endpoint ring is freed, it is either cached in a per-device ring
cache, or simply freed if the ring cache is full. If the ring was added
to the cache, then virt_dev->num_rings_cached is incremented. The cache
is designed to hold up to 31 endpoint rings, in array indexes 0 to 30.
When the device is freed (when the slot was disabled),
xhci_free_virt_device() is called, it would free the cached rings in
array indexes 0 to virt_dev->num_rings_cached.
Unfortunately, the original code in xhci_free_or_cache_endpoint_ring()
would put the first entry into the ring cache in array index 1, instead of
array index 0. This was caused by the second assignment to rings_cached:
rings_cached = virt_dev->num_rings_cached;
if (rings_cached < XHCI_MAX_RINGS_CACHED) {
virt_dev->num_rings_cached++;
rings_cached = virt_dev->num_rings_cached;
virt_dev->ring_cache[rings_cached] =
virt_dev->eps[ep_index].ring;
This meant that when the device was freed, cached rings with indexes 0 to
N would be freed, and the last cached ring in index N+1 would not be
freed. When the driver was unloaded, this caused interesting messages
like:
xhci_hcd 0000:06:00.0: dma_pool_destroy xHCI ring segments, ffff880063040000 busy
This should be queued to stable kernels back to 2.6.33.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: stable@kernel.org
-rw-r--r-- | drivers/usb/host/xhci-mem.c | 7 |
1 files changed, 3 insertions, 4 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 6938cc8d848f..26caba4c1950 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c | |||
@@ -209,14 +209,13 @@ void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci, | |||
209 | 209 | ||
210 | rings_cached = virt_dev->num_rings_cached; | 210 | rings_cached = virt_dev->num_rings_cached; |
211 | if (rings_cached < XHCI_MAX_RINGS_CACHED) { | 211 | if (rings_cached < XHCI_MAX_RINGS_CACHED) { |
212 | virt_dev->num_rings_cached++; | ||
213 | rings_cached = virt_dev->num_rings_cached; | ||
214 | virt_dev->ring_cache[rings_cached] = | 212 | virt_dev->ring_cache[rings_cached] = |
215 | virt_dev->eps[ep_index].ring; | 213 | virt_dev->eps[ep_index].ring; |
214 | virt_dev->num_rings_cached++; | ||
216 | xhci_dbg(xhci, "Cached old ring, " | 215 | xhci_dbg(xhci, "Cached old ring, " |
217 | "%d ring%s cached\n", | 216 | "%d ring%s cached\n", |
218 | rings_cached, | 217 | virt_dev->num_rings_cached, |
219 | (rings_cached > 1) ? "s" : ""); | 218 | (virt_dev->num_rings_cached > 1) ? "s" : ""); |
220 | } else { | 219 | } else { |
221 | xhci_ring_free(xhci, virt_dev->eps[ep_index].ring); | 220 | xhci_ring_free(xhci, virt_dev->eps[ep_index].ring); |
222 | xhci_dbg(xhci, "Ring cache full (%d rings), " | 221 | xhci_dbg(xhci, "Ring cache full (%d rings), " |