blob: d1125ed780f1fc20b9b8e064efa9a0fb25e2f8c9 [file] [log] [blame]
Yu Huang6b2f0be2017-10-18 16:11:49 -07001/*
2 **************************************************************************
3 * Copyright (c) 2017-2018, 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
19/*
20 * Notify data structure
21 */
22struct nss_qrfs_notify_data {
23 nss_qrfs_msg_callback_t qrfs_callback;
24 void *app_data;
25};
26
27static struct nss_qrfs_notify_data nss_qrfs_notify[NSS_CORE_MAX];
28
29/*
30 * nss_qrfs_verify_if_num()
31 * Verify if_num passed to us.
32 */
33static bool nss_qrfs_verify_if_num(uint32_t if_num)
34{
35 return if_num == NSS_QRFS_INTERFACE;
36}
37
38/*
39 * nss_qrfs_msg_handler()
40 * Handle NSS -> HLOS messages for QRFS
41 */
42static void nss_qrfs_msg_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm, void *app_data)
43{
44 struct nss_qrfs_msg *nqm = (struct nss_qrfs_msg *)ncm;
45 nss_qrfs_msg_callback_t cb;
46
47 if (!nss_qrfs_verify_if_num(ncm->interface)) {
48 nss_warning("%p: invalid interface %d for QRFS\n", nss_ctx, ncm->interface);
49 return;
50 }
51
52 /*
53 * Is this a valid request/response?
54 */
55 if (ncm->type >= NSS_QRFS_MSG_MAX) {
56 nss_warning("%p: invalid message %d for QRFS\n", nss_ctx, ncm->type);
57 return;
58 }
59
60 if (nss_cmn_get_msg_len(ncm) > sizeof(struct nss_qrfs_msg)) {
61 nss_warning("%p: message length is greater than required: %d\n", nss_ctx, nss_cmn_get_msg_len(ncm));
62 return;
63 }
64
65 /*
66 * Log failures
67 */
68 nss_core_log_msg_failures(nss_ctx, ncm);
69
70 /*
71 * Update the callback and app_data for NOTIFY messages
72 */
73 if (ncm->response == NSS_CMM_RESPONSE_NOTIFY) {
74 ncm->cb = (nss_ptr_t)nss_qrfs_notify[nss_ctx->id].qrfs_callback;
75 ncm->app_data = (nss_ptr_t)nss_qrfs_notify[nss_ctx->id].app_data;
76 }
77
78 /*
79 * Do we have a callback?
80 */
81 if (!ncm->cb) {
82 return;
83 }
84
85 /*
86 * callback
87 */
88 cb = (nss_qrfs_msg_callback_t)ncm->cb;
89 cb((void *)ncm->app_data, nqm);
90}
91
92/*
93 * nss_qrfs_get_ctx()
94 */
95static struct nss_ctx_instance *nss_qrfs_get_ctx(int core_id)
96{
97 return &nss_top_main.nss[core_id];
98}
99
100/*
101 * nss_qrfs_get_flow_keys()
102 * Get 5 tuple information from flow keys and set in flow rule message.
103 */
104#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 18, 21))
105static bool nss_qrfs_get_flow_keys(struct nss_ctx_instance *nss_ctx, struct sk_buff *skb,
106 struct nss_qrfs_flow_rule_msg *nqfrm)
107{
108 struct flow_keys keys;
109 uint16_t protocol = skb->protocol;
110 bool res;
111 struct ipv6hdr *ip6hdr;
112
113 res = skb_flow_dissect(skb, &keys);
114 if (!res) {
115 nss_warning("%p: failed to get flow keys\n", nss_ctx);
116 return res;
117 }
118
119 nqfrm->protocol = keys.ip_proto;
120 nqfrm->src_port = keys.port16[0];
121 nqfrm->dst_port = keys.port16[1];
122
123 if (protocol == htons(ETH_P_IP)) {
124 nqfrm->ip_version = 4;
125 nqfrm->src_addr[0] = keys.src;
126 nqfrm->dst_addr[0] = keys.dst;
127 return true;
128 }
129
130 nqfrm->ip_version = 6;
131 ip6hdr = (struct ipv6hdr *)skb_network_header(skb);
132 if (!ip6hdr) {
133 nss_warning("%p: failed to get IPv6 address\n", nss_ctx);
134 return false;
135 }
136
137 memcpy(nqfrm->src_addr, &ip6hdr->saddr, sizeof(struct in6_addr));
138 memcpy(nqfrm->dst_addr, &ip6hdr->daddr, sizeof(struct in6_addr));
139
140 return true;
141}
142#else
143static bool nss_qrfs_get_flow_keys(struct nss_ctx_instance *nss_ctx, struct sk_buff *skb,
144 struct nss_qrfs_flow_rule_msg *nqfrm)
145{
146 struct flow_keys keys;
147 bool res;
148
149 res = skb_flow_dissect_flow_keys(skb, &keys, 0);
150 if (!res) {
151 nss_warning("%p: failed to get flow keys\n", nss_ctx);
152 return res;
153 }
154
155 nqfrm->protocol = (uint16_t)keys.basic.ip_proto;
156 nqfrm->src_port = keys.ports.src;
157 nqfrm->dst_port = keys.ports.dst;
158
159 if (keys.basic.n_proto == htons(ETH_P_IP)) {
160 nqfrm->ip_version = 4;
161 nqfrm->src_addr[0] = keys.addrs.v4addrs.src;
162 nqfrm->dst_addr[0] = keys.addrs.v4addrs.dst;
163 return true;
164 }
165
166 nqfrm->ip_version = 6;
167 memcpy(nqfrm->src_addr, &keys.addrs.v6addrs.src, sizeof(struct in6_addr));
168 memcpy(nqfrm->dst_addr, &keys.addrs.v6addrs.dst, sizeof(struct in6_addr));
169
170 return true;
171}
172#endif
173
174/*
175 * nss_qrfs_flow_add_msg_callback()
176 * Callback function for receiving flow add response messages.
177 */
178static void nss_qrfs_flow_add_msg_callback(void *app_data, struct nss_qrfs_msg *nqm)
179{
180 struct nss_ctx_instance *nss_ctx __maybe_unused = (struct nss_ctx_instance *)app_data;
181 struct nss_qrfs_flow_rule_msg *nqfrm;
182
183 if (nqm->cm.type != NSS_QRFS_MSG_FLOW_ADD) {
184 nss_warning("%p: invalid flow response message %d\n", nss_ctx, nqm->cm.type);
185 return;
186 }
187
188 nqfrm = &nqm->msg.flow_add;
189
190 if ((nqfrm->ip_version != 4) && (nqfrm->ip_version != 6)) {
191 nss_warning("%p: invalid IP version %d\n", nss_ctx, nqfrm->ip_version);
192 return;
193 }
194
195 if (nqm->cm.response != NSS_CMN_RESPONSE_ACK) {
196 nss_warning("%p: flow add configuration error: %d for NSS core %d\n",
197 nss_ctx, nqm->cm.error, nss_ctx->id);
198 }
199}
200
201/*
202 * nss_qrfs_flow_delete_msg_callback()
203 * Callback function for receiving flow delete response messages.
204 */
205static void nss_qrfs_flow_delete_msg_callback(void *app_data, struct nss_qrfs_msg *nqm)
206{
207 struct nss_ctx_instance *nss_ctx __maybe_unused = (struct nss_ctx_instance *)app_data;
208 struct nss_qrfs_flow_rule_msg *nqfrm;
209
210 if (nqm->cm.type != NSS_QRFS_MSG_FLOW_DELETE) {
211 nss_warning("%p: invalid flow response message %d\n", nss_ctx, nqm->cm.type);
212 return;
213 }
214
215 nqfrm = &nqm->msg.flow_delete;
216
217 if ((nqfrm->ip_version != 4) && (nqfrm->ip_version != 6)) {
218 nss_warning("%p: invalid IP version %d\n", nss_ctx, nqfrm->ip_version);
219 return;
220 }
221
222 if (nqm->cm.response != NSS_CMN_RESPONSE_ACK) {
223 nss_warning("%p: flow delete configuration error: %d for NSS core %d\n",
224 nss_ctx, nqm->cm.error, nss_ctx->id);
225 }
226}
227
228/*
229 * nss_qrfs_msg_init()
230 * Initialize the common header of QRFS message
231 */
232static void nss_qrfs_msg_init(struct nss_qrfs_msg *nqm, uint16_t if_num, uint32_t type, uint32_t len, void *cb, void *app_data)
233{
234 nss_cmn_msg_init(&nqm->cm, if_num, type, len, cb, app_data);
235}
236
237/*
238 * nss_qrfs_tx_msg()
239 * Transmit a QRFS message to NSS firmware
240 */
241static nss_tx_status_t nss_qrfs_tx_msg(struct nss_ctx_instance *nss_ctx, struct nss_qrfs_msg *msg)
242{
243 struct nss_qrfs_msg *nqm;
244 struct nss_cmn_msg *ncm = &msg->cm;
245 struct sk_buff *nbuf;
246 int32_t status;
247
248 if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) {
249 nss_warning("%p: message dropped as core not ready\n", nss_ctx);
250 return NSS_TX_FAILURE_NOT_READY;
251 }
252
253 /*
254 * Sanity check the message
255 */
256 if (!nss_qrfs_verify_if_num(ncm->interface)) {
257 nss_warning("%p: interface is not QRFS interface: %d\n", nss_ctx, ncm->interface);
258 return NSS_TX_FAILURE;
259 }
260
261 if (ncm->type >= NSS_QRFS_MSG_MAX) {
262 nss_warning("%p: message type is out of range: %d\n", nss_ctx, ncm->type);
263 return NSS_TX_FAILURE;
264 }
265
266 if (nss_cmn_get_msg_len(ncm) > sizeof(struct nss_qrfs_msg)) {
267 nss_warning("%p: message length is invalid: %d\n", nss_ctx, nss_cmn_get_msg_len(ncm));
268 return NSS_TX_FAILURE;
269 }
270
271 nbuf = dev_alloc_skb(NSS_NBUF_PAYLOAD_SIZE);
272 if (unlikely(!nbuf)) {
273 NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_NBUF_ALLOC_FAILS]);
274 nss_warning("%p: message allocation failed\n", nss_ctx);
275 return NSS_TX_FAILURE;
276 }
277
278 /*
279 * Copy the message to our skb
280 */
281 nqm = (struct nss_qrfs_msg *)skb_put(nbuf, sizeof(struct nss_qrfs_msg));
282 memcpy(nqm, msg, sizeof(struct nss_qrfs_msg));
283
284 status = nss_core_send_buffer(nss_ctx, 0, nbuf, NSS_IF_CMD_QUEUE, H2N_BUFFER_CTRL, 0);
285 if (status != NSS_CORE_STATUS_SUCCESS) {
286 dev_kfree_skb_any(nbuf);
287 nss_warning("%p: unable to enqueue QRFS message\n", nss_ctx);
288 return NSS_TX_FAILURE;
289 }
290
291 nss_hal_send_interrupt(nss_ctx, NSS_H2N_INTR_DATA_COMMAND_QUEUE);
292
293 NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_TX_CMD_REQ]);
294
295 return NSS_TX_SUCCESS;
296}
297
298/*
299 * nss_qrfs_add_flow_rule()
300 * Set a QRFS flow rule add message and transmit the message to NSS core.
301 */
302static nss_tx_status_t nss_qrfs_add_flow_rule(struct nss_ctx_instance *nss_ctx, uint32_t if_num,
303 struct sk_buff *skb, uint32_t cpu, bool need_cb)
304{
305 struct nss_qrfs_msg nqm;
306 struct nss_qrfs_flow_rule_msg *nqfrm;
307 nss_tx_status_t status;
308 nss_qrfs_msg_callback_t cb = NULL;
309 void *app_data = NULL;
310 bool res;
311
312 memset(&nqm, 0, sizeof(struct nss_qrfs_msg));
313
314 if (need_cb) {
315 cb = nss_qrfs_flow_add_msg_callback;
316 app_data = (void *)nss_ctx;
317 }
318
319 /*
320 * Initialize common header of QRFS flow rule add message.
321 */
322 nss_qrfs_msg_init(&nqm, NSS_QRFS_INTERFACE, NSS_QRFS_MSG_FLOW_ADD,
323 sizeof(struct nss_qrfs_flow_rule_msg), cb, app_data);
324
325 /*
326 * Set flow rule of QRFS flow rule add message
327 */
328 nqfrm = &nqm.msg.flow_add;
329 res = nss_qrfs_get_flow_keys(nss_ctx, skb, nqfrm);
330 if (!res) {
331 return NSS_TX_FAILURE;
332 }
333
334 nqfrm->cpu = (uint16_t)cpu;
335 nqfrm->if_num = if_num;
336
337 /*
338 * Send QRFS flow rule add message to NSS core
339 */
340 status = nss_qrfs_tx_msg(nss_ctx, &nqm);
341 if (status == NSS_TX_SUCCESS) {
342 return status;
343 }
344
345 return NSS_TX_FAILURE;
346}
347
348/*
349 * nss_qrfs_delete_flow_rule()
350 * Set a QRFS delete flow rule message and transmit the message to all NSS core.
351 */
352static nss_tx_status_t nss_qrfs_delete_flow_rule(struct nss_ctx_instance *nss_ctx, uint32_t if_num,
353 struct sk_buff *skb, uint32_t cpu, bool need_cb)
354{
355 struct nss_qrfs_msg nqm;
356 struct nss_qrfs_flow_rule_msg *nqfrm;
357 nss_tx_status_t status;
358 nss_qrfs_msg_callback_t cb = NULL;
359 void *app_data = NULL;
360 bool res;
361
362 memset(&nqm, 0, sizeof(struct nss_qrfs_msg));
363
364 if (need_cb) {
365 cb = nss_qrfs_flow_delete_msg_callback;
366 app_data = (void *)nss_ctx;
367 }
368
369 /*
370 * Initialize common header of QRFS flow rule delete message.
371 */
372 nss_qrfs_msg_init(&nqm, NSS_QRFS_INTERFACE, NSS_QRFS_MSG_FLOW_DELETE,
373 sizeof(struct nss_qrfs_flow_rule_msg), cb, app_data);
374
375 /*
376 * Set flow rule of QRFS flow rule delete message
377 */
378 nqfrm = &nqm.msg.flow_delete;
379 res = nss_qrfs_get_flow_keys(nss_ctx, skb, nqfrm);
380 if (!res) {
381 return NSS_TX_FAILURE;
382 }
383
384 nqfrm->cpu = (uint16_t)cpu;
385 nqfrm->if_num = if_num;
386
387 /*
388 * Send QRFS flow rule delete message to NSS core
389 */
390 status = nss_qrfs_tx_msg(nss_ctx, &nqm);
391 if (status == NSS_TX_SUCCESS) {
392 return status;
393 }
394
395 return NSS_TX_FAILURE;
396}
397
398/*
399 * nss_qrfs_set_flow_rule()
400 * Set a QRFS flow rule message and transmit the message to all NSS cores.
401 */
402nss_tx_status_t nss_qrfs_set_flow_rule(struct net_device *netdev, uint32_t if_num,
403 struct sk_buff *skb, uint32_t cpu, uint32_t action)
404{
405 struct nss_ctx_instance *nss_ctx;
406 nss_tx_status_t status;
407 int i;
408
409 for (i = 0; i < NSS_CORE_MAX; i++) {
410 nss_ctx = nss_qrfs_get_ctx(i);
411
412 /*
413 * Set QRFS flow rule message and transmit the message to NSS core.
414 */
415 if (action == NSS_QRFS_MSG_FLOW_ADD) {
416 status = nss_qrfs_add_flow_rule(nss_ctx, if_num, skb, cpu, true);
417 } else {
418 status = nss_qrfs_delete_flow_rule(nss_ctx, if_num, skb, cpu, true);
419 }
420
421 if (status != NSS_TX_SUCCESS) {
422 nss_warning("%p: failed to send flow rule to NSS core %d\n", nss_ctx, i);
423 return NSS_TX_FAILURE;
424 }
425 }
426
427 return NSS_TX_SUCCESS;
428}
429EXPORT_SYMBOL(nss_qrfs_set_flow_rule);
430
431/*
432 * nss_qrfs_register_handler()
433 */
434void nss_qrfs_register_handler(struct nss_ctx_instance *nss_ctx)
435{
436 nss_core_register_handler(nss_ctx, NSS_QRFS_INTERFACE, nss_qrfs_msg_handler, NULL);
437}
438EXPORT_SYMBOL(nss_qrfs_register_handler);
439
440/*
441 * nss_qrfs_notify_register()
442 * Register to receive QRFS notify messages.
443 */
444struct nss_ctx_instance *nss_qrfs_notify_register(int core, nss_qrfs_msg_callback_t cb, void *app_data)
445{
446 if (core >= NSS_CORE_MAX) {
447 nss_warning("Input core number %d is wrong\n", core);
448 return NULL;
449 }
450
451 nss_qrfs_notify[core].qrfs_callback = cb;
452 nss_qrfs_notify[core].app_data = app_data;
453
454 return (struct nss_ctx_instance *)&nss_top_main.nss[core];
455}
456
457/*
458 * nss_qrfs_notify_unregister()
459 * Unregister to receive QRFS notify messages.
460 */
461void nss_qrfs_notify_unregister(int core)
462{
463 if (core >= NSS_CORE_MAX) {
464 nss_warning("Input core number %d is wrong\n", core);
465 return;
466 }
467
468 nss_qrfs_notify[core].qrfs_callback = NULL;
469 nss_qrfs_notify[core].app_data = NULL;
470}
471
472/*
473 * nss_qrfs_init()
474 */
475void nss_qrfs_init(void)
476{
477 int core;
478
479 for (core = 0; core < NSS_CORE_MAX; core++) {
480 nss_qrfs_notify_register(core, NULL, NULL);
481 }
482}