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