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