From 3bbb5b150feb08bcfa6920c3a23df38d2e7b037a Mon Sep 17 00:00:00 2001
From: Madhan Mohan R <madhanmohan.r@cypress.com>
Date: Fri, 4 Jan 2019 14:42:04 +0530
Subject: [PATCH 031/296] non-upstream: workaround for 4373 USB WMM 5.2.27 test
 failure

With the addition of skb_orphan in the datapath, though the throughput
increases in TX path, it introduces an issue by removing the flowcontrol
from upper layer and allowing more data to flow for different access
category.

This workaround is to disable using skb_orphan when running multi-stream
data. This change will not be required in linux 4.15 and later versions.

We only use this feature when Linux version is less than 4.16.0.


Signed-off-by: Raveendran Somu <raveendran.somu@cypress.com>
Signed-off-by: Wright Feng <wright.feng@cypress.com>
Signed-off-by: Chi-hsien Lin <chi-hsien.lin@cypress.com>
---
 .../broadcom/brcm80211/brcmfmac/bus.h         |  4 ++
 .../broadcom/brcm80211/brcmfmac/fwsignal.c    | 46 +++++++++++++++++++
 .../broadcom/brcm80211/brcmfmac/usb.c         |  8 +++-
 3 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
index dc756dbd0333..f7f3131bb8ba 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
@@ -10,6 +10,7 @@
 #include <linux/firmware.h>
 #include <linux/device.h>
 #include "debug.h"
+#include <linux/version.h>
 
 /* IDs of the 6 default common rings of msgbuf protocol */
 #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT	0
@@ -184,6 +185,9 @@ struct brcmf_bus {
 	struct brcmf_bus_msgbuf *msgbuf;
 
 	struct list_head list;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0))
+	bool allow_skborphan;
+#endif
 };
 
 /*
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
index 36af81975855..3981ad62b888 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
@@ -502,6 +502,9 @@ struct brcmf_fws_info {
 	bool creditmap_received;
 	u8 mode;
 	bool avoid_queueing;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0))
+	int fifo_init_credit[BRCMF_FWS_FIFO_COUNT];
+#endif
 };
 
 #define BRCMF_FWS_TLV_DEF(name, id, len) \
@@ -1617,9 +1620,13 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
 			fws->fifo_credit_map |= 1 << i;
 		else
 			fws->fifo_credit_map &= ~(1 << i);
+
 		WARN_ONCE(fws->fifo_credit[i] < 0,
 			  "fifo_credit[%d] is negative(%d)\n", i,
 			  fws->fifo_credit[i]);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0))
+		fws->fifo_init_credit[i] = fws->fifo_credit[i];
+#endif
 	}
 	brcmf_fws_schedule_deq(fws);
 	brcmf_fws_unlock(fws);
@@ -2197,6 +2204,38 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp)
 	brcmf_fws_unlock(fws);
 }
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0))
+static bool brcmf_fws_ismultistream(struct brcmf_fws_info *fws)
+{
+	bool ret = false;
+	u8 credit_usage = 0;
+
+	/* Check only for BE, VI and VO traffic */
+	u32 delay_map = fws->fifo_delay_map &
+		((1 << BRCMF_FWS_FIFO_AC_BE) |
+		 (1 << BRCMF_FWS_FIFO_AC_VI) |
+		 (1 << BRCMF_FWS_FIFO_AC_VO));
+
+	if (hweight_long(delay_map) > 1) {
+		ret = true;
+	} else {
+		if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_BE] <
+			fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_BE])
+			credit_usage++;
+		if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_VI] <
+			fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_VI])
+			credit_usage++;
+		if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_VO] <
+			fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_VO])
+			credit_usage++;
+
+		if (credit_usage > 1)
+			ret = true;
+	}
+	return ret;
+}
+#endif
+
 static void brcmf_fws_dequeue_worker(struct work_struct *worker)
 {
 	struct brcmf_fws_info *fws;
@@ -2210,6 +2249,13 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
 	fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
 	drvr = fws->drvr;
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0))
+	if (brcmf_fws_ismultistream(fws))
+		drvr->bus_if->allow_skborphan = false;
+	else
+		drvr->bus_if->allow_skborphan = true;
+#endif
+
 	brcmf_fws_lock(fws);
 	for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
 	     fifo--) {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
index 6902f8773214..0b907ef4ab9e 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
@@ -639,7 +639,10 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
 		goto fail;
 	}
 
-	skb_orphan(skb);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0))
+	if (devinfo->bus_pub.bus->allow_skborphan)
+		skb_orphan(skb);
+#endif
 	req->skb = skb;
 	req->devinfo = devinfo;
 	usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe,
@@ -1276,6 +1279,9 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo,
 	bus->proto_type = BRCMF_PROTO_BCDC;
 	bus->fwvid = fwvid;
 	bus->always_use_fws_queue = true;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0))
+	bus->allow_skborphan = true;
+#endif
 #ifdef CONFIG_PM
 	bus->wowl_supported = true;
 #endif
-- 
2.25.1

