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