[qca-nss-drv] Add N2H statistics notifier

Change-Id: I3525f26a8b218aa3c7678c9179dec84b99824958
Signed-off-by: Wayne Tan <wtan@codeaurora.org>
diff --git a/Makefile b/Makefile
index 957d3ab..d1bc7f4 100644
--- a/Makefile
+++ b/Makefile
@@ -25,6 +25,7 @@
 			nss_core.o \
 			nss_coredump.o \
 			nss_drv_stats.o \
+			nss_drv_strings.o \
 			nss_dynamic_interface.o \
 			nss_dynamic_interface_log.o \
 			nss_dynamic_interface_stats.o \
@@ -88,6 +89,7 @@
 			nss_mirror_stats.o \
 			nss_n2h.o \
 			nss_n2h_stats.o \
+			nss_n2h_strings.o \
 			nss_oam.o \
 			nss_oam_log.o \
 			nss_phys_if.o \
diff --git a/exports/nss_n2h.h b/exports/nss_n2h.h
index 8c20548..1613f41 100644
--- a/exports/nss_n2h.h
+++ b/exports/nss_n2h.h
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2020, 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.
@@ -50,6 +50,8 @@
 	uint32_t high_water;
 };
 
+#ifdef __KERNEL__ /* only kernel will use. */
+
 /**
  * nss_n2h_cfg_pvt
  *	N2H private data configuration.
@@ -63,6 +65,50 @@
 	int response;						/**< Response from the firmware. */
 };
 
+#endif /*__KERNEL__ */
+
+/**
+ * nss_n2h_stats_types
+ *	N2H node statistics.
+ */
+enum nss_n2h_stats_types {
+	NSS_N2H_STATS_QUEUE_DROPPED = NSS_STATS_NODE_MAX,
+		/**< Number of packets dropped because the exception queue is too full. */
+	NSS_N2H_STATS_TOTAL_TICKS,	/**< Total clock ticks spend inside the N2H. */
+	NSS_N2H_STATS_WORST_CASE_TICKS,	/**< Worst case iteration of the exception path in ticks. */
+	NSS_N2H_STATS_ITERATIONS,	/**< Number of iterations around the N2H. */
+	NSS_N2H_STATS_PBUF_OCM_TOTAL_COUNT,	/**< Number of pbuf OCM total count. */
+	NSS_N2H_STATS_PBUF_OCM_FREE_COUNT,	/**< Number of pbuf OCM free count. */
+	NSS_N2H_STATS_PBUF_OCM_ALLOC_FAILS_WITH_PAYLOAD,
+					/**< Number of pbuf OCM allocations that have failed with payload. */
+	NSS_N2H_STATS_PBUF_OCM_ALLOC_FAILS_NO_PAYLOAD,
+					/**< Number of pbuf OCM allocations that have failed without payload. */
+	NSS_N2H_STATS_PBUF_DEFAULT_TOTAL_COUNT,	/**< Number of pbuf default total count. */
+	NSS_N2H_STATS_PBUF_DEFAULT_FREE_COUNT,	/**< Number of pbuf default free count. */
+	NSS_N2H_STATS_PBUF_DEFAULT_ALLOC_FAILS_WITH_PAYLOAD,
+					/**< Number of pbuf default allocations that have failed with payload. */
+	NSS_N2H_STATS_PBUF_DEFAULT_ALLOC_FAILS_NO_PAYLOAD,
+	/**< Number of pbuf default allocations that have failed without payload. */
+
+	NSS_N2H_STATS_PAYLOAD_ALLOC_FAILS,	/**< Number of pbuf allocations that have failed because there were no free payloads. */
+	NSS_N2H_STATS_PAYLOAD_FREE_COUNT,	/**< Number of free payloads that exist. */
+
+	NSS_N2H_STATS_H2N_CONTROL_PACKETS,	/**< Control packets received from HLOS. */
+	NSS_N2H_STATS_H2N_CONTROL_BYTES,	/**< Control bytes received from HLOS. */
+	NSS_N2H_STATS_N2H_CONTROL_PACKETS,	/**< Control packets sent to HLOS. */
+	NSS_N2H_STATS_N2H_CONTROL_BYTES,	/**< Control bytes sent to HLOS. */
+
+	NSS_N2H_STATS_H2N_DATA_PACKETS,		/**< Data packets received from HLOS. */
+	NSS_N2H_STATS_H2N_DATA_BYTES,		/**< Data bytes received from HLOS. */
+	NSS_N2H_STATS_N2H_DATA_PACKETS,		/**< Data packets sent to HLOS. */
+	NSS_N2H_STATS_N2H_DATA_BYTES,		/**< Data bytes sent to HLOS. */
+	NSS_N2H_STATS_N2H_TOT_PAYLOADS,		/**< Number of payloads in NSS. */
+	NSS_N2H_STATS_N2H_INTERFACE_INVALID,	/**< Number of bad interface access. */
+	NSS_N2H_STATS_ENQUEUE_RETRIES,		/**< Number of enqueue retries by N2H. */
+
+	NSS_N2H_STATS_MAX,			/**< Maximum message type. */
+};
+
 /**
  * nss_n2h_metadata_types
  *	Message types for N2H requests and responses.
@@ -110,6 +156,16 @@
 };
 
 /**
+ * nss_n2h_stats_notification
+ *	N2H statistics structure.
+ */
+struct nss_n2h_stats_notification {
+	uint32_t core_id;			/**< Core ID. */
+	uint64_t n2h_stats[NSS_N2H_STATS_MAX];	/**< N2H statistics. */
+	uint64_t drv_stats[NSS_STATS_DRV_MAX];	/**< Driver statistics. */
+};
+
+/**
  * nss_n2h_rps
  *	N2H RPS configuration.
  */
