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

Change-Id: I89e91955a382db5dc141b727cd75fddcbe596a18
Signed-off-by: Jackson Bockus <jbockus@codeaurora.org>
diff --git a/nss_project.c b/nss_project.c
new file mode 100644
index 0000000..237b1be
--- /dev/null
+++ b/nss_project.c
@@ -0,0 +1,361 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2017, 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_project.h
+ *	NSS project APIs.
+ */
+#include "nss_tx_rx_common.h"
+
+static int nss_project_wt_stats_enable;
+
+/*
+ * nss_project_free_wt_stats()
+ *	Frees a number of allocated worker thread statistics.
+ */
+static void nss_project_free_wt_stats(struct nss_worker_thread_stats *wt_stats, int num_alloc)
+{
+	int i;
+
+	if (!wt_stats) {
+		return;
+	}
+
+	for (i = 0; i < num_alloc; i++) {
+		kfree(wt_stats[i].irq_stats);
+	}
+	kfree(wt_stats);
+}
+
+/*
+ * nss_project_alloc_wt_stats()
+ * 	Allocates worker thread stats for a given  number of threads and IRQs.
+ */
+static struct nss_worker_thread_stats *nss_project_alloc_wt_stats(uint32_t thread_count, uint32_t irq_count)
+{
+	struct nss_worker_thread_stats *wt_stats;
+	int i;
+
+	wt_stats = kzalloc(thread_count * sizeof(struct nss_worker_thread_stats), GFP_ATOMIC);
+	if (unlikely(!wt_stats)) {
+		return NULL;
+	}
+
+	for (i = 0; i < thread_count; i++) {
+		wt_stats[i].irq_stats =
+			kzalloc(irq_count * sizeof(struct nss_project_irq_stats), GFP_ATOMIC);
+		if (unlikely(!wt_stats[i].irq_stats)) {
+			nss_project_free_wt_stats(wt_stats, i);
+			return NULL;
+		}
+	}
+
+	return wt_stats;
+}
+
+/*
+ * nss_project_wt_stats_enable_callback()
+ *	Callback function for wt stats enable messages
+ */
+static void nss_project_wt_stats_enable_callback(void *app_data, struct nss_project_msg *msg)
+{
+	struct nss_ctx_instance *nss_ctx = (struct nss_ctx_instance *)app_data;
+	struct nss_project_msg_wt_stats_enable *stats_enable = &msg->msg.wt_stats_enable;
+	struct nss_worker_thread_stats *stats_temp;
+
+	NSS_VERIFY_CTX_MAGIC(nss_ctx);
+	if (msg->cm.response != NSS_CMN_RESPONSE_ACK) {
+		return;
+	}
+
+	nss_info("%p: Received response ACK for worker thread stats enable msg.\n", nss_ctx);
+
+	/*
+	 * If statistics have already been allocated, nothing else to do.
+	 */
+	if (nss_ctx->wt_stats) {
+		return;
+	}
+
+	stats_temp = nss_project_alloc_wt_stats(stats_enable->worker_thread_count,
+						stats_enable->irq_count);
+	if (unlikely(!stats_temp)) {
+		nss_warning("%p: Unable to allocate worker thread statistics.\n", nss_ctx);
+		return;
+	}
+
+	spin_lock_bh(&nss_ctx->nss_top->stats_lock);
+	nss_ctx->wt_stats = stats_temp;
+	nss_ctx->worker_thread_count = stats_enable->worker_thread_count;
+	nss_ctx->irq_count = stats_enable->irq_count;
+	spin_unlock_bh(&nss_ctx->nss_top->stats_lock);
+}
+
+/*
+ * nss_project_wt_stats_send_enable()
+ *	Sends message to firmware to enable or disable worker_thread statistics collection.
+ */
+static nss_tx_status_t nss_project_wt_stats_send_enable(struct nss_ctx_instance *nss_ctx, bool enable)
+{
+	struct nss_project_msg *npm;
+	struct sk_buff *nbuf;
+	int32_t status;
+
+	NSS_VERIFY_CTX_MAGIC(nss_ctx);
+	if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) {
+		nss_warning("%p: project msg dropped as core not ready\n", nss_ctx);
+		return NSS_TX_FAILURE_NOT_READY;
+	}
+
+	/*
+	 * Allocate the sk_buff and use its payload as an nss_project_msg
+	 */
+	nbuf = dev_alloc_skb(NSS_NBUF_PAYLOAD_SIZE);
+	if (unlikely(!nbuf)) {
+		NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_NBUF_ALLOC_FAILS]);
+		nss_warning("%p: msg dropped as command allocation failed\n", nss_ctx);
+		return NSS_TX_FAILURE;
+	}
+
+	npm = (struct nss_project_msg *)skb_put(nbuf, NSS_NBUF_PAYLOAD_SIZE);
+
+	/*
+	 * Populate the message
+	 */
+	memset(npm, 0, sizeof(struct nss_project_msg));
+	nss_cmn_msg_init(&(npm->cm), NSS_PROJECT_INTERFACE,
+		NSS_PROJECT_MSG_WT_STATS_ENABLE,
+		sizeof(struct nss_project_msg_wt_stats_enable),
+		(void *)nss_project_wt_stats_enable_callback,
+		(void *)nss_ctx);
+	npm->msg.wt_stats_enable.enable = enable;
+
+	/*
+	 * Send the sk_buff
+	 */
+	status = nss_core_send_buffer(nss_ctx, 0, nbuf, NSS_IF_CMD_QUEUE, H2N_BUFFER_CTRL, 0);
+	if (status != NSS_CORE_STATUS_SUCCESS) {
+		dev_kfree_skb_any(nbuf);
+		nss_warning("%p: unable to enqueue project msg\n", nss_ctx);
+		return NSS_TX_FAILURE;
+	}
+
+	nss_hal_send_interrupt(nss_ctx, NSS_H2N_INTR_DATA_COMMAND_QUEUE);
+
+	NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_TX_CMD_REQ]);
+	return NSS_TX_SUCCESS;
+}
+
+/*
+ * nss_project_wt_stats_update()
+ *	Updates stored statistics with the data found in the notify.
+ */
+static void nss_project_wt_stats_update(struct nss_ctx_instance *nss_ctx,
+			struct nss_project_msg_wt_stats_notify *stats_notify)
+{
+	struct nss_worker_thread_stats *wt_stats;
+	int i;
+
+	if (unlikely(!nss_ctx->wt_stats)) {
+		nss_warning("%p: Worker thread statistics not yet allocated.\n", nss_ctx);
+		return;
+	}
+
+	if (unlikely(stats_notify->threadno >= nss_ctx->worker_thread_count)) {
+		nss_warning("%p: Invalid WT number %d\n", nss_ctx, stats_notify->threadno);
+		return;
+	}
+
+	if (unlikely(stats_notify->stats_written > NSS_PROJECT_IRQS_PER_MESSAGE)) {
+		nss_warning("%p: Invalid worker thread stats written count %d\n",
+				nss_ctx, stats_notify->stats_written);
+		return;
+	}
+
+	wt_stats = &(nss_ctx->wt_stats[stats_notify->threadno]);
+
+	if (unlikely(!wt_stats->irq_stats)) {
+		nss_warning("%p: Worker thread statistics not allocated for thread %d\n",
+				nss_ctx, stats_notify->threadno);
+		return;
+	}
+
+	spin_lock_bh(&nss_ctx->nss_top->stats_lock);
+	for (i = 0; i < stats_notify->stats_written; ++i) {
+		int irq = stats_notify->stats[i].irq;
+		if (unlikely(irq >= nss_ctx->irq_count)) {
+			nss_warning("%p: Invalid IRQ number %d\n", nss_ctx, irq);
+			continue;
+		}
+
+		wt_stats->irq_stats[irq] = stats_notify->stats[i];
+	}
+	spin_unlock_bh(&nss_ctx->nss_top->stats_lock);
+}
+
+/*
+ * nss_project_msg_handler()
+ *	Handles metadata messages on the project interface.
+ */
+static void nss_project_msg_handler(struct nss_ctx_instance *nss_ctx,
+	struct nss_cmn_msg *ncm, __attribute__((unused))void *app_data)
+{
+	struct nss_project_msg *npm = (struct nss_project_msg *)ncm;
+	nss_project_msg_callback_t cb;
+
+	/*
+	 * Sanity checks on message
+	 */
+	if (npm->cm.type >= NSS_PROJECT_MSG_MAX) {
+		nss_warning("%p: message type out of range: %d\n", nss_ctx, npm->cm.type);
+		return;
+	}
+
+	if (nss_cmn_get_msg_len(&(npm->cm)) > sizeof(struct nss_project_msg)) {
+		nss_warning("%p: message length is invalid: %d\n", nss_ctx, nss_cmn_get_msg_len(&(npm->cm)));
+		return;
+	}
+
+	switch (npm->cm.type) {
+	case NSS_PROJECT_MSG_WT_STATS_NOTIFY:
+		nss_project_wt_stats_update(nss_ctx, &(npm->msg.wt_stats_notify));
+		return;
+	}
+
+	nss_core_log_msg_failures(nss_ctx, ncm);
+
+	if (!ncm->cb) {
+		return;
+	}
+
+	cb = (nss_project_msg_callback_t)ncm->cb;
+	cb((void *)nss_ctx, npm);
+}
+
+/*
+ * nss_project_wt_stats_handler()
+ *	Sysctl handler for wt_stats.
+ *
+ * Uses proc_dointvec to process data. For a write operation, also sends worker
+ * thread stats enable messages containing the new value to each NSS core.
+ */
+static int nss_project_wt_stats_handler(struct ctl_table *ctl, int write,
+	void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+	int ret;
+	int i;
+
+	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+
+	/*
+	 * In case of error, stop now.
+	 */
+	if (ret) {
+		return ret;
+	}
+
+	/*
+	 * No additional behavior necessary for a read operation.
+	 */
+	if (!write) {
+		return ret;
+	}
+
+	/*
+	 * If a value was written, send a message containing that value to each
+	 * NSS core.
+	 */
+	for (i = 0; i < NSS_MAX_CORES; ++i) {
+		nss_project_wt_stats_send_enable(&(nss_top_main.nss[i]),
+			nss_project_wt_stats_enable);
+	}
+	return ret;
+
+}
+
+/*
+ * Tree of ctl_tables used to put the wt_stats proc node in the correct place in
+ * the file system. Allows the command $ echo 1 > proc/sys/dev/nss/project/wt_stats
+ * to enable worker thread statistics (echoing 0 into the same target will disable).
+ */
+static struct ctl_table nss_project_table[] = {
+	{
+		.procname		= "wt_stats",
+		.data			= &nss_project_wt_stats_enable,
+		.maxlen			= sizeof(int),
+		.mode			= 0644,
+		.proc_handler		= &nss_project_wt_stats_handler,
+	},
+	{ }
+};
+
+static struct ctl_table nss_project_dir[] = {
+	{
+		.procname		= "project",
+		.mode			= 0555,
+		.child			= nss_project_table,
+	},
+	{ }
+};
+
+static struct ctl_table nss_project_root_dir[] = {
+	{
+		.procname		= "nss",
+		.mode			= 0555,
+		.child			= nss_project_dir,
+	},
+	{ }
+};
+
+static struct ctl_table nss_project_root[] = {
+	{
+		.procname		= "dev",
+		.mode			= 0555,
+		.child			= nss_project_root_dir,
+	},
+	{ }
+};
+
+static struct ctl_table_header *nss_project_header;
+
+/*
+ * nss_project_register_sysctl()
+ *	Registers any sysctl handlers for the project.
+ */
+void nss_project_register_sysctl(void)
+{
+	nss_project_header = register_sysctl_table(nss_project_root);
+}
+
+/*
+ * nss_project_unregister_sysctl()
+ *	De-registers any sysctl handlers for the project.
+ */
+void nss_project_unregister_sysctl(void)
+{
+	if (nss_project_header) {
+		unregister_sysctl_table(nss_project_header);
+	}
+}
+
+/*
+ * nss_project_register_handler()
+ *	Registers the handler for NSS->HLOS messages
+ */
+void nss_project_register_handler(struct nss_ctx_instance *nss_ctx)
+{
+	nss_core_register_handler(nss_ctx, NSS_PROJECT_INTERFACE, nss_project_msg_handler, NULL);
+}