[qca-nss-drv]: Adding gre_redir stats to debugfs

Adding gre_redir tunnel stats to debugfs.

CRs-Fixed: 754011

Change-Id: I5d49dc57bae1a096f9e4a158669ff5ee46904904
Signed-off-by: Ankit Dhanuka <adhanuka@codeaurora.org>
diff --git a/exports/nss_gre_redir.h b/exports/nss_gre_redir.h
index 8c386a2..6b41522 100644
--- a/exports/nss_gre_redir.h
+++ b/exports/nss_gre_redir.h
@@ -22,6 +22,7 @@
 #ifndef __NSS_GRE_REDIR_H
 #define __NSS_GRE_REDIR_H
 
+#define NSS_GRE_REDIR_MAX_INTERFACES 3
 #define NSS_GRE_REDIR_IP_DF_OVERRIDE_FLAG 0x80
 #define NSS_GRE_REDIR_PER_PACKET_METADATA_OFFSET 4
 #define NSS_GRE_REDIR_IP_HDR_TYPE_IPV4 1
@@ -83,7 +84,7 @@
 };
 
 /**
- * #brief: GRE tunnel statistics sync message structure.
+ * @brief: GRE tunnel statistics sync message structure.
  */
 struct nss_gre_redir_stats_sync_msg {
 	struct nss_cmn_node_stats node_stats;	/**< Tunnel stats sync */
@@ -91,6 +92,16 @@
 };
 
 /**
+ * @brief: GRE tunnel statistics as seen by HLOS.
+ */
+struct nss_gre_redir_tunnel_stats {
+	int if_num;				/**< Tunnel Interface num */
+	bool valid;				/**< Tunnel validity flag */
+	struct nss_cmn_node_stats node_stats;	/**< Tunnel stats sync */
+	uint32_t tx_dropped;			/**< Tunnel Tx drops */
+};
+
+/**
  * @brief Message structure to send/receive GRE tunnel message.
  */
 struct nss_gre_redir_msg {
@@ -193,4 +204,14 @@
 extern nss_tx_status_t nss_gre_redir_tx_buf(struct nss_ctx_instance *nss_ctx, struct sk_buff *os_buf,
 						uint32_t if_num);
 
+/**
+ * @brief Get gre_redir tunnel statistics
+ *
+ * @param index index in tunnel stats array.
+ * @param stats tunnel stats structure.
+ *
+ * @return true or false.
+ */
+extern bool nss_gre_redir_get_stats(int index, struct nss_gre_redir_tunnel_stats *stats);
+
 #endif /* __NSS_GRE_REDIR_H */
diff --git a/nss_core.h b/nss_core.h
index ad994c5..3e97c4b 100755
--- a/nss_core.h
+++ b/nss_core.h
@@ -532,6 +532,7 @@
 	struct dentry *gmac_dentry;	/* GMAC ethnode stats dentry */
 	struct dentry *capwap_decap_dentry;     /* CAPWAP decap ethnode stats dentry */
 	struct dentry *capwap_encap_dentry;     /* CAPWAP encap ethnode stats dentry */
+	struct dentry *gre_redir_dentry;	/* gre_redir ethnode stats dentry */
 
 	struct nss_ctx_instance nss[NSS_MAX_CORES];
 					/* NSS contexts */
diff --git a/nss_gre_redir.c b/nss_gre_redir.c
index 3b1d496..78c65b5 100644
--- a/nss_gre_redir.c
+++ b/nss_gre_redir.c
@@ -17,11 +17,48 @@
 #include "nss_tx_rx_common.h"
 
 /*
+ * Spinlock to update tunnel stats
+ */
+DEFINE_SPINLOCK(nss_gre_redir_stats_lock);
+
+/*
+ * Array to hold tunnel stats along with if_num
+ */
+static struct nss_gre_redir_tunnel_stats tun_stats[NSS_GRE_REDIR_MAX_INTERFACES];
+
+/*
+ * nss_gre_redir_tunnel_update_stats()
+ * 	Update gre_redir tunnel stats.
+ */
+static void nss_gre_redir_tunnel_update_stats(struct nss_ctx_instance *nss_ctx, int if_num, struct nss_gre_redir_stats_sync_msg *ngss)
+{
+	int i;
+
+	spin_lock_bh(&nss_gre_redir_stats_lock);
+	for (i = 0; i < NSS_GRE_REDIR_MAX_INTERFACES; i++) {
+		if ((tun_stats[i].if_num == if_num) && (tun_stats[i].valid)) {
+
+			tun_stats[i].node_stats.rx_packets += ngss->node_stats.rx_packets;
+			tun_stats[i].node_stats.rx_bytes += ngss->node_stats.rx_bytes;
+			tun_stats[i].node_stats.tx_packets += ngss->node_stats.tx_packets;
+			tun_stats[i].node_stats.tx_bytes += ngss->node_stats.tx_bytes;
+			tun_stats[i].node_stats.rx_dropped += ngss->node_stats.rx_dropped;
+			tun_stats[i].tx_dropped += ngss->tx_dropped;
+
+			break;
+		}
+	}
+	spin_unlock_bh(&nss_gre_redir_stats_lock);
+}
+
+
+/*
  * nss_gre_redir_handler()
  * 	Handle NSS -> HLOS messages for gre tunnel
  */
 static void nss_gre_redir_msg_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm, __attribute__((unused))void *app_data)
 {
+	struct nss_gre_redir_msg *ngrm = (struct nss_gre_redir_msg *)ncm;
 	void *ctx;
 	nss_gre_redir_msg_callback_t cb;
 
@@ -58,6 +95,20 @@
 	 */
 	nss_core_log_msg_failures(nss_ctx, ncm);
 
+	switch (ncm->type) {
+	case NSS_GRE_REDIR_RX_STATS_SYNC_MSG:
+		/*
+		 * Update Tunnel statistics.
+		 */
+		if (!(nss_is_dynamic_interface(ncm->interface))) {
+			nss_warning("%p: stats received for wrong interface %d\n", nss_ctx, ncm->interface);
+			break;
+		}
+
+		nss_gre_redir_tunnel_update_stats(nss_ctx, ncm->interface, &ngrm->msg.stats_sync);
+		break;
+	}
+
 	/*
 	 * Do we have a call back
 	 */
@@ -74,14 +125,31 @@
 	/*
 	 * call gre tunnel callback
 	 */
-	if (!ctx) {
-		nss_warning("%p: Event received for gre tunnel interface %d before registration", nss_ctx, ncm->interface);
-		return;
-	}
-
 	cb(ctx, ncm);
 }
 
