[][[Merlin][AX3000][MT7986A][MT76][WiFi][SnS][TC30]APcli occurred oom issue many times]

[Description]
Fix oom issue when wed on
copy ccif tx message to pre-alloc buf
then free message

[Release-log]
N/A

[[Info to Customer]]
none

Change-Id: I8b5b601350863ed90e18e09b121548e7ab9e913e
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/6578201
Build: srv_hbgsm110
diff --git a/autobuild_mac80211_release/target/linux/mediatek/patches-5.4/9997-add-wed-rx-support-for-mt7896.patch b/autobuild_mac80211_release/target/linux/mediatek/patches-5.4/9997-add-wed-rx-support-for-mt7896.patch
index e99920a..00909d5 100644
--- a/autobuild_mac80211_release/target/linux/mediatek/patches-5.4/9997-add-wed-rx-support-for-mt7896.patch
+++ b/autobuild_mac80211_release/target/linux/mediatek/patches-5.4/9997-add-wed-rx-support-for-mt7896.patch
@@ -16,8 +16,8 @@
  drivers/net/ethernet/mediatek/mtk_wed_mcu.c   | 586 ++++++++++++++++
  drivers/net/ethernet/mediatek/mtk_wed_mcu.h   | 125 ++++
  drivers/net/ethernet/mediatek/mtk_wed_regs.h  | 144 +++-
- drivers/net/ethernet/mediatek/mtk_wed_wo.c    | 573 ++++++++++++++++
- drivers/net/ethernet/mediatek/mtk_wed_wo.h    | 327 +++++++++
+ drivers/net/ethernet/mediatek/mtk_wed_wo.c    | 564 ++++++++++++++++
+ drivers/net/ethernet/mediatek/mtk_wed_wo.h    | 324 +++++++++
  include/linux/soc/mediatek/mtk_wed.h          |  74 ++-
  14 files changed, 2796 insertions(+), 75 deletions(-)
  create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_ccif.c
@@ -1359,7 +1359,7 @@
 +	tasklet_disable(&wo->irq_tasklet);
 +	netif_napi_del(&wo->napi);
 +
-+	mtk_wed_wo_q_tx_clean(wo, &wo->q_tx, true);
++	mtk_wed_wo_q_tx_clean(wo, &wo->q_tx);
 +	mtk_wed_wo_q_rx_clean(wo, &wo->q_rx);
 +	mtk_wed_wo_q_free(wo, &wo->q_tx);
 +	mtk_wed_wo_q_free(wo, &wo->q_rx);
@@ -2539,7 +2539,7 @@
 index 0000000..8434272
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-@@ -0,0 +1,573 @@
+@@ -0,0 +1,564 @@
 +// SPDX-License-Identifier: GPL-2.0-only
 +
 +#include <linux/kernel.h>
@@ -2611,10 +2611,11 @@
 +}
 +
 +static int
-+woif_q_rx_fill(struct mtk_wed_wo *wo, struct wed_wo_queue *q)
++woif_q_rx_fill(struct mtk_wed_wo *wo, struct wed_wo_queue *q, bool rx)
 +{
 +	int len = q->buf_size, frames = 0;
 +	struct wed_wo_queue_entry *entry;
++	struct page_frag_cache *page = &q->tx_page;
 +	struct wed_wo_desc *desc;
 +	dma_addr_t addr;
 +	u32 ctrl = 0;
@@ -2625,9 +2626,11 @@
 +
 +	spin_lock_bh(&q->lock);
 +
-+	while (q->queued < q->ndesc) {
++	if(rx)
++		page = &q->rx_page;
 +
-+		buf = page_frag_alloc(&q->rx_page, len, GFP_ATOMIC);
++	while (q->queued < q->ndesc) {
++		buf = page_frag_alloc(page, len, GFP_ATOMIC);
 +		if (!buf)
 +			break;
 +
@@ -2636,8 +2639,6 @@
 +			skb_free_frag(buf);
 +			break;
 +		}
-+		dma_sync_single_for_cpu(wo->hw->dev, addr, len,
-+					DMA_TO_DEVICE);
 +
 +		q->head = (q->head + 1) % q->ndesc;
 +
@@ -2647,13 +2648,13 @@
 +		entry->dma_addr = addr;
 +		entry->dma_len = len;
 +
-+		ctrl = FIELD_PREP(WED_CTL_SD_LEN0, entry->dma_len);
-+		ctrl |= WED_CTL_LAST_SEC0;
++		if (rx) {
++			ctrl = FIELD_PREP(WED_CTL_SD_LEN0, entry->dma_len);
++			ctrl |= WED_CTL_LAST_SEC0;
 +
-+		WRITE_ONCE(desc->buf0, cpu_to_le32(addr));
-+		WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
-+		dma_sync_single_for_device(wo->hw->dev, addr, len,
-+					DMA_TO_DEVICE);
++			WRITE_ONCE(desc->buf0, cpu_to_le32(addr));
++			WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
++		}
 +		q->queued++;
 +		q->entry[q->head].buf = buf;
 +
@@ -2668,7 +2669,7 @@
 +static void
 +woif_q_rx_fill_process(struct mtk_wed_wo *wo, struct wed_wo_queue *q)
 +{
-+	if(woif_q_rx_fill(wo, q))
++	if(woif_q_rx_fill(wo, q, true))
 +		woif_q_kick(wo, q, -1);
 +}
 +
@@ -2706,8 +2707,11 @@
 +	if (!q->entry)
 +		return -ENOMEM;
 +
-+	if (idx == 0)
++	if (idx == 0) {
++		/* alloc tx buf */
++		woif_q_rx_fill(dev, &dev->q_tx, false);
 +		woif_q_reset(dev, &dev->q_tx);
++	}
 +
 +	return 0;
 +}
@@ -2730,44 +2734,36 @@
 +}
 +
 +static void
