Merge "[qca-nss-drv] Added statistics for worker threads."
diff --git a/Makefile b/Makefile
index 276487b..4cab556 100644
--- a/Makefile
+++ b/Makefile
@@ -40,6 +40,7 @@
nss_phys_if.o \
nss_pm.o \
nss_profiler.o \
+ nss_project.o \
nss_portid.o \
nss_ppe.o \
nss_pppoe.o \
diff --git a/exports/nss_api_if.h b/exports/nss_api_if.h
index a5cd4f0..d23e18c 100644
--- a/exports/nss_api_if.h
+++ b/exports/nss_api_if.h
@@ -65,6 +65,7 @@
#include "nss_vlan.h"
#include "nss_wifili_if.h"
#include "nss_dscp2pri.h"
+#include "nss_project.h"
#endif
/**
diff --git a/exports/nss_project.h b/exports/nss_project.h
new file mode 100644
index 0000000..a1a2cc9
--- /dev/null
+++ b/exports/nss_project.h
@@ -0,0 +1,176 @@
+/*
+ **************************************************************************
+ * 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 interface definitions.
+ */
+
+#ifndef __NSS_PROJECT_H
+#define __NSS_PROJECT_H
+
+/**
+ * @addtogroup nss_project_subsystem
+ * @{
+ */
+
+ /**
+ * Maximum number of IRQs for which a message will have statistics.
+ *
+ * Must be defined on firmware and host such that NSS_PROJECT_IRQS_PER_MESSAGE *
+ * sizeof(struct nss_project_irq_stats) + 8 + sizeof(struct nss_cmn_msg) is smaller
+ * than the maximum payload size of an sk_buff (1792), 8 being the number of
+ * bytes needed to store the thread number and number of statistics written.
+ */
+#define NSS_PROJECT_IRQS_PER_MESSAGE 32
+
+/**
+ * nss_project_message_types
+ * Project message types.
+ */
+enum nss_project_message_types {
+ NSS_PROJECT_MSG_WT_STATS_ENABLE,
+ /**< Message to enable or disable worker thread statistics. */
+ NSS_PROJECT_MSG_WT_STATS_NOTIFY,
+ /**< NSS to HLOS message containing worker thread statistics. */
+ NSS_PROJECT_MSG_MAX,
+};
+
+/**
+ * nss_project_error_types
+ * Project error types.
+ */
+enum nss_project_error_types {
+ NSS_PROJECT_ERROR_UNKNOWN_MSG,
+ /**< Unrecognized message type. */
+ NSS_PROJECT_ERROR_WT_STATS_UNSUPPORTED,
+ /**< The firmware does not support worker thread statistics. */
+ NSS_PROJECT_ERROR_WT_STATS_REDUNDANT_ENABLE,
+ /**< The firmware received a redundant request to enable worker thread statistics. */
+ NSS_PROJECT_ERROR_MAX,
+};
+
+/**
+ * nss_project_msg_wt_stats_enable
+ * Enables or disables worker thread statistics collection.
+ */
+struct nss_project_msg_wt_stats_enable {
+
+ /*
+ * NSS to HLOS
+ */
+ uint32_t worker_thread_count;
+ /**< Number of worker threads supported by this core. */
+ uint32_t irq_count;
+ /**< Number of IRQs supported by this core. */
+
+ /*
+ * HLOS to NSS
+ */
+ bool enable; /**< True to enable, false to disable. */
+};
+
+/**
+ * nss_project_irq_stats
+ * Statistics for an individual IRQ on a worker thread.
+ */
+struct nss_project_irq_stats {
+ uint64_t count; /**< Number of times callback has been executed */
+ uint32_t callback; /**< Address of the callback function */
+ uint32_t irq; /**< IRQ number to which callback function is bound */
+ uint32_t ticks_min; /**< Fewest ticks taken in callback function */
+ uint32_t ticks_avg; /**< Exponential moving average of ticks */
+ uint32_t ticks_max; /**< Maximum ticks */
+ uint32_t insn_min; /**< Fewest instructions executed in callback function */
+ uint32_t insn_avg; /**< Exponential moving average of instruction count */
+ uint32_t insn_max; /**< Maximum instructions */
+};
+
+/**
+ * nss_project_msg_wt_stats_notify
+ * Message containing statistics for active worker_thread IRQs.
+ */
+struct nss_project_msg_wt_stats_notify {
+ uint32_t threadno; /**< The thread whose stats are contained. */
+ uint32_t stats_written; /**< The number of statistics written to the array. */
+ struct nss_project_irq_stats stats[NSS_PROJECT_IRQS_PER_MESSAGE];
+ /**< The per-IRQ statistics for the worker thread */
+};
+
+/**
+ * nss_project_msg
+ * General message structure for project messages.
+ */
+struct nss_project_msg {
+ struct nss_cmn_msg cm; /**< Common message header. */
+
+ /**
+ * Payload of a message to or from the project code.
+ */
+ union {
+ struct nss_project_msg_wt_stats_enable wt_stats_enable;
+ /**< Enable or disable worker thread statistics. */
+ struct nss_project_msg_wt_stats_notify wt_stats_notify;
+ /**< One-way worker thread statistics message. */
+ } msg;
+};
+
+/**
+ * Callback function for receiving project messages.
+ *
+ * @datatypes
+ * nss_project_msg
+ *
+ * @param[in] app_data Pointer to the application context of the message.
+ * @param[in] msg Pointer to the project message.
+ */
+typedef void (*nss_project_msg_callback_t)(void *app_data, struct nss_project_msg *msg);
+
+/**
+ * nss_project_register_sysctl
+ * Registers the project sysctl table to the sysctl tree.
+ *
+ * @return
+ * None.
+ */
+void nss_project_register_sysctl(void);
+
+/**
+ * nss_project_unregister_sysctl
+ * De-registers the project sysctl table from the sysctl tree.
+ *
+ * @return
+ * None.
+ *
+ * @dependencies
+ * The system control must have been previously registered.
+ */
+void nss_project_unregister_sysctl(void);
+
+/**
+ * nss_project_register_handler
+ * Registers the project message handler.
+ *
+ * @return
+ * None.
+ */
+void nss_project_register_handler(struct nss_ctx_instance *nss_ctx);
+
+/**
+ * @}
+ */
+
+#endif /* __NSS_PROJECT_H */
diff --git a/nss_core.h b/nss_core.h
index b6ba153..2608bca 100644
--- a/nss_core.h
+++ b/nss_core.h
@@ -190,6 +190,7 @@
*/
#define NSS_PPPOE_NUM_SESSION_PER_INTERFACE 4
/* Number of maximum simultaneous PPPoE sessions per physical interface */
+
/*
* NSS Frequency Defines and Values
*
@@ -1444,6 +1445,13 @@
};
/*
+ * Holds statistics for every worker thread on a core
+ */
+struct nss_worker_thread_stats {
+ struct nss_project_irq_stats *irq_stats;
+};
+
+/*
* NSS context instance (one per NSS core)
*/
struct nss_ctx_instance {
@@ -1479,6 +1487,10 @@
/* Current MTU value of physical interface */
uint64_t stats_n2h[NSS_STATS_N2H_MAX];
/* N2H node stats: includes node, n2h, pbuf in this order */
+ uint32_t worker_thread_count; /* Number of NSS core worker threads for statistics */
+ uint32_t irq_count; /* Number of NSS core IRQs for statistics */
+ struct nss_worker_thread_stats *wt_stats;
+ /* Worker thread statistics */
struct nss_rx_cb_list nss_rx_interface_handlers[NSS_MAX_CORES][NSS_MAX_NET_INTERFACES];
/* NSS interface callback handlers */
struct nss_subsystem_dataplane_register subsys_dp_register[NSS_MAX_NET_INTERFACES];
@@ -1559,6 +1571,8 @@
struct dentry *virt_if_dentry; /* virt_if stats dentry */
struct dentry *tx_rx_virt_if_dentry; /* tx_rx_virt_if stats dentry. Will be deprecated soon */
struct dentry *wifili_dentry; /* wifili stats dentry */
+ struct dentry *project_dentry; /* per-project stats dentry */
+
struct nss_ctx_instance nss[NSS_MAX_CORES];
/* NSS contexts */
/*
diff --git a/nss_hal/nss_hal.c b/nss_hal/nss_hal.c
index 13d4de5..28372f2 100644
--- a/nss_hal/nss_hal.c
+++ b/nss_hal/nss_hal.c
@@ -333,6 +333,7 @@
*/
nss_dynamic_interface_register_handler(nss_ctx);
nss_n2h_register_handler(nss_ctx);
+ nss_project_register_handler(nss_ctx);
/*
* Check functionalities are supported by this NSS core
@@ -499,6 +500,7 @@
#if (NSS_FREQ_SCALE_SUPPORT == 1)
nss_freq_register_handler();
#endif
+
nss_lso_rx_register_handler(nss_ctx);
}
diff --git a/nss_init.c b/nss_init.c
index 1bee547..433d508 100644
--- a/nss_init.c
+++ b/nss_init.c
@@ -745,6 +745,11 @@
nss_dscp2pri_register_sysctl();
/*
+ * Register sysctl for project config
+ */
+ nss_project_register_sysctl();
+
+ /*
* Setup Runtime Sample values
*/
nss_runtime_samples.freq_scale_index = 1;
@@ -831,6 +836,7 @@
nss_ipv6_unregister_sysctl();
nss_dscp2pri_unregister_sysctl();
+ nss_project_unregister_sysctl();
nss_data_plane_destroy_delay_work();
/*
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);
+}
diff --git a/nss_stats.c b/nss_stats.c
index 2df2c92..d8adebc 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 */
};
/*
@@ -4215,6 +4217,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)
@@ -4229,6 +4314,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;
@@ -4429,6 +4515,10 @@
NSS_STATS_DECLARE_FILE_OPERATIONS(wifili)
/*
+ * wt_stats_ops
+ */
+NSS_STATS_DECLARE_FILE_OPERATIONS(wt)
+/*
* nss_stats_init()
* Enable NSS statistics
*/
@@ -4458,6 +4548,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];
@@ -4984,6 +5077,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();
}