Commit 2710f818 authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman
Browse files

xhci: Stop unnecessary tracking of free trbs in a ring



Trying to keep track of free trbs in a ring by adding and subtracting
deltas each time a enqueue or dequeue is increased or moved has proven to
be buggy and complicated, especially over long periods of time.

Recently a bug in counting free trbs was fixed, now taking into account
cancelled URBs that were turned into no-ops, preventing free_trbs to
slowly wander off causing unnecessary ring expansion. See
commit fe82f16a ("xhci: Fix incorrect tracking of free space on
transfer rings")

Turns out its a lot easier to just calculate the numer of free TRB
based on ring size and the current enqueue and dequeue pointer values.
This is currently only needed for the command ring as multi segment
transfer rings already ensures there is enough room the ring during
the ring expansion check.

We could get rid of the ring->num_trbs_free entry completely, but as
the xhci DbC code also uses it we don't clean that up in this patch.

Reported-by: default avatarMiller Hunter <MillerH@hearthnhome.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217242


Tested-by: default avatarMiller Hunter <MillerH@hearthnhome.com>
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Message-ID: <20230602144009.1225632-8-mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent f5af638f
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -143,7 +143,6 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
	xhci_link_segments(ring->enq_seg, first, ring->type, chain_links);
	xhci_link_segments(last, next, ring->type, chain_links);
	ring->num_segs += num_segs;
	ring->num_trbs_free += (TRBS_PER_SEGMENT - 1) * num_segs;

	if (ring->type != TYPE_EVENT && ring->enq_seg == ring->last_seg) {
		ring->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control
+33 −42
Original line number Diff line number Diff line
@@ -174,12 +174,10 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)

	/* All other rings have link trbs */
	if (!trb_is_link(ring->dequeue)) {
		if (last_trb_on_seg(ring->deq_seg, ring->dequeue)) {
		if (last_trb_on_seg(ring->deq_seg, ring->dequeue))
			xhci_warn(xhci, "Missing link TRB at end of segment\n");
		} else {
		else
			ring->dequeue++;
			ring->num_trbs_free++;
		}
	}

	while (trb_is_link(ring->dequeue)) {
@@ -221,9 +219,6 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
	unsigned int link_trb_count = 0;

	chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
	/* If this is not event ring, there is one less usable TRB */
	if (!trb_is_link(ring->enqueue))
		ring->num_trbs_free--;

	if (last_trb_on_seg(ring->enq_seg, ring->enqueue)) {
		xhci_err(xhci, "Tried to move enqueue past ring segment\n");
@@ -276,24 +271,40 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
	trace_xhci_inc_enq(ring);
}

static int xhci_num_trbs_to(struct xhci_segment *start_seg, union xhci_trb *start,
			    struct xhci_segment *end_seg, union xhci_trb *end,
			    unsigned int num_segs)
/*
 * Return number of free normal TRBs from enqueue to dequeue pointer on ring.
 * Not counting an assumed link TRB at end of each TRBS_PER_SEGMENT sized segment.
 * Only for transfer and command rings where driver is the producer, not for
 * event rings.
 */
static unsigned int xhci_num_trbs_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
{
	struct xhci_segment *enq_seg = ring->enq_seg;
	union xhci_trb *enq = ring->enqueue;
	union xhci_trb *last_on_seg;
	int num = 0;
	unsigned int free = 0;
	int i = 0;

	/* Ring might be empty even if enq != deq if enq is left on a link trb */
	if (trb_is_link(enq)) {
		enq_seg = enq_seg->next;
		enq = enq_seg->trbs;
	}

	/* Empty ring, common case, don't walk the segments */
	if (enq == ring->dequeue)
		return ring->num_segs * (TRBS_PER_SEGMENT - 1);

	do {
		if (start_seg == end_seg && end >= start)
			return num + (end - start);
		last_on_seg = &start_seg->trbs[TRBS_PER_SEGMENT - 1];
		num += last_on_seg - start;
		start_seg = start_seg->next;
		start = start_seg->trbs;
	} while (i++ <= num_segs);
		if (ring->deq_seg == enq_seg && ring->dequeue >= enq)
			return free + (ring->dequeue - enq);
		last_on_seg = &enq_seg->trbs[TRBS_PER_SEGMENT - 1];
		free += last_on_seg - enq;
		enq_seg = enq_seg->next;
		enq = enq_seg->trbs;
	} while (i++ <= ring->num_segs);

	return -EINVAL;
	return free;
}

/*
@@ -1291,10 +1302,7 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
		unsigned int ep_index)
{
	union xhci_trb *dequeue_temp;
	int num_trbs_free_temp;
	bool revert = false;

	num_trbs_free_temp = ep_ring->num_trbs_free;
	dequeue_temp = ep_ring->dequeue;

	/* If we get two back-to-back stalls, and the first stalled transfer
@@ -1310,7 +1318,6 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,

	while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
		/* We have more usable TRBs */
		ep_ring->num_trbs_free++;
		ep_ring->dequeue++;
		if (trb_is_link(ep_ring->dequeue)) {
			if (ep_ring->dequeue ==
@@ -1320,15 +1327,10 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
			ep_ring->dequeue = ep_ring->deq_seg->trbs;
		}
		if (ep_ring->dequeue == dequeue_temp) {
			revert = true;
			xhci_dbg(xhci, "Unable to find new dequeue pointer\n");
			break;
		}
	}

	if (revert) {
		xhci_dbg(xhci, "Unable to find new dequeue pointer\n");
		ep_ring->num_trbs_free = num_trbs_free_temp;
	}
}

