Branch data Line data Source code
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 : 0 : 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 : 0 : fflush(stdout);
45 : 0 : for (count = 0; count <= MAX_CHECK_TIME; count++) {
46 : 0 : if (force_quit)
47 : 0 : return;
48 : :
49 : : all_ports_up = 1;
50 : 0 : RTE_ETH_FOREACH_DEV(portid)
51 : : {
52 : 0 : if (force_quit)
53 : : return;
54 : :
55 : 0 : if ((port_mask & (1 << portid)) == 0)
56 : 0 : continue;
57 : :
58 : : memset(&link, 0, sizeof(link));
59 : : rc = rte_eth_link_get_nowait(portid, &link);
60 : 0 : if (rc < 0) {
61 : : all_ports_up = 0;
62 : 0 : if (print_flag == 1)
63 : 0 : printf("Port %u link get failed: %s\n",
64 : : portid, rte_strerror(-rc));
65 : 0 : continue;
66 : : }
67 : :
68 : : /* Print link rc if flag set */
69 : 0 : if (print_flag == 1) {
70 : 0 : 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 : 0 : continue;
74 : : }
75 : :
76 : : /* Clear all_ports_up flag if any link down */
77 : 0 : 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 : 0 : if (print_flag == 1)
85 : : break;
86 : :
87 : 0 : if (all_ports_up == 0) {
88 : : printf(".");
89 : 0 : fflush(stdout);
90 : : rte_delay_ms(CHECK_INTERVAL);
91 : : }
92 : :
93 : : /* Set the print_flag if all ports up or timeout */
94 : 0 : 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 : 0 : parser_usecases_read(char *usecases)
103 : : {
104 : : bool valid = false;
105 : : uint32_t i, j = 0;
106 : 0 : char *token, *saveptr = NULL;
107 : :
108 : 0 : token = strtok_r(usecases, ",", &saveptr);
109 : 0 : while (token != NULL) {
110 : 0 : for (i = 0; i < RTE_DIM(supported_usecases); i++) {
111 : 0 : if (strcmp(supported_usecases[i], token) == 0) {
112 : 0 : graph_config.usecases[j].enabled = true;
113 : 0 : rte_strscpy(graph_config.usecases[j].name, token, 31);
114 : : valid = true;
115 : 0 : j++;
116 : 0 : break;
117 : : }
118 : : }
119 : 0 : token = strtok_r(NULL, ",", &saveptr);
120 : : }
121 : :
122 : 0 : 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 : 0 : coremask = graph_config.params.coremask;
132 : 0 : while (coremask) {
133 : 0 : if (coremask & 0x1)
134 : 0 : nb_worker++;
135 : :
136 : 0 : coremask = (coremask >> 1);
137 : : }
138 : :
139 : : return nb_worker;
140 : : }
141 : :
142 : : static struct rte_node_ethdev_config *
143 : 0 : 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 : 0 : n_tx_queue = graph_worker_count_get();
151 : : if (n_tx_queue > RTE_MAX_ETHPORTS)
152 : : n_tx_queue = RTE_MAX_ETHPORTS;
153 : :
154 : 0 : RTE_ETH_FOREACH_DEV(portid) {
155 : : /* Skip ports that are not enabled */
156 : 0 : if ((enabled_port_mask & (1 << portid)) == 0) {
157 : : printf("\nSkipping disabled port %d\n", portid);
158 : 0 : continue;
159 : : }
160 : :
161 : 0 : nb_rx_queue = ethdev_rx_num_rx_queues_get(portid);
162 : :
163 : : /* Setup ethdev node config */
164 : 0 : ethdev_conf[nb_conf].port_id = portid;
165 : 0 : ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue;
166 : 0 : ethdev_conf[nb_conf].num_tx_queues = n_tx_queue;
167 : 0 : ethdev_conf[nb_conf].mp = ethdev_mempool_list_by_portid(portid);
168 : 0 : ethdev_conf[nb_conf].mp_count = 1; /* Check with pools */
169 : :
170 : 0 : nb_conf++;
171 : : }
172 : :
173 : 0 : for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
174 : 0 : if (rte_lcore_is_enabled(lcore_id) == 0)
175 : 0 : continue;
176 : :
177 : : qconf = &lcore_conf[lcore_id];
178 : : printf("\nInitializing rx queues on lcore %u ... ", lcore_id);
179 : 0 : fflush(stdout);
180 : :
181 : : /* Init RX queues */
182 : 0 : for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
183 : 0 : portid = qconf->rx_queue_list[queue].port_id;
184 : 0 : queueid = qconf->rx_queue_list[queue].queue_id;
185 : :
186 : : /* Add this queue node to its graph */
187 : 0 : snprintf(qconf->rx_queue_list[queue].node_name, RTE_NODE_NAMESIZE,
188 : : "ethdev_rx-%u-%u", portid, queueid);
189 : : }
190 : 0 : if (qconf->n_rx_queue)
191 : 0 : nb_graphs++;
192 : : }
193 : :
194 : : printf("\n");
195 : :
196 : 0 : ethdev_start();
197 : 0 : check_all_ports_link_status(enabled_port_mask);
198 : :
199 : 0 : *num_conf = nb_conf;
200 : 0 : *num_graphs = nb_graphs;
201 : 0 : return ethdev_conf;
202 : : }
203 : :
204 : : static void
205 : 0 : graph_stats_print_to_file(void)
206 : : {
207 : : struct rte_graph_cluster_stats_param s_param;
208 : : struct rte_graph_cluster_stats *stats;
209 : 0 : const char *pattern = "worker_*";
210 : : FILE *fp = NULL;
211 : : size_t sz, len;
212 : :
213 : : /* Prepare stats object */
214 : 0 : fp = fopen("/tmp/graph_stats.txt", "w+");
215 : 0 : if (fp == NULL)
216 : 0 : rte_exit(EXIT_FAILURE, "Error in opening stats file\n");
217 : :
218 : : memset(&s_param, 0, sizeof(s_param));
219 : 0 : s_param.f = fp;
220 : 0 : s_param.socket_id = SOCKET_ID_ANY;
221 : 0 : s_param.graph_patterns = &pattern;
222 : 0 : s_param.nb_graph_patterns = 1;
223 : :
224 : 0 : stats = rte_graph_cluster_stats_create(&s_param);
225 : 0 : if (stats == NULL)
226 : 0 : rte_exit(EXIT_FAILURE, "Unable to create stats object\n");
227 : :
228 : : /* Clear screen and move to top left */
229 : 0 : rte_graph_cluster_stats_get(stats, 0);
230 : : rte_delay_ms(1E3);
231 : :
232 : 0 : fseek(fp, 0L, SEEK_END);
233 : 0 : sz = ftell(fp);
234 : 0 : fseek(fp, 0L, SEEK_SET);
235 : :
236 : 0 : len = strlen(conn->msg_out);
237 : 0 : conn->msg_out += len;
238 : :
239 : : sz = fread(conn->msg_out, sizeof(char), sz, fp);
240 : 0 : len = strlen(conn->msg_out);
241 : 0 : conn->msg_out_len_max -= len;
242 : 0 : rte_graph_cluster_stats_destroy(stats);
243 : :
244 : 0 : fclose(fp);
245 : 0 : }
246 : :
247 : : void
248 : 0 : cmd_graph_stats_show_parsed(__rte_unused void *parsed_result, __rte_unused struct cmdline *cl,
249 : : __rte_unused void *data)
250 : : {
251 : 0 : graph_stats_print_to_file();
252 : 0 : }
253 : :
254 : : bool
255 : 0 : graph_status_get(void)
256 : : {
257 : 0 : return graph_started;
258 : : }
259 : :
260 : : void
261 : 0 : 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 : 0 : uint32_t nb_graphs = 0, nb_conf, i;
266 : : int rc = -EINVAL;
267 : :
268 : 0 : conf = graph_rxtx_node_config_get(&nb_conf, &nb_graphs);
269 : 0 : for (i = 0; i < MAX_GRAPH_USECASES; i++) {
270 : 0 : if (!strcmp(graph_config.usecases[i].name, "l3fwd")) {
271 : 0 : if (graph_config.usecases[i].enabled) {
272 : 0 : rc = usecase_l3fwd_configure(conf, nb_conf, nb_graphs);
273 : 0 : break;
274 : : }
275 : : }
276 : 0 : if (!strcmp(graph_config.usecases[i].name, "l2fwd")) {
277 : 0 : if (graph_config.usecases[i].enabled) {
278 : 0 : rc = usecase_l2fwd_configure(conf, nb_conf, nb_graphs);
279 : 0 : break;
280 : : }
281 : : }
282 : : }
283 : :
284 : 0 : if (!rc)
285 : 0 : graph_started = true;
286 : 0 : }
287 : :
288 : : static int
289 : 0 : 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 : 0 : if (!parser_usecases_read(usecases))
295 : : return -EINVAL;
296 : :
297 : 0 : for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
298 : 0 : if (rte_lcore_is_enabled(lcore_id))
299 : 0 : eal_coremask |= RTE_BIT64(lcore_id);
300 : : }
301 : :
302 : 0 : for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
303 : 0 : core_num = 1 << lcore_id;
304 : 0 : if (config->params.coremask & core_num) {
305 : 0 : if (eal_coremask & core_num)
306 : 0 : continue;
307 : : else
308 : : return -EINVAL;
309 : : }
310 : : }
311 : :
312 : 0 : graph_config.params.bsz = config->params.bsz;
313 : 0 : graph_config.params.tmo = config->params.tmo;
314 : 0 : graph_config.params.coremask = config->params.coremask;
315 : 0 : graph_config.model = config->model;
316 : 0 : graph_config.pcap_ena = config->pcap_ena;
317 : 0 : graph_config.num_pcap_pkts = config->num_pcap_pkts;
318 : 0 : graph_config.pcap_file = strdup(config->pcap_file);
319 : :
320 : 0 : return 0;
321 : : }
322 : :
323 : : void
324 : 0 : graph_pcap_config_get(uint8_t *pcap_ena, uint64_t *num_pkts, char **file)
325 : : {
326 : :
327 : 0 : *pcap_ena = graph_config.pcap_ena;
328 : 0 : *num_pkts = graph_config.num_pcap_pkts;
329 : 0 : *file = graph_config.pcap_file;
330 : 0 : }
331 : :
332 : : int
333 : 0 : 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 : 0 : graph = qconf->graph;
344 : :
345 : 0 : if (!graph) {
346 : 0 : RTE_LOG(INFO, APP_GRAPH, "Lcore %u has nothing to do\n", lcore_id);
347 : 0 : return 0;
348 : : }
349 : :
350 : 0 : RTE_LOG(INFO, APP_GRAPH, "Entering main loop on lcore %u, graph %s(%p)\n", lcore_id,
351 : : qconf->name, graph);
352 : :
353 : 0 : while (likely(!force_quit))
354 : 0 : rte_graph_walk(graph);
355 : :
356 : : return 0;
357 : : }
358 : :
359 : : void
360 : 0 : graph_stats_print(void)
361 : : {
362 : 0 : const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
363 : 0 : const char clr[] = {27, '[', '2', 'J', '\0'};
364 : : struct rte_graph_cluster_stats_param s_param;
365 : : struct rte_graph_cluster_stats *stats;
366 : 0 : const char *pattern = "worker_*";
367 : :
368 : : /* Prepare stats object */
369 : : memset(&s_param, 0, sizeof(s_param));
370 : 0 : s_param.f = stdout;
371 : 0 : s_param.socket_id = SOCKET_ID_ANY;
372 : 0 : s_param.graph_patterns = &pattern;
373 : 0 : s_param.nb_graph_patterns = 1;
374 : :
375 : 0 : stats = rte_graph_cluster_stats_create(&s_param);
376 : 0 : if (stats == NULL)
377 : 0 : rte_exit(EXIT_FAILURE, "Unable to create stats object\n");
378 : :
379 : 0 : while (!force_quit) {
380 : : /* Clear screen and move to top left */
381 : : printf("%s%s", clr, topLeft);
382 : 0 : rte_graph_cluster_stats_get(stats, 0);
383 : : rte_delay_ms(1E3);
384 : 0 : if (app_graph_exit())
385 : 0 : force_quit = true;
386 : : }
387 : :
388 : 0 : rte_graph_cluster_stats_destroy(stats);
389 : 0 : }
390 : :
391 : : uint64_t
392 : 0 : graph_coremask_get(void)
393 : : {
394 : 0 : return graph_config.params.coremask;
395 : : }
396 : :
397 : : void
398 : 0 : 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 : 0 : model_name = res->model_name;
407 : 0 : if (strcmp(model_name, "default") == 0) {
408 : : model = GRAPH_MODEL_RTC;
409 : 0 : } else if (strcmp(model_name, "rtc") == 0) {
410 : : model = GRAPH_MODEL_RTC;
411 : 0 : } else if (strcmp(model_name, "mcd") == 0) {
412 : : model = GRAPH_MODEL_MCD;
413 : : } else {
414 : : printf(MSG_ARG_NOT_FOUND, "model arguments");
415 : 0 : return;
416 : : }
417 : :
418 : 0 : config.params.bsz = res->size;
419 : 0 : config.params.tmo = res->ns;
420 : 0 : config.params.coremask = res->mask;
421 : 0 : config.model = model;
422 : 0 : config.pcap_ena = res->pcap_ena;
423 : 0 : config.num_pcap_pkts = res->num_pcap_pkts;
424 : 0 : config.pcap_file = res->pcap_file;
425 : 0 : rc = graph_config_add(res->usecase, &config);
426 : 0 : if (rc < 0) {
427 : 0 : cli_exit();
428 : 0 : printf(MSG_CMD_FAIL, res->graph);
429 : 0 : rte_exit(EXIT_FAILURE, "coremask is Invalid\n");
430 : : }
431 : : }
432 : :
433 : : void
434 : 0 : 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 : 0 : len = strlen(conn->msg_out);
440 : 0 : conn->msg_out += len;
441 : 0 : 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 : 0 : len = strlen(conn->msg_out);
446 : 0 : conn->msg_out_len_max -= len;
447 : 0 : }
|