port: configure loop count for source port
[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_ring.h>
14 #include <rte_swx_port_source_sink.h>
15 #include <rte_swx_port_fd.h>
16 #include <rte_swx_pipeline.h>
17 #include <rte_swx_ctl.h>
18
19 #include "cli.h"
20
21 #include "obj.h"
22 #include "thread.h"
23
24 #ifndef CMD_MAX_TOKENS
25 #define CMD_MAX_TOKENS     256
26 #endif
27
28 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
29 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
30 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
31 #define MSG_ARG_NOT_ENOUGH  "Not enough arguments for command \"%s\".\n"
32 #define MSG_ARG_TOO_MANY    "Too many arguments for command \"%s\".\n"
33 #define MSG_ARG_MISMATCH    "Wrong number of arguments for command \"%s\".\n"
34 #define MSG_ARG_NOT_FOUND   "Argument \"%s\" not found.\n"
35 #define MSG_ARG_INVALID     "Invalid value for argument \"%s\".\n"
36 #define MSG_FILE_ERR        "Error in file \"%s\" at line %u.\n"
37 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
38 #define MSG_CMD_FAIL        "Command \"%s\" failed.\n"
39
40 #define skip_white_spaces(pos)                  \
41 ({                                              \
42         __typeof__(pos) _p = (pos);             \
43         for ( ; isspace(*_p); _p++)             \
44                 ;                               \
45         _p;                                     \
46 })
47
48 static int
49 parser_read_uint64(uint64_t *value, const char *p)
50 {
51         char *next;
52         uint64_t val;
53
54         p = skip_white_spaces(p);
55         if (!isdigit(*p))
56                 return -EINVAL;
57
58         val = strtoul(p, &next, 0);
59         if (p == next)
60                 return -EINVAL;
61
62         p = next;
63         switch (*p) {
64         case 'T':
65                 val *= 1024ULL;
66                 /* fall through */
67         case 'G':
68                 val *= 1024ULL;
69                 /* fall through */
70         case 'M':
71                 val *= 1024ULL;
72                 /* fall through */
73         case 'k':
74         case 'K':
75                 val *= 1024ULL;
76                 p++;
77                 break;
78         }
79
80         p = skip_white_spaces(p);
81         if (*p != '\0')
82                 return -EINVAL;
83
84         *value = val;
85         return 0;
86 }
87
88 static int
89 parser_read_uint32(uint32_t *value, const char *p)
90 {
91         uint64_t val = 0;
92         int ret = parser_read_uint64(&val, p);
93
94         if (ret < 0)
95                 return ret;
96
97         if (val > UINT32_MAX)
98                 return -ERANGE;
99
100         *value = val;
101         return 0;
102 }
103
104 static int
105 parser_read_uint16(uint16_t *value, const char *p)
106 {
107         uint64_t val = 0;
108         int ret = parser_read_uint64(&val, p);
109
110         if (ret < 0)
111                 return ret;
112
113         if (val > UINT16_MAX)
114                 return -ERANGE;
115
116         *value = val;
117         return 0;
118 }
119
120 #define PARSE_DELIMITER " \f\n\r\t\v"
121
122 static int
123 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
124 {
125         uint32_t i;
126
127         if ((string == NULL) ||
128                 (tokens == NULL) ||
129                 (*n_tokens < 1))
130                 return -EINVAL;
131
132         for (i = 0; i < *n_tokens; i++) {
133                 tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
134                 if (tokens[i] == NULL)
135                         break;
136         }
137
138         if ((i == *n_tokens) && strtok_r(string, PARSE_DELIMITER, &string))
139                 return -E2BIG;
140
141         *n_tokens = i;
142         return 0;
143 }
144
145 static int
146 is_comment(char *in)
147 {
148         if ((strlen(in) && index("!#%;", in[0])) ||
149                 (strncmp(in, "//", 2) == 0) ||
150                 (strncmp(in, "--", 2) == 0))
151                 return 1;
152
153         return 0;
154 }
155
156 static const char cmd_mempool_help[] =
157 "mempool <mempool_name>\n"
158 "   buffer <buffer_size>\n"
159 "   pool <pool_size>\n"
160 "   cache <cache_size>\n"
161 "   cpu <cpu_id>\n";
162
163 static void
164 cmd_mempool(char **tokens,
165         uint32_t n_tokens,
166         char *out,
167         size_t out_size,
168         void *obj)
169 {
170         struct mempool_params p;
171         char *name;
172         struct mempool *mempool;
173
174         if (n_tokens != 10) {
175                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
176                 return;
177         }
178
179         name = tokens[1];
180
181         if (strcmp(tokens[2], "buffer") != 0) {
182                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
183                 return;
184         }
185
186         if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
187                 snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
188                 return;
189         }
190
191         if (strcmp(tokens[4], "pool") != 0) {
192                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
193                 return;
194         }
195
196         if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
197                 snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
198                 return;
199         }
200
201         if (strcmp(tokens[6], "cache") != 0) {
202                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
203                 return;
204         }
205
206         if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
207                 snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
208                 return;
209         }
210
211         if (strcmp(tokens[8], "cpu") != 0) {
212                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
213                 return;
214         }
215
216         if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
217                 snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
218                 return;
219         }
220
221         mempool = mempool_create(obj, name, &p);
222         if (mempool == NULL) {
223                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
224                 return;
225         }
226 }
227
228 static const char cmd_link_help[] =
229 "link <link_name>\n"
230 "   dev <device_name> | port <port_id>\n"
231 "   rxq <n_queues> <queue_size> <mempool_name>\n"
232 "   txq <n_queues> <queue_size>\n"
233 "   promiscuous on | off\n"
234 "   [rss <qid_0> ... <qid_n>]\n";
235
236 static void
237 cmd_link(char **tokens,
238         uint32_t n_tokens,
239         char *out,
240         size_t out_size,
241         void *obj)
242 {
243         struct link_params p;
244         struct link_params_rss rss;
245         struct link *link;
246         char *name;
247
248         memset(&p, 0, sizeof(p));
249
250         if ((n_tokens < 13) || (n_tokens > 14 + LINK_RXQ_RSS_MAX)) {
251                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
252                 return;
253         }
254         name = tokens[1];
255
256         if (strcmp(tokens[2], "dev") == 0)
257                 p.dev_name = tokens[3];
258         else if (strcmp(tokens[2], "port") == 0) {
259                 p.dev_name = NULL;
260
261                 if (parser_read_uint16(&p.port_id, tokens[3]) != 0) {
262                         snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
263                         return;
264                 }
265         } else {
266                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
267                 return;
268         }
269
270         if (strcmp(tokens[4], "rxq") != 0) {
271                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
272                 return;
273         }
274
275         if (parser_read_uint32(&p.rx.n_queues, tokens[5]) != 0) {
276                 snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
277                 return;
278         }
279         if (parser_read_uint32(&p.rx.queue_size, tokens[6]) != 0) {
280                 snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
281                 return;
282         }
283
284         p.rx.mempool_name = tokens[7];
285
286         if (strcmp(tokens[8], "txq") != 0) {
287                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
288                 return;
289         }
290
291         if (parser_read_uint32(&p.tx.n_queues, tokens[9]) != 0) {
292                 snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
293                 return;
294         }
295
296         if (parser_read_uint32(&p.tx.queue_size, tokens[10]) != 0) {
297                 snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
298                 return;
299         }
300
301         if (strcmp(tokens[11], "promiscuous") != 0) {
302                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
303                 return;
304         }
305
306         if (strcmp(tokens[12], "on") == 0)
307                 p.promiscuous = 1;
308         else if (strcmp(tokens[12], "off") == 0)
309                 p.promiscuous = 0;
310         else {
311                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
312                 return;
313         }
314
315         /* RSS */
316         p.rx.rss = NULL;
317         if (n_tokens > 13) {
318                 uint32_t queue_id, i;
319
320                 if (strcmp(tokens[13], "rss") != 0) {
321                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
322                         return;
323                 }
324
325                 p.rx.rss = &rss;
326
327                 rss.n_queues = 0;
328                 for (i = 14; i < n_tokens; i++) {
329                         if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
330                                 snprintf(out, out_size, MSG_ARG_INVALID,
331                                         "queue_id");
332                                 return;
333                         }
334
335                         rss.queue_id[rss.n_queues] = queue_id;
336                         rss.n_queues++;
337                 }
338         }
339
340         link = link_create(obj, name, &p);
341         if (link == NULL) {
342                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
343                 return;
344         }
345 }
346
347 /* Print the link stats and info */
348 static void
349 print_link_info(struct link *link, char *out, size_t out_size)
350 {
351         struct rte_eth_stats stats;
352         struct rte_ether_addr mac_addr;
353         struct rte_eth_link eth_link;
354         uint16_t mtu;
355         int ret;
356
357         memset(&stats, 0, sizeof(stats));
358         rte_eth_stats_get(link->port_id, &stats);
359
360         ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
361         if (ret != 0) {
362                 snprintf(out, out_size, "\n%s: MAC address get failed: %s",
363                          link->name, rte_strerror(-ret));
364                 return;
365         }
366
367         ret = rte_eth_link_get(link->port_id, &eth_link);
368         if (ret < 0) {
369                 snprintf(out, out_size, "\n%s: link get failed: %s",
370                          link->name, rte_strerror(-ret));
371                 return;
372         }
373
374         rte_eth_dev_get_mtu(link->port_id, &mtu);
375
376         snprintf(out, out_size,
377                 "\n"
378                 "%s: flags=<%s> mtu %u\n"
379                 "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
380                 "\tport# %u  speed %s\n"
381                 "\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
382                 "\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
383                 "\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
384                 "\tTX errors %" PRIu64"\n",
385                 link->name,
386                 eth_link.link_status == 0 ? "DOWN" : "UP",
387                 mtu,
388                 RTE_ETHER_ADDR_BYTES(&mac_addr),
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_ring_help[] =
448 "ring <ring_name> size <size> numa <numa_node>\n";
449
450 static void
451 cmd_ring(char **tokens,
452         uint32_t n_tokens,
453         char *out,
454         size_t out_size,
455         void *obj)
456 {
457         struct ring_params p;
458         char *name;
459         struct ring *ring;
460
461         if (n_tokens != 6) {
462                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
463                 return;
464         }
465
466         name = tokens[1];
467
468         if (strcmp(tokens[2], "size") != 0) {
469                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
470                 return;
471         }
472
473         if (parser_read_uint32(&p.size, tokens[3]) != 0) {
474                 snprintf(out, out_size, MSG_ARG_INVALID, "size");
475                 return;
476         }
477
478         if (strcmp(tokens[4], "numa") != 0) {
479                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
480                 return;
481         }
482
483         if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) {
484                 snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
485                 return;
486         }
487
488         ring = ring_create(obj, name, &p);
489         if (!ring) {
490                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
491                 return;
492         }
493 }
494
495 static const char cmd_tap_help[] =
496 "tap <tap_name>\n";
497
498 static void
499 cmd_tap(char **tokens,
500         uint32_t n_tokens,
501         char *out,
502         size_t out_size,
503         void *obj)
504 {
505         struct tap *tap;
506         char *name;
507
508         if (n_tokens < 2) {
509                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
510                 return;
511         }
512         name = tokens[1];
513
514         tap = tap_create(obj, name);
515         if (tap == NULL) {
516                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
517                 return;
518         }
519 }
520
521 static const char cmd_pipeline_create_help[] =
522 "pipeline <pipeline_name> create <numa_node>\n";
523
524 static void
525 cmd_pipeline_create(char **tokens,
526         uint32_t n_tokens,
527         char *out,
528         size_t out_size,
529         void *obj)
530 {
531         struct pipeline *p;
532         char *name;
533         uint32_t numa_node;
534
535         if (n_tokens != 4) {
536                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
537                 return;
538         }
539
540         name = tokens[1];
541
542         if (parser_read_uint32(&numa_node, tokens[3]) != 0) {
543                 snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
544                 return;
545         }
546
547         p = pipeline_create(obj, name, (int)numa_node);
548         if (!p) {
549                 snprintf(out, out_size, "pipeline create error.");
550                 return;
551         }
552 }
553
554 static const char cmd_pipeline_port_in_help[] =
555 "pipeline <pipeline_name> port in <port_id>\n"
556 "   link <link_name> rxq <queue_id> bsz <burst_size>\n"
557 "   ring <ring_name> bsz <burst_size>\n"
558 "   | source <mempool_name> <file_name> loop <n_loops>\n"
559 "   | tap <tap_name> mempool <mempool_name> mtu <mtu> bsz <burst_size>\n";
560
561 static void
562 cmd_pipeline_port_in(char **tokens,
563         uint32_t n_tokens,
564         char *out,
565         size_t out_size,
566         void *obj)
567 {
568         struct pipeline *p;
569         int status;
570         uint32_t port_id = 0, t0;
571
572         if (n_tokens < 6) {
573                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
574                 return;
575         }
576
577         p = pipeline_find(obj, tokens[1]);
578         if (!p || p->ctl) {
579                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
580                 return;
581         }
582
583         if (strcmp(tokens[2], "port") != 0) {
584                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
585                 return;
586         }
587
588         if (strcmp(tokens[3], "in") != 0) {
589                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
590                 return;
591         }
592
593         if (parser_read_uint32(&port_id, tokens[4]) != 0) {
594                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
595                 return;
596         }
597
598         t0 = 5;
599
600         if (strcmp(tokens[t0], "link") == 0) {
601                 struct rte_swx_port_ethdev_reader_params params;
602                 struct link *link;
603
604                 if (n_tokens < t0 + 6) {
605                         snprintf(out, out_size, MSG_ARG_MISMATCH,
606                                 "pipeline port in link");
607                         return;
608                 }
609
610                 link = link_find(obj, tokens[t0 + 1]);
611                 if (!link) {
612                         snprintf(out, out_size, MSG_ARG_INVALID,
613                                 "link_name");
614                         return;
615                 }
616                 params.dev_name = link->dev_name;
617
618                 if (strcmp(tokens[t0 + 2], "rxq") != 0) {
619                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
620                         return;
621                 }
622
623                 if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
624                         snprintf(out, out_size, MSG_ARG_INVALID,
625                                 "queue_id");
626                         return;
627                 }
628
629                 if (strcmp(tokens[t0 + 4], "bsz") != 0) {
630                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
631                         return;
632                 }
633
634                 if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
635                         snprintf(out, out_size, MSG_ARG_INVALID,
636                                 "burst_size");
637                         return;
638                 }
639
640                 t0 += 6;
641
642                 status = rte_swx_pipeline_port_in_config(p->p,
643                         port_id,
644                         "ethdev",
645                         &params);
646         } else if (strcmp(tokens[t0], "ring") == 0) {
647                 struct rte_swx_port_ring_reader_params params;
648                 struct ring *ring;
649
650                 if (n_tokens < t0 + 4) {
651                         snprintf(out, out_size, MSG_ARG_MISMATCH,
652                                 "pipeline port in ring");
653                         return;
654                 }
655
656                 ring = ring_find(obj, tokens[t0 + 1]);
657                 if (!ring) {
658                         snprintf(out, out_size, MSG_ARG_INVALID,
659                                 "ring_name");
660                         return;
661                 }
662                 params.name = ring->name;
663
664                 if (strcmp(tokens[t0 + 2], "bsz") != 0) {
665                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
666                         return;
667                 }
668
669                 if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
670                         snprintf(out, out_size, MSG_ARG_INVALID,
671                                 "burst_size");
672                         return;
673                 }
674
675                 t0 += 4;
676
677                 status = rte_swx_pipeline_port_in_config(p->p,
678                         port_id,
679                         "ring",
680                         &params);
681         } else if (strcmp(tokens[t0], "source") == 0) {
682                 struct rte_swx_port_source_params params;
683                 struct mempool *mp;
684
685                 if (n_tokens < t0 + 5) {
686                         snprintf(out, out_size, MSG_ARG_MISMATCH,
687                                 "pipeline port in source");
688                         return;
689                 }
690
691                 mp = mempool_find(obj, tokens[t0 + 1]);
692                 if (!mp) {
693                         snprintf(out, out_size, MSG_ARG_INVALID,
694                                 "mempool_name");
695                         return;
696                 }
697                 params.pool = mp->m;
698
699                 params.file_name = tokens[t0 + 2];
700
701                 if (strcmp(tokens[t0 + 3], "loop") != 0) {
702                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "loop");
703                         return;
704                 }
705
706                 if (parser_read_uint64(&params.n_loops, tokens[t0 + 4])) {
707                         snprintf(out, out_size, MSG_ARG_INVALID,
708                                 "n_loops");
709                         return;
710                 }
711
712                 t0 += 5;
713
714                 status = rte_swx_pipeline_port_in_config(p->p,
715                         port_id,
716                         "source",
717                         &params);
718         } else if (strcmp(tokens[t0], "tap") == 0) {
719                 struct rte_swx_port_fd_reader_params params;
720                 struct tap *tap;
721                 struct mempool *mp;
722
723                 if (n_tokens < t0 + 8) {
724                         snprintf(out, out_size, MSG_ARG_MISMATCH,
725                                 "pipeline port in tap");
726                         return;
727                 }
728
729                 tap = tap_find(obj, tokens[t0 + 1]);
730                 if (!tap) {
731                         snprintf(out, out_size, MSG_ARG_INVALID,
732                                 "tap_name");
733                         return;
734                 }
735                 params.fd = tap->fd;
736
737                 if (strcmp(tokens[t0 + 2], "mempool") != 0) {
738                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
739                                 "mempool");
740                         return;
741                 }
742
743                 mp = mempool_find(obj, tokens[t0 + 3]);
744                 if (!mp) {
745                         snprintf(out, out_size, MSG_ARG_INVALID,
746                                 "mempool_name");
747                         return;
748                 }
749                 params.mempool = mp->m;
750
751                 if (strcmp(tokens[t0 + 4], "mtu") != 0) {
752                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
753                                 "mtu");
754                         return;
755                 }
756
757                 if (parser_read_uint32(&params.mtu, tokens[t0 + 5]) != 0) {
758                         snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
759                         return;
760                 }
761
762                 if (strcmp(tokens[t0 + 6], "bsz") != 0) {
763                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
764                         return;
765                 }
766
767                 if (parser_read_uint32(&params.burst_size, tokens[t0 + 7])) {
768                         snprintf(out, out_size, MSG_ARG_INVALID,
769                                 "burst_size");
770                         return;
771                 }
772
773                 t0 += 8;
774
775                 status = rte_swx_pipeline_port_in_config(p->p,
776                         port_id,
777                         "fd",
778                         &params);
779
780         } else {
781                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
782                 return;
783         }
784
785         if (status) {
786                 snprintf(out, out_size, "port in error.");
787                 return;
788         }
789
790         if (n_tokens != t0) {
791                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
792                 return;
793         }
794 }
795
796 static const char cmd_pipeline_port_out_help[] =
797 "pipeline <pipeline_name> port out <port_id>\n"
798 "   link <link_name> txq <txq_id> bsz <burst_size>\n"
799 "   ring <ring_name> bsz <burst_size>\n"
800 "   | sink <file_name> | none\n"
801 "   | tap <tap_name> bsz <burst_size>\n";
802
803 static void
804 cmd_pipeline_port_out(char **tokens,
805         uint32_t n_tokens,
806         char *out,
807         size_t out_size,
808         void *obj)
809 {
810         struct pipeline *p;
811         int status;
812         uint32_t port_id = 0, t0;
813
814         if (n_tokens < 6) {
815                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
816                 return;
817         }
818
819         p = pipeline_find(obj, tokens[1]);
820         if (!p || p->ctl) {
821                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
822                 return;
823         }
824
825         if (strcmp(tokens[2], "port") != 0) {
826                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
827                 return;
828         }
829
830         if (strcmp(tokens[3], "out") != 0) {
831                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
832                 return;
833         }
834
835         if (parser_read_uint32(&port_id, tokens[4]) != 0) {
836                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
837                 return;
838         }
839
840         t0 = 5;
841
842         if (strcmp(tokens[t0], "link") == 0) {
843                 struct rte_swx_port_ethdev_writer_params params;
844                 struct link *link;
845
846                 if (n_tokens < t0 + 6) {
847                         snprintf(out, out_size, MSG_ARG_MISMATCH,
848                                 "pipeline port out link");
849                         return;
850                 }
851
852                 link = link_find(obj, tokens[t0 + 1]);
853                 if (!link) {
854                         snprintf(out, out_size, MSG_ARG_INVALID,
855                                 "link_name");
856                         return;
857                 }
858                 params.dev_name = link->dev_name;
859
860                 if (strcmp(tokens[t0 + 2], "txq") != 0) {
861                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
862                         return;
863                 }
864
865                 if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
866                         snprintf(out, out_size, MSG_ARG_INVALID,
867                                 "queue_id");
868                         return;
869                 }
870
871                 if (strcmp(tokens[t0 + 4], "bsz") != 0) {
872                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
873                         return;
874                 }
875
876                 if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
877                         snprintf(out, out_size, MSG_ARG_INVALID,
878                                 "burst_size");
879                         return;
880                 }
881
882                 t0 += 6;
883
884                 status = rte_swx_pipeline_port_out_config(p->p,
885                         port_id,
886                         "ethdev",
887                         &params);
888         } else if (strcmp(tokens[t0], "ring") == 0) {
889                 struct rte_swx_port_ring_writer_params params;
890                 struct ring *ring;
891
892                 if (n_tokens < t0 + 4) {
893                         snprintf(out, out_size, MSG_ARG_MISMATCH,
894                                 "pipeline port out link");
895                         return;
896                 }
897
898                 ring = ring_find(obj, tokens[t0 + 1]);
899                 if (!ring) {
900                         snprintf(out, out_size, MSG_ARG_INVALID,
901                                 "ring_name");
902                         return;
903                 }
904                 params.name = ring->name;
905
906                 if (strcmp(tokens[t0 + 2], "bsz") != 0) {
907                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
908                         return;
909                 }
910
911                 if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
912                         snprintf(out, out_size, MSG_ARG_INVALID,
913                                 "burst_size");
914                         return;
915                 }
916
917                 t0 += 4;
918
919                 status = rte_swx_pipeline_port_out_config(p->p,
920                         port_id,
921                         "ring",
922                         &params);
923         } else if (strcmp(tokens[t0], "sink") == 0) {
924                 struct rte_swx_port_sink_params params;
925
926                 params.file_name = strcmp(tokens[t0 + 1], "none") ?
927                         tokens[t0 + 1] : NULL;
928
929                 t0 += 2;
930
931                 status = rte_swx_pipeline_port_out_config(p->p,
932                         port_id,
933                         "sink",
934                         &params);
935         } else if (strcmp(tokens[t0], "tap") == 0) {
936                 struct rte_swx_port_fd_writer_params params;
937                 struct tap *tap;
938
939                 if (n_tokens < t0 + 4) {
940                         snprintf(out, out_size, MSG_ARG_MISMATCH,
941                                 "pipeline port out tap");
942                         return;
943                 }
944
945                 tap = tap_find(obj, tokens[t0 + 1]);
946                 if (!tap) {
947                         snprintf(out, out_size, MSG_ARG_INVALID,
948                                 "tap_name");
949                         return;
950                 }
951                 params.fd = tap->fd;
952
953                 if (strcmp(tokens[t0 + 2], "bsz") != 0) {
954                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
955                         return;
956                 }
957
958                 if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
959                         snprintf(out, out_size, MSG_ARG_INVALID,
960                                 "burst_size");
961                         return;
962                 }
963
964                 t0 += 4;
965
966                 status = rte_swx_pipeline_port_out_config(p->p,
967                         port_id,
968                         "fd",
969                         &params);
970         } else {
971                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
972                 return;
973         }
974
975         if (status) {
976                 snprintf(out, out_size, "port out error.");
977                 return;
978         }
979
980         if (n_tokens != t0) {
981                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
982                 return;
983         }
984 }
985
986 static const char cmd_pipeline_build_help[] =
987 "pipeline <pipeline_name> build <spec_file>\n";
988
989 static void
990 cmd_pipeline_build(char **tokens,
991         uint32_t n_tokens,
992         char *out,
993         size_t out_size,
994         void *obj)
995 {
996         struct pipeline *p = NULL;
997         FILE *spec = NULL;
998         uint32_t err_line;
999         const char *err_msg;
1000         int status;
1001
1002         if (n_tokens != 4) {
1003                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1004                 return;
1005         }
1006
1007         p = pipeline_find(obj, tokens[1]);
1008         if (!p || p->ctl) {
1009                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
1010                 return;
1011         }
1012
1013         spec = fopen(tokens[3], "r");
1014         if (!spec) {
1015                 snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
1016                 return;
1017         }
1018
1019         status = rte_swx_pipeline_build_from_spec(p->p,
1020                 spec,
1021                 &err_line,
1022                 &err_msg);
1023         fclose(spec);
1024         if (status) {
1025                 snprintf(out, out_size, "Error %d at line %u: %s\n.",
1026                         status, err_line, err_msg);
1027                 return;
1028         }
1029
1030         p->ctl = rte_swx_ctl_pipeline_create(p->p);
1031         if (!p->ctl) {
1032                 snprintf(out, out_size, "Pipeline control create failed.");
1033                 rte_swx_pipeline_free(p->p);
1034                 return;
1035         }
1036 }
1037
1038 static void
1039 table_entry_free(struct rte_swx_table_entry *entry)
1040 {
1041         if (!entry)
1042                 return;
1043
1044         free(entry->key);
1045         free(entry->key_mask);
1046         free(entry->action_data);
1047         free(entry);
1048 }
1049
1050 #ifndef MAX_LINE_SIZE
1051 #define MAX_LINE_SIZE 2048
1052 #endif
1053
1054 static int
1055 pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p,
1056                            const char *table_name,
1057                            FILE *file,
1058                            uint32_t *file_line_number)
1059 {
1060         char *line = NULL;
1061         uint32_t line_id = 0;
1062         int status = 0;
1063
1064         /* Buffer allocation. */
1065         line = malloc(MAX_LINE_SIZE);
1066         if (!line)
1067                 return -ENOMEM;
1068
1069         /* File read. */
1070         for (line_id = 1; ; line_id++) {
1071                 struct rte_swx_table_entry *entry;
1072                 int is_blank_or_comment;
1073
1074                 if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1075                         break;
1076
1077                 entry = rte_swx_ctl_pipeline_table_entry_read(p,
1078                                                               table_name,
1079                                                               line,
1080                                                               &is_blank_or_comment);
1081                 if (!entry) {
1082                         if (is_blank_or_comment)
1083                                 continue;
1084
1085                         status = -EINVAL;
1086                         goto error;
1087                 }
1088
1089                 status = rte_swx_ctl_pipeline_table_entry_add(p,
1090                                                               table_name,
1091                                                               entry);
1092                 table_entry_free(entry);
1093                 if (status)
1094                         goto error;
1095         }
1096
1097 error:
1098         free(line);
1099         *file_line_number = line_id;
1100         return status;
1101 }
1102
1103 static const char cmd_pipeline_table_add_help[] =
1104 "pipeline <pipeline_name> table <table_name> add <file_name>\n";
1105
1106 static void
1107 cmd_pipeline_table_add(char **tokens,
1108                        uint32_t n_tokens,
1109                        char *out,
1110                        size_t out_size,
1111                        void *obj)
1112 {
1113         struct pipeline *p;
1114         char *pipeline_name, *table_name, *file_name;
1115         FILE *file = NULL;
1116         uint32_t file_line_number = 0;
1117         int status;
1118
1119         if (n_tokens != 6) {
1120                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1121                 return;
1122         }
1123
1124         pipeline_name = tokens[1];
1125         p = pipeline_find(obj, pipeline_name);
1126         if (!p || !p->ctl) {
1127                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1128                 return;
1129         }
1130
1131         table_name = tokens[3];
1132
1133         file_name = tokens[5];
1134         file = fopen(file_name, "r");
1135         if (!file) {
1136                 snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1137                 return;
1138         }
1139
1140         status = pipeline_table_entries_add(p->ctl,
1141                                             table_name,
1142                                             file,
1143                                             &file_line_number);
1144         if (status)
1145                 snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1146                          file_name,
1147                          file_line_number);
1148
1149         fclose(file);
1150 }
1151
1152 static int
1153 pipeline_table_entries_delete(struct rte_swx_ctl_pipeline *p,
1154                               const char *table_name,
1155                               FILE *file,
1156                               uint32_t *file_line_number)
1157 {
1158         char *line = NULL;
1159         uint32_t line_id = 0;
1160         int status = 0;
1161
1162         /* Buffer allocation. */
1163         line = malloc(MAX_LINE_SIZE);
1164         if (!line)
1165                 return -ENOMEM;
1166
1167         /* File read. */
1168         for (line_id = 1; ; line_id++) {
1169                 struct rte_swx_table_entry *entry;
1170                 int is_blank_or_comment;
1171
1172                 if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1173                         break;
1174
1175                 entry = rte_swx_ctl_pipeline_table_entry_read(p,
1176                                                               table_name,
1177                                                               line,
1178                                                               &is_blank_or_comment);
1179                 if (!entry) {
1180                         if (is_blank_or_comment)
1181                                 continue;
1182
1183                         status = -EINVAL;
1184                         goto error;
1185                 }
1186
1187                 status = rte_swx_ctl_pipeline_table_entry_delete(p,
1188                                                                  table_name,
1189                                                                  entry);
1190                 table_entry_free(entry);
1191                 if (status)
1192                         goto error;
1193         }
1194
1195 error:
1196         *file_line_number = line_id;
1197         free(line);
1198         return status;
1199 }
1200
1201 static const char cmd_pipeline_table_delete_help[] =
1202 "pipeline <pipeline_name> table <table_name> delete <file_name>\n";
1203
1204 static void
1205 cmd_pipeline_table_delete(char **tokens,
1206                           uint32_t n_tokens,
1207                           char *out,
1208                           size_t out_size,
1209                           void *obj)
1210 {
1211         struct pipeline *p;
1212         char *pipeline_name, *table_name, *file_name;
1213         FILE *file = NULL;
1214         uint32_t file_line_number = 0;
1215         int status;
1216
1217         if (n_tokens != 6) {
1218                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1219                 return;
1220         }
1221
1222         pipeline_name = tokens[1];
1223         p = pipeline_find(obj, pipeline_name);
1224         if (!p || !p->ctl) {
1225                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1226                 return;
1227         }
1228
1229         table_name = tokens[3];
1230
1231         file_name = tokens[5];
1232         file = fopen(file_name, "r");
1233         if (!file) {
1234                 snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1235                 return;
1236         }
1237
1238         status = pipeline_table_entries_delete(p->ctl,
1239                                                table_name,
1240                                                file,
1241                                                &file_line_number);
1242         if (status)
1243                 snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1244                          file_name,
1245                          file_line_number);
1246
1247         fclose(file);
1248 }
1249
1250 static int
1251 pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *p,
1252                                  const char *table_name,
1253                                  FILE *file,
1254                                  uint32_t *file_line_number)
1255 {
1256         char *line = NULL;
1257         uint32_t line_id = 0;
1258         int status = 0;
1259
1260         /* Buffer allocation. */
1261         line = malloc(MAX_LINE_SIZE);
1262         if (!line)
1263                 return -ENOMEM;
1264
1265         /* File read. */
1266         for (line_id = 1; ; line_id++) {
1267                 struct rte_swx_table_entry *entry;
1268                 int is_blank_or_comment;
1269
1270                 if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1271                         break;
1272
1273                 entry = rte_swx_ctl_pipeline_table_entry_read(p,
1274                                                               table_name,
1275                                                               line,
1276                                                               &is_blank_or_comment);
1277                 if (!entry) {
1278                         if (is_blank_or_comment)
1279                                 continue;
1280
1281                         status = -EINVAL;
1282                         goto error;
1283                 }
1284
1285                 status = rte_swx_ctl_pipeline_table_default_entry_add(p,
1286                                                                       table_name,
1287                                                                       entry);
1288                 table_entry_free(entry);
1289                 if (status)
1290                         goto error;
1291         }
1292
1293 error:
1294         *file_line_number = line_id;
1295         free(line);
1296         return status;
1297 }
1298
1299 static const char cmd_pipeline_table_default_help[] =
1300 "pipeline <pipeline_name> table <table_name> default <file_name>\n";
1301
1302 static void
1303 cmd_pipeline_table_default(char **tokens,
1304                            uint32_t n_tokens,
1305                            char *out,
1306                            size_t out_size,
1307                            void *obj)
1308 {
1309         struct pipeline *p;
1310         char *pipeline_name, *table_name, *file_name;
1311         FILE *file = NULL;
1312         uint32_t file_line_number = 0;
1313         int status;
1314
1315         if (n_tokens != 6) {
1316                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1317                 return;
1318         }
1319
1320         pipeline_name = tokens[1];
1321         p = pipeline_find(obj, pipeline_name);
1322         if (!p || !p->ctl) {
1323                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1324                 return;
1325         }
1326
1327         table_name = tokens[3];
1328
1329         file_name = tokens[5];
1330         file = fopen(file_name, "r");
1331         if (!file) {
1332                 snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1333                 return;
1334         }
1335
1336         status = pipeline_table_default_entry_add(p->ctl,
1337                                                   table_name,
1338                                                   file,
1339                                                   &file_line_number);
1340         if (status)
1341                 snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1342                          file_name,
1343                          file_line_number);
1344
1345         fclose(file);
1346 }
1347
1348 static const char cmd_pipeline_table_show_help[] =
1349 "pipeline <pipeline_name> table <table_name> show\n";
1350
1351 static void
1352 cmd_pipeline_table_show(char **tokens,
1353         uint32_t n_tokens,
1354         char *out,
1355         size_t out_size,
1356         void *obj)
1357 {
1358         struct pipeline *p;
1359         char *pipeline_name, *table_name;
1360         int status;
1361
1362         if (n_tokens != 5) {
1363                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1364                 return;
1365         }
1366
1367         pipeline_name = tokens[1];
1368         p = pipeline_find(obj, pipeline_name);
1369         if (!p || !p->ctl) {
1370                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1371                 return;
1372         }
1373
1374         table_name = tokens[3];
1375         status = rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name);
1376         if (status)
1377                 snprintf(out, out_size, MSG_ARG_INVALID, "table_name");
1378 }
1379
1380 static const char cmd_pipeline_selector_group_add_help[] =
1381 "pipeline <pipeline_name> selector <selector_name> group add\n";
1382
1383 static void
1384 cmd_pipeline_selector_group_add(char **tokens,
1385         uint32_t n_tokens,
1386         char *out,
1387         size_t out_size,
1388         void *obj)
1389 {
1390         struct pipeline *p;
1391         char *pipeline_name, *selector_name;
1392         uint32_t group_id;
1393         int status;
1394
1395         if (n_tokens != 6) {
1396                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1397                 return;
1398         }
1399
1400         pipeline_name = tokens[1];
1401         p = pipeline_find(obj, pipeline_name);
1402         if (!p || !p->ctl) {
1403                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1404                 return;
1405         }
1406
1407         if (strcmp(tokens[2], "selector") != 0) {
1408                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1409                 return;
1410         }
1411
1412         selector_name = tokens[3];
1413
1414         if (strcmp(tokens[4], "group") ||
1415                 strcmp(tokens[5], "add")) {
1416                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group add");
1417                 return;
1418         }
1419
1420         status = rte_swx_ctl_pipeline_selector_group_add(p->ctl,
1421                 selector_name,
1422                 &group_id);
1423         if (status)
1424                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1425         else
1426                 snprintf(out, out_size, "Group ID: %u\n", group_id);
1427 }
1428
1429 static const char cmd_pipeline_selector_group_delete_help[] =
1430 "pipeline <pipeline_name> selector <selector_name> group delete <group_id>\n";
1431
1432 static void
1433 cmd_pipeline_selector_group_delete(char **tokens,
1434         uint32_t n_tokens,
1435         char *out,
1436         size_t out_size,
1437         void *obj)
1438 {
1439         struct pipeline *p;
1440         char *pipeline_name, *selector_name;
1441         uint32_t group_id;
1442         int status;
1443
1444         if (n_tokens != 7) {
1445                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1446                 return;
1447         }
1448
1449         pipeline_name = tokens[1];
1450         p = pipeline_find(obj, pipeline_name);
1451         if (!p || !p->ctl) {
1452                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1453                 return;
1454         }
1455
1456         if (strcmp(tokens[2], "selector") != 0) {
1457                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1458                 return;
1459         }
1460
1461         selector_name = tokens[3];
1462
1463         if (strcmp(tokens[4], "group") ||
1464                 strcmp(tokens[5], "delete")) {
1465                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group delete");
1466                 return;
1467         }
1468
1469         if (parser_read_uint32(&group_id, tokens[6]) != 0) {
1470                 snprintf(out, out_size, MSG_ARG_INVALID, "group_id");
1471                 return;
1472         }
1473
1474         status = rte_swx_ctl_pipeline_selector_group_delete(p->ctl,
1475                 selector_name,
1476                 group_id);
1477         if (status)
1478                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1479 }
1480
1481 #define GROUP_MEMBER_INFO_TOKENS_MAX 6
1482
1483 static int
1484 token_is_comment(const char *token)
1485 {
1486         if ((token[0] == '#') ||
1487             (token[0] == ';') ||
1488             ((token[0] == '/') && (token[1] == '/')))
1489                 return 1; /* TRUE. */
1490
1491         return 0; /* FALSE. */
1492 }
1493
1494 static int
1495 pipeline_selector_group_member_read(const char *string,
1496                                       uint32_t *group_id,
1497                                       uint32_t *member_id,
1498                                       uint32_t *weight,
1499                                       int *is_blank_or_comment)
1500 {
1501         char *token_array[GROUP_MEMBER_INFO_TOKENS_MAX], **tokens;
1502         char *s0 = NULL, *s;
1503         uint32_t n_tokens = 0, group_id_val = 0, member_id_val = 0, weight_val = 0;
1504         int blank_or_comment = 0;
1505
1506         /* Check input arguments. */
1507         if (!string || !string[0])
1508                 goto error;
1509
1510         /* Memory allocation. */
1511         s0 = strdup(string);
1512         if (!s0)
1513                 goto error;
1514
1515         /* Parse the string into tokens. */
1516         for (s = s0; ; ) {
1517                 char *token;
1518
1519                 token = strtok_r(s, " \f\n\r\t\v", &s);
1520                 if (!token || token_is_comment(token))
1521                         break;
1522
1523                 if (n_tokens >= GROUP_MEMBER_INFO_TOKENS_MAX)
1524                         goto error;
1525
1526                 token_array[n_tokens] = token;
1527                 n_tokens++;
1528         }
1529
1530         if (!n_tokens) {
1531                 blank_or_comment = 1;
1532                 goto error;
1533         }
1534
1535         tokens = token_array;
1536
1537         if (n_tokens < 4 ||
1538                 strcmp(tokens[0], "group") ||
1539                 strcmp(tokens[2], "member"))
1540                 goto error;
1541
1542         /*
1543          * Group ID.
1544          */
1545         if (parser_read_uint32(&group_id_val, tokens[1]) != 0)
1546                 goto error;
1547         *group_id = group_id_val;
1548
1549         /*
1550          * Member ID.
1551          */
1552         if (parser_read_uint32(&member_id_val, tokens[3]) != 0)
1553                 goto error;
1554         *member_id = member_id_val;
1555
1556         tokens += 4;
1557         n_tokens -= 4;
1558
1559         /*
1560          * Weight.
1561          */
1562         if (n_tokens && !strcmp(tokens[0], "weight")) {
1563                 if (n_tokens < 2)
1564                         goto error;
1565
1566                 if (parser_read_uint32(&weight_val, tokens[1]) != 0)
1567                         goto error;
1568                 *weight = weight_val;
1569
1570                 tokens += 2;
1571                 n_tokens -= 2;
1572         }
1573
1574         if (n_tokens)
1575                 goto error;
1576
1577         free(s0);
1578         return 0;
1579
1580 error:
1581         free(s0);
1582         if (is_blank_or_comment)
1583                 *is_blank_or_comment = blank_or_comment;
1584         return -EINVAL;
1585 }
1586
1587 static int
1588 pipeline_selector_group_members_add(struct rte_swx_ctl_pipeline *p,
1589                            const char *selector_name,
1590                            FILE *file,
1591                            uint32_t *file_line_number)
1592 {
1593         char *line = NULL;
1594         uint32_t line_id = 0;
1595         int status = 0;
1596
1597         /* Buffer allocation. */
1598         line = malloc(MAX_LINE_SIZE);
1599         if (!line)
1600                 return -ENOMEM;
1601
1602         /* File read. */
1603         for (line_id = 1; ; line_id++) {
1604                 uint32_t group_id, member_id, weight;
1605                 int is_blank_or_comment;
1606
1607                 if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1608                         break;
1609
1610                 status = pipeline_selector_group_member_read(line,
1611                                                               &group_id,
1612                                                               &member_id,
1613                                                               &weight,
1614                                                               &is_blank_or_comment);
1615                 if (status) {
1616                         if (is_blank_or_comment)
1617                                 continue;
1618
1619                         goto error;
1620                 }
1621
1622                 status = rte_swx_ctl_pipeline_selector_group_member_add(p,
1623                         selector_name,
1624                         group_id,
1625                         member_id,
1626                         weight);
1627                 if (status)
1628                         goto error;
1629         }
1630
1631 error:
1632         free(line);
1633         *file_line_number = line_id;
1634         return status;
1635 }
1636
1637 static const char cmd_pipeline_selector_group_member_add_help[] =
1638 "pipeline <pipeline_name> selector <selector_name> group member add <file_name>";
1639
1640 static void
1641 cmd_pipeline_selector_group_member_add(char **tokens,
1642         uint32_t n_tokens,
1643         char *out,
1644         size_t out_size,
1645         void *obj)
1646 {
1647         struct pipeline *p;
1648         char *pipeline_name, *selector_name, *file_name;
1649         FILE *file = NULL;
1650         uint32_t file_line_number = 0;
1651         int status;
1652
1653         if (n_tokens != 8) {
1654                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1655                 return;
1656         }
1657
1658         pipeline_name = tokens[1];
1659         p = pipeline_find(obj, pipeline_name);
1660         if (!p || !p->ctl) {
1661                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1662                 return;
1663         }
1664
1665         if (strcmp(tokens[2], "selector") != 0) {
1666                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1667                 return;
1668         }
1669
1670         selector_name = tokens[3];
1671
1672         if (strcmp(tokens[4], "group") ||
1673                 strcmp(tokens[5], "member") ||
1674                 strcmp(tokens[6], "add")) {
1675                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member add");
1676                 return;
1677         }
1678
1679         file_name = tokens[7];
1680         file = fopen(file_name, "r");
1681         if (!file) {
1682                 snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1683                 return;
1684         }
1685
1686         status = pipeline_selector_group_members_add(p->ctl,
1687                                             selector_name,
1688                                             file,
1689                                             &file_line_number);
1690         if (status)
1691                 snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1692                          file_name,
1693                          file_line_number);
1694
1695         fclose(file);
1696 }
1697
1698 static int
1699 pipeline_selector_group_members_delete(struct rte_swx_ctl_pipeline *p,
1700                            const char *selector_name,
1701                            FILE *file,
1702                            uint32_t *file_line_number)
1703 {
1704         char *line = NULL;
1705         uint32_t line_id = 0;
1706         int status = 0;
1707
1708         /* Buffer allocation. */
1709         line = malloc(MAX_LINE_SIZE);
1710         if (!line)
1711                 return -ENOMEM;
1712
1713         /* File read. */
1714         for (line_id = 1; ; line_id++) {
1715                 uint32_t group_id, member_id, weight;
1716                 int is_blank_or_comment;
1717
1718                 if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1719                         break;
1720
1721                 status = pipeline_selector_group_member_read(line,
1722                                                               &group_id,
1723                                                               &member_id,
1724                                                               &weight,
1725                                                               &is_blank_or_comment);
1726                 if (status) {
1727                         if (is_blank_or_comment)
1728                                 continue;
1729
1730                         goto error;
1731                 }
1732
1733                 status = rte_swx_ctl_pipeline_selector_group_member_delete(p,
1734                         selector_name,
1735                         group_id,
1736                         member_id);
1737                 if (status)
1738                         goto error;
1739         }
1740
1741 error:
1742         free(line);
1743         *file_line_number = line_id;
1744         return status;
1745 }
1746
1747 static const char cmd_pipeline_selector_group_member_delete_help[] =
1748 "pipeline <pipeline_name> selector <selector_name> group member delete <file_name>";
1749
1750 static void
1751 cmd_pipeline_selector_group_member_delete(char **tokens,
1752         uint32_t n_tokens,
1753         char *out,
1754         size_t out_size,
1755         void *obj)
1756 {
1757         struct pipeline *p;
1758         char *pipeline_name, *selector_name, *file_name;
1759         FILE *file = NULL;
1760         uint32_t file_line_number = 0;
1761         int status;
1762
1763         if (n_tokens != 8) {
1764                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1765                 return;
1766         }
1767
1768         pipeline_name = tokens[1];
1769         p = pipeline_find(obj, pipeline_name);
1770         if (!p || !p->ctl) {
1771                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1772                 return;
1773         }
1774
1775         if (strcmp(tokens[2], "selector") != 0) {
1776                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1777                 return;
1778         }
1779
1780         selector_name = tokens[3];
1781
1782         if (strcmp(tokens[4], "group") ||
1783                 strcmp(tokens[5], "member") ||
1784                 strcmp(tokens[6], "delete")) {
1785                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member delete");
1786                 return;
1787         }
1788
1789         file_name = tokens[7];
1790         file = fopen(file_name, "r");
1791         if (!file) {
1792                 snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1793                 return;
1794         }
1795
1796         status = pipeline_selector_group_members_delete(p->ctl,
1797                                             selector_name,
1798                                             file,
1799                                             &file_line_number);
1800         if (status)
1801                 snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1802                          file_name,
1803                          file_line_number);
1804
1805         fclose(file);
1806 }
1807
1808 static const char cmd_pipeline_selector_show_help[] =
1809 "pipeline <pipeline_name> selector <selector_name> show\n";
1810
1811 static void
1812 cmd_pipeline_selector_show(char **tokens,
1813         uint32_t n_tokens,
1814         char *out,
1815         size_t out_size,
1816         void *obj)
1817 {
1818         struct pipeline *p;
1819         char *pipeline_name, *selector_name;
1820         int status;
1821
1822         if (n_tokens != 5) {
1823                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1824                 return;
1825         }
1826
1827         pipeline_name = tokens[1];
1828         p = pipeline_find(obj, pipeline_name);
1829         if (!p || !p->ctl) {
1830                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1831                 return;
1832         }
1833
1834         selector_name = tokens[3];
1835         status = rte_swx_ctl_pipeline_selector_fprintf(stdout,
1836                 p->ctl, selector_name);
1837         if (status)
1838                 snprintf(out, out_size, MSG_ARG_INVALID, "selector_name");
1839 }
1840
1841 static int
1842 pipeline_learner_default_entry_add(struct rte_swx_ctl_pipeline *p,
1843                                    const char *learner_name,
1844                                    FILE *file,
1845                                    uint32_t *file_line_number)
1846 {
1847         char *line = NULL;
1848         uint32_t line_id = 0;
1849         int status = 0;
1850
1851         /* Buffer allocation. */
1852         line = malloc(MAX_LINE_SIZE);
1853         if (!line)
1854                 return -ENOMEM;
1855
1856         /* File read. */
1857         for (line_id = 1; ; line_id++) {
1858                 struct rte_swx_table_entry *entry;
1859                 int is_blank_or_comment;
1860
1861                 if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1862                         break;
1863
1864                 entry = rte_swx_ctl_pipeline_learner_default_entry_read(p,
1865                                                                         learner_name,
1866                                                                         line,
1867                                                                         &is_blank_or_comment);
1868                 if (!entry) {
1869                         if (is_blank_or_comment)
1870                                 continue;
1871
1872                         status = -EINVAL;
1873                         goto error;
1874                 }
1875
1876                 status = rte_swx_ctl_pipeline_learner_default_entry_add(p,
1877                                                                         learner_name,
1878                                                                         entry);
1879                 table_entry_free(entry);
1880                 if (status)
1881                         goto error;
1882         }
1883
1884 error:
1885         *file_line_number = line_id;
1886         free(line);
1887         return status;
1888 }
1889
1890 static const char cmd_pipeline_learner_default_help[] =
1891 "pipeline <pipeline_name> learner <learner_name> default <file_name>\n";
1892
1893 static void
1894 cmd_pipeline_learner_default(char **tokens,
1895                              uint32_t n_tokens,
1896                              char *out,
1897                              size_t out_size,
1898                              void *obj)
1899 {
1900         struct pipeline *p;
1901         char *pipeline_name, *learner_name, *file_name;
1902         FILE *file = NULL;
1903         uint32_t file_line_number = 0;
1904         int status;
1905
1906         if (n_tokens != 6) {
1907                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1908                 return;
1909         }
1910
1911         pipeline_name = tokens[1];
1912         p = pipeline_find(obj, pipeline_name);
1913         if (!p || !p->ctl) {
1914                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1915                 return;
1916         }
1917
1918         learner_name = tokens[3];
1919
1920         file_name = tokens[5];
1921         file = fopen(file_name, "r");
1922         if (!file) {
1923                 snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1924                 return;
1925         }
1926
1927         status = pipeline_learner_default_entry_add(p->ctl,
1928                                                     learner_name,
1929                                                     file,
1930                                                     &file_line_number);
1931         if (status)
1932                 snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1933                          file_name,
1934                          file_line_number);
1935
1936         fclose(file);
1937 }
1938
1939 static const char cmd_pipeline_commit_help[] =
1940 "pipeline <pipeline_name> commit\n";
1941
1942 static void
1943 cmd_pipeline_commit(char **tokens,
1944         uint32_t n_tokens,
1945         char *out,
1946         size_t out_size,
1947         void *obj)
1948 {
1949         struct pipeline *p;
1950         char *pipeline_name;
1951         int status;
1952
1953         if (n_tokens != 3) {
1954                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1955                 return;
1956         }
1957
1958         pipeline_name = tokens[1];
1959         p = pipeline_find(obj, pipeline_name);
1960         if (!p || !p->ctl) {
1961                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1962                 return;
1963         }
1964
1965         status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
1966         if (status)
1967                 snprintf(out, out_size, "Commit failed. "
1968                         "Use \"commit\" to retry or \"abort\" to discard the pending work.\n");
1969 }
1970
1971 static const char cmd_pipeline_abort_help[] =
1972 "pipeline <pipeline_name> abort\n";
1973
1974 static void
1975 cmd_pipeline_abort(char **tokens,
1976         uint32_t n_tokens,
1977         char *out,
1978         size_t out_size,
1979         void *obj)
1980 {
1981         struct pipeline *p;
1982         char *pipeline_name;
1983
1984         if (n_tokens != 3) {
1985                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1986                 return;
1987         }
1988
1989         pipeline_name = tokens[1];
1990         p = pipeline_find(obj, pipeline_name);
1991         if (!p || !p->ctl) {
1992                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1993                 return;
1994         }
1995
1996         rte_swx_ctl_pipeline_abort(p->ctl);
1997 }
1998
1999 static const char cmd_pipeline_regrd_help[] =
2000 "pipeline <pipeline_name> regrd <register_array_name> <index>\n";
2001
2002 static void
2003 cmd_pipeline_regrd(char **tokens,
2004         uint32_t n_tokens,
2005         char *out,
2006         size_t out_size,
2007         void *obj)
2008 {
2009         struct pipeline *p;
2010         const char *name;
2011         uint64_t value;
2012         uint32_t idx;
2013         int status;
2014
2015         if (n_tokens != 5) {
2016                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2017                 return;
2018         }
2019
2020         p = pipeline_find(obj, tokens[1]);
2021         if (!p || !p->ctl) {
2022                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2023                 return;
2024         }
2025
2026         if (strcmp(tokens[2], "regrd")) {
2027                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regrd");
2028                 return;
2029         }
2030
2031         name = tokens[3];
2032
2033         if (parser_read_uint32(&idx, tokens[4])) {
2034                 snprintf(out, out_size, MSG_ARG_INVALID, "index");
2035                 return;
2036         }
2037
2038         status = rte_swx_ctl_pipeline_regarray_read(p->p, name, idx, &value);
2039         if (status) {
2040                 snprintf(out, out_size, "Command failed.\n");
2041                 return;
2042         }
2043
2044         snprintf(out, out_size, "0x%" PRIx64 "\n", value);
2045 }
2046
2047 static const char cmd_pipeline_regwr_help[] =
2048 "pipeline <pipeline_name> regwr <register_array_name> <index> <value>\n";
2049
2050 static void
2051 cmd_pipeline_regwr(char **tokens,
2052         uint32_t n_tokens,
2053         char *out,
2054         size_t out_size,
2055         void *obj)
2056 {
2057         struct pipeline *p;
2058         const char *name;
2059         uint64_t value;
2060         uint32_t idx;
2061         int status;
2062
2063         if (n_tokens != 6) {
2064                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2065                 return;
2066         }
2067
2068         p = pipeline_find(obj, tokens[1]);
2069         if (!p || !p->ctl) {
2070                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2071                 return;
2072         }
2073
2074         if (strcmp(tokens[2], "regwr")) {
2075                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regwr");
2076                 return;
2077         }
2078
2079         name = tokens[3];
2080
2081         if (parser_read_uint32(&idx, tokens[4])) {
2082                 snprintf(out, out_size, MSG_ARG_INVALID, "index");
2083                 return;
2084         }
2085
2086         if (parser_read_uint64(&value, tokens[5])) {
2087                 snprintf(out, out_size, MSG_ARG_INVALID, "value");
2088                 return;
2089         }
2090
2091         status = rte_swx_ctl_pipeline_regarray_write(p->p, name, idx, value);
2092         if (status) {
2093                 snprintf(out, out_size, "Command failed.\n");
2094                 return;
2095         }
2096 }
2097
2098 static const char cmd_pipeline_meter_profile_add_help[] =
2099 "pipeline <pipeline_name> meter profile <profile_name> add "
2100         "cir <cir> pir <pir> cbs <cbs> pbs <pbs>\n";
2101
2102 static void
2103 cmd_pipeline_meter_profile_add(char **tokens,
2104         uint32_t n_tokens,
2105         char *out,
2106         size_t out_size,
2107         void *obj)
2108 {
2109         struct rte_meter_trtcm_params params;
2110         struct pipeline *p;
2111         const char *profile_name;
2112         int status;
2113
2114         if (n_tokens != 14) {
2115                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2116                 return;
2117         }
2118
2119         p = pipeline_find(obj, tokens[1]);
2120         if (!p || !p->ctl) {
2121                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2122                 return;
2123         }
2124
2125         if (strcmp(tokens[2], "meter")) {
2126                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2127                 return;
2128         }
2129
2130         if (strcmp(tokens[3], "profile")) {
2131                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2132                 return;
2133         }
2134
2135         profile_name = tokens[4];
2136
2137         if (strcmp(tokens[5], "add")) {
2138                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
2139                 return;
2140         }
2141
2142         if (strcmp(tokens[6], "cir")) {
2143                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
2144                 return;
2145         }
2146
2147         if (parser_read_uint64(&params.cir, tokens[7])) {
2148                 snprintf(out, out_size, MSG_ARG_INVALID, "cir");
2149                 return;
2150         }
2151
2152         if (strcmp(tokens[8], "pir")) {
2153                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
2154                 return;
2155         }
2156
2157         if (parser_read_uint64(&params.pir, tokens[9])) {
2158                 snprintf(out, out_size, MSG_ARG_INVALID, "pir");
2159                 return;
2160         }
2161
2162         if (strcmp(tokens[10], "cbs")) {
2163                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
2164                 return;
2165         }
2166
2167         if (parser_read_uint64(&params.cbs, tokens[11])) {
2168                 snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
2169                 return;
2170         }
2171
2172         if (strcmp(tokens[12], "pbs")) {
2173                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
2174                 return;
2175         }
2176
2177         if (parser_read_uint64(&params.pbs, tokens[13])) {
2178                 snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
2179                 return;
2180         }
2181
2182         status = rte_swx_ctl_meter_profile_add(p->p, profile_name, &params);
2183         if (status) {
2184                 snprintf(out, out_size, "Command failed.\n");
2185                 return;
2186         }
2187 }
2188
2189 static const char cmd_pipeline_meter_profile_delete_help[] =
2190 "pipeline <pipeline_name> meter profile <profile_name> delete\n";
2191
2192 static void
2193 cmd_pipeline_meter_profile_delete(char **tokens,
2194         uint32_t n_tokens,
2195         char *out,
2196         size_t out_size,
2197         void *obj)
2198 {
2199         struct pipeline *p;
2200         const char *profile_name;
2201         int status;
2202
2203         if (n_tokens != 6) {
2204                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2205                 return;
2206         }
2207
2208         p = pipeline_find(obj, tokens[1]);
2209         if (!p || !p->ctl) {
2210                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2211                 return;
2212         }
2213
2214         if (strcmp(tokens[2], "meter")) {
2215                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2216                 return;
2217         }
2218
2219         if (strcmp(tokens[3], "profile")) {
2220                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2221                 return;
2222         }
2223
2224         profile_name = tokens[4];
2225
2226         if (strcmp(tokens[5], "delete")) {
2227                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
2228                 return;
2229         }
2230
2231         status = rte_swx_ctl_meter_profile_delete(p->p, profile_name);
2232         if (status) {
2233                 snprintf(out, out_size, "Command failed.\n");
2234                 return;
2235         }
2236 }
2237
2238 static const char cmd_pipeline_meter_reset_help[] =
2239 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
2240         "reset\n";
2241
2242 static void
2243 cmd_pipeline_meter_reset(char **tokens,
2244         uint32_t n_tokens,
2245         char *out,
2246         size_t out_size,
2247         void *obj)
2248 {
2249         struct pipeline *p;
2250         const char *name;
2251         uint32_t idx0 = 0, idx1 = 0;
2252
2253         if (n_tokens != 9) {
2254                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2255                 return;
2256         }
2257
2258         p = pipeline_find(obj, tokens[1]);
2259         if (!p || !p->ctl) {
2260                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2261                 return;
2262         }
2263
2264         if (strcmp(tokens[2], "meter")) {
2265                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2266                 return;
2267         }
2268
2269         name = tokens[3];
2270
2271         if (strcmp(tokens[4], "from")) {
2272                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2273                 return;
2274         }
2275
2276         if (parser_read_uint32(&idx0, tokens[5])) {
2277                 snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2278                 return;
2279         }
2280
2281         if (strcmp(tokens[6], "to")) {
2282                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2283                 return;
2284         }
2285
2286         if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
2287                 snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2288                 return;
2289         }
2290
2291         if (strcmp(tokens[8], "reset")) {
2292                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "reset");
2293                 return;
2294         }
2295
2296         for ( ; idx0 <= idx1; idx0++) {
2297                 int status;
2298
2299                 status = rte_swx_ctl_meter_reset(p->p, name, idx0);
2300                 if (status) {
2301                         snprintf(out, out_size, "Command failed for index %u.\n", idx0);
2302                         return;
2303                 }
2304         }
2305 }
2306
2307 static const char cmd_pipeline_meter_set_help[] =
2308 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
2309         "set profile <profile_name>\n";
2310
2311 static void
2312 cmd_pipeline_meter_set(char **tokens,
2313         uint32_t n_tokens,
2314         char *out,
2315         size_t out_size,
2316         void *obj)
2317 {
2318         struct pipeline *p;
2319         const char *name, *profile_name;
2320         uint32_t idx0 = 0, idx1 = 0;
2321
2322         if (n_tokens != 11) {
2323                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2324                 return;
2325         }
2326
2327         p = pipeline_find(obj, tokens[1]);
2328         if (!p || !p->ctl) {
2329                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2330                 return;
2331         }
2332
2333         if (strcmp(tokens[2], "meter")) {
2334                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2335                 return;
2336         }
2337
2338         name = tokens[3];
2339
2340         if (strcmp(tokens[4], "from")) {
2341                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2342                 return;
2343         }
2344
2345         if (parser_read_uint32(&idx0, tokens[5])) {
2346                 snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2347                 return;
2348         }
2349
2350         if (strcmp(tokens[6], "to")) {
2351                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2352                 return;
2353         }
2354
2355         if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
2356                 snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2357                 return;
2358         }
2359
2360         if (strcmp(tokens[8], "set")) {
2361                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "set");
2362                 return;
2363         }
2364
2365         if (strcmp(tokens[9], "profile")) {
2366                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2367                 return;
2368         }
2369
2370         profile_name = tokens[10];
2371
2372         for ( ; idx0 <= idx1; idx0++) {
2373                 int status;
2374
2375                 status = rte_swx_ctl_meter_set(p->p, name, idx0, profile_name);
2376                 if (status) {
2377                         snprintf(out, out_size, "Command failed for index %u.\n", idx0);
2378                         return;
2379                 }
2380         }
2381 }
2382
2383 static const char cmd_pipeline_meter_stats_help[] =
2384 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
2385         "stats\n";
2386
2387 static void
2388 cmd_pipeline_meter_stats(char **tokens,
2389         uint32_t n_tokens,
2390         char *out,
2391         size_t out_size,
2392         void *obj)
2393 {
2394         struct rte_swx_ctl_meter_stats stats;
2395         struct pipeline *p;
2396         const char *name;
2397         uint32_t idx0 = 0, idx1 = 0;
2398
2399         if (n_tokens != 9) {
2400                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2401                 return;
2402         }
2403
2404         p = pipeline_find(obj, tokens[1]);
2405         if (!p || !p->ctl) {
2406                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2407                 return;
2408         }
2409
2410         if (strcmp(tokens[2], "meter")) {
2411                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2412                 return;
2413         }
2414
2415         name = tokens[3];
2416
2417         if (strcmp(tokens[4], "from")) {
2418                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2419                 return;
2420         }
2421
2422         if (parser_read_uint32(&idx0, tokens[5])) {
2423                 snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2424                 return;
2425         }
2426
2427         if (strcmp(tokens[6], "to")) {
2428                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2429                 return;
2430         }
2431
2432         if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
2433                 snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2434                 return;
2435         }
2436
2437         if (strcmp(tokens[8], "stats")) {
2438                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2439                 return;
2440         }
2441
2442         /* Table header. */
2443         snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2444                  "-------",
2445                  "----------------", "----------------", "----------------",
2446                  "----------------", "----------------", "----------------");
2447         out_size -= strlen(out);
2448         out += strlen(out);
2449
2450         snprintf(out, out_size, "| %4s | %16s | %16s | %16s | %16s | %16s | %16s |\n",
2451                  "METER #",
2452                  "GREEN (packets)", "YELLOW (packets)", "RED (packets)",
2453                  "GREEN (bytes)", "YELLOW (bytes)", "RED (bytes)");
2454         out_size -= strlen(out);
2455         out += strlen(out);
2456
2457         snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2458                  "-------",
2459                  "----------------", "----------------", "----------------",
2460                  "----------------", "----------------", "----------------");
2461         out_size -= strlen(out);
2462         out += strlen(out);
2463
2464         /* Table rows. */
2465         for ( ; idx0 <= idx1; idx0++) {
2466                 int status;
2467
2468                 status = rte_swx_ctl_meter_stats_read(p->p, name, idx0, &stats);
2469                 if (status) {
2470                         snprintf(out, out_size, "Pipeline meter stats error at index %u.\n", idx0);
2471                         out_size -= strlen(out);
2472                         out += strlen(out);
2473                         return;
2474                 }
2475
2476                 snprintf(out, out_size, "| %7d | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64
2477                          " | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64 " |\n",
2478                          idx0,
2479                          stats.n_pkts[RTE_COLOR_GREEN],
2480                          stats.n_pkts[RTE_COLOR_YELLOW],
2481                          stats.n_pkts[RTE_COLOR_RED],
2482                          stats.n_bytes[RTE_COLOR_GREEN],
2483                          stats.n_bytes[RTE_COLOR_YELLOW],
2484                          stats.n_bytes[RTE_COLOR_RED]);
2485                 out_size -= strlen(out);
2486                 out += strlen(out);
2487         }
2488 }
2489
2490 static const char cmd_pipeline_stats_help[] =
2491 "pipeline <pipeline_name> stats\n";
2492
2493 static void
2494 cmd_pipeline_stats(char **tokens,
2495         uint32_t n_tokens,
2496         char *out,
2497         size_t out_size,
2498         void *obj)
2499 {
2500         struct rte_swx_ctl_pipeline_info info;
2501         struct pipeline *p;
2502         uint32_t i;
2503         int status;
2504
2505         if (n_tokens != 3) {
2506                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2507                 return;
2508         }
2509
2510         p = pipeline_find(obj, tokens[1]);
2511         if (!p || !p->ctl) {
2512                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2513                 return;
2514         }
2515
2516         if (strcmp(tokens[2], "stats")) {
2517                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2518                 return;
2519         }
2520
2521         status = rte_swx_ctl_pipeline_info_get(p->p, &info);
2522         if (status) {
2523                 snprintf(out, out_size, "Pipeline info get error.");
2524                 return;
2525         }
2526
2527         snprintf(out, out_size, "Input ports:\n");
2528         out_size -= strlen(out);
2529         out += strlen(out);
2530
2531         for (i = 0; i < info.n_ports_in; i++) {
2532                 struct rte_swx_port_in_stats stats;
2533
2534                 rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
2535
2536                 snprintf(out, out_size, "\tPort %u:"
2537                         " packets %" PRIu64
2538                         " bytes %" PRIu64
2539                         " empty %" PRIu64 "\n",
2540                         i, stats.n_pkts, stats.n_bytes, stats.n_empty);
2541                 out_size -= strlen(out);
2542                 out += strlen(out);
2543         }
2544
2545         snprintf(out, out_size, "\nOutput ports:\n");
2546         out_size -= strlen(out);
2547         out += strlen(out);
2548
2549         for (i = 0; i < info.n_ports_out; i++) {
2550                 struct rte_swx_port_out_stats stats;
2551
2552                 rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
2553
2554                 snprintf(out, out_size, "\tPort %u:"
2555                         " packets %" PRIu64
2556                         " bytes %" PRIu64 "\n",
2557                         i, stats.n_pkts, stats.n_bytes);
2558                 out_size -= strlen(out);
2559                 out += strlen(out);
2560         }
2561
2562         snprintf(out, out_size, "\nTables:\n");
2563         out_size -= strlen(out);
2564         out += strlen(out);
2565
2566         for (i = 0; i < info.n_tables; i++) {
2567                 struct rte_swx_ctl_table_info table_info;
2568                 uint64_t n_pkts_action[info.n_actions];
2569                 struct rte_swx_table_stats stats = {
2570                         .n_pkts_hit = 0,
2571                         .n_pkts_miss = 0,
2572                         .n_pkts_action = n_pkts_action,
2573                 };
2574                 uint32_t j;
2575
2576                 status = rte_swx_ctl_table_info_get(p->p, i, &table_info);
2577                 if (status) {
2578                         snprintf(out, out_size, "Table info get error.");
2579                         return;
2580                 }
2581
2582                 status = rte_swx_ctl_pipeline_table_stats_read(p->p, table_info.name, &stats);
2583                 if (status) {
2584                         snprintf(out, out_size, "Table stats read error.");
2585                         return;
2586                 }
2587
2588                 snprintf(out, out_size, "\tTable %s:\n"
2589                         "\t\tHit (packets): %" PRIu64 "\n"
2590                         "\t\tMiss (packets): %" PRIu64 "\n",
2591                         table_info.name,
2592                         stats.n_pkts_hit,
2593                         stats.n_pkts_miss);
2594                 out_size -= strlen(out);
2595                 out += strlen(out);
2596
2597                 for (j = 0; j < info.n_actions; j++) {
2598                         struct rte_swx_ctl_action_info action_info;
2599
2600                         status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
2601                         if (status) {
2602                                 snprintf(out, out_size, "Action info get error.");
2603                                 return;
2604                         }
2605
2606                         snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2607                                 action_info.name,
2608                                 stats.n_pkts_action[j]);
2609                         out_size -= strlen(out);
2610                         out += strlen(out);
2611                 }
2612         }
2613
2614         snprintf(out, out_size, "\nLearner tables:\n");
2615         out_size -= strlen(out);
2616         out += strlen(out);
2617
2618         for (i = 0; i < info.n_learners; i++) {
2619                 struct rte_swx_ctl_learner_info learner_info;
2620                 uint64_t n_pkts_action[info.n_actions];
2621                 struct rte_swx_learner_stats stats = {
2622                         .n_pkts_hit = 0,
2623                         .n_pkts_miss = 0,
2624                         .n_pkts_action = n_pkts_action,
2625                 };
2626                 uint32_t j;
2627
2628                 status = rte_swx_ctl_learner_info_get(p->p, i, &learner_info);
2629                 if (status) {
2630                         snprintf(out, out_size, "Learner table info get error.");
2631                         return;
2632                 }
2633
2634                 status = rte_swx_ctl_pipeline_learner_stats_read(p->p, learner_info.name, &stats);
2635                 if (status) {
2636                         snprintf(out, out_size, "Learner table stats read error.");
2637                         return;
2638                 }
2639
2640                 snprintf(out, out_size, "\tLearner table %s:\n"
2641                         "\t\tHit (packets): %" PRIu64 "\n"
2642                         "\t\tMiss (packets): %" PRIu64 "\n"
2643                         "\t\tLearn OK (packets): %" PRIu64 "\n"
2644                         "\t\tLearn error (packets): %" PRIu64 "\n"
2645                         "\t\tForget (packets): %" PRIu64 "\n",
2646                         learner_info.name,
2647                         stats.n_pkts_hit,
2648                         stats.n_pkts_miss,
2649                         stats.n_pkts_learn_ok,
2650                         stats.n_pkts_learn_err,
2651                         stats.n_pkts_forget);
2652                 out_size -= strlen(out);
2653                 out += strlen(out);
2654
2655                 for (j = 0; j < info.n_actions; j++) {
2656                         struct rte_swx_ctl_action_info action_info;
2657
2658                         status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
2659                         if (status) {
2660                                 snprintf(out, out_size, "Action info get error.");
2661                                 return;
2662                         }
2663
2664                         snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2665                                 action_info.name,
2666                                 stats.n_pkts_action[j]);
2667                         out_size -= strlen(out);
2668                         out += strlen(out);
2669                 }
2670         }
2671 }
2672
2673 static const char cmd_thread_pipeline_enable_help[] =
2674 "thread <thread_id> pipeline <pipeline_name> enable\n";
2675
2676 static void
2677 cmd_thread_pipeline_enable(char **tokens,
2678         uint32_t n_tokens,
2679         char *out,
2680         size_t out_size,
2681         void *obj)
2682 {
2683         char *pipeline_name;
2684         struct pipeline *p;
2685         uint32_t thread_id;
2686         int status;
2687
2688         if (n_tokens != 5) {
2689                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2690                 return;
2691         }
2692
2693         if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
2694                 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
2695                 return;
2696         }
2697
2698         if (strcmp(tokens[2], "pipeline") != 0) {
2699                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2700                 return;
2701         }
2702
2703         pipeline_name = tokens[3];
2704         p = pipeline_find(obj, pipeline_name);
2705         if (!p || !p->ctl) {
2706                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2707                 return;
2708         }
2709
2710         if (strcmp(tokens[4], "enable") != 0) {
2711                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
2712                 return;
2713         }
2714
2715         status = thread_pipeline_enable(thread_id, obj, pipeline_name);
2716         if (status) {
2717                 snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
2718                 return;
2719         }
2720 }
2721
2722 static const char cmd_thread_pipeline_disable_help[] =
2723 "thread <thread_id> pipeline <pipeline_name> disable\n";
2724
2725 static void
2726 cmd_thread_pipeline_disable(char **tokens,
2727         uint32_t n_tokens,
2728         char *out,
2729         size_t out_size,
2730         void *obj)
2731 {
2732         struct pipeline *p;
2733         char *pipeline_name;
2734         uint32_t thread_id;
2735         int status;
2736
2737         if (n_tokens != 5) {
2738                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2739                 return;
2740         }
2741
2742         if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
2743                 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
2744                 return;
2745         }
2746
2747         if (strcmp(tokens[2], "pipeline") != 0) {
2748                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2749                 return;
2750         }
2751
2752         pipeline_name = tokens[3];
2753         p = pipeline_find(obj, pipeline_name);
2754         if (!p || !p->ctl) {
2755                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2756                 return;
2757         }
2758
2759         if (strcmp(tokens[4], "disable") != 0) {
2760                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
2761                 return;
2762         }
2763
2764         status = thread_pipeline_disable(thread_id, obj, pipeline_name);
2765         if (status) {
2766                 snprintf(out, out_size, MSG_CMD_FAIL,
2767                         "thread pipeline disable");
2768                 return;
2769         }
2770 }
2771
2772 static void
2773 cmd_help(char **tokens,
2774          uint32_t n_tokens,
2775          char *out,
2776          size_t out_size,
2777          void *arg __rte_unused)
2778 {
2779         tokens++;
2780         n_tokens--;
2781
2782         if (n_tokens == 0) {
2783                 snprintf(out, out_size,
2784                         "Type 'help <command>' for command details.\n\n"
2785                         "List of commands:\n"
2786                         "\tmempool\n"
2787                         "\tlink\n"
2788                         "\ttap\n"
2789                         "\tpipeline create\n"
2790                         "\tpipeline port in\n"
2791                         "\tpipeline port out\n"
2792                         "\tpipeline build\n"
2793                         "\tpipeline table add\n"
2794                         "\tpipeline table delete\n"
2795                         "\tpipeline table default\n"
2796                         "\tpipeline table show\n"
2797                         "\tpipeline selector group add\n"
2798                         "\tpipeline selector group delete\n"
2799                         "\tpipeline selector group member add\n"
2800                         "\tpipeline selector group member delete\n"
2801                         "\tpipeline selector show\n"
2802                         "\tpipeline learner default\n"
2803                         "\tpipeline commit\n"
2804                         "\tpipeline abort\n"
2805                         "\tpipeline regrd\n"
2806                         "\tpipeline regwr\n"
2807                         "\tpipeline meter profile add\n"
2808                         "\tpipeline meter profile delete\n"
2809                         "\tpipeline meter reset\n"
2810                         "\tpipeline meter set\n"
2811                         "\tpipeline meter stats\n"
2812                         "\tpipeline stats\n"
2813                         "\tthread pipeline enable\n"
2814                         "\tthread pipeline disable\n\n");
2815                 return;
2816         }
2817
2818         if (strcmp(tokens[0], "mempool") == 0) {
2819                 snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
2820                 return;
2821         }
2822
2823         if (strcmp(tokens[0], "link") == 0) {
2824                 snprintf(out, out_size, "\n%s\n", cmd_link_help);
2825                 return;
2826         }
2827
2828         if (strcmp(tokens[0], "ring") == 0) {
2829                 snprintf(out, out_size, "\n%s\n", cmd_ring_help);
2830                 return;
2831         }
2832
2833         if (strcmp(tokens[0], "tap") == 0) {
2834                 snprintf(out, out_size, "\n%s\n", cmd_tap_help);
2835                 return;
2836         }
2837
2838         if ((strcmp(tokens[0], "pipeline") == 0) &&
2839                 (n_tokens == 2) && (strcmp(tokens[1], "create") == 0)) {
2840                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help);
2841                 return;
2842         }
2843
2844         if ((strcmp(tokens[0], "pipeline") == 0) &&
2845                 (n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) {
2846                 if (strcmp(tokens[2], "in") == 0) {
2847                         snprintf(out, out_size, "\n%s\n",
2848                                 cmd_pipeline_port_in_help);
2849                         return;
2850                 }
2851
2852                 if (strcmp(tokens[2], "out") == 0) {
2853                         snprintf(out, out_size, "\n%s\n",
2854                                 cmd_pipeline_port_out_help);
2855                         return;
2856                 }
2857         }
2858
2859         if ((strcmp(tokens[0], "pipeline") == 0) &&
2860                 (n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
2861                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
2862                 return;
2863         }
2864
2865         if ((strcmp(tokens[0], "pipeline") == 0) &&
2866                 (n_tokens == 3) &&
2867                 (strcmp(tokens[1], "table") == 0) &&
2868                 (strcmp(tokens[2], "add") == 0)) {
2869                 snprintf(out, out_size, "\n%s\n",
2870                         cmd_pipeline_table_add_help);
2871                 return;
2872         }
2873
2874         if ((strcmp(tokens[0], "pipeline") == 0) &&
2875                 (n_tokens == 3) &&
2876                 (strcmp(tokens[1], "table") == 0) &&
2877                 (strcmp(tokens[2], "delete") == 0)) {
2878                 snprintf(out, out_size, "\n%s\n",
2879                         cmd_pipeline_table_delete_help);
2880                 return;
2881         }
2882
2883         if ((strcmp(tokens[0], "pipeline") == 0) &&
2884                 (n_tokens == 3) &&
2885                 (strcmp(tokens[1], "table") == 0) &&
2886                 (strcmp(tokens[2], "default") == 0)) {
2887                 snprintf(out, out_size, "\n%s\n",
2888                         cmd_pipeline_table_default_help);
2889                 return;
2890         }
2891
2892         if ((strcmp(tokens[0], "pipeline") == 0) &&
2893                 (n_tokens == 3) &&
2894                 (strcmp(tokens[1], "table") == 0) &&
2895                 (strcmp(tokens[2], "show") == 0)) {
2896                 snprintf(out, out_size, "\n%s\n",
2897                         cmd_pipeline_table_show_help);
2898                 return;
2899         }
2900
2901         if ((strcmp(tokens[0], "pipeline") == 0) &&
2902                 (n_tokens == 4) &&
2903                 (strcmp(tokens[1], "selector") == 0) &&
2904                 (strcmp(tokens[2], "group") == 0) &&
2905                 (strcmp(tokens[3], "add") == 0)) {
2906                 snprintf(out, out_size, "\n%s\n",
2907                         cmd_pipeline_selector_group_add_help);
2908                 return;
2909         }
2910
2911         if ((strcmp(tokens[0], "pipeline") == 0) &&
2912                 (n_tokens == 4) &&
2913                 (strcmp(tokens[1], "selector") == 0) &&
2914                 (strcmp(tokens[2], "group") == 0) &&
2915                 (strcmp(tokens[3], "delete") == 0)) {
2916                 snprintf(out, out_size, "\n%s\n",
2917                         cmd_pipeline_selector_group_delete_help);
2918                 return;
2919         }
2920
2921         if ((strcmp(tokens[0], "pipeline") == 0) &&
2922                 (n_tokens == 5) &&
2923                 (strcmp(tokens[1], "selector") == 0) &&
2924                 (strcmp(tokens[2], "group") == 0) &&
2925                 (strcmp(tokens[3], "member") == 0) &&
2926                 (strcmp(tokens[4], "add") == 0)) {
2927                 snprintf(out, out_size, "\n%s\n",
2928                         cmd_pipeline_selector_group_member_add_help);
2929                 return;
2930         }
2931
2932         if ((strcmp(tokens[0], "pipeline") == 0) &&
2933                 (n_tokens == 5) &&
2934                 (strcmp(tokens[1], "selector") == 0) &&
2935                 (strcmp(tokens[2], "group") == 0) &&
2936                 (strcmp(tokens[3], "member") == 0) &&
2937                 (strcmp(tokens[4], "delete") == 0)) {
2938                 snprintf(out, out_size, "\n%s\n",
2939                         cmd_pipeline_selector_group_member_delete_help);
2940                 return;
2941         }
2942
2943         if ((strcmp(tokens[0], "pipeline") == 0) &&
2944                 (n_tokens == 3) &&
2945                 (strcmp(tokens[1], "selector") == 0) &&
2946                 (strcmp(tokens[2], "show") == 0)) {
2947                 snprintf(out, out_size, "\n%s\n",
2948                         cmd_pipeline_selector_show_help);
2949                 return;
2950         }
2951
2952         if ((strcmp(tokens[0], "pipeline") == 0) &&
2953                 (n_tokens == 3) &&
2954                 (strcmp(tokens[1], "learner") == 0) &&
2955                 (strcmp(tokens[2], "default") == 0)) {
2956                 snprintf(out, out_size, "\n%s\n",
2957                         cmd_pipeline_learner_default_help);
2958                 return;
2959         }
2960
2961         if ((strcmp(tokens[0], "pipeline") == 0) &&
2962                 (n_tokens == 2) &&
2963                 (strcmp(tokens[1], "commit") == 0)) {
2964                 snprintf(out, out_size, "\n%s\n",
2965                         cmd_pipeline_commit_help);
2966                 return;
2967         }
2968
2969         if ((strcmp(tokens[0], "pipeline") == 0) &&
2970                 (n_tokens == 2) &&
2971                 (strcmp(tokens[1], "abort") == 0)) {
2972                 snprintf(out, out_size, "\n%s\n",
2973                         cmd_pipeline_abort_help);
2974                 return;
2975         }
2976
2977         if ((strcmp(tokens[0], "pipeline") == 0) &&
2978                 (n_tokens == 2) && (strcmp(tokens[1], "regrd") == 0)) {
2979                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_regrd_help);
2980                 return;
2981         }
2982
2983         if ((strcmp(tokens[0], "pipeline") == 0) &&
2984                 (n_tokens == 2) && (strcmp(tokens[1], "regwr") == 0)) {
2985                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_regwr_help);
2986                 return;
2987         }
2988
2989         if (!strcmp(tokens[0], "pipeline") &&
2990                 (n_tokens == 4) && !strcmp(tokens[1], "meter")
2991                 && !strcmp(tokens[2], "profile")
2992                 && !strcmp(tokens[3], "add")) {
2993                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_add_help);
2994                 return;
2995         }
2996
2997         if (!strcmp(tokens[0], "pipeline") &&
2998                 (n_tokens == 4) && !strcmp(tokens[1], "meter")
2999                 && !strcmp(tokens[2], "profile")
3000                 && !strcmp(tokens[3], "delete")) {
3001                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_delete_help);
3002                 return;
3003         }
3004
3005         if (!strcmp(tokens[0], "pipeline") &&
3006                 (n_tokens == 3) && !strcmp(tokens[1], "meter")
3007                 && !strcmp(tokens[2], "reset")) {
3008                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_reset_help);
3009                 return;
3010         }
3011
3012         if (!strcmp(tokens[0], "pipeline") &&
3013                 (n_tokens == 3) && !strcmp(tokens[1], "meter")
3014                 && !strcmp(tokens[2], "set")) {
3015                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_set_help);
3016                 return;
3017         }
3018
3019         if (!strcmp(tokens[0], "pipeline") &&
3020                 (n_tokens == 3) && !strcmp(tokens[1], "meter")
3021                 && !strcmp(tokens[2], "stats")) {
3022                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_stats_help);
3023                 return;
3024         }
3025
3026         if ((strcmp(tokens[0], "pipeline") == 0) &&
3027                 (n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
3028                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
3029                 return;
3030         }
3031
3032         if ((n_tokens == 3) &&
3033                 (strcmp(tokens[0], "thread") == 0) &&
3034                 (strcmp(tokens[1], "pipeline") == 0)) {
3035                 if (strcmp(tokens[2], "enable") == 0) {
3036                         snprintf(out, out_size, "\n%s\n",
3037                                 cmd_thread_pipeline_enable_help);
3038                         return;
3039                 }
3040
3041                 if (strcmp(tokens[2], "disable") == 0) {
3042                         snprintf(out, out_size, "\n%s\n",
3043                                 cmd_thread_pipeline_disable_help);
3044                         return;
3045                 }
3046         }
3047
3048         snprintf(out, out_size, "Invalid command\n");
3049 }
3050
3051 void
3052 cli_process(char *in, char *out, size_t out_size, void *obj)
3053 {
3054         char *tokens[CMD_MAX_TOKENS];
3055         uint32_t n_tokens = RTE_DIM(tokens);
3056         int status;
3057
3058         if (is_comment(in))
3059                 return;
3060
3061         status = parse_tokenize_string(in, tokens, &n_tokens);
3062         if (status) {
3063                 snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
3064                 return;
3065         }
3066
3067         if (n_tokens == 0)
3068                 return;
3069
3070         if (strcmp(tokens[0], "help") == 0) {
3071                 cmd_help(tokens, n_tokens, out, out_size, obj);
3072                 return;
3073         }
3074
3075         if (strcmp(tokens[0], "mempool") == 0) {
3076                 cmd_mempool(tokens, n_tokens, out, out_size, obj);
3077                 return;
3078         }
3079
3080         if (strcmp(tokens[0], "link") == 0) {
3081                 if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) {
3082                         cmd_link_show(tokens, n_tokens, out, out_size, obj);
3083                         return;
3084                 }
3085
3086                 cmd_link(tokens, n_tokens, out, out_size, obj);
3087                 return;
3088         }
3089
3090         if (strcmp(tokens[0], "ring") == 0) {
3091                 cmd_ring(tokens, n_tokens, out, out_size, obj);
3092                 return;
3093         }
3094
3095         if (strcmp(tokens[0], "tap") == 0) {
3096                 cmd_tap(tokens, n_tokens, out, out_size, obj);
3097                 return;
3098         }
3099
3100         if (strcmp(tokens[0], "pipeline") == 0) {
3101                 if ((n_tokens >= 3) &&
3102                         (strcmp(tokens[2], "create") == 0)) {
3103                         cmd_pipeline_create(tokens, n_tokens, out, out_size,
3104                                 obj);
3105                         return;
3106                 }
3107
3108                 if ((n_tokens >= 4) &&
3109                         (strcmp(tokens[2], "port") == 0) &&
3110                         (strcmp(tokens[3], "in") == 0)) {
3111                         cmd_pipeline_port_in(tokens, n_tokens, out, out_size,
3112                                 obj);
3113                         return;
3114                 }
3115
3116                 if ((n_tokens >= 4) &&
3117                         (strcmp(tokens[2], "port") == 0) &&
3118                         (strcmp(tokens[3], "out") == 0)) {
3119                         cmd_pipeline_port_out(tokens, n_tokens, out, out_size,
3120                                 obj);
3121                         return;
3122                 }
3123
3124                 if ((n_tokens >= 3) &&
3125                         (strcmp(tokens[2], "build") == 0)) {
3126                         cmd_pipeline_build(tokens, n_tokens, out, out_size,
3127                                 obj);
3128                         return;
3129                 }
3130
3131                 if ((n_tokens >= 5) &&
3132                         (strcmp(tokens[2], "table") == 0) &&
3133                         (strcmp(tokens[4], "add") == 0)) {
3134                         cmd_pipeline_table_add(tokens, n_tokens, out,
3135                                 out_size, obj);
3136                         return;
3137                 }
3138
3139                 if ((n_tokens >= 5) &&
3140                         (strcmp(tokens[2], "table") == 0) &&
3141                         (strcmp(tokens[4], "delete") == 0)) {
3142                         cmd_pipeline_table_delete(tokens, n_tokens, out,
3143                                 out_size, obj);
3144                         return;
3145                 }
3146
3147                 if ((n_tokens >= 5) &&
3148                         (strcmp(tokens[2], "table") == 0) &&
3149                         (strcmp(tokens[4], "default") == 0)) {
3150                         cmd_pipeline_table_default(tokens, n_tokens, out,
3151                                 out_size, obj);
3152                         return;
3153                 }
3154
3155                 if ((n_tokens >= 5) &&
3156                         (strcmp(tokens[2], "table") == 0) &&
3157                         (strcmp(tokens[4], "show") == 0)) {
3158                         cmd_pipeline_table_show(tokens, n_tokens, out,
3159                                 out_size, obj);
3160                         return;
3161                 }
3162
3163                 if ((n_tokens >= 6) &&
3164                         (strcmp(tokens[2], "selector") == 0) &&
3165                         (strcmp(tokens[4], "group") == 0) &&
3166                         (strcmp(tokens[5], "add") == 0)) {
3167                         cmd_pipeline_selector_group_add(tokens, n_tokens, out,
3168                                 out_size, obj);
3169                         return;
3170                 }
3171
3172                 if ((n_tokens >= 6) &&
3173                         (strcmp(tokens[2], "selector") == 0) &&
3174                         (strcmp(tokens[4], "group") == 0) &&
3175                         (strcmp(tokens[5], "delete") == 0)) {
3176                         cmd_pipeline_selector_group_delete(tokens, n_tokens, out,
3177                                 out_size, obj);
3178                         return;
3179                 }
3180
3181                 if ((n_tokens >= 7) &&
3182                         (strcmp(tokens[2], "selector") == 0) &&
3183                         (strcmp(tokens[4], "group") == 0) &&
3184                         (strcmp(tokens[5], "member") == 0) &&
3185                         (strcmp(tokens[6], "add") == 0)) {
3186                         cmd_pipeline_selector_group_member_add(tokens, n_tokens, out,
3187                                 out_size, obj);
3188                         return;
3189                 }
3190
3191                 if ((n_tokens >= 7) &&
3192                         (strcmp(tokens[2], "selector") == 0) &&
3193                         (strcmp(tokens[4], "group") == 0) &&
3194                         (strcmp(tokens[5], "member") == 0) &&
3195                         (strcmp(tokens[6], "delete") == 0)) {
3196                         cmd_pipeline_selector_group_member_delete(tokens, n_tokens, out,
3197                                 out_size, obj);
3198                         return;
3199                 }
3200
3201                 if ((n_tokens >= 5) &&
3202                         (strcmp(tokens[2], "selector") == 0) &&
3203                         (strcmp(tokens[4], "show") == 0)) {
3204                         cmd_pipeline_selector_show(tokens, n_tokens, out,
3205                                 out_size, obj);
3206                         return;
3207                 }
3208
3209                 if ((n_tokens >= 5) &&
3210                         (strcmp(tokens[2], "learner") == 0) &&
3211                         (strcmp(tokens[4], "default") == 0)) {
3212                         cmd_pipeline_learner_default(tokens, n_tokens, out,
3213                                 out_size, obj);
3214                         return;
3215                 }
3216
3217                 if ((n_tokens >= 3) &&
3218                         (strcmp(tokens[2], "commit") == 0)) {
3219                         cmd_pipeline_commit(tokens, n_tokens, out,
3220                                 out_size, obj);
3221                         return;
3222                 }
3223
3224                 if ((n_tokens >= 3) &&
3225                         (strcmp(tokens[2], "abort") == 0)) {
3226                         cmd_pipeline_abort(tokens, n_tokens, out,
3227                                 out_size, obj);
3228                         return;
3229                 }
3230
3231                 if ((n_tokens >= 3) &&
3232                         (strcmp(tokens[2], "regrd") == 0)) {
3233                         cmd_pipeline_regrd(tokens, n_tokens, out, out_size, obj);
3234                         return;
3235                 }
3236
3237                 if ((n_tokens >= 3) &&
3238                         (strcmp(tokens[2], "regwr") == 0)) {
3239                         cmd_pipeline_regwr(tokens, n_tokens, out, out_size, obj);
3240                         return;
3241                 }
3242
3243                 if ((n_tokens >= 6) &&
3244                         (strcmp(tokens[2], "meter") == 0) &&
3245                         (strcmp(tokens[3], "profile") == 0) &&
3246                         (strcmp(tokens[5], "add") == 0)) {
3247                         cmd_pipeline_meter_profile_add(tokens, n_tokens, out, out_size, obj);
3248                         return;
3249                 }
3250
3251                 if ((n_tokens >= 6) &&
3252                         (strcmp(tokens[2], "meter") == 0) &&
3253                         (strcmp(tokens[3], "profile") == 0) &&
3254                         (strcmp(tokens[5], "delete") == 0)) {
3255                         cmd_pipeline_meter_profile_delete(tokens, n_tokens, out, out_size, obj);
3256                         return;
3257                 }
3258
3259                 if ((n_tokens >= 9) &&
3260                         (strcmp(tokens[2], "meter") == 0) &&
3261                         (strcmp(tokens[8], "reset") == 0)) {
3262                         cmd_pipeline_meter_reset(tokens, n_tokens, out, out_size, obj);
3263                         return;
3264                 }
3265
3266                 if ((n_tokens >= 9) &&
3267                         (strcmp(tokens[2], "meter") == 0) &&
3268                         (strcmp(tokens[8], "set") == 0)) {
3269                         cmd_pipeline_meter_set(tokens, n_tokens, out, out_size, obj);
3270                         return;
3271                 }
3272
3273                 if ((n_tokens >= 9) &&
3274                         (strcmp(tokens[2], "meter") == 0) &&
3275                         (strcmp(tokens[8], "stats") == 0)) {
3276                         cmd_pipeline_meter_stats(tokens, n_tokens, out, out_size, obj);
3277                         return;
3278                 }
3279
3280                 if ((n_tokens >= 3) &&
3281                         (strcmp(tokens[2], "stats") == 0)) {
3282                         cmd_pipeline_stats(tokens, n_tokens, out, out_size,
3283                                 obj);
3284                         return;
3285                 }
3286         }
3287
3288         if (strcmp(tokens[0], "thread") == 0) {
3289                 if ((n_tokens >= 5) &&
3290                         (strcmp(tokens[4], "enable") == 0)) {
3291                         cmd_thread_pipeline_enable(tokens, n_tokens,
3292                                 out, out_size, obj);
3293                         return;
3294                 }
3295
3296                 if ((n_tokens >= 5) &&
3297                         (strcmp(tokens[4], "disable") == 0)) {
3298                         cmd_thread_pipeline_disable(tokens, n_tokens,
3299                                 out, out_size, obj);
3300                         return;
3301                 }
3302         }
3303
3304         snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
3305 }
3306
3307 int
3308 cli_script_process(const char *file_name,
3309         size_t msg_in_len_max,
3310         size_t msg_out_len_max,
3311         void *obj)
3312 {
3313         char *msg_in = NULL, *msg_out = NULL;
3314         FILE *f = NULL;
3315
3316         /* Check input arguments */
3317         if ((file_name == NULL) ||
3318                 (strlen(file_name) == 0) ||
3319                 (msg_in_len_max == 0) ||
3320                 (msg_out_len_max == 0))
3321                 return -EINVAL;
3322
3323         msg_in = malloc(msg_in_len_max + 1);
3324         msg_out = malloc(msg_out_len_max + 1);
3325         if ((msg_in == NULL) ||
3326                 (msg_out == NULL)) {
3327                 free(msg_out);
3328                 free(msg_in);
3329                 return -ENOMEM;
3330         }
3331
3332         /* Open input file */
3333         f = fopen(file_name, "r");
3334         if (f == NULL) {
3335                 free(msg_out);
3336                 free(msg_in);
3337                 return -EIO;
3338         }
3339
3340         /* Read file */
3341         for ( ; ; ) {
3342                 if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
3343                         break;
3344
3345                 printf("%s", msg_in);
3346                 msg_out[0] = 0;
3347
3348                 cli_process(msg_in,
3349                         msg_out,
3350                         msg_out_len_max,
3351                         obj);
3352
3353                 if (strlen(msg_out))
3354                         printf("%s", msg_out);
3355         }
3356
3357         /* Close file */
3358         fclose(f);
3359         free(msg_out);
3360         free(msg_in);
3361         return 0;
3362 }