/*
@@ -2183,7 +2185,6 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
		     u32 trb_comp_code)
{
	struct xhci_ep_ctx *ep_ctx;
	int trbs_freed;

	ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index);

@@ -2253,13 +2254,6 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
	}

	/* Update ring dequeue pointer */
	trbs_freed = xhci_num_trbs_to(ep_ring->deq_seg, ep_ring->dequeue,
				      td->last_trb_seg, td->last_trb,
				      ep_ring->num_segs);
	if (trbs_freed < 0)
		xhci_dbg(xhci, "Failed to count freed trbs at TD finish\n");
	else
		ep_ring->num_trbs_free += trbs_freed;
	ep_ring->dequeue = td->last_trb;
	ep_ring->deq_seg = td->last_trb_seg;
	inc_deq(xhci, ep_ring);
@@ -2483,7 +2477,6 @@ static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
	/* Update ring dequeue pointer */
	ep->ring->dequeue = td->last_trb;
	ep->ring->deq_seg = td->last_trb_seg;
	ep->ring->num_trbs_free += td->num_trbs - 1;
	inc_deq(xhci, ep->ring);

	return xhci_td_cleanup(xhci, td, ep->ring, status);
@@ -3227,7 +3220,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,

	if (ep_ring != xhci->cmd_ring) {
		new_segs = xhci_ring_expansion_needed(xhci, ep_ring, num_trbs);
	} else if (ep_ring->num_trbs_free <= num_trbs) {
	} else if (xhci_num_trbs_free(xhci, ep_ring) <= num_trbs) {
		xhci_err(xhci, "Do not support expand command ring\n");
		return -ENOMEM;
	}
@@ -4205,7 +4198,6 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
	ep_ring->enqueue = urb_priv->td[0].first_trb;
	ep_ring->enq_seg = urb_priv->td[0].start_seg;
	ep_ring->cycle_state = start_cycle;
	ep_ring->num_trbs_free = ep_ring->num_trbs_free_temp;
	usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
	return ret;
}
@@ -4287,7 +4279,6 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
	}

skip_start_over:
	ep_ring->num_trbs_free_temp = ep_ring->num_trbs_free;

	return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index);
}
+1 −4
Original line number Diff line number Diff line
@@ -458,7 +458,6 @@ DECLARE_EVENT_CLASS(xhci_log_ring,
		__field(unsigned int, num_segs)
		__field(unsigned int, stream_id)
		__field(unsigned int, cycle_state)
		__field(unsigned int, num_trbs_free)
		__field(unsigned int, bounce_buf_len)
	),
	TP_fast_assign(
@@ -469,18 +468,16 @@ DECLARE_EVENT_CLASS(xhci_log_ring,
		__entry->enq_seg = ring->enq_seg->dma;
		__entry->deq_seg = ring->deq_seg->dma;
		__entry->cycle_state = ring->cycle_state;
		__entry->num_trbs_free = ring->num_trbs_free;
		__entry->bounce_buf_len = ring->bounce_buf_len;
		__entry->enq = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue);
		__entry->deq = xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue);
	),
	TP_printk("%s %p: enq %pad(%pad) deq %pad(%pad) segs %d stream %d free_trbs %d bounce %d cycle %d",
	TP_printk("%s %p: enq %pad(%pad) deq %pad(%pad) segs %d stream %d bounce %d cycle %d",
			xhci_ring_type_string(__entry->type), __entry->ring,
			&__entry->enq, &__entry->enq_seg,
			&__entry->deq, &__entry->deq_seg,
			__entry->num_segs,
			__entry->stream_id,
			__entry->num_trbs_free,
			__entry->bounce_buf_len,
			__entry->cycle_state
		)
+1 −2
Original line number Diff line number Diff line
@@ -1633,8 +1633,7 @@ struct xhci_ring {
	u32			cycle_state;
	unsigned int		stream_id;
	unsigned int		num_segs;
	unsigned int		num_trbs_free;
	unsigned int		num_trbs_free_temp;
	unsigned int		num_trbs_free; /* used only by xhci DbC */
	unsigned int		bounce_buf_len;
	enum xhci_ring_type	type;
	bool			last_td_was_short;