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