-+woif_q_tx_clean(struct mtk_wed_wo *wo, struct wed_wo_queue *q, bool flush)
++woif_q_tx_clean(struct mtk_wed_wo *wo, struct wed_wo_queue *q)
 +{
-+	int last;
++	struct page *page;
++	int i = 0;
 +
 +	if (!q || !q->ndesc)
 +		return;
 +
-+	spin_lock_bh(&q->cleanup_lock);
-+	if (flush)
-+		last = -1;
-+	else
-+		last = woccif_r32(wo, q->regs->dma_idx);
-+
-+	while (q->queued > 0 && q->tail != last) {
++	spin_lock_bh(&q->lock);
++	while (i < q->ndesc) {
 +		struct wed_wo_queue_entry *e;
 +
-+		e = &q->entry[q->tail + 1];
++		e = &q->entry[i];
++		i++;
 +
++		if (!e)
++			continue;
 +		dma_unmap_single(wo->hw->dev, e->dma_addr, e->dma_len,
 +				 DMA_TO_DEVICE);
 +
-+
-+		memset(e, 0, sizeof(*e));
-+
-+		spin_lock_bh(&q->lock);
-+		q->tail = (q->tail + 1) % q->ndesc;
-+		q->queued--;
-+		spin_unlock_bh(&q->lock);
-+
++		skb_free_frag(e->buf);
 +	}
-+	spin_unlock_bh(&q->cleanup_lock);
++	spin_unlock_bh(&q->lock);
 +
-+	if (flush) {
-+		spin_lock_bh(&q->lock);
-+		woif_q_sync_idx(wo, q);
-+		woif_q_kick(wo, q, 0);
-+		spin_unlock_bh(&q->lock);
-+	}
++	if (!q->tx_page.va)
++		return;
++
++	page = virt_to_page(q->tx_page.va);
++	__page_frag_cache_drain(page, q->tx_page.pagecnt_bias);
++	memset(&q->tx_page, 0, sizeof(q->tx_page));
 +}
 +
 +static void *
@@ -2838,7 +2834,6 @@
 +	page = virt_to_page(q->rx_page.va);
 +	__page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
 +	memset(&q->rx_page, 0, sizeof(q->rx_page));
-+
 +}
 +
 +static int
@@ -2851,7 +2846,7 @@
 +
 +	if (dev->q_rx.ndesc) {
 +		netif_napi_add(&dev->napi_dev, &dev->napi, poll, 64);
-+		woif_q_rx_fill(dev, &dev->q_rx);
++		woif_q_rx_fill(dev, &dev->q_rx, true);
 +		woif_q_reset(dev, &dev->q_rx);
 +		napi_enable(&dev->napi);
 +	}
