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