diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 78320ec125c4..df2adff6c33a 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -207,6 +207,8 @@ static void wil_print_sring(struct seq_file *s, struct wil6210_priv *wil, seq_puts(s, "???\n"); } seq_printf(s, " desc_rdy_pol = %d\n", sring->desc_rdy_pol); + seq_printf(s, " invalid_buff_id_cnt = %d\n", + sring->invalid_buff_id_cnt); if (sring->va && (sring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) { uint i; diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index 492f7bdf39d9..f6fce6ff73d9 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -29,6 +29,7 @@ #define WIL_EDMA_MAX_DATA_OFFSET (2) /* RX buffer size must be aligned to 4 bytes */ #define WIL_EDMA_RX_BUF_LEN_DEFAULT (2048) +#define MAX_INVALID_BUFF_ID_RETRY (3) static void wil_tx_desc_unmap_edma(struct device *dev, union wil_tx_desc *desc, @@ -312,7 +313,8 @@ static int wil_init_rx_buff_arr(struct wil6210_priv *wil, struct list_head *free = &wil->rx_buff_mgmt.free; int i; - wil->rx_buff_mgmt.buff_arr = kcalloc(size, sizeof(struct wil_rx_buff), + wil->rx_buff_mgmt.buff_arr = kcalloc(size + 1, + sizeof(struct wil_rx_buff), GFP_KERNEL); if (!wil->rx_buff_mgmt.buff_arr) return -ENOMEM; @@ -321,14 +323,16 @@ static int wil_init_rx_buff_arr(struct wil6210_priv *wil, INIT_LIST_HEAD(active); INIT_LIST_HEAD(free); - /* Linkify the list */ + /* Linkify the list. + * buffer id 0 should not be used (marks invalid id). + */ buff_arr = wil->rx_buff_mgmt.buff_arr; - for (i = 0; i < size; i++) { + for (i = 1; i <= size; i++) { list_add(&buff_arr[i].list, free); buff_arr[i].id = i; } - wil->rx_buff_mgmt.size = size; + wil->rx_buff_mgmt.size = size + 1; return 0; } @@ -876,26 +880,50 @@ again: /* Extract the buffer ID from the status message */ buff_id = le16_to_cpu(wil_rx_status_get_buff_id(msg)); - if (unlikely(!wil_val_in_range(buff_id, 0, wil->rx_buff_mgmt.size))) { - wil_err(wil, "Corrupt buff_id=%d, sring->swhead=%d\n", - buff_id, sring->swhead); - wil_sring_advance_swhead(sring); - goto again; + + while (!buff_id) { + struct wil_rx_status_extended *s; + int invalid_buff_id_retry = 0; + + wil_dbg_txrx(wil, + "buff_id is not updated yet by HW, (swhead 0x%x)\n", + sring->swhead); + if (++invalid_buff_id_retry > MAX_INVALID_BUFF_ID_RETRY) + break; + + /* Read the status message again */ + s = (struct wil_rx_status_extended *) + (sring->va + (sring->elem_size * sring->swhead)); + *(struct wil_rx_status_extended *)msg = *s; + buff_id = le16_to_cpu(wil_rx_status_get_buff_id(msg)); } - wil_sring_advance_swhead(sring); + if (unlikely(!wil_val_in_range(buff_id, 1, wil->rx_buff_mgmt.size))) { + wil_err(wil, "Corrupt buff_id=%d, sring->swhead=%d\n", + buff_id, sring->swhead); + wil_rx_status_reset_buff_id(sring); + wil_sring_advance_swhead(sring); + sring->invalid_buff_id_cnt++; + goto again; + } /* Extract the SKB from the rx_buff management array */ skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; wil->rx_buff_mgmt.buff_arr[buff_id].skb = NULL; if (!skb) { wil_err(wil, "No Rx skb at buff_id %d\n", buff_id); + wil_rx_status_reset_buff_id(sring); /* Move the buffer from the active list to the free list */ - list_move(&wil->rx_buff_mgmt.buff_arr[buff_id].list, - &wil->rx_buff_mgmt.free); + list_move_tail(&wil->rx_buff_mgmt.buff_arr[buff_id].list, + &wil->rx_buff_mgmt.free); + wil_sring_advance_swhead(sring); + sring->invalid_buff_id_cnt++; goto again; } + wil_rx_status_reset_buff_id(sring); + wil_sring_advance_swhead(sring); + memcpy(&pa, skb->cb, sizeof(pa)); dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); dmalen = le16_to_cpu(wil_rx_status_get_length(msg)); @@ -910,8 +938,8 @@ again: sizeof(struct wil_rx_status_extended), false); /* Move the buffer from the active list to the free list */ - list_move(&wil->rx_buff_mgmt.buff_arr[buff_id].list, - &wil->rx_buff_mgmt.free); + list_move_tail(&wil->rx_buff_mgmt.buff_arr[buff_id].list, + &wil->rx_buff_mgmt.free); eop = wil_rx_status_get_eop(msg); diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.h b/drivers/net/wireless/ath/wil6210/txrx_edma.h index 9cf9ecf6e8cf..bb4ff28b73e5 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.h +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.h @@ -427,6 +427,12 @@ static inline int wil_rx_status_get_eop(void *msg) /* EoP = End of Packet */ 30, 30); } +static inline void wil_rx_status_reset_buff_id(struct wil_status_ring *s) +{ + ((struct wil_rx_status_compressed *) + (s->va + (s->elem_size * s->swhead)))->buff_id = 0; +} + static inline __le16 wil_rx_status_get_buff_id(void *msg) { return ((struct wil_rx_status_compressed *)msg)->buff_id; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index f508d52c867a..8724d9975606 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -568,6 +568,7 @@ struct wil_status_ring { bool is_rx; u8 desc_rdy_pol; /* Expected descriptor ready bit polarity */ struct wil_ring_rx_data rx_data; + u32 invalid_buff_id_cnt; /* relevant only for RX */ }; #define WIL_STA_TID_NUM (16)