@@ -477,6 +533,38 @@
  */
 extern nss_tx_status_t nss_n2h_update_queue_config_async(struct nss_ctx_instance *nss_ctx, bool mq_en, uint16_t *qlimits);
 
+#ifdef __KERNEL__ /* only kernel will use. */
+
+/**
+ * nss_n2h_stats_register_notifier
+ *	Registers a statistics notifier.
+ *
+ * @datatypes
+ * notifier_block
+ *
+ * @param[in] nb Notifier block.
+ *
+ * @return
+ * 0 on success or -2 on failure.
+ */
+extern int nss_n2h_stats_register_notifier(struct notifier_block *nb);
+
+/**
+ * nss_n2h_stats_unregister_notifier
+ *	Deregisters a statistics notifier.
+ *
+ * @datatypes
+ * notifier_block
+ *
+ * @param[in] nb Notifier block.
+ *
+ * @return
+ * 0 on success or -2 on failure.
+ */
+extern int nss_n2h_stats_unregister_notifier(struct notifier_block *nb);
+
+#endif /*__KERNEL__ */
+
 /**
  * @}
  */
diff --git a/exports/nss_stats_public.h b/exports/nss_stats_public.h
index 4f1baa4..f282ffd 100644
--- a/exports/nss_stats_public.h
+++ b/exports/nss_stats_public.h
@@ -53,6 +53,55 @@
 	NSS_STATS_NODE_MAX,			/**< Maximum message type. */
 };
 
+/*
+ * WARNING: There is a 1:1 mapping between values of enum nss_stats_drv and corresponding
+ * statistics string array in nss_drv_strings.c.
+ */
+/**
+ * nss_stats_drv
+ *	HLOS driver statistics.
+ */
+enum nss_stats_drv {
+	NSS_STATS_DRV_NBUF_ALLOC_FAILS = 0,	/**< Networking buffer allocation errors. */
+	NSS_STATS_DRV_PAGED_BUF_ALLOC_FAILS,	/**< Paged buffer allocation errors. */
+	NSS_STATS_DRV_TX_QUEUE_FULL_0,		/**< Tx queue full for Core 0. */
+	NSS_STATS_DRV_TX_QUEUE_FULL_1,		/**< Tx queue full for Core 1. */
+	NSS_STATS_DRV_TX_EMPTY,			/**< Host-to-network empty buffers. */
+	NSS_STATS_DRV_PAGED_TX_EMPTY,		/**< Host-to-network paged empty buffers. */
+	NSS_STATS_DRV_TX_PACKET,		/**< Host-to-network data packets. */
+	NSS_STATS_DRV_TX_CMD_REQ,		/**< Host-to-network control packets. */
+	NSS_STATS_DRV_TX_CRYPTO_REQ,		/**< Host-to-network crypto requests. */
+	NSS_STATS_DRV_TX_BUFFER_REUSE,		/**< Host-to-network reuse buffer count. */
+	NSS_STATS_DRV_RX_EMPTY,			/**< Network-to-host empty buffers. */
+	NSS_STATS_DRV_RX_PACKET,		/**< Network-to-host data packets. */
+	NSS_STATS_DRV_RX_CMD_RESP,		/**< Network-to-host command responses. */
+	NSS_STATS_DRV_RX_STATUS,		/**< Network-to-host status packets. */
+	NSS_STATS_DRV_RX_CRYPTO_RESP,		/**< Network-to-host crypto responses. */
+	NSS_STATS_DRV_RX_VIRTUAL,		/**< Network-to-host virtual packets. */
+	NSS_STATS_DRV_TX_SIMPLE,		/**< Host-to-network simple SKB packets. */
+	NSS_STATS_DRV_TX_NR_FRAGS,		/**< Host-to-network number of fragmented SKB packets. */
+	NSS_STATS_DRV_TX_FRAGLIST,		/**< Host-to-network fragmentation list of SKB packets. */
+	NSS_STATS_DRV_RX_SIMPLE,		/**< Network-to-host simple SKB packets. */
+	NSS_STATS_DRV_RX_NR_FRAGS,		/**< Network-to-host number of fragmented SKB packets. */
+	NSS_STATS_DRV_RX_SKB_FRAGLIST,		/**< Network-to-host fragmentation list of SKB packets. */
+	NSS_STATS_DRV_RX_BAD_DESCRIPTOR,	/**< Network-to-host bad descriptor reads. */
+	NSS_STATS_DRV_NSS_SKB_COUNT,		/**< NSS SKB pool count. */
+	NSS_STATS_DRV_CHAIN_SEG_PROCESSED,	/**< Network-to-host SKB chain processed count. */
+	NSS_STATS_DRV_FRAG_SEG_PROCESSED,	/**< Network-to-host fragments processed count. */
+	NSS_STATS_DRV_TX_CMD_QUEUE_FULL,	/**< Tx host-to-network control packets fail due to queue full. */
+#ifdef NSS_MULTI_H2N_DATA_RING_SUPPORT
+	NSS_STATS_DRV_TX_PACKET_QUEUE_0,	/**< Host-to-network data packets on queue0. */
+	NSS_STATS_DRV_TX_PACKET_QUEUE_1,	/**< Host-to-network data packets on queue1. */
+	NSS_STATS_DRV_TX_PACKET_QUEUE_2,	/**< Host-to-network data packets on queue2. */
+	NSS_STATS_DRV_TX_PACKET_QUEUE_3,	/**< Host-to-network data packets on queue3. */
+	NSS_STATS_DRV_TX_PACKET_QUEUE_4,	/**< Host-to-network data packets on queue4. */
+	NSS_STATS_DRV_TX_PACKET_QUEUE_5,	/**< Host-to-network data packets on queue5. */
+	NSS_STATS_DRV_TX_PACKET_QUEUE_6,	/**< Host-to-network data packets on queue6. */
+	NSS_STATS_DRV_TX_PACKET_QUEUE_7,	/**< Host-to-network data packets on queue7. */
+#endif
+	NSS_STATS_DRV_MAX,			/**< Maximum message type. */
+};
+
 /**
  * nss_stats_types
  *	List of statistics categories.
diff --git a/nss_drv_stats.c b/nss_drv_stats.c
index ae27608..30b8cb5 100644
--- a/nss_drv_stats.c
+++ b/nss_drv_stats.c
@@ -15,60 +15,14 @@
  */
 
 #include "nss_core.h"
