Jackson Bockus | c2a4e68 | 2017-06-23 11:59:29 -0700 | [diff] [blame] | 1 | /* |
| 2 | ************************************************************************** |
| 3 | * Copyright (c) 2017, The Linux Foundation. All rights reserved. |
| 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; |
| 114 | struct sk_buff *nbuf; |
| 115 | int32_t status; |
| 116 | |
| 117 | NSS_VERIFY_CTX_MAGIC(nss_ctx); |
| 118 | if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) { |
| 119 | nss_warning("%p: project msg dropped as core not ready\n", nss_ctx); |
| 120 | return NSS_TX_FAILURE_NOT_READY; |
| 121 | } |
| 122 | |
| 123 | /* |
| 124 | * Allocate the sk_buff and use its payload as an nss_project_msg |
| 125 | */ |
| 126 | nbuf = dev_alloc_skb(NSS_NBUF_PAYLOAD_SIZE); |
| 127 | if (unlikely(!nbuf)) { |
| 128 | NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_NBUF_ALLOC_FAILS]); |
| 129 | nss_warning("%p: msg dropped as command allocation failed\n", nss_ctx); |
| 130 | return NSS_TX_FAILURE; |
| 131 | } |
| 132 | |
| 133 | npm = (struct nss_project_msg *)skb_put(nbuf, NSS_NBUF_PAYLOAD_SIZE); |
| 134 | |
| 135 | /* |
| 136 | * Populate the message |
| 137 | */ |
| 138 | memset(npm, 0, sizeof(struct nss_project_msg)); |
| 139 | nss_cmn_msg_init(&(npm->cm), NSS_PROJECT_INTERFACE, |
| 140 | NSS_PROJECT_MSG_WT_STATS_ENABLE, |
| 141 | sizeof(struct nss_project_msg_wt_stats_enable), |
| 142 | (void *)nss_project_wt_stats_enable_callback, |
| 143 | (void *)nss_ctx); |
| 144 | npm->msg.wt_stats_enable.enable = enable; |
| 145 | |
| 146 | /* |
| 147 | * Send the sk_buff |
| 148 | */ |
| 149 | status = nss_core_send_buffer(nss_ctx, 0, nbuf, NSS_IF_CMD_QUEUE, H2N_BUFFER_CTRL, 0); |
| 150 | if (status != NSS_CORE_STATUS_SUCCESS) { |
| 151 | dev_kfree_skb_any(nbuf); |
| 152 | nss_warning("%p: unable to enqueue project msg\n", nss_ctx); |
| 153 | return NSS_TX_FAILURE; |
| 154 | } |
| 155 | |
| 156 | nss_hal_send_interrupt(nss_ctx, NSS_H2N_INTR_DATA_COMMAND_QUEUE); |
| 157 | |
| 158 | NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_TX_CMD_REQ]); |
| 159 | return NSS_TX_SUCCESS; |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | * nss_project_wt_stats_update() |
| 164 | * Updates stored statistics with the data found in the notify. |
| 165 | */ |
| 166 | static void nss_project_wt_stats_update(struct nss_ctx_instance *nss_ctx, |
| 167 | struct nss_project_msg_wt_stats_notify *stats_notify) |
| 168 | { |
| 169 | struct nss_worker_thread_stats *wt_stats; |
| 170 | int i; |
| 171 | |
| 172 | if (unlikely(!nss_ctx->wt_stats)) { |
| 173 | nss_warning("%p: Worker thread statistics not yet allocated.\n", nss_ctx); |
| 174 | return; |
| 175 | } |
| 176 | |
| 177 | if (unlikely(stats_notify->threadno >= nss_ctx->worker_thread_count)) { |
| 178 | nss_warning("%p: Invalid WT number %d\n", nss_ctx, stats_notify->threadno); |
| 179 | return; |
| 180 | } |
| 181 | |
| 182 | if (unlikely(stats_notify->stats_written > NSS_PROJECT_IRQS_PER_MESSAGE)) { |
| 183 | nss_warning("%p: Invalid worker thread stats written count %d\n", |
| 184 | nss_ctx, stats_notify->stats_written); |
| 185 | return; |
| 186 | } |
| 187 | |
| 188 | wt_stats = &(nss_ctx->wt_stats[stats_notify->threadno]); |
| 189 | |
| 190 | if (unlikely(!wt_stats->irq_stats)) { |
| 191 | nss_warning("%p: Worker thread statistics not allocated for thread %d\n", |
| 192 | nss_ctx, stats_notify->threadno); |
| 193 | return; |
| 194 | } |
| 195 | |
| 196 | spin_lock_bh(&nss_ctx->nss_top->stats_lock); |
| 197 | for (i = 0; i < stats_notify->stats_written; ++i) { |
| 198 | int irq = stats_notify->stats[i].irq; |
| 199 | if (unlikely(irq >= nss_ctx->irq_count)) { |
| 200 | nss_warning("%p: Invalid IRQ number %d\n", nss_ctx, irq); |
| 201 | continue; |
| 202 | } |
| 203 | |
| 204 | wt_stats->irq_stats[irq] = stats_notify->stats[i]; |
| 205 | } |
| 206 | spin_unlock_bh(&nss_ctx->nss_top->stats_lock); |
| 207 | } |
| 208 | |
| 209 | /* |
| 210 | * nss_project_msg_handler() |
| 211 | * Handles metadata messages on the project interface. |
| 212 | */ |
| 213 | static void nss_project_msg_handler(struct nss_ctx_instance *nss_ctx, |
| 214 | struct nss_cmn_msg *ncm, __attribute__((unused))void *app_data) |
| 215 | { |
| 216 | struct nss_project_msg *npm = (struct nss_project_msg *)ncm; |
| 217 | nss_project_msg_callback_t cb; |
| 218 | |
| 219 | /* |
| 220 | * Sanity checks on message |
| 221 | */ |
| 222 | if (npm->cm.type >= NSS_PROJECT_MSG_MAX) { |
| 223 | nss_warning("%p: message type out of range: %d\n", nss_ctx, npm->cm.type); |
| 224 | return; |
| 225 | } |
| 226 | |
| 227 | if (nss_cmn_get_msg_len(&(npm->cm)) > sizeof(struct nss_project_msg)) { |
| 228 | nss_warning("%p: message length is invalid: %d\n", nss_ctx, nss_cmn_get_msg_len(&(npm->cm))); |
| 229 | return; |
| 230 | } |
| 231 | |
| 232 | switch (npm->cm.type) { |
| 233 | case NSS_PROJECT_MSG_WT_STATS_NOTIFY: |
| 234 | nss_project_wt_stats_update(nss_ctx, &(npm->msg.wt_stats_notify)); |
| 235 | return; |
| 236 | } |
| 237 | |
| 238 | nss_core_log_msg_failures(nss_ctx, ncm); |
| 239 | |
| 240 | if (!ncm->cb) { |
| 241 | return; |
| 242 | } |
| 243 | |
| 244 | cb = (nss_project_msg_callback_t)ncm->cb; |
| 245 | cb((void *)nss_ctx, npm); |
| 246 | } |
| 247 | |
| 248 | /* |
| 249 | * nss_project_wt_stats_handler() |
| 250 | * Sysctl handler for wt_stats. |
| 251 | * |
| 252 | * Uses proc_dointvec to process data. For a write operation, also sends worker |
| 253 | * thread stats enable messages containing the new value to each NSS core. |
| 254 | */ |
| 255 | static int nss_project_wt_stats_handler(struct ctl_table *ctl, int write, |
| 256 | void __user *buffer, size_t *lenp, loff_t *ppos) |
| 257 | { |
| 258 | int ret; |
| 259 | int i; |
| 260 | |
| 261 | ret = proc_dointvec(ctl, write, buffer, lenp, ppos); |
| 262 | |
| 263 | /* |
| 264 | * In case of error, stop now. |
| 265 | */ |
| 266 | if (ret) { |
| 267 | return ret; |
| 268 | } |
| 269 | |
| 270 | /* |
| 271 | * No additional behavior necessary for a read operation. |
| 272 | */ |
| 273 | if (!write) { |
| 274 | return ret; |
| 275 | } |
| 276 | |
| 277 | /* |
| 278 | * If a value was written, send a message containing that value to each |
| 279 | * NSS core. |
| 280 | */ |
| 281 | for (i = 0; i < NSS_MAX_CORES; ++i) { |
| 282 | nss_project_wt_stats_send_enable(&(nss_top_main.nss[i]), |
| 283 | nss_project_wt_stats_enable); |
| 284 | } |
| 285 | return ret; |
| 286 | |
| 287 | } |
| 288 | |
| 289 | /* |
| 290 | * Tree of ctl_tables used to put the wt_stats proc node in the correct place in |
| 291 | * the file system. Allows the command $ echo 1 > proc/sys/dev/nss/project/wt_stats |
| 292 | * to enable worker thread statistics (echoing 0 into the same target will disable). |
| 293 | */ |
| 294 | static struct ctl_table nss_project_table[] = { |
| 295 | { |
| 296 | .procname = "wt_stats", |
| 297 | .data = &nss_project_wt_stats_enable, |
| 298 | .maxlen = sizeof(int), |
| 299 | .mode = 0644, |
| 300 | .proc_handler = &nss_project_wt_stats_handler, |
| 301 | }, |
| 302 | { } |
| 303 | }; |
| 304 | |
| 305 | static struct ctl_table nss_project_dir[] = { |
| 306 | { |
| 307 | .procname = "project", |
| 308 | .mode = 0555, |
| 309 | .child = nss_project_table, |
| 310 | }, |
| 311 | { } |
| 312 | }; |
| 313 | |
| 314 | static struct ctl_table nss_project_root_dir[] = { |
| 315 | { |
| 316 | .procname = "nss", |
| 317 | .mode = 0555, |
| 318 | .child = nss_project_dir, |
| 319 | }, |
| 320 | { } |
| 321 | }; |
| 322 | |
| 323 | static struct ctl_table nss_project_root[] = { |
| 324 | { |
| 325 | .procname = "dev", |
| 326 | .mode = 0555, |
| 327 | .child = nss_project_root_dir, |
| 328 | }, |
| 329 | { } |
| 330 | }; |
| 331 | |
| 332 | static struct ctl_table_header *nss_project_header; |
| 333 | |
| 334 | /* |
| 335 | * nss_project_register_sysctl() |
| 336 | * Registers any sysctl handlers for the project. |
| 337 | */ |
| 338 | void nss_project_register_sysctl(void) |
| 339 | { |
| 340 | nss_project_header = register_sysctl_table(nss_project_root); |
| 341 | } |
| 342 | |
| 343 | /* |
| 344 | * nss_project_unregister_sysctl() |
| 345 | * De-registers any sysctl handlers for the project. |
| 346 | */ |
| 347 | void nss_project_unregister_sysctl(void) |
| 348 | { |
| 349 | if (nss_project_header) { |
| 350 | unregister_sysctl_table(nss_project_header); |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | /* |
| 355 | * nss_project_register_handler() |
| 356 | * Registers the handler for NSS->HLOS messages |
| 357 | */ |
| 358 | void nss_project_register_handler(struct nss_ctx_instance *nss_ctx) |
| 359 | { |
| 360 | nss_core_register_handler(nss_ctx, NSS_PROJECT_INTERFACE, nss_project_msg_handler, NULL); |
| 361 | } |