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: 8+ 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-13 22:46 ` [PATCH 3/5] wifi: mt76: usb: add optional RX aggregation support Sean Wang
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-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox