blob: 0b37252e4032632caf264579665c8b7c6395efe7 [file] [log] [blame]
ratheesh kannoth7af985d2015-06-24 15:08:40 +05301/*
2 **************************************************************************
Stephen Wangaed46332016-12-12 17:29:03 -08003 * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
ratheesh kannoth7af985d2015-06-24 15:08:40 +05304 * 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;
ratheesh kannoth7af985d2015-06-24 15:08:40 +053039 nss_l2tpv2_session_debug_stats[i].stats[NSS_STATS_L2TPV2_SESSION_ENCAP_PBUF_ALLOC_FAIL_PKTS] += stats_msg->debug_stats.encap_pbuf_alloc_fail;
40 nss_l2tpv2_session_debug_stats[i].stats[NSS_STATS_L2TPV2_SESSION_DECAP_PBUF_ALLOC_FAIL_PKTS] += stats_msg->debug_stats.decap_pbuf_alloc_fail;
41 break;
42 }
43 }
44 spin_unlock_bh(&nss_l2tpv2_session_debug_stats_lock);
45}
46
47/*
48 * nss_l2tpv2_global_session_stats_get()
49 * Get session l2tpv2 statitics.
50 */
51void nss_l2tpv2_session_debug_stats_get(void *stats_mem)
52{
53 struct nss_stats_l2tpv2_session_debug *stats = (struct nss_stats_l2tpv2_session_debug *)stats_mem;
54 int i;
55
56 if (!stats) {
57 nss_warning("No memory to copy l2tpv2 session stats");
58 return;
59 }
60
61 spin_lock_bh(&nss_l2tpv2_session_debug_stats_lock);
62 for (i = 0; i < NSS_MAX_L2TPV2_DYNAMIC_INTERFACES; i++) {
63 if (nss_l2tpv2_session_debug_stats[i].valid) {
64 memcpy(stats, &nss_l2tpv2_session_debug_stats[i], sizeof(struct nss_stats_l2tpv2_session_debug));
65 stats++;
66 }
67 }
68 spin_unlock_bh(&nss_l2tpv2_session_debug_stats_lock);
69}
70
71/*
72 * nss_l2tpv2_handler()
73 * Handle NSS -> HLOS messages for l2tpv2 tunnel
74 */
75
76static void nss_l2tpv2_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm, __attribute__((unused))void *app_data)
77{
78 struct nss_l2tpv2_msg *ntm = (struct nss_l2tpv2_msg *)ncm;
79 void *ctx;
80
81 nss_l2tpv2_msg_callback_t cb;
82
83 BUG_ON(!(nss_is_dynamic_interface(ncm->interface) || ncm->interface == NSS_L2TPV2_INTERFACE));
84
85 /*
86 * Is this a valid request/response packet?
87 */
88 if (ncm->type >= NSS_L2TPV2_MSG_MAX) {
89 nss_warning("%p: received invalid message %d for L2TP interface", nss_ctx, ncm->type);
90 return;
91 }
92
Suruchi Agarwalef8a8702016-01-08 12:40:08 -080093 if (nss_cmn_get_msg_len(ncm) > sizeof(struct nss_l2tpv2_msg)) {
94 nss_warning("%p: message length is invalid: %d", nss_ctx, nss_cmn_get_msg_len(ncm));
ratheesh kannoth7af985d2015-06-24 15:08:40 +053095 return;
96 }
97
98 switch (ntm->cm.type) {
99
100 case NSS_L2TPV2_MSG_SYNC_STATS:
101 /*
102 * session debug stats embeded in session stats msg
103 */
104 nss_l2tpv2_session_debug_stats_sync(nss_ctx, &ntm->msg.stats, ncm->interface);
105 break;
106 }
107
108 /*
109 * Update the callback and app_data for NOTIFY messages, l2tpv2 sends all notify messages
110 * to the same callback/app_data.
111 */
112 if (ncm->response == NSS_CMM_RESPONSE_NOTIFY) {
Stephen Wangaed46332016-12-12 17:29:03 -0800113 ncm->cb = (nss_ptr_t)nss_ctx->nss_top->l2tpv2_msg_callback;
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530114 }
115
116 /*
117 * Log failures
118 */
119 nss_core_log_msg_failures(nss_ctx, ncm);
120
121 /*
122 * Do we have a call back
123 */
124 if (!ncm->cb) {
125 return;
126 }
127
128 /*
129 * callback
130 */
131 cb = (nss_l2tpv2_msg_callback_t)ncm->cb;
Stephen Wang84e0e992016-09-07 12:31:40 -0700132 ctx = nss_ctx->subsys_dp_register[ncm->interface].ndev;
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530133
134 /*
135 * call l2tpv2 tunnel callback
136 */
137 if (!ctx) {
138 nss_warning("%p: Event received for l2tpv2 tunnel interface %d before registration", nss_ctx, ncm->interface);
139 return;
140 }
141
142 cb(ctx, ntm);
143}
144
145/*
146 * nss_l2tpv2_tx()
147 * Transmit a l2tpv2 message to NSS firmware
148 */
149nss_tx_status_t nss_l2tpv2_tx(struct nss_ctx_instance *nss_ctx, struct nss_l2tpv2_msg *msg)
150{
151 struct nss_l2tpv2_msg *nm;
152 struct nss_cmn_msg *ncm = &msg->cm;
153 struct sk_buff *nbuf;
154 int32_t status;
155
156 NSS_VERIFY_CTX_MAGIC(nss_ctx);
157 if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) {
158 nss_warning("%p: l2tpv2 msg dropped as core not ready", nss_ctx);
159 return NSS_TX_FAILURE_NOT_READY;
160 }
161
162 /*
163 * Sanity check the message
164 */
165 if (!nss_is_dynamic_interface(ncm->interface)) {
166 nss_warning("%p: tx request for non dynamic interface: %d", nss_ctx, ncm->interface);
167 return NSS_TX_FAILURE;
168 }
169
170 if (ncm->type > NSS_L2TPV2_MSG_MAX) {
171 nss_warning("%p: message type out of range: %d", nss_ctx, ncm->type);
172 return NSS_TX_FAILURE;
173 }
174
Suruchi Agarwalef8a8702016-01-08 12:40:08 -0800175 if (nss_cmn_get_msg_len(ncm) > sizeof(struct nss_l2tpv2_msg)) {
176 nss_warning("%p: message length is invalid: %d", nss_ctx, nss_cmn_get_msg_len(ncm));
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530177 return NSS_TX_FAILURE;
178 }
179
180 nbuf = dev_alloc_skb(NSS_NBUF_PAYLOAD_SIZE);
181 if (unlikely(!nbuf)) {
182 NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_NBUF_ALLOC_FAILS]);
183 nss_warning("%p: msg dropped as command allocation failed", nss_ctx);
184 return NSS_TX_FAILURE;
185 }
186
187 /*
188 * Copy the message to our skb
189 */
190 nm = (struct nss_l2tpv2_msg *)skb_put(nbuf, sizeof(struct nss_l2tpv2_msg));
191 memcpy(nm, msg, sizeof(struct nss_l2tpv2_msg));
192
193 status = nss_core_send_buffer(nss_ctx, 0, nbuf, NSS_IF_CMD_QUEUE, H2N_BUFFER_CTRL, 0);
194 if (status != NSS_CORE_STATUS_SUCCESS) {
195 dev_kfree_skb_any(nbuf);
196 nss_warning("%p: Unable to enqueue 'l2tpv2 message'\n", nss_ctx);
197 if (status == NSS_CORE_STATUS_FAILURE_QUEUE) {
198 return NSS_TX_FAILURE_QUEUE;
199 }
200 return NSS_TX_FAILURE;
201 }
202
Stephen Wang90c67de2016-04-26 15:15:59 -0700203 nss_hal_send_interrupt(nss_ctx, NSS_H2N_INTR_DATA_COMMAND_QUEUE);
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530204
205 NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_TX_CMD_REQ]);
206 return NSS_TX_SUCCESS;
207}
208
209/*
210 ***********************************
211 * Register/Unregister/Miscellaneous APIs
212 ***********************************
213 */
214
215/*
216 * nss_register_l2tpv2_if()
217 */
218struct nss_ctx_instance *nss_register_l2tpv2_if(uint32_t if_num, nss_l2tpv2_callback_t l2tpv2_callback,
219 nss_l2tpv2_msg_callback_t event_callback, struct net_device *netdev, uint32_t features)
220{
Stephen Wang84e0e992016-09-07 12:31:40 -0700221 struct nss_ctx_instance *nss_ctx = (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.l2tpv2_handler_id];
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530222 int i = 0;
Stephen Wang84e0e992016-09-07 12:31:40 -0700223
224 nss_assert(nss_ctx);
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530225 nss_assert(nss_is_dynamic_interface(if_num));
226
Stephen Wang84e0e992016-09-07 12:31:40 -0700227 nss_ctx->subsys_dp_register[if_num].ndev = netdev;
228 nss_ctx->subsys_dp_register[if_num].cb = l2tpv2_callback;
229 nss_ctx->subsys_dp_register[if_num].app_data = NULL;
230 nss_ctx->subsys_dp_register[if_num].features = features;
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530231
232 nss_top_main.l2tpv2_msg_callback = event_callback;
233
Thomas Wu91f4bdf2017-06-09 12:03:02 -0700234 nss_core_register_handler(nss_ctx, if_num, nss_l2tpv2_handler, NULL);
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530235
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
Stephen Wang84e0e992016-09-07 12:31:40 -0700247 return nss_ctx;
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530248}
249
250/*
251 * nss_unregister_l2tpv2_if()
252 */
253void nss_unregister_l2tpv2_if(uint32_t if_num)
254{
Stephen Wang84e0e992016-09-07 12:31:40 -0700255 struct nss_ctx_instance *nss_ctx = (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.l2tpv2_handler_id];
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530256 int i;
Stephen Wang84e0e992016-09-07 12:31:40 -0700257
258 nss_assert(nss_ctx);
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530259 nss_assert(nss_is_dynamic_interface(if_num));
260
Stephen Wang84e0e992016-09-07 12:31:40 -0700261 nss_ctx->subsys_dp_register[if_num].ndev = NULL;
262 nss_ctx->subsys_dp_register[if_num].cb = NULL;
263 nss_ctx->subsys_dp_register[if_num].app_data = NULL;
264 nss_ctx->subsys_dp_register[if_num].features = 0;
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530265
266 nss_top_main.l2tpv2_msg_callback = NULL;
267
Thomas Wu91f4bdf2017-06-09 12:03:02 -0700268 nss_core_unregister_handler(nss_ctx, if_num);
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530269
270 spin_lock_bh(&nss_l2tpv2_session_debug_stats_lock);
271 for (i = 0; i < NSS_MAX_L2TPV2_DYNAMIC_INTERFACES; i++) {
272 if (nss_l2tpv2_session_debug_stats[i].if_num == if_num) {
273 memset(&nss_l2tpv2_session_debug_stats[i], 0, sizeof(struct nss_stats_l2tpv2_session_debug));
274 break;
275 }
276 }
277 spin_unlock_bh(&nss_l2tpv2_session_debug_stats_lock);
278}
279
280/*
281 * nss_get_l2tpv2_context()
282 */
283struct nss_ctx_instance *nss_l2tpv2_get_context()
284{
285 return (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.l2tpv2_handler_id];
286}
287
288/*
289 * nss_l2tpv2_msg_init()
290 * Initialize nss_l2tpv2 msg.
291 */
292void nss_l2tpv2_msg_init(struct nss_l2tpv2_msg *ncm, uint16_t if_num, uint32_t type, uint32_t len, void *cb, void *app_data)
293{
294 nss_cmn_msg_init(&ncm->cm, if_num, type, len, cb, app_data);
295}
296
297/* nss_l2tpv2_register_handler()
298 * debugfs stats msg handler received on static l2tpv2 interface
299 */
300void nss_l2tpv2_register_handler(void)
301{
Thomas Wu91f4bdf2017-06-09 12:03:02 -0700302 struct nss_ctx_instance *nss_ctx = nss_l2tpv2_get_context();
303
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530304 nss_info("nss_l2tpv2_register_handler");
Thomas Wu91f4bdf2017-06-09 12:03:02 -0700305 nss_core_register_handler(nss_ctx, NSS_L2TPV2_INTERFACE, nss_l2tpv2_handler, NULL);
ratheesh kannoth7af985d2015-06-24 15:08:40 +0530306}
307
308EXPORT_SYMBOL(nss_l2tpv2_get_context);
309EXPORT_SYMBOL(nss_l2tpv2_tx);
310EXPORT_SYMBOL(nss_unregister_l2tpv2_if);
311EXPORT_SYMBOL(nss_l2tpv2_msg_init);
312EXPORT_SYMBOL(nss_register_l2tpv2_if);