Linux wireless drivers development
 help / color / mirror / Atom feed
From: Lorenzo Bianconi <lorenzo@kernel.org>
To: Sean Wang <sean.wang@kernel.org>
Cc: Felix Fietkau <nbd@nbd.name>,
	linux-wireless@vger.kernel.org,
	linux-mediatek@lists.infradead.org,
	Sean Wang <sean.wang@mediatek.com>
Subject: Re: [PATCH 2/5] wifi: mt76: usb: support out-of-order RX URB completion
Date: Mon, 15 Jun 2026 08:35:54 +0200	[thread overview]
Message-ID: <ai-dSgeYkT64NDob@lore-rh-laptop> (raw)
In-Reply-To: <20260613224655.2405686-3-sean.wang@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 5856 bytes --]

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

I guess you can move it at the beginning of mt76u_rx_urb_alloc(), right?

> +	if (!rxe) {
> +		usb_free_urb(e->urb);

IIRC, if mt76u_rx_urb_alloc() fails, mt76u_free_rx_queue() will run so we do
not need to run usb_free_urb() manually here, right?

> +		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"))

can idx be >= of q->ndesc?

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

This seems a fix to me since theoretically we could have the same issue in
the current codebase, right?

Regards,
Lorenzo

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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

  reply	other threads:[~2026-06-15  6:35 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 ` [PATCH 2/5] wifi: mt76: usb: support out-of-order RX URB completion Sean Wang
2026-06-15  6:35   ` Lorenzo Bianconi [this message]
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=ai-dSgeYkT64NDob@lore-rh-laptop \
    --to=lorenzo@kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=nbd@nbd.name \
    --cc=sean.wang@kernel.org \
    --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