1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2020 Intel Corporation
3 */
4
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include <rte_common.h>
11 #include <rte_ethdev.h>
12 #include <rte_swx_port_ethdev.h>
13 #include <rte_swx_port_source_sink.h>
14 #include <rte_swx_pipeline.h>
15 #include <rte_swx_ctl.h>
16
17 #include "cli.h"
18
19 #include "obj.h"
20 #include "thread.h"
21
22 #ifndef CMD_MAX_TOKENS
23 #define CMD_MAX_TOKENS 256
24 #endif
25
26 #define MSG_OUT_OF_MEMORY "Not enough memory.\n"
27 #define MSG_CMD_UNKNOWN "Unknown command \"%s\".\n"
28 #define MSG_CMD_UNIMPLEM "Command \"%s\" not implemented.\n"
29 #define MSG_ARG_NOT_ENOUGH "Not enough arguments for command \"%s\".\n"
30 #define MSG_ARG_TOO_MANY "Too many arguments for command \"%s\".\n"
31 #define MSG_ARG_MISMATCH "Wrong number of arguments for command \"%s\".\n"
32 #define MSG_ARG_NOT_FOUND "Argument \"%s\" not found.\n"
33 #define MSG_ARG_INVALID "Invalid value for argument \"%s\".\n"
34 #define MSG_FILE_ERR "Error in file \"%s\" at line %u.\n"
35 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
36 #define MSG_CMD_FAIL "Command \"%s\" failed.\n"
37
38 #define skip_white_spaces(pos) \
39 ({ \
40 __typeof__(pos) _p = (pos); \
41 for ( ; isspace(*_p); _p++) \
42 ; \
43 _p; \
44 })
45
46 static int
parser_read_uint64(uint64_t * value,const char * p)47 parser_read_uint64(uint64_t *value, const char *p)
48 {
49 char *next;
50 uint64_t val;
51
52 p = skip_white_spaces(p);
53 if (!isdigit(*p))
54 return -EINVAL;
55
56 val = strtoul(p, &next, 10);
57 if (p == next)
58 return -EINVAL;
59
60 p = next;
61 switch (*p) {
62 case 'T':
63 val *= 1024ULL;
64 /* fall through */
65 case 'G':
66 val *= 1024ULL;
67 /* fall through */
68 case 'M':
69 val *= 1024ULL;
70 /* fall through */
71 case 'k':
72 case 'K':
73 val *= 1024ULL;
74 p++;
75 break;
76 }
77
78 p = skip_white_spaces(p);
79 if (*p != '\0')
80 return -EINVAL;
81
82 *value = val;
83 return 0;
84 }
85
86 static int
parser_read_uint32(uint32_t * value,const char * p)87 parser_read_uint32(uint32_t *value, const char *p)
88 {
89 uint64_t val = 0;
90 int ret = parser_read_uint64(&val, p);
91
92 if (ret < 0)
93 return ret;
94
95 if (val > UINT32_MAX)
96 return -ERANGE;
97
98 *value = val;
99 return 0;
100 }
101
102 static int
parser_read_uint16(uint16_t * value,const char * p)103 parser_read_uint16(uint16_t *value, const char *p)
104 {
105 uint64_t val = 0;
106 int ret = parser_read_uint64(&val, p);
107
108 if (ret < 0)
109 return ret;
110
111 if (val > UINT16_MAX)
112 return -ERANGE;
113
114 *value = val;
115 return 0;
116 }
117
118 #define PARSE_DELIMITER " \f\n\r\t\v"
119
120 static int
parse_tokenize_string(char * string,char * tokens[],uint32_t * n_tokens)121 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
122 {
123 uint32_t i;
124
125 if ((string == NULL) ||
126 (tokens == NULL) ||
127 (*n_tokens < 1))
128 return -EINVAL;
129
130 for (i = 0; i < *n_tokens; i++) {
131 tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
132 if (tokens[i] == NULL)
133 break;
134 }
135
136 if ((i == *n_tokens) && strtok_r(string, PARSE_DELIMITER, &string))
137 return -E2BIG;
138
139 *n_tokens = i;
140 return 0;
141 }
142
143 static int
is_comment(char * in)144 is_comment(char *in)
145 {
146 if ((strlen(in) && index("!#%;", in[0])) ||
147 (strncmp(in, "//", 2) == 0) ||
148 (strncmp(in, "--", 2) == 0))
149 return 1;
150
151 return 0;
152 }
153
154 static const char cmd_mempool_help[] =
155 "mempool <mempool_name>\n"
156 " buffer <buffer_size>\n"
157 " pool <pool_size>\n"
158 " cache <cache_size>\n"
159 " cpu <cpu_id>\n";
160
161 static void
cmd_mempool(char ** tokens,uint32_t n_tokens,char * out,size_t out_size,void * obj)162 cmd_mempool(char **tokens,
163 uint32_t n_tokens,
164 char *out,
165 size_t out_size,
166 void *obj)
167 {
168 struct mempool_params p;
169 char *name;
170 struct mempool *mempool;
171
172 if (n_tokens != 10) {
173 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
174 return;
175 }
176
177 name = tokens[1];
178
179 if (strcmp(tokens[2], "buffer") != 0) {
180 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
181 return;
182 }
183
184 if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
185 snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
186 return;
187 }
188
189 if (strcmp(tokens[4], "pool") != 0) {
190 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
191 return;
192 }
193
194 if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
195 snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
196 return;
197 }
198
199 if (strcmp(tokens[6], "cache") != 0) {
200 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
201 return;
202 }
203
204 if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
205 snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
206 return;
207 }
208
209 if (strcmp(tokens[8], "cpu") != 0) {
210 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
211 return;
212 }
213
214 if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
215 snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
216 return;
217 }
218
219 mempool = mempool_create(obj, name, &p);
220 if (mempool == NULL) {
221 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
222 return;
223 }
224 }
225
226 static const char cmd_link_help[] =
227 "link <link_name>\n"
228 " dev <device_name> | port <port_id>\n"
229 " rxq <n_queues> <queue_size> <mempool_name>\n"
230 " txq <n_queues> <queue_size>\n"
231 " promiscuous on | off\n"
232 " [rss <qid_0> ... <qid_n>]\n";
233
234 static void
cmd_link(char ** tokens,uint32_t n_tokens,char * out,size_t out_size,void * obj)235 cmd_link(char **tokens,
236 uint32_t n_tokens,
237 char *out,
238 size_t out_size,
239 void *obj)
240 {
241 struct link_params p;
242 struct link_params_rss rss;
243 struct link *link;
244 char *name;
245
246 memset(&p, 0, sizeof(p));
247
248 if ((n_tokens < 13) || (n_tokens > 14 + LINK_RXQ_RSS_MAX)) {
249 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
250 return;
251 }
252 name = tokens[1];
253
254 if (strcmp(tokens[2], "dev") == 0)
255 p.dev_name = tokens[3];
256 else if (strcmp(tokens[2], "port") == 0) {
257 p.dev_name = NULL;
258
259 if (parser_read_uint16(&p.port_id, tokens[3]) != 0) {
260 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
261 return;
262 }
263 } else {
264 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
265 return;
266 }
267
268 if (strcmp(tokens[4], "rxq") != 0) {
269 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
270 return;
271 }
272
273 if (parser_read_uint32(&p.rx.n_queues, tokens[5]) != 0) {
274 snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
275 return;
276 }
277 if (parser_read_uint32(&p.rx.queue_size, tokens[6]) != 0) {
278 snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
279 return;
280 }
281
282 p.rx.mempool_name = tokens[7];
283
284 if (strcmp(tokens[8], "txq") != 0) {
285 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
286 return;
287 }
288
289 if (parser_read_uint32(&p.tx.n_queues, tokens[9]) != 0) {
290 snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
291 return;
292 }
293
294 if (parser_read_uint32(&p.tx.queue_size, tokens[10]) != 0) {
295 snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
296 return;
297 }
298
299 if (strcmp(tokens[11], "promiscuous") != 0) {
300 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
301 return;
302 }
303
304 if (strcmp(tokens[12], "on") == 0)
305 p.promiscuous = 1;
306 else if (strcmp(tokens[12], "off") == 0)
307 p.promiscuous = 0;
308 else {
309 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
310 return;
311 }
312
313 /* RSS */
314 p.rx.rss = NULL;
315 if (n_tokens > 13) {
316 uint32_t queue_id, i;
317
318 if (strcmp(tokens[13], "rss") != 0) {
319 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
320 return;
321 }
322
323 p.rx.rss = &rss;
324
325 rss.n_queues = 0;
326 for (i = 14; i < n_tokens; i++) {
327 if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
328 snprintf(out, out_size, MSG_ARG_INVALID,
329 "queue_id");
330 return;
331 }
332
333 rss.queue_id[rss.n_queues] = queue_id;
334 rss.n_queues++;
335 }
336 }
337
338 link = link_create(obj, name, &p);
339 if (link == NULL) {
340 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
341 return;
342 }
343 }
344
345 /* Print the link stats and info */
346 static void
print_link_info(struct link * link,char * out,size_t out_size)347 print_link_info(struct link *link, char *out, size_t out_size)
348 {
349 struct rte_eth_stats stats;
350 struct rte_ether_addr mac_addr;
351 struct rte_eth_link eth_link;
352 uint16_t mtu;
353 int ret;
354
355 memset(&stats, 0, sizeof(stats));
356 rte_eth_stats_get(link->port_id, &stats);
357
358 ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
359 if (ret != 0) {
360 snprintf(out, out_size, "\n%s: MAC address get failed: %s",
361 link->name, rte_strerror(-ret));
362 return;
363 }
364
365 ret = rte_eth_link_get(link->port_id, ð_link);
366 if (ret < 0) {
367 snprintf(out, out_size, "\n%s: link get failed: %s",
368 link->name, rte_strerror(-ret));
369 return;
370 }
371
372 rte_eth_dev_get_mtu(link->port_id, &mtu);
373
374 snprintf(out, out_size,
375 "\n"
376 "%s: flags=<%s> mtu %u\n"
377 "\tether %02X:%02X:%02X:%02X:%02X:%02X rxqueues %u txqueues %u\n"
378 "\tport# %u speed %s\n"
379 "\tRX packets %" PRIu64" bytes %" PRIu64"\n"
380 "\tRX errors %" PRIu64" missed %" PRIu64" no-mbuf %" PRIu64"\n"
381 "\tTX packets %" PRIu64" bytes %" PRIu64"\n"
382 "\tTX errors %" PRIu64"\n",
383 link->name,
384 eth_link.link_status == 0 ? "DOWN" : "UP",
385 mtu,
386 mac_addr.addr_bytes[0], mac_addr.addr_bytes[1],
387 mac_addr.addr_bytes[2], mac_addr.addr_bytes[3],
388 mac_addr.addr_bytes[4], mac_addr.addr_bytes[5],
389 link->n_rxq,
390 link->n_txq,
391 link->port_id,
392 rte_eth_link_speed_to_str(eth_link.link_speed),
393 stats.ipackets,
394 stats.ibytes,
395 stats.ierrors,
396 stats.imissed,
397 stats.rx_nombuf,
398 stats.opackets,
399 stats.obytes,
400 stats.oerrors);
401 }
402
403 /*
404 * link show [<link_name>]
405 */
406 static void
cmd_link_show(char ** tokens,uint32_t n_tokens,char * out,size_t out_size,void * obj)407 cmd_link_show(char **tokens,
408 uint32_t n_tokens,
409 char *out,
410 size_t out_size,
411 void *obj)
412 {
413 struct link *link;
414 char *link_name;
415
416 if (n_tokens != 2 && n_tokens != 3) {
417 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
418 return;
419 }
420
421 if (n_tokens == 2) {
422 link = link_next(obj, NULL);
423
424 while (link != NULL) {
425 out_size = out_size - strlen(out);
426 out = &out[strlen(out)];
427
428 print_link_info(link, out, out_size);
429 link = link_next(obj, link);
430 }
431 } else {
432 out_size = out_size - strlen(out);
433 out = &out[strlen(out)];
434
435 link_name = tokens[2];
436 link = link_find(obj, link_name);
437
438 if (link == NULL) {
439 snprintf(out, out_size, MSG_ARG_INVALID,
440 "Link does not exist");
441 return;
442 }
443 print_link_info(link, out, out_size);
444 }
445 }
446
447 static const char cmd_pipeline_create_help[] =
448 "pipeline <pipeline_name> create <numa_node>\n";
449
450 static void
cmd_pipeline_create(char ** tokens,uint32_t n_tokens,char * out,size_t out_size,void * obj)451 cmd_pipeline_create(char **tokens,
452 uint32_t n_tokens,
453 char *out,
454 size_t out_size,
455 void *obj)
456 {
457 struct pipeline *p;
458 char *name;
459 uint32_t numa_node;
460
461 if (n_tokens != 4) {
462 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
463 return;
464 }
465
466 name = tokens[1];
467
468 if (parser_read_uint32(&numa_node, tokens[3]) != 0) {
469 snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
470 return;
471 }
472
473 p = pipeline_create(obj, name, (int)numa_node);
474 if (!p) {
475 snprintf(out, out_size, "pipeline create error.");
476 return;
477 }
478 }
479
480 static const char cmd_pipeline_port_in_help[] =
481 "pipeline <pipeline_name> port in <port_id>\n"
482 " link <link_name> rxq <queue_id> bsz <burst_size>\n"
483 " | source <mempool_name> <file_name>\n";
484
485 static void
cmd_pipeline_port_in(char ** tokens,uint32_t n_tokens,char * out,size_t out_size,void * obj)486 cmd_pipeline_port_in(char **tokens,
487 uint32_t n_tokens,
488 char *out,
489 size_t out_size,
490 void *obj)
491 {
492 struct pipeline *p;
493 int status;
494 uint32_t port_id = 0, t0;
495
496 if (n_tokens < 6) {
497 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
498 return;
499 }
500
501 p = pipeline_find(obj, tokens[1]);
502 if (!p || p->ctl) {
503 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
504 return;
505 }
506
507 if (strcmp(tokens[2], "port") != 0) {
508 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
509 return;
510 }
511
512 if (strcmp(tokens[3], "in") != 0) {
513 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
514 return;
515 }
516
517 if (parser_read_uint32(&port_id, tokens[4]) != 0) {
518 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
519 return;
520 }
521
522 t0 = 5;
523
524 if (strcmp(tokens[t0], "link") == 0) {
525 struct rte_swx_port_ethdev_reader_params params;
526 struct link *link;
527
528 if (n_tokens < t0 + 6) {
529 snprintf(out, out_size, MSG_ARG_MISMATCH,
530 "pipeline port in link");
531 return;
532 }
533
534 link = link_find(obj, tokens[t0 + 1]);
535 if (!link) {
536 snprintf(out, out_size, MSG_ARG_INVALID,
537 "link_name");
538 return;
539 }
540 params.dev_name = link->dev_name;
541
542 if (strcmp(tokens[t0 + 2], "rxq") != 0) {
543 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
544 return;
545 }
546
547 if (parser_read_uint16(¶ms.queue_id, tokens[t0 + 3]) != 0) {
548 snprintf(out, out_size, MSG_ARG_INVALID,
549 "queue_id");
550 return;
551 }
552
553 if (strcmp(tokens[t0 + 4], "bsz") != 0) {
554 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
555 return;
556 }
557
558 if (parser_read_uint32(¶ms.burst_size, tokens[t0 + 5])) {
559 snprintf(out, out_size, MSG_ARG_INVALID,
560 "burst_size");
561 return;
562 }
563
564 t0 += 6;
565
566 status = rte_swx_pipeline_port_in_config(p->p,
567 port_id,
568 "ethdev",
569 ¶ms);
570 } else if (strcmp(tokens[t0], "source") == 0) {
571 struct rte_swx_port_source_params params;
572 struct mempool *mp;
573
574 if (n_tokens < t0 + 3) {
575 snprintf(out, out_size, MSG_ARG_MISMATCH,
576 "pipeline port in source");
577 return;
578 }
579
580 mp = mempool_find(obj, tokens[t0 + 1]);
581 if (!mp) {
582 snprintf(out, out_size, MSG_ARG_INVALID,
583 "mempool_name");
584 return;
585 }
586 params.pool = mp->m;
587
588 params.file_name = tokens[t0 + 2];
589
590 t0 += 3;
591
592 status = rte_swx_pipeline_port_in_config(p->p,
593 port_id,
594 "source",
595 ¶ms);
596 } else {
597 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
598 return;
599 }
600
601 if (status) {
602 snprintf(out, out_size, "port in error.");
603 return;
604 }
605
606 if (n_tokens != t0) {
607 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
608 return;
609 }
610 }
611
612 static const char cmd_pipeline_port_out_help[] =
613 "pipeline <pipeline_name> port out <port_id>\n"
614 " link <link_name> txq <txq_id> bsz <burst_size>\n"
615 " | sink <file_name> | none\n";
616
617 static void
cmd_pipeline_port_out(char ** tokens,uint32_t n_tokens,char * out,size_t out_size,void * obj)618 cmd_pipeline_port_out(char **tokens,
619 uint32_t n_tokens,
620 char *out,
621 size_t out_size,
622 void *obj)
623 {
624 struct pipeline *p;
625 int status;
626 uint32_t port_id = 0, t0;
627
628 if (n_tokens < 6) {
629 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
630 return;
631 }
632
633 p = pipeline_find(obj, tokens[1]);
634 if (!p || p->ctl) {
635 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
636 return;
637 }
638
639 if (strcmp(tokens[2], "port") != 0) {
640 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
641 return;
642 }
643
644 if (strcmp(tokens[3], "out") != 0) {
645 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
646 return;
647 }
648
649 if (parser_read_uint32(&port_id, tokens[4]) != 0) {
650 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
651 return;
652 }
653
654 t0 = 5;
655
656 if (strcmp(tokens[t0], "link") == 0) {
657 struct rte_swx_port_ethdev_writer_params params;
658 struct link *link;
659
660 if (n_tokens < t0 + 6) {
661 snprintf(out, out_size, MSG_ARG_MISMATCH,
662 "pipeline port out link");
663 return;
664 }
665
666 link = link_find(obj, tokens[t0 + 1]);
667 if (!link) {
668 snprintf(out, out_size, MSG_ARG_INVALID,
669 "link_name");
670 return;
671 }
672 params.dev_name = link->dev_name;
673
674 if (strcmp(tokens[t0 + 2], "txq") != 0) {
675 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
676 return;
677 }
678
679 if (parser_read_uint16(¶ms.queue_id, tokens[t0 + 3]) != 0) {
680 snprintf(out, out_size, MSG_ARG_INVALID,
681 "queue_id");
682 return;
683 }
684
685 if (strcmp(tokens[t0 + 4], "bsz") != 0) {
686 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
687 return;
688 }
689
690 if (parser_read_uint32(¶ms.burst_size, tokens[t0 + 5])) {
691 snprintf(out, out_size, MSG_ARG_INVALID,
692 "burst_size");
693 return;
694 }
695
696 t0 += 6;
697
698 status = rte_swx_pipeline_port_out_config(p->p,
699 port_id,
700 "ethdev",
701 ¶ms);
702 } else if (strcmp(tokens[t0], "sink") == 0) {
703 struct rte_swx_port_sink_params params;
704
705 params.file_name = strcmp(tokens[t0 + 1], "none") ?
706 tokens[t0 + 1] : NULL;
707
708 t0 += 2;
709
710 status = rte_swx_pipeline_port_out_config(p->p,
711 port_id,
712 "sink",
713 ¶ms);
714 } else {
715 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
716 return;
717 }
718
719 if (status) {
720 snprintf(out, out_size, "port out error.");
721 return;
722 }
723
724 if (n_tokens != t0) {
725 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
726 return;
727 }
728 }
729
730 static const char cmd_pipeline_build_help[] =
731 "pipeline <pipeline_name> build <spec_file>\n";
732
733 static void
cmd_pipeline_build(char ** tokens,uint32_t n_tokens,char * out,size_t out_size,void * obj)734 cmd_pipeline_build(char **tokens,
735 uint32_t n_tokens,
736 char *out,
737 size_t out_size,
738 void *obj)
739 {
740 struct pipeline *p = NULL;
741 FILE *spec = NULL;
742 uint32_t err_line;
743 const char *err_msg;
744 int status;
745
746 if (n_tokens != 4) {
747 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
748 return;
749 }
750
751 p = pipeline_find(obj, tokens[1]);
752 if (!p || p->ctl) {
753 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
754 return;
755 }
756
757 spec = fopen(tokens[3], "r");
758 if (!spec) {
759 snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
760 return;
761 }
762
763 status = rte_swx_pipeline_build_from_spec(p->p,
764 spec,
765 &err_line,
766 &err_msg);
767 fclose(spec);
768 if (status) {
769 snprintf(out, out_size, "Error %d at line %u: %s\n.",
770 status, err_line, err_msg);
771 return;
772 }
773
774 p->ctl = rte_swx_ctl_pipeline_create(p->p);
775 if (!p->ctl) {
776 snprintf(out, out_size, "Pipeline control create failed.");
777 rte_swx_pipeline_free(p->p);
778 return;
779 }
780 }
781
782 static void
table_entry_free(struct rte_swx_table_entry * entry)783 table_entry_free(struct rte_swx_table_entry *entry)
784 {
785 if (!entry)
786 return;
787
788 free(entry->key);
789 free(entry->key_mask);
790 free(entry->action_data);
791 free(entry);
792 }
793
794 static const char cmd_pipeline_table_update_help[] =
795 "pipeline <pipeline_name> table <table_name> update <file_name_add> "
796 "<file_name_delete> <file_name_default>";
797
798 static void
cmd_pipeline_table_update(char ** tokens,uint32_t n_tokens,char * out,size_t out_size,void * obj)799 cmd_pipeline_table_update(char **tokens,
800 uint32_t n_tokens,
801 char *out,
802 size_t out_size,
803 void *obj)
804 {
805 struct pipeline *p;
806 char *pipeline_name, *table_name, *line = NULL;
807 char *file_name_add, *file_name_delete, *file_name_default;
808 FILE *file_add = NULL, *file_delete = NULL, *file_default = NULL;
809 uint32_t line_id;
810 int status;
811
812 if (n_tokens != 8) {
813 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
814 return;
815 }
816
817 pipeline_name = tokens[1];
818 p = pipeline_find(obj, pipeline_name);
819 if (!p || !p->ctl) {
820 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
821 return;
822 }
823
824 if (strcmp(tokens[2], "table") != 0) {
825 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
826 return;
827 }
828
829 table_name = tokens[3];
830
831 if (strcmp(tokens[4], "update") != 0) {
832 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "update");
833 return;
834 }
835
836 file_name_add = tokens[5];
837 file_name_delete = tokens[6];
838 file_name_default = tokens[7];
839
840 /* File open. */
841 if (strcmp(file_name_add, "none")) {
842 file_add = fopen(file_name_add, "r");
843 if (!file_add) {
844 snprintf(out, out_size, "Cannot open file %s",
845 file_name_add);
846 goto error;
847 }
848 }
849
850 if (strcmp(file_name_delete, "none")) {
851 file_delete = fopen(file_name_delete, "r");
852 if (!file_delete) {
853 snprintf(out, out_size, "Cannot open file %s",
854 file_name_delete);
855 goto error;
856 }
857 }
858
859 if (strcmp(file_name_default, "none")) {
860 file_default = fopen(file_name_default, "r");
861 if (!file_default) {
862 snprintf(out, out_size, "Cannot open file %s",
863 file_name_default);
864 goto error;
865 }
866 }
867
868 if (!file_add && !file_delete && !file_default) {
869 snprintf(out, out_size, "Nothing to be done.");
870 return;
871 }
872
873 /* Buffer allocation. */
874 line = malloc(2048);
875 if (!line) {
876 snprintf(out, out_size, MSG_OUT_OF_MEMORY);
877 goto error;
878 }
879
880 /* Add. */
881 if (file_add)
882 for (line_id = 1; ; line_id++) {
883 struct rte_swx_table_entry *entry;
884
885 if (fgets(line, 2048, file_add) == NULL)
886 break;
887
888 entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
889 table_name,
890 line);
891 if (!entry) {
892 snprintf(out, out_size, MSG_FILE_ERR,
893 file_name_add, line_id);
894 goto error;
895 }
896
897 status = rte_swx_ctl_pipeline_table_entry_add(p->ctl,
898 table_name,
899 entry);
900 table_entry_free(entry);
901 if (status) {
902 snprintf(out, out_size,
903 "Invalid entry in file %s at line %u",
904 file_name_add, line_id);
905 goto error;
906 }
907 }
908
909
910 /* Delete. */
911 if (file_delete)
912 for (line_id = 1; ; line_id++) {
913 struct rte_swx_table_entry *entry;
914
915 if (fgets(line, 2048, file_delete) == NULL)
916 break;
917
918 entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
919 table_name,
920 line);
921 if (!entry) {
922 snprintf(out, out_size, MSG_FILE_ERR,
923 file_name_delete, line_id);
924 goto error;
925 }
926
927 status = rte_swx_ctl_pipeline_table_entry_delete(p->ctl,
928 table_name,
929 entry);
930 table_entry_free(entry);
931 if (status) {
932 snprintf(out, out_size,
933 "Invalid entry in file %s at line %u",
934 file_name_delete, line_id);
935 goto error;
936 }
937 }
938
939 /* Default. */
940 if (file_default)
941 for (line_id = 1; ; line_id++) {
942 struct rte_swx_table_entry *entry;
943
944 if (fgets(line, 2048, file_default) == NULL)
945 break;
946
947 entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
948 table_name,
949 line);
950 if (!entry) {
951 snprintf(out, out_size, MSG_FILE_ERR,
952 file_name_default, line_id);
953 goto error;
954 }
955
956 status = rte_swx_ctl_pipeline_table_default_entry_add(p->ctl,
957 table_name,
958 entry);
959 table_entry_free(entry);
960 if (status) {
961 snprintf(out, out_size,
962 "Invalid entry in file %s at line %u",
963 file_name_default, line_id);
964 goto error;
965 }
966 }
967
968 status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
969 if (status) {
970 snprintf(out, out_size, "Commit failed.");
971 goto error;
972 }
973
974
975 rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name);
976
977 free(line);
978 if (file_add)
979 fclose(file_add);
980 if (file_delete)
981 fclose(file_delete);
982 if (file_default)
983 fclose(file_default);
984 return;
985
986 error:
987 rte_swx_ctl_pipeline_abort(p->ctl);
988 free(line);
989 if (file_add)
990 fclose(file_add);
991 if (file_delete)
992 fclose(file_delete);
993 if (file_default)
994 fclose(file_default);
995 }
996
997 static const char cmd_pipeline_stats_help[] =
998 "pipeline <pipeline_name> stats\n";
999
1000 static void
cmd_pipeline_stats(char ** tokens,uint32_t n_tokens,char * out,size_t out_size,void * obj)1001 cmd_pipeline_stats(char **tokens,
1002 uint32_t n_tokens,
1003 char *out,
1004 size_t out_size,
1005 void *obj)
1006 {
1007 struct rte_swx_ctl_pipeline_info info;
1008 struct pipeline *p;
1009 uint32_t i;
1010 int status;
1011
1012 if (n_tokens != 3) {
1013 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1014 return;
1015 }
1016
1017 p = pipeline_find(obj, tokens[1]);
1018 if (!p || !p->ctl) {
1019 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1020 return;
1021 }
1022
1023 if (strcmp(tokens[2], "stats")) {
1024 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1025 return;
1026 }
1027
1028 status = rte_swx_ctl_pipeline_info_get(p->p, &info);
1029 if (status) {
1030 snprintf(out, out_size, "Pipeline info get error.");
1031 return;
1032 }
1033
1034 snprintf(out, out_size, "Input ports:\n");
1035 out_size -= strlen(out);
1036 out += strlen(out);
1037
1038 for (i = 0; i < info.n_ports_in; i++) {
1039 struct rte_swx_port_in_stats stats;
1040
1041 rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
1042
1043 snprintf(out, out_size, "\tPort %u:"
1044 " packets %" PRIu64
1045 " bytes %" PRIu64
1046 " empty %" PRIu64 "\n",
1047 i, stats.n_pkts, stats.n_bytes, stats.n_empty);
1048 out_size -= strlen(out);
1049 out += strlen(out);
1050 }
1051
1052 snprintf(out, out_size, "Output ports:\n");
1053 out_size -= strlen(out);
1054 out += strlen(out);
1055
1056 for (i = 0; i < info.n_ports_out; i++) {
1057 struct rte_swx_port_out_stats stats;
1058
1059 rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
1060
1061 snprintf(out, out_size, "\tPort %u:"
1062 " packets %" PRIu64
1063 " bytes %" PRIu64 "\n",
1064 i, stats.n_pkts, stats.n_bytes);
1065 out_size -= strlen(out);
1066 out += strlen(out);
1067 }
1068 }
1069
1070 static const char cmd_thread_pipeline_enable_help[] =
1071 "thread <thread_id> pipeline <pipeline_name> enable\n";
1072
1073 static void
cmd_thread_pipeline_enable(char ** tokens,uint32_t n_tokens,char * out,size_t out_size,void * obj)1074 cmd_thread_pipeline_enable(char **tokens,
1075 uint32_t n_tokens,
1076 char *out,
1077 size_t out_size,
1078 void *obj)
1079 {
1080 char *pipeline_name;
1081 struct pipeline *p;
1082 uint32_t thread_id;
1083 int status;
1084
1085 if (n_tokens != 5) {
1086 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1087 return;
1088 }
1089
1090 if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
1091 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
1092 return;
1093 }
1094
1095 if (strcmp(tokens[2], "pipeline") != 0) {
1096 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
1097 return;
1098 }
1099
1100 pipeline_name = tokens[3];
1101 p = pipeline_find(obj, pipeline_name);
1102 if (!p || !p->ctl) {
1103 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1104 return;
1105 }
1106
1107 if (strcmp(tokens[4], "enable") != 0) {
1108 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
1109 return;
1110 }
1111
1112 status = thread_pipeline_enable(thread_id, obj, pipeline_name);
1113 if (status) {
1114 snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
1115 return;
1116 }
1117 }
1118
1119 static const char cmd_thread_pipeline_disable_help[] =
1120 "thread <thread_id> pipeline <pipeline_name> disable\n";
1121
1122 static void
cmd_thread_pipeline_disable(char ** tokens,uint32_t n_tokens,char * out,size_t out_size,void * obj)1123 cmd_thread_pipeline_disable(char **tokens,
1124 uint32_t n_tokens,
1125 char *out,
1126 size_t out_size,
1127 void *obj)
1128 {
1129 struct pipeline *p;
1130 char *pipeline_name;
1131 uint32_t thread_id;
1132 int status;
1133
1134 if (n_tokens != 5) {
1135 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1136 return;
1137 }
1138
1139 if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
1140 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
1141 return;
1142 }
1143
1144 if (strcmp(tokens[2], "pipeline") != 0) {
1145 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
1146 return;
1147 }
1148
1149 pipeline_name = tokens[3];
1150 p = pipeline_find(obj, pipeline_name);
1151 if (!p || !p->ctl) {
1152 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1153 return;
1154 }
1155
1156 if (strcmp(tokens[4], "disable") != 0) {
1157 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
1158 return;
1159 }
1160
1161 status = thread_pipeline_disable(thread_id, obj, pipeline_name);
1162 if (status) {
1163 snprintf(out, out_size, MSG_CMD_FAIL,
1164 "thread pipeline disable");
1165 return;
1166 }
1167 }
1168
1169 static void
cmd_help(char ** tokens,uint32_t n_tokens,char * out,size_t out_size,void * arg __rte_unused)1170 cmd_help(char **tokens,
1171 uint32_t n_tokens,
1172 char *out,
1173 size_t out_size,
1174 void *arg __rte_unused)
1175 {
1176 tokens++;
1177 n_tokens--;
1178
1179 if (n_tokens == 0) {
1180 snprintf(out, out_size,
1181 "Type 'help <command>' for command details.\n\n"
1182 "List of commands:\n"
1183 "\tmempool\n"
1184 "\tlink\n"
1185 "\tpipeline create\n"
1186 "\tpipeline port in\n"
1187 "\tpipeline port out\n"
1188 "\tpipeline build\n"
1189 "\tpipeline table update\n"
1190 "\tpipeline stats\n"
1191 "\tthread pipeline enable\n"
1192 "\tthread pipeline disable\n\n");
1193 return;
1194 }
1195
1196 if (strcmp(tokens[0], "mempool") == 0) {
1197 snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
1198 return;
1199 }
1200
1201 if (strcmp(tokens[0], "link") == 0) {
1202 snprintf(out, out_size, "\n%s\n", cmd_link_help);
1203 return;
1204 }
1205
1206 if ((strcmp(tokens[0], "pipeline") == 0) &&
1207 (n_tokens == 2) && (strcmp(tokens[1], "create") == 0)) {
1208 snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help);
1209 return;
1210 }
1211
1212 if ((strcmp(tokens[0], "pipeline") == 0) &&
1213 (n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) {
1214 if (strcmp(tokens[2], "in") == 0) {
1215 snprintf(out, out_size, "\n%s\n",
1216 cmd_pipeline_port_in_help);
1217 return;
1218 }
1219
1220 if (strcmp(tokens[2], "out") == 0) {
1221 snprintf(out, out_size, "\n%s\n",
1222 cmd_pipeline_port_out_help);
1223 return;
1224 }
1225 }
1226
1227 if ((strcmp(tokens[0], "pipeline") == 0) &&
1228 (n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
1229 snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
1230 return;
1231 }
1232
1233 if ((strcmp(tokens[0], "pipeline") == 0) &&
1234 (n_tokens == 3) &&
1235 (strcmp(tokens[1], "table") == 0) &&
1236 (strcmp(tokens[2], "update") == 0)) {
1237 snprintf(out, out_size, "\n%s\n",
1238 cmd_pipeline_table_update_help);
1239 return;
1240 }
1241
1242 if ((strcmp(tokens[0], "pipeline") == 0) &&
1243 (n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
1244 snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
1245 return;
1246 }
1247
1248 if ((n_tokens == 3) &&
1249 (strcmp(tokens[0], "thread") == 0) &&
1250 (strcmp(tokens[1], "pipeline") == 0)) {
1251 if (strcmp(tokens[2], "enable") == 0) {
1252 snprintf(out, out_size, "\n%s\n",
1253 cmd_thread_pipeline_enable_help);
1254 return;
1255 }
1256
1257 if (strcmp(tokens[2], "disable") == 0) {
1258 snprintf(out, out_size, "\n%s\n",
1259 cmd_thread_pipeline_disable_help);
1260 return;
1261 }
1262 }
1263
1264 snprintf(out, out_size, "Invalid command\n");
1265 }
1266
1267 void
cli_process(char * in,char * out,size_t out_size,void * obj)1268 cli_process(char *in, char *out, size_t out_size, void *obj)
1269 {
1270 char *tokens[CMD_MAX_TOKENS];
1271 uint32_t n_tokens = RTE_DIM(tokens);
1272 int status;
1273
1274 if (is_comment(in))
1275 return;
1276
1277 status = parse_tokenize_string(in, tokens, &n_tokens);
1278 if (status) {
1279 snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
1280 return;
1281 }
1282
1283 if (n_tokens == 0)
1284 return;
1285
1286 if (strcmp(tokens[0], "help") == 0) {
1287 cmd_help(tokens, n_tokens, out, out_size, obj);
1288 return;
1289 }
1290
1291 if (strcmp(tokens[0], "mempool") == 0) {
1292 cmd_mempool(tokens, n_tokens, out, out_size, obj);
1293 return;
1294 }
1295
1296 if (strcmp(tokens[0], "link") == 0) {
1297 if (strcmp(tokens[1], "show") == 0) {
1298 cmd_link_show(tokens, n_tokens, out, out_size, obj);
1299 return;
1300 }
1301
1302 cmd_link(tokens, n_tokens, out, out_size, obj);
1303 return;
1304 }
1305
1306 if (strcmp(tokens[0], "pipeline") == 0) {
1307 if ((n_tokens >= 3) &&
1308 (strcmp(tokens[2], "create") == 0)) {
1309 cmd_pipeline_create(tokens, n_tokens, out, out_size,
1310 obj);
1311 return;
1312 }
1313
1314 if ((n_tokens >= 4) &&
1315 (strcmp(tokens[2], "port") == 0) &&
1316 (strcmp(tokens[3], "in") == 0)) {
1317 cmd_pipeline_port_in(tokens, n_tokens, out, out_size,
1318 obj);
1319 return;
1320 }
1321
1322 if ((n_tokens >= 4) &&
1323 (strcmp(tokens[2], "port") == 0) &&
1324 (strcmp(tokens[3], "out") == 0)) {
1325 cmd_pipeline_port_out(tokens, n_tokens, out, out_size,
1326 obj);
1327 return;
1328 }
1329
1330 if ((n_tokens >= 3) &&
1331 (strcmp(tokens[2], "build") == 0)) {
1332 cmd_pipeline_build(tokens, n_tokens, out, out_size,
1333 obj);
1334 return;
1335 }
1336
1337 if ((n_tokens >= 3) &&
1338 (strcmp(tokens[2], "table") == 0)) {
1339 cmd_pipeline_table_update(tokens, n_tokens, out,
1340 out_size, obj);
1341 return;
1342 }
1343
1344 if ((n_tokens >= 3) &&
1345 (strcmp(tokens[2], "stats") == 0)) {
1346 cmd_pipeline_stats(tokens, n_tokens, out, out_size,
1347 obj);
1348 return;
1349 }
1350 }
1351
1352 if (strcmp(tokens[0], "thread") == 0) {
1353 if ((n_tokens >= 5) &&
1354 (strcmp(tokens[4], "enable") == 0)) {
1355 cmd_thread_pipeline_enable(tokens, n_tokens,
1356 out, out_size, obj);
1357 return;
1358 }
1359
1360 if ((n_tokens >= 5) &&
1361 (strcmp(tokens[4], "disable") == 0)) {
1362 cmd_thread_pipeline_disable(tokens, n_tokens,
1363 out, out_size, obj);
1364 return;
1365 }
1366 }
1367
1368 snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
1369 }
1370
1371 int
cli_script_process(const char * file_name,size_t msg_in_len_max,size_t msg_out_len_max,void * obj)1372 cli_script_process(const char *file_name,
1373 size_t msg_in_len_max,
1374 size_t msg_out_len_max,
1375 void *obj)
1376 {
1377 char *msg_in = NULL, *msg_out = NULL;
1378 FILE *f = NULL;
1379
1380 /* Check input arguments */
1381 if ((file_name == NULL) ||
1382 (strlen(file_name) == 0) ||
1383 (msg_in_len_max == 0) ||
1384 (msg_out_len_max == 0))
1385 return -EINVAL;
1386
1387 msg_in = malloc(msg_in_len_max + 1);
1388 msg_out = malloc(msg_out_len_max + 1);
1389 if ((msg_in == NULL) ||
1390 (msg_out == NULL)) {
1391 free(msg_out);
1392 free(msg_in);
1393 return -ENOMEM;
1394 }
1395
1396 /* Open input file */
1397 f = fopen(file_name, "r");
1398 if (f == NULL) {
1399 free(msg_out);
1400 free(msg_in);
1401 return -EIO;
1402 }
1403
1404 /* Read file */
1405 for ( ; ; ) {
1406 if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
1407 break;
1408
1409 printf("%s", msg_in);
1410 msg_out[0] = 0;
1411
1412 cli_process(msg_in,
1413 msg_out,
1414 msg_out_len_max,
1415 obj);
1416
1417 if (strlen(msg_out))
1418 printf("%s", msg_out);
1419 }
1420
1421 /* Close file */
1422 fclose(f);
1423 free(msg_out);
1424 free(msg_in);
1425 return 0;
1426 }
1427