+/*
+ * nss_gre_redir_get_stats()
+ * 	get gre_redir tunnel stats.
+ */
+bool nss_gre_redir_get_stats(int index, struct nss_gre_redir_tunnel_stats *stats)
+{
+	spin_lock_bh(&nss_gre_redir_stats_lock);
+	if (!tun_stats[index].valid) {
+		spin_unlock_bh(&nss_gre_redir_stats_lock);
+		return false;
+	}
+
+	if (nss_is_dynamic_interface(tun_stats[index].if_num) == false) {
+		spin_unlock_bh(&nss_gre_redir_stats_lock);
+		return false;
+	}
+
+	memcpy(stats, &tun_stats[index], sizeof(struct nss_gre_redir_tunnel_stats));
+	spin_unlock_bh(&nss_gre_redir_stats_lock);
+
+	return true;
+}
 
 /*
  * nss_gre_redir_tx_msg()
@@ -202,6 +270,7 @@
 							nss_gre_redir_msg_callback_t cb_func_msg)
 {
 	uint32_t status;
+	int i;
 
 	nss_assert((if_num >= NSS_DYNAMIC_IF_START) && (if_num < (NSS_DYNAMIC_IF_START + NSS_MAX_DYNAMIC_INTERFACES)));
 
@@ -218,6 +287,16 @@
         nss_top_main.if_rx_callback[if_num] = cb_func_data;
         nss_top_main.if_rx_msg_callback[if_num] = cb_func_msg;
 
+	spin_lock_bh(&nss_gre_redir_stats_lock);
+	for (i = 0; i < NSS_GRE_REDIR_MAX_INTERFACES; i++) {
+		if (!(tun_stats[i].valid)) {
+			tun_stats[i].valid = true;
+			tun_stats[i].if_num = if_num;
+			break;
+		}
+	}
+	spin_unlock_bh(&nss_gre_redir_stats_lock);
+
         return (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.gre_redir_handler_id];
 }
 
@@ -227,6 +306,7 @@
 void nss_gre_redir_unregister_if(uint32_t if_num)
 {
 	uint32_t status;
+	int i;
 
 	nss_assert((if_num >= NSS_DYNAMIC_IF_START) && (if_num < (NSS_DYNAMIC_IF_START + NSS_MAX_DYNAMIC_INTERFACES)));
 
@@ -236,9 +316,19 @@
 		return;
 	}
 
-        nss_top_main.if_rx_callback[if_num] = NULL;
-        nss_top_main.if_ctx[if_num] = NULL;
-        nss_top_main.if_rx_msg_callback[if_num] = NULL;
+	nss_top_main.if_rx_callback[if_num] = NULL;
+	nss_top_main.if_ctx[if_num] = NULL;
+	nss_top_main.if_rx_msg_callback[if_num] = NULL;
+
+	spin_lock_bh(&nss_gre_redir_stats_lock);
+	for (i = 0; i < NSS_GRE_REDIR_MAX_INTERFACES; i++) {
+		if ((tun_stats[i].if_num == if_num) && (tun_stats[i].valid)) {
+			tun_stats[i].valid = false;
+			tun_stats[i].if_num = -1;
+			break;
+		}
+	}
+	spin_unlock_bh(&nss_gre_redir_stats_lock);
 }
 
 /*
@@ -258,3 +348,4 @@
 EXPORT_SYMBOL(nss_gre_redir_tx_buf);
 EXPORT_SYMBOL(nss_gre_redir_register_if);
 EXPORT_SYMBOL(nss_gre_redir_unregister_if);
+EXPORT_SYMBOL(nss_gre_redir_get_stats);
diff --git a/nss_stats.c b/nss_stats.c
index 4874690..5d2e9cd 100644
--- a/nss_stats.c
+++ b/nss_stats.c
@@ -41,6 +41,7 @@
  */
 struct nss_stats_data {
 	uint32_t if_num;	/**< Interface number for CAPWAP stats */
+	uint32_t index;		/**< Index for GRE_REDIR stats */
 };
 
 /*
@@ -1205,6 +1206,118 @@
 }
 
 /*
+ * nss_stats_gre_redir()
+ * 	Make a row for GRE_REDIR stats.
+ */
+static ssize_t nss_stats_gre_redir(char *line, int len, int i, struct nss_gre_redir_tunnel_stats *s)
+{
+	char *header[] = { "TX Packets", "TX Bytes", "TX Drops", "RX Packets", "RX Bytes", "Rx Drops" };
+	uint64_t tcnt = 0;
+
+	switch (i) {
+	case 0:
+		tcnt = s->node_stats.tx_packets;
+		break;
+	case 1:
+		tcnt = s->node_stats.tx_bytes;
+		break;
+	case 2:
+		tcnt = s->tx_dropped;
+		break;
+	case 3:
+		tcnt = s->node_stats.rx_packets;
+		break;
+	case 4:
+		tcnt = s->node_stats.rx_bytes;
+		break;
+	case 5:
+		tcnt = s->node_stats.rx_dropped;
+		break;
+	default:
+		i = 6;
+		break;
+	}
+
+	return (snprintf(line, len, "%s = %llu\n", header[i], tcnt));
+}
+
+/*
+ * nss_stats_gre_redir_read()
+ * 	READ gre_redir tunnel stats.
+ */
+static ssize_t nss_stats_gre_redir_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+	struct nss_stats_data *data = fp->private_data;
+	ssize_t bytes_read = 0;
+	struct nss_gre_redir_tunnel_stats stats;
+	size_t bytes;
+	char line[80];
+	int start, end;
+	int index = 0;
+
+	if (data) {
+		index = data->index;
+	}
+
+	/*
+	 * If we are done accomodating all the GRE_REDIR tunnels.
+	 */
+	if (index >= NSS_GRE_REDIR_MAX_INTERFACES) {
+		return 0;
+	}
+
+	for (; index < NSS_GRE_REDIR_MAX_INTERFACES; index++) {
+		bool isthere;
+
+		/*
+		 * If gre_redir tunnel does not exists, then isthere will be false.
+		 */
+		isthere = nss_gre_redir_get_stats(index, &stats);
+		if (!isthere) {
+			continue;
+		}
+
+		bytes = snprintf(line, sizeof(line), "\nTunnel if_num: %2d\n", stats.if_num);
+		if ((bytes_read + bytes) > sz) {
+			break;
+		}
+
+		if (copy_to_user(ubuf + bytes_read, line, bytes) != 0) {
+			bytes_read = -EFAULT;
+			goto fail;
+		}
+		bytes_read += bytes;
+		start = 0;
+		end = 6;
+		while (bytes_read < sz && start < end) {
+			bytes = nss_stats_gre_redir(line, sizeof(line), start, &stats);
+
+			if ((bytes_read + bytes) > sz)
+				break;
+
+			if (copy_to_user(ubuf + bytes_read, line, bytes) != 0) {
+				bytes_read = -EFAULT;
+				goto fail;
+			}
+
+			bytes_read += bytes;
+			start++;
+		}
+	}
+
+	if (bytes_read > 0) {
+		*ppos = bytes_read;
+	}
+
+	if (data) {
+		data->index = index;
+	}
+
+fail:
+	return bytes_read;
+}
+
+/*
  * nss_stats_open()
  */
 static int nss_stats_open(struct inode *inode, struct file *filp)
@@ -1217,6 +1330,7 @@
 	}
 	memset(data, 0, sizeof (struct nss_stats_data));
 	data->if_num = NSS_DYNAMIC_IF_START;
+	data->index = 0;
 	filp->private_data = data;
 
 	return 0;
@@ -1296,6 +1410,11 @@
 NSS_STATS_DECLARE_FILE_OPERATIONS(eth_rx)
 
 /*
+ * gre_redir_ops
+ */
+NSS_STATS_DECLARE_FILE_OPERATIONS(gre_redir)
+
+/*
  * nss_stats_init()
  * 	Enable NSS statistics
  */
@@ -1436,6 +1555,16 @@
 		nss_warning("Failed to create qca-nss-drv/stats/capwap_decap file in debugfs");
 		return;
 	}
+
+	/*
+	 * GRE_REDIR stats
+	 */
+	nss_top_main.gre_redir_dentry = debugfs_create_file("gre_redir", 0400,
+	nss_top_main.stats_dentry, &nss_top_main, &nss_stats_gre_redir_ops);
+	if (unlikely(nss_top_main.gre_redir_dentry == NULL)) {
+		nss_warning("Failed to create qca-nss-drv/stats/gre_redir file in debugfs");
+		return;
+	}
 }