xref: /dpdk/examples/pipeline/cli.c (revision 5a19633079efa223cb47f99afec7ee11e1073604)
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_source_sink.h>
14 #include <rte_swx_pipeline.h>
15 #include <rte_swx_ctl.h>
16 
17 #include "cli.h"
18 
19 #include "obj.h"
20 #include "thread.h"
21 
22 #ifndef CMD_MAX_TOKENS
23 #define CMD_MAX_TOKENS     256
24 #endif
25 
26 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
27 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
28 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
29 #define MSG_ARG_NOT_ENOUGH  "Not enough arguments for command \"%s\".\n"
30 #define MSG_ARG_TOO_MANY    "Too many arguments for command \"%s\".\n"
31 #define MSG_ARG_MISMATCH    "Wrong number of arguments for command \"%s\".\n"
32 #define MSG_ARG_NOT_FOUND   "Argument \"%s\" not found.\n"
33 #define MSG_ARG_INVALID     "Invalid value for argument \"%s\".\n"
34 #define MSG_FILE_ERR        "Error in file \"%s\" at line %u.\n"
35 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
36 #define MSG_CMD_FAIL        "Command \"%s\" failed.\n"
37 
38 #define skip_white_spaces(pos)			\
39 ({						\
40 	__typeof__(pos) _p = (pos);		\
41 	for ( ; isspace(*_p); _p++)		\
42 		;				\
43 	_p;					\
44 })
45 
46 static int
47 parser_read_uint64(uint64_t *value, const char *p)
48 {
49 	char *next;
50 	uint64_t val;
51 
52 	p = skip_white_spaces(p);
53 	if (!isdigit(*p))
54 		return -EINVAL;
55 
56 	val = strtoul(p, &next, 10);
57 	if (p == next)
58 		return -EINVAL;
59 
60 	p = next;
61 	switch (*p) {
62 	case 'T':
63 		val *= 1024ULL;
64 		/* fall through */
65 	case 'G':
66 		val *= 1024ULL;
67 		/* fall through */
68 	case 'M':
69 		val *= 1024ULL;
70 		/* fall through */
71 	case 'k':
72 	case 'K':
73 		val *= 1024ULL;
74 		p++;
75 		break;
76 	}
77 
78 	p = skip_white_spaces(p);
79 	if (*p != '\0')
80 		return -EINVAL;
81 
82 	*value = val;
83 	return 0;
84 }
85 
86 static int
87 parser_read_uint32(uint32_t *value, const char *p)
88 {
89 	uint64_t val = 0;
90 	int ret = parser_read_uint64(&val, p);
91 
92 	if (ret < 0)
93 		return ret;
94 
95 	if (val > UINT32_MAX)
96 		return -ERANGE;
97 
98 	*value = val;
99 	return 0;
100 }
101 
102 static int
103 parser_read_uint16(uint16_t *value, const char *p)
104 {
105 	uint64_t val = 0;
106 	int ret = parser_read_uint64(&val, p);
107 
108 	if (ret < 0)
109 		return ret;
110 
111 	if (val > UINT16_MAX)
112 		return -ERANGE;
113 
114 	*value = val;
115 	return 0;
116 }
117 
118 #define PARSE_DELIMITER " \f\n\r\t\v"
119 
120 static int
121 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
122 {
123 	uint32_t i;
124 
125 	if ((string == NULL) ||
126 		(tokens == NULL) ||
127 		(*n_tokens < 1))
128 		return -EINVAL;
129 
130 	for (i = 0; i < *n_tokens; i++) {
131 		tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
132 		if (tokens[i] == NULL)
133 			break;
134 	}
135 
136 	if ((i == *n_tokens) && strtok_r(string, PARSE_DELIMITER, &string))
137 		return -E2BIG;
138 
139 	*n_tokens = i;
140 	return 0;
141 }
142 
143 static int
144 is_comment(char *in)
145 {
146 	if ((strlen(in) && index("!#%;", in[0])) ||
147 		(strncmp(in, "//", 2) == 0) ||
148 		(strncmp(in, "--", 2) == 0))
149 		return 1;
150 
151 	return 0;
152 }
153 
154 static const char cmd_mempool_help[] =
155 "mempool <mempool_name>\n"
156 "   buffer <buffer_size>\n"
157 "   pool <pool_size>\n"
158 "   cache <cache_size>\n"
159 "   cpu <cpu_id>\n";
160 
161 static void
162 cmd_mempool(char **tokens,
163 	uint32_t n_tokens,
164 	char *out,
165 	size_t out_size,
166 	void *obj)
167 {
168 	struct mempool_params p;
169 	char *name;
170 	struct mempool *mempool;
171 
172 	if (n_tokens != 10) {
173 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
174 		return;
175 	}
176 
177 	name = tokens[1];
178 
179 	if (strcmp(tokens[2], "buffer") != 0) {
180 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
181 		return;
182 	}
183 
184 	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
185 		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
186 		return;
187 	}
188 
189 	if (strcmp(tokens[4], "pool") != 0) {
190 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
191 		return;
192 	}
193 
194 	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
195 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
196 		return;
197 	}
198 
199 	if (strcmp(tokens[6], "cache") != 0) {
200 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
201 		return;
202 	}
203 
204 	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
205 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
206 		return;
207 	}
208 
209 	if (strcmp(tokens[8], "cpu") != 0) {
210 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
211 		return;
212 	}
213 
214 	if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
215 		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
216 		return;
217 	}
218 
219 	mempool = mempool_create(obj, name, &p);
220 	if (mempool == NULL) {
221 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
222 		return;
223 	}
224 }
225 
226 static const char cmd_link_help[] =
227 "link <link_name>\n"
228 "   dev <device_name> | port <port_id>\n"
229 "   rxq <n_queues> <queue_size> <mempool_name>\n"
230 "   txq <n_queues> <queue_size>\n"
231 "   promiscuous on | off\n"
232 "   [rss <qid_0> ... <qid_n>]\n";
233 
234 static void
235 cmd_link(char **tokens,
236 	uint32_t n_tokens,
237 	char *out,
238 	size_t out_size,
239 	void *obj)
240 {
241 	struct link_params p;
242 	struct link_params_rss rss;
243 	struct link *link;
244 	char *name;
245 
246 	memset(&p, 0, sizeof(p));
247 
248 	if ((n_tokens < 13) || (n_tokens > 14 + LINK_RXQ_RSS_MAX)) {
249 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
250 		return;
251 	}
252 	name = tokens[1];
253 
254 	if (strcmp(tokens[2], "dev") == 0)
255 		p.dev_name = tokens[3];
256 	else if (strcmp(tokens[2], "port") == 0) {
257 		p.dev_name = NULL;
258 
259 		if (parser_read_uint16(&p.port_id, tokens[3]) != 0) {
260 			snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
261 			return;
262 		}
263 	} else {
264 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
265 		return;
266 	}
267 
268 	if (strcmp(tokens[4], "rxq") != 0) {
269 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
270 		return;
271 	}
272 
273 	if (parser_read_uint32(&p.rx.n_queues, tokens[5]) != 0) {
274 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
275 		return;
276 	}
277 	if (parser_read_uint32(&p.rx.queue_size, tokens[6]) != 0) {
278 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
279 		return;
280 	}
281 
282 	p.rx.mempool_name = tokens[7];
283 
284 	if (strcmp(tokens[8], "txq") != 0) {
285 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
286 		return;
287 	}
288 
289 	if (parser_read_uint32(&p.tx.n_queues, tokens[9]) != 0) {
290 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
291 		return;
292 	}
293 
294 	if (parser_read_uint32(&p.tx.queue_size, tokens[10]) != 0) {
295 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
296 		return;
297 	}
298 
299 	if (strcmp(tokens[11], "promiscuous") != 0) {
300 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
301 		return;
302 	}
303 
304 	if (strcmp(tokens[12], "on") == 0)
305 		p.promiscuous = 1;
306 	else if (strcmp(tokens[12], "off") == 0)
307 		p.promiscuous = 0;
308 	else {
309 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
310 		return;
311 	}
312 
313 	/* RSS */
314 	p.rx.rss = NULL;
315 	if (n_tokens > 13) {
316 		uint32_t queue_id, i;
317 
318 		if (strcmp(tokens[13], "rss") != 0) {
319 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
320 			return;
321 		}
322 
323 		p.rx.rss = &rss;
324 
325 		rss.n_queues = 0;
326 		for (i = 14; i < n_tokens; i++) {
327 			if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
328 				snprintf(out, out_size, MSG_ARG_INVALID,
329 					"queue_id");
330 				return;
331 			}
332 
333 			rss.queue_id[rss.n_queues] = queue_id;
334 			rss.n_queues++;
335 		}
336 	}
337 
338 	link = link_create(obj, name, &p);
339 	if (link == NULL) {
340 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
341 		return;
342 	}
343 }
344 
345 /* Print the link stats and info */
346 static void
347 print_link_info(struct link *link, char *out, size_t out_size)
348 {
349 	struct rte_eth_stats stats;
350 	struct rte_ether_addr mac_addr;
351 	struct rte_eth_link eth_link;
352 	uint16_t mtu;
353 	int ret;
354 
355 	memset(&stats, 0, sizeof(stats));
356 	rte_eth_stats_get(link->port_id, &stats);
357 
358 	ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
359 	if (ret != 0) {
360 		snprintf(out, out_size, "\n%s: MAC address get failed: %s",
361 			 link->name, rte_strerror(-ret));
362 		return;
363 	}
364 
365 	ret = rte_eth_link_get(link->port_id, &eth_link);
366 	if (ret < 0) {
367 		snprintf(out, out_size, "\n%s: link get failed: %s",
368 			 link->name, rte_strerror(-ret));
369 		return;
370 	}
371 
372 	rte_eth_dev_get_mtu(link->port_id, &mtu);
373 
374 	snprintf(out, out_size,
375 		"\n"
376 		"%s: flags=<%s> mtu %u\n"
377 		"\tether %02X:%02X:%02X:%02X:%02X:%02X rxqueues %u txqueues %u\n"
378 		"\tport# %u  speed %s\n"
379 		"\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
380 		"\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
381 		"\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
382 		"\tTX errors %" PRIu64"\n",
383 		link->name,
384 		eth_link.link_status == 0 ? "DOWN" : "UP",
385 		mtu,
386 		mac_addr.addr_bytes[0], mac_addr.addr_bytes[1],
387 		mac_addr.addr_bytes[2], mac_addr.addr_bytes[3],
388 		mac_addr.addr_bytes[4], mac_addr.addr_bytes[5],
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_pipeline_create_help[] =
448 "pipeline <pipeline_name> create <numa_node>\n";
449 
450 static void
451 cmd_pipeline_create(char **tokens,
452 	uint32_t n_tokens,
453 	char *out,
454 	size_t out_size,
455 	void *obj)
456 {
457 	struct pipeline *p;
458 	char *name;
459 	uint32_t numa_node;
460 
461 	if (n_tokens != 4) {
462 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
463 		return;
464 	}
465 
466 	name = tokens[1];
467 
468 	if (parser_read_uint32(&numa_node, tokens[3]) != 0) {
469 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
470 		return;
471 	}
472 
473 	p = pipeline_create(obj, name, (int)numa_node);
474 	if (!p) {
475 		snprintf(out, out_size, "pipeline create error.");
476 		return;
477 	}
478 }
479 
480 static const char cmd_pipeline_port_in_help[] =
481 "pipeline <pipeline_name> port in <port_id>\n"
482 "   link <link_name> rxq <queue_id> bsz <burst_size>\n"
483 "   | source <mempool_name> <file_name>\n";
484 
485 static void
486 cmd_pipeline_port_in(char **tokens,
487 	uint32_t n_tokens,
488 	char *out,
489 	size_t out_size,
490 	void *obj)
491 {
492 	struct pipeline *p;
493 	int status;
494 	uint32_t port_id = 0, t0;
495 
496 	if (n_tokens < 6) {
497 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
498 		return;
499 	}
500 
501 	p = pipeline_find(obj, tokens[1]);
502 	if (!p || p->ctl) {
503 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
504 		return;
505 	}
506 
507 	if (strcmp(tokens[2], "port") != 0) {
508 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
509 		return;
510 	}
511 
512 	if (strcmp(tokens[3], "in") != 0) {
513 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
514 		return;
515 	}
516 
517 	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
518 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
519 		return;
520 	}
521 
522 	t0 = 5;
523 
524 	if (strcmp(tokens[t0], "link") == 0) {
525 		struct rte_swx_port_ethdev_reader_params params;
526 		struct link *link;
527 
528 		if (n_tokens < t0 + 6) {
529 			snprintf(out, out_size, MSG_ARG_MISMATCH,
530 				"pipeline port in link");
531 			return;
532 		}
533 
534 		link = link_find(obj, tokens[t0 + 1]);
535 		if (!link) {
536 			snprintf(out, out_size, MSG_ARG_INVALID,
537 				"link_name");
538 			return;
539 		}
540 		params.dev_name = link->dev_name;
541 
542 		if (strcmp(tokens[t0 + 2], "rxq") != 0) {
543 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
544 			return;
545 		}
546 
547 		if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
548 			snprintf(out, out_size, MSG_ARG_INVALID,
549 				"queue_id");
550 			return;
551 		}
552 
553 		if (strcmp(tokens[t0 + 4], "bsz") != 0) {
554 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
555 			return;
556 		}
557 
558 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
559 			snprintf(out, out_size, MSG_ARG_INVALID,
560 				"burst_size");
561 			return;
562 		}
563 
564 		t0 += 6;
565 
566 		status = rte_swx_pipeline_port_in_config(p->p,
567 			port_id,
568 			"ethdev",
569 			&params);
570 	} else if (strcmp(tokens[t0], "source") == 0) {
571 		struct rte_swx_port_source_params params;
572 		struct mempool *mp;
573 
574 		if (n_tokens < t0 + 3) {
575 			snprintf(out, out_size, MSG_ARG_MISMATCH,
576 				"pipeline port in source");
577 			return;
578 		}
579 
580 		mp = mempool_find(obj, tokens[t0 + 1]);
581 		if (!mp) {
582 			snprintf(out, out_size, MSG_ARG_INVALID,
583 				"mempool_name");
584 			return;
585 		}
586 		params.pool = mp->m;
587 
588 		params.file_name = tokens[t0 + 2];
589 
590 		t0 += 3;
591 
592 		status = rte_swx_pipeline_port_in_config(p->p,
593 			port_id,
594 			"source",
595 			&params);
596 	} else {
597 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
598 		return;
599 	}
600 
601 	if (status) {
602 		snprintf(out, out_size, "port in error.");
603 		return;
604 	}
605 
606 	if (n_tokens != t0) {
607 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
608 		return;
609 	}
610 }
611 
612 static const char cmd_pipeline_port_out_help[] =
613 "pipeline <pipeline_name> port out <port_id>\n"
614 "   link <link_name> txq <txq_id> bsz <burst_size>\n"
615 "   | sink <file_name> | none\n";
616 
617 static void
618 cmd_pipeline_port_out(char **tokens,
619 	uint32_t n_tokens,
620 	char *out,
621 	size_t out_size,
622 	void *obj)
623 {
624 	struct pipeline *p;
625 	int status;
626 	uint32_t port_id = 0, t0;
627 
628 	if (n_tokens < 6) {
629 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
630 		return;
631 	}
632 
633 	p = pipeline_find(obj, tokens[1]);
634 	if (!p || p->ctl) {
635 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
636 		return;
637 	}
638 
639 	if (strcmp(tokens[2], "port") != 0) {
640 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
641 		return;
642 	}
643 
644 	if (strcmp(tokens[3], "out") != 0) {
645 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
646 		return;
647 	}
648 
649 	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
650 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
651 		return;
652 	}
653 
654 	t0 = 5;
655 
656 	if (strcmp(tokens[t0], "link") == 0) {
657 		struct rte_swx_port_ethdev_writer_params params;
658 		struct link *link;
659 
660 		if (n_tokens < t0 + 6) {
661 			snprintf(out, out_size, MSG_ARG_MISMATCH,
662 				"pipeline port out link");
663 			return;
664 		}
665 
666 		link = link_find(obj, tokens[t0 + 1]);
667 		if (!link) {
668 			snprintf(out, out_size, MSG_ARG_INVALID,
669 				"link_name");
670 			return;
671 		}
672 		params.dev_name = link->dev_name;
673 
674 		if (strcmp(tokens[t0 + 2], "txq") != 0) {
675 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
676 			return;
677 		}
678 
679 		if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
680 			snprintf(out, out_size, MSG_ARG_INVALID,
681 				"queue_id");
682 			return;
683 		}
684 
685 		if (strcmp(tokens[t0 + 4], "bsz") != 0) {
686 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
687 			return;
688 		}
689 
690 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
691 			snprintf(out, out_size, MSG_ARG_INVALID,
692 				"burst_size");
693 			return;
694 		}
695 
696 		t0 += 6;
697 
698 		status = rte_swx_pipeline_port_out_config(p->p,
699 			port_id,
700 			"ethdev",
701 			&params);
702 	} else if (strcmp(tokens[t0], "sink") == 0) {
703 		struct rte_swx_port_sink_params params;
704 
705 		params.file_name = strcmp(tokens[t0 + 1], "none") ?
706 			tokens[t0 + 1] : NULL;
707 
708 		t0 += 2;
709 
710 		status = rte_swx_pipeline_port_out_config(p->p,
711 			port_id,
712 			"sink",
713 			&params);
714 	} else {
715 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
716 		return;
717 	}
718 
719 	if (status) {
720 		snprintf(out, out_size, "port out error.");
721 		return;
722 	}
723 
724 	if (n_tokens != t0) {
725 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
726 		return;
727 	}
728 }
729 
730 static const char cmd_pipeline_build_help[] =
731 "pipeline <pipeline_name> build <spec_file>\n";
732 
733 static void
734 cmd_pipeline_build(char **tokens,
735 	uint32_t n_tokens,
736 	char *out,
737 	size_t out_size,
738 	void *obj)
739 {
740 	struct pipeline *p = NULL;
741 	FILE *spec = NULL;
742 	uint32_t err_line;
743 	const char *err_msg;
744 	int status;
745 
746 	if (n_tokens != 4) {
747 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
748 		return;
749 	}
750 
751 	p = pipeline_find(obj, tokens[1]);
752 	if (!p || p->ctl) {
753 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
754 		return;
755 	}
756 
757 	spec = fopen(tokens[3], "r");
758 	if (!spec) {
759 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
760 		return;
761 	}
762 
763 	status = rte_swx_pipeline_build_from_spec(p->p,
764 		spec,
765 		&err_line,
766 		&err_msg);
767 	fclose(spec);
768 	if (status) {
769 		snprintf(out, out_size, "Error %d at line %u: %s\n.",
770 			status, err_line, err_msg);
771 		return;
772 	}
773 
774 	p->ctl = rte_swx_ctl_pipeline_create(p->p);
775 	if (!p->ctl) {
776 		snprintf(out, out_size, "Pipeline control create failed.");
777 		rte_swx_pipeline_free(p->p);
778 		return;
779 	}
780 }
781 
782 static void
783 table_entry_free(struct rte_swx_table_entry *entry)
784 {
785 	if (!entry)
786 		return;
787 
788 	free(entry->key);
789 	free(entry->key_mask);
790 	free(entry->action_data);
791 	free(entry);
792 }
793 
794 static const char cmd_pipeline_table_update_help[] =
795 "pipeline <pipeline_name> table <table_name> update <file_name_add> "
796 "<file_name_delete> <file_name_default>";
797 
798 static void
799 cmd_pipeline_table_update(char **tokens,
800 	uint32_t n_tokens,
801 	char *out,
802 	size_t out_size,
803 	void *obj)
804 {
805 	struct pipeline *p;
806 	char *pipeline_name, *table_name, *line = NULL;
807 	char *file_name_add, *file_name_delete, *file_name_default;
808 	FILE *file_add = NULL, *file_delete = NULL, *file_default = NULL;
809 	uint32_t line_id;
810 	int status;
811 
812 	if (n_tokens != 8) {
813 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
814 		return;
815 	}
816 
817 	pipeline_name = tokens[1];
818 	p = pipeline_find(obj, pipeline_name);
819 	if (!p || !p->ctl) {
820 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
821 		return;
822 	}
823 
824 	if (strcmp(tokens[2], "table") != 0) {
825 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
826 		return;
827 	}
828 
829 	table_name = tokens[3];
830 
831 	if (strcmp(tokens[4], "update") != 0) {
832 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "update");
833 		return;
834 	}
835 
836 	file_name_add = tokens[5];
837 	file_name_delete = tokens[6];
838 	file_name_default = tokens[7];
839 
840 	/* File open. */
841 	if (strcmp(file_name_add, "none")) {
842 		file_add = fopen(file_name_add, "r");
843 		if (!file_add) {
844 			snprintf(out, out_size, "Cannot open file %s",
845 				file_name_add);
846 			goto error;
847 		}
848 	}
849 
850 	if (strcmp(file_name_delete, "none")) {
851 		file_delete = fopen(file_name_delete, "r");
852 		if (!file_delete) {
853 			snprintf(out, out_size, "Cannot open file %s",
854 				file_name_delete);
855 			goto error;
856 		}
857 	}
858 
859 	if (strcmp(file_name_default, "none")) {
860 		file_default = fopen(file_name_default, "r");
861 		if (!file_default) {
862 			snprintf(out, out_size, "Cannot open file %s",
863 				file_name_default);
864 			goto error;
865 		}
866 	}
867 
868 	if (!file_add && !file_delete && !file_default) {
869 		snprintf(out, out_size, "Nothing to be done.");
870 		return;
871 	}
872 
873 	/* Buffer allocation. */
874 	line = malloc(2048);
875 	if (!line) {
876 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
877 		goto error;
878 	}
879 
880 	/* Add. */
881 	if (file_add)
882 		for (line_id = 1; ; line_id++) {
883 			struct rte_swx_table_entry *entry;
884 
885 			if (fgets(line, 2048, file_add) == NULL)
886 				break;
887 
888 			entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
889 				table_name,
890 				line);
891 			if (!entry) {
892 				snprintf(out, out_size, MSG_FILE_ERR,
893 					file_name_add, line_id);
894 				goto error;
895 			}
896 
897 			status = rte_swx_ctl_pipeline_table_entry_add(p->ctl,
898 				table_name,
899 				entry);
900 			table_entry_free(entry);
901 			if (status) {
902 				snprintf(out, out_size,
903 					"Invalid entry in file %s at line %u",
904 					file_name_add, line_id);
905 				goto error;
906 			}
907 		}
908 
909 
910 	/* Delete. */
911 	if (file_delete)
912 		for (line_id = 1; ; line_id++) {
913 			struct rte_swx_table_entry *entry;
914 
915 			if (fgets(line, 2048, file_delete) == NULL)
916 				break;
917 
918 			entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
919 				table_name,
920 				line);
921 			if (!entry) {
922 				snprintf(out, out_size, MSG_FILE_ERR,
923 					file_name_delete, line_id);
924 				goto error;
925 			}
926 
927 			status = rte_swx_ctl_pipeline_table_entry_delete(p->ctl,
928 				table_name,
929 				entry);
930 			table_entry_free(entry);
931 			if (status)  {
932 				snprintf(out, out_size,
933 					"Invalid entry in file %s at line %u",
934 					file_name_delete, line_id);
935 				goto error;
936 			}
937 		}
938 
939 	/* Default. */
940 	if (file_default)
941 		for (line_id = 1; ; line_id++) {
942 			struct rte_swx_table_entry *entry;
943 
944 			if (fgets(line, 2048, file_default) == NULL)
945 				break;
946 
947 			entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
948 				table_name,
949 				line);
950 			if (!entry) {
951 				snprintf(out, out_size, MSG_FILE_ERR,
952 					file_name_default, line_id);
953 				goto error;
954 			}
955 
956 			status = rte_swx_ctl_pipeline_table_default_entry_add(p->ctl,
957 				table_name,
958 				entry);
959 			table_entry_free(entry);
960 			if (status) {
961 				snprintf(out, out_size,
962 					"Invalid entry in file %s at line %u",
963 					file_name_default, line_id);
964 				goto error;
965 			}
966 		}
967 
968 	status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
969 	if (status) {
970 		snprintf(out, out_size, "Commit failed.");
971 		goto error;
972 	}
973 
974 
975 	rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name);
976 
977 	free(line);
978 	if (file_add)
979 		fclose(file_add);
980 	if (file_delete)
981 		fclose(file_delete);
982 	if (file_default)
983 		fclose(file_default);
984 	return;
985 
986 error:
987 	rte_swx_ctl_pipeline_abort(p->ctl);
988 	free(line);
989 	if (file_add)
990 		fclose(file_add);
991 	if (file_delete)
992 		fclose(file_delete);
993 	if (file_default)
994 		fclose(file_default);
995 }
996 
997 static const char cmd_pipeline_stats_help[] =
998 "pipeline <pipeline_name> stats\n";
999 
1000 static void
1001 cmd_pipeline_stats(char **tokens,
1002 	uint32_t n_tokens,
1003 	char *out,
1004 	size_t out_size,
1005 	void *obj)
1006 {
1007 	struct rte_swx_ctl_pipeline_info info;
1008 	struct pipeline *p;
1009 	uint32_t i;
1010 	int status;
1011 
1012 	if (n_tokens != 3) {
1013 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1014 		return;
1015 	}
1016 
1017 	p = pipeline_find(obj, tokens[1]);
1018 	if (!p || !p->ctl) {
1019 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1020 		return;
1021 	}
1022 
1023 	if (strcmp(tokens[2], "stats")) {
1024 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1025 		return;
1026 	}
1027 
1028 	status = rte_swx_ctl_pipeline_info_get(p->p, &info);
1029 	if (status) {
1030 		snprintf(out, out_size, "Pipeline info get error.");
1031 		return;
1032 	}
1033 
1034 	snprintf(out, out_size, "Input ports:\n");
1035 	out_size -= strlen(out);
1036 	out += strlen(out);
1037 
1038 	for (i = 0; i < info.n_ports_in; i++) {
1039 		struct rte_swx_port_in_stats stats;
1040 
1041 		rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
1042 
1043 		snprintf(out, out_size, "\tPort %u:"
1044 			" packets %" PRIu64
1045 			" bytes %" PRIu64
1046 			" empty %" PRIu64 "\n",
1047 			i, stats.n_pkts, stats.n_bytes, stats.n_empty);
1048 		out_size -= strlen(out);
1049 		out += strlen(out);
1050 	}
1051 
1052 	snprintf(out, out_size, "Output ports:\n");
1053 	out_size -= strlen(out);
1054 	out += strlen(out);
1055 
1056 	for (i = 0; i < info.n_ports_out; i++) {
1057 		struct rte_swx_port_out_stats stats;
1058 
1059 		rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
1060 
1061 		snprintf(out, out_size, "\tPort %u:"
1062 			" packets %" PRIu64
1063 			" bytes %" PRIu64 "\n",
1064 			i, stats.n_pkts, stats.n_bytes);
1065 		out_size -= strlen(out);
1066 		out += strlen(out);
1067 	}
1068 }
1069 
1070 static const char cmd_thread_pipeline_enable_help[] =
1071 "thread <thread_id> pipeline <pipeline_name> enable\n";
1072 
1073 static void
1074 cmd_thread_pipeline_enable(char **tokens,
1075 	uint32_t n_tokens,
1076 	char *out,
1077 	size_t out_size,
1078 	void *obj)
1079 {
1080 	char *pipeline_name;
1081 	struct pipeline *p;
1082 	uint32_t thread_id;
1083 	int status;
1084 
1085 	if (n_tokens != 5) {
1086 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1087 		return;
1088 	}
1089 
1090 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
1091 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
1092 		return;
1093 	}
1094 
1095 	if (strcmp(tokens[2], "pipeline") != 0) {
1096 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
1097 		return;
1098 	}
1099 
1100 	pipeline_name = tokens[3];
1101 	p = pipeline_find(obj, pipeline_name);
1102 	if (!p || !p->ctl) {
1103 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1104 		return;
1105 	}
1106 
1107 	if (strcmp(tokens[4], "enable") != 0) {
1108 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
1109 		return;
1110 	}
1111 
1112 	status = thread_pipeline_enable(thread_id, obj, pipeline_name);
1113 	if (status) {
1114 		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
1115 		return;
1116 	}
1117 }
1118 
1119 static const char cmd_thread_pipeline_disable_help[] =
1120 "thread <thread_id> pipeline <pipeline_name> disable\n";
1121 
1122 static void
1123 cmd_thread_pipeline_disable(char **tokens,
1124 	uint32_t n_tokens,
1125 	char *out,
1126 	size_t out_size,
1127 	void *obj)
1128 {
1129 	struct pipeline *p;
1130 	char *pipeline_name;
1131 	uint32_t thread_id;
1132 	int status;
1133 
1134 	if (n_tokens != 5) {
1135 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1136 		return;
1137 	}
1138 
1139 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
1140 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
1141 		return;
1142 	}
1143 
1144 	if (strcmp(tokens[2], "pipeline") != 0) {
1145 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
1146 		return;
1147 	}
1148 
1149 	pipeline_name = tokens[3];
1150 	p = pipeline_find(obj, pipeline_name);
1151 	if (!p || !p->ctl) {
1152 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1153 		return;
1154 	}
1155 
1156 	if (strcmp(tokens[4], "disable") != 0) {
1157 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
1158 		return;
1159 	}
1160 
1161 	status = thread_pipeline_disable(thread_id, obj, pipeline_name);
1162 	if (status) {
1163 		snprintf(out, out_size, MSG_CMD_FAIL,
1164 			"thread pipeline disable");
1165 		return;
1166 	}
1167 }
1168 
1169 static void
1170 cmd_help(char **tokens,
1171 	 uint32_t n_tokens,
1172 	 char *out,
1173 	 size_t out_size,
1174 	 void *arg __rte_unused)
1175 {
1176 	tokens++;
1177 	n_tokens--;
1178 
1179 	if (n_tokens == 0) {
1180 		snprintf(out, out_size,
1181 			"Type 'help <command>' for command details.\n\n"
1182 			"List of commands:\n"
1183 			"\tmempool\n"
1184 			"\tlink\n"
1185 			"\tpipeline create\n"
1186 			"\tpipeline port in\n"
1187 			"\tpipeline port out\n"
1188 			"\tpipeline build\n"
1189 			"\tpipeline table update\n"
1190 			"\tpipeline stats\n"
1191 			"\tthread pipeline enable\n"
1192 			"\tthread pipeline disable\n\n");
1193 		return;
1194 	}
1195 
1196 	if (strcmp(tokens[0], "mempool") == 0) {
1197 		snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
1198 		return;
1199 	}
1200 
1201 	if (strcmp(tokens[0], "link") == 0) {
1202 		snprintf(out, out_size, "\n%s\n", cmd_link_help);
1203 		return;
1204 	}
1205 
1206 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1207 		(n_tokens == 2) && (strcmp(tokens[1], "create") == 0)) {
1208 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help);
1209 		return;
1210 	}
1211 
1212 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1213 		(n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) {
1214 		if (strcmp(tokens[2], "in") == 0) {
1215 			snprintf(out, out_size, "\n%s\n",
1216 				cmd_pipeline_port_in_help);
1217 			return;
1218 		}
1219 
1220 		if (strcmp(tokens[2], "out") == 0) {
1221 			snprintf(out, out_size, "\n%s\n",
1222 				cmd_pipeline_port_out_help);
1223 			return;
1224 		}
1225 	}
1226 
1227 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1228 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
1229 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
1230 		return;
1231 	}
1232 
1233 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1234 		(n_tokens == 3) &&
1235 		(strcmp(tokens[1], "table") == 0) &&
1236 		(strcmp(tokens[2], "update") == 0)) {
1237 		snprintf(out, out_size, "\n%s\n",
1238 			cmd_pipeline_table_update_help);
1239 		return;
1240 	}
1241 
1242 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1243 		(n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
1244 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
1245 		return;
1246 	}
1247 
1248 	if ((n_tokens == 3) &&
1249 		(strcmp(tokens[0], "thread") == 0) &&
1250 		(strcmp(tokens[1], "pipeline") == 0)) {
1251 		if (strcmp(tokens[2], "enable") == 0) {
1252 			snprintf(out, out_size, "\n%s\n",
1253 				cmd_thread_pipeline_enable_help);
1254 			return;
1255 		}
1256 
1257 		if (strcmp(tokens[2], "disable") == 0) {
1258 			snprintf(out, out_size, "\n%s\n",
1259 				cmd_thread_pipeline_disable_help);
1260 			return;
1261 		}
1262 	}
1263 
1264 	snprintf(out, out_size, "Invalid command\n");
1265 }
1266 
1267 void
1268 cli_process(char *in, char *out, size_t out_size, void *obj)
1269 {
1270 	char *tokens[CMD_MAX_TOKENS];
1271 	uint32_t n_tokens = RTE_DIM(tokens);
1272 	int status;
1273 
1274 	if (is_comment(in))
1275 		return;
1276 
1277 	status = parse_tokenize_string(in, tokens, &n_tokens);
1278 	if (status) {
1279 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
1280 		return;
1281 	}
1282 
1283 	if (n_tokens == 0)
1284 		return;
1285 
1286 	if (strcmp(tokens[0], "help") == 0) {
1287 		cmd_help(tokens, n_tokens, out, out_size, obj);
1288 		return;
1289 	}
1290 
1291 	if (strcmp(tokens[0], "mempool") == 0) {
1292 		cmd_mempool(tokens, n_tokens, out, out_size, obj);
1293 		return;
1294 	}
1295 
1296 	if (strcmp(tokens[0], "link") == 0) {
1297 		if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) {
1298 			cmd_link_show(tokens, n_tokens, out, out_size, obj);
1299 			return;
1300 		}
1301 
1302 		cmd_link(tokens, n_tokens, out, out_size, obj);
1303 		return;
1304 	}
1305 
1306 	if (strcmp(tokens[0], "pipeline") == 0) {
1307 		if ((n_tokens >= 3) &&
1308 			(strcmp(tokens[2], "create") == 0)) {
1309 			cmd_pipeline_create(tokens, n_tokens, out, out_size,
1310 				obj);
1311 			return;
1312 		}
1313 
1314 		if ((n_tokens >= 4) &&
1315 			(strcmp(tokens[2], "port") == 0) &&
1316 			(strcmp(tokens[3], "in") == 0)) {
1317 			cmd_pipeline_port_in(tokens, n_tokens, out, out_size,
1318 				obj);
1319 			return;
1320 		}
1321 
1322 		if ((n_tokens >= 4) &&
1323 			(strcmp(tokens[2], "port") == 0) &&
1324 			(strcmp(tokens[3], "out") == 0)) {
1325 			cmd_pipeline_port_out(tokens, n_tokens, out, out_size,
1326 				obj);
1327 			return;
1328 		}
1329 
1330 		if ((n_tokens >= 3) &&
1331 			(strcmp(tokens[2], "build") == 0)) {
1332 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
1333 				obj);
1334 			return;
1335 		}
1336 
1337 		if ((n_tokens >= 3) &&
1338 			(strcmp(tokens[2], "table") == 0)) {
1339 			cmd_pipeline_table_update(tokens, n_tokens, out,
1340 				out_size, obj);
1341 			return;
1342 		}
1343 
1344 		if ((n_tokens >= 3) &&
1345 			(strcmp(tokens[2], "stats") == 0)) {
1346 			cmd_pipeline_stats(tokens, n_tokens, out, out_size,
1347 				obj);
1348 			return;
1349 		}
1350 	}
1351 
1352 	if (strcmp(tokens[0], "thread") == 0) {
1353 		if ((n_tokens >= 5) &&
1354 			(strcmp(tokens[4], "enable") == 0)) {
1355 			cmd_thread_pipeline_enable(tokens, n_tokens,
1356 				out, out_size, obj);
1357 			return;
1358 		}
1359 
1360 		if ((n_tokens >= 5) &&
1361 			(strcmp(tokens[4], "disable") == 0)) {
1362 			cmd_thread_pipeline_disable(tokens, n_tokens,
1363 				out, out_size, obj);
1364 			return;
1365 		}
1366 	}
1367 
1368 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
1369 }
1370 
1371 int
1372 cli_script_process(const char *file_name,
1373 	size_t msg_in_len_max,
1374 	size_t msg_out_len_max,
1375 	void *obj)
1376 {
1377 	char *msg_in = NULL, *msg_out = NULL;
1378 	FILE *f = NULL;
1379 
1380 	/* Check input arguments */
1381 	if ((file_name == NULL) ||
1382 		(strlen(file_name) == 0) ||
1383 		(msg_in_len_max == 0) ||
1384 		(msg_out_len_max == 0))
1385 		return -EINVAL;
1386 
1387 	msg_in = malloc(msg_in_len_max + 1);
1388 	msg_out = malloc(msg_out_len_max + 1);
1389 	if ((msg_in == NULL) ||
1390 		(msg_out == NULL)) {
1391 		free(msg_out);
1392 		free(msg_in);
1393 		return -ENOMEM;
1394 	}
1395 
1396 	/* Open input file */
1397 	f = fopen(file_name, "r");
1398 	if (f == NULL) {
1399 		free(msg_out);
1400 		free(msg_in);
1401 		return -EIO;
1402 	}
1403 
1404 	/* Read file */
1405 	for ( ; ; ) {
1406 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
1407 			break;
1408 
1409 		printf("%s", msg_in);
1410 		msg_out[0] = 0;
1411 
1412 		cli_process(msg_in,
1413 			msg_out,
1414 			msg_out_len_max,
1415 			obj);
1416 
1417 		if (strlen(msg_out))
1418 			printf("%s", msg_out);
1419 	}
1420 
1421 	/* Close file */
1422 	fclose(f);
1423 	free(msg_out);
1424 	free(msg_in);
1425 	return 0;
1426 }
1427