-
-/*
- * nss_drv_stats_str
- *	Host driver stats strings.
- */
-struct nss_stats_info nss_drv_stats_str[NSS_DRV_STATS_MAX] = {
-	{"nbuf_alloc_errors"		, NSS_STATS_TYPE_ERROR},
-	{"paged_buf_alloc_errors"	, NSS_STATS_TYPE_ERROR},
-	{"tx_queue_full[0]"		, NSS_STATS_TYPE_ERROR},
-	{"tx_queue_full[1]"		, NSS_STATS_TYPE_ERROR},
-	{"tx_buffers_empty"		, NSS_STATS_TYPE_SPECIAL},
-	{"tx_paged_buffers_empty"	, NSS_STATS_TYPE_SPECIAL},
-	{"tx_buffer_pkt"		, NSS_STATS_TYPE_SPECIAL},
-	{"tx_buffers_cmd"		, NSS_STATS_TYPE_SPECIAL},
-	{"tx_buffers_crypto"		, NSS_STATS_TYPE_SPECIAL},
-	{"tx_buffers_reuse"		, NSS_STATS_TYPE_SPECIAL},
-	{"rx_buffers_empty"		, NSS_STATS_TYPE_SPECIAL},
-	{"rx_buffers_pkt"		, NSS_STATS_TYPE_SPECIAL},
-	{"rx_buffers_ext_pkt"		, NSS_STATS_TYPE_SPECIAL},
-	{"rx_buffers_cmd_resp"		, NSS_STATS_TYPE_SPECIAL},
-	{"rx_buffers_status_sync"	, NSS_STATS_TYPE_SPECIAL},
-	{"rx_buffers_crypto"		, NSS_STATS_TYPE_SPECIAL},
-	{"rx_buffers_virtual"		, NSS_STATS_TYPE_SPECIAL},
-	{"tx_skb_simple"		, NSS_STATS_TYPE_SPECIAL},
-	{"tx_skb_nr_frags"		, NSS_STATS_TYPE_SPECIAL},
-	{"tx_skb_fraglist"		, NSS_STATS_TYPE_SPECIAL},
-	{"rx_skb_simple"		, NSS_STATS_TYPE_SPECIAL},
-	{"rx_skb_nr_frags"		, NSS_STATS_TYPE_SPECIAL},
-	{"rx_skb_fraglist"		, NSS_STATS_TYPE_SPECIAL},
-	{"rx_bad_desciptor"		, NSS_STATS_TYPE_ERROR},
-	{"invalid_interface"		, NSS_STATS_TYPE_ERROR},
-	{"invalid_core_id"		, NSS_STATS_TYPE_ERROR},
-	{"invalid_buffer_type"		, NSS_STATS_TYPE_ERROR},
-	{"nss_skb_count"		, NSS_STATS_TYPE_SPECIAL},
-	{"rx_chain_seg_processed"	, NSS_STATS_TYPE_SPECIAL},
-	{"rx_frag_seg_processed"	, NSS_STATS_TYPE_SPECIAL},
-	{"tx_buffers_cmd_queue_full"	, NSS_STATS_TYPE_ERROR},
-#ifdef NSS_MULTI_H2N_DATA_RING_SUPPORT
-	{"tx_buffers_data_queue[0]"	, NSS_STATS_TYPE_SPECIAL},
-	{"tx_buffers_data_queue[1]"	, NSS_STATS_TYPE_SPECIAL},
-	{"tx_buffers_data_queue[2]"	, NSS_STATS_TYPE_SPECIAL},
-	{"tx_buffers_data_queue[3]"	, NSS_STATS_TYPE_SPECIAL},
-	{"tx_buffers_data_queue[4]"	, NSS_STATS_TYPE_SPECIAL},
-	{"tx_buffers_data_queue[5]"	, NSS_STATS_TYPE_SPECIAL},
-	{"tx_buffers_data_queue[6]"	, NSS_STATS_TYPE_SPECIAL},
-	{"tx_buffers_data_queue[7]"	, NSS_STATS_TYPE_SPECIAL},
-#endif
-};
+#include "nss_drv_strings.h"
+#include "nss_drv_stats.h"
 
 /*
  * nss_drv_stats_read()
  *	Read HLOS driver stats.
  */
