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, ð_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(¶ms.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(¶ms.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 ¶ms); 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 ¶ms); 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(¶ms.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(¶ms.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 ¶ms); 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 ¶ms); 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 const char cmd_pipeline_table_update_help[] = 783 "pipeline <pipeline_name> table <table_name> update <file_name_add> " 784 "<file_name_delete> <file_name_default>"; 785 786 static void 787 cmd_pipeline_table_update(char **tokens, 788 uint32_t n_tokens, 789 char *out, 790 size_t out_size, 791 void *obj) 792 { 793 struct pipeline *p; 794 char *pipeline_name, *table_name, *line = NULL; 795 char *file_name_add, *file_name_delete, *file_name_default; 796 FILE *file_add = NULL, *file_delete = NULL, *file_default = NULL; 797 uint32_t line_id; 798 int status; 799 800 if (n_tokens != 8) { 801 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 802 return; 803 } 804 805 pipeline_name = tokens[1]; 806 p = pipeline_find(obj, pipeline_name); 807 if (!p || !p->ctl) { 808 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); 809 return; 810 } 811 812 if (strcmp(tokens[2], "table") != 0) { 813 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table"); 814 return; 815 } 816 817 table_name = tokens[3]; 818 819 if (strcmp(tokens[4], "update") != 0) { 820 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "update"); 821 return; 822 } 823 824 file_name_add = tokens[5]; 825 file_name_delete = tokens[6]; 826 file_name_default = tokens[7]; 827 828 /* File open. */ 829 if (strcmp(file_name_add, "none")) { 830 file_add = fopen(file_name_add, "r"); 831 if (!file_add) { 832 snprintf(out, out_size, "Cannot open file %s", 833 file_name_add); 834 goto error; 835 } 836 } 837 838 if (strcmp(file_name_delete, "none")) { 839 file_delete = fopen(file_name_delete, "r"); 840 if (!file_delete) { 841 snprintf(out, out_size, "Cannot open file %s", 842 file_name_delete); 843 goto error; 844 } 845 } 846 847 if (strcmp(file_name_default, "none")) { 848 file_default = fopen(file_name_default, "r"); 849 if (!file_default) { 850 snprintf(out, out_size, "Cannot open file %s", 851 file_name_default); 852 goto error; 853 } 854 } 855 856 if (!file_add && !file_delete && !file_default) { 857 snprintf(out, out_size, "Nothing to be done."); 858 return; 859 } 860 861 /* Buffer allocation. */ 862 line = malloc(2048); 863 if (!line) { 864 snprintf(out, out_size, MSG_OUT_OF_MEMORY); 865 goto error; 866 } 867 868 /* Add. */ 869 if (file_add) 870 for (line_id = 1; ; line_id++) { 871 struct rte_swx_table_entry *entry; 872 873 if (fgets(line, 2048, file_add) == NULL) 874 break; 875 876 entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl, 877 table_name, 878 line); 879 if (!entry) { 880 snprintf(out, out_size, MSG_FILE_ERR, 881 file_name_add, line_id); 882 goto error; 883 } 884 885 status = rte_swx_ctl_pipeline_table_entry_add(p->ctl, 886 table_name, 887 entry); 888 if (status) { 889 snprintf(out, out_size, 890 "Invalid entry in file %s at line %u", 891 file_name_add, line_id); 892 goto error; 893 } 894 } 895 896 897 /* Delete. */ 898 if (file_delete) 899 for (line_id = 1; ; line_id++) { 900 struct rte_swx_table_entry *entry; 901 902 if (fgets(line, 2048, file_delete) == NULL) 903 break; 904 905 entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl, 906 table_name, 907 line); 908 if (!entry) { 909 snprintf(out, out_size, MSG_FILE_ERR, 910 file_name_delete, line_id); 911 goto error; 912 } 913 914 status = rte_swx_ctl_pipeline_table_entry_delete(p->ctl, 915 table_name, 916 entry); 917 if (status) { 918 snprintf(out, out_size, 919 "Invalid entry in file %s at line %u", 920 file_name_delete, line_id); 921 goto error; 922 } 923 } 924 925 /* Default. */ 926 if (file_default) 927 for (line_id = 1; ; line_id++) { 928 struct rte_swx_table_entry *entry; 929 930 if (fgets(line, 2048, file_default) == NULL) 931 break; 932 933 entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl, 934 table_name, 935 line); 936 if (!entry) { 937 snprintf(out, out_size, MSG_FILE_ERR, 938 file_name_default, line_id); 939 goto error; 940 } 941 942 status = rte_swx_ctl_pipeline_table_default_entry_add(p->ctl, 943 table_name, 944 entry); 945 if (status) { 946 snprintf(out, out_size, 947 "Invalid entry in file %s at line %u", 948 file_name_default, line_id); 949 goto error; 950 } 951 } 952 953 status = rte_swx_ctl_pipeline_commit(p->ctl, 1); 954 if (status) { 955 snprintf(out, out_size, "Commit failed."); 956 goto error; 957 } 958 959 960 rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name); 961 962 free(line); 963 if (file_add) 964 fclose(file_add); 965 if (file_delete) 966 fclose(file_delete); 967 if (file_default) 968 fclose(file_default); 969 return; 970 971 error: 972 rte_swx_ctl_pipeline_abort(p->ctl); 973 free(line); 974 if (file_add) 975 fclose(file_add); 976 if (file_delete) 977 fclose(file_delete); 978 if (file_default) 979 fclose(file_default); 980 } 981 982 static const char cmd_pipeline_stats_help[] = 983 "pipeline <pipeline_name> stats\n"; 984 985 static void 986 cmd_pipeline_stats(char **tokens, 987 uint32_t n_tokens, 988 char *out, 989 size_t out_size, 990 void *obj) 991 { 992 struct rte_swx_ctl_pipeline_info info; 993 struct pipeline *p; 994 uint32_t i; 995 int status; 996 997 if (n_tokens != 3) { 998 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 999 return; 1000 } 1001 1002 p = pipeline_find(obj, tokens[1]); 1003 if (!p || !p->ctl) { 1004 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); 1005 return; 1006 } 1007 1008 if (strcmp(tokens[2], "stats")) { 1009 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats"); 1010 return; 1011 } 1012 1013 status = rte_swx_ctl_pipeline_info_get(p->p, &info); 1014 if (status) { 1015 snprintf(out, out_size, "Pipeline info get error."); 1016 return; 1017 } 1018 1019 snprintf(out, out_size, "Input ports:\n"); 1020 out_size -= strlen(out); 1021 out += strlen(out); 1022 1023 for (i = 0; i < info.n_ports_in; i++) { 1024 struct rte_swx_port_in_stats stats; 1025 1026 rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats); 1027 1028 snprintf(out, out_size, "\tPort %u:" 1029 " packets %" PRIu64 1030 " bytes %" PRIu64 1031 " empty %" PRIu64 "\n", 1032 i, stats.n_pkts, stats.n_bytes, stats.n_empty); 1033 out_size -= strlen(out); 1034 out += strlen(out); 1035 } 1036 1037 snprintf(out, out_size, "Output ports:\n"); 1038 out_size -= strlen(out); 1039 out += strlen(out); 1040 1041 for (i = 0; i < info.n_ports_out; i++) { 1042 struct rte_swx_port_out_stats stats; 1043 1044 rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats); 1045 1046 snprintf(out, out_size, "\tPort %u:" 1047 " packets %" PRIu64 1048 " bytes %" PRIu64 "\n", 1049 i, stats.n_pkts, stats.n_bytes); 1050 out_size -= strlen(out); 1051 out += strlen(out); 1052 } 1053 } 1054 1055 static const char cmd_thread_pipeline_enable_help[] = 1056 "thread <thread_id> pipeline <pipeline_name> enable\n"; 1057 1058 static void 1059 cmd_thread_pipeline_enable(char **tokens, 1060 uint32_t n_tokens, 1061 char *out, 1062 size_t out_size, 1063 void *obj) 1064 { 1065 char *pipeline_name; 1066 struct pipeline *p; 1067 uint32_t thread_id; 1068 int status; 1069 1070 if (n_tokens != 5) { 1071 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 1072 return; 1073 } 1074 1075 if (parser_read_uint32(&thread_id, tokens[1]) != 0) { 1076 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id"); 1077 return; 1078 } 1079 1080 if (strcmp(tokens[2], "pipeline") != 0) { 1081 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline"); 1082 return; 1083 } 1084 1085 pipeline_name = tokens[3]; 1086 p = pipeline_find(obj, pipeline_name); 1087 if (!p || !p->ctl) { 1088 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); 1089 return; 1090 } 1091 1092 if (strcmp(tokens[4], "enable") != 0) { 1093 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable"); 1094 return; 1095 } 1096 1097 status = thread_pipeline_enable(thread_id, obj, pipeline_name); 1098 if (status) { 1099 snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable"); 1100 return; 1101 } 1102 } 1103 1104 static const char cmd_thread_pipeline_disable_help[] = 1105 "thread <thread_id> pipeline <pipeline_name> disable\n"; 1106 1107 static void 1108 cmd_thread_pipeline_disable(char **tokens, 1109 uint32_t n_tokens, 1110 char *out, 1111 size_t out_size, 1112 void *obj) 1113 { 1114 struct pipeline *p; 1115 char *pipeline_name; 1116 uint32_t thread_id; 1117 int status; 1118 1119 if (n_tokens != 5) { 1120 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 1121 return; 1122 } 1123 1124 if (parser_read_uint32(&thread_id, tokens[1]) != 0) { 1125 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id"); 1126 return; 1127 } 1128 1129 if (strcmp(tokens[2], "pipeline") != 0) { 1130 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline"); 1131 return; 1132 } 1133 1134 pipeline_name = tokens[3]; 1135 p = pipeline_find(obj, pipeline_name); 1136 if (!p || !p->ctl) { 1137 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); 1138 return; 1139 } 1140 1141 if (strcmp(tokens[4], "disable") != 0) { 1142 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable"); 1143 return; 1144 } 1145 1146 status = thread_pipeline_disable(thread_id, obj, pipeline_name); 1147 if (status) { 1148 snprintf(out, out_size, MSG_CMD_FAIL, 1149 "thread pipeline disable"); 1150 return; 1151 } 1152 } 1153 1154 static void 1155 cmd_help(char **tokens, 1156 uint32_t n_tokens, 1157 char *out, 1158 size_t out_size, 1159 void *arg __rte_unused) 1160 { 1161 tokens++; 1162 n_tokens--; 1163 1164 if (n_tokens == 0) { 1165 snprintf(out, out_size, 1166 "Type 'help <command>' for command details.\n\n" 1167 "List of commands:\n" 1168 "\tmempool\n" 1169 "\tlink\n" 1170 "\tpipeline create\n" 1171 "\tpipeline port in\n" 1172 "\tpipeline port out\n" 1173 "\tpipeline build\n" 1174 "\tpipeline table update\n" 1175 "\tpipeline stats\n" 1176 "\tthread pipeline enable\n" 1177 "\tthread pipeline disable\n\n"); 1178 return; 1179 } 1180 1181 if (strcmp(tokens[0], "mempool") == 0) { 1182 snprintf(out, out_size, "\n%s\n", cmd_mempool_help); 1183 return; 1184 } 1185 1186 if (strcmp(tokens[0], "link") == 0) { 1187 snprintf(out, out_size, "\n%s\n", cmd_link_help); 1188 return; 1189 } 1190 1191 if ((strcmp(tokens[0], "pipeline") == 0) && 1192 (n_tokens == 2) && (strcmp(tokens[1], "create") == 0)) { 1193 snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help); 1194 return; 1195 } 1196 1197 if ((strcmp(tokens[0], "pipeline") == 0) && 1198 (n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) { 1199 if (strcmp(tokens[2], "in") == 0) { 1200 snprintf(out, out_size, "\n%s\n", 1201 cmd_pipeline_port_in_help); 1202 return; 1203 } 1204 1205 if (strcmp(tokens[2], "out") == 0) { 1206 snprintf(out, out_size, "\n%s\n", 1207 cmd_pipeline_port_out_help); 1208 return; 1209 } 1210 } 1211 1212 if ((strcmp(tokens[0], "pipeline") == 0) && 1213 (n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) { 1214 snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help); 1215 return; 1216 } 1217 1218 if ((strcmp(tokens[0], "pipeline") == 0) && 1219 (n_tokens == 3) && 1220 (strcmp(tokens[1], "table") == 0) && 1221 (strcmp(tokens[2], "update") == 0)) { 1222 snprintf(out, out_size, "\n%s\n", 1223 cmd_pipeline_table_update_help); 1224 return; 1225 } 1226 1227 if ((strcmp(tokens[0], "pipeline") == 0) && 1228 (n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) { 1229 snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help); 1230 return; 1231 } 1232 1233 if ((n_tokens == 3) && 1234 (strcmp(tokens[0], "thread") == 0) && 1235 (strcmp(tokens[1], "pipeline") == 0)) { 1236 if (strcmp(tokens[2], "enable") == 0) { 1237 snprintf(out, out_size, "\n%s\n", 1238 cmd_thread_pipeline_enable_help); 1239 return; 1240 } 1241 1242 if (strcmp(tokens[2], "disable") == 0) { 1243 snprintf(out, out_size, "\n%s\n", 1244 cmd_thread_pipeline_disable_help); 1245 return; 1246 } 1247 } 1248 1249 snprintf(out, out_size, "Invalid command\n"); 1250 } 1251 1252 void 1253 cli_process(char *in, char *out, size_t out_size, void *obj) 1254 { 1255 char *tokens[CMD_MAX_TOKENS]; 1256 uint32_t n_tokens = RTE_DIM(tokens); 1257 int status; 1258 1259 if (is_comment(in)) 1260 return; 1261 1262 status = parse_tokenize_string(in, tokens, &n_tokens); 1263 if (status) { 1264 snprintf(out, out_size, MSG_ARG_TOO_MANY, ""); 1265 return; 1266 } 1267 1268 if (n_tokens == 0) 1269 return; 1270 1271 if (strcmp(tokens[0], "help") == 0) { 1272 cmd_help(tokens, n_tokens, out, out_size, obj); 1273 return; 1274 } 1275 1276 if (strcmp(tokens[0], "mempool") == 0) { 1277 cmd_mempool(tokens, n_tokens, out, out_size, obj); 1278 return; 1279 } 1280 1281 if (strcmp(tokens[0], "link") == 0) { 1282 if (strcmp(tokens[1], "show") == 0) { 1283 cmd_link_show(tokens, n_tokens, out, out_size, obj); 1284 return; 1285 } 1286 1287 cmd_link(tokens, n_tokens, out, out_size, obj); 1288 return; 1289 } 1290 1291 if (strcmp(tokens[0], "pipeline") == 0) { 1292 if ((n_tokens >= 3) && 1293 (strcmp(tokens[2], "create") == 0)) { 1294 cmd_pipeline_create(tokens, n_tokens, out, out_size, 1295 obj); 1296 return; 1297 } 1298 1299 if ((n_tokens >= 4) && 1300 (strcmp(tokens[2], "port") == 0) && 1301 (strcmp(tokens[3], "in") == 0)) { 1302 cmd_pipeline_port_in(tokens, n_tokens, out, out_size, 1303 obj); 1304 return; 1305 } 1306 1307 if ((n_tokens >= 4) && 1308 (strcmp(tokens[2], "port") == 0) && 1309 (strcmp(tokens[3], "out") == 0)) { 1310 cmd_pipeline_port_out(tokens, n_tokens, out, out_size, 1311 obj); 1312 return; 1313 } 1314 1315 if ((n_tokens >= 3) && 1316 (strcmp(tokens[2], "build") == 0)) { 1317 cmd_pipeline_build(tokens, n_tokens, out, out_size, 1318 obj); 1319 return; 1320 } 1321 1322 if ((n_tokens >= 3) && 1323 (strcmp(tokens[2], "table") == 0)) { 1324 cmd_pipeline_table_update(tokens, n_tokens, out, 1325 out_size, obj); 1326 return; 1327 } 1328 1329 if ((n_tokens >= 3) && 1330 (strcmp(tokens[2], "stats") == 0)) { 1331 cmd_pipeline_stats(tokens, n_tokens, out, out_size, 1332 obj); 1333 return; 1334 } 1335 } 1336 1337 if (strcmp(tokens[0], "thread") == 0) { 1338 if ((n_tokens >= 5) && 1339 (strcmp(tokens[4], "enable") == 0)) { 1340 cmd_thread_pipeline_enable(tokens, n_tokens, 1341 out, out_size, obj); 1342 return; 1343 } 1344 1345 if ((n_tokens >= 5) && 1346 (strcmp(tokens[4], "disable") == 0)) { 1347 cmd_thread_pipeline_disable(tokens, n_tokens, 1348 out, out_size, obj); 1349 return; 1350 } 1351 } 1352 1353 snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]); 1354 } 1355 1356 int 1357 cli_script_process(const char *file_name, 1358 size_t msg_in_len_max, 1359 size_t msg_out_len_max, 1360 void *obj) 1361 { 1362 char *msg_in = NULL, *msg_out = NULL; 1363 FILE *f = NULL; 1364 1365 /* Check input arguments */ 1366 if ((file_name == NULL) || 1367 (strlen(file_name) == 0) || 1368 (msg_in_len_max == 0) || 1369 (msg_out_len_max == 0)) 1370 return -EINVAL; 1371 1372 msg_in = malloc(msg_in_len_max + 1); 1373 msg_out = malloc(msg_out_len_max + 1); 1374 if ((msg_in == NULL) || 1375 (msg_out == NULL)) { 1376 free(msg_out); 1377 free(msg_in); 1378 return -ENOMEM; 1379 } 1380 1381 /* Open input file */ 1382 f = fopen(file_name, "r"); 1383 if (f == NULL) { 1384 free(msg_out); 1385 free(msg_in); 1386 return -EIO; 1387 } 1388 1389 /* Read file */ 1390 for ( ; ; ) { 1391 if (fgets(msg_in, msg_in_len_max + 1, f) == NULL) 1392 break; 1393 1394 printf("%s", msg_in); 1395 msg_out[0] = 0; 1396 1397 cli_process(msg_in, 1398 msg_out, 1399 msg_out_len_max, 1400 obj); 1401 1402 if (strlen(msg_out)) 1403 printf("%s", msg_out); 1404 } 1405 1406 /* Close file */ 1407 fclose(f); 1408 free(msg_out); 1409 free(msg_in); 1410 return 0; 1411 } 1412