blob: 4174e89cffb0115f316ac85ac14d62976bfae076 [file] [log] [blame]
ratheesh kannoth7af985d2015-06-24 15:08:40 +05301/*
2 **************************************************************************
Suruchi Agarwalef8a8702016-01-08 12:40:08 -08003 * Copyright (c) 2015-2016, 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) {
113 ncm->cb = (uint32_t)nss_ctx->nss_top->l2tpv2_msg_callback;
114 }
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;
132 ctx = nss_ctx->nss_top->subsys_dp_register[ncm->interface].ndev;
133
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
203 nss_hal_send_interrupt(nss_ctx->nmap, nss_ctx->h2n_desc_rings[NSS_IF_CMD_QUEUE].desc_ring.int_bit,
204 NSS_REGS_H2N_INTR_STATUS_DATA_COMMAND_QUEUE);
205
206 NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_TX_CMD_REQ]);
207 return NSS_TX_SUCCESS;
208}
209
210/*
211 ***********************************
212 * Register/Unregister/Miscellaneous APIs
213 ***********************************
214 */
215
216/*
217 * nss_register_l2tpv2_if()
218 */
219struct nss_ctx_instance *nss_register_l2tpv2_if(uint32_t if_num, nss_l2tpv2_callback_t l2tpv2_callback,
220 nss_l2tpv2_msg_callback_t event_callback, struct net_device *netdev, uint32_t features)
221{
222
223 int i = 0;
224 nss_assert(nss_is_dynamic_interface(if_num));
225
226 nss_top_main.subsys_dp_register[if_num].ndev = netdev;
227 nss_top_main.subsys_dp_register[if_num].cb = l2tpv2_callback;
228 nss_top_main.subsys_dp_register[if_num].app_data = NULL;
229 nss_top_main.subsys_dp_register[if_num].features = features;
230
231 nss_top_main.l2tpv2_msg_callback = event_callback;
232
233 nss_core_register_handler(if_num, nss_l2tpv2_handler, NULL);
234
235 spin_lock_bh(&nss_l2tpv2_session_debug_stats_lock);
236 for (i = 0; i < NSS_MAX_L2TPV2_DYNAMIC_INTERFACES; i++) {
237 if (!nss_l2tpv2_session_debug_stats[i].valid) {
238 nss_l2tpv2_session_debug_stats[i].valid = true;
239 nss_l2tpv2_session_debug_stats[i].if_num = if_num;
240 nss_l2tpv2_session_debug_stats[i].if_index = netdev->ifindex;
241 break;
242 }
243 }
244 spin_unlock_bh(&nss_l2tpv2_session_debug_stats_lock);
245
246 return (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.l2tpv2_handler_id];
247}
248
249/*
250 * nss_unregister_l2tpv2_if()
251 */
252void nss_unregister_l2tpv2_if(uint32_t if_num)
253{
254 int i;
255 nss_assert(nss_is_dynamic_interface(if_num));
256
257 nss_top_main.subsys_dp_register[if_num].ndev = NULL;
258 nss_top_main.subsys_dp_register[if_num].cb = NULL;
259 nss_top_main.subsys_dp_register[if_num].app_data = NULL;
260 nss_top_main.subsys_dp_register[if_num].features = 0;
261
262 nss_top_main.l2tpv2_msg_callback = NULL;
263
264 nss_core_unregister_handler(if_num);
265
266 spin_lock_bh(&nss_l2tpv2_session_debug_stats_lock);
267 for (i = 0; i < NSS_MAX_L2TPV2_DYNAMIC_INTERFACES; i++) {
268 if (nss_l2tpv2_session_debug_stats[i].if_num == if_num) {
269 memset(&nss_l2tpv2_session_debug_stats[i], 0, sizeof(struct nss_stats_l2tpv2_session_debug));
270 break;
271 }
272 }
273 spin_unlock_bh(&nss_l2tpv2_session_debug_stats_lock);
274}
275
276/*
277 * nss_get_l2tpv2_context()
278 */
279struct nss_ctx_instance *nss_l2tpv2_get_context()
280{
281 return (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.l2tpv2_handler_id];
282}
283
284/*
285 * nss_l2tpv2_msg_init()
286 * Initialize nss_l2tpv2 msg.
287 */
288void nss_l2tpv2_msg_init(struct nss_l2tpv2_msg *ncm, uint16_t if_num, uint32_t type, uint32_t len, void *cb, void *app_data)
289{
290 nss_cmn_msg_init(&ncm->cm, if_num, type, len, cb, app_data);
291}
292
293/* nss_l2tpv2_register_handler()
294 * debugfs stats msg handler received on static l2tpv2 interface
295 */
296void nss_l2tpv2_register_handler(void)
297{
298 nss_info("nss_l2tpv2_register_handler");
299 nss_core_register_handler(NSS_L2TPV2_INTERFACE, nss_l2tpv2_handler, NULL);
300}
301
302EXPORT_SYMBOL(nss_l2tpv2_get_context);
303EXPORT_SYMBOL(nss_l2tpv2_tx);
304EXPORT_SYMBOL(nss_unregister_l2tpv2_if);
305EXPORT_SYMBOL(nss_l2tpv2_msg_init);
306EXPORT_SYMBOL(nss_register_l2tpv2_if);