-ssize_t nss_drv_stats_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+static ssize_t nss_drv_stats_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
 {
 	int32_t i;
 
@@ -100,7 +54,7 @@
 		stats_shadow[i] = NSS_PKT_STATS_READ(&nss_top_main.stats_drv[i]);
 	}
 
-	size_wr += nss_stats_print("drv", NULL, NSS_STATS_SINGLE_INSTANCE, nss_drv_stats_str, stats_shadow, NSS_DRV_STATS_MAX, lbuf, size_wr, size_al);
+	size_wr += nss_stats_print("drv", NULL, NSS_STATS_SINGLE_INSTANCE, nss_drv_strings_stats, stats_shadow, NSS_DRV_STATS_MAX, lbuf, size_wr, size_al);
 
 	bytes_read = simple_read_from_buffer(ubuf, sz, ppos, lbuf, strlen(lbuf));
 	kfree(lbuf);
@@ -110,6 +64,20 @@
 }
 
 /*
+ * drv_stats_ops
+ */
+NSS_STATS_DECLARE_FILE_OPERATIONS(drv);
+
+/*
+ * nss_drv_stats_dentry_create()
+ *	Create DRV statistics debug entry.
+ */
+void nss_drv_stats_dentry_create(void)
+{
+	nss_stats_create_dentry("drv", &nss_drv_stats_ops);
+}
+
+/*
  * TODO: Move this (nss_wt_stats_read) function to new file (nss_wt_stats.c)
  */
 
diff --git a/nss_drv_stats.h b/nss_drv_stats.h
index f96c14a..543dd5c 100644
--- a/nss_drv_stats.h
+++ b/nss_drv_stats.h
@@ -75,6 +75,6 @@
 	NSS_DRV_STATS_MAX,
 };
 
-extern ssize_t nss_drv_stats_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos);
+extern void nss_drv_stats_dentry_create(void);
 extern ssize_t nss_wt_stats_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos);
 #endif /* __NSS_DRV_STATS_H */
