Jackson Bockus | c2a4e68 | 2017-06-23 11:59:29 -0700 | [diff] [blame] | 1 | /* |
| 2 | ************************************************************************** |
Stephen Wang | 3e2dbd1 | 2018-03-14 17:28:17 -0700 | [diff] [blame] | 3 | * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. |
Jackson Bockus | c2a4e68 | 2017-06-23 11:59:29 -0700 | [diff] [blame] | 4 | * Permission to use, copy, modify, and/or distribute this software for |
| 5 | * any purpose with or without fee is hereby granted, provided that the |
| 6 | * above copyright notice and this permission notice appear in all copies. |
| 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
| 13 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 14 | ************************************************************************** |
| 15 | */ |
| 16 | |
| 17 | /* |
| 18 | * @file nss_project.h |
| 19 | * NSS project APIs. |
| 20 | */ |
| 21 | #include "nss_tx_rx_common.h" |
| 22 | |
| 23 | static int nss_project_wt_stats_enable; |
| 24 | |
| 25 | /* |
| 26 | * nss_project_free_wt_stats() |
| 27 | * Frees a number of allocated worker thread statistics. |
| 28 | */ |
| 29 | static void nss_project_free_wt_stats(struct nss_worker_thread_stats *wt_stats, int num_alloc) |
| 30 | { |
| 31 | int i; |
| 32 | |
| 33 | if (!wt_stats) { |
| 34 | return; |
| 35 | } |
| 36 | |
| 37 | for (i = 0; i < num_alloc; i++) { |
| 38 | kfree(wt_stats[i].irq_stats); |
| 39 | } |
| 40 | kfree(wt_stats); |
| 41 | } |
| 42 | |
| 43 | /* |
| 44 | * nss_project_alloc_wt_stats() |
| 45 | * Allocates worker thread stats for a given number of threads and IRQs. |
| 46 | */ |
| 47 | static struct nss_worker_thread_stats *nss_project_alloc_wt_stats(uint32_t thread_count, uint32_t irq_count) |
| 48 | { |
| 49 | struct nss_worker_thread_stats *wt_stats; |
| 50 | int i; |
| 51 | |
| 52 | wt_stats = kzalloc(thread_count * sizeof(struct nss_worker_thread_stats), GFP_ATOMIC); |
| 53 | if (unlikely(!wt_stats)) { |
| 54 | return NULL; |
| 55 | } |
| 56 | |
| 57 | for (i = 0; i < thread_count; i++) { |
| 58 | wt_stats[i].irq_stats = |
| 59 | kzalloc(irq_count * sizeof(struct nss_project_irq_stats), GFP_ATOMIC); |
| 60 | if (unlikely(!wt_stats[i].irq_stats)) { |
| 61 | nss_project_free_wt_stats(wt_stats, i); |
| 62 | return NULL; |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | return wt_stats; |
| 67 | } |
| 68 | |
| 69 | /* |
| 70 | * nss_project_wt_stats_enable_callback() |
| 71 | * Callback function for wt stats enable messages |
| 72 | */ |
| 73 | static void nss_project_wt_stats_enable_callback(void *app_data, struct nss_project_msg *msg) |
| 74 | { |
| 75 | struct nss_ctx_instance *nss_ctx = (struct nss_ctx_instance *)app_data; |
| 76 | struct nss_project_msg_wt_stats_enable *stats_enable = &msg->msg.wt_stats_enable; |
| 77 | struct nss_worker_thread_stats *stats_temp; |
| 78 | |
| 79 | NSS_VERIFY_CTX_MAGIC(nss_ctx); |
| 80 | if (msg->cm.response != NSS_CMN_RESPONSE_ACK) { |
| 81 | return; |
| 82 | } |
| 83 | |
| 84 | nss_info("%p: Received response ACK for worker thread stats enable msg.\n", nss_ctx); |
| 85 | |
| 86 | /* |
| 87 | * If statistics have already been allocated, nothing else to do. |
| 88 | */ |
| 89 | if (nss_ctx->wt_stats) { |
| 90 | return; |
| 91 | } |
| 92 | |
| 93 | stats_temp = nss_project_alloc_wt_stats(stats_enable->worker_thread_count, |
| 94 | stats_enable->irq_count); |
| 95 | if (unlikely(!stats_temp)) { |
| 96 | nss_warning("%p: Unable to allocate worker thread statistics.\n", nss_ctx); |
| 97 | return; |
| 98 | } |
| 99 | |
| 100 | spin_lock_bh(&nss_ctx->nss_top->stats_lock); |
| 101 | nss_ctx->wt_stats = stats_temp; |
| 102 | nss_ctx->worker_thread_count = stats_enable->worker_thread_count; |
| 103 | nss_ctx->irq_count = stats_enable->irq_count; |
| 104 | spin_unlock_bh(&nss_ctx->nss_top->stats_lock); |
| 105 | } |
| 106 | |
| 107 | /* |
| 108 | * nss_project_wt_stats_send_enable() |
| 109 | * Sends message to firmware to enable or disable worker_thread statistics collection. |
| 110 | */ |
| 111 | static nss_tx_status_t nss_project_wt_stats_send_enable(struct nss_ctx_instance *nss_ctx, bool enable) |
| 112 | { |
| 113 | struct nss_project_msg *npm; |
Stephen Wang | 3e2dbd1 | 2018-03-14 17:28:17 -0700 | [diff] [blame] | 114 | struct nss_cmn_msg *ncm; |
| 115 | nss_tx_status_t ret; |
Jackson Bockus | c2a4e68 | 2017-06-23 11:59:29 -0700 | [diff] [blame] | 116 | |
Stephen Wang | 3e2dbd1 | 2018-03-14 17:28:17 -0700 | [diff] [blame] | 117 | npm = kzalloc(sizeof(*npm), GFP_KERNEL); |
| 118 | if (!npm) { |
| 119 | nss_warning("%p: Failed to allocate buffer for message\n", nss_ctx); |
Jackson Bockus | c2a4e68 | 2017-06-23 11:59:29 -0700 | [diff] [blame] | 120 | return NSS_TX_FAILURE; |
| 121 | } |
| 122 | |
Jackson Bockus | c2a4e68 | 2017-06-23 11:59:29 -0700 | [diff] [blame] | 123 | /* |
| 124 | * Populate the message |
| 125 | */ |
Stephen Wang | 3e2dbd1 | 2018-03-14 17:28:17 -0700 | [diff] [blame] | 126 | ncm = &npm->cm; |
| 127 | nss_cmn_msg_init(ncm, NSS_PROJECT_INTERFACE, |
Jackson Bockus | c2a4e68 | 2017-06-23 11:59:29 -0700 | [diff] [blame] | 128 | NSS_PROJECT_MSG_WT_STATS_ENABLE, |
| 129 | sizeof(struct nss_project_msg_wt_stats_enable), |
| 130 | (void *)nss_project_wt_stats_enable_callback, |
| 131 | (void *)nss_ctx); |
| 132 | npm->msg.wt_stats_enable.enable = enable; |
| 133 | |
Stephen Wang | 3e2dbd1 | 2018-03-14 17:28:17 -0700 | [diff] [blame] | 134 | ret = nss_core_send_cmd(nss_ctx, npm, sizeof(*npm), NSS_NBUF_PAYLOAD_SIZE); |
| 135 | kfree(npm); |
| 136 | return ret; |
Jackson Bockus | c2a4e68 | 2017-06-23 11:59:29 -0700 | [diff] [blame] | 137 | } |
| 138 | |
| 139 | /* |
| 140 | * nss_project_wt_stats_update() |
| 141 | * Updates stored statistics with the data found in the notify. |
| 142 | */ |
| 143 | static void nss_project_wt_stats_update(struct nss_ctx_instance *nss_ctx, |
| 144 | struct nss_project_msg_wt_stats_notify *stats_notify) |
| 145 | { |
| 146 | struct nss_worker_thread_stats *wt_stats; |
| 147 | int i; |
| 148 | |
| 149 | if (unlikely(!nss_ctx->wt_stats)) { |
| 150 | nss_warning("%p: Worker thread statistics not yet allocated.\n", nss_ctx); |
| 151 | return; |
| 152 | } |
| 153 | |
| 154 | if (unlikely(stats_notify->threadno >= nss_ctx->worker_thread_count)) { |
| 155 | nss_warning("%p: Invalid WT number %d\n", nss_ctx, stats_notify->threadno); |
| 156 | return; |
| 157 | } |
| 158 | |
| 159 | if (unlikely(stats_notify->stats_written > NSS_PROJECT_IRQS_PER_MESSAGE)) { |
| 160 | nss_warning("%p: Invalid worker thread stats written count %d\n", |
| 161 | nss_ctx, stats_notify->stats_written); |
| 162 | return; |
| 163 | } |
| 164 | |
| 165 | wt_stats = &(nss_ctx->wt_stats[stats_notify->threadno]); |
| 166 | |
| 167 | if (unlikely(!wt_stats->irq_stats)) { |
| 168 | nss_warning("%p: Worker thread statistics not allocated for thread %d\n", |
| 169 | nss_ctx, stats_notify->threadno); |
| 170 | return; |
| 171 | } |
| 172 | |
| 173 | spin_lock_bh(&nss_ctx->nss_top->stats_lock); |
| 174 | for (i = 0; i < stats_notify->stats_written; ++i) { |
| 175 | int irq = stats_notify->stats[i].irq; |
| 176 | if (unlikely(irq >= nss_ctx->irq_count)) { |
| 177 | nss_warning("%p: Invalid IRQ number %d\n", nss_ctx, irq); |
| 178 | continue; |
| 179 | } |
| 180 | |
| 181 | wt_stats->irq_stats[irq] = stats_notify->stats[i]; |
| 182 | } |
| 183 | spin_unlock_bh(&nss_ctx->nss_top->stats_lock); |
| 184 | } |
| 185 | |
| 186 | /* |
| 187 | * nss_project_msg_handler() |
| 188 | * Handles metadata messages on the project interface. |
| 189 | */ |
| 190 | static void nss_project_msg_handler(struct nss_ctx_instance *nss_ctx, |
| 191 | struct nss_cmn_msg *ncm, __attribute__((unused))void *app_data) |
| 192 | { |
| 193 | struct nss_project_msg *npm = (struct nss_project_msg *)ncm; |
| 194 | nss_project_msg_callback_t cb; |
| 195 | |
| 196 | /* |
| 197 | * Sanity checks on message |
| 198 | */ |
| 199 | if (npm->cm.type >= NSS_PROJECT_MSG_MAX) { |
| 200 | nss_warning("%p: message type out of range: %d\n", nss_ctx, npm->cm.type); |
| 201 | return; |
| 202 | } |
| 203 | |
| 204 | if (nss_cmn_get_msg_len(&(npm->cm)) > sizeof(struct nss_project_msg)) { |
| 205 | nss_warning("%p: message length is invalid: %d\n", nss_ctx, nss_cmn_get_msg_len(&(npm->cm))); |
| 206 | return; |
| 207 | } |
| 208 | |
| 209 | switch (npm->cm.type) { |
| 210 | case NSS_PROJECT_MSG_WT_STATS_NOTIFY: |
| 211 | nss_project_wt_stats_update(nss_ctx, &(npm->msg.wt_stats_notify)); |
| 212 | return; |
| 213 | } |
| 214 | |
| 215 | nss_core_log_msg_failures(nss_ctx, ncm); |
| 216 | |
| 217 | if (!ncm->cb) { |
| 218 | return; |
| 219 | } |
| 220 | |
| 221 | cb = (nss_project_msg_callback_t)ncm->cb; |
| 222 | cb((void *)nss_ctx, npm); |
| 223 | } |
| 224 | |
| 225 | /* |
| 226 | * nss_project_wt_stats_handler() |
| 227 | * Sysctl handler for wt_stats. |
| 228 | * |
| 229 | * Uses proc_dointvec to process data. For a write operation, also sends worker |
| 230 | * thread stats enable messages containing the new value to each NSS core. |
| 231 | */ |
| 232 | static int nss_project_wt_stats_handler(struct ctl_table *ctl, int write, |
| 233 | void __user *buffer, size_t *lenp, loff_t *ppos) |
| 234 | { |
| 235 | int ret; |
| 236 | int i; |
| 237 | |
| 238 | ret = proc_dointvec(ctl, write, buffer, lenp, ppos); |
| 239 | |
| 240 | /* |
| 241 | * In case of error, stop now. |
| 242 | */ |
| 243 | if (ret) { |
| 244 | return ret; |
| 245 | } |
| 246 | |
| 247 | /* |
| 248 | * No additional behavior necessary for a read operation. |
| 249 | */ |
| 250 | if (!write) { |
| 251 | return ret; |
| 252 | } |
| 253 | |
| 254 | /* |
| 255 | * If a value was written, send a message containing that value to each |
| 256 | * NSS core. |
| 257 | */ |
Suman Ghosh | 9f7b370 | 2018-09-21 19:51:40 +0530 | [diff] [blame] | 258 | for (i = 0; i < nss_top_main.num_nss; ++i) { |
Jackson Bockus | c2a4e68 | 2017-06-23 11:59:29 -0700 | [diff] [blame] | 259 | nss_project_wt_stats_send_enable(&(nss_top_main.nss[i]), |
| 260 | nss_project_wt_stats_enable); |
| 261 | } |
| 262 | return ret; |
| 263 | |
| 264 | } |
| 265 | |
| 266 | /* |
| 267 | * Tree of ctl_tables used to put the wt_stats proc node in the correct place in |
| 268 | * the file system. Allows the command $ echo 1 > proc/sys/dev/nss/project/wt_stats |
| 269 | * to enable worker thread statistics (echoing 0 into the same target will disable). |
| 270 | */ |
| 271 | static struct ctl_table nss_project_table[] = { |
| 272 | { |
| 273 | .procname = "wt_stats", |
| 274 | .data = &nss_project_wt_stats_enable, |
| 275 | .maxlen = sizeof(int), |
| 276 | .mode = 0644, |
| 277 | .proc_handler = &nss_project_wt_stats_handler, |
| 278 | }, |
| 279 | { } |
| 280 | }; |
| 281 | |
| 282 | static struct ctl_table nss_project_dir[] = { |
| 283 | { |
| 284 | .procname = "project", |
| 285 | .mode = 0555, |
| 286 | .child = nss_project_table, |
| 287 | }, |
| 288 | { } |
| 289 | }; |
| 290 | |
| 291 | static struct ctl_table nss_project_root_dir[] = { |
| 292 | { |
| 293 | .procname = "nss", |
| 294 | .mode = 0555, |
| 295 | .child = nss_project_dir, |
| 296 | }, |
| 297 | { } |
| 298 | }; |
| 299 | |
| 300 | static struct ctl_table nss_project_root[] = { |
| 301 | { |
| 302 | .procname = "dev", |
| 303 | .mode = 0555, |
| 304 | .child = nss_project_root_dir, |
| 305 | }, |
| 306 | { } |
| 307 | }; |
| 308 | |
| 309 | static struct ctl_table_header *nss_project_header; |
| 310 | |
| 311 | /* |
| 312 | * nss_project_register_sysctl() |
| 313 | * Registers any sysctl handlers for the project. |
| 314 | */ |
| 315 | void nss_project_register_sysctl(void) |
| 316 | { |
| 317 | nss_project_header = register_sysctl_table(nss_project_root); |
| 318 | } |
| 319 | |
| 320 | /* |
| 321 | * nss_project_unregister_sysctl() |
| 322 | * De-registers any sysctl handlers for the project. |
| 323 | */ |
| 324 | void nss_project_unregister_sysctl(void) |
| 325 | { |
| 326 | if (nss_project_header) { |
| 327 | unregister_sysctl_table(nss_project_header); |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | /* |
| 332 | * nss_project_register_handler() |
| 333 | * Registers the handler for NSS->HLOS messages |
| 334 | */ |
| 335 | void nss_project_register_handler(struct nss_ctx_instance *nss_ctx) |
| 336 | { |
| 337 | nss_core_register_handler(nss_ctx, NSS_PROJECT_INTERFACE, nss_project_msg_handler, NULL); |
| 338 | } |