blob: 237b1be76fc001187c1208a67cb07aa88e8cbec7 [file] [log] [blame]
Jackson Bockusc2a4e682017-06-23 11:59:29 -07001/*
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
23static int nss_project_wt_stats_enable;
24
25/*
26 * nss_project_free_wt_stats()
27 * Frees a number of allocated worker thread statistics.
28 */
29static 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 */
47static 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 */
73static 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 */
111static 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 */
166static 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 */
213static 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 */
255static 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 */
294static 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
305static struct ctl_table nss_project_dir[] = {
306 {
307 .procname = "project",
308 .mode = 0555,
309 .child = nss_project_table,
310 },
311 { }
312};
313
314static struct ctl_table nss_project_root_dir[] = {
315 {
316 .procname = "nss",
317 .mode = 0555,
318 .child = nss_project_dir,
319 },
320 { }
321};
322
323static struct ctl_table nss_project_root[] = {
324 {
325 .procname = "dev",
326 .mode = 0555,
327 .child = nss_project_root_dir,
328 },
329 { }
330};
331
332static struct ctl_table_header *nss_project_header;
333
334/*
335 * nss_project_register_sysctl()
336 * Registers any sysctl handlers for the project.
337 */
338void 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 */
347void 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 */
358void 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}