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