From b990cfb88f45f98f6c1860b2789301844de68bbf Mon Sep 17 00:00:00 2001 From: Hrishikesh Vidwans Date: Thu, 19 Sep 2019 11:34:53 +0300 Subject: [PATCH] wil6210: allow sending special packets when tx ring is full When traffic is very high, some special packets such as ARP may get dropped causing the tx traffic to stall intermittently. This patch preserves spare tx descriptors so that special packets can be sent even when the tx ring is almost full. The number of reserved entries can be changed by writing to tx_reserved_entries via debugfs, taking effect in subsequent connections. This feature is enabled when either drop_if_ring_full or ac_queues are enabled. Change-Id: I6c5215500966840445f2631fb32494750e60303d Signed-off-by: Hrishikesh Vidwans Signed-off-by: Ahmad Masri --- drivers/net/wireless/ath/wil6210/debugfs.c | 17 ++++++ drivers/net/wireless/ath/wil6210/main.c | 4 ++ drivers/net/wireless/ath/wil6210/txrx.c | 55 +++++++++++++++++--- drivers/net/wireless/ath/wil6210/txrx.h | 3 +- drivers/net/wireless/ath/wil6210/txrx_edma.c | 9 ++-- drivers/net/wireless/ath/wil6210/wil6210.h | 12 ++++- 6 files changed, 88 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 7207a879e45d..bbffa989e554 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -99,6 +99,22 @@ static void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil, v = (ring_id % 2 ? (v >> 16) : (v & 0xffff)); seq_printf(s, " hwhead = %u\n", v); + if (!ring->is_rx) { + struct wil_ring_tx_data *txdata = + &wil->ring_tx_data[ring_id]; + + seq_printf(s, " available = %d\n", + wil_ring_avail_tx(ring) - + txdata->tx_reserved_count); + seq_printf(s, " used = %d\n", + wil_ring_used_tx(ring)); + seq_printf(s, "\n tx_res_count = %d\n", + txdata->tx_reserved_count); + seq_printf(s, " tx_res_count_used = %d\n", + txdata->tx_reserved_count_used); + seq_printf(s, " tx_res_count_unavail = %d\n", + txdata->tx_reserved_count_not_avail); + } } seq_printf(s, " hwtail = [0x%08x] -> ", ring->hwtail); x = wmi_addr(wil, ring->hwtail); @@ -2461,6 +2477,7 @@ static const struct dbg_off dbg_wil_off[] = { WIL_FIELD(amsdu_en, 0644, doff_u8), WIL_FIELD(force_edmg_channel, 0644, doff_u8), WIL_FIELD(ap_ps, 0644, doff_u8), + WIL_FIELD(tx_reserved_entries, 0644, doff_u32), {}, }; diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index c5120d8c8455..ff95b1a02777 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1896,6 +1896,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) ftm->rx_offset); } + wil->tx_reserved_entries = ((drop_if_ring_full || ac_queues) ? + WIL_DEFAULT_TX_RESERVED_ENTRIES : + 0); + if (wil->platform_ops.notify) { rc = wil->platform_ops.notify(wil->platform_handle, WIL_PLATFORM_EVT_FW_RDY); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 531e9a7403ba..406beb73cec4 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1161,7 +1161,8 @@ static int wil_tx_desc_map(union wil_tx_desc *desc, dma_addr_t pa, return 0; } -void wil_tx_data_init(struct wil_ring_tx_data *txdata) +void wil_tx_data_init(const struct wil6210_priv *wil, + struct wil_ring_tx_data *txdata) { spin_lock_bh(&txdata->lock); txdata->dot1x_open = 0; @@ -1174,6 +1175,9 @@ void wil_tx_data_init(struct wil_ring_tx_data *txdata) txdata->agg_amsdu = 0; txdata->addba_in_progress = false; txdata->mid = U8_MAX; + txdata->tx_reserved_count = wil->tx_reserved_entries; + txdata->tx_reserved_count_used = 0; + txdata->tx_reserved_count_not_avail = 0; spin_unlock_bh(&txdata->lock); } @@ -1228,7 +1232,7 @@ static int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, goto out; } - wil_tx_data_init(txdata); + wil_tx_data_init(wil, txdata); vring->is_rx = false; vring->size = size; rc = wil_vring_alloc(wil, vring); @@ -1397,7 +1401,7 @@ int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size) goto out; } - wil_tx_data_init(txdata); + wil_tx_data_init(wil, txdata); vring->is_rx = false; vring->size = size; rc = wil_vring_alloc(wil, vring); @@ -2038,6 +2042,20 @@ err_exit: return rc; } +static inline bool is_special_packet(const struct sk_buff *skb) +{ + if (skb->protocol == cpu_to_be16(ETH_P_ARP) || + skb->protocol == cpu_to_be16(ETH_P_RARP) || + (skb->protocol == cpu_to_be16(ETH_P_IP) && + ip_hdr(skb)->protocol == IPPROTO_ICMP) || + (skb->protocol == cpu_to_be16(ETH_P_IPV6) && + ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) || + skb->protocol == cpu_to_be16(ETH_P_PAE)) + return true; + + return false; +} + static int __wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, struct wil_ring *ring, struct sk_buff *skb) { @@ -2045,7 +2063,6 @@ static int __wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, struct vring_tx_desc dd, *d = ⅆ volatile struct vring_tx_desc *_d; u32 swhead = ring->swhead; - int avail = wil_ring_avail_tx(ring); int nr_frags = skb_shinfo(skb)->nr_frags; uint f = 0; int ring_index = ring - wil->ring_tx; @@ -2055,6 +2072,11 @@ static int __wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, int used; bool mcast = (ring_index == vif->bcast_ring); uint len = skb_headlen(skb); + bool special_packet = (wil->tx_reserved_entries != 0 && + is_special_packet(skb)); + int avail = wil_ring_avail_tx(ring) - + (special_packet ? 0 : txdata->tx_reserved_count); + u8 ctx_flags = special_packet ? WIL_CTX_FLAG_RESERVED_USED : 0; wil_dbg_txrx(wil, "tx_ring: %d bytes to ring %d, nr_frags %d\n", skb->len, ring_index, nr_frags); @@ -2063,9 +2085,17 @@ static int __wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, return -EINVAL; if (unlikely(avail < 1 + nr_frags)) { - wil_err_ratelimited(wil, - "Tx ring[%2d] full. No space for %d fragments\n", - ring_index, 1 + nr_frags); + if (special_packet) { + txdata->tx_reserved_count_not_avail++; + wil_err_ratelimited(wil, + "TX ring[%2d] full. No space for %d fragments for special packet. Tx-reserved-count is %d\n", + ring_index, 1 + nr_frags, + txdata->tx_reserved_count); + } else { + wil_err_ratelimited(wil, + "Tx ring[%2d] full. No space for %d fragments\n", + ring_index, 1 + nr_frags); + } return -ENOMEM; } _d = &ring->va[i].tx.legacy; @@ -2080,6 +2110,7 @@ static int __wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, if (unlikely(dma_mapping_error(dev, pa))) return -EINVAL; ring->ctx[i].mapped_as = wil_mapped_as_single; + ring->ctx[i].flags = ctx_flags; /* 1-st segment */ wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d, pa, len, ring_index); @@ -2117,6 +2148,8 @@ static int __wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, goto dma_error; } ring->ctx[i].mapped_as = wil_mapped_as_page; + ring->ctx[i].flags = ctx_flags; + wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d, pa, len, ring_index); /* no need to check return code - @@ -2149,6 +2182,14 @@ static int __wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, ring_index, used, used + nr_frags + 1); } + if (special_packet) { + txdata->tx_reserved_count -= (f + 1); + txdata->tx_reserved_count_used += (f + 1); + wil_dbg_txrx(wil, + "Ring[%2d] tx_reserved_count: %d, reduced by %d\n", + ring_index, txdata->tx_reserved_count, f + 1); + } + /* Make sure to advance the head only after descriptor update is done. * This will prevent a race condition where the completion thread * will see the DU bit set from previous run and will handle the diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index d6e69926caa9..7aa1afe902d5 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -690,7 +690,8 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, int size, u16 ssn); void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, struct wil_tid_ampdu_rx *r); -void wil_tx_data_init(struct wil_ring_tx_data *txdata); +void wil_tx_data_init(const struct wil6210_priv *wil, + struct wil_ring_tx_data *txdata); void wil_init_txrx_ops_legacy_dma(struct wil6210_priv *wil); void wil_tx_latency_calc(struct wil6210_priv *wil, struct sk_buff *skb, struct wil_sta_info *sta); diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index ec767b950215..ff6a740f3aab 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -750,7 +750,7 @@ static int wil_ring_init_tx_edma(struct wil6210_vif *vif, int ring_id, "init TX ring: ring_id=%u, cid=%u, tid=%u, sring_id=%u\n", ring_id, cid, tid, wil->tx_sring_idx); - wil_tx_data_init(txdata); + wil_tx_data_init(wil, txdata); ring->size = size; rc = wil_ring_alloc_desc_ring(wil, ring, true); if (rc) @@ -1294,6 +1294,9 @@ int wil_tx_sring_handler(struct wil6210_priv *wil, (const void *)&msg, sizeof(msg), false); + if (ctx->flags & WIL_CTX_FLAG_RESERVED_USED) + txdata->tx_reserved_count++; + wil_tx_desc_unmap_edma(dev, (union wil_tx_desc *)d, ctx); @@ -1460,7 +1463,7 @@ static int __wil_tx_ring_tso_edma(struct wil6210_priv *wil, struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_index]; int nr_frags = skb_shinfo(skb)->nr_frags; int min_desc_required = nr_frags + 2; /* Headers, Head, Fragments */ - int used, avail = wil_ring_avail_tx(ring); + int used, avail = wil_ring_avail_tx(ring) - txdata->tx_reserved_count; int f, hdrlen, headlen; int gso_type; bool is_ipv4; @@ -1622,7 +1625,7 @@ static int wil_ring_init_bcast_edma(struct wil6210_vif *vif, int ring_id, wil_ipa_set_bcast_sring_id(wil, sring_id); } - wil_tx_data_init(txdata); + wil_tx_data_init(wil, txdata); ring->size = size; ring->is_rx = false; rc = wil_ring_alloc_desc_ring(wil, ring, true); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index e656890d4d77..8454c662f861 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -106,6 +106,7 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) #define WIL6210_MAX_STATUS_RINGS (8) #define WIL6210_MAX_HEADROOM_SIZE (256) #define WIL_WMI_CALL_GENERAL_TO_MS 100 +#define WIL_DEFAULT_TX_RESERVED_ENTRIES (16) /* Hardware offload block adds the following: * 26 bytes - 3-address QoS data header @@ -515,13 +516,17 @@ enum { /* for wil_ctx.mapped_as */ wil_mapped_as_page = 2, }; +/* for wil_ctx.flags */ +#define WIL_CTX_FLAG_RESERVED_USED 0x01 + /** * struct wil_ctx - software context for ring descriptor */ struct wil_ctx { struct sk_buff *skb; u8 nr_frags; - u8 mapped_as; + u8 mapped_as:4; + u8 flags:4; }; struct wil_desc_ring_rx_swtail { /* relevant for enhanced DMA only */ @@ -655,6 +660,9 @@ struct wil_ring_tx_data { bool addba_in_progress; /* if set, agg_xxx is for request in progress */ u8 mid; spinlock_t lock; + u32 tx_reserved_count; /* available reserved tx entries */ + u32 tx_reserved_count_used; + u32 tx_reserved_count_not_avail; }; enum { /* for wil6210_priv.status */ @@ -1111,6 +1119,8 @@ struct wil6210_priv { struct work_struct pci_linkdown_recovery_worker; void *ipa_handle; + + u32 tx_reserved_entries; /* Used only in Talyn code-path */ }; #define wil_to_wiphy(i) (i->wiphy)