blob: 5ead0148e689de83069ebfbc1faae1de52fe8a42 [file] [log] [blame]
Saurabh Misra09dddeb2014-09-30 16:38:07 -07001/*
2 **************************************************************************
3 * Copyright (c) 2014, 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 * nss_capwap.h
19 * NSS CAPWAP driver interface APIs
20 */
21#include "nss_core.h"
22#include <nss_hal.h>
23#include <linux/module.h>
24#include "nss_cmn.h"
25#include "nss_capwap.h"
26
27/*
28 * Spinlock for protecting tunnel operations colliding with a tunnel destroy
29 */
30DEFINE_SPINLOCK(nss_capwap_spinlock);
31
32/*
33 * Array of pointer for NSS CAPWAP handles. Each handle has per-tunnel
34 * stats based on the if_num which is an index.
35 *
36 * Per CAPWAP tunnel/interface number instance.
37 */
38struct nss_capwap_handle {
39 atomic_t refcnt; /**< Reference count on the tunnel */
40 uint32_t interface_num; /**< Interface number */
41 uint32_t tunnel_status; /**< 0=disable, 1=enabled */
42 struct nss_ctx_instance *ctx; /**< Pointer to context */
43 nss_capwap_msg_callback_t msg_callback; /**< Msg callback */
44 void *app_data; /**< App data (argument) */
45 struct nss_capwap_tunnel_stats stats; /**< Stats per-interface number */
46};
47static struct nss_capwap_handle *nss_capwap_hdl[NSS_MAX_DYNAMIC_INTERFACES];
48
49/*
50 * Global definitions.
51 */
52extern struct nss_top_instance nss_top_main;
53
54/*
55 * nss_capwap_verify_if_num()
56 * Verify if_num passed to us.
57 */
58static bool nss_capwap_verify_if_num(uint32_t if_num)
59{
60 if (nss_is_dynamic_interface(if_num) == false) {
61 return false;
62 }
63
64 if (nss_dynamic_interface_get_type(if_num) != NSS_DYNAMIC_INTERFACE_TYPE_CAPWAP) {
65 return false;
66 }
67
68 return true;
69}
70
71/*
72 * nss_capwap_refcnt_inc()
73 * Increments refcnt on the tunnel.
74 */
75static void nss_capwap_refcnt_inc(int32_t if_num)
76{
77 if_num = if_num - NSS_DYNAMIC_IF_START;
78 atomic_inc(&nss_capwap_hdl[if_num]->refcnt);
79 nss_assert(atomic_read(&nss_capwap_hdl[if_num]->refcnt) > 0);
80}
81
82/*
83 * nss_capwap_refcnt_dec()
84 * Decrements refcnt on the tunnel.
85 */
86static void nss_capwap_refcnt_dec(int32_t if_num)
87{
88 if_num = if_num - NSS_DYNAMIC_IF_START;
89 nss_assert(atomic_read(&nss_capwap_hdl[if_num]->refcnt) > 0);
90 atomic_dec(&nss_capwap_hdl[if_num]->refcnt);
91}
92
93/*
94 * nss_capwap_get_refcnt()
95 * Get refcnt on the tunnel.
96 */
97static uint32_t nss_capwap_get_refcnt(int32_t if_num)
98{
99 if_num = if_num - NSS_DYNAMIC_IF_START;
100 return atomic_read(&nss_capwap_hdl[if_num]->refcnt);
101}
102
103/*
104 * nss_capwap_set_msg_callback()
105 * This sets the message callback handler and its associated context
106 */
107static void nss_capwap_set_msg_callback(int32_t if_num, nss_capwap_msg_callback_t cb, void *app_data)
108{
109 struct nss_capwap_handle *h;
110
111 h = nss_capwap_hdl[if_num - NSS_DYNAMIC_IF_START];
112 if (!h) {
113 return;
114 }
115
116 h->app_data = app_data;
117 h->msg_callback = cb;
118}
119
120/*
121 * nss_capwap_get_msg_callback()
122 * This gets the message callback handler and its associated context
123 */
124static nss_capwap_msg_callback_t nss_capwap_get_msg_callback(int32_t if_num, void **app_data)
125{
126 struct nss_capwap_handle *h;
127
128 h = nss_capwap_hdl[if_num - NSS_DYNAMIC_IF_START];
129 if (!h) {
130 *app_data = NULL;
131 return NULL;
132 }
133
134 *app_data = h->app_data;
135 return h->msg_callback;
136}
137
138/*
139 * nss_capwapmgr_update_stats()
140 * Update per-tunnel stats for each CAPWAP interface.
141 */
142static void nss_capwapmgr_update_stats(struct nss_capwap_handle *handle, struct nss_capwap_stats_msg *fstats)
143{
144 struct nss_capwap_tunnel_stats *stats;
145
146 stats = &handle->stats;
147
148 stats->rx_segments += fstats->rx_segments;
149 stats->tx_segments += fstats->tx_segments;
150 stats->dtls_pkts += fstats->dtls_pkts;
151 stats->oversize_drops += fstats->oversize_drops;
152 stats->frag_timeout_drops += fstats->frag_timeout_drops;
153 stats->rx_queue_full_drops += fstats->rx_queue_full_drops;
154 stats->rx_n2h_queue_full_drops += fstats->rx_n2h_queue_full_drops;
155 stats->rx_mem_failure_drops += fstats->rx_mem_failure_drops;
156
157 stats->tx_queue_full_drops += fstats->tx_queue_full_drops;
158 stats->tx_mem_failure_drops += fstats->tx_mem_failure_drops;
159
160 /*
161 * add pnode stats now.
162 */
163 stats->pnode_stats.rx_packets += fstats->pnode_stats.rx_packets;
164 stats->pnode_stats.rx_bytes += fstats->pnode_stats.rx_bytes;
165 stats->pnode_stats.rx_dropped += fstats->pnode_stats.rx_dropped;
166 stats->pnode_stats.tx_packets += fstats->pnode_stats.tx_packets;
167 stats->pnode_stats.tx_bytes += fstats->pnode_stats.tx_bytes;
168}
169
170/*
171 * nss_capwap_handler()
172 * Handle NSS -> HLOS messages for CAPWAP
173 */
174static void nss_capwap_msg_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm, __attribute__((unused))void *app_data)
175{
176 struct nss_capwap_msg *ntm = (struct nss_capwap_msg *)ncm;
177 nss_capwap_msg_callback_t cb;
178
179 /*
180 * Is this a valid request/response packet?
181 */
182 if (ncm->type > NSS_CAPWAP_MSG_TYPE_MAX) {
183 nss_warning("%p: received invalid message %d for CAPWAP interface", nss_ctx, ncm->type);
184 return;
185 }
186
187 if (ncm->len > sizeof(struct nss_capwap_msg)) {
188 nss_warning("%p: Length of message is greater than required: %d", nss_ctx, ncm->interface);
189 return;
190 }
191
192 nss_core_log_msg_failures(nss_ctx, ncm);
193
194 switch (ntm->cm.type) {
195 case NSS_CAPWAP_MSG_TYPE_SYNC_STATS: {
196 uint32_t if_num;
197
198 if_num = ncm->interface - NSS_DYNAMIC_IF_START;
199 if (nss_capwap_hdl[if_num] != NULL) {
200 nss_capwapmgr_update_stats(nss_capwap_hdl[if_num], &ntm->msg.stats);
201 }
202 }
203 }
204
205 /*
206 * Update the callback and app_data for NOTIFY messages.
207 */
208 if (ncm->response == NSS_CMM_RESPONSE_NOTIFY) {
209 void *app_data;
210
211 ncm->cb = (uint32_t)nss_capwap_get_msg_callback(ncm->interface, &app_data);
212 ncm->app_data = (uint32_t)app_data;
213 }
214
215 /*
216 * Do we have a callback
217 */
218 if (!ncm->cb) {
219 nss_trace("%p: cb is null for interface %d", nss_ctx, ncm->interface);
220 return;
221 }
222
223 cb = (nss_capwap_msg_callback_t)ncm->cb;
224 cb((void *)ncm->app_data, ntm);
225}
226
227/*
228 * nss_capwap_instance_alloc()
229 * Allocate CAPWAP tunnel instance
230 */
231static bool nss_capwap_instance_alloc(struct nss_ctx_instance *nss_ctx, uint32_t if_num)
232{
233 struct nss_capwap_handle *h;
234
235 /*
236 * Allocate a handle
237 */
238 h = kmalloc(sizeof(struct nss_capwap_handle), GFP_ATOMIC);
239 if (h == NULL) {
240 nss_warning("%p: no memory for allocating CAPWAP instance for interface : %d", nss_ctx, if_num);
241 return false;
242 }
243 memset(h, 0, sizeof (struct nss_capwap_handle));
244 atomic_set(&h->refcnt, 0);
245 memset(&h->stats, 0, sizeof (struct nss_capwap_tunnel_stats));
246 h->interface_num = if_num;
247
248 spin_lock(&nss_capwap_spinlock);
249 if (nss_capwap_hdl[if_num - NSS_DYNAMIC_IF_START] != NULL) {
250 spin_unlock(&nss_capwap_spinlock);
251 kfree(h);
252 nss_warning("%p: Another thread is already allocated instance for :%d", nss_ctx, if_num);
253 return false;
254 }
255
256 nss_capwap_hdl[if_num - NSS_DYNAMIC_IF_START] = h;
257 spin_unlock(&nss_capwap_spinlock);
258
259 return true;
260}
261
262/*
263 * nss_capwap_tx_msg()
264 * Transmit a CAPWAP message to NSS FW. Don't call this from softirq/interrupts.
265 */
266nss_tx_status_t nss_capwap_tx_msg(struct nss_ctx_instance *nss_ctx, struct nss_capwap_msg *msg)
267{
268 struct nss_capwap_msg *nm;
269 struct nss_cmn_msg *ncm = &msg->cm;
270 struct sk_buff *nbuf;
271 int32_t status;
272 int32_t if_num;
273
274 BUG_ON(in_interrupt());
275 BUG_ON(in_softirq());
276 BUG_ON(in_serving_softirq());
277
278 if (nss_capwap_verify_if_num(msg->cm.interface) == false) {
279 return NSS_TX_FAILURE_BAD_PARAM;
280 }
281
282 if (ncm->type >= NSS_CAPWAP_MSG_TYPE_MAX) {
283 return NSS_TX_FAILURE_BAD_PARAM;
284 }
285
286 if_num = msg->cm.interface - NSS_DYNAMIC_IF_START;
287 spin_lock(&nss_capwap_spinlock);
288 if (!nss_capwap_hdl[if_num]) {
289 spin_unlock(&nss_capwap_spinlock);
290 nss_warning("%p: capwap tunnel if_num is not there: %d", nss_ctx, msg->cm.interface);
291 return NSS_TX_FAILURE_BAD_PARAM;
292 }
293 nss_capwap_refcnt_inc(msg->cm.interface);
294 spin_unlock(&nss_capwap_spinlock);
295
296 if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) {
297 nss_warning("%p: capwap msg dropped as core not ready", nss_ctx);
298 status = NSS_TX_FAILURE_NOT_READY;
299 goto out;
300 }
301
302 if (ncm->len > sizeof(struct nss_capwap_msg)) {
303 nss_warning("%p: message length is invalid: %d", nss_ctx, ncm->len);
304 status = NSS_TX_FAILURE_BAD_PARAM;
305 goto out;
306 }
307
308 nbuf = dev_alloc_skb(NSS_NBUF_PAYLOAD_SIZE);
309 if (unlikely(!nbuf)) {
310 spin_lock_bh(&nss_ctx->nss_top->stats_lock);
311 nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_NBUF_ALLOC_FAILS]++;
312 spin_unlock_bh(&nss_ctx->nss_top->stats_lock);
313 nss_warning("%p: msg dropped as command allocation failed", nss_ctx);
314 status = NSS_TX_FAILURE;
315 goto out;
316 }
317
318 /*
319 * Copy the message to our skb
320 */
321 nm = (struct nss_capwap_msg *)skb_put(nbuf, sizeof(struct nss_capwap_msg));
322 memcpy(nm, msg, sizeof(struct nss_capwap_msg));
323
324 status = nss_core_send_buffer(nss_ctx, 0, nbuf, NSS_IF_CMD_QUEUE, H2N_BUFFER_CTRL, 0);
325 if (status != NSS_CORE_STATUS_SUCCESS) {
326 dev_kfree_skb_any(nbuf);
327 nss_warning("%p: Unable to enqueue 'capwap message' \n", nss_ctx);
328 goto out;
329 }
330
331 nss_hal_send_interrupt(nss_ctx->nmap, nss_ctx->h2n_desc_rings[NSS_IF_CMD_QUEUE].desc_ring.int_bit,
332 NSS_REGS_H2N_INTR_STATUS_DATA_COMMAND_QUEUE);
333
334 NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_TX_CMD_REQ]);
335
336out:
337 nss_capwap_refcnt_dec(msg->cm.interface);
338 return status;
339}
340EXPORT_SYMBOL(nss_capwap_tx_msg);
341
342/*
343 * nss_capwap_tx_data()
344 * Transmit data buffer (skb) to a NSS interface number
345 */
346nss_tx_status_t nss_capwap_tx_data(struct nss_ctx_instance *nss_ctx, struct sk_buff *os_buf, uint32_t if_num)
347{
348 return nss_if_tx_buf(nss_ctx, os_buf, if_num);
349}
350EXPORT_SYMBOL(nss_capwap_tx_data);
351
352/*
353 ***********************************
354 * Register/Unregister/Miscellaneous APIs
355 ***********************************
356 */
357
358/*
359 * nss_capwap_get_stats()
360 * API for getting stats from a CAPWAP tunnel interface stats
361 */
362bool nss_capwap_get_stats(uint32_t if_num, struct nss_capwap_tunnel_stats *stats)
363{
364 if (nss_capwap_verify_if_num(if_num) == false) {
365 return false;
366 }
367
368 if_num = if_num - NSS_DYNAMIC_IF_START;
369 spin_lock(&nss_capwap_spinlock);
370 if (nss_capwap_hdl[if_num] == NULL) {
371 spin_unlock(&nss_capwap_spinlock);
372 return false;
373 }
374
375 memcpy(stats, &nss_capwap_hdl[if_num]->stats, sizeof(struct nss_capwap_tunnel_stats));
376 spin_unlock(&nss_capwap_spinlock);
377 return true;
378}
379EXPORT_SYMBOL(nss_capwap_get_stats);
380
381/*
382 * nss_capwap_notify_register()
383 * Registers a message notifier with NSS FW. It should not be called from
384 * softirq or interrupts.
385 */
386struct nss_ctx_instance *nss_capwap_notify_register(uint32_t if_num, nss_capwap_msg_callback_t cb, void *app_data)
387{
388 struct nss_ctx_instance *nss_ctx;
389
390 nss_ctx = &nss_top_main.nss[nss_top_main.capwap_handler_id];
391
392 if (nss_capwap_verify_if_num(if_num) == false) {
393 nss_warning("%p: notfiy register received for invalid interface %d", nss_ctx, if_num);
394 return NULL;
395 }
396
397 spin_lock(&nss_capwap_spinlock);
398 if (nss_capwap_hdl[if_num - NSS_DYNAMIC_IF_START] != NULL) {
399 spin_unlock(&nss_capwap_spinlock);
400 nss_warning("%p: notfiy register tunnel already exists for interface %d", nss_ctx, if_num);
401 return NULL;
402 }
403 spin_unlock(&nss_capwap_spinlock);
404
405 return nss_ctx;
406}
407EXPORT_SYMBOL(nss_capwap_notify_register);
408
409/*
410 * nss_capwap_notify_unregister()
411 * unregister the CAPWAP notifier for the given interface number (if_num).
412 * It shouldn't be called from softirq or interrupts.
413 */
414nss_tx_status_t nss_capwap_notify_unregister(struct nss_ctx_instance *nss_ctx, uint32_t if_num)
415{
416 struct nss_top_instance *nss_top;
417 int index;
418
419 if (nss_capwap_verify_if_num(if_num) == false) {
420 nss_warning("%p: notify unregister received for invalid interface %d", nss_ctx, if_num);
421 return NSS_TX_FAILURE_BAD_PARAM;
422 }
423
424 nss_top = nss_ctx->nss_top;
425 if (nss_top == NULL) {
426 nss_warning("%p: notify unregister received for invalid nss_top %d", nss_ctx, if_num);
427 return NSS_TX_FAILURE_BAD_PARAM;
428 }
429
430 index = if_num - NSS_DYNAMIC_IF_START;
431 spin_lock(&nss_capwap_spinlock);
432 if (nss_capwap_hdl[index] == NULL) {
433 spin_unlock(&nss_capwap_spinlock);
434 nss_warning("%p: notify unregister received for unallocated if_num: %d", nss_ctx, if_num);
435 return NSS_TX_FAILURE_BAD_PARAM;
436 }
437
438 /*
439 * It's the responsibility of caller to wait and call us again. We return failure saying
440 * that we can't remove msg handler now.
441 */
442 if (nss_capwap_get_refcnt(if_num) != 0) {
443 spin_unlock(&nss_capwap_spinlock);
444 nss_warning("%p: notify unregister tunnel %d: has reference", nss_ctx, if_num);
445 return NSS_TX_FAILURE_QUEUE;
446 }
447
448 nss_capwap_set_msg_callback(if_num, NULL, NULL);
449 spin_unlock(&nss_capwap_spinlock);
450
451 return NSS_TX_SUCCESS;
452}
453EXPORT_SYMBOL(nss_capwap_notify_unregister);
454
455/*
456 * nss_capwap_data_register()
457 * Registers a data packet notifier with NSS FW.
458 */
459struct nss_ctx_instance *nss_capwap_data_register(uint32_t if_num, nss_capwap_buf_callback_t cb, void *app_data)
460{
461 struct nss_ctx_instance *nss_ctx;
462 int core_status;
463
464 nss_ctx = nss_capwap_get_ctx();
465 if (nss_capwap_verify_if_num(if_num) == false) {
466 nss_warning("%p: data register received for invalid interface %d", nss_ctx, if_num);
467 return NULL;
468 }
469
470 spin_lock(&nss_capwap_spinlock);
471 if (nss_ctx->nss_top->if_ctx[if_num] != NULL) {
472 spin_unlock(&nss_capwap_spinlock);
473 return NULL;
474 }
475 spin_unlock(&nss_capwap_spinlock);
476
477 core_status = nss_core_register_handler(if_num, nss_capwap_msg_handler, NULL);
478 if (core_status != NSS_CORE_STATUS_SUCCESS) {
479 nss_warning("%p: nss core register handler failed for if_num:%d with error :%d", nss_ctx, if_num, core_status);
480 return NULL;
481 }
482
483 if (nss_capwap_instance_alloc(nss_ctx, if_num) == false) {
484 nss_warning("%p: couldn't allocate tunnel instance for if_num:%d", nss_ctx, if_num);
485 return NULL;
486 }
487
488 nss_ctx->nss_top->if_ctx[if_num] = app_data;
489 nss_ctx->nss_top->if_rx_callback[if_num] = cb;
490
491 return nss_ctx;
492}
493EXPORT_SYMBOL(nss_capwap_data_register);
494
495/*
496 * nss_capwap_data_unregister()
497 * Unregister a data packet notifier with NSS FW
498 */
499bool nss_capwap_data_unregister(uint32_t if_num)
500{
501 struct nss_ctx_instance *nss_ctx;
502 struct nss_capwap_handle *h;
503
504 nss_ctx = nss_capwap_get_ctx();
505 if (nss_capwap_verify_if_num(if_num) == false) {
506 nss_warning("%p: data unregister received for invalid interface %d", nss_ctx, if_num);
507 return false;
508 }
509
510 spin_lock(&nss_capwap_spinlock);
511 /*
512 * It's the responsibility of caller to wait and call us again.
513 */
514 if (nss_capwap_get_refcnt(if_num) != 0) {
515 spin_unlock(&nss_capwap_spinlock);
516 nss_warning("%p: notify unregister tunnel %d: has reference", nss_ctx, if_num);
517 return false;
518 }
519 h = nss_capwap_hdl[if_num - NSS_DYNAMIC_IF_START];
520 nss_capwap_hdl[if_num - NSS_DYNAMIC_IF_START] = NULL;
521 spin_unlock(&nss_capwap_spinlock);
522
523 (void) nss_core_unregister_handler(if_num);
524
525 nss_ctx->nss_top->if_rx_callback[if_num] = NULL;
526 nss_ctx->nss_top->if_ctx[if_num] = NULL;
527 kfree(h);
528 return true;
529}
530EXPORT_SYMBOL(nss_capwap_data_unregister);
531
532/*
533 * nss_capwap_get_ctx()
534 * Return a CAPWAP NSS context.
535 */
536struct nss_ctx_instance *nss_capwap_get_ctx()
537{
538 struct nss_ctx_instance *nss_ctx;
539
540 nss_ctx = &nss_top_main.nss[nss_top_main.capwap_handler_id];
541 return nss_ctx;
542}
543EXPORT_SYMBOL(nss_capwap_get_ctx);
544
545/*
546 * nss_capwap_init()
547 * Initializes CAPWAP. Gets called from nss_init.c
548 */
549void nss_capwap_init()
550{
551 memset(&nss_capwap_hdl, 0, sizeof(nss_capwap_hdl));
552}
Sundarajan Srinivasan02e6c2b2014-10-06 11:51:12 -0700553
554/*
555 * nss_capwap_msg_init()
556 * Initialize capwap message.
557 */
558void nss_capwap_msg_init(struct nss_capwap_msg *ncm, uint16_t if_num, uint32_t type, uint32_t len,
559 nss_capwap_msg_callback_t *cb, void *app_data)
560{
561 nss_cmn_msg_init(&ncm->cm, if_num, type, len, (void*)cb, app_data);
562}
563EXPORT_SYMBOL(nss_capwap_msg_init);