[RFC PATCH 14/20] xhci: power-of-2 ring sizes

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

 



In preparation for dynamic ring expansion while walking scatterlists for
v1.0+ xhci hosts force the ring sizes to be a power-of-2.  This property
combined with the conversion of segments to a doubly linked list allows
for translating ring pointers to integers enabling simple math to
interrogate the state of the ring.  Beyond determining the amount of
free space in the ring this enables easy to calculate answers to
questions like:

* whether a trb is free or allocated?
* which segment precedes the current segment?
* how many trbs before the enqueue pointer wraps into the same segment
  as the dequeue pointer?

The open coded "num_trbs alignment" statement in xhci_ring_expansion()
is replaced with the standard ALIGN macro.

Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx>
---
 drivers/usb/host/xhci-mem.c |   45 ++++++++++++++++++++++---------------------
 drivers/usb/host/xhci.c     |    2 +-
 drivers/usb/host/xhci.h     |    8 +++++++-
 3 files changed, 31 insertions(+), 24 deletions(-)

diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index b1ba9ec79c88..edaa49798172 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -117,7 +117,6 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
 	/* link them logically */
 	list_splice_init(segments, &insert_head->list);
 
-	ring->num_segs += num_segs;
 	ring->num_trbs_free += (TRBS_PER_SEGMENT - 1) * num_segs;
 
 	BUG_ON(xhci_is_event_ring(ring));
@@ -281,7 +280,7 @@ static void xhci_initialize_ring_info(struct xhci_ring *ring,
 	 * Each segment has a link TRB, and leave an extra TRB for SW
 	 * accounting purpose
 	 */
-	ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
+	ring->num_trbs_free = (1 << ring->order) * (TRBS_PER_SEGMENT - 1) - 1;
 }
 
 /* Allocate segments and link them for a ring */
@@ -324,7 +323,7 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
  * See section 4.9.1 and figures 15 and 16.
  */
 static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
-		unsigned int num_segs, unsigned int cycle_state,
+		unsigned int order, unsigned int cycle_state,
 		enum xhci_ring_type type, gfp_t flags)
 {
 	struct xhci_ring *ring;
@@ -335,13 +334,10 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
 		return NULL;
 
 	xhci_ring_init_type(xhci, ring, type);
-	ring->num_segs = num_segs;
+	ring->order = order;
 	INIT_LIST_HEAD(&ring->segments);
 	INIT_LIST_HEAD(&ring->td_list);
-	if (num_segs == 0)
-		return ring;
-
-	ret = xhci_alloc_segments_for_ring(xhci, &ring->segments, num_segs,
+	ret = xhci_alloc_segments_for_ring(xhci, &ring->segments, (1 << order),
 			cycle_state, ring->ops, flags);
 	if (ret)
 		goto fail;
@@ -425,17 +421,21 @@ static void xhci_reinit_cached_ring(struct xhci_hcd *xhci,
 int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
 				unsigned int num_trbs, gfp_t flags)
 {
-	unsigned int		num_segs;
-	unsigned int		num_segs_needed;
-	int			ret;
+	int ret;
 	LIST_HEAD(segments);
+	unsigned int num_segs, inc, base;
 
-	num_segs_needed = (num_trbs + (TRBS_PER_SEGMENT - 1) - 1) /
-				(TRBS_PER_SEGMENT - 1);
+	num_segs = ALIGN(num_trbs, TRBS_PER_SEGMENT) / TRBS_PER_SEGMENT;
 
-	/* Allocate number of segments we needed, or double the ring size */
-	num_segs = ring->num_segs > num_segs_needed ?
-			ring->num_segs : num_segs_needed;
+	/*
+	 * Increase the order to accommodate the number of new segments
+	 * needed
+	 */
+	inc = 1;
+	base = xhci_ring_num_segs(ring);
+	while (((1 << (ring->order + inc)) - base) < num_segs)
+		inc++;
+	num_segs = (1 << (ring->order + inc)) - base;
 
 	ret = xhci_alloc_segments_for_ring(xhci, &segments,
 			num_segs, ring->cycle_state, ring->ops, flags);
@@ -451,9 +451,10 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
 	}
 
 	xhci_link_rings(xhci, ring, &segments, num_segs);
+	ring->order = ilog2((1 << ring->order) + num_segs);
 	xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion,
 			"ring expansion succeed, now has %d segments",
-			ring->num_segs);
+			xhci_ring_num_segs(ring));
 
 	return 0;
 }
