[qca-nss-drv] Added statistics for worker threads.

Change-Id: I89e91955a382db5dc141b727cd75fddcbe596a18
Signed-off-by: Jackson Bockus <jbockus@codeaurora.org>
diff --git a/nss_stats.c b/nss_stats.c
index 0447fa5..6a22315 100644
--- a/nss_stats.c
+++ b/nss_stats.c
@@ -43,9 +43,11 @@
  * Private data for every file descriptor
  */
 struct nss_stats_data {
-	uint32_t if_num;	/**< Interface number for stats */
-	uint32_t index;		/**< Index for GRE_REDIR stats */
-	uint32_t edma_id;	/**< EDMA port ID or ring ID */
+	uint32_t if_num;	/* Interface number for stats */
+	uint32_t index;		/* Index for GRE_REDIR stats */
+	uint32_t edma_id;	/* EDMA port ID or ring ID */
+	struct nss_ctx_instance *nss_ctx;
+				/* The core for project stats */
 };
 
 /*
@@ -4211,6 +4213,89 @@
 }
 
 /*
+ * nss_stats_wt_read()
+ *	Reads and formats worker thread statistics and outputs them to ubuf
+ */
+static ssize_t nss_stats_wt_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+	struct nss_stats_data *data = fp->private_data;
+	struct nss_ctx_instance *nss_ctx = data->nss_ctx;
+	struct nss_project_irq_stats *shadow;
+	uint32_t thread_count = nss_ctx->worker_thread_count;
+	uint32_t irq_count = nss_ctx->irq_count;
+
+	/*
+	 * Three lines for each IRQ
+	 */
+	uint32_t max_output_lines = thread_count * 3 * irq_count;
+	size_t size_al = max_output_lines * NSS_STATS_MAX_STR_LENGTH;
+	size_t size_wr = 0;
+	ssize_t bytes_read = 0;
+	char *lbuf;
+	int i;
+	int j;
+
+	lbuf = kzalloc(size_al, GFP_KERNEL);
+	if (unlikely(!lbuf)) {
+		nss_warning("Could not allocate memory for local statistics buffer\n");
+		return 0;
+	}
+
+	shadow = kzalloc(thread_count * irq_count * sizeof(struct nss_project_irq_stats), GFP_KERNEL);
+	if (unlikely(!shadow)) {
+		nss_warning("Could not allocate memory for stats shadow\n");
+		kfree(lbuf);
+		return 0;
+	}
+
+	spin_lock_bh(&nss_top_main.stats_lock);
+	if (unlikely(!nss_ctx->wt_stats)) {
+		spin_unlock_bh(&nss_top_main.stats_lock);
+		nss_warning("Worker thread statistics not allocated\n");
+		kfree(lbuf);
+		kfree(shadow);
+		return 0;
+	}
+	for (i = 0; i < thread_count; ++i) {
+
+		/*
+		 * The statistics shadow is an array with thread_count * irq_count
+		 * items in it. Each item is located at the index:
+		 *	(thread number) * (irq_count) + (irq number)
+		 * thus simulating a two-dimensional array.
+		 */
+		for (j = 0; j < irq_count; ++j) {
+			shadow[i * irq_count + j] = nss_ctx->wt_stats[i].irq_stats[j];
+		}
+	}
+	spin_unlock_bh(&nss_top_main.stats_lock);
+
+	for (i = 0; i < thread_count; ++i) {
+		for (j = 0; j < irq_count; ++j) {
+			struct nss_project_irq_stats *is = &(shadow[i * irq_count + j]);
+			if (!(is->count)) {
+				continue;
+			}
+
+			size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,
+				"t-%d:irq-%d callback: 0x%x, count: %llu\n",
+				i, j, is->callback, is->count);
+			size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,
+				"t-%d:irq-%d tick min: %10u  avg: %10u  max:%10u\n",
+				i, j, is->ticks_min, is->ticks_avg, is->ticks_max);
+			size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,
+				"t-%d:irq-%d insn min: %10u  avg: %10u  max:%10u\n\n",
+				i, j, is->insn_min, is->insn_avg, is->insn_max);
+		}
+	}
+	bytes_read = simple_read_from_buffer(ubuf, sz, ppos, lbuf, strlen(lbuf));
+	kfree(lbuf);
+	kfree(shadow);
+
+	return bytes_read;
+}
+
+/*
  * nss_stats_open()
  */
 static int nss_stats_open(struct inode *inode, struct file *filp)
@@ -4225,6 +4310,7 @@
 	data->if_num = NSS_DYNAMIC_IF_START;
 	data->index = 0;
 	data->edma_id = (nss_ptr_t)inode->i_private;
+	data->nss_ctx = (struct nss_ctx_instance *)(inode->i_private);
 	filp->private_data = data;
 
 	return 0;
@@ -4425,6 +4511,10 @@
 NSS_STATS_DECLARE_FILE_OPERATIONS(wifili)
 
 /*
+ * wt_stats_ops
+ */
+NSS_STATS_DECLARE_FILE_OPERATIONS(wt)
+/*
  * nss_stats_init()
  * 	Enable NSS statistics
  */
@@ -4454,6 +4544,9 @@
 	struct dentry *ppe_cpu_d = NULL;
 	struct dentry *ppe_exception_d = NULL;
 	struct dentry *ppe_nonexception_d = NULL;
+	struct dentry *core_dentry = NULL;
+	struct dentry *wt_dentry = NULL;
+
 
 	char file_name[10];
 
@@ -4980,6 +5073,37 @@
 		return;
 	}
 
+	/*
+	 * Per-project stats
+	 */
+	nss_top_main.project_dentry = debugfs_create_dir("project",
+						nss_top_main.stats_dentry);
+	if (unlikely(nss_top_main.project_dentry == NULL)) {
+		nss_warning("Failed to create qca-nss-drv/stats/project directory in debugfs");
+		return;
+	}
+
+	for (i = 0; i < NSS_MAX_CORES; ++i) {
+		memset(file_name, 0, sizeof(file_name));
+		scnprintf(file_name, sizeof(file_name), "core%d", i);
+		core_dentry = debugfs_create_dir(file_name,
+						nss_top_main.project_dentry);
+		if (unlikely(core_dentry == NULL)) {
+			nss_warning("Failed to create qca-nss-drv/stats/project/core%d directory in debugfs", i);
+			return;
+		}
+
+		wt_dentry = debugfs_create_file("worker_threads",
+						0400,
+						core_dentry,
+						&(nss_top_main.nss[i]),
+						&nss_stats_wt_ops);
+		if (unlikely(wt_dentry == NULL)) {
+			nss_warning("Failed to create qca-nss-drv/stats/project/core%d/worker_threads file in debugfs", i);
+			return;
+		}
+	}
+
 	nss_log_init();
 }