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