diff --git a/nss_drv_strings.c b/nss_drv_strings.c
new file mode 100644
index 0000000..2595615
--- /dev/null
+++ b/nss_drv_strings.c
@@ -0,0 +1,92 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2020, 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_stats.h"
+#include "nss_core.h"
+#include "nss_strings.h"
+
+/*
+ * nss_drv_strings_stats
+ *	Host driver stats names.
+ */
+struct nss_stats_info nss_drv_strings_stats[NSS_DRV_STATS_MAX] = {
+	{"nbuf_alloc_errors"		, NSS_STATS_TYPE_ERROR},
+	{"paged_buf_alloc_errors"	, NSS_STATS_TYPE_ERROR},
+	{"tx_queue_full[0]"		, NSS_STATS_TYPE_ERROR},
+	{"tx_queue_full[1]"		, NSS_STATS_TYPE_ERROR},
+	{"tx_buffers_empty"		, NSS_STATS_TYPE_SPECIAL},
+	{"tx_paged_buffers_empty"	, NSS_STATS_TYPE_SPECIAL},
+	{"tx_buffer_pkt"		, NSS_STATS_TYPE_SPECIAL},
+	{"tx_buffers_cmd"		, NSS_STATS_TYPE_SPECIAL},
+	{"tx_buffers_crypto"		, NSS_STATS_TYPE_SPECIAL},
+	{"tx_buffers_reuse"		, NSS_STATS_TYPE_SPECIAL},
+	{"rx_buffers_empty"		, NSS_STATS_TYPE_SPECIAL},
+	{"rx_buffers_pkt"		, NSS_STATS_TYPE_SPECIAL},
+	{"rx_buffers_ext_pkt"		, NSS_STATS_TYPE_SPECIAL},
+	{"rx_buffers_cmd_resp"		, NSS_STATS_TYPE_SPECIAL},
+	{"rx_buffers_status_sync"	, NSS_STATS_TYPE_SPECIAL},
+	{"rx_buffers_crypto"		, NSS_STATS_TYPE_SPECIAL},
+	{"rx_buffers_virtual"		, NSS_STATS_TYPE_SPECIAL},
+	{"tx_skb_simple"		, NSS_STATS_TYPE_SPECIAL},
+	{"tx_skb_nr_frags"		, NSS_STATS_TYPE_SPECIAL},
+	{"tx_skb_fraglist"		, NSS_STATS_TYPE_SPECIAL},
+	{"rx_skb_simple"		, NSS_STATS_TYPE_SPECIAL},
+	{"rx_skb_nr_frags"		, NSS_STATS_TYPE_SPECIAL},
+	{"rx_skb_fraglist"		, NSS_STATS_TYPE_SPECIAL},
+	{"rx_bad_desciptor"		, NSS_STATS_TYPE_ERROR},
+	{"invalid_interface"            , NSS_STATS_TYPE_ERROR},
+	{"invalid_core_id"              , NSS_STATS_TYPE_ERROR},
+	{"invalid_buffer_type"          , NSS_STATS_TYPE_ERROR},
+	{"nss_skb_count"		, NSS_STATS_TYPE_SPECIAL},
+	{"rx_chain_seg_processed"	, NSS_STATS_TYPE_SPECIAL},
+	{"rx_frag_seg_processed"	, NSS_STATS_TYPE_SPECIAL},
+	{"tx_buffers_cmd_queue_full"	, NSS_STATS_TYPE_ERROR},
+#ifdef NSS_MULTI_H2N_DATA_RING_SUPPORT
+	{"tx_buffers_data_queue[0]"	, NSS_STATS_TYPE_SPECIAL},
+	{"tx_buffers_data_queue[1]"	, NSS_STATS_TYPE_SPECIAL},
+	{"tx_buffers_data_queue[2]"	, NSS_STATS_TYPE_SPECIAL},
+	{"tx_buffers_data_queue[3]"	, NSS_STATS_TYPE_SPECIAL},
+	{"tx_buffers_data_queue[4]"	, NSS_STATS_TYPE_SPECIAL},
+	{"tx_buffers_data_queue[5]"	, NSS_STATS_TYPE_SPECIAL},
+	{"tx_buffers_data_queue[6]"	, NSS_STATS_TYPE_SPECIAL},
+	{"tx_buffers_data_queue[7]"	, NSS_STATS_TYPE_SPECIAL},
+#endif
+};
+
+/*
+ * nss_drv_strings_read()
+ *	Read drv node statistics names.
+ */
+static ssize_t nss_drv_strings_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+	return nss_strings_print(ubuf, sz, ppos, nss_drv_strings_stats, NSS_DRV_STATS_MAX);
+}
+
+/*
+ * nss_drv_strings_ops
+ */
+NSS_STRINGS_DECLARE_FILE_OPERATIONS(drv);
+
+/*
+ * nss_drv_strings_dentry_create()
+ *      Create drv statistics strings debug entry.
+ */
+void nss_drv_strings_dentry_create(void)
+{
+	nss_strings_create_dentry("drv", &nss_drv_strings_ops);
+}
diff --git a/nss_drv_strings.h b/nss_drv_strings.h
new file mode 100644
index 0000000..72a1fd7
--- /dev/null
+++ b/nss_drv_strings.h
@@ -0,0 +1,26 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2020, 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_DRV_STRINGS_H
+#define __NSS_DRV_STRINGS_H
+
+extern struct nss_stats_info nss_drv_strings_stats[NSS_DRV_STATS_MAX];
+
+extern void nss_drv_strings_dentry_create(void);
+
+#endif /* __NSS_DRV_STRINGS_H */
diff --git a/nss_n2h.c b/nss_n2h.c
index dd414f3..846bd64 100644
--- a/nss_n2h.c
+++ b/nss_n2h.c
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2020, 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.
@@ -21,6 +21,8 @@
 
 #include "nss_tx_rx_common.h"
 #include "nss_n2h_stats.h"
+#include "nss_n2h_strings.h"
+#include "nss_drv_strings.h"
 
 #define NSS_N2H_MAX_BUF_POOL_SIZE (1024 * 1024 * 20) /* 20MB */
 #define NSS_N2H_MIN_EMPTY_POOL_BUF_SZ		32
@@ -94,7 +96,11 @@
 		break;
 
 	case NSS_RX_METADATA_TYPE_N2H_STATS_SYNC:
+		/*
+		 * Update driver statistics and send statistics notifications to the registered modules.
+		 */
 		nss_n2h_stats_sync(nss_ctx, &nnm->msg.stats_sync);
+		nss_n2h_stats_notify(nss_ctx);
 		break;
 
 	default:
@@ -2057,6 +2063,9 @@
 	if (nss_ctx->id == NSS_CORE_0) {
 		nss_n2h_stats_dentry_create();
 	}
+	nss_n2h_strings_dentry_create();
+
+	nss_drv_strings_dentry_create();
 }
 
 /*
diff --git a/nss_n2h_stats.c b/nss_n2h_stats.c
index 4cdc9ad..60ff88b 100644
--- a/nss_n2h_stats.c
+++ b/nss_n2h_stats.c
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, 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.
@@ -16,46 +16,13 @@
 
 #include "nss_core.h"
 #include "nss_n2h_stats.h"
+#include "nss_n2h.h"
+#include "nss_n2h_strings.h"
 
 /*
- * nss_n2h_stats_str
- *	N2H stats strings
+ * Declare atomic notifier data structure for statistics.
  */
