From 3acca4ecfe25a7d97e7cb820fd8c7c6324a1f318 Mon Sep 17 00:00:00 2001
From: Sowmiya Sree Elavalagan <ssreeela@codeaurora.org>
Date: Tue, 18 Aug 2020 16:17:25 +0530
Subject: [PATCH] ath11k : Add NSS redirect support

Add NSS Redirect support for ath11k. Tested on ipq8074
Most of the changes are similar to the one done for ath10k with
minor changes to send to nss with eth header

Redirect can be enabled by setting nss_redirect module param during
mac80211 insmod followed by ecm start
insmod mac80211.ko nss_redirect=1
/etc/init.d/qca-nss-ecm start

Check for ipv4_hash_hits counts in ipv4 stats and eth_rx
in qca-nss-drv to check whether packets are redirected or not.

Verified both in nwifi and ethernet mode by running tcp and udp
traffic.

Co-developed by: Sriram R <srirrama@codeaurora.org>
Signed-off-by: Sriram R <srirrama@codeaurora.org>
Signed-off-by: Sowmiya Sree Elavalagan <ssreeela@codeaurora.org>
---
 net/mac80211/ieee80211_i.h |  2 ++
 net/mac80211/iface.c       | 43 ++++++++++++++++++++++++++++++++
 net/mac80211/rx.c          | 61 +++++++++++++++++++++++++++++++++++++---------
 net/mac80211/tx.c          | 32 ++++++++++++++++++++++++
 4 files changed, 126 insertions(+), 12 deletions(-)

--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -35,6 +35,9 @@
 #include "sta_info.h"
 #include "debug.h"
 #include "drop.h"
+#ifdef CPTCFG_MAC80211_NSS_SUPPORT
+#include <nss_api_if.h>
+#endif
 
 extern const struct cfg80211_ops mac80211_config_ops;
 
@@ -1184,6 +1187,9 @@ struct ieee80211_sub_if_data {
 		struct dentry *default_beacon_key;
 	} debugfs;
 #endif
+#ifdef CPTCFG_MAC80211_NSS_SUPPORT
+	struct nss_virt_if_handle *nssctx;
+#endif
 
 	/* must be last, dynamically sized area in this! */
 	struct ieee80211_vif vif;
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -27,6 +27,12 @@
 #include "wme.h"
 #include "rate.h"
 
+#ifdef CPTCFG_MAC80211_NSS_SUPPORT
+bool nss_redirect = false;
+module_param(nss_redirect, bool, 0644);
+MODULE_PARM_DESC(nss_redirect, "module param to enable NSS Redirect; 1-enable, 0-disable");
+#endif
+
 /**
  * DOC: Interface list locking
  *
@@ -776,6 +782,13 @@ static int ieee80211_stop(struct net_dev
 		ieee80211_stop_mbssid(sdata);
 	}
 
+#ifdef CPTCFG_MAC80211_NSS_SUPPORT
+	if (sdata->nssctx) {
+		nss_virt_if_destroy_sync(sdata->nssctx);
+		sdata_info(sdata, "Destroyed NSS virtual interface\n");
+	}
+#endif
+
 	wiphy_lock(sdata->local->hw.wiphy);
 	wiphy_work_cancel(sdata->local->hw.wiphy, &sdata->activate_links_work);
 
@@ -1227,6 +1240,34 @@ void ieee80211_del_virtual_monitor(struc
 	kfree(sdata);
 }
 
+#ifdef CPTCFG_MAC80211_NSS_SUPPORT
+/* This callback is registered for nss redirect to receive packet exceptioned from nss in Rx path.
+ * When packet does not match any of the ecm rules is redirected back here.
+ */
+void receive_from_nss(struct net_device *dev, struct sk_buff *sk_buff, struct napi_struct *napi)
+{
+	struct net_device *netdev;
+	struct sk_buff *skb;
+	struct ieee80211_sub_if_data *sdata;
+
+	if (!dev) {
+		kfree(sk_buff);
+		return;
+	}
+
+	netdev = (struct net_device *)dev;
+	sdata = netdev_priv(netdev);
+	if (sdata->dev != dev) {
+		kfree(sk_buff);
+		return;
+	}
+	skb = (struct sk_buff *)sk_buff;
+	skb->dev = netdev;
+	skb->protocol = eth_type_trans(skb, netdev);
+	napi_gro_receive(napi, skb);
+}
+#endif
+
 /*
  * NOTE: Be very careful when changing this function, it must NOT return
  * an error on interface type changes that have been pre-checked, so most
@@ -1457,6 +1498,21 @@ int ieee80211_do_open(struct wireless_de
 
 	ieee80211_recalc_ps(local);
 
+#ifdef CPTCFG_MAC80211_NSS_SUPPORT
+	sdata->nssctx = NULL;
+	if (nss_redirect) {
+		sdata->nssctx = nss_virt_if_create_sync(dev);
+		if (sdata->nssctx) {
+			sdata_info(sdata, "Created a NSS virtual interface\n");
+			nss_virt_if_register(sdata->nssctx, receive_from_nss, sdata->dev);
+		} else {
+			sdata_info(
+				sdata,
+				"Failed to create a NSS virtual interface\n");
+		}
+	}
+#endif
+
 	set_bit(SDATA_STATE_RUNNING, &sdata->state);
 
 	return 0;
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2613,6 +2613,60 @@ static bool ieee80211_frame_allowed(stru
 	return true;
 }
 
+#ifdef CPTCFG_MAC80211_NSS_SUPPORT
+#define case_rtn_string(val) case val: return #val
+
+static const char *nss_tx_status_str(nss_tx_status_t status)
+{
+	switch (status) {
+		case_rtn_string(NSS_TX_SUCCESS);
+		case_rtn_string(NSS_TX_FAILURE);
+		case_rtn_string(NSS_TX_FAILURE_QUEUE);
+		case_rtn_string(NSS_TX_FAILURE_NOT_READY);
+		case_rtn_string(NSS_TX_FAILURE_TOO_LARGE);
+		case_rtn_string(NSS_TX_FAILURE_TOO_SHORT);
+		case_rtn_string(NSS_TX_FAILURE_NOT_SUPPORTED);
+		case_rtn_string(NSS_TX_FAILURE_BAD_PARAM);
+		case_rtn_string(NSS_TX_FAILURE_NOT_ENABLED);
+		case_rtn_string(NSS_TX_FAILURE_SYNC_BAD_PARAM);
+		case_rtn_string(NSS_TX_FAILURE_SYNC_TIMEOUT);
+		case_rtn_string(NSS_TX_FAILURE_SYNC_FW_ERR);
+	default:
+		return "Unknown NSS TX status";
+	}
+}
+
+static void netif_rx_nss(struct ieee80211_rx_data *rx,
+			 struct sk_buff *skb)
+{
+	struct ieee80211_sub_if_data *sdata = rx->sdata;
+	int ret;
+
+	if (!sdata->nssctx)
+		goto out;
+
+	/* NSS expects ethernet header in skb data so resetting here */
+	skb_push(skb, ETH_HLEN);
+	ret = nss_virt_if_tx_buf(sdata->nssctx, skb);
+	if (ret) {
+		if (net_ratelimit()) {
+			if (NSS_TX_FAILURE_NOT_ENABLED == ret)
+			  goto out;
+			sdata_err(sdata, "NSS TX failed with error: %s\n",
+				  nss_tx_status_str(ret));
+		}
+		goto out;
+	}
+
+	return;
+out:
+	if (rx->napi)
+		napi_gro_receive(rx->napi, skb);
+	else
+		netif_receive_skb(skb);
+}
+#endif
+
 static void ieee80211_deliver_skb_to_local_stack(struct sk_buff *skb,
 						 struct ieee80211_rx_data *rx)
 {
@@ -2652,11 +2706,15 @@ static void ieee80211_deliver_skb_to_loc
 			     !ether_addr_equal(ehdr->h_dest, sdata->vif.addr)))
 			ether_addr_copy(ehdr->h_dest, sdata->vif.addr);
 