@@ -668,7 +669,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
 		struct xhci_segment *first_seg;
 
 		stream_info->stream_rings[cur_stream] =
-			xhci_ring_alloc(xhci, 2, 1, TYPE_STREAM, mem_flags);
+			xhci_ring_alloc(xhci, 1, 1, TYPE_STREAM, mem_flags);
 		cur_ring = stream_info->stream_rings[cur_stream];
 		if (!cur_ring)
 			goto cleanup_rings;
@@ -970,7 +971,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
 	}
 
 	/* Allocate endpoint 0 ring */
-	dev->eps[0].ring = xhci_ring_alloc(xhci, 2, 1, TYPE_CTRL, flags);
+	dev->eps[0].ring = xhci_ring_alloc(xhci, 1, 1, TYPE_CTRL, flags);
 	if (!dev->eps[0].ring)
 		goto fail;
 
@@ -1391,7 +1392,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
 	type = usb_endpoint_type(&ep->desc);
 	/* Set up the endpoint ring */
 	virt_dev->eps[ep_index].new_ring =
-		xhci_ring_alloc(xhci, 2, 1, type, mem_flags);
+		xhci_ring_alloc(xhci, 1, 1, type, mem_flags);
 	if (!virt_dev->eps[ep_index].new_ring) {
 		/* Attempt to use the ring cache */
 		if (virt_dev->num_rings_cached == 0)
@@ -2359,7 +2360,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
 		goto fail;
 
 	/* Set up the command ring to have one segments for now. */
-	xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, flags);
+	xhci->cmd_ring = xhci_ring_alloc(xhci, 0, 1, TYPE_COMMAND, flags);
 	if (!xhci->cmd_ring)
 		goto fail;
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init,
@@ -2404,7 +2405,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
 	 * the event ring segment table (ERST).  Section 4.9.3.
 	 */
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring");
-	xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
+	xhci->event_ring = xhci_ring_alloc(xhci, ERST_ORDER, 1, TYPE_EVENT,
 						flags);
 	if (!xhci->event_ring)
 		goto fail;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 082426a20e70..d365ada52130 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -830,8 +830,8 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci)
 	/* Reset the software enqueue and dequeue pointers */
 	xhci_ring_set_enqueue(ring, &enq);
 	xhci_ring_set_dequeue(ring, &enq);
+	ring->num_trbs_free = (1 << ring->order) * (TRBS_PER_SEGMENT - 1) - 1;
 
-	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 c685036cd2a6..c38b10b96898 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1348,7 +1348,7 @@ struct xhci_ring {
 	 */
 	u32			cycle_state;
 	unsigned int		stream_id;
-	unsigned int		num_segs;
+	unsigned int		order;
 	unsigned int		num_trbs_free;
 	unsigned int		num_trbs_free_temp;
 	bool			last_td_was_short;
@@ -1358,6 +1358,11 @@ struct xhci_ring {
 	const struct xhci_ring_ops *ops;
 };
 
+static inline unsigned int xhci_ring_num_segs(struct xhci_ring *ring)
+{
+	return 1 << ring->order;
+}
+
 static inline union xhci_trb *xhci_ring_enqueue(struct xhci_ring *ring)
 {
 	return ring->enq.ptr;
@@ -1457,6 +1462,7 @@ struct urb_priv {
  * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table,
  * meaning 64 ring segments.
  * Initial allocated size of the ERST, in number of entries */
+#define	ERST_ORDER	0
 #define	ERST_NUM_SEGS	1
 /* Initial allocated size of the ERST, in number of entries */
 #define	ERST_SIZE	64

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