[PATCH v3 3/9] xhci: Use a fast upper bound for the number of TRB for a SG request

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



prepare_transfer() only needs an upper bound for the number of TRB required.
For scatter-gather requests this can be calculated cheaply.
However a request with a lot of fragments, or for a long transfer,
might then get bounced because the number of fragments exceeds the
size of a ring segment (and we can only add LINK TRB at aligned offsets)
If the upper bound won't fit in the current segment, then calculate
the actual number or fragments (more efficiently than before).

Ignore zero-length non-terminal fragments.

Signed-off-by: David Laight <david.laight@xxxxxxxxxx>
---
 drivers/usb/host/xhci-ring.c | 64 +++++++++++++++++++++++++++-----------------
 1 file changed, 39 insertions(+), 25 deletions(-)

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index b60932b..a9cf058 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3117,37 +3117,48 @@ static int prepare_transfer(struct xhci_hcd *xhci,
 	return 0;
 }
 
-static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb)
+static unsigned int count_sg_trbs_needed(struct xhci_ring *ep_ring,
+		struct urb *urb)
 {
-	int num_sgs, num_trbs, running_total, temp, i;
+	unsigned int num_trbs, len_left, i;
 	struct scatterlist *sg;
 
-	sg = NULL;
-	num_sgs = urb->num_mapped_sgs;
-	temp = urb->transfer_buffer_length;
+	/*
+	 * This code only needs an upper bound for the number of trbs
+	 * which we can calculate cheaply.
+	 *
+	 * However if the quick calculation exceeds the size of a
+	 * ring segment the request would be bounced later.
+	 */
+
+	num_trbs = urb->num_mapped_sgs * 2;
+	num_trbs += urb->transfer_buffer_length / TRB_MAX_BUFF_SIZE;
+
+	/* See if the request fits in the space in the ring segment */
+	if (ep_ring->enqueue + num_trbs <
+			ep_ring->enq_seg->trbs + TRBS_PER_SEGMENT)
+		return num_trbs;
 
+	/*
+	 * Calculate the actual number of fragments needed, this might
+	 * fit in the current ring segment, and is needed if the
+	 * upper bound exceeds the ring size.
+	 */
 	num_trbs = 0;
-	for_each_sg(urb->sg, sg, num_sgs, i) {
-		unsigned int len = sg_dma_len(sg);
-
-		/* Scatter gather list entries may cross 64KB boundaries */
-		running_total = TRB_MAX_BUFF_SIZE -
-			(sg_dma_address(sg) & (TRB_MAX_BUFF_SIZE - 1));
-		running_total &= TRB_MAX_BUFF_SIZE - 1;
-		if (running_total != 0)
-			num_trbs++;
-
-		/* How many more 64KB chunks to transfer, how many more TRBs? */
-		while (running_total < sg_dma_len(sg) && running_total < temp) {
-			num_trbs++;
-			running_total += TRB_MAX_BUFF_SIZE;
-		}
-		len = min_t(int, len, temp);
-		temp -= len;
-		if (temp == 0)
+	len_left = urb->transfer_buffer_length;
+	for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) {
+		unsigned int len;
+
+		/* 1 TRB + 1 for each 64k boundary crossed */
+		len = min_t(unsigned int, sg_dma_len(sg), len_left);
+		num_trbs += DIV_ROUND_UP(len + (sg_dma_address(sg) &
+				(TRB_MAX_BUFF_SIZE - 1)), TRB_MAX_BUFF_SIZE);
+
+		len_left -= len;
+		if (len_left == 0)
 			break;
 	}
-	return num_trbs;
+	return num_trbs == 0 ? 1 : num_trbs;
 }
 
 static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id,
@@ -3276,7 +3287,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 	if (!ep_ring)
 		return -EINVAL;
 
-	num_trbs = count_sg_trbs_needed(xhci, urb);
+	num_trbs = count_sg_trbs_needed(ep_ring, urb);
 	num_sgs = urb->num_mapped_sgs;
 	total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length,
 			usb_endpoint_maxp(&urb->ep->desc));
@@ -3334,6 +3345,8 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 		 * TRB to indicate it's the last TRB in the chain.
 		 */
 		if (trb_buff_len != len_left) {
+			if (trb_buff_len == 0)
+				goto next_frag;
 			field |= TRB_CHAIN;
 		} else {
 			/* FIXME - add check for ZERO_PACKET flag before this */
@@ -3375,6 +3388,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 		 * Are we done queueing all the TRBs for this sg entry?
 		 */
 		this_sg_len -= trb_buff_len;
+next_frag:
 		if (this_sg_len == 0) {
 			--num_sgs;
 			sg = sg_next(sg);
-- 
1.8.1.2



--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux