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