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> bsz <size> tmo <ns> coremask <bitmask> " 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"}; 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; 107 108 token = strtok(usecases, ","); 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(NULL, ","); 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 static void 248 cli_graph_stats(__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 static void 261 cli_graph_start(__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 } 277 278 if (!rc) 279 graph_started = true; 280 } 281 282 static int 283 graph_config_add(char *usecases, struct graph_config *config) 284 { 285 uint64_t lcore_id, core_num; 286 uint64_t eal_coremask = 0; 287 288 if (!parser_usecases_read(usecases)) 289 return -EINVAL; 290 291 for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { 292 if (rte_lcore_is_enabled(lcore_id)) 293 eal_coremask |= RTE_BIT64(lcore_id); 294 } 295 296 for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { 297 core_num = 1 << lcore_id; 298 if (config->params.coremask & core_num) { 299 if (eal_coremask & core_num) 300 continue; 301 else 302 return -EINVAL; 303 } 304 } 305 306 graph_config.params.bsz = config->params.bsz; 307 graph_config.params.tmo = config->params.tmo; 308 graph_config.params.coremask = config->params.coremask; 309 graph_config.model = config->model; 310 graph_config.pcap_ena = config->pcap_ena; 311 graph_config.num_pcap_pkts = config->num_pcap_pkts; 312 graph_config.pcap_file = strdup(config->pcap_file); 313 314 return 0; 315 } 316 317 void 318 graph_pcap_config_get(uint8_t *pcap_ena, uint64_t *num_pkts, char **file) 319 { 320 321 *pcap_ena = graph_config.pcap_ena; 322 *num_pkts = graph_config.num_pcap_pkts; 323 *file = graph_config.pcap_file; 324 } 325 326 int 327 graph_walk_start(void *conf) 328 { 329 struct lcore_conf *qconf; 330 struct rte_graph *graph; 331 uint32_t lcore_id; 332 333 RTE_SET_USED(conf); 334 335 lcore_id = rte_lcore_id(); 336 qconf = &lcore_conf[lcore_id]; 337 graph = qconf->graph; 338 339 if (!graph) { 340 RTE_LOG(INFO, APP_GRAPH, "Lcore %u has nothing to do\n", lcore_id); 341 return 0; 342 } 343 344 RTE_LOG(INFO, APP_GRAPH, "Entering main loop on lcore %u, graph %s(%p)\n", lcore_id, 345 qconf->name, graph); 346 347 while (likely(!force_quit)) 348 rte_graph_walk(graph); 349 350 return 0; 351 } 352 353 void 354 graph_stats_print(void) 355 { 356 const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; 357 const char clr[] = {27, '[', '2', 'J', '\0'}; 358 struct rte_graph_cluster_stats_param s_param; 359 struct rte_graph_cluster_stats *stats; 360 const char *pattern = "worker_*"; 361 362 /* Prepare stats object */ 363 memset(&s_param, 0, sizeof(s_param)); 364 s_param.f = stdout; 365 s_param.socket_id = SOCKET_ID_ANY; 366 s_param.graph_patterns = &pattern; 367 s_param.nb_graph_patterns = 1; 368 369 stats = rte_graph_cluster_stats_create(&s_param); 370 if (stats == NULL) 371 rte_exit(EXIT_FAILURE, "Unable to create stats object\n"); 372 373 while (!force_quit) { 374 /* Clear screen and move to top left */ 375 printf("%s%s", clr, topLeft); 376 rte_graph_cluster_stats_get(stats, 0); 377 rte_delay_ms(1E3); 378 if (app_graph_exit()) 379 force_quit = true; 380 } 381 382 rte_graph_cluster_stats_destroy(stats); 383 } 384 385 uint64_t 386 graph_coremask_get(void) 387 { 388 return graph_config.params.coremask; 389 } 390 391 static void 392 cli_graph(void *parsed_result, __rte_unused struct cmdline *cl, __rte_unused void *data) 393 { 394 struct graph_config_cmd_tokens *res = parsed_result; 395 struct graph_config config; 396 char *model_name; 397 uint8_t model; 398 int rc; 399 400 model_name = res->model_name; 401 if (strcmp(model_name, "default") == 0) { 402 model = GRAPH_MODEL_RTC; 403 } else if (strcmp(model_name, "rtc") == 0) { 404 model = GRAPH_MODEL_RTC; 405 } else if (strcmp(model_name, "mcd") == 0) { 406 model = GRAPH_MODEL_MCD; 407 } else { 408 printf(MSG_ARG_NOT_FOUND, "model arguments"); 409 return; 410 } 411 412 config.params.bsz = res->size; 413 config.params.tmo = res->ns; 414 config.params.coremask = res->mask; 415 config.model = model; 416 config.pcap_ena = res->pcap_ena; 417 config.num_pcap_pkts = res->num_pcap_pkts; 418 config.pcap_file = res->pcap_file; 419 rc = graph_config_add(res->usecase, &config); 420 if (rc < 0) { 421 cli_exit(); 422 printf(MSG_CMD_FAIL, res->graph); 423 rte_exit(EXIT_FAILURE, "coremask is Invalid\n"); 424 } 425 } 426 427 static void 428 cli_graph_help(__rte_unused void *parsed_result, __rte_unused struct cmdline *cl, 429 __rte_unused void *data) 430 { 431 size_t len; 432 433 len = strlen(conn->msg_out); 434 conn->msg_out += len; 435 snprintf(conn->msg_out, conn->msg_out_len_max, "\n%s\n%s\n%s\n%s\n", 436 "----------------------------- graph command help -----------------------------", 437 cmd_graph_help, "graph start", "graph stats show"); 438 439 len = strlen(conn->msg_out); 440 conn->msg_out_len_max -= len; 441 } 442 443 cmdline_parse_token_string_t graph_display_graph = 444 TOKEN_STRING_INITIALIZER(struct graph_stats_cmd_tokens, graph, "graph"); 445 cmdline_parse_token_string_t graph_display_stats = 446 TOKEN_STRING_INITIALIZER(struct graph_stats_cmd_tokens, stats, "stats"); 447 cmdline_parse_token_string_t graph_display_show = 448 TOKEN_STRING_INITIALIZER(struct graph_stats_cmd_tokens, show, "show"); 449 450 cmdline_parse_inst_t graph_stats_cmd_ctx = { 451 .f = cli_graph_stats, 452 .data = NULL, 453 .help_str = "graph stats show", 454 .tokens = { 455 (void *)&graph_display_graph, 456 (void *)&graph_display_stats, 457 (void *)&graph_display_show, 458 NULL, 459 }, 460 }; 461 462 cmdline_parse_token_string_t graph_config_start_graph = 463 TOKEN_STRING_INITIALIZER(struct graph_start_cmd_tokens, graph, "graph"); 464 cmdline_parse_token_string_t graph_config_start = 465 TOKEN_STRING_INITIALIZER(struct graph_start_cmd_tokens, start, "start"); 466 467 cmdline_parse_inst_t graph_start_cmd_ctx = { 468 .f = cli_graph_start, 469 .data = NULL, 470 .help_str = "graph start", 471 .tokens = { 472 (void *)&graph_config_start_graph, 473 (void *)&graph_config_start, 474 NULL, 475 }, 476 }; 477 478 cmdline_parse_token_string_t graph_config_add_graph = 479 TOKEN_STRING_INITIALIZER(struct graph_config_cmd_tokens, graph, "graph"); 480 cmdline_parse_token_string_t graph_config_add_usecase = 481 TOKEN_STRING_INITIALIZER(struct graph_config_cmd_tokens, usecase, NULL); 482 cmdline_parse_token_string_t graph_config_add_coremask = 483 TOKEN_STRING_INITIALIZER(struct graph_config_cmd_tokens, coremask, "coremask"); 484 cmdline_parse_token_num_t graph_config_add_mask = 485 TOKEN_NUM_INITIALIZER(struct graph_config_cmd_tokens, mask, RTE_UINT64); 486 cmdline_parse_token_string_t graph_config_add_bsz = 487 TOKEN_STRING_INITIALIZER(struct graph_config_cmd_tokens, bsz, "bsz"); 488 cmdline_parse_token_num_t graph_config_add_size = 489 TOKEN_NUM_INITIALIZER(struct graph_config_cmd_tokens, size, RTE_UINT16); 490 cmdline_parse_token_string_t graph_config_add_tmo = 491 TOKEN_STRING_INITIALIZER(struct graph_config_cmd_tokens, tmo, "tmo"); 492 cmdline_parse_token_num_t graph_config_add_ns = 493 TOKEN_NUM_INITIALIZER(struct graph_config_cmd_tokens, ns, RTE_UINT64); 494 cmdline_parse_token_string_t graph_config_add_model = 495 TOKEN_STRING_INITIALIZER(struct graph_config_cmd_tokens, model, "model"); 496 cmdline_parse_token_string_t graph_config_add_model_name = 497 TOKEN_STRING_INITIALIZER(struct graph_config_cmd_tokens, model_name, "rtc#mcd#default"); 498 cmdline_parse_token_string_t graph_config_add_capt_ena = 499 TOKEN_STRING_INITIALIZER(struct graph_config_cmd_tokens, capt_ena, "pcap_enable"); 500 cmdline_parse_token_num_t graph_config_add_pcap_ena = 501 TOKEN_NUM_INITIALIZER(struct graph_config_cmd_tokens, pcap_ena, RTE_UINT8); 502 cmdline_parse_token_string_t graph_config_add_capt_pkts_count = 503 TOKEN_STRING_INITIALIZER(struct graph_config_cmd_tokens, capt_pkts_count, "num_pcap_pkts"); 504 cmdline_parse_token_num_t graph_config_add_num_pcap_pkts = 505 TOKEN_NUM_INITIALIZER(struct graph_config_cmd_tokens, num_pcap_pkts, RTE_UINT64); 506 cmdline_parse_token_string_t graph_config_add_capt_file = 507 TOKEN_STRING_INITIALIZER(struct graph_config_cmd_tokens, capt_file, "pcap_file"); 508 cmdline_parse_token_string_t graph_config_add_pcap_file = 509 TOKEN_STRING_INITIALIZER(struct graph_config_cmd_tokens, pcap_file, NULL); 510 511 cmdline_parse_inst_t graph_config_cmd_ctx = { 512 .f = cli_graph, 513 .data = NULL, 514 .help_str = cmd_graph_help, 515 .tokens = { 516 (void *)&graph_config_add_graph, 517 (void *)&graph_config_add_usecase, 518 (void *)&graph_config_add_coremask, 519 (void *)&graph_config_add_mask, 520 (void *)&graph_config_add_bsz, 521 (void *)&graph_config_add_size, 522 (void *)&graph_config_add_tmo, 523 (void *)&graph_config_add_ns, 524 (void *)&graph_config_add_model, 525 (void *)&graph_config_add_model_name, 526 (void *)&graph_config_add_capt_ena, 527 (void *)&graph_config_add_pcap_ena, 528 (void *)&graph_config_add_capt_pkts_count, 529 (void *)&graph_config_add_num_pcap_pkts, 530 (void *)&graph_config_add_capt_file, 531 (void *)&graph_config_add_pcap_file, 532 NULL, 533 }, 534 }; 535 536 cmdline_parse_token_string_t graph_help_cmd = 537 TOKEN_STRING_INITIALIZER(struct graph_help_cmd_tokens, help, "help"); 538 cmdline_parse_token_string_t graph_help_graph = 539 TOKEN_STRING_INITIALIZER(struct graph_help_cmd_tokens, graph, "graph"); 540 541 cmdline_parse_inst_t graph_help_cmd_ctx = { 542 .f = cli_graph_help, 543 .data = NULL, 544 .help_str = "", 545 .tokens = { 546 (void *)&graph_help_cmd, 547 (void *)&graph_help_graph, 548 NULL, 549 }, 550 }; 551