aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-12-02 13:37:50 -0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-12-02 13:37:50 -0800
commitd63e502dbebf095e4ffd7a40acfc3056fbe892fd (patch)
treeee95416c3efb1f75ab4bd43ed2560a23c97739d3
parentc24cb6c8b501ebdf1aacec7960110a9741a45ced (diff)
parent35773dac5f862cb1c82ea151eba3e2f6de51ec3e (diff)
downloadmfd-fixes-d63e502dbebf095e4ffd7a40acfc3056fbe892fd.tar.gz
Merge tag 'for-usb-linus-2013-12-02' of git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-linus
Sarah writes: xhci: Regression fix for 3.13. Hi Greg, Here's one bug fix for 3.13. usb-net added support for bulk scatter-gather in 3.12, and it triggered a bug in the xHCI driver. This bug causes xHCI hosts to send an unexpected short transfer, which will cause the USB ethernet device to stop sending packets. The patch is marked for the 3.12 stable kernel. It's a long standing bug, but the usb-net drivers are the first to trigger it. The only other driver that does bulk scatter-gather (usb-storage) will not trigger this bug. I'm not sure what the effect of the no-op TRBs will be on various xHCI host controllers, so I would only like to be conservative and only queue it for 3.13 and 3.12 stable. Please queue this for 3.13. Sarah Sharp
-rw-r--r--drivers/usb/host/xhci-ring.c54
-rw-r--r--include/linux/usb.h2
2 files changed, 54 insertions, 2 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 1e2f3f4958436..53c2e296467fc 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2973,8 +2973,58 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
}
while (1) {
- if (room_on_ring(xhci, ep_ring, num_trbs))
- break;
+ if (room_on_ring(xhci, ep_ring, num_trbs)) {
+ union xhci_trb *trb = ep_ring->enqueue;
+ unsigned int usable = ep_ring->enq_seg->trbs +
+ TRBS_PER_SEGMENT - 1 - trb;
+ u32 nop_cmd;
+
+ /*
+ * Section 4.11.7.1 TD Fragments states that a link
+ * TRB must only occur at the boundary between
+ * data bursts (eg 512 bytes for 480M).
+ * While it is possible to split a large fragment
+ * we don't know the size yet.
+ * Simplest solution is to fill the trb before the
+ * LINK with nop commands.
+ */
+ if (num_trbs == 1 || num_trbs <= usable || usable == 0)
+ break;
+
+ if (ep_ring->type != TYPE_BULK)
+ /*
+ * While isoc transfers might have a buffer that
+ * crosses a 64k boundary it is unlikely.
+ * Since we can't add NOPs without generating
+ * gaps in the traffic just hope it never
+ * happens at the end of the ring.
+ * This could be fixed by writing a LINK TRB
+ * instead of the first NOP - however the
+ * TRB_TYPE_LINK_LE32() calls would all need
+ * changing to check the ring length.
+ */
+ break;
+
+ if (num_trbs >= TRBS_PER_SEGMENT) {
+ xhci_err(xhci, "Too many fragments %d, max %d\n",
+ num_trbs, TRBS_PER_SEGMENT - 1);
+ return -ENOMEM;
+ }
+
+ nop_cmd = cpu_to_le32(TRB_TYPE(TRB_TR_NOOP) |
+ ep_ring->cycle_state);
+ ep_ring->num_trbs_free -= usable;
+ do {
+ trb->generic.field[0] = 0;
+ trb->generic.field[1] = 0;
+ trb->generic.field[2] = 0;
+ trb->generic.field[3] = nop_cmd;
+ trb++;
+ } while (--usable);
+ ep_ring->enqueue = trb;
+ if (room_on_ring(xhci, ep_ring, num_trbs))
+ break;
+ }
if (ep_ring == xhci->cmd_ring) {
xhci_err(xhci, "Do not support expand command ring\n");
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 7454865ad1483..512ab162832cc 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1264,6 +1264,8 @@ typedef void (*usb_complete_t)(struct urb *);
* @sg: scatter gather buffer list, the buffer size of each element in
* the list (except the last) must be divisible by the endpoint's
* max packet size if no_sg_constraint isn't set in 'struct usb_bus'
+ * (FIXME: scatter-gather under xHCI is broken for periodic transfers.
+ * Do not use urb->sg for interrupt endpoints for now, only bulk.)
* @num_mapped_sgs: (internal) number of mapped sg entries
* @num_sgs: number of entries in the sg list
* @transfer_buffer_length: How big is transfer_buffer. The transfer may