]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
USB: xhci: Fix check for room on the ring.
authorSarah Sharp <sarah.a.sharp@linux.intel.com>
Tue, 18 May 2010 23:05:26 +0000 (16:05 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 20 May 2010 20:21:30 +0000 (13:21 -0700)
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>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/xhci-ring.c

index b520b37c0f36ed391e115259b37b06bc96f5cda6..407d33fb5e84a043cf9ec0532b063c7de517712d 100644 (file)
@@ -242,10 +242,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)