+#ifdef CPTCFG_MAC80211_NSS_SUPPORT
+		netif_rx_nss(rx, skb);
+#else
 		/* deliver to local stack */
 		if (rx->list)
 			list_add_tail(&skb->list, rx->list);
 		else
 			netif_receive_skb(skb);
+#endif
 	}
 }
 
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -4527,6 +4527,35 @@ static void ieee80211_mlo_multicast_tx(s
 	kfree_skb(skb);
 }
 
+#ifdef CPTCFG_MAC80211_NSS_SUPPORT
+void ieee80211_xmit_nss_fixup(struct sk_buff *skb,
+			      struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+	/* Packets from NSS does not have valid protocol, priority and other
+	 * network stack values. Derive required parameters (priority
+	 * and network_header) from payload for QoS header.
+	 * XXX: Here the assumption is that packet are in 802.3 format.
+	 * As of now priority is handled only for IPv4 and IPv6.
+	 */
+
+	if (sdata->nssctx && likely(!skb->protocol)) {
+		skb_set_network_header(skb, 14);
+		switch (((struct ethhdr *)skb->data)->h_proto) {
+		case htons(ETH_P_IP):
+			skb->priority = (ipv4_get_dsfield(ip_hdr(skb)) &
+					 0xfc) >> 5;
+			break;
+		case htons(ETH_P_IPV6):
+			skb->priority = (ipv6_get_dsfield(ipv6_hdr(skb)) &
+					 0xfc) >> 5;
+			break;
+		}
+	}
+}
+#endif
+
 /**
  * ieee80211_subif_start_xmit - netif start_xmit function for 802.3 vifs
  * @skb: packet to be sent
@@ -4542,6 +4571,10 @@ netdev_tx_t ieee80211_subif_start_xmit(s
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	const struct ethhdr *eth = (void *)skb->data;
 
+#ifdef CPTCFG_MAC80211_NSS_SUPPORT
+	ieee80211_xmit_nss_fixup(skb, dev);
+#endif
+
 	if (likely(!is_multicast_ether_addr(eth->h_dest)))
 		goto normal;
 
@@ -4731,6 +4764,9 @@ netdev_tx_t ieee80211_subif_start_xmit_8
 	struct ieee80211_key *key;
 	struct sta_info *sta;
 
+#ifdef CPTCFG_MAC80211_NSS_SUPPORT
+	ieee80211_xmit_nss_fixup(skb, dev);
+#endif
 	if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) {
 		kfree_skb(skb);
 		return NETDEV_TX_OK;
