Merge "[qca-nss-drv] Add GRE redir mark static interface"
diff --git a/Makefile b/Makefile
index 6605399..0fd7b9f 100644
--- a/Makefile
+++ b/Makefile
@@ -39,6 +39,9 @@
 			nss_gre_redir_lag_us_log.o \
 			nss_gre_redir_lag_us_stats.o \
 			nss_gre_redir_stats.o \
+			nss_gre_redir_mark.o \
+			nss_gre_redir_mark_log.o \
+			nss_gre_redir_mark_stats.o \
 			nss_gre_tunnel.o \
 			nss_gre_tunnel_log.o \
 			nss_gre_tunnel_stats.o \
diff --git a/exports/nss_api_if.h b/exports/nss_api_if.h
index 18df2d3..8ed1aff 100644
--- a/exports/nss_api_if.h
+++ b/exports/nss_api_if.h
@@ -78,6 +78,7 @@
 #include "nss_pm.h"
 #include "nss_freq.h"
 #include "nss_tstamp.h"
+#include "nss_gre_redir_mark.h"
 #endif
 
 /**
@@ -96,7 +97,7 @@
 #define NSS_MAX_PHYSICAL_INTERFACES 8	/**< Maximum number of physical interfaces. */
 #define NSS_MAX_VIRTUAL_INTERFACES 16	/**< Maximum number of virtual interfaces. */
 #define NSS_MAX_TUNNEL_INTERFACES 4	/**< Maximum number of tunnel interfaces. */
-#define NSS_MAX_SPECIAL_INTERFACES 56	/**< Maximum number of special interfaces. */
+#define NSS_MAX_SPECIAL_INTERFACES 57	/**< Maximum number of special interfaces. */
 #define NSS_MAX_WIFI_RADIO_INTERFACES 3	/**< Maximum number of radio interfaces. */
 
 /*
@@ -228,6 +229,8 @@
 		/**< Special interface number for timestamp transmit. */
 #define NSS_TSTAMP_RX_INTERFACE (NSS_SPECIAL_IF_START + 55)
 		/**< Special interface number for timestamp receive. */
