diff options
Diffstat (limited to 'drivers/net/wireless/ath/wil6210/txrx_edma.c')
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/txrx_edma.c | 185 |
1 files changed, 110 insertions, 75 deletions
diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index 3380aaef456c..1ba1f21ebea2 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -1,17 +1,6 @@ +// SPDX-License-Identifier: ISC /* - * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. */ #include <linux/etherdevice.h> @@ -26,9 +15,14 @@ #include "txrx.h" #include "trace.h" +/* Max number of entries (packets to complete) to update the hwtail of tx + * status ring. Should be power of 2 + */ +#define WIL_EDMA_TX_SRING_UPDATE_HW_TAIL 128 #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, @@ -153,9 +147,7 @@ out_free: return rc; } -/** - * Allocate one skb for Rx descriptor RING - */ +/* Allocate one skb for Rx descriptor RING */ static int wil_ring_alloc_skb_edma(struct wil6210_priv *wil, struct wil_ring *ring, u32 i) { @@ -216,10 +208,17 @@ static int wil_ring_alloc_skb_edma(struct wil6210_priv *wil, } static inline -void wil_get_next_rx_status_msg(struct wil_status_ring *sring, void *msg) +void wil_get_next_rx_status_msg(struct wil_status_ring *sring, u8 *dr_bit, + void *msg) { - memcpy(msg, (void *)(sring->va + (sring->elem_size * sring->swhead)), - sring->elem_size); + struct wil_rx_status_compressed *_msg; + + _msg = (struct wil_rx_status_compressed *) + (sring->va + (sring->elem_size * sring->swhead)); + *dr_bit = WIL_GET_BITS(_msg->d0, 31, 31); + /* make sure dr_bit is read before the rest of status msg */ + rmb(); + memcpy(msg, (void *)_msg, sring->elem_size); } static inline void wil_sring_advance_swhead(struct wil_status_ring *sring) @@ -268,6 +267,9 @@ static void wil_move_all_rx_buff_to_free_list(struct wil6210_priv *wil, struct list_head *active = &wil->rx_buff_mgmt.active; dma_addr_t pa; + if (!wil->rx_buff_mgmt.buff_arr) + return; + while (!list_empty(active)) { struct wil_rx_buff *rx_buff = list_first_entry(active, struct wil_rx_buff, list); @@ -312,7 +314,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 +324,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; } @@ -428,6 +433,9 @@ static void wil_ring_free_edma(struct wil6210_priv *wil, struct wil_ring *ring) &ring->pa, ring->ctx); wil_move_all_rx_buff_to_free_list(wil, ring); + dma_free_coherent(dev, sizeof(*ring->edma_rx_swtail.va), + ring->edma_rx_swtail.va, + ring->edma_rx_swtail.pa); goto out; } @@ -540,7 +548,7 @@ static int wil_rx_crypto_check_edma(struct wil6210_priv *wil, s = &wil->sta[cid]; c = mc ? &s->group_crypto_rx : &s->tid_crypto_rx[tid]; cc = &c->key_id[key_id]; - pn = (u8 *)&st->ext.pn_15_0; + pn = (u8 *)&st->ext.pn; if (!cc->key_set) { wil_err_ratelimited(wil, @@ -573,8 +581,7 @@ static bool wil_is_rx_idle_edma(struct wil6210_priv *wil) if (!sring->va) continue; - wil_get_next_rx_status_msg(sring, msg); - dr_bit = wil_rx_status_get_desc_rdy_bit(msg); + wil_get_next_rx_status_msg(sring, &dr_bit, msg); /* Check if there are unhandled RX status messages */ if (dr_bit == sring->desc_rdy_pol) @@ -727,7 +734,7 @@ static int wil_ring_init_tx_edma(struct wil6210_vif *vif, int ring_id, txdata->enabled = 0; spin_unlock_bh(&txdata->lock); wil_ring_free_edma(wil, ring); - wil->ring2cid_tid[ring_id][0] = WIL6210_MAX_CID; + wil->ring2cid_tid[ring_id][0] = wil->max_assoc_sta; wil->ring2cid_tid[ring_id][1] = 0; out: @@ -804,18 +811,9 @@ static int wil_rx_error_check_edma(struct wil6210_priv *wil, struct sk_buff *skb, struct wil_net_stats *stats) { - int error; int l2_rx_status; - int l3_rx_status; - int l4_rx_status; void *msg = wil_skb_rxstatus(skb); - error = wil_rx_status_get_error(msg); - if (!error) { - skb->ip_summed = CHECKSUM_UNNECESSARY; - return 0; - } - l2_rx_status = wil_rx_status_get_l2_rx_status(msg); if (l2_rx_status != 0) { wil_dbg_txrx(wil, "L2 RX error, l2_rx_status=0x%x\n", @@ -844,17 +842,7 @@ static int wil_rx_error_check_edma(struct wil6210_priv *wil, return -EFAULT; } - l3_rx_status = wil_rx_status_get_l3_rx_status(msg); - l4_rx_status = wil_rx_status_get_l4_rx_status(msg); - if (!l3_rx_status && !l4_rx_status) - skb->ip_summed = CHECKSUM_UNNECESSARY; - /* If HW reports bad checksum, let IP stack re-check it - * For example, HW don't understand Microsoft IP stack that - * mis-calculates TCP checksum - if it should be 0x0, - * it writes 0xffff in violation of RFC 1624 - */ - else - stats->rx_csum_err++; + skb->ip_summed = wil_rx_status_get_checksum(msg, stats); return 0; } @@ -879,12 +867,12 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil, u8 data_offset; struct wil_rx_status_extended *s; u16 sring_idx = sring - wil->srings; + int invalid_buff_id_retry; BUILD_BUG_ON(sizeof(struct wil_rx_status_extended) > sizeof(skb->cb)); again: - wil_get_next_rx_status_msg(sring, msg); - dr_bit = wil_rx_status_get_desc_rdy_bit(msg); + wil_get_next_rx_status_msg(sring, &dr_bit, msg); /* Completed handling all the ready status messages */ if (dr_bit != sring->desc_rdy_pol) @@ -892,26 +880,55 @@ 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))) { + + invalid_buff_id_retry = 0; + while (!buff_id) { + struct wil_rx_status_extended *s; + + 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)); + } + + 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); + print_hex_dump(KERN_ERR, "RxS ", DUMP_PREFIX_OFFSET, 16, 1, + msg, wil->use_compressed_rx_status ? + sizeof(struct wil_rx_status_compressed) : + sizeof(struct wil_rx_status_extended), false); + + wil_rx_status_reset_buff_id(sring); wil_sring_advance_swhead(sring); + sring->invalid_buff_id_cnt++; goto again; } - wil_sring_advance_swhead(sring); - /* 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)); @@ -926,13 +943,13 @@ 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); cid = wil_rx_status_get_cid(msg); - if (unlikely(!wil_val_in_range(cid, 0, WIL6210_MAX_CID))) { + if (unlikely(!wil_val_in_range(cid, 0, wil->max_assoc_sta))) { wil_err(wil, "Corrupt cid=%d, sring->swhead=%d\n", cid, sring->swhead); rxdata->skipping = true; @@ -940,8 +957,8 @@ again: } stats = &wil->sta[cid].stats; - if (unlikely(skb->len < ETH_HLEN)) { - wil_dbg_txrx(wil, "Short frame, len = %d\n", skb->len); + if (unlikely(dmalen < ETH_HLEN)) { + wil_dbg_txrx(wil, "Short frame, len = %d\n", dmalen); stats->rx_short_frame++; rxdata->skipping = true; goto skipping; @@ -949,6 +966,11 @@ again: if (unlikely(dmalen > sz)) { wil_err(wil, "Rx size too large: %d bytes!\n", dmalen); + print_hex_dump(KERN_ERR, "RxS ", DUMP_PREFIX_OFFSET, 16, 1, + msg, wil->use_compressed_rx_status ? + sizeof(struct wil_rx_status_compressed) : + sizeof(struct wil_rx_status_extended), false); + stats->rx_large_frame++; rxdata->skipping = true; } @@ -1004,6 +1026,10 @@ skipping: stats->last_mcs_rx = wil_rx_status_get_mcs(msg); if (stats->last_mcs_rx < ARRAY_SIZE(stats->rx_per_mcs)) stats->rx_per_mcs[stats->last_mcs_rx]++; + else if (stats->last_mcs_rx == WIL_EXTENDED_MCS_26) + stats->rx_per_mcs[WIL_BASE_MCS_FOR_EXTENDED_26]++; + + stats->last_cb_mode_rx = wil_rx_status_get_cb_mode(msg); } if (!wil->use_rx_hw_reordering && !wil->use_compressed_rx_status && @@ -1114,17 +1140,19 @@ static int wil_tx_desc_map_edma(union wil_tx_desc *desc, } static inline void -wil_get_next_tx_status_msg(struct wil_status_ring *sring, +wil_get_next_tx_status_msg(struct wil_status_ring *sring, u8 *dr_bit, struct wil_ring_tx_status *msg) { struct wil_ring_tx_status *_msg = (struct wil_ring_tx_status *) (sring->va + (sring->elem_size * sring->swhead)); + *dr_bit = _msg->desc_ready >> TX_STATUS_DESC_READY_POS; + /* make sure dr_bit is read before the rest of status msg */ + rmb(); *msg = *_msg; } -/** - * Clean up transmitted skb's from the Tx descriptor RING. +/* Clean up transmitted skb's from the Tx descriptor RING. * Return number of descriptors cleared. */ int wil_tx_sring_handler(struct wil6210_priv *wil, @@ -1137,10 +1165,10 @@ int wil_tx_sring_handler(struct wil6210_priv *wil, /* Total number of completed descriptors in all descriptor rings */ int desc_cnt = 0; int cid; - struct wil_net_stats *stats = NULL; + struct wil_net_stats *stats; struct wil_tx_enhanced_desc *_d; unsigned int ring_id; - unsigned int num_descs; + unsigned int num_descs, num_statuses = 0; int i; u8 dr_bit; /* Descriptor Ready bit */ struct wil_ring_tx_status msg; @@ -1148,8 +1176,7 @@ int wil_tx_sring_handler(struct wil6210_priv *wil, int used_before_complete; int used_new; - wil_get_next_tx_status_msg(sring, &msg); - dr_bit = msg.desc_ready >> TX_STATUS_DESC_READY_POS; + wil_get_next_tx_status_msg(sring, &dr_bit, &msg); /* Process completion messages while DR bit has the expected polarity */ while (dr_bit == sring->desc_rdy_pol) { @@ -1187,8 +1214,8 @@ int wil_tx_sring_handler(struct wil6210_priv *wil, ndev = vif_to_ndev(vif); cid = wil->ring2cid_tid[ring_id][0]; - if (cid < WIL6210_MAX_CID) - stats = &wil->sta[cid].stats; + stats = (cid < wil->max_assoc_sta) ? &wil->sta[cid].stats : + NULL; wil_dbg_txrx(wil, "tx_status: completed desc_ring (%d), num_descs (%d)\n", @@ -1236,6 +1263,10 @@ int wil_tx_sring_handler(struct wil6210_priv *wil, if (stats) stats->tx_errors++; } + + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) + wil_tx_complete_handle_eapol(vif, skb); + wil_consume_skb(skb, msg.status == 0); } memset(ctx, 0, sizeof(*ctx)); @@ -1261,24 +1292,28 @@ int wil_tx_sring_handler(struct wil6210_priv *wil, } again: + num_statuses++; + if (num_statuses % WIL_EDMA_TX_SRING_UPDATE_HW_TAIL == 0) + /* update HW tail to allow HW to push new statuses */ + wil_w(wil, sring->hwtail, sring->swhead); + wil_sring_advance_swhead(sring); - wil_get_next_tx_status_msg(sring, &msg); - dr_bit = msg.desc_ready >> TX_STATUS_DESC_READY_POS; + wil_get_next_tx_status_msg(sring, &dr_bit, &msg); } /* shall we wake net queues? */ if (desc_cnt) wil_update_net_queues(wil, vif, NULL, false); - /* Update the HW tail ptr (RD ptr) */ - wil_w(wil, sring->hwtail, (sring->swhead - 1) % sring->size); + if (num_statuses % WIL_EDMA_TX_SRING_UPDATE_HW_TAIL != 0) + /* Update the HW tail ptr (RD ptr) */ + wil_w(wil, sring->hwtail, (sring->swhead - 1) % sring->size); return desc_cnt; } -/** - * Sets the descriptor @d up for csum and/or TSO offloading. The corresponding +/* Sets the descriptor @d up for csum and/or TSO offloading. The corresponding * @skb is used to obtain the protocol and headers length. * @tso_desc_type is a descriptor type for TSO: 0 - a header, 1 - first data, * 2 - middle, 3 - last descriptor. @@ -1446,7 +1481,7 @@ static int __wil_tx_ring_tso_edma(struct wil6210_priv *wil, /* Rest of the descriptors are from the SKB fragments */ for (f = 0; f < nr_frags; f++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; - int len = frag->size; + int len = skb_frag_size(frag); wil_dbg_txrx(wil, "TSO: frag[%d]: len %u, descs_used %d\n", f, len, descs_used); |