-struct nss_stats_info nss_n2h_stats_str[NSS_N2H_STATS_MAX] = {
-	{"rx_pkts"				, NSS_STATS_TYPE_COMMON},
-	{"rx_byts"				, NSS_STATS_TYPE_COMMON},
-	{"tx_pkts"				, NSS_STATS_TYPE_COMMON},
-	{"tx_byts"				, NSS_STATS_TYPE_COMMON},
-	{"rx_queue[0]_drops"			, NSS_STATS_TYPE_DROP},
-	{"rx_queue[1]_drops"			, NSS_STATS_TYPE_DROP},
-	{"rx_queue[2]_drops"			, NSS_STATS_TYPE_DROP},
-	{"rx_queue[3]_drops"			, NSS_STATS_TYPE_DROP},
-	{"queue_drops"				, NSS_STATS_TYPE_DROP},
-	{"ticks"				, NSS_STATS_TYPE_SPECIAL},
-	{"worst_ticks"				, NSS_STATS_TYPE_SPECIAL},
-	{"iterations"				, NSS_STATS_TYPE_SPECIAL},
-	{"pbuf_ocm_total_count"			, NSS_STATS_TYPE_SPECIAL},
-	{"pbuf_ocm_free_count"			, NSS_STATS_TYPE_SPECIAL},
-	{"pbuf_ocm_alloc_fails_with_payload"	, NSS_STATS_TYPE_SPECIAL},
-	{"pbuf_ocm_alloc_fails_no_payload"	, NSS_STATS_TYPE_SPECIAL},
-	{"pbuf_default_free_count"		, NSS_STATS_TYPE_SPECIAL},
-	{"pbuf_default_total_count"		, NSS_STATS_TYPE_SPECIAL},
-	{"pbuf_default_alloc_fails_with_payload", NSS_STATS_TYPE_SPECIAL},
-	{"pbuf_default_alloc_fails_no_payload"	, NSS_STATS_TYPE_SPECIAL},
-	{"payload_fails"			, NSS_STATS_TYPE_SPECIAL},
-	{"payload_free_count"			, NSS_STATS_TYPE_SPECIAL},
-	{"h2n_control_pkts"			, NSS_STATS_TYPE_SPECIAL},
-	{"h2n_control_byts"			, NSS_STATS_TYPE_SPECIAL},
-	{"n2h_control_pkts"			, NSS_STATS_TYPE_SPECIAL},
-	{"n2h_control_byts"			, NSS_STATS_TYPE_SPECIAL},
-	{"h2n_data_pkts"			, NSS_STATS_TYPE_SPECIAL},
-	{"h2n_data_byts"			, NSS_STATS_TYPE_SPECIAL},
-	{"n2h_data_pkts"			, NSS_STATS_TYPE_SPECIAL},
-	{"n2h_data_byts"			, NSS_STATS_TYPE_SPECIAL},
-	{"n2h_tot_payloads"			, NSS_STATS_TYPE_SPECIAL},
-	{"n2h_data_interface_invalid"		, NSS_STATS_TYPE_SPECIAL},
-	{"enqueue_retries"			, NSS_STATS_TYPE_SPECIAL}
-};
+ATOMIC_NOTIFIER_HEAD(nss_n2h_stats_notifier);
 
 uint64_t nss_n2h_stats[NSS_MAX_CORES][NSS_N2H_STATS_MAX];
 
@@ -101,7 +68,7 @@
 		spin_unlock_bh(&nss_top_main.stats_lock);
 		size_wr += nss_stats_banner(lbuf, size_wr, size_al, "n2h", core);
 		size_wr += nss_stats_print("n2h", NULL, NSS_STATS_SINGLE_INSTANCE
-						, nss_n2h_stats_str
+						, nss_n2h_strings_stats
 						, stats_shadow
 						, NSS_N2H_STATS_MAX
 						, lbuf, size_wr, size_al);
@@ -117,7 +84,7 @@
 /*
  * nss_n2h_stats_ops
  */
