[RFC v2 2/7] xHCI: count free TRBs on transfer ring

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

 



In the past, the room_on_ring() check was implemented by walking all over
the ring, which is wasteful and complicated.

Count the number of free TRBs instead. The free TRBs number should be
updated when enqueue/dequeue pointer is updated, or upon the completion
of a set dequeue pointer command.

Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx>
---
 drivers/usb/host/xhci-mem.c  |    6 +++
 drivers/usb/host/xhci-ring.c |   72 +++++++++++++++--------------------------
 drivers/usb/host/xhci.c      |    1 +
 drivers/usb/host/xhci.h      |    1 +
 4 files changed, 34 insertions(+), 46 deletions(-)

diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index e794c87..98338a7 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -169,6 +169,12 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
 	if (num_segs == 0)
 		return ring;
 
+	/* Each segment has a link TRB, and leave an extra TRB for SW
+	 * accounting purpose
+	 */
+
+	ring->num_trbs_free = num_segs * (TRBS_PER_SEGMENT - 1) - 1;
+
 	ring->first_seg = xhci_segment_alloc(xhci, flags);
 	if (!ring->first_seg)
 		goto fail;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index e4b7f00..f914c2d 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -145,10 +145,16 @@ static void next_trb(struct xhci_hcd *xhci,
  */
 static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer)
 {
-	union xhci_trb *next = ++(ring->dequeue);
+	union xhci_trb *next;
 	unsigned long long addr;
 
 	ring->deq_updates++;
+
+	/* If software is the producer, there is one more usable TRB */
+	if (!consumer && !last_trb(xhci, ring, ring->deq_seg, ring->dequeue))
+		ring->num_trbs_free++;
+	next = ++(ring->dequeue);
+
 	/* Update the dequeue pointer further if that was a link TRB or we're at
 	 * the end of an event ring segment (which doesn't have link TRBS)
 	 */
@@ -192,6 +198,9 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
 	unsigned long long addr;
 
 	chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
+	/* If software is the producer, there is one less usable TRB */
+	if (!consumer && !last_trb(xhci, ring, ring->enq_seg, ring->enqueue))
+		ring->num_trbs_free--;
 	next = ++(ring->enqueue);
 
 	ring->enq_updates++;
@@ -247,54 +256,14 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
 /*
  * Check to see if there's room to enqueue num_trbs on the ring.  See rules
  * above.
- * FIXME: this would be simpler and faster if we just kept track of the number
- * of free TRBs in a ring.
  */
 static int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
 		unsigned int num_trbs)
 {
-	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;
-
-	/* If we are currently pointing to a link TRB, advance the
-	 * enqueue pointer before checking for space */
-	while (last_trb(xhci, ring, enq_seg, enq)) {
-		enq_seg = enq_seg->next;
-		enq = enq_seg->trbs;
-	}
-
-	/* Check if ring is empty */
-	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;
-		}
+	if (ring->num_trbs_free >= num_trbs)
 		return 1;
-	}
-	/* Make sure there's an extra empty TRB available */
-	for (i = 0; i <= num_trbs; ++i) {
-		if (enq == ring->dequeue)
-			return 0;
-		enq++;
-		while (last_trb(xhci, ring, enq_seg, enq)) {
-			enq_seg = enq_seg->next;
-			enq = enq_seg->trbs;
-		}
-	}
-	return 1;
+
+	return 0;
 }
 
 /* Ring the host controller doorbell after placing a command on the ring */
@@ -983,8 +952,19 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
 			/* Update the ring's dequeue segment and dequeue pointer
 			 * to reflect the new position.
 			 */
-			ep_ring->deq_seg = dev->eps[ep_index].queued_deq_seg;
-			ep_ring->dequeue = dev->eps[ep_index].queued_deq_ptr;
+			while (ep_ring->dequeue !=
+					dev->eps[ep_index].queued_deq_ptr) {
+				/* We have more usable TRBs */
+				ep_ring->num_trbs_free++;
+				ep_ring->dequeue++;
+				while (last_trb(xhci, ep_ring, ep_ring->deq_seg,
+						ep_ring->dequeue)) {
+					ep_ring->deq_seg =
+						ep_ring->deq_seg->next;
+					ep_ring->dequeue =
+						ep_ring->deq_seg->trbs;
+				}
+			}
 		} else {
 			xhci_warn(xhci, "Mismatch between completed Set TR Deq "
 					"Ptr command & xHCI internal state.\n");
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 1ff95a0..e70ddf2 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -721,6 +721,7 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci)
 	ring->enq_seg = ring->deq_seg;
 	ring->enqueue = ring->dequeue;
 
+	ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
 	/*
 	 * Ring is now zeroed, so the HW should look for change of ownership
 	 * when the cycle bit is set to 1.
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index a7f3d23..981dd83f 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1272,6 +1272,7 @@ struct xhci_ring {
 	u32			cycle_state;
 	unsigned int		stream_id;
 	unsigned int		num_segs;
+	unsigned int		num_trbs_free;
 	bool			last_td_was_short;
 };
 
-- 
1.7.4.1


--
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