xref: /dpdk/app/graph/graph.c (revision 9236e5b31d909b45fa52c5e19adf02108a2052d1)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2023 Marvell.
3  */
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #include <cmdline_parse.h>
10 #include <cmdline_parse_num.h>
11 #include <cmdline_parse_string.h>
12 #include <cmdline_socket.h>
13 #include <rte_ethdev.h>
14 #include <rte_graph_worker.h>
15 #include <rte_log.h>
16 
17 #include "graph_priv.h"
18 #include "module_api.h"
19 
20 #define RTE_LOGTYPE_APP_GRAPH RTE_LOGTYPE_USER1
21 
22 static const char
23 cmd_graph_help[] = "graph <usecases> coremask <bitmask> bsz <size> tmo <ns> "
24 		   "model <rtc | mcd | default> pcap_enable <0 | 1> num_pcap_pkts <num>"
25 		   "pcap_file <output_capture_file>";
26 
27 static const char * const supported_usecases[] = {"l3fwd", "l2fwd"};
28 struct graph_config graph_config;
29 bool graph_started;
30 
31 /* Check the link rc of all ports in up to 9s, and print them finally */
32 static void
33 check_all_ports_link_status(uint32_t port_mask)
34 {
35 #define CHECK_INTERVAL 100 /* 100ms */
36 #define MAX_CHECK_TIME 90  /* 9s (90 * 100ms) in total */
37 	char link_rc_text[RTE_ETH_LINK_MAX_STR_LEN];
38 	uint8_t count, all_ports_up, print_flag = 0;
39 	struct rte_eth_link link;
40 	uint16_t portid;
41 	int rc;
42 
43 	printf("\nChecking link status...");
44 	fflush(stdout);
45 	for (count = 0; count <= MAX_CHECK_TIME; count++) {
46 		if (force_quit)
47 			return;
48 
49 		all_ports_up = 1;
50 		RTE_ETH_FOREACH_DEV(portid)
51 		{
52 			if (force_quit)
53 				return;
54 
55 			if ((port_mask & (1 << portid)) == 0)
56 				continue;
57 
58 			memset(&link, 0, sizeof(link));
59 			rc = rte_eth_link_get_nowait(portid, &link);
60 			if (rc < 0) {
61 				all_ports_up = 0;
62 				if (print_flag == 1)
63 					printf("Port %u link get failed: %s\n",
64 					       portid, rte_strerror(-rc));
65 				continue;
66 			}
67 
68 			/* Print link rc if flag set */
69 			if (print_flag == 1) {
70 				rte_eth_link_to_str(link_rc_text, sizeof(link_rc_text),
71 					&link);
72 				printf("Port %d %s\n", portid, link_rc_text);
73 				continue;
74 			}
75 
76 			/* Clear all_ports_up flag if any link down */
77 			if (link.link_status == RTE_ETH_LINK_DOWN) {
78 				all_ports_up = 0;
79 				break;
80 			}
81 		}
82 
83 		/* After finally printing all link rc, get out */
84 		if (print_flag == 1)
85 			break;
86 
87 		if (all_ports_up == 0) {
88 			printf(".");
89 			fflush(stdout);
90 			rte_delay_ms(CHECK_INTERVAL);
91 		}
92 
93 		/* Set the print_flag if all ports up or timeout */
94 		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
95 			print_flag = 1;
96 			printf("Done\n");
97 		}
98 	}
99 }
100 
101 static bool
102 parser_usecases_read(char *usecases)
103 {
104 	bool valid = false;
105 	uint32_t i, j = 0;
106 	char *token, *saveptr = NULL;
107 
108 	token = strtok_r(usecases, ",", &saveptr);
109 	while (token != NULL) {
110 		for (i = 0; i < RTE_DIM(supported_usecases); i++) {
111 			if (strcmp(supported_usecases[i], token) == 0) {
112 				graph_config.usecases[j].enabled = true;
113 				rte_strscpy(graph_config.usecases[j].name, token, 31);
114 				valid = true;
115 				j++;
116 				break;
117 			}
118 		}
119 		token = strtok_r(NULL, ",", &saveptr);
120 	}
121 
122 	return valid;
123 }
124 
125 static uint64_t
126 graph_worker_count_get(void)
127 {
128 	uint64_t nb_worker = 0;
129 	uint64_t coremask;
130 
131 	coremask = graph_config.params.coremask;
132 	while (coremask) {
133 		if (coremask & 0x1)
134 			nb_worker++;
135 
136 		coremask = (coremask >> 1);
137 	}
138 
139 	return nb_worker;
140 }
141 
142 static struct rte_node_ethdev_config *
143 graph_rxtx_node_config_get(uint32_t *num_conf, uint32_t *num_graphs)
144 {
145 	uint32_t n_tx_queue, nb_conf = 0, lcore_id;
146 	uint16_t queueid, portid, nb_graphs = 0;
147 	uint8_t nb_rx_queue, queue;
148 	struct lcore_conf *qconf;
149 
150 	n_tx_queue = graph_worker_count_get();
151 	if (n_tx_queue > RTE_MAX_ETHPORTS)
152 		n_tx_queue = RTE_MAX_ETHPORTS;
153 
154 	RTE_ETH_FOREACH_DEV(portid) {
155 		/* Skip ports that are not enabled */
156 		if ((enabled_port_mask & (1 << portid)) == 0) {
157 			printf("\nSkipping disabled port %d\n", portid);
158 			continue;
159 		}
160 
161 		nb_rx_queue = ethdev_rx_num_rx_queues_get(portid);
162 
163 		/* Setup ethdev node config */
164 		ethdev_conf[nb_conf].port_id = portid;
165 		ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue;
166 		ethdev_conf[nb_conf].num_tx_queues = n_tx_queue;
167 		ethdev_conf[nb_conf].mp = ethdev_mempool_list_by_portid(portid);
168 		ethdev_conf[nb_conf].mp_count = 1; /* Check with pools */
169 
170 		nb_conf++;
171 	}
172 
173 	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
174 		if (rte_lcore_is_enabled(lcore_id) == 0)
175 			continue;
176 
177 		qconf = &lcore_conf[lcore_id];
178 		printf("\nInitializing rx queues on lcore %u ... ", lcore_id);
179 		fflush(stdout);
180 
181 		/* Init RX queues */
182 		for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
183 			portid = qconf->rx_queue_list[queue].port_id;
184 			queueid = qconf->rx_queue_list[queue].queue_id;
185 
186 			/* Add this queue node to its graph */
187 			snprintf(qconf->rx_queue_list[queue].node_name, RTE_NODE_NAMESIZE,
188 				 "ethdev_rx-%u-%u", portid, queueid);
189 		}
190 		if (qconf->n_rx_queue)
191 			nb_graphs++;
192 	}
193 
194 	printf("\n");
195 
196 	ethdev_start();
197 	check_all_ports_link_status(enabled_port_mask);
198 
199 	*num_conf = nb_conf;
200 	*num_graphs = nb_graphs;
201 	return ethdev_conf;
202 }
203 
204 static void
205 graph_stats_print_to_file(void)
206 {
207 	struct rte_graph_cluster_stats_param s_param;
208 	struct rte_graph_cluster_stats *stats;
209 	const char *pattern = "worker_*";
210 	FILE *fp = NULL;
211 	size_t sz, len;
212 
213 	/* Prepare stats object */
214 	fp = fopen("/tmp/graph_stats.txt", "w+");
215 	if (fp == NULL)
216 		rte_exit(EXIT_FAILURE, "Error in opening stats file\n");
217 
218 	memset(&s_param, 0, sizeof(s_param));
219 	s_param.f = fp;
220 	s_param.socket_id = SOCKET_ID_ANY;
221 	s_param.graph_patterns = &pattern;
222 	s_param.nb_graph_patterns = 1;
223 
224 	stats = rte_graph_cluster_stats_create(&s_param);
225 	if (stats == NULL)
226 		rte_exit(EXIT_FAILURE, "Unable to create stats object\n");
227 
228 	/* Clear screen and move to top left */
229 	rte_graph_cluster_stats_get(stats, 0);
230 	rte_delay_ms(1E3);
231 
232 	fseek(fp, 0L, SEEK_END);
233 	sz = ftell(fp);
234 	fseek(fp, 0L, SEEK_SET);
235 
236 	len = strlen(conn->msg_out);
237 	conn->msg_out += len;
238 
239 	sz = fread(conn->msg_out, sizeof(char), sz, fp);
240 	len = strlen(conn->msg_out);
241 	conn->msg_out_len_max -= len;
242 	rte_graph_cluster_stats_destroy(stats);
243 
244 	fclose(fp);
245 }
246 
247 void
248 cmd_graph_stats_show_parsed(__rte_unused void *parsed_result, __rte_unused struct cmdline *cl,
249 		__rte_unused void *data)
250 {
251 	graph_stats_print_to_file();
252 }
253 
254 bool
255 graph_status_get(void)
256 {
257 	return graph_started;
258 }
259 
260 void
261 cmd_graph_start_parsed(__rte_unused void *parsed_result, __rte_unused struct cmdline *cl,
262 		__rte_unused void *data)
263 {
264 	struct rte_node_ethdev_config *conf;
265 	uint32_t nb_graphs = 0, nb_conf, i;
266 	int rc = -EINVAL;
267 
268 	conf = graph_rxtx_node_config_get(&nb_conf, &nb_graphs);
269 	for (i = 0; i < MAX_GRAPH_USECASES; i++) {
270 		if (!strcmp(graph_config.usecases[i].name, "l3fwd")) {
271 			if (graph_config.usecases[i].enabled) {
272 				rc  = usecase_l3fwd_configure(conf, nb_conf, nb_graphs);
273 				break;
274 			}
275 		}
276 		if (!strcmp(graph_config.usecases[i].name, "l2fwd")) {
277 			if (graph_config.usecases[i].enabled) {
278 				rc  = usecase_l2fwd_configure(conf, nb_conf, nb_graphs);
279 				break;
280 			}
281 		}
282 	}
283 
284 	if (!rc)
285 		graph_started = true;
286 }
287 
288 static int
289 graph_config_add(char *usecases, struct graph_config *config)
290 {
291 	uint64_t lcore_id, core_num;
292 	uint64_t eal_coremask = 0;
293 
294 	if (!parser_usecases_read(usecases))
295 		return -EINVAL;
296 
297 	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
298 		if (rte_lcore_is_enabled(lcore_id))
299 			eal_coremask |= RTE_BIT64(lcore_id);
300 	}
301 
302 	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
303 		core_num = 1 << lcore_id;
304 		if (config->params.coremask & core_num) {
305 			if (eal_coremask & core_num)
306 				continue;
307 			else
308 				return -EINVAL;
309 		}
310 	}
311 
312 	graph_config.params.bsz = config->params.bsz;
313 	graph_config.params.tmo = config->params.tmo;
314 	graph_config.params.coremask = config->params.coremask;
315 	graph_config.model = config->model;
316 	graph_config.pcap_ena = config->pcap_ena;
317 	graph_config.num_pcap_pkts = config->num_pcap_pkts;
318 	graph_config.pcap_file = strdup(config->pcap_file);
319 
320 	return 0;
321 }
322 
323 void
324 graph_pcap_config_get(uint8_t *pcap_ena, uint64_t *num_pkts, char **file)
325 {
326 
327 	*pcap_ena = graph_config.pcap_ena;
328 	*num_pkts = graph_config.num_pcap_pkts;
329 	*file = graph_config.pcap_file;
330 }
331 
332 int
333 graph_walk_start(void *conf)
334 {
335 	struct lcore_conf *qconf;
336 	struct rte_graph *graph;
337 	uint32_t lcore_id;
338 
339 	RTE_SET_USED(conf);
340 
341 	lcore_id = rte_lcore_id();
342 	qconf = &lcore_conf[lcore_id];
343 	graph = qconf->graph;
344 
345 	if (!graph) {
346 		RTE_LOG(INFO, APP_GRAPH, "Lcore %u has nothing to do\n", lcore_id);
347 		return 0;
348 	}
349 
350 	RTE_LOG(INFO, APP_GRAPH, "Entering main loop on lcore %u, graph %s(%p)\n", lcore_id,
351 		qconf->name, graph);
352 
353 	while (likely(!force_quit))
354 		rte_graph_walk(graph);
355 
356 	return 0;
357 }
358 
359 void
360 graph_stats_print(void)
361 {
362 	const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
363 	const char clr[] = {27, '[', '2', 'J', '\0'};
364 	struct rte_graph_cluster_stats_param s_param;
365 	struct rte_graph_cluster_stats *stats;
366 	const char *pattern = "worker_*";
367 
368 	/* Prepare stats object */
369 	memset(&s_param, 0, sizeof(s_param));
370 	s_param.f = stdout;
371 	s_param.socket_id = SOCKET_ID_ANY;
372 	s_param.graph_patterns = &pattern;
373 	s_param.nb_graph_patterns = 1;
374 
375 	stats = rte_graph_cluster_stats_create(&s_param);
376 	if (stats == NULL)
377 		rte_exit(EXIT_FAILURE, "Unable to create stats object\n");
378 
379 	while (!force_quit) {
380 		/* Clear screen and move to top left */
381 		printf("%s%s", clr, topLeft);
382 		rte_graph_cluster_stats_get(stats, 0);
383 		rte_delay_ms(1E3);
384 		if (app_graph_exit())
385 			force_quit = true;
386 	}
387 
388 	rte_graph_cluster_stats_destroy(stats);
389 }
390 
391 uint64_t
392 graph_coremask_get(void)
393 {
394 	return graph_config.params.coremask;
395 }
396 
397 void
398 cmd_graph_parsed(void *parsed_result, __rte_unused struct cmdline *cl, __rte_unused void *data)
399 {
400 	struct cmd_graph_result *res = parsed_result;
401 	struct graph_config config;
402 	char *model_name;
403 	uint8_t model;
404 	int rc;
405 
406 	model_name = res->model_name;
407 	if (strcmp(model_name, "default") == 0) {
408 		model = GRAPH_MODEL_RTC;
409 	} else if (strcmp(model_name, "rtc") == 0) {
410 		model = GRAPH_MODEL_RTC;
411 	} else if (strcmp(model_name, "mcd") == 0) {
412 		model = GRAPH_MODEL_MCD;
413 	} else {
414 		printf(MSG_ARG_NOT_FOUND, "model arguments");
415 		return;
416 	}
417 
418 	config.params.bsz = res->size;
419 	config.params.tmo = res->ns;
420 	config.params.coremask = res->mask;
421 	config.model = model;
422 	config.pcap_ena = res->pcap_ena;
423 	config.num_pcap_pkts = res->num_pcap_pkts;
424 	config.pcap_file = res->pcap_file;
425 	rc = graph_config_add(res->usecase, &config);
426 	if (rc < 0) {
427 		cli_exit();
428 		printf(MSG_CMD_FAIL, res->graph);
429 		rte_exit(EXIT_FAILURE, "coremask is Invalid\n");
430 	}
431 }
432 
433 void
434 cmd_help_graph_parsed(__rte_unused void *parsed_result, __rte_unused struct cmdline *cl,
435 		      __rte_unused void *data)
436 {
437 	size_t len;
438 
439 	len = strlen(conn->msg_out);
440 	conn->msg_out += len;
441 	snprintf(conn->msg_out, conn->msg_out_len_max, "\n%s\n%s\n%s\n%s\n",
442 		 "----------------------------- graph command help -----------------------------",
443 		 cmd_graph_help, "graph start", "graph stats show");
444 
445 	len = strlen(conn->msg_out);
446 	conn->msg_out_len_max -= len;
447 }
448