examples/pipeline: fix resource leak
[dpdk.git] / examples / pipeline / cli.c
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
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
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
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
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
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
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
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
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, &eth_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
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
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
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(&params.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(&params.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                         &params);
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                         &params);
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
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(&params.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(&params.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                         &params);
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                         &params);
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
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
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
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
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
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
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
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
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
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 }