@@ -2884,33 +2879,33 @@
 +{
 +	struct wed_wo_queue_entry *entry;
 +	struct wed_wo_desc *desc;
-+	int len, ret, idx = -1;
++	int len, ret = 0, idx = -1;
 +	dma_addr_t addr;
 +	u32 ctrl = 0;
 +
 +	len = skb->len;
-+	addr = dma_map_single(wo->hw->dev, skb->data, len, DMA_TO_DEVICE);
-+	if (unlikely(dma_mapping_error(wo->hw->dev, addr)))
-+		goto error;
-+
-+	/* packet tx, force trigger tx clean. */
-+		woif_q_tx_clean(wo, q, false);
++	spin_lock_bh(&q->lock);
 +
-+	if (q->queued >= q->ndesc) {
++	q->tail = woccif_r32(wo, q->regs->dma_idx);
++	q->head = (q->head + 1) % q->ndesc;
++	if (q->tail == q->head) {
 +		ret = -ENOMEM;
 +		goto error;
 +	}
 +
-+	spin_lock_bh(&q->lock);
-+
-+	q->head = (q->head + 1) % q->ndesc;
 +	idx = q->head;
-+
 +	desc = &q->desc[idx];
 +	entry = &q->entry[idx];
 +
-+	entry->dma_addr = addr;
-+	entry->dma_len = len;
++	if (len > entry->dma_len) {
++		ret = -ENOMEM;
++		goto error;
++	}
++	addr = entry->dma_addr;
++
++	dma_sync_single_for_cpu(wo->hw->dev, addr, len, DMA_TO_DEVICE);
++	memcpy(entry->buf, skb->data, len);
++	dma_sync_single_for_device(wo->hw->dev, addr, len, DMA_TO_DEVICE);
 +
 +	ctrl = FIELD_PREP(WED_CTL_SD_LEN0, len);
 +	ctrl |= WED_CTL_LAST_SEC0;
@@ -2919,18 +2914,14 @@
 +	WRITE_ONCE(desc->buf0, cpu_to_le32(addr));
 +	WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
 +
-+	q->queued++;
-+	q->entry[idx].skb = skb;
-+
 +	woif_q_kick(wo, q, 0);
 +	wo->drv_ops->kickout(wo);
 +
 +	spin_unlock_bh(&q->lock);
-+	return 0;
 +
 +error:
 +	dev_kfree_skb(skb);
-+	return -ENOMEM;
++	return ret;
 +}
 +
 +static const struct wed_wo_queue_ops wo_queue_ops = {
@@ -2947,7 +2938,7 @@
 +static int
 +mtk_wed_wo_rx_process(struct mtk_wed_wo *wo, struct wed_wo_queue *q, int budget)
 +{
-+	int len, data_len, done = 0;
++	int len, done = 0;
 +	struct sk_buff *skb;
 +	unsigned char *data;
 +	bool more;
@@ -3118,7 +3109,7 @@
 index 0000000..5824f39
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-@@ -0,0 +1,327 @@
+@@ -0,0 +1,324 @@
 +// SPDX-License-Identifier: GPL-2.0-only
 +/* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name> */
 +
@@ -3205,6 +3196,7 @@
 +
 +	dma_addr_t desc_dma;
 +	struct page_frag_cache rx_page;
++	struct page_frag_cache tx_page;
 +};
 +
 +
@@ -3344,10 +3336,7 @@
 +
 +	int (*tx_skb)(struct mtk_wed_wo *wo, struct wed_wo_queue *q,
 +		      struct sk_buff *skb);
-+	int (*tx_skb1)(struct mtk_wed_wo *wo, struct wed_wo_queue *q,
-+		       u8 *msg, u32 msg_len);
-+	void (*tx_clean)(struct mtk_wed_wo *wo, struct wed_wo_queue *q,
-+			 bool flush);
++	void (*tx_clean)(struct mtk_wed_wo *wo, struct wed_wo_queue *q);
 +
 +	void (*rx_clean)(struct mtk_wed_wo *wo, struct wed_wo_queue *q);
 +
@@ -3382,7 +3371,6 @@
 +#define mtk_wed_wo_q_free(wo, ...)	(wo)->queue_ops->free((wo), __VA_ARGS__)
 +#define mtk_wed_wo_q_reset(wo, ...)	(wo)->queue_ops->reset((wo), __VA_ARGS__)
 +#define mtk_wed_wo_q_tx_skb(wo, ...)	(wo)->queue_ops->tx_skb((wo), __VA_ARGS__)
-+#define mtk_wed_wo_q_tx_skb1(wo, ...)	(wo)->queue_ops->tx_skb1((wo), __VA_ARGS__)
 +#define mtk_wed_wo_q_tx_clean(wo, ...)	(wo)->queue_ops->tx_clean((wo), __VA_ARGS__)
 +#define mtk_wed_wo_q_rx_clean(wo, ...)	(wo)->queue_ops->rx_clean((wo), __VA_ARGS__)
 +#define mtk_wed_wo_q_kick(wo, ...)	(wo)->queue_ops->kick((wo), __VA_ARGS__)