blob: 66a48c13144836c69b0001fa3986aa3c5a7b97e8 [file] [log] [blame]
Bhaskar Valaboju3aea9fe2018-10-08 11:37:08 +05301/*
2 **************************************************************************
3 * Copyright (c) 2019, 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 "nss_tx_rx_common.h"
18#include "nss_qvpn_stats.h"
19#include "nss_qvpn_log.h"
20
21#define NSS_QVPN_TX_TIMEOUT 1000 /* 1 Second */
22
23/*
24 * Private data structure
25 */
26static struct nss_qvpn_pvt {
27 struct semaphore sem;
28 struct completion complete;
29 unsigned long if_map[NSS_QVPN_INTERFACE_MAX_LONG];
30 enum nss_qvpn_error_type resp;
31} qvpn_pvt;
32
33/*
34 * nss_qvpn_verify_if_num()
35 * Verify if_num passed to us.
36 */
37static bool nss_qvpn_verify_if_num(uint32_t if_num)
38{
39 enum nss_dynamic_interface_type if_type;
40
41 if_type = nss_dynamic_interface_get_type(nss_qvpn_get_context(), if_num);
42 if ((if_type != NSS_DYNAMIC_INTERFACE_TYPE_QVPN_INNER) &&
43 (if_type != NSS_DYNAMIC_INTERFACE_TYPE_QVPN_OUTER)) {
44 nss_warning("%p: if_num = %u interface type returned is %d\n", nss_qvpn_get_context(), if_num, if_type);
45 return false;
46 }
47
48 return true;
49}
50
51/*
52 * nss_qvpn_tunnel_stats_sync
53 * Update qvpn interface statistics.
54 */
55static void nss_qvpn_tunnel_stats_sync(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm)
56{
57 struct nss_qvpn_msg *ndcm = (struct nss_qvpn_msg *)ncm;
58 struct nss_top_instance *nss_top = nss_ctx->nss_top;
59 struct nss_qvpn_stats_sync_msg *msg_stats = &ndcm->msg.stats;
60 uint64_t *if_stats;
61
62 spin_lock_bh(&nss_top->stats_lock);
63
64 /*
65 * Update common node stats
66 */
67 if_stats = nss_top->stats_node[ncm->interface];
68 if_stats[NSS_STATS_NODE_RX_PKTS] += msg_stats->node_stats.rx_packets;
69 if_stats[NSS_STATS_NODE_RX_BYTES] += msg_stats->node_stats.rx_bytes;
70 if_stats[NSS_STATS_NODE_RX_QUEUE_0_DROPPED] += msg_stats->node_stats.rx_dropped[0];
71 if_stats[NSS_STATS_NODE_RX_QUEUE_1_DROPPED] += msg_stats->node_stats.rx_dropped[1];
72 if_stats[NSS_STATS_NODE_RX_QUEUE_2_DROPPED] += msg_stats->node_stats.rx_dropped[2];
73 if_stats[NSS_STATS_NODE_RX_QUEUE_3_DROPPED] += msg_stats->node_stats.rx_dropped[3];
74
75 if_stats[NSS_STATS_NODE_TX_PKTS] += msg_stats->node_stats.tx_packets;
76 if_stats[NSS_STATS_NODE_TX_BYTES] += msg_stats->node_stats.tx_bytes;
77
78 spin_unlock_bh(&nss_top->stats_lock);
79}
80
81/*
82 * nss_qvpn_handler()
83 * Handle NSS to HLOS messages for QVPN
84 */
85static void nss_qvpn_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm, void *app_data)
86{
87 nss_qvpn_msg_callback_t cb;
88
89 nss_assert(nss_qvpn_verify_if_num(ncm->interface));
90
91 NSS_VERIFY_CTX_MAGIC(nss_ctx);
92
93 /*
94 * Is this a valid request/response packet?
95 */
96 if (ncm->type >= NSS_QVPN_MSG_TYPE_MAX) {
97 nss_warning("%p: received invalid message %d for qvpn interface", nss_ctx, ncm->type);
98 return;
99 }
100
101 if (nss_cmn_get_msg_len(ncm) > sizeof(struct nss_qvpn_msg)) {
102 nss_warning("%p: length of message is greater than required: %d", nss_ctx, nss_cmn_get_msg_len(ncm));
103 return;
104 }
105
106 /*
107 * Log failures
108 */
109
110 nss_core_log_msg_failures(nss_ctx, ncm);
111 /*
112 * Trace messages.
113 */
114 nss_qvpn_log_rx_msg((struct nss_qvpn_msg *)ncm);
115
116 if (ncm->type == NSS_QVPN_MSG_TYPE_SYNC_STATS) {
117 nss_qvpn_tunnel_stats_sync(nss_ctx, ncm);
118 }
119
120 /*
121 * Update the callback and app_data for NOTIFY messages, qvpn sends all notify messages
122 * to the same callback/app_data.
123 */
124 if (ncm->response == NSS_CMN_RESPONSE_NOTIFY) {
125 ncm->cb = (nss_ptr_t)nss_top_main.if_rx_msg_callback[ncm->interface];
126 ncm->app_data = (nss_ptr_t)nss_ctx->nss_rx_interface_handlers[nss_ctx->id][ncm->interface].app_data;
127 }
128
129 /*
130 * load, test & call
131 */
132 cb = (nss_qvpn_msg_callback_t)ncm->cb;
133 if (unlikely(!cb)) {
134 nss_trace("%p: rx handler unregistered for i/f: %u\n", nss_ctx, ncm->interface);
135 return;
136 }
137
138 cb((void *)ncm->app_data, ncm);
139}
140
141/*
142 * nss_qvpn_callback()
143 * Callback to handle the completion of NSS to HLOS messages.
144 */
145static void nss_qvpn_callback(void *app_data, struct nss_qvpn_msg *nvm)
146{
147 enum nss_qvpn_error_type *resp = (enum nss_qvpn_error_type *)app_data;
148
149 *resp = (nvm->cm.response == NSS_CMN_RESPONSE_ACK) ? NSS_QVPN_ERROR_TYPE_NONE : nvm->cm.error;
150
151 /*
152 * Write memory barrier
153 */
154 smp_wmb();
155
156 complete(&qvpn_pvt.complete);
157}
158
159/*
160 * nss_qvpn_ifmap_get()
161 * Return QVPN active interfaces map.
162 */
163unsigned long *nss_qvpn_ifmap_get(void)
164{
165 return qvpn_pvt.if_map;
166}
167
168/*
169 * nss_qvpn_get_context()
170 * Return NSS QVPN context.
171 */
172struct nss_ctx_instance *nss_qvpn_get_context(void)
173{
174 return &nss_top_main.nss[nss_top_main.qvpn_handler_id];
175}
176EXPORT_SYMBOL(nss_qvpn_get_context);
177
178/*
179 * nss_qvpn_tx_msg()
180 * Transmit a QVPN message to NSS firmware
181 */
182nss_tx_status_t nss_qvpn_tx_msg(struct nss_ctx_instance *nss_ctx, struct nss_qvpn_msg *msg)
183{
184 struct nss_cmn_msg *ncm = &msg->cm;
185
186 /*
187 * Sanity check the message
188 */
189 if (!nss_qvpn_verify_if_num(ncm->interface)) {
190 nss_warning("%p: tx request for interface that is not a qvpn: %u\n", nss_ctx, ncm->interface);
191 return NSS_TX_FAILURE_BAD_PARAM;
192 }
193
194 if (ncm->type >= NSS_QVPN_MSG_TYPE_MAX) {
195 nss_warning("%p: message type out of range: %d\n", nss_ctx, ncm->type);
196 return NSS_TX_FAILURE_BAD_PARAM;
197 }
198
199 /*
200 * Trace messages.
201 */
202 nss_qvpn_log_tx_msg(msg);
203
204 return nss_core_send_cmd(nss_ctx, msg, sizeof(*msg), NSS_NBUF_PAYLOAD_SIZE);
205}
206EXPORT_SYMBOL(nss_qvpn_tx_msg);
207
208/*
209 * nss_qvpn_tx_msg_sync()
210 * Transmit a QVPN message to NSS firmware synchronously.
211 */
212nss_tx_status_t nss_qvpn_tx_msg_sync(struct nss_ctx_instance *nss_ctx, struct nss_qvpn_msg *nvm,
213 uint32_t if_num, enum nss_qvpn_msg_type type, uint16_t len, enum nss_qvpn_error_type *resp)
214{
215 struct nss_qvpn_msg nqm;
216 nss_tx_status_t status;
217 int ret = 0;
218
219 NSS_VERIFY_CTX_MAGIC(nss_ctx);
220
221 if (len > sizeof(nqm.msg)) {
222 nss_warning("%p: Incorrect message length=%u for type %d and if_num=%u\n", nss_ctx, len, type, if_num);
223 return NSS_TX_FAILURE_TOO_LARGE;
224 }
225
226 if (!resp) {
227 nss_warning("%p: Invalid input, resp=NULL\n", nss_ctx);
228 return NSS_TX_FAILURE_BAD_PARAM;
229 }
230
231 nss_qvpn_msg_init(&nqm, if_num, type, len, nss_qvpn_callback, &qvpn_pvt.resp);
232 memcpy(&nqm.msg, &nvm->msg, len);
233
234 down(&qvpn_pvt.sem);
235
236 status = nss_qvpn_tx_msg(nss_ctx, &nqm);
237 if (status != NSS_TX_SUCCESS) {
238 nss_warning("%p: qvpn_tx_msg failed\n", nss_ctx);
239 goto done;
240 }
241
242 ret = wait_for_completion_timeout(&qvpn_pvt.complete, msecs_to_jiffies(NSS_QVPN_TX_TIMEOUT));
243 if (!ret) {
244 nss_warning("%p: qvpn msg tx failed due to timeout\n", nss_ctx);
245 status = NSS_TX_FAILURE_SYNC_TIMEOUT;
246 goto done;
247 }
248
249 /*
250 * Read memory barrier
251 */
252 smp_rmb();
253
254 *resp = qvpn_pvt.resp;
255 if (*resp != NSS_QVPN_ERROR_TYPE_NONE)
256 status = NSS_TX_FAILURE;
257done:
258 up(&qvpn_pvt.sem);
259 return status;
260}
261EXPORT_SYMBOL(nss_qvpn_tx_msg_sync);
262
263/*
264 * nss_qvpn_tx_buf()
265 * Send packet to QVPN interface owned by NSS
266 */
267nss_tx_status_t nss_qvpn_tx_buf(struct nss_ctx_instance *nss_ctx, uint32_t if_num, struct sk_buff *skb)
268{
269 if (!nss_qvpn_verify_if_num(if_num)) {
270 nss_warning("%p: tx request for interface that is not a qvpn: %u\n", nss_ctx, if_num);
271 return NSS_TX_FAILURE_BAD_PARAM;
272 }
273
274 return nss_core_send_packet(nss_ctx, skb, if_num, 0);
275}
276EXPORT_SYMBOL(nss_qvpn_tx_buf);
277
278/*
279 * nss_qvpn_msg_init()
280 * Initialize nss_qvpn_msg.
281 */
282void nss_qvpn_msg_init(struct nss_qvpn_msg *ncm, uint16_t if_num, uint32_t type, uint32_t len, void *cb, void *app_data)
283{
284 nss_cmn_msg_init(&ncm->cm, if_num, type, len, cb, app_data);
285}
286EXPORT_SYMBOL(nss_qvpn_msg_init);
287
288/*
289 * nss_qvpn_register_if()
290 * Register QVPN interface.
291 */
292struct nss_ctx_instance *nss_qvpn_register_if(uint32_t if_num, nss_qvpn_callback_t qvpn_data_callback,
293 nss_qvpn_msg_callback_t qvpn_event_callback,
294 struct net_device *netdev, uint32_t features, void *app_ctx)
295{
296 struct nss_ctx_instance *nss_ctx = nss_qvpn_get_context();
297
298 nss_assert(nss_ctx);
299 nss_assert(nss_qvpn_verify_if_num(if_num));
300
301 nss_core_register_subsys_dp(nss_ctx, if_num, qvpn_data_callback, NULL, app_ctx, netdev, features);
302 nss_core_register_handler(nss_ctx, if_num, nss_qvpn_handler, app_ctx);
303 nss_top_main.if_rx_msg_callback[if_num] = qvpn_event_callback;
304
305 set_bit(if_num, qvpn_pvt.if_map);
306 return nss_ctx;
307}
308EXPORT_SYMBOL(nss_qvpn_register_if);
309
310/*
311 * nss_unregister_qvpn_if()
312 * Unregister QVPN interface.
313 */
314void nss_qvpn_unregister_if(uint32_t if_num)
315{
316 struct nss_ctx_instance *nss_ctx = nss_qvpn_get_context();
317
318 nss_assert(nss_qvpn_verify_if_num(if_num));
319
320 clear_bit(if_num, qvpn_pvt.if_map);
321
322 nss_core_unregister_subsys_dp(nss_ctx, if_num);
323 nss_core_unregister_handler(nss_ctx, if_num);
324
325 nss_top_main.if_rx_msg_callback[if_num] = NULL;
326}
327EXPORT_SYMBOL(nss_qvpn_unregister_if);
328
329/*
330 * nss_qvpn_ifnum_with_core_id()
331 * Append core id to QVPN interface number
332 */
333int nss_qvpn_ifnum_with_core_id(int if_num)
334{
335 struct nss_ctx_instance *nss_ctx = nss_qvpn_get_context();
336
337 NSS_VERIFY_CTX_MAGIC(nss_ctx);
338 if (nss_qvpn_verify_if_num(if_num) == false) {
339 nss_info("%p: if_num: %u is not QVPN interface\n", nss_ctx, if_num);
340 return 0;
341 }
342 return NSS_INTERFACE_NUM_APPEND_COREID(nss_ctx, if_num);
343}
344EXPORT_SYMBOL(nss_qvpn_ifnum_with_core_id);
345
346/*
347 * nss_qvpn_register_handler()
348 * Intialize QVPN driver and register handler.
349 */
350void nss_qvpn_register_handler(void)
351{
352 struct nss_ctx_instance *nss_ctx = nss_qvpn_get_context();
353
354 nss_info("nss_qvpn_register_handler\n");
355 nss_core_register_handler(nss_ctx, NSS_QVPN_INTERFACE, nss_qvpn_handler, NULL);
356 sema_init(&qvpn_pvt.sem, 1);
357 init_completion(&qvpn_pvt.complete);
358 nss_qvpn_stats_dentry_create();
359}