-NSS_STATS_DECLARE_FILE_OPERATIONS(n2h)
+NSS_STATS_DECLARE_FILE_OPERATIONS(n2h);
 
 /*
  * nss_n2h_stats_dentry_create()
@@ -205,3 +172,43 @@
 
 	spin_unlock_bh(&nss_top->stats_lock);
 }
+
+/*
+ * nss_n2h_stats_notify()
+ *	Sends notifications to all the registered modules.
+ *
+ * Leverage NSS-FW statistics timing to update Netlink.
+ */
+void nss_n2h_stats_notify(struct nss_ctx_instance *nss_ctx)
+{
+	int i;
+	struct nss_n2h_stats_notification stats;
+
+	for (i = 0; (i < NSS_STATS_DRV_MAX); i++) {
+		stats.drv_stats[i] = NSS_PKT_STATS_READ(&nss_top_main.stats_drv[i]);
+	}
+
+	stats.core_id = nss_ctx->id;
+	memcpy(stats.n2h_stats, nss_n2h_stats[stats.core_id], sizeof(stats.n2h_stats));
+	atomic_notifier_call_chain(&nss_n2h_stats_notifier, NSS_STATS_EVENT_NOTIFY, (void *)&stats);
+}
+
+/*
+ * nss_n2h_stats_register_notifier()
+ *	Registers statistics notifier.
+ */
+int nss_n2h_stats_register_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&nss_n2h_stats_notifier, nb);
+}
+EXPORT_SYMBOL(nss_n2h_stats_register_notifier);
+
+/*
+ * nss_n2h_stats_unregister_notifier()
+ *	Deregisters statistics notifier.
+ */
+int nss_n2h_stats_unregister_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&nss_n2h_stats_notifier, nb);
+}
+EXPORT_SYMBOL(nss_n2h_stats_unregister_notifier);
diff --git a/nss_n2h_stats.h b/nss_n2h_stats.h
index 3b24c98..96d065e 100644
--- a/nss_n2h_stats.h
+++ b/nss_n2h_stats.h
@@ -1,6 +1,6 @@
 /*
  ******************************************************************************
- * Copyright (c) 2017, 2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017,2019-2020 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.
@@ -18,49 +18,9 @@
 #define __NSS_N2H_STATS_H
 
 /*
- * N2H node statistics
- */
-enum nss_n2h_stats_types {
-	NSS_N2H_STATS_QUEUE_DROPPED = NSS_STATS_NODE_MAX,
-					/* Number of packets dropped because the exception queue is too full */
-	NSS_N2H_STATS_TOTAL_TICKS,	/* Total clock ticks spend inside the N2H */
-	NSS_N2H_STATS_WORST_CASE_TICKS,	/* Worst case iteration of the exception path in ticks */
-	NSS_N2H_STATS_ITERATIONS,	/* Number of iterations around the N2H */
-	NSS_N2H_STATS_PBUF_OCM_TOTAL_COUNT,	/* Number of pbuf ocm total count */
-	NSS_N2H_STATS_PBUF_OCM_FREE_COUNT,	/* Number of pbuf ocm free count */
-	NSS_N2H_STATS_PBUF_OCM_ALLOC_FAILS_WITH_PAYLOAD,
-						/* Number of pbuf ocm allocations that have failed with payload */
-	NSS_N2H_STATS_PBUF_OCM_ALLOC_FAILS_NO_PAYLOAD,
-						/* Number of pbuf ocm allocations that have failed without payload */
-	NSS_N2H_STATS_PBUF_DEFAULT_TOTAL_COUNT,	/* Number of pbuf default total count */
-	NSS_N2H_STATS_PBUF_DEFAULT_FREE_COUNT,	/* Number of pbuf default free count */
-	NSS_N2H_STATS_PBUF_DEFAULT_ALLOC_FAILS_WITH_PAYLOAD,
-						/* Number of pbuf default allocations that have failed with payload */
-	NSS_N2H_STATS_PBUF_DEFAULT_ALLOC_FAILS_NO_PAYLOAD,
-						/* Number of pbuf default allocations that have failed without payload */
-
-	NSS_N2H_STATS_PAYLOAD_ALLOC_FAILS,	/* Number of pbuf allocations that have failed because there were no free payloads */
-	NSS_N2H_STATS_PAYLOAD_FREE_COUNT,	/* Number of free payloads that exist */
-
-	NSS_N2H_STATS_H2N_CONTROL_PACKETS,	/* Control packets received from HLOS */
-	NSS_N2H_STATS_H2N_CONTROL_BYTES,	/* Control bytes received from HLOS */
-	NSS_N2H_STATS_N2H_CONTROL_PACKETS,	/* Control packets sent to HLOS */
-	NSS_N2H_STATS_N2H_CONTROL_BYTES,	/* Control bytes sent to HLOS */
-
-	NSS_N2H_STATS_H2N_DATA_PACKETS,		/* Data packets received from HLOS */
-	NSS_N2H_STATS_H2N_DATA_BYTES,		/* Data bytes received from HLOS */
-	NSS_N2H_STATS_N2H_DATA_PACKETS,		/* Data packets sent to HLOS */
-	NSS_N2H_STATS_N2H_DATA_BYTES,		/* Data bytes sent to HLOS */
-	NSS_N2H_STATS_N2H_TOT_PAYLOADS,		/* No. of payloads in NSS */
-	NSS_N2H_STATS_N2H_INTERFACE_INVALID,	/* No. of bad interface access */
-	NSS_N2H_STATS_ENQUEUE_RETRIES,		/* No. of enqueue retries by N2H */
-
-	NSS_N2H_STATS_MAX,
-};
-
-/*
  * N2H statistics APIs
  */
+extern void nss_n2h_stats_notify(struct nss_ctx_instance *nss_ctx);
 extern void nss_n2h_stats_sync(struct nss_ctx_instance *nss_ctx, struct nss_n2h_stats_sync *nnss);
 extern void nss_n2h_stats_dentry_create(void);
 
