blob: a37ee922b3855d76624703d5355f0ed07f0c7df7 [file] [log] [blame]
ratheesh kannoth7af985d2015-06-24 15:08:40 +05301/*
2 **************************************************************************
3 * Copyright (c) 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#include <linux/l2tp.h>
18#include <net/sock.h>
19#include "nss_tx_rx_common.h"
20
21/*
22 * Data structures to store l2tpv2 nss debug stats
23 */
24static DEFINE_SPINLOCK(nss_l2tpv2_session_debug_stats_lock);
25static struct nss_stats_l2tpv2_session_debug nss_l2tpv2_session_debug_stats[NSS_MAX_L2TPV2_DYNAMIC_INTERFACES];
26
27/*
28 * nss_l2tpv2_session_debug_stats_sync
29 * Per session debug stats for l2tpv2
30 */
31void nss_l2tpv2_session_debug_stats_sync(struct nss_ctx_instance *nss_ctx, struct nss_l2tpv2_sync_session_stats_msg *stats_msg, uint16_t if_num)
32{
33 int i;
34 spin_lock_bh(&nss_l2tpv2_session_debug_stats_lock);
35 for (i = 0; i < NSS_MAX_L2TPV2_DYNAMIC_INTERFACES; i++) {
36 if (nss_l2tpv2_session_debug_stats[i].if_num == if_num) {
37 nss_l2tpv2_session_debug_stats[i].stats[NSS_STATS_L2TPV2_SESSION_RX_PPP_LCP_PKTS] += stats_msg->debug_stats.rx_ppp_lcp_pkts;
38 nss_l2tpv2_session_debug_stats[i].stats[NSS_STATS_L2TPV2_SESSION_RX_EXP_DATA_PKTS] += stats_msg->debug_stats.rx_exception_data_pkts;
39 nss_l2tpv2_session_debug_stats[i].stats[NSS_STATS_L2TPV2_SESSION_RX_EXP_DATA_PKTS] += stats_msg->debug_stats.rx_exception_data_pkts;
40 nss_l2tpv2_session_debug_stats[i].stats[NSS_STATS_L2TPV2_SESSION_ENCAP_PBUF_ALLOC_FAIL_PKTS] += stats_msg->debug_stats.encap_pbuf_alloc_fail;
41 nss_l2tpv2_session_debug_stats[i].stats[NSS_STATS_L2TPV2_SESSION_DECAP_PBUF_ALLOC_FAIL_PKTS] += stats_msg->debug_stats.decap_pbuf_alloc_fail;
42 break;
43 }
44 }
45 spin_unlock_bh(&nss_l2tpv2_session_debug_stats_lock);
46}
47
48/*
49 * nss_l2tpv2_global_session_stats_get()
50 * Get session l2tpv2 statitics.
51 */
52void nss_l2tpv2_session_debug_stats_get(void *stats_mem)
53{
54 struct nss_stats_l2tpv2_session_debug *stats = (struct nss_stats_l2tpv2_session_debug *)stats_mem;
55 int i;
56
57 if (!stats) {
58 nss_warning("No memory to copy l2tpv2 session stats");
59 return;
60 }
61
62 spin_lock_bh(&nss_l2tpv2_session_debug_stats_lock);
63 for (i = 0; i < NSS_MAX_L2TPV2_DYNAMIC_INTERFACES; i++) {
64 if (nss_l2tpv2_session_debug_stats[i].valid) {
65 memcpy(stats, &nss_l2tpv2_session_debug_stats[i], sizeof(struct nss_stats_l2tpv2_session_debug));
66 stats++;
67 }
68 }
69 spin_unlock_bh(&nss_l2tpv2_session_debug_stats_lock);
70}
71
72/*
73 * nss_l2tpv2_handler()
74 * Handle NSS -> HLOS messages for l2tpv2 tunnel
75 */
76
77static void nss_l2tpv2_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm, __attribute__((unused))void *app_data)
78{
79 struct nss_l2tpv2_msg *ntm = (struct nss_l2tpv2_msg *)ncm;
80 void *ctx;
81
82 nss_l2tpv2_msg_callback_t cb;
83
84 BUG_ON(!(nss_is_dynamic_interface(ncm->interface) || ncm->interface == NSS_L2TPV2_INTERFACE));
85
86 /*
87 * Is this a valid request/response packet?
88 */
89 if (ncm->type >= NSS_L2TPV2_MSG_MAX) {
90 nss_warning("%p: received invalid message %d for L2TP interface", nss_ctx, ncm->type);
91 return;
92 }
93
94 if (ncm->len > sizeof(struct nss_l2tpv2_msg)) {
95 nss_warning("%p: tx request for another interface: %d", nss_ctx, ncm->interface);
96 return;
97 }
98
99 switch (ntm->cm.type) {
100
101 case NSS_L2TPV2_MSG_SYNC_STATS:
102 /*
103 * session debug stats embeded in session stats msg
104 */
105 nss_l2tpv2_session_debug_stats_sync(nss_ctx, &ntm->msg.stats, ncm->interface);
106 break;
107 }
108
109 /*
110 * Update the callback and app_data for NOTIFY messages, l2tpv2 sends all notify messages
111 * to the same callback/app_data.
112 */
113 if (ncm->response == NSS_CMM_RESPONSE_NOTIFY) {
114 ncm->cb = (uint32_t)nss_ctx->nss_top->l2tpv2_msg_callback;
115 }
116
117 /*
118 * Log failures
119 */
120 nss_core_log_msg_failures(nss_ctx, ncm);
121
122 /*
123 * Do we have a call back
124 */
125 if (!ncm->cb) {
126 return;
127 }
128
129 /*
130 * callback
131 */
132 cb = (nss_l2tpv2_msg_callback_t)ncm->cb;
133 ctx = nss_ctx->nss_top->subsys_dp_register[ncm->interface].ndev;
134
135 /*
136 * call l2tpv2 tunnel callback
137 */
138 if (!ctx) {
139 nss_warning("%p: Event received for l2tpv2 tunnel interface %d before registration", nss_ctx, ncm->interface);
140 return;
141 }
142
143 cb(ctx, ntm);
144}
145
146/*
147 * nss_l2tpv2_tx()
148 * Transmit a l2tpv2 message to NSS firmware
149 */
150nss_tx_status_t nss_l2tpv2_tx(struct nss_ctx_instance *nss_ctx, struct nss_l2tpv2_msg *msg)
151{
152 struct nss_l2tpv2_msg *nm;
153 struct nss_cmn_msg *ncm = &msg->cm;
154 struct sk_buff *nbuf;
155 int32_t status;
156
157 NSS_VERIFY_CTX_MAGIC(nss_ctx);
158 if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) {
159 nss_warning("%p: l2tpv2 msg dropped as core not ready", nss_ctx);
160 return NSS_TX_FAILURE_NOT_READY;
161 }
162
163 /*
164 * Sanity check the message
165 */
166 if (!nss_is_dynamic_interface(ncm->interface)) {
167 nss_warning("%p: tx request for non dynamic interface: %d", nss_ctx, ncm->interface);
168 return NSS_TX_FAILURE;
169 }
170
171 if (ncm->type > NSS_L2TPV2_MSG_MAX) {
172 nss_warning("%p: message type out of range: %d", nss_ctx, ncm->type);
173 return NSS_TX_FAILURE;
174 }
175
176 if (ncm->len > sizeof(struct nss_l2tpv2_msg)) {
177 nss_warning("%p: message length is invalid: %d", nss_ctx, ncm->len);
178 return NSS_TX_FAILURE;
179 }
180
181 nbuf = dev_alloc_skb(NSS_NBUF_PAYLOAD_SIZE);
182 if (unlikely(!nbuf)) {
183 NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_NBUF_ALLOC_FAILS]);
184 nss_warning("%p: msg dropped as command allocation failed", nss_ctx);
185 return NSS_TX_FAILURE;
186 }
187
188 /*
189 * Copy the message to our skb
190 */
191 nm = (struct nss_l2tpv2_msg *)skb_put(nbuf, sizeof(struct nss_l2tpv2_msg));
192 memcpy(nm, msg, sizeof(struct nss_l2tpv2_msg));
193
194 status = nss_core_send_buffer(nss_ctx, 0, nbuf, NSS_IF_CMD_QUEUE, H2N_BUFFER_CTRL, 0);
195 if (status != NSS_CORE_STATUS_SUCCESS) {
196 dev_kfree_skb_any(nbuf);
197 nss_warning("%p: Unable to enqueue 'l2tpv2 message'\n", nss_ctx);
198 if (status == NSS_CORE_STATUS_FAILURE_QUEUE) {
199 return NSS_TX_FAILURE_QUEUE;
200 }
201 return NSS_TX_FAILURE;
202 }
203
204 nss_hal_send_interrupt(nss_ctx->nmap, nss_ctx->h2n_desc_rings[NSS_IF_CMD_QUEUE].desc_ring.int_bit,
205 NSS_REGS_H2N_INTR_STATUS_DATA_COMMAND_QUEUE);
206
207 NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_TX_CMD_REQ]);
208 return NSS_TX_SUCCESS;
209}
210
211/*
212 ***********************************
213 * Register/Unregister/Miscellaneous APIs
214 ***********************************
215 */
216
217/*
218 * nss_register_l2tpv2_if()
219 */
220struct nss_ctx_instance *nss_register_l2tpv2_if(uint32_t if_num, nss_l2tpv2_callback_t l2tpv2_callback,
221 nss_l2tpv2_msg_callback_t event_callback, struct net_device *netdev, uint32_t features)
222{
223
224 int i = 0;
225 nss_assert(nss_is_dynamic_interface(if_num));
226
227 nss_top_main.subsys_dp_register[if_num].ndev = netdev;
228 nss_top_main.subsys_dp_register[if_num].cb = l2tpv2_callback;
229 nss_top_main.subsys_dp_register[if_num].app_data = NULL;
230 nss_top_main.subsys_dp_register[if_num].features = features;
231
232 nss_top_main.l2tpv2_msg_callback = event_callback;
233
234 nss_core_register_handler(if_num, nss_l2tpv2_handler, NULL);
235
236 spin_lock_bh(&nss_l2tpv2_session_debug_stats_lock);
237 for (i = 0; i < NSS_MAX_L2TPV2_DYNAMIC_INTERFACES; i++) {
238 if (!nss_l2tpv2_session_debug_stats[i].valid) {
239 nss_l2tpv2_session_debug_stats[i].valid = true;
240 nss_l2tpv2_session_debug_stats[i].if_num = if_num;
241 nss_l2tpv2_session_debug_stats[i].if_index = netdev->ifindex;
242 break;
243 }
244 }
245 spin_unlock_bh(&nss_l2tpv2_session_debug_stats_lock);
246
247 return (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.l2tpv2_handler_id];
248}
249
250/*
251 * nss_unregister_l2tpv2_if()
252 */
253void nss_unregister_l2tpv2_if(uint32_t if_num)
254{
255 int i;
256 nss_assert(nss_is_dynamic_interface(if_num));
257
258 nss_top_main.subsys_dp_register[if_num].ndev = NULL;
259 nss_top_main.subsys_dp_register[if_num].cb = NULL;
260 nss_top_main.subsys_dp_register[if_num].app_data = NULL;
261 nss_top_main.subsys_dp_register[if_num].features = 0;
262
263 nss_top_main.l2tpv2_msg_callback = NULL;
264
265 nss_core_unregister_handler(if_num);
266
267 spin_lock_bh(&nss_l2tpv2_session_debug_stats_lock);
268 for (i = 0; i < NSS_MAX_L2TPV2_DYNAMIC_INTERFACES; i++) {
269 if (nss_l2tpv2_session_debug_stats[i].if_num == if_num) {
270 memset(&nss_l2tpv2_session_debug_stats[i], 0, sizeof(struct nss_stats_l2tpv2_session_debug));
271 break;
272 }
273 }
274 spin_unlock_bh(&nss_l2tpv2_session_debug_stats_lock);
275}
276
277/*
278 * nss_get_l2tpv2_context()
279 */
280struct nss_ctx_instance *nss_l2tpv2_get_context()
281{
282 return (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.l2tpv2_handler_id];
283}
284
285/*
286 * nss_l2tpv2_msg_init()
287 * Initialize nss_l2tpv2 msg.
288 */
289void nss_l2tpv2_msg_init(struct nss_l2tpv2_msg *ncm, uint16_t if_num, uint32_t type, uint32_t len, void *cb, void *app_data)
290{
291 nss_cmn_msg_init(&ncm->cm, if_num, type, len, cb, app_data);
292}
293
294/* nss_l2tpv2_register_handler()
295 * debugfs stats msg handler received on static l2tpv2 interface
296 */
297void nss_l2tpv2_register_handler(void)
298{
299 nss_info("nss_l2tpv2_register_handler");
300 nss_core_register_handler(NSS_L2TPV2_INTERFACE, nss_l2tpv2_handler, NULL);
301}
302
303EXPORT_SYMBOL(nss_l2tpv2_get_context);
304EXPORT_SYMBOL(nss_l2tpv2_tx);
305EXPORT_SYMBOL(nss_unregister_l2tpv2_if);
306EXPORT_SYMBOL(nss_l2tpv2_msg_init);
307EXPORT_SYMBOL(nss_register_l2tpv2_if);