From: Sean Wang <sean.wang@kernel.org>
To: Felix Fietkau <nbd@nbd.name>, Lorenzo Bianconi <lorenzo@kernel.org>
Cc: linux-wireless@vger.kernel.org,
linux-mediatek@lists.infradead.org,
Sean Wang <sean.wang@mediatek.com>
Subject: [PATCH 2/5] wifi: mt76: usb: support out-of-order RX URB completion
Date: Sat, 13 Jun 2026 17:46:52 -0500 [thread overview]
Message-ID: <20260613224655.2405686-3-sean.wang@kernel.org> (raw)
In-Reply-To: <20260613224655.2405686-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Keep per-URB RX queue context and complete entries by their real queue
position instead of assuming the completed URB is always at q->head.
USB RX URBs can complete out of order, and advancing q->head too early
can corrupt RX queue accounting and process buffers in the wrong order.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt76.h | 5 ++
drivers/net/wireless/mediatek/mt76/usb.c | 77 ++++++++++++++++-------
2 files changed, 61 insertions(+), 21 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 122e77a5f2f4..81740aa7df71 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -655,6 +655,11 @@ struct mt76_mcu {
wait_queue_head_t wait;
};
+struct mt76u_rx_entry {
+ struct mt76_queue_entry *e;
+ struct mt76_queue *q;
+};
+
#define MT_TX_SG_MAX_SIZE 8
#define MT_RX_SG_MAX_SIZE 4
#define MT_NUM_TX_ENTRIES 256
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index ce68e1d0c786..cab36630c978 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -397,11 +397,25 @@ mt76u_urb_alloc(struct mt76_dev *dev, struct mt76_queue_entry *e,
return 0;
}
+static void mt76u_urb_free(struct urb *urb)
+{
+ int i;
+
+ for (i = 0; i < urb->num_sgs; i++)
+ mt76_put_page_pool_buf(sg_virt(&urb->sg[i]), false);
+
+ if (urb->transfer_buffer)
+ mt76_put_page_pool_buf(urb->transfer_buffer, false);
+
+ usb_free_urb(urb);
+}
+
static int
mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q,
struct mt76_queue_entry *e)
{
enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN];
+ struct mt76u_rx_entry *rxe;
int err, sg_size;
sg_size = qid == MT_RXQ_MAIN ? MT_RX_SG_MAX_SIZE : 0;
@@ -409,20 +423,25 @@ mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q,
if (err)
return err;
- return mt76u_refill_rx(dev, q, e->urb, sg_size);
-}
-
-static void mt76u_urb_free(struct urb *urb)
-{
- int i;
+ rxe = kzalloc_obj(*rxe, GFP_KERNEL);
+ if (!rxe) {
+ usb_free_urb(e->urb);
+ e->urb = NULL;
+ return -ENOMEM;
+ }
- for (i = 0; i < urb->num_sgs; i++)
- mt76_put_page_pool_buf(sg_virt(&urb->sg[i]), false);
+ rxe->e = e;
+ rxe->q = q;
+ e->urb->context = rxe;
- if (urb->transfer_buffer)
- mt76_put_page_pool_buf(urb->transfer_buffer, false);
+ err = mt76u_refill_rx(dev, q, e->urb, sg_size);
+ if (err) {
+ kfree(rxe);
+ mt76u_urb_free(e->urb);
+ e->urb = NULL;
+ }
- usb_free_urb(urb);
+ return err;
}
static void
@@ -566,8 +585,12 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb,
static void mt76u_complete_rx(struct urb *urb)
{
struct mt76_dev *dev = dev_get_drvdata(&urb->dev->dev);
- struct mt76_queue *q = urb->context;
+ struct mt76u_rx_entry *rxe = urb->context;
+ struct mt76_queue_entry *e = rxe->e;
+ unsigned int idx, pending, pos;
+ struct mt76_queue *q = rxe->q;
unsigned long flags;
+ bool wake = false;
trace_rx_urb(dev, urb);
@@ -586,18 +609,28 @@ static void mt76u_complete_rx(struct urb *urb)
}
spin_lock_irqsave(&q->lock, flags);
- if (WARN_ONCE(q->entry[q->head].urb != urb, "rx urb mismatch"))
+ idx = e - q->entry;
+ pending = q->ndesc - q->queued;
+ pos = (idx + q->ndesc - q->head) % q->ndesc;
+ if (WARN_ONCE(idx >= q->ndesc || pos >= pending, "rx urb mismatch"))
goto out;
- q->head = (q->head + 1) % q->ndesc;
- q->queued++;
-
- if (q == &dev->q_rx[MT_RXQ_MAIN])
- napi_schedule(&dev->napi[MT_RXQ_MAIN]);
- else
- mt76_worker_schedule(&dev->usb.rx_worker);
+ e->done = true;
+ while (q->entry[q->head].done) {
+ q->entry[q->head].done = false;
+ q->head = (q->head + 1) % q->ndesc;
+ q->queued++;
+ wake = true;
+ }
out:
spin_unlock_irqrestore(&q->lock, flags);
+
+ if (wake) {
+ if (q == &dev->q_rx[MT_RXQ_MAIN])
+ napi_schedule(&dev->napi[MT_RXQ_MAIN]);
+ else
+ mt76_worker_schedule(&dev->usb.rx_worker);
+ }
}
static int
@@ -607,7 +640,7 @@ mt76u_submit_rx_buf(struct mt76_dev *dev, enum mt76_rxq_id qid,
int ep = qid == MT_RXQ_MAIN ? MT_EP_IN_PKT_RX : MT_EP_IN_CMD_RESP;
mt76u_fill_bulk_urb(dev, USB_DIR_IN, ep, urb,
- mt76u_complete_rx, &dev->q_rx[qid]);
+ mt76u_complete_rx, urb->context);
trace_submit_urb(dev, urb);
return usb_submit_urb(urb, GFP_ATOMIC);
@@ -678,6 +711,7 @@ mt76u_submit_rx_buffers(struct mt76_dev *dev, enum mt76_rxq_id qid)
spin_lock_irqsave(&q->lock, flags);
for (i = 0; i < q->ndesc; i++) {
+ q->entry[i].done = false;
err = mt76u_submit_rx_buf(dev, qid, q->entry[i].urb);
if (err < 0)
break;
@@ -733,6 +767,7 @@ mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
if (!q->entry[i].urb)
continue;
+ kfree(q->entry[i].urb->context);
mt76u_urb_free(q->entry[i].urb);
q->entry[i].urb = NULL;
}
--
2.43.0
next prev parent reply other threads:[~2026-06-13 22:47 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-13 22:46 [PATCH 0/5] wifi: mt76: add USB RX aggregation support Sean Wang
2026-06-13 22:46 ` [PATCH 1/5] wifi: mt76: usb: size RX page-pool pages from queue buffer Sean Wang
2026-06-14 9:28 ` Lorenzo Bianconi
2026-06-13 22:46 ` Sean Wang [this message]
2026-06-15 6:35 ` [PATCH 2/5] wifi: mt76: usb: support out-of-order RX URB completion Lorenzo Bianconi
2026-06-13 22:46 ` [PATCH 3/5] wifi: mt76: usb: add optional RX aggregation support Sean Wang
2026-06-15 7:08 ` Lorenzo Bianconi
2026-06-13 22:46 ` [PATCH 4/5] wifi: mt76: usb: add debugfs aggregation stats Sean Wang
2026-06-13 22:46 ` [PATCH 5/5] wifi: mt76: mt7927u: enable USB RX aggregation Sean Wang
2026-06-15 7:10 ` Lorenzo Bianconi
2026-06-14 9:26 ` [PATCH 0/5] wifi: mt76: add USB RX aggregation support Lorenzo Bianconi
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=20260613224655.2405686-3-sean.wang@kernel.org \
--to=sean.wang@kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=linux-wireless@vger.kernel.org \
--cc=lorenzo@kernel.org \
--cc=nbd@nbd.name \
--cc=sean.wang@mediatek.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.