summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2010-05-18 16:05:26 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2010-07-05 11:15:43 -0700
commita23417e033339c38ff35f3438fdea74e03ddf39b (patch)
treec24780240196760d9f1481edd1e5a6b34a190ab3 /drivers/usb
parent442f1232ae1b23382c967967b05932d2660025cd (diff)
USB: xhci: Fix check for room on the ring.
commit 44ebd037c54f80db3121ac9f5fe6e677b76e11d5 upstream. The length of the scatter gather list a driver can enqueue is limited by the bus' sg_tablesize to 62 entries. Each entry will be described by at least one transfer request block (TRB). If the entry's buffer crosses a 64KB boundary, then that entry will have to be described by two or more TRBs. So even if the USB device driver respects sg_tablesize, the whole scatter list may take more than 62 TRBs to describe, and won't fit on the ring. Don't assume that an empty ring means there is enough room on the transfer ring. The old code would unconditionally queue this too-large transfer, and over write the beginning of the transfer. This would mean the cycle bit was unchanged in those overwritten transfers, causing the hardware to think it didn't own the TRBs, and the host would seem to hang. Now drivers may see submit_urb() fail with -ENOMEM if the transfers are too big to fit on the ring. 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-ring.c19
1 files changed, 18 insertions, 1 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index d527710f6ef7..e45dd9f8f29b 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -241,10 +241,27 @@ static int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
int i;
union xhci_trb *enq = ring->enqueue;
struct xhci_segment *enq_seg = ring->enq_seg;
+ struct xhci_segment *cur_seg;
+ unsigned int left_on_ring;
/* Check if ring is empty */
- if (enq == ring->dequeue)
+ if (enq == ring->dequeue) {
+ /* Can't use link trbs */
+ left_on_ring = TRBS_PER_SEGMENT - 1;
+ for (cur_seg = enq_seg->next; cur_seg != enq_seg;
+ cur_seg = cur_seg->next)
+ left_on_ring += TRBS_PER_SEGMENT - 1;
+
+ /* Always need one TRB free in the ring. */
+ left_on_ring -= 1;
+ if (num_trbs > left_on_ring) {
+ xhci_warn(xhci, "Not enough room on ring; "
+ "need %u TRBs, %u TRBs left\n",
+ num_trbs, left_on_ring);
+ return 0;
+ }
return 1;
+ }
/* Make sure there's an extra empty TRB available */
for (i = 0; i <= num_trbs; ++i) {
if (enq == ring->dequeue)