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