1/* 2 ************************************************************************** 3 * Copyright (c) 2014,2015, 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 if_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_refcnt() 95 * Get refcnt on the tunnel. 96 */ 97static uint32_t nss_capwap_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->dtls_pkts += fstats->dtls_pkts; 150 151 stats->rx_dup_frag += fstats->rx_dup_frag; 152 stats->rx_oversize_drops += fstats->rx_oversize_drops; 153 stats->rx_frag_timeout_drops += fstats->rx_frag_timeout_drops; 154 stats->rx_queue_full_drops += fstats->rx_queue_full_drops; 155 stats->rx_n2h_queue_full_drops += fstats->rx_n2h_queue_full_drops; 156 stats->rx_mem_failure_drops += fstats->rx_mem_failure_drops; 157 stats->rx_csum_drops += fstats->rx_csum_drops; 158 stats->rx_malformed += fstats->rx_malformed; 159 stats->rx_frag_gap_drops += fstats->rx_frag_gap_drops; 160 161 stats->tx_segments += fstats->tx_segments; 162 stats->tx_queue_full_drops += fstats->tx_queue_full_drops; 163 stats->tx_mem_failure_drops += fstats->tx_mem_failure_drops; 164 stats->tx_dropped_sg_ref += fstats->tx_dropped_sg_ref; 165 stats->tx_dropped_ver_mis += fstats->tx_dropped_ver_mis; 166 stats->tx_dropped_hroom += fstats->tx_dropped_hroom; 167 stats->tx_dropped_dtls += fstats->tx_dropped_dtls; 168 stats->tx_dropped_nwireless += fstats->tx_dropped_nwireless; 169 stats->tx_dropped_unalign += fstats->tx_dropped_unalign; 170 171 /* 172 * add pnode stats now. 173 */ 174 stats->pnode_stats.rx_packets += fstats->pnode_stats.rx_packets; 175 stats->pnode_stats.rx_bytes += fstats->pnode_stats.rx_bytes; 176 stats->pnode_stats.rx_dropped += fstats->pnode_stats.rx_dropped; 177 stats->pnode_stats.tx_packets += fstats->pnode_stats.tx_packets; 178 stats->pnode_stats.tx_bytes += fstats->pnode_stats.tx_bytes; 179} 180 181/* 182 * nss_capwap_handler() 183 * Handle NSS -> HLOS messages for CAPWAP 184 */ 185static void nss_capwap_msg_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm, __attribute__((unused))void *app_data) 186{ 187 struct nss_capwap_msg *ntm = (struct nss_capwap_msg *)ncm; 188 nss_capwap_msg_callback_t cb; 189 190 /* 191 * Is this a valid request/response packet? 192 */ 193 if (ncm->type > NSS_CAPWAP_MSG_TYPE_MAX) { 194 nss_warning("%p: received invalid message %d for CAPWAP interface", nss_ctx, ncm->type); 195 return; 196 } 197 198 if (ncm->len > sizeof(struct nss_capwap_msg)) { 199 nss_warning("%p: Length of message is greater than required: %d", nss_ctx, ncm->interface); 200 return; 201 } 202 203 nss_core_log_msg_failures(nss_ctx, ncm); 204 205 switch (ntm->cm.type) { 206 case NSS_CAPWAP_MSG_TYPE_SYNC_STATS: { 207 uint32_t if_num; 208 209 if_num = ncm->interface - NSS_DYNAMIC_IF_START; 210 if (nss_capwap_hdl[if_num] != NULL) { 211 nss_capwapmgr_update_stats(nss_capwap_hdl[if_num], &ntm->msg.stats); 212 } 213 } 214 } 215 216 /* 217 * Update the callback and app_data for NOTIFY messages. 218 */ 219 if (ncm->response == NSS_CMM_RESPONSE_NOTIFY) { 220 ncm->cb = (uint32_t)nss_capwap_get_msg_callback(ncm->interface, (void **)&ncm->app_data); 221 } 222 223 /* 224 * Do we have a callback 225 */ 226 if (!ncm->cb) { 227 nss_trace("%p: cb is null for interface %d", nss_ctx, ncm->interface); 228 return; 229 } 230 231 cb = (nss_capwap_msg_callback_t)ncm->cb; 232 cb((void *)ncm->app_data, ntm); 233} 234 235/* 236 * nss_capwap_instance_alloc() 237 * Allocate CAPWAP tunnel instance 238 */ 239static bool nss_capwap_instance_alloc(struct nss_ctx_instance *nss_ctx, uint32_t if_num) 240{ 241 struct nss_capwap_handle *h; 242 243 /* 244 * Allocate a handle 245 */ 246 h = kmalloc(sizeof(struct nss_capwap_handle), GFP_ATOMIC); 247 if (h == NULL) { 248 nss_warning("%p: no memory for allocating CAPWAP instance for interface : %d", nss_ctx, if_num); 249 return false; 250 } 251 252 memset(h, 0, sizeof(struct nss_capwap_handle)); 253 h->if_num = if_num; 254 255 spin_lock(&nss_capwap_spinlock); 256 if (nss_capwap_hdl[if_num - NSS_DYNAMIC_IF_START] != NULL) { 257 spin_unlock(&nss_capwap_spinlock); 258 kfree(h); 259 nss_warning("%p: Another thread is already allocated instance for :%d", nss_ctx, if_num); 260 return false; 261 } 262 263 nss_capwap_hdl[if_num - NSS_DYNAMIC_IF_START] = h; 264 spin_unlock(&nss_capwap_spinlock); 265 266 return true; 267} 268 269/* 270 * nss_capwap_tx_msg() 271 * Transmit a CAPWAP message to NSS FW. Don't call this from softirq/interrupts. 272 */ 273nss_tx_status_t nss_capwap_tx_msg(struct nss_ctx_instance *nss_ctx, struct nss_capwap_msg *msg) 274{ 275 struct nss_capwap_msg *nm; 276 struct nss_cmn_msg *ncm = &msg->cm; 277 struct sk_buff *nbuf; 278 int32_t status; 279 int32_t if_num; 280 281 BUG_ON(in_interrupt()); 282 BUG_ON(in_softirq()); 283 BUG_ON(in_serving_softirq()); 284 285 if (nss_capwap_verify_if_num(msg->cm.interface) == false) { 286 return NSS_TX_FAILURE_BAD_PARAM; 287 } 288 289 if (ncm->type >= NSS_CAPWAP_MSG_TYPE_MAX) { 290 return NSS_TX_FAILURE_BAD_PARAM; 291 } 292 293 if_num = msg->cm.interface - NSS_DYNAMIC_IF_START; 294 spin_lock(&nss_capwap_spinlock); 295 if (!nss_capwap_hdl[if_num]) { 296 spin_unlock(&nss_capwap_spinlock); 297 nss_warning("%p: capwap tunnel if_num is not there: %d", nss_ctx, msg->cm.interface); 298 return NSS_TX_FAILURE_BAD_PARAM; 299 } 300 nss_capwap_refcnt_inc(msg->cm.interface); 301 spin_unlock(&nss_capwap_spinlock); 302 303 if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) { 304 nss_warning("%p: capwap msg dropped as core not ready", nss_ctx); 305 status = NSS_TX_FAILURE_NOT_READY; 306 goto out; 307 } 308 309 if (ncm->len > sizeof(struct nss_capwap_msg)) { 310 nss_warning("%p: message length is invalid: %d", nss_ctx, ncm->len); 311 status = NSS_TX_FAILURE_BAD_PARAM; 312 goto out; 313 } 314 315 nbuf = dev_alloc_skb(NSS_NBUF_PAYLOAD_SIZE); 316 if (unlikely(!nbuf)) { 317 NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_NBUF_ALLOC_FAILS]); 318 nss_warning("%p: msg dropped as command allocation failed", nss_ctx); 319 status = NSS_TX_FAILURE; 320 goto out; 321 } 322 323 /* 324 * Copy the message to our skb 325 */ 326 nm = (struct nss_capwap_msg *)skb_put(nbuf, sizeof(struct nss_capwap_msg)); 327 memcpy(nm, msg, sizeof(struct nss_capwap_msg)); 328 329 status = nss_core_send_buffer(nss_ctx, 0, nbuf, NSS_IF_CMD_QUEUE, H2N_BUFFER_CTRL, 0); 330 if (status != NSS_CORE_STATUS_SUCCESS) { 331 dev_kfree_skb_any(nbuf); 332 nss_warning("%p: Unable to enqueue 'capwap message' \n", nss_ctx); 333 goto out; 334 } 335 336 nss_hal_send_interrupt(nss_ctx->nmap, nss_ctx->h2n_desc_rings[NSS_IF_CMD_QUEUE].desc_ring.int_bit, 337 NSS_REGS_H2N_INTR_STATUS_DATA_COMMAND_QUEUE); 338 339 NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_TX_CMD_REQ]); 340 341out: 342 nss_capwap_refcnt_dec(msg->cm.interface); 343 return status; 344} 345EXPORT_SYMBOL(nss_capwap_tx_msg); 346 347/* 348 * nss_capwap_tx_data() 349 * Transmit data buffer (skb) to a NSS interface number 350 */ 351nss_tx_status_t nss_capwap_tx_data(struct nss_ctx_instance *nss_ctx, struct sk_buff *os_buf, uint32_t if_num) 352{ 353 return nss_if_tx_buf(nss_ctx, os_buf, if_num); 354} 355EXPORT_SYMBOL(nss_capwap_tx_data); 356 357/* 358 *********************************** 359 * Register/Unregister/Miscellaneous APIs 360 *********************************** 361 */ 362 363/* 364 * nss_capwap_get_stats() 365 * API for getting stats from a CAPWAP tunnel interface stats 366 */ 367bool nss_capwap_get_stats(uint32_t if_num, struct nss_capwap_tunnel_stats *stats) 368{ 369 if (nss_capwap_verify_if_num(if_num) == false) { 370 return false; 371 } 372 373 if_num = if_num - NSS_DYNAMIC_IF_START; 374 spin_lock(&nss_capwap_spinlock); 375 if (nss_capwap_hdl[if_num] == NULL) { 376 spin_unlock(&nss_capwap_spinlock); 377 return false; 378 } 379 380 memcpy(stats, &nss_capwap_hdl[if_num]->stats, sizeof(struct nss_capwap_tunnel_stats)); 381 spin_unlock(&nss_capwap_spinlock); 382 return true; 383} 384EXPORT_SYMBOL(nss_capwap_get_stats); 385 386/* 387 * nss_capwap_notify_register() 388 * Registers a message notifier with NSS FW. It should not be called from 389 * softirq or interrupts. 390 */ 391struct nss_ctx_instance *nss_capwap_notify_register(uint32_t if_num, nss_capwap_msg_callback_t cb, void *app_data) 392{ 393 struct nss_ctx_instance *nss_ctx; 394 395 nss_ctx = &nss_top_main.nss[nss_top_main.capwap_handler_id]; 396 397 if (nss_capwap_verify_if_num(if_num) == false) { 398 nss_warning("%p: notfiy register received for invalid interface %d", nss_ctx, if_num); 399 return NULL; 400 } 401 402 spin_lock(&nss_capwap_spinlock); 403 if (nss_capwap_hdl[if_num - NSS_DYNAMIC_IF_START] != NULL) { 404 spin_unlock(&nss_capwap_spinlock); 405 nss_warning("%p: notfiy register tunnel already exists for interface %d", nss_ctx, if_num); 406 return NULL; 407 } 408 spin_unlock(&nss_capwap_spinlock); 409 410 return nss_ctx; 411} 412EXPORT_SYMBOL(nss_capwap_notify_register); 413 414/* 415 * nss_capwap_notify_unregister() 416 * unregister the CAPWAP notifier for the given interface number (if_num). 417 * It shouldn't be called from softirq or interrupts. 418 */ 419nss_tx_status_t nss_capwap_notify_unregister(struct nss_ctx_instance *nss_ctx, uint32_t if_num) 420{ 421 struct nss_top_instance *nss_top; 422 int index; 423 424 if (nss_capwap_verify_if_num(if_num) == false) { 425 nss_warning("%p: notify unregister received for invalid interface %d", nss_ctx, if_num); 426 return NSS_TX_FAILURE_BAD_PARAM; 427 } 428 429 nss_top = nss_ctx->nss_top; 430 if (nss_top == NULL) { 431 nss_warning("%p: notify unregister received for invalid nss_top %d", nss_ctx, if_num); 432 return NSS_TX_FAILURE_BAD_PARAM; 433 } 434 435 index = if_num - NSS_DYNAMIC_IF_START; 436 spin_lock(&nss_capwap_spinlock); 437 if (nss_capwap_hdl[index] == NULL) { 438 spin_unlock(&nss_capwap_spinlock); 439 nss_warning("%p: notify unregister received for unallocated if_num: %d", nss_ctx, if_num); 440 return NSS_TX_FAILURE_BAD_PARAM; 441 } 442 443 /* 444 * It's the responsibility of caller to wait and call us again. We return failure saying 445 * that we can't remove msg handler now. 446 */ 447 if (nss_capwap_refcnt(if_num) != 0) { 448 spin_unlock(&nss_capwap_spinlock); 449 nss_warning("%p: notify unregister tunnel %d: has reference", nss_ctx, if_num); 450 return NSS_TX_FAILURE_QUEUE; 451 } 452 453 nss_capwap_set_msg_callback(if_num, NULL, NULL); 454 spin_unlock(&nss_capwap_spinlock); 455 456 return NSS_TX_SUCCESS; 457} 458EXPORT_SYMBOL(nss_capwap_notify_unregister); 459 460/* 461 * nss_capwap_data_register() 462 * Registers a data packet notifier with NSS FW. 463 */ 464struct nss_ctx_instance *nss_capwap_data_register(uint32_t if_num, nss_capwap_buf_callback_t cb, struct net_device *netdev, uint32_t features) 465{ 466 struct nss_ctx_instance *nss_ctx; 467 int core_status; 468 469 nss_ctx = nss_capwap_get_ctx(); 470 if (nss_capwap_verify_if_num(if_num) == false) { 471 nss_warning("%p: data register received for invalid interface %d", nss_ctx, if_num); 472 return NULL; 473 } 474 475 spin_lock(&nss_capwap_spinlock); 476 if (nss_ctx->nss_top->subsys_dp_register[if_num].ndev != NULL) { 477 spin_unlock(&nss_capwap_spinlock); 478 return NULL; 479 } 480 spin_unlock(&nss_capwap_spinlock); 481 482 core_status = nss_core_register_handler(if_num, nss_capwap_msg_handler, NULL); 483 if (core_status != NSS_CORE_STATUS_SUCCESS) { 484 nss_warning("%p: nss core register handler failed for if_num:%d with error :%d", nss_ctx, if_num, core_status); 485 return NULL; 486 } 487 488 if (nss_capwap_instance_alloc(nss_ctx, if_num) == false) { 489 nss_warning("%p: couldn't allocate tunnel instance for if_num:%d", nss_ctx, if_num); 490 return NULL; 491 } 492 493 nss_ctx->nss_top->subsys_dp_register[if_num].cb = cb; 494 nss_ctx->nss_top->subsys_dp_register[if_num].app_data = NULL; 495 nss_ctx->nss_top->subsys_dp_register[if_num].ndev = netdev; 496 nss_ctx->nss_top->subsys_dp_register[if_num].features = features; 497 498 return nss_ctx; 499} 500EXPORT_SYMBOL(nss_capwap_data_register); 501 502/* 503 * nss_capwap_data_unregister() 504 * Unregister a data packet notifier with NSS FW 505 */ 506bool nss_capwap_data_unregister(uint32_t if_num) 507{ 508 struct nss_ctx_instance *nss_ctx; 509 struct nss_capwap_handle *h; 510 511 nss_ctx = nss_capwap_get_ctx(); 512 if (nss_capwap_verify_if_num(if_num) == false) { 513 nss_warning("%p: data unregister received for invalid interface %d", nss_ctx, if_num); 514 return false; 515 } 516 517 spin_lock(&nss_capwap_spinlock); 518 /* 519 * It's the responsibility of caller to wait and call us again. 520 */ 521 if (nss_capwap_refcnt(if_num) != 0) { 522 spin_unlock(&nss_capwap_spinlock); 523 nss_warning("%p: notify unregister tunnel %d: has reference", nss_ctx, if_num); 524 return false; 525 } 526 h = nss_capwap_hdl[if_num - NSS_DYNAMIC_IF_START]; 527 nss_capwap_hdl[if_num - NSS_DYNAMIC_IF_START] = NULL; 528 spin_unlock(&nss_capwap_spinlock); 529 530 (void) nss_core_unregister_handler(if_num); 531 532 nss_ctx->nss_top->subsys_dp_register[if_num].cb = NULL; 533 nss_ctx->nss_top->subsys_dp_register[if_num].app_data = NULL; 534 nss_ctx->nss_top->subsys_dp_register[if_num].ndev = NULL; 535 nss_ctx->nss_top->subsys_dp_register[if_num].features = 0; 536 537 kfree(h); 538 return true; 539} 540EXPORT_SYMBOL(nss_capwap_data_unregister); 541 542/* 543 * nss_capwap_get_ctx() 544 * Return a CAPWAP NSS context. 545 */ 546struct nss_ctx_instance *nss_capwap_get_ctx() 547{ 548 struct nss_ctx_instance *nss_ctx; 549 550 nss_ctx = &nss_top_main.nss[nss_top_main.capwap_handler_id]; 551 return nss_ctx; 552} 553EXPORT_SYMBOL(nss_capwap_get_ctx); 554 555/* 556 * nss_capwap_get_max_buf_size() 557 * Return a CAPWAP NSS max_buf_size. 558 */ 559uint32_t nss_capwap_get_max_buf_size(struct nss_ctx_instance *nss_ctx) 560{ 561 return nss_core_get_max_buf_size(nss_ctx); 562} 563EXPORT_SYMBOL(nss_capwap_get_max_buf_size); 564 565/* 566 * nss_capwap_init() 567 * Initializes CAPWAP. Gets called from nss_init.c 568 */ 569void nss_capwap_init() 570{ 571 memset(&nss_capwap_hdl, 0, sizeof(nss_capwap_hdl)); 572} 573 574/* 575 * nss_capwap_msg_init() 576 * Initialize capwap message. 577 */ 578void nss_capwap_msg_init(struct nss_capwap_msg *ncm, uint16_t if_num, uint32_t type, uint32_t len, 579 nss_capwap_msg_callback_t cb, void *app_data) 580{ 581 nss_cmn_msg_init(&ncm->cm, if_num, type, len, (void*)cb, app_data); 582} 583EXPORT_SYMBOL(nss_capwap_msg_init); 584