+#define NSS_GRE_REDIR_MARK_INTERFACE (NSS_SPECIAL_IF_START + 56)
+		/**< Special interface number for GRE redirect mark. */
 
 /**
  * Wireless Multimedia Extention Access Category to TID. @hideinitializer
diff --git a/exports/nss_gre_redir_mark.h b/exports/nss_gre_redir_mark.h
new file mode 100644
index 0000000..8d71f51
--- /dev/null
+++ b/exports/nss_gre_redir_mark.h
@@ -0,0 +1,278 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2019, 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.
+ **************************************************************************
+ */
+
+/**
+ * @file nss_gre_redir_mark.h
+ *	NSS GRE Redirect mark interface definitions.
+ */
+
+#ifndef __NSS_GRE_REDIR_MARK_H
+#define __NSS_GRE_REDIR_MARK_H
+
+/**
+ * nss_gre_redir_mark messages
+ *	Message types for GRE redirect mark requests and responses.
+ */
+enum nss_gre_redir_mark_msg_types {
+	NSS_GRE_REDIR_MARK_REG_CB_MSG,		/**< Register callback configuration message. */
+	NSS_GRE_REDIR_MARK_STATS_SYNC_MSG,	/**< Statistics synchronization message. */
+	NSS_GRE_REDIR_MARK_MSG_MAX,		/**< Maximum message type. */
+};
+
+/**
+ * nss_gre_redir_mark errors
+ *	Error codes for GRE redirect mark configuration message.
+ */
+enum nss_gre_redir_mark_error_types {
+	NSS_GRE_REDIR_MARK_ERROR_NONE,		/**< Configuration successful. */
+	NSS_GRE_REDIR_MARK_ERROR_INV_IF_NUM,	/**< Invalid interface number for callback registration. */
+	NSS_GRE_REDIR_MARK_ERROR_INV_ETH_TYPE,	/**< Invalid Ethernet type for the destination interface. */
+	NSS_GRE_REDIR_MARK_ERROR_TYPE_MAX
+};
+
+/**
+ * nss_gre_redir_mark_metadata
+ *	HLOS to NSS per packet downstream metadata.
+ */
+struct nss_gre_redir_mark_metadata {
+	uint32_t dst_ifnum;		/**< Destination Tx interface number. */
+	uint8_t wifi_tid;		/**< TID value. */
+	uint8_t app_id;			/**< Application ID. */
+	uint16_t hw_hash_idx;		/**< Hardware AST hash index value. */
+	uint32_t tx_status;		/**< Tx status. */
+	uint16_t offset;		/**< Buffer offset from the metadata. */
+	uint16_t magic;			/**< Metadata magic. */
+};
+
+/**
+ * nss_gre_redir_mark_stats
+ *	GRE redirect mark statistics.
+ */
+struct nss_gre_redir_mark_stats_sync_msg {
+	struct nss_cmn_node_stats node_stats;	/**< Common node statistics. */
+	uint32_t hlos_magic_fail;               /**< HLOS magic fail count. */
+	uint32_t invalid_dst_drop;		/**< Invalid Tx interface drop count. */
+	uint32_t dst_enqueue_success;		/**< Next egress interface enqueue success count. */
+	uint32_t dst_enqueue_drop;		/**< Next egress interface enqueue drop count. */
+	uint32_t inv_appid;			/**< Invalid application ID for the Tx completion packets. */
+	uint32_t headroom_unavail;		/**< Packet headroom unavailable to write metadata. */
+	uint32_t tx_completion_success;		/**< Tx completion host enqueue success count. */
+	uint32_t tx_completion_drop;		/**< Tx completion host enqueue drop count. */
+};
+
+/**
+ * nss_gre_redir_mark_register_cb_msg
+ *	Tx completion function register configuration message.
+ */
+struct nss_gre_redir_mark_register_cb_msg {
+	uint32_t nss_if_num;			/**< NSS Tx interface number on which callback needs to be registered. */
+};
+
+/**
+ * nss_gre_redir_mark_msg
+ *	Structure that describes the interface message.
+ */
+struct nss_gre_redir_mark_msg {
+	struct nss_cmn_msg cm;		/**< Common message. */
+
+	/**
+	 * Payload of a GRE redirect mark message.
+	 */
+	union {
+		struct nss_gre_redir_mark_register_cb_msg reg_cb_msg;
+					/**< Configuration message to register for callback on completion. */
+		struct nss_gre_redir_mark_stats_sync_msg stats_sync;
+					/**< Mark node statistics synchronization. */
+	} msg;				/**< Message payload for GRE redirect mark messages exchanged with NSS core. */
+};
+
+/**
+ * Callback function for receiving GRE redirect mark data.
+ *
+ * @datatypes
+ * net_device \n
+ * sk_buff \n
+ * napi_struct
+ *
+ * @param[in] netdev  Pointer to the associated network device.
+ * @param[in] skb     Pointer to the data socket buffer.
+ * @param[in] napi    Pointer to the NAPI structure.
+ */
+typedef void (*nss_gre_redir_mark_data_callback_t)(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi);
+
+/**
+ * Callback function for receiving GRE redirect mark messages.
+ *
+ * @datatypes
+ * nss_cmn_msg
+ *
+ * @param[in] app_data  Pointer to the application context of the message.
+ * @param[in] msg       Pointer to the message data.
+ */
+typedef void (*nss_gre_redir_mark_msg_callback_t)(void *app_data, struct nss_cmn_msg *msg);
+
+/**
+ * nss_gre_redir_mark_unregister_if
+ *      Deregisters a GRE redirect mark interface from the NSS.
+ *
+ * @param[in] if_num  GRE redirect mark interface number.
+ *
+ * @return
+ * None.
+ *
+ * @dependencies
+ * The GRE redirect mark interface must have been previously registered.
+ *
+ * @return
+ * True if successful, else false.
+ */
+extern bool nss_gre_redir_mark_unregister_if(uint32_t if_num);
+
+/**
+ * nss_gre_redir_mark_tx_buf
+ *      Sends data buffers to NSS firmware asynchronously.
+ *
+ * @datatypes
+ * nss_ctx_instance \n
+ * sk_buff
+ *
+ * @param[in] nss_ctx  Pointer to the NSS context.
+ * @param[in] os_buf   Pointer to the OS buffer (e.g. skbuff).
+ * @param[in] if_num   GRE redirect mark interface number.
+ *
+ * @return
+ * Status of the Tx operation.
+ */
+extern nss_tx_status_t nss_gre_redir_mark_tx_buf(struct nss_ctx_instance *nss_ctx, struct sk_buff *os_buf,
+		uint32_t if_num);
+
+/**
+ * nss_gre_redir_exception_ds_reg_cb
+ *	Configure a callback on VAP for downstream GRE exception flows.
+ *
+ * @datatypes
+ * nss_gre_redir_exception_ds_reg_cb_msg
+ *
+ * @param[in] ifnum  NSS interface number.
+ * @param[in] ngrcm  Downstream exception callback registration message.
+ *
+ * @return
+ * Status of Tx operation.
+ */
+extern nss_tx_status_t nss_gre_redir_mark_reg_cb(int ifnum,
+                struct nss_gre_redir_mark_register_cb_msg *ngrcm);
+
+/**
+ * nss_gre_redir_mark_tx_msg
+ *	Sends GRE redirect mark messages.
+ *
+ * @datatypes
+ * nss_ctx_instance \n
+ * nss_gre_redir_msg
+ *
+ * @param[in] nss_ctx  Pointer to the NSS context.
+ * @param[in] msg      Pointer to the message data.
+ *
+ * @return
+ * Status of the Tx operation.
+ */
+extern nss_tx_status_t nss_gre_redir_mark_tx_msg(struct nss_ctx_instance *nss_ctx, struct nss_gre_redir_mark_msg *msg);
+
+/**
+ * nss_gre_redir_mark_tx_msg_sync
+ *	Sends messages to NSS firmware synchronously.
+ *
+ * @datatypes
+ * nss_ctx_instance \n
+ * nss_gre_redir_mark_msg
+ *
+ * @param[in] nss_ctx  NSS core context.
+ * @param[in] ngrm     Pointer to GRE redirect mark message data.
+ *
+ * @return
+ * Status of Tx operation.
+ */
+extern nss_tx_status_t nss_gre_redir_mark_tx_msg_sync(struct nss_ctx_instance *nss_ctx, struct nss_gre_redir_mark_msg *ngrm);
+
+/**
+ * nss_gre_redir_mark_get_stats
+ *      Gets GRE redirect mark statistics.
+ *
+ * @datatypes
+ * nss_gre_redir_mark_stats
+ *
+ * @param[out] stats  Pointer to the memory address, which must be large enough to
+ *                    hold all the statistics.
+ *
+ * @return
+ * TRUE or FALSE.
+ */
+extern bool nss_gre_redir_mark_get_stats(void *stats);
+
+/**
+ * nss_gre_redir_alloc_and_register_node
+ *      Registers GRE redirect mark static node with NSS.
+ *
+ * @datatypes
+ * net_device \n
+ * nss_gre_redir_mark_data_callback_t \n
+ * nss_gre_redir_mark_msg_callback_t
+ *
+ * @param[in] netdev       Pointer to the associated network device.
+ * @param[in] if_num       NSS interface number.
+ * @param[in] cb_func_data Callback for the data.
+ * @param[in] cb_func_msg  Callback for the message.
+ * @param[in] features     Data socket buffer types supported by this interface.
+ *
+ * @return
+ * NSS interface number allocated.
+ */
+extern struct nss_ctx_instance *nss_gre_redir_mark_register_if(struct net_device *netdev, uint32_t if_num,
+		nss_gre_redir_mark_data_callback_t cb_func_data, nss_gre_redir_mark_msg_callback_t cb_func_msg,
+		uint32_t features);
+
+/**
+ * nss_gre_redir_mark_get_context
+ *	Gets the GRE redirect mark context.
+ *
+ * @return
+ * Pointer to the NSS core context.
+ */
+extern struct nss_ctx_instance *nss_gre_redir_mark_get_context(void);
+
+/**
+ * nss_gre_redir_mark_get_dentry
+ *	Returns directory entry created in debug filesystem for statistics.
+ *
+ * @return
+ * Pointer to created directory entry for GRE redirect mark.
+ */
+extern struct dentry *nss_gre_redir_mark_get_dentry(void);
+
+/*
+ * nss_gre_redir_mark_get_device
+ *	Gets the original device from probe.
+ *
+ * @return
+ * Pointer to the device.
+ */
+extern struct device *nss_gre_redir_mark_get_device(void);
+
+/**
+ * @}
+ */
+
+#endif /* __NSS_GRE_REDIR_MARK_H */
diff --git a/nss_core.h b/nss_core.h
index c217140..47e49cc 100644
--- a/nss_core.h
+++ b/nss_core.h
@@ -608,6 +608,7 @@
 	uint8_t qvpn_handler_id;
 	uint8_t pvxlan_handler_id;
 	uint8_t igs_handler_id;
