From: Michal Pecio <michal.pecio@gmail.com>
To: Mathias Nyman <mathias.nyman@intel.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 2/3] usb: xhci: Simplify moving HW Dequeue Pointer past cancelled TDs
Date: Thu, 20 Feb 2025 23:46:08 +0100 [thread overview]
Message-ID: <20250220234608.6c237c3c@foxbook> (raw)
In-Reply-To: <20250220234355.2386cb6d@foxbook>
xhci_move_dequeue_past_td() uses a relatively complex and inefficient
procedure to find new dequeue position after the cancelled TD.
Replace it with a simpler function which moves dequeue immediately to
the first pending TD, or to enqueue if the ring is empty.
The outcome should be basically equivalent, because we only clear xHC
cache if it stopped or halted on some cancelled TD and moving past the
TD effectively means moving to the first remaining TD, if any.
If the cancelled TD is followed by more cancelled TDs turned into No-
Ops, we will now jump over them and save the xHC some work.
Signed-off-by: Michal Pecio <michal.pecio@gmail.com>
---
drivers/usb/host/xhci-ring.c | 64 ++++++++++--------------------------
1 file changed, 18 insertions(+), 46 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index c983d22842dc..46ca98066856 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -635,9 +635,9 @@ static u64 xhci_get_hw_deq(struct xhci_hcd *xhci, struct xhci_virt_device *vdev,
return le64_to_cpu(ep_ctx->deq);
}
-static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
- unsigned int slot_id, unsigned int ep_index,
- unsigned int stream_id, struct xhci_td *td)
+/* Move HW dequeue to the first pending TD or to our enqueue if there are no TDs */
+static int set_ring_dequeue(struct xhci_hcd *xhci, unsigned int slot_id,
+ unsigned int ep_index, unsigned int stream_id)
{
struct xhci_virt_device *dev = xhci->devs[slot_id];
struct xhci_virt_ep *ep = &dev->eps[ep_index];
@@ -645,58 +645,31 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
struct xhci_command *cmd;
struct xhci_segment *new_seg;
union xhci_trb *new_deq;
+ struct xhci_td *td;
int new_cycle;
dma_addr_t addr;
- u64 hw_dequeue;
- bool cycle_found = false;
- bool td_last_trb_found = false;
u32 trb_sct = 0;
int ret;
- ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id,
- ep_index, stream_id);
+ ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id, ep_index, stream_id);
+
if (!ep_ring) {
xhci_warn(xhci, "WARN can't find new dequeue, invalid stream ID %u\n",
stream_id);
return -ENODEV;
}
- hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id);
- new_seg = ep_ring->deq_seg;
- new_deq = ep_ring->dequeue;
- new_cycle = hw_dequeue & 0x1;
+ if (!list_empty(&ep_ring->td_list)) {
+ td = list_first_entry(&ep_ring->td_list, struct xhci_td, td_list);
+ new_seg = td->start_seg;
+ new_deq = td->start_trb;
+ new_cycle = le32_to_cpu(new_deq->generic.field[3]) & TRB_CYCLE;
+ } else {
+ new_seg = ep_ring->enq_seg;
+ new_deq = ep_ring->enqueue;
+ new_cycle = ep_ring->cycle_state;
+ }
- /*
- * We want to find the pointer, segment and cycle state of the new trb
- * (the one after current TD's end_trb). We know the cycle state at
- * hw_dequeue, so walk the ring until both hw_dequeue and end_trb are
- * found.
- */
- do {
- if (!cycle_found && xhci_trb_virt_to_dma(new_seg, new_deq)
- == (dma_addr_t)(hw_dequeue & ~0xf)) {
- cycle_found = true;
- if (td_last_trb_found)
- break;
- }
- if (new_deq == td->end_trb)
- td_last_trb_found = true;
-
- if (cycle_found && trb_is_link(new_deq) &&
- link_trb_toggles_cycle(new_deq))
- new_cycle ^= 0x1;
-
- next_trb(&new_seg, &new_deq);
-
- /* Search wrapped around, bail out */
- if (new_deq == ep->ring->dequeue) {
- xhci_err(xhci, "Error: Failed finding new dequeue state\n");
- return -EINVAL;
- }
-
- } while (!cycle_found || !td_last_trb_found);
-
- /* Don't update the ring cycle state for the producer (us). */
addr = xhci_trb_virt_to_dma(new_seg, new_deq);
if (addr == 0) {
xhci_warn(xhci, "Can't find dma of new dequeue ptr\n");
@@ -1060,9 +1033,8 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
if (!cached_td)
return 0;
- err = xhci_move_dequeue_past_td(xhci, slot_id, ep->ep_index,
- cached_td->urb->stream_id,
- cached_td);
+ err = set_ring_dequeue(xhci, slot_id, ep->ep_index, cached_td->urb->stream_id);
+
if (err) {
/* Failed to move past cached td, just set cached TDs to no-op */
list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) {
--
2.48.1
next prev parent reply other threads:[~2025-02-20 22:46 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-02-20 22:43 [PATCH 0/3] xhci: ring queuing cleanups Michal Pecio
2025-02-20 22:44 ` [PATCH 1/3] usb: xhci: Simplify update_ring_for_set_deq_completion() Michal Pecio
2025-02-21 13:23 ` Mathias Nyman
2025-03-05 8:24 ` Michał Pecio
2025-02-20 22:46 ` Michal Pecio [this message]
2025-02-20 22:47 ` [PATCH 3/3] usb: xhci: Unify duplicate inc_enq() code Michal Pecio
2025-02-21 14:54 ` Mathias Nyman
2025-02-23 23:45 ` Michał Pecio
2025-02-24 11:49 ` Mathias Nyman
2025-02-24 21:01 ` Michał Pecio
2025-02-25 9:33 ` Mathias Nyman
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250220234608.6c237c3c@foxbook \
--to=michal.pecio@gmail.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=mathias.nyman@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox