mt76: usb: fix rx A-MSDU support

Commit f8f527b16db5 ("mt76: usb: use EP max packet aligned buffer sizes
for rx") breaks A-MSDU support. When A-MSDU is enable the device can
receive frames up to q->buf_size but they will be discarded in
mt76u_process_rx_entry since there is no enough room for
skb_shared_info. Fix the issue reallocating the skb and copying in the
linear area the first 128B of the received frames and in the frag_list
the remaining part.

Fixes: f8f527b16db5 ("mt76: usb: use EP max packet aligned buffer sizes for rx")
Signed-off-by: Lorenzo Bianconi <[email protected]>
This commit is contained in:
Lorenzo Bianconi
2019-06-07 19:03:54 +02:00
committed by Felix Fietkau
parent 34eea1414b
commit 2bfae5a252
2 changed files with 44 additions and 9 deletions
+1
View File
@@ -30,6 +30,7 @@
#define MT_TX_RING_SIZE 256
#define MT_MCU_RING_SIZE 32
#define MT_RX_BUF_SIZE 2048
#define MT_SKB_HEAD_LEN 128
struct mt76_dev;
struct mt76_wcid;
+43 -9
View File
@@ -429,6 +429,48 @@ static int mt76u_get_rx_entry_len(u8 *data, u32 data_len)
return dma_len;
}
static struct sk_buff *
mt76u_build_rx_skb(u8 *data, int len, int buf_size,
int *nsgs)
{
int data_len = min(len, MT_SKB_HEAD_LEN);
struct sk_buff *skb;
if (SKB_WITH_OVERHEAD(buf_size) >= MT_DMA_HDR_LEN + len) {
/* fast path */
skb = build_skb(data, buf_size);
if (!skb)
return NULL;
skb_reserve(skb, MT_DMA_HDR_LEN);
__skb_put(skb, len);
return skb;
}
/* slow path, not enough space for data and
* skb_shared_info
*/
skb = alloc_skb(data_len, GFP_ATOMIC);
if (!skb)
return NULL;
skb_put_data(skb, data + MT_DMA_HDR_LEN, data_len);
data += (data_len + MT_DMA_HDR_LEN);
len -= data_len;
if (len > 0) {
struct page *page = virt_to_head_page(data);
int offset = data - (u8 *)page_address(page);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
page, offset, len, buf_size);
} else {
*nsgs = 0;
}
return skb;
}
static int
mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb)
{
@@ -446,19 +488,11 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb)
return 0;
data_len = min_t(int, len, data_len - MT_DMA_HDR_LEN);
if (MT_DMA_HDR_LEN + data_len > SKB_WITH_OVERHEAD(q->buf_size)) {
dev_err_ratelimited(dev->dev, "rx data too big %d\n", data_len);
return 0;
}
skb = build_skb(data, q->buf_size);
skb = mt76u_build_rx_skb(data, data_len, q->buf_size, &nsgs);
if (!skb)
return 0;
skb_reserve(skb, MT_DMA_HDR_LEN);
__skb_put(skb, data_len);
len -= data_len;
while (len > 0 && nsgs < urb->num_sgs) {
data_len = min_t(int, len, urb->sg[nsgs].length);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,