+	uint8_t gre_redir_mark_handler_id;
 
 	/*
 	 * Data/Message callbacks for various interfaces
@@ -913,6 +914,8 @@
 				/* Does this core handle pvxlan? */
 	enum nss_feature_enabled igs_enabled;
 				/* Does this core handle igs? */
+	enum nss_feature_enabled gre_redir_mark_enabled;
+				/* Does this core handle GRE redir mark? */
 };
 #endif
 
diff --git a/nss_gre_redir_mark.c b/nss_gre_redir_mark.c
new file mode 100644
index 0000000..e413030
--- /dev/null
+++ b/nss_gre_redir_mark.c
@@ -0,0 +1,403 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2019, 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.
+ **************************************************************************
+ */
+
+#include "nss_tx_rx_common.h"
+#include "nss_gre_redir_mark_stats.h"
+#include "nss_gre_redir_mark_log.h"
+#define NSS_GRE_REDIR_MARK_TX_TIMEOUT 3000 /* 3 Seconds */
+
+/*
+ * Private data structure for handling synchronous messaging.
+ */
+static struct {
+	struct semaphore sem;
+	struct completion complete;
+	int response;
+} nss_gre_redir_mark_pvt;
+
+/*
+ * Spinlock to update GRE redir mark stats.
+ */
+static DEFINE_SPINLOCK(nss_gre_redir_mark_stats_lock);
+
+/*
+ * Global GRE redir mark stats structure.
+ */
+static struct nss_gre_redir_mark_stats gre_mark_stats;
+
+/*
+ * nss_gre_redir_mark_msg_sync_callback()
+ *	Callback to handle the completion of HLOS-->NSS messages.
+ */
+static void nss_gre_redir_mark_msg_sync_callback(void *app_data, struct nss_gre_redir_mark_msg *nim)
+{
+	nss_gre_redir_mark_pvt.response = NSS_TX_SUCCESS;
+	if (nim->cm.response != NSS_CMN_RESPONSE_ACK) {
+		nss_warning("GRE mark Error response %d\n", nim->cm.response);
+		nss_gre_redir_mark_pvt.response = NSS_TX_FAILURE;
+	}
+
+	complete(&nss_gre_redir_mark_pvt.complete);
+}
+
+/*
+ * nss_gre_redir_mark_stats_sync()
+ *	Update GRE redir mark stats.
+ */
+static void nss_gre_redir_mark_stats_sync(struct nss_ctx_instance *nss_ctx, int if_num, struct nss_gre_redir_mark_stats_sync_msg *ngss)
+{
+	struct net_device *dev;
+	dev = nss_cmn_get_interface_dev(nss_ctx, if_num);
+	if (!dev) {
+		nss_warning("%p: Unable to find net device for the interface %d\n", nss_ctx, if_num);
+		return;
+	}
+
+	if (if_num != NSS_GRE_REDIR_MARK_INTERFACE) {
+		nss_warning("%p: Unknown type for interface %d\n", nss_ctx, if_num);
+		return;
+	}
+
+	/*
+	 * Update the stats in exclusive mode to prevent the read from the process
+	 * context through debug fs.
+	 */
+	spin_lock_bh(&nss_gre_redir_mark_stats_lock);
+
+	/*
+	 * Update the common node stats
+	 */
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_TX_PKTS] += ngss->node_stats.tx_packets;
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_TX_BYTES] += ngss->node_stats.tx_bytes;
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_RX_PKTS] += ngss->node_stats.rx_packets;
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_RX_BYTES] += ngss->node_stats.rx_bytes;
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_RX_DROPS] += nss_cmn_rx_dropped_sum(&(ngss->node_stats));
+
+	/*
+	 * Update the GRE redir mark specific stats
+	 */
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_HLOS_MAGIC_FAILED] += ngss->hlos_magic_fail;
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_INV_DST_IF_DROPS] += ngss->invalid_dst_drop;
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_DST_IF_ENQUEUE] += ngss->dst_enqueue_success;
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_DST_IF_ENQUEUE_DROPS] += ngss->dst_enqueue_drop;
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_INV_APPID] += ngss->inv_appid;
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_HEADROOM_UNAVAILABLE] += ngss->headroom_unavail;
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_TX_COMPLETION_SUCCESS] += ngss->tx_completion_success;
+	gre_mark_stats.stats[NSS_GRE_REDIR_MARK_STATS_TX_COMPLETION_DROPS] += ngss->tx_completion_drop;
+
+	spin_unlock_bh(&nss_gre_redir_mark_stats_lock);
+}
+
+/*
+ * nss_gre_redir_mark_handler()
+ *	Handle NSS to HLOS messages for GRE redir mark
+ */
+static void nss_gre_redir_mark_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm, void *app_data)
+{
+	struct nss_gre_redir_mark_msg *ngrm = (struct nss_gre_redir_mark_msg *)ncm;
+	nss_gre_redir_mark_msg_callback_t cb;
+
+	NSS_VERIFY_CTX_MAGIC(nss_ctx);
+
+	/*
+	 * Is this a valid request/response packet?
+	 */
+	if (ncm->type >= NSS_GRE_REDIR_MARK_MSG_MAX) {
+		nss_warning("%p: received invalid message %d for GRE redir mark interface", nss_ctx, ncm->type);
+		return;
+	}
+
+	if (nss_cmn_get_msg_len(ncm) > sizeof(struct nss_gre_redir_mark_msg)) {
+		nss_warning("%p: length of message is greater than required: %d", nss_ctx, nss_cmn_get_msg_len(ncm));
+		return;
+	}
+
+	/*
+	 * Log failures
+	 */
+	nss_core_log_msg_failures(nss_ctx, ncm);
+
+	/*
+	 * Trace messages.
+	 */
+	nss_gre_redir_mark_log_rx_msg((struct nss_gre_redir_mark_msg *)ncm);
+
+	if (ncm->type == NSS_GRE_REDIR_MARK_STATS_SYNC_MSG) {
+		nss_gre_redir_mark_stats_sync(nss_ctx, ncm->interface, &ngrm->msg.stats_sync);
+	}
+
+	/*
+	 * Update the callback and app_data for NOTIFY messages, GRE redir mark sends all notify messages
+	 * to the same callback/app_data. The app data here represent the netdevice of the GRE redir mark
+	 * interface.
+	 */
+	if (ncm->response == NSS_CMN_RESPONSE_NOTIFY) {
+		ncm->cb = (nss_ptr_t)nss_top_main.if_rx_msg_callback[ncm->interface];
+		ncm->app_data = (nss_ptr_t)nss_ctx->subsys_dp_register[ncm->interface].ndev;
+	}
+
+	/*
+	 * load and call the registered synchronous message callback.
+	 */
+	cb = (nss_gre_redir_mark_msg_callback_t)ncm->cb;
+	if (unlikely(!cb)) {
+		return;
+	}
+
+	cb((void *)ncm->app_data, ncm);
+}
+
+/*
+ * nss_gre_redir_mark_get_stats()
+ *	Get gre_redir tunnel stats.
+ */
+bool nss_gre_redir_mark_get_stats(void *stats_mem)
+{
+	struct nss_gre_redir_mark_stats *stats = (struct nss_gre_redir_mark_stats *)stats_mem;
+	if (!stats) {
+		nss_warning("No memory to copy GRE redir mark stats");
+		return false;
+	}
+
+	/*
+	 * Copy the GRE redir mark stats in the memory.
+	 */
+	spin_lock_bh(&nss_gre_redir_mark_stats_lock);
+	memcpy(stats, &gre_mark_stats, sizeof(struct nss_gre_redir_mark_stats));
+	spin_unlock_bh(&nss_gre_redir_mark_stats_lock);
+	return true;
+}
+EXPORT_SYMBOL(nss_gre_redir_mark_get_stats);
+
+/*
+ * nss_gre_redir_mark_reg_cb()
+ *	Configure a callback on VAP.
+ */
+nss_tx_status_t nss_gre_redir_mark_reg_cb(int ifnum,
+                struct nss_gre_redir_mark_register_cb_msg *ngrcm)
+{
+        struct nss_gre_redir_mark_msg config;
+        struct nss_ctx_instance *nss_ctx __maybe_unused = nss_gre_redir_mark_get_context();
+        nss_tx_status_t status;
+        uint32_t vap_type;
+        uint32_t len = sizeof(struct nss_gre_redir_mark_register_cb_msg);
+
+        if (!nss_ctx) {
+                nss_warning("Unable to retrieve NSS context.\n");
+                return NSS_TX_FAILURE_BAD_PARAM;
+        }
+
+        vap_type = nss_dynamic_interface_get_type(nss_ctx, ngrcm->nss_if_num);
+        if ((vap_type != NSS_DYNAMIC_INTERFACE_TYPE_VAP)) {
+                nss_warning("%p: Incorrect type for vap interface type = %u", nss_ctx, vap_type);
+                return NSS_TX_FAILURE_BAD_PARAM;
+        }
+
+        /*
+         * Configure the node
+         */
+        nss_cmn_msg_init(&config.cm, NSS_GRE_REDIR_MARK_INTERFACE, NSS_GRE_REDIR_MARK_REG_CB_MSG, len, NULL, NULL);
+        config.msg.reg_cb_msg.nss_if_num = ngrcm->nss_if_num;
+
+        status = nss_gre_redir_mark_tx_msg_sync(nss_ctx, &config);
+        if (status != NSS_TX_SUCCESS) {
+                nss_warning("%p: Unable to register callback from GRE redir mark interface %d\n", nss_ctx, ifnum);
+        }
+
+        return status;
+}
+EXPORT_SYMBOL(nss_gre_redir_mark_reg_cb);
+
+/*
+ * nss_gre_redir_mark_tx_msg()
+ *	Transmit a GRE MARK configuration message to NSS FW.
+ */
+nss_tx_status_t nss_gre_redir_mark_tx_msg(struct nss_ctx_instance *nss_ctx, struct nss_gre_redir_mark_msg *msg)
+{
+	struct nss_cmn_msg *ncm = &msg->cm;
+
+	/*
+	 * Trace Messages
+	 */
+	nss_gre_redir_mark_log_tx_msg(msg);
+
+	/*
+	 * interface should be of type of redir mark
+	 */
+	if (ncm->interface != NSS_GRE_REDIR_MARK_INTERFACE) {
+		nss_warning("%p: tx request for another interface: %d", nss_ctx, ncm->interface);
+		return NSS_TX_FAILURE;
+	}
+
+	if (ncm->type >= NSS_GRE_REDIR_MARK_MSG_MAX) {
+		nss_warning("%p: message type out of range: %d", nss_ctx, ncm->type);
+		return NSS_TX_FAILURE;
+	}
+
+	return nss_core_send_cmd(nss_ctx, msg, sizeof(*msg), NSS_NBUF_PAYLOAD_SIZE);
+}
+EXPORT_SYMBOL(nss_gre_redir_mark_tx_msg);
+
+/*
+ * nss_gre_redir_mark_tx_msg_sync()
+ *	Transmit a GRE redir mark message to NSS firmware synchronously.
+ */
+nss_tx_status_t nss_gre_redir_mark_tx_msg_sync(struct nss_ctx_instance *nss_ctx, struct nss_gre_redir_mark_msg *ngrm)
+{
+	nss_tx_status_t status;
+	int ret = 0;
+
+	/*
+	 * Decrease the semaphore count to send the message exclusively.
+	 */
+	down(&nss_gre_redir_mark_pvt.sem);
+	ngrm->cm.cb = (nss_ptr_t)nss_gre_redir_mark_msg_sync_callback;
+	ngrm->cm.app_data = (nss_ptr_t)NULL;
+	status = nss_gre_redir_mark_tx_msg(nss_ctx, ngrm);
+	if (status != NSS_TX_SUCCESS) {
+		nss_warning("%p: GRE redir mark tx_msg failed\n", nss_ctx);
+		up(&nss_gre_redir_mark_pvt.sem);
+		return status;
+	}
+
+	ret = wait_for_completion_timeout(&nss_gre_redir_mark_pvt.complete, msecs_to_jiffies(NSS_GRE_REDIR_MARK_TX_TIMEOUT));
+	if (!ret) {
+		nss_warning("%p: GRE redir mark message tx sync failed due to timeout\n", nss_ctx);
+		nss_gre_redir_mark_pvt.response = NSS_TX_FAILURE;
+	}
+
+	status = nss_gre_redir_mark_pvt.response;
+	up(&nss_gre_redir_mark_pvt.sem);
+	return status;
+}
+EXPORT_SYMBOL(nss_gre_redir_mark_tx_msg_sync);
+
+/*
+ * nss_gre_redir_mark_tx_buf()
+ *	Send packet to GRE redir mark interface owned by NSS.
+ */
+nss_tx_status_t nss_gre_redir_mark_tx_buf(struct nss_ctx_instance *nss_ctx, struct sk_buff *os_buf, uint32_t if_num)
+{
+	nss_trace("%p: GRE redir mark If Tx packet, interface id:%d, data=%p", nss_ctx, if_num, os_buf->data);
+
+	/*
+	 * We expect Tx packets to the GRE redir mark interface only.
+	 */
+	if (if_num != NSS_GRE_REDIR_MARK_INTERFACE) {
+		nss_warning("%p: Invalid interface:%d for GRE redir mark packets\n", nss_ctx, if_num);
+		return NSS_TX_FAILURE_BAD_PARAM;
+	}
+
+	return nss_core_send_packet(nss_ctx, os_buf, if_num, 0);
+}
+EXPORT_SYMBOL(nss_gre_redir_mark_tx_buf);
+
+/*
+ * nss_gre_redir_mark_get_context()
+ *	Return NSS GRE redir mark context.
+ */
+struct nss_ctx_instance *nss_gre_redir_mark_get_context(void)
+{
+	return &nss_top_main.nss[nss_top_main.gre_redir_mark_handler_id];
+}
+EXPORT_SYMBOL(nss_gre_redir_mark_get_context);
+
+/*
+ * nss_gre_redir_mark_unregister_if()
+ *	Unregister dynamic node for GRE_REDIR_MARK redir.
+ */
+bool nss_gre_redir_mark_unregister_if(uint32_t if_num)
+{
+	struct nss_ctx_instance *nss_ctx __maybe_unused = (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.gre_redir_handler_id];
+	struct net_device *dev;
+
+	nss_assert(nss_ctx);
+	nss_assert(if_num == NSS_GRE_REDIR_MARK_INTERFACE);
+
+	dev = nss_cmn_get_interface_dev(nss_ctx, if_num);
+
+	BUG_ON(!dev);
+
+	nss_core_unregister_subsys_dp(nss_ctx, if_num);
+	nss_top_main.if_rx_msg_callback[if_num] = NULL;
+	return true;
+}
+EXPORT_SYMBOL(nss_gre_redir_mark_unregister_if);
+
+/*
+ * nss_gre_redir_mark_register_if()
+ *	Register staticr GRE redir mark interface with data-plane.
+ */
+struct nss_ctx_instance *nss_gre_redir_mark_register_if(struct net_device *netdev, uint32_t if_num,
+		nss_gre_redir_mark_data_callback_t cb_func_data, nss_gre_redir_mark_msg_callback_t cb_func_msg,
+		uint32_t features)
+{
+	struct nss_ctx_instance *nss_ctx = (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.gre_redir_handler_id];
+
+	nss_assert(nss_ctx);
+	nss_assert(if_num == NSS_GRE_REDIR_MARK_INTERFACE);
+
+	/*
+	 * Registering the interface with network data path.
+	 */
+	nss_core_register_subsys_dp(nss_ctx, if_num, cb_func_data, NULL, NULL, netdev, features);
+	nss_top_main.if_rx_msg_callback[if_num] = cb_func_msg;
+	return nss_ctx;
+}
+EXPORT_SYMBOL(nss_gre_redir_mark_register_if);
+
+/*
+ * nss_gre_redir_mark_get_device()
+ *	Gets the original device from probe.
+ */
+struct device *nss_gre_redir_mark_get_device(void)
+{
+	struct nss_ctx_instance *nss_ctx = nss_gre_redir_mark_get_context();
+	return nss_ctx->dev;
+}
+EXPORT_SYMBOL(nss_gre_redir_mark_get_device);
+
+/*
+ * nss_gre_redir_mark_register_handler()
+ *	Register GRE redir mark and register handler
+ */
+void nss_gre_redir_mark_register_handler(void)
+{
+	struct nss_ctx_instance *nss_ctx = nss_gre_redir_mark_get_context();
+	struct dentry *gre_redir_mark_dentry = NULL;
+	uint32_t status = NSS_CORE_STATUS_FAILURE;
+
+	/*
+	 * Create the debug fs entry for the stats.
+	 */
+	gre_redir_mark_dentry = nss_gre_redir_mark_stats_dentry_create();
+	if (!gre_redir_mark_dentry) {
+		nss_warning("%p: Not able to create debugfs entry\n", nss_ctx);
+		return;
+	}
+
+	sema_init(&nss_gre_redir_mark_pvt.sem, 1);
+	init_completion(&nss_gre_redir_mark_pvt.complete);
+
+	nss_info("nss_gre_redir_mark_register_handler\n");
+	status = nss_core_register_handler(nss_ctx, NSS_GRE_REDIR_MARK_INTERFACE, nss_gre_redir_mark_handler, NULL);
+	if (status != NSS_CORE_STATUS_SUCCESS) {
+		debugfs_remove_recursive(gre_redir_mark_dentry);
+		gre_redir_mark_dentry = NULL;
+		nss_warning("%p: Not able to register handler for GRE redir mark with NSS core\n", nss_ctx);
+		return;
+	}
+}
diff --git a/nss_gre_redir_mark_log.c b/nss_gre_redir_mark_log.c
new file mode 100644
index 0000000..e99e6f2
--- /dev/null
+++ b/nss_gre_redir_mark_log.c
@@ -0,0 +1,121 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2019, 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.
+ **************************************************************************
+ */
+
+
+#include "nss_core.h"
+
+/*
+ * nss_gre_redir_mark_log_message_types_str
+ *	GRE redir mark message strings
+ */
+static int8_t *nss_gre_redir_mark_log_message_types_str[NSS_GRE_REDIR_MARK_MSG_MAX] __maybe_unused = {
+	"GRE redir mark register callback message",
+	"GRE redir mark statistics synchronization"
+};
+
+/*
+ * nss_gre_redir_mark_log_error_response_types_str
+ *	Strings for error types for GRE redir mark messages
+ */
+static int8_t *nss_gre_redir_mark_log_error_response_types_str[NSS_GRE_REDIR_MARK_ERROR_TYPE_MAX] __maybe_unused = {
+	"GRE redir mark No error",
+	"GRE redir mark Invalid interface for callback registration",
+	"GRE redir mark Invalid ethertype for Tx interface"
+};
+
+/*
+ * nss_gre_redir_mark_log_reg_cb_msg()
+ *	Log NSS GRE redir mark configuration message
+ */
+static void nss_gre_redir_mark_log_reg_cb_msg(struct nss_gre_redir_mark_msg *ncm)
+{
+	struct nss_gre_redir_mark_register_cb_msg *reg_cb_msg __maybe_unused = &ncm->msg.reg_cb_msg;
+	nss_trace("%p: NSS GRE redir mark callback registration message \n"
+			"nss_if_num: %d\n", ncm, reg_cb_msg->nss_if_num);
+}
+
+/*
+ * nss_gre_redir_mark_log_verbose()
+ *	Log message contents.
+ */
+static void nss_gre_redir_mark_log_verbose(struct nss_gre_redir_mark_msg *ncm)
+{
+	switch (ncm->cm.type) {
+	case NSS_GRE_REDIR_MARK_REG_CB_MSG:
+		nss_gre_redir_mark_log_reg_cb_msg(ncm);
+		break;
+
+	case NSS_GRE_REDIR_MARK_STATS_SYNC_MSG:
+		/*
+		 * No log for valid stats message.
+		 */
+		break;
+
+	default:
+		nss_trace("%p: Invalid message type\n", ncm);
+		break;
+	}
+}
+
+/*
+ * nss_gre_redir_mark_log_tx_msg()
+ *	Log messages transmitted to FW.
+ */
+void nss_gre_redir_mark_log_tx_msg(struct nss_gre_redir_mark_msg *ngm)
+{
+	if (ngm->cm.type >= NSS_GRE_REDIR_MARK_MSG_MAX) {
+		nss_warning("%p: Invalid message type\n", ngm);
+		return;
+	}
+
+	nss_info("%p: type[%d]:%s\n", ngm, ngm->cm.type, nss_gre_redir_mark_log_message_types_str[ngm->cm.type]);
+	nss_gre_redir_mark_log_verbose(ngm);
+}
+/*
+ * nss_gre_redir_mark_log_rx_msg()
+ *	Log messages received from FW.
+ */
+void nss_gre_redir_mark_log_rx_msg(struct nss_gre_redir_mark_msg *ncm)
+{
+	if (ncm->cm.response >= NSS_CMN_RESPONSE_LAST) {
+		nss_warning("%p: Invalid response\n", ncm);
+		return;
+	}
+
+	if (ncm->cm.response == NSS_CMN_RESPONSE_NOTIFY || (ncm->cm.response == NSS_CMN_RESPONSE_ACK)) {
+		nss_info("%p: type[%d]:%s, response[%d]:%s\n", ncm, ncm->cm.type,
+			nss_gre_redir_mark_log_message_types_str[ncm->cm.type],
+			ncm->cm.response, nss_cmn_response_str[ncm->cm.response]);
+		goto verbose;
+	}
+
+	if (ncm->cm.error >= NSS_GRE_REDIR_MARK_ERROR_TYPE_MAX) {
+		nss_warning("%p: msg failure - type[%d]:%s, response[%d]:%s, error[%d]:Invalid error\n",
+			ncm, ncm->cm.type, nss_gre_redir_mark_log_message_types_str[ncm->cm.type],
+			ncm->cm.response, nss_cmn_response_str[ncm->cm.response],
+			ncm->cm.error);
+		goto verbose;
+	}
+
+	nss_info("%p: msg nack - type[%d]:%s, response[%d]:%s, error[%d]:%s\n",
+		ncm, ncm->cm.type, nss_gre_redir_mark_log_message_types_str[ncm->cm.type],
+		ncm->cm.response, nss_cmn_response_str[ncm->cm.response],
+		ncm->cm.error, nss_gre_redir_mark_log_error_response_types_str[ncm->cm.error]);
+
+verbose:
+	nss_gre_redir_mark_log_verbose(ncm);
+}
+
diff --git a/nss_gre_redir_mark_log.h b/nss_gre_redir_mark_log.h
new file mode 100644
index 0000000..27e2ffb
--- /dev/null
+++ b/nss_gre_redir_mark_log.h
@@ -0,0 +1,37 @@
+/*
+ ******************************************************************************
+ * Copyright (c) 2019, 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.
+ * ****************************************************************************
+ */
+
+#ifndef __NSS_GRE_REDIR_MARK_LOG_H__
+#define __NSS_GRE_REDIR_MARK_LOG_H__
+
+/*
+ * nss_gre_redir_mark_log.h
+ *	NSS GRE_REDIR_MARK Log Header File.
+ */
+
+/*
+ * nss_gre_redir_mark_log_tx_msg
+ *	Logs GRE_REDIR_MARK message that is sent to the NSS firmware.
+ */
+void nss_gre_redir_mark_log_tx_msg(struct nss_gre_redir_mark_msg *ncm);
+
+/*
+ * nss_gre_redir_mark_log_rx_msg
+ *	Logs GRE_REDIR_MARK message that is received from the NSS firmware.
+ */
+void nss_gre_redir_mark_log_rx_msg(struct nss_gre_redir_mark_msg *ncm);
+
+#endif /* __NSS_GRE_REDIR_MARK_LOG_H__ */
diff --git a/nss_gre_redir_mark_stats.c b/nss_gre_redir_mark_stats.c
new file mode 100644
index 0000000..11f1841
--- /dev/null
+++ b/nss_gre_redir_mark_stats.c
@@ -0,0 +1,163 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2019, 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.
+ **************************************************************************
+ */
+
+#include "nss_core.h"
+#include "nss_stats.h"
+#include "nss_gre_redir_mark.h"
+#include "nss_gre_redir_mark_stats.h"
+
+#define NSS_GRE_REDIR_MARK_STATS_STR_LEN 50
+#define NSS_GRE_REDIR_MARK_STATS_LEN ((NSS_GRE_REDIR_MARK_STATS_MAX + 7 ) * NSS_GRE_REDIR_MARK_STATS_STR_LEN)
+/*
+ * nss_gre_redir_mark_stats_str
+ *	GRE redir mark statistics string
+ */
+static int8_t *nss_gre_redir_mark_stats_str[NSS_GRE_REDIR_MARK_STATS_MAX] = {
+	"TX Packets",
+	"TX Bytes",
+	"RX Packets",
+	"RX Bytes",
+	"RX Drops",
+	"HLOS magic failed",
+	"Tx inv_dst_if Drops",
+	"Tx dst_if Enqueue",
+	"Tx dst_if Enqueue Drops",
+	"Invalid appid",
+	"Headroom Unavailable",
+	"Tx Completion Host Enqueue Success",
+	"Tx Completion Host Enqueue Drops",
+};
+
+/*
+ * nss_gre_redir_mark_stats_cpy()
+ *	Fill the stats.
+ */
+static ssize_t nss_gre_redir_mark_stats_cpy(char *lbuf, int len, int i, struct nss_gre_redir_mark_stats *s)
+{
+	uint64_t tcnt = 0;
+
+	switch (i) {
+	case NSS_GRE_REDIR_MARK_STATS_TX_PKTS:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_TX_PKTS];
+		return scnprintf(lbuf, len, "Common node stats start:\n\n%s = %llu\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	case NSS_GRE_REDIR_MARK_STATS_TX_BYTES:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_TX_BYTES];
+		return scnprintf(lbuf, len, "%s = %llu\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	case NSS_GRE_REDIR_MARK_STATS_RX_PKTS:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_RX_PKTS];
+		return scnprintf(lbuf, len, "%s = %llu\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	case NSS_GRE_REDIR_MARK_STATS_RX_BYTES:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_RX_BYTES];
+		return scnprintf(lbuf, len, "%s = %llu\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	case NSS_GRE_REDIR_MARK_STATS_RX_DROPS:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_RX_DROPS];
+		return scnprintf(lbuf, len, "%s = %llu\nCommon node stats end.\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	case NSS_GRE_REDIR_MARK_STATS_HLOS_MAGIC_FAILED:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_HLOS_MAGIC_FAILED];
+		return scnprintf(lbuf, len, "Offload stats start:\n\n%s = %llu\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	case NSS_GRE_REDIR_MARK_STATS_INV_DST_IF_DROPS:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_INV_DST_IF_DROPS];
+		return scnprintf(lbuf, len, "%s = %llu\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	case NSS_GRE_REDIR_MARK_STATS_DST_IF_ENQUEUE:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_DST_IF_ENQUEUE];
+		return scnprintf(lbuf, len, "%s = %llu\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	case NSS_GRE_REDIR_MARK_STATS_DST_IF_ENQUEUE_DROPS:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_DST_IF_ENQUEUE_DROPS];
+		return scnprintf(lbuf, len, "%s = %llu\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	case NSS_GRE_REDIR_MARK_STATS_INV_APPID:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_INV_APPID];
+		return scnprintf(lbuf, len, "%s = %llu\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	case NSS_GRE_REDIR_MARK_STATS_HEADROOM_UNAVAILABLE:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_HEADROOM_UNAVAILABLE];
+		return scnprintf(lbuf, len, "%s = %llu\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	case NSS_GRE_REDIR_MARK_STATS_TX_COMPLETION_SUCCESS:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_TX_COMPLETION_SUCCESS];
+		return scnprintf(lbuf, len, "%s = %llu\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	case NSS_GRE_REDIR_MARK_STATS_TX_COMPLETION_DROPS:
+		tcnt = s->stats[NSS_GRE_REDIR_MARK_STATS_TX_COMPLETION_DROPS];
+		return scnprintf(lbuf, len, "%s = %llu\nOffload stats end.\n", nss_gre_redir_mark_stats_str[i], tcnt);
+	default:
+		nss_warning("Unknown stats type %d.\n", i);
+		return 0;
+	}
+}
+
+/*
+ * nss_gre_redir_mark_stats_read()
+ *	READ GRE redir mark stats.
+ */
+static ssize_t nss_gre_redir_mark_stats_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+	struct nss_gre_redir_mark_stats stats;
+	size_t size_wr = 0;
+	int start, end;
+	ssize_t bytes_read = 0;
+	bool isthere;
+	size_t size_al = ((NSS_GRE_REDIR_MARK_STATS_MAX + 7 ) * NSS_GRE_REDIR_MARK_STATS_STR_LEN);
+
+	char *lbuf = kzalloc(size_al, GFP_KERNEL);
+	if (unlikely(!lbuf)) {
+		nss_warning("Could not allocate memory for local statistics buffer");
+		return 0;
+	}
+
+	/*
+	 * If GRE redir mark does not exists, then (isthere) will be false.
+	 */
+	isthere = nss_gre_redir_mark_get_stats((void*)&stats);
+	if (!isthere) {
+		nss_warning("Could not get GRE redirect stats");
+		kfree(lbuf);
+		return 0;
+	}
+
+	size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "\nGRE redir mark stats\n");
+
+	start = NSS_GRE_REDIR_MARK_STATS_TX_PKTS;
+	end = NSS_GRE_REDIR_MARK_STATS_MAX;
+	while (start < end) {
+		size_wr += nss_gre_redir_mark_stats_cpy(lbuf + size_wr, size_al - size_wr, start, &stats);
+		start++;
+	}
+
+	bytes_read = simple_read_from_buffer(ubuf, sz, ppos, lbuf, size_wr);
+
+	kfree(lbuf);
+	return bytes_read;
+}
+
+/*
+ * nss_gre_redir_mark_stats_ops
+ */
+NSS_STATS_DECLARE_FILE_OPERATIONS(gre_redir_mark)
+
+/*
+ * nss_gre_redir_mark_stats_dentry_create()
+ *	Create debugfs directory entry for stats.
+ */
+struct dentry *nss_gre_redir_mark_stats_dentry_create(void)
+{
+	struct dentry *gre_redir_mark;
+
+	gre_redir_mark = debugfs_create_file("gre_redir_mark", 0400, nss_top_main.stats_dentry,
+			&nss_top_main, &nss_gre_redir_mark_stats_ops);
+	if (unlikely(!gre_redir_mark)) {
+		nss_warning("Failed to create file entry qca-nss-drv/stats/gre_redir_mark/\n");
+		return NULL;
+	}
+
+	return gre_redir_mark;
+}
diff --git a/nss_gre_redir_mark_stats.h b/nss_gre_redir_mark_stats.h
new file mode 100644
index 0000000..8017f6d
--- /dev/null
+++ b/nss_gre_redir_mark_stats.h
@@ -0,0 +1,52 @@
+/*
+ ******************************************************************************
+ * Copyright (c) 2019, 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.
+ * ****************************************************************************
+ */
+
+#ifndef __NSS_GRE_REDIR_MARK_STATS_H__
+#define __NSS_GRE_REDIR_MARK_STATS_H__
+
+/*
+ * GRE REDIR statistics types
+ */
+enum nss_gre_redir_mark_stats_types {
+	NSS_GRE_REDIR_MARK_STATS_TX_PKTS,
+	NSS_GRE_REDIR_MARK_STATS_TX_BYTES,
+	NSS_GRE_REDIR_MARK_STATS_RX_PKTS,
+	NSS_GRE_REDIR_MARK_STATS_RX_BYTES,
+	NSS_GRE_REDIR_MARK_STATS_RX_DROPS,
+	NSS_GRE_REDIR_MARK_STATS_HLOS_MAGIC_FAILED,
+	NSS_GRE_REDIR_MARK_STATS_INV_DST_IF_DROPS,
+	NSS_GRE_REDIR_MARK_STATS_DST_IF_ENQUEUE,
+	NSS_GRE_REDIR_MARK_STATS_DST_IF_ENQUEUE_DROPS,
+	NSS_GRE_REDIR_MARK_STATS_INV_APPID,
+	NSS_GRE_REDIR_MARK_STATS_HEADROOM_UNAVAILABLE,
+	NSS_GRE_REDIR_MARK_STATS_TX_COMPLETION_SUCCESS,
+	NSS_GRE_REDIR_MARK_STATS_TX_COMPLETION_DROPS,
+	NSS_GRE_REDIR_MARK_STATS_MAX
+};
+
+/*
+ * NSS core stats -- for H2N/N2H gre_redir_mark debug stats
+ */
+struct nss_gre_redir_mark_stats {
+	uint64_t stats[NSS_GRE_REDIR_MARK_STATS_MAX];
+};
+
+/*
+ * NSS GRE REDIR Mark statistics APIs
+ */
+extern struct dentry *nss_gre_redir_mark_stats_dentry_create(void);
+
+#endif /* __NSS_GRE_REDIR_MARK_STATS_H__ */
diff --git a/nss_hal/nss_hal.c b/nss_hal/nss_hal.c
index 9315e30..75f68d6 100644
--- a/nss_hal/nss_hal.c
+++ b/nss_hal/nss_hal.c
@@ -107,6 +107,7 @@
 	npd->gre_enabled = of_property_read_bool(np, "qcom,gre-enabled");
 	npd->gre_redir_enabled = of_property_read_bool(np, "qcom,gre-redir-enabled");
 	npd->gre_tunnel_enabled = of_property_read_bool(np, "qcom,gre_tunnel_enabled");
+	npd->gre_redir_mark_enabled = of_property_read_bool(np, "qcom,gre-redir-mark-enabled");
 	npd->ipsec_enabled = of_property_read_bool(np, "qcom,ipsec-enabled");
 	npd->ipv4_enabled = of_property_read_bool(np, "qcom,ipv4-enabled");
 	npd->ipv4_reasm_enabled = of_property_read_bool(np, "qcom,ipv4-reasm-enabled");
@@ -536,6 +537,11 @@
 		nss_info("%d: NSS IGS is enabled", nss_dev->id);
 	}
 
+	if (npd->gre_redir_mark_enabled == NSS_FEATURE_ENABLED) {
+		nss_top->gre_redir_mark_handler_id = nss_dev->id;
+		nss_gre_redir_mark_register_handler();
+	}
+
 	if (nss_ctx->id == 0) {
 #if (NSS_FREQ_SCALE_SUPPORT == 1)
 		nss_freq_register_handler();
diff --git a/nss_tx_rx_common.h b/nss_tx_rx_common.h
index 3397e20..d747279 100644
--- a/nss_tx_rx_common.h
+++ b/nss_tx_rx_common.h
@@ -92,6 +92,7 @@
 extern void nss_trustsec_tx_register_handler(void);
 extern void nss_wifili_register_handler(void);
 extern void nss_ppe_register_handler(void);
+extern void nss_gre_redir_mark_register_handler(void);
 
 /*
  * nss_if_msg_handler()