> mt76_npu_dequeue() reads the slot count of a frame from the info word > of its first descriptor before checking the done bit of that slot. If > the NPU completes the slot in between, the count is still the 0 left > by the refill, so a frame spanning two slots is taken as a single slot > frame and its second slot is parsed as the start of the next frame. > With mt7996 this shows up as bogus tx-free events, which hit the > WARN_ON_ONCE() checks in mt7996_mac_tx_free(). From the code, such an > event can also release tokens that are still in use. > > Check the done bit of the first slot before reading the slot count, > and order the descriptor reads with dma_rmb(). Relying on the patch I shared for 1/2, I guess this is no more a problem since we will just delay the skb allocation. Agree or am I missing something? Regards, Lorenzo > > Fixes: 7fb554b1b623 ("wifi: mt76: Introduce the NPU generic layer") > Cc: stable@vger.kernel.org > Signed-off-by: Florian Maurer > --- > drivers/net/wireless/mediatek/mt76/npu.c | 9 ++++++++- > 1 file changed, 8 insertions(+), 1 deletion(-) > > diff --git a/drivers/net/wireless/mediatek/mt76/npu.c b/drivers/net/wireless/mediatek/mt76/npu.c > index 6e503a4a890c..f00ae683a7eb 100644 > --- a/drivers/net/wireless/mediatek/mt76/npu.c > +++ b/drivers/net/wireless/mediatek/mt76/npu.c > @@ -72,18 +72,25 @@ static struct sk_buff *mt76_npu_dequeue(struct mt76_dev *dev, > int i, nframes, index = q->tail; > struct sk_buff *skb = NULL; > > + if (!q->queued || > + !FIELD_GET(NPU_RX_DMA_DESC_DONE_MASK, desc[index].ctrl)) > + return NULL; > + > + /* The slot count is only valid once the first slot is complete */ > + dma_rmb(); > nframes = FIELD_GET(NPU_RX_DMA_PKT_COUNT_MASK, desc[index].info); > nframes = max_t(int, nframes, 1); > if (nframes > q->queued) > return NULL; > > /* Only take the frame once all of its slots are complete */ > - for (i = 0; i < nframes; i++) { > + for (i = 1; i < nframes; i++) { > int idx = (index + i) % q->ndesc; > > if (!FIELD_GET(NPU_RX_DMA_DESC_DONE_MASK, desc[idx].ctrl)) > return NULL; > } > + dma_rmb(); > > for (i = 0; i < nframes; i++) { > struct mt76_queue_entry *e = &q->entry[index]; > -- > 2.47.3 >