diff --git a/nss_n2h_strings.c b/nss_n2h_strings.c
new file mode 100644
index 0000000..a9d34d7
--- /dev/null
+++ b/nss_n2h_strings.c
@@ -0,0 +1,83 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2020, 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_stats.h"
+#include "nss_core.h"
+#include <nss_n2h.h>
+#include "nss_strings.h"
+
+/*
+ * nss_n2h_strings_stats
+ *	N2H statistics strings.
+ */
+struct nss_stats_info nss_n2h_strings_stats[NSS_N2H_STATS_MAX] = {
+	{"rx_pkts"			, NSS_STATS_TYPE_COMMON},
+	{"rx_byts"			, NSS_STATS_TYPE_COMMON},
+	{"tx_pkts"			, NSS_STATS_TYPE_COMMON},
+	{"tx_byts"			, NSS_STATS_TYPE_COMMON},
+	{"rx_queue[0]_drops"		, NSS_STATS_TYPE_DROP},
+	{"rx_queue[1]_drops"		, NSS_STATS_TYPE_DROP},
+	{"rx_queue[2]_drops"		, NSS_STATS_TYPE_DROP},
+	{"rx_queue[3]_drops"		, NSS_STATS_TYPE_DROP},
+	{"queue_drops"			, NSS_STATS_TYPE_DROP},
+	{"ticks"			, NSS_STATS_TYPE_SPECIAL},
+	{"worst_ticks"			, NSS_STATS_TYPE_SPECIAL},
+	{"iterations"			, NSS_STATS_TYPE_SPECIAL},
+	{"pbuf_ocm_alloc_fails"		, NSS_STATS_TYPE_SPECIAL},
+	{"pbuf_ocm_free_count"		, NSS_STATS_TYPE_SPECIAL},
+	{"pbuf_ocm_total_count"		, NSS_STATS_TYPE_SPECIAL},
+	{"pbuf_default_alloc_fails"	, NSS_STATS_TYPE_SPECIAL},
+	{"pbuf_default_free_count"	, NSS_STATS_TYPE_SPECIAL},
+	{"pbuf_default_total_count"	, NSS_STATS_TYPE_SPECIAL},
+	{"payload_fails"		, NSS_STATS_TYPE_SPECIAL},
+	{"payload_free_count"		, NSS_STATS_TYPE_SPECIAL},
+	{"h2n_control_pkts"		, NSS_STATS_TYPE_SPECIAL},
+	{"h2n_control_byts"		, NSS_STATS_TYPE_SPECIAL},
+	{"n2h_control_pkts"		, NSS_STATS_TYPE_SPECIAL},
+	{"n2h_control_byts"		, NSS_STATS_TYPE_SPECIAL},
+	{"h2n_data_pkts"		, NSS_STATS_TYPE_SPECIAL},
+	{"h2n_data_byts"		, NSS_STATS_TYPE_SPECIAL},
+	{"n2h_data_pkts"		, NSS_STATS_TYPE_SPECIAL},
+	{"n2h_data_byts"		, NSS_STATS_TYPE_SPECIAL},
+	{"n2h_tot_payloads"		, NSS_STATS_TYPE_SPECIAL},
+	{"n2h_data_interface_invalid"	, NSS_STATS_TYPE_SPECIAL},
+	{"enqueue_retries"		, NSS_STATS_TYPE_SPECIAL}
+};
+
+/*
+ * nss_n2h_strings_read()
+ *	Read N2H node statistics names.
+ */
+static ssize_t nss_n2h_strings_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+	return nss_strings_print(ubuf, sz, ppos, nss_n2h_strings_stats, NSS_N2H_STATS_MAX);
+}
+
+/*
+ * nss_n2h_strings_ops
+ */
+NSS_STRINGS_DECLARE_FILE_OPERATIONS(n2h);
+
+/*
+ * nss_n2h_strings_dentry_create()
+ *	Create N2H statistics strings debug entry.
+ */
+void nss_n2h_strings_dentry_create(void)
+{
+	nss_strings_create_dentry("n2h", &nss_n2h_strings_ops);
+}
diff --git a/nss_n2h_strings.h b/nss_n2h_strings.h
new file mode 100644
index 0000000..5d8c213
--- /dev/null
+++ b/nss_n2h_strings.h
@@ -0,0 +1,25 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2020, 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_N2H_STRINGS_H
+#define __NSS_N2H_STRINGS_H
+
+extern struct nss_stats_info nss_n2h_strings_stats[NSS_N2H_STATS_MAX];
+extern void nss_n2h_strings_dentry_create(void);
+
+#endif /* __NSS_N2H_STRINGS_H */
diff --git a/nss_stats.c b/nss_stats.c
index 9872943..9605c37 100644
--- a/nss_stats.c
+++ b/nss_stats.c
@@ -16,6 +16,7 @@
 
 #include "nss_core.h"
 #include "nss_strings.h"
+#include "nss_drv_stats.h"
 
 /*
  * Maximum banner length:
@@ -382,15 +383,10 @@
 }
 
 /*
- * TODO: Move the rest of the code to (nss_wt_stats.c, nss_gmac_stats.c, nss_drv_stats.c) accordingly.
+ * TODO: Move the rest of the code to (nss_wt_stats.c, nss_gmac_stats.c) accordingly.
  */
 
 /*
- * drv_stats_ops
- */
-NSS_STATS_DECLARE_FILE_OPERATIONS(drv);
-
-/*
  * gmac_stats_ops
  */
 NSS_STATS_DECLARE_FILE_OPERATIONS(gmac);
@@ -443,7 +439,7 @@
 	/*
 	 * drv_stats
 	 */
-	nss_stats_create_dentry("drv", &nss_drv_stats_ops);
+	nss_drv_stats_dentry_create();
 
 	/*
 	 * gmac_stats
diff --git a/nss_strings.c b/nss_strings.c
index a60c3c2..432b154 100644
--- a/nss_strings.c
+++ b/nss_strings.c
@@ -21,6 +21,7 @@
 
 #include "nss_strings.h"
 #include "nss_core.h"
+#include "nss_drv_strings.h"
 
 /*
  * common stats