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