76a58ee28305cec544ad8cbc3705694ed313b5fa
[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> <fie_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 const char cmd_pipeline_table_update_help[] =
783 "pipeline <pipeline_name> table <table_name> update <file_name_add> "
784 "<file_name_delete> <file_name_default>";
785
786 static void
787 cmd_pipeline_table_update(char **tokens,
788         uint32_t n_tokens,
789         char *out,
790         size_t out_size,
791         void *obj)
792 {
793         struct pipeline *p;
794         char *pipeline_name, *table_name, *line = NULL;
795         char *file_name_add, *file_name_delete, *file_name_default;
796         FILE *file_add = NULL, *file_delete = NULL, *file_default = NULL;
797         uint32_t line_id;
798         int status;
799
800         if (n_tokens != 8) {
801                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
802                 return;
803         }
804
805         pipeline_name = tokens[1];
806         p = pipeline_find(obj, pipeline_name);
807         if (!p || !p->ctl) {
808                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
809                 return;
810         }
811
812         if (strcmp(tokens[2], "table") != 0) {
813                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
814                 return;
815         }
816
817         table_name = tokens[3];
818
819         if (strcmp(tokens[4], "update") != 0) {
820                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "update");
821                 return;
822         }
823
824         file_name_add = tokens[5];
825         file_name_delete = tokens[6];
826         file_name_default = tokens[7];
827
828         /* File open. */
829         if (strcmp(file_name_add, "none")) {
830                 file_add = fopen(file_name_add, "r");
831                 if (!file_add) {
832                         snprintf(out, out_size, "Cannot open file %s",
833                                 file_name_add);
834                         goto error;
835                 }
836         }
837
838         if (strcmp(file_name_delete, "none")) {
839                 file_delete = fopen(file_name_delete, "r");
840                 if (!file_delete) {
841                         snprintf(out, out_size, "Cannot open file %s",
842                                 file_name_delete);
843                         goto error;
844                 }
845         }
846
847         if (strcmp(file_name_default, "none")) {
848                 file_default = fopen(file_name_default, "r");
849                 if (!file_default) {
850                         snprintf(out, out_size, "Cannot open file %s",
851                                 file_name_default);
852                         goto error;
853                 }
854         }
855
856         if (!file_add && !file_delete && !file_default) {
857                 snprintf(out, out_size, "Nothing to be done.");
858                 return;
859         }
860
861         /* Buffer allocation. */
862         line = malloc(2048);
863         if (!line) {
864                 snprintf(out, out_size, MSG_OUT_OF_MEMORY);
865                 goto error;
866         }
867
868         /* Add. */
869         if (file_add) {
870                 for (line_id = 1; ; line_id++) {
871                         struct rte_swx_table_entry *entry;
872
873                         if (fgets(line, 2048, file_add) == NULL)
874                                 break;
875
876                         entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
877                                 table_name,
878                                 line);
879                         if (!entry) {
880                                 snprintf(out, out_size, MSG_FILE_ERR,
881                                         file_name_add, line_id);
882                                 goto error;
883                         }
884
885                         status = rte_swx_ctl_pipeline_table_entry_add(p->ctl,
886                                 table_name,
887                                 entry);
888                         if (status) {
889                                 snprintf(out, out_size,
890                                         "Invalid entry in file %s at line %u",
891                                         file_name_add, line_id);
892                                 goto error;
893                         }
894                 }
895
896                 fclose(file_add);
897         }
898
899         /* Delete. */
900         if (file_delete) {
901                 for (line_id = 1; ; line_id++) {
902                         struct rte_swx_table_entry *entry;
903
904                         if (fgets(line, 2048, file_delete) == NULL)
905                                 break;
906
907                         entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
908                                 table_name,
909                                 line);
910                         if (!entry) {
911                                 snprintf(out, out_size, MSG_FILE_ERR,
912                                         file_name_delete, line_id);
913                                 goto error;
914                         }
915
916                         status = rte_swx_ctl_pipeline_table_entry_delete(p->ctl,
917                                 table_name,
918                                 entry);
919                         if (status)  {
920                                 snprintf(out, out_size,
921                                         "Invalid entry in file %s at line %u",
922                                         file_name_delete, line_id);
923                                 goto error;
924                         }
925                 }
926
927                 fclose(file_delete);
928         }
929
930         /* Default. */
931         if (file_default) {
932                 for (line_id = 1; ; line_id++) {
933                         struct rte_swx_table_entry *entry;
934
935                         if (fgets(line, 2048, file_default) == NULL)
936                                 break;
937
938                         entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
939                                 table_name,
940                                 line);
941                         if (!entry) {
942                                 snprintf(out, out_size, MSG_FILE_ERR,
943                                         file_name_default, line_id);
944                                 goto error;
945                         }
946
947                         status = rte_swx_ctl_pipeline_table_default_entry_add(p->ctl,
948                                 table_name,
949                                 entry);
950                         if (status) {
951                                 snprintf(out, out_size,
952                                         "Invalid entry in file %s at line %u",
953                                         file_name_default, line_id);
954                                 goto error;
955                         }
956                 }
957
958                 fclose(file_default);
959         }
960
961         status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
962         if (status) {
963                 snprintf(out, out_size, "Commit failed.");
964                 goto error;
965         }
966
967         free(line);
968
969         rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name);
970
971         return;
972
973 error:
974         rte_swx_ctl_pipeline_abort(p->ctl);
975         free(line);
976         if (file_add)
977                 fclose(file_add);
978         if (file_delete)
979                 fclose(file_delete);
980         if (file_default)
981                 fclose(file_default);
982 }
983
984 static const char cmd_pipeline_stats_help[] =
985 "pipeline <pipeline_name> stats\n";
986
987 static void
988 cmd_pipeline_stats(char **tokens,
989         uint32_t n_tokens,
990         char *out,
991         size_t out_size,
992         void *obj)
993 {
994         struct rte_swx_ctl_pipeline_info info;
995         struct pipeline *p;
996         uint32_t i;
997         int status;
998
999         if (n_tokens != 3) {
1000                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1001                 return;
1002         }
1003
1004         p = pipeline_find(obj, tokens[1]);
1005         if (!p || !p->ctl) {
1006                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1007                 return;
1008         }
1009
1010         if (strcmp(tokens[2], "stats")) {
1011                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1012                 return;
1013         }
1014
1015         status = rte_swx_ctl_pipeline_info_get(p->p, &info);
1016         if (status) {
1017                 snprintf(out, out_size, "Pipeline info get error.");
1018                 return;
1019         }
1020
1021         snprintf(out, out_size, "Input ports:\n");
1022         out_size -= strlen(out);
1023         out += strlen(out);
1024
1025         for (i = 0; i < info.n_ports_in; i++) {
1026                 struct rte_swx_port_in_stats stats;
1027
1028                 rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
1029
1030                 snprintf(out, out_size, "\tPort %u:"
1031                         " packets %" PRIu64
1032                         " bytes %" PRIu64
1033                         " empty %" PRIu64 "\n",
1034                         i, stats.n_pkts, stats.n_bytes, stats.n_empty);
1035                 out_size -= strlen(out);
1036                 out += strlen(out);
1037         }
1038
1039         snprintf(out, out_size, "Output ports:\n");
1040         out_size -= strlen(out);
1041         out += strlen(out);
1042
1043         for (i = 0; i < info.n_ports_out; i++) {
1044                 struct rte_swx_port_out_stats stats;
1045
1046                 rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
1047
1048                 snprintf(out, out_size, "\tPort %u:"
1049                         " packets %" PRIu64
1050                         " bytes %" PRIu64 "\n",
1051                         i, stats.n_pkts, stats.n_bytes);
1052                 out_size -= strlen(out);
1053                 out += strlen(out);
1054         }
1055 }
1056
1057 static const char cmd_thread_pipeline_enable_help[] =
1058 "thread <thread_id> pipeline <pipeline_name> enable\n";
1059
1060 static void
1061 cmd_thread_pipeline_enable(char **tokens,
1062         uint32_t n_tokens,
1063         char *out,
1064         size_t out_size,
1065         void *obj)
1066 {
1067         char *pipeline_name;
1068         struct pipeline *p;
1069         uint32_t thread_id;
1070         int status;
1071
1072         if (n_tokens != 5) {
1073                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1074                 return;
1075         }
1076
1077         if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
1078                 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
1079                 return;
1080         }
1081
1082         if (strcmp(tokens[2], "pipeline") != 0) {
1083                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
1084                 return;
1085         }
1086
1087         pipeline_name = tokens[3];
1088         p = pipeline_find(obj, pipeline_name);
1089         if (!p || !p->ctl) {
1090                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1091                 return;
1092         }
1093
1094         if (strcmp(tokens[4], "enable") != 0) {
1095                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
1096                 return;
1097         }
1098
1099         status = thread_pipeline_enable(thread_id, obj, pipeline_name);
1100         if (status) {
1101                 snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
1102                 return;
1103         }
1104 }
1105
1106 static const char cmd_thread_pipeline_disable_help[] =
1107 "thread <thread_id> pipeline <pipeline_name> disable\n";
1108
1109 static void
1110 cmd_thread_pipeline_disable(char **tokens,
1111         uint32_t n_tokens,
1112         char *out,
1113         size_t out_size,
1114         void *obj)
1115 {
1116         struct pipeline *p;
1117         char *pipeline_name;
1118         uint32_t thread_id;
1119         int status;
1120
1121         if (n_tokens != 5) {
1122                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1123                 return;
1124         }
1125
1126         if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
1127                 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
1128                 return;
1129         }
1130
1131         if (strcmp(tokens[2], "pipeline") != 0) {
1132                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
1133                 return;
1134         }
1135
1136         pipeline_name = tokens[3];
1137         p = pipeline_find(obj, pipeline_name);
1138         if (!p || !p->ctl) {
1139                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1140                 return;
1141         }
1142
1143         if (strcmp(tokens[4], "disable") != 0) {
1144                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
1145                 return;
1146         }
1147
1148         status = thread_pipeline_disable(thread_id, obj, pipeline_name);
1149         if (status) {
1150                 snprintf(out, out_size, MSG_CMD_FAIL,
1151                         "thread pipeline disable");
1152                 return;
1153         }
1154 }
1155
1156 static void
1157 cmd_help(char **tokens,
1158          uint32_t n_tokens,
1159          char *out,
1160          size_t out_size,
1161          void *arg __rte_unused)
1162 {
1163         tokens++;
1164         n_tokens--;
1165
1166         if (n_tokens == 0) {
1167                 snprintf(out, out_size,
1168                         "Type 'help <command>' for command details.\n\n");
1169                 return;
1170         }
1171
1172         if (strcmp(tokens[0], "mempool") == 0) {
1173                 snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
1174                 return;
1175         }
1176
1177         if (strcmp(tokens[0], "link") == 0) {
1178                 snprintf(out, out_size, "\n%s\n", cmd_link_help);
1179                 return;
1180         }
1181
1182         if ((strcmp(tokens[0], "pipeline") == 0) &&
1183                 ((n_tokens == 1) && (strcmp(tokens[2], "create")) == 0)) {
1184                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help);
1185                 return;
1186         }
1187
1188         if ((strcmp(tokens[0], "pipeline") == 0) &&
1189                 (strcmp(tokens[1], "port") == 0)) {
1190                 if ((n_tokens == 3) && (strcmp(tokens[2], "in")) == 0) {
1191                         snprintf(out, out_size, "\n%s\n",
1192                                 cmd_pipeline_port_in_help);
1193                         return;
1194                 }
1195
1196                 if ((n_tokens == 3) && (strcmp(tokens[2], "out")) == 0) {
1197                         snprintf(out, out_size, "\n%s\n",
1198                                 cmd_pipeline_port_out_help);
1199                         return;
1200                 }
1201         }
1202
1203         if ((strcmp(tokens[0], "pipeline") == 0) &&
1204                 ((n_tokens >= 2) && (strcmp(tokens[2], "build")) == 0)) {
1205                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
1206                 return;
1207         }
1208
1209         if ((strcmp(tokens[0], "pipeline") == 0) &&
1210                 ((n_tokens >= 2) && (strcmp(tokens[2], "table")) == 0)) {
1211                 snprintf(out, out_size, "\n%s\n",
1212                         cmd_pipeline_table_update_help);
1213                 return;
1214         }
1215
1216         if ((strcmp(tokens[0], "pipeline") == 0) &&
1217                 ((n_tokens >= 2) && (strcmp(tokens[2], "stats")) == 0)) {
1218                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
1219                 return;
1220         }
1221
1222         if ((n_tokens == 3) &&
1223                 (strcmp(tokens[0], "thread") == 0) &&
1224                 (strcmp(tokens[1], "pipeline") == 0)) {
1225                 if (strcmp(tokens[2], "enable") == 0) {
1226                         snprintf(out, out_size, "\n%s\n",
1227                                 cmd_thread_pipeline_enable_help);
1228                         return;
1229                 }
1230
1231                 if (strcmp(tokens[2], "disable") == 0) {
1232                         snprintf(out, out_size, "\n%s\n",
1233                                 cmd_thread_pipeline_disable_help);
1234                         return;
1235                 }
1236         }
1237
1238         snprintf(out, out_size, "Invalid command\n");
1239 }
1240
1241 void
1242 cli_process(char *in, char *out, size_t out_size, void *obj)
1243 {
1244         char *tokens[CMD_MAX_TOKENS];
1245         uint32_t n_tokens = RTE_DIM(tokens);
1246         int status;
1247
1248         if (is_comment(in))
1249                 return;
1250
1251         status = parse_tokenize_string(in, tokens, &n_tokens);
1252         if (status) {
1253                 snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
1254                 return;
1255         }
1256
1257         if (n_tokens == 0)
1258                 return;
1259
1260         if (strcmp(tokens[0], "help") == 0) {
1261                 cmd_help(tokens, n_tokens, out, out_size, obj);
1262                 return;
1263         }
1264
1265         if (strcmp(tokens[0], "mempool") == 0) {
1266                 cmd_mempool(tokens, n_tokens, out, out_size, obj);
1267                 return;
1268         }
1269
1270         if (strcmp(tokens[0], "link") == 0) {
1271                 if (strcmp(tokens[1], "show") == 0) {
1272                         cmd_link_show(tokens, n_tokens, out, out_size, obj);
1273                         return;
1274                 }
1275
1276                 cmd_link(tokens, n_tokens, out, out_size, obj);
1277                 return;
1278         }
1279
1280         if (strcmp(tokens[0], "pipeline") == 0) {
1281                 if ((n_tokens >= 3) &&
1282                         (strcmp(tokens[2], "create") == 0)) {
1283                         cmd_pipeline_create(tokens, n_tokens, out, out_size,
1284                                 obj);
1285                         return;
1286                 }
1287
1288                 if ((n_tokens >= 4) &&
1289                         (strcmp(tokens[2], "port") == 0) &&
1290                         (strcmp(tokens[3], "in") == 0)) {
1291                         cmd_pipeline_port_in(tokens, n_tokens, out, out_size,
1292                                 obj);
1293                         return;
1294                 }
1295
1296                 if ((n_tokens >= 4) &&
1297                         (strcmp(tokens[2], "port") == 0) &&
1298                         (strcmp(tokens[3], "out") == 0)) {
1299                         cmd_pipeline_port_out(tokens, n_tokens, out, out_size,
1300                                 obj);
1301                         return;
1302                 }
1303
1304                 if ((n_tokens >= 3) &&
1305                         (strcmp(tokens[2], "build") == 0)) {
1306                         cmd_pipeline_build(tokens, n_tokens, out, out_size,
1307                                 obj);
1308                         return;
1309                 }
1310
1311                 if ((n_tokens >= 3) &&
1312                         (strcmp(tokens[2], "table") == 0)) {
1313                         cmd_pipeline_table_update(tokens, n_tokens, out,
1314                                 out_size, obj);
1315                         return;
1316                 }
1317
1318                 if ((n_tokens >= 3) &&
1319                         (strcmp(tokens[2], "stats") == 0)) {
1320                         cmd_pipeline_stats(tokens, n_tokens, out, out_size,
1321                                 obj);
1322                         return;
1323                 }
1324         }
1325
1326         if (strcmp(tokens[0], "thread") == 0) {
1327                 if ((n_tokens >= 5) &&
1328                         (strcmp(tokens[4], "enable") == 0)) {
1329                         cmd_thread_pipeline_enable(tokens, n_tokens,
1330                                 out, out_size, obj);
1331                         return;
1332                 }
1333
1334                 if ((n_tokens >= 5) &&
1335                         (strcmp(tokens[4], "disable") == 0)) {
1336                         cmd_thread_pipeline_disable(tokens, n_tokens,
1337                                 out, out_size, obj);
1338                         return;
1339                 }
1340         }
1341
1342         snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
1343 }
1344
1345 int
1346 cli_script_process(const char *file_name,
1347         size_t msg_in_len_max,
1348         size_t msg_out_len_max,
1349         void *obj)
1350 {
1351         char *msg_in = NULL, *msg_out = NULL;
1352         FILE *f = NULL;
1353
1354         /* Check input arguments */
1355         if ((file_name == NULL) ||
1356                 (strlen(file_name) == 0) ||
1357                 (msg_in_len_max == 0) ||
1358                 (msg_out_len_max == 0))
1359                 return -EINVAL;
1360
1361         msg_in = malloc(msg_in_len_max + 1);
1362         msg_out = malloc(msg_out_len_max + 1);
1363         if ((msg_in == NULL) ||
1364                 (msg_out == NULL)) {
1365                 free(msg_out);
1366                 free(msg_in);
1367                 return -ENOMEM;
1368         }
1369
1370         /* Open input file */
1371         f = fopen(file_name, "r");
1372         if (f == NULL) {
1373                 free(msg_out);
1374                 free(msg_in);
1375                 return -EIO;
1376         }
1377
1378         /* Read file */
1379         for ( ; ; ) {
1380                 if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
1381                         break;
1382
1383                 printf("%s", msg_in);
1384                 msg_out[0] = 0;
1385
1386                 cli_process(msg_in,
1387                         msg_out,
1388                         msg_out_len_max,
1389                         obj);
1390
1391                 if (strlen(msg_out))
1392                         printf("%s", msg_out);
1393         }
1394
1395         /* Close file */
1396         fclose(f);
1397         free(msg_out);
1398         free(msg_in);
1399         return 0;
1400 }