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_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 return; 1168 } 1169 1170 if (strcmp(tokens[0], "mempool") == 0) { 1171 snprintf(out, out_size, "\n%s\n", cmd_mempool_help); 1172 return; 1173 } 1174 1175 if (strcmp(tokens[0], "link") == 0) { 1176 snprintf(out, out_size, "\n%s\n", cmd_link_help); 1177 return; 1178 } 1179 1180 if ((strcmp(tokens[0], "pipeline") == 0) && 1181 ((n_tokens == 1) && (strcmp(tokens[2], "create")) == 0)) { 1182 snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help); 1183 return; 1184 } 1185 1186 if ((strcmp(tokens[0], "pipeline") == 0) && 1187 (strcmp(tokens[1], "port") == 0)) { 1188 if ((n_tokens == 3) && (strcmp(tokens[2], "in")) == 0) { 1189 snprintf(out, out_size, "\n%s\n", 1190 cmd_pipeline_port_in_help); 1191 return; 1192 } 1193 1194 if ((n_tokens == 3) && (strcmp(tokens[2], "out")) == 0) { 1195 snprintf(out, out_size, "\n%s\n", 1196 cmd_pipeline_port_out_help); 1197 return; 1198 } 1199 } 1200 1201 if ((strcmp(tokens[0], "pipeline") == 0) && 1202 ((n_tokens >= 2) && (strcmp(tokens[2], "build")) == 0)) { 1203 snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help); 1204 return; 1205 } 1206 1207 if ((strcmp(tokens[0], "pipeline") == 0) && 1208 ((n_tokens >= 2) && (strcmp(tokens[2], "table")) == 0)) { 1209 snprintf(out, out_size, "\n%s\n", 1210 cmd_pipeline_table_update_help); 1211 return; 1212 } 1213 1214 if ((strcmp(tokens[0], "pipeline") == 0) && 1215 ((n_tokens >= 2) && (strcmp(tokens[2], "stats")) == 0)) { 1216 snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help); 1217 return; 1218 } 1219 1220 if ((n_tokens == 3) && 1221 (strcmp(tokens[0], "thread") == 0) && 1222 (strcmp(tokens[1], "pipeline") == 0)) { 1223 if (strcmp(tokens[2], "enable") == 0) { 1224 snprintf(out, out_size, "\n%s\n", 1225 cmd_thread_pipeline_enable_help); 1226 return; 1227 } 1228 1229 if (strcmp(tokens[2], "disable") == 0) { 1230 snprintf(out, out_size, "\n%s\n", 1231 cmd_thread_pipeline_disable_help); 1232 return; 1233 } 1234 } 1235 1236 snprintf(out, out_size, "Invalid command\n"); 1237 } 1238 1239 void 1240 cli_process(char *in, char *out, size_t out_size, void *obj) 1241 { 1242 char *tokens[CMD_MAX_TOKENS]; 1243 uint32_t n_tokens = RTE_DIM(tokens); 1244 int status; 1245 1246 if (is_comment(in)) 1247 return; 1248 1249 status = parse_tokenize_string(in, tokens, &n_tokens); 1250 if (status) { 1251 snprintf(out, out_size, MSG_ARG_TOO_MANY, ""); 1252 return; 1253 } 1254 1255 if (n_tokens == 0) 1256 return; 1257 1258 if (strcmp(tokens[0], "help") == 0) { 1259 cmd_help(tokens, n_tokens, out, out_size, obj); 1260 return; 1261 } 1262 1263 if (strcmp(tokens[0], "mempool") == 0) { 1264 cmd_mempool(tokens, n_tokens, out, out_size, obj); 1265 return; 1266 } 1267 1268 if (strcmp(tokens[0], "link") == 0) { 1269 if (strcmp(tokens[1], "show") == 0) { 1270 cmd_link_show(tokens, n_tokens, out, out_size, obj); 1271 return; 1272 } 1273 1274 cmd_link(tokens, n_tokens, out, out_size, obj); 1275 return; 1276 } 1277 1278 if (strcmp(tokens[0], "pipeline") == 0) { 1279 if ((n_tokens >= 3) && 1280 (strcmp(tokens[2], "create") == 0)) { 1281 cmd_pipeline_create(tokens, n_tokens, out, out_size, 1282 obj); 1283 return; 1284 } 1285 1286 if ((n_tokens >= 4) && 1287 (strcmp(tokens[2], "port") == 0) && 1288 (strcmp(tokens[3], "in") == 0)) { 1289 cmd_pipeline_port_in(tokens, n_tokens, out, out_size, 1290 obj); 1291 return; 1292 } 1293 1294 if ((n_tokens >= 4) && 1295 (strcmp(tokens[2], "port") == 0) && 1296 (strcmp(tokens[3], "out") == 0)) { 1297 cmd_pipeline_port_out(tokens, n_tokens, out, out_size, 1298 obj); 1299 return; 1300 } 1301 1302 if ((n_tokens >= 3) && 1303 (strcmp(tokens[2], "build") == 0)) { 1304 cmd_pipeline_build(tokens, n_tokens, out, out_size, 1305 obj); 1306 return; 1307 } 1308 1309 if ((n_tokens >= 3) && 1310 (strcmp(tokens[2], "table") == 0)) { 1311 cmd_pipeline_table_update(tokens, n_tokens, out, 1312 out_size, obj); 1313 return; 1314 } 1315 1316 if ((n_tokens >= 3) && 1317 (strcmp(tokens[2], "stats") == 0)) { 1318 cmd_pipeline_stats(tokens, n_tokens, out, out_size, 1319 obj); 1320 return; 1321 } 1322 } 1323 1324 if (strcmp(tokens[0], "thread") == 0) { 1325 if ((n_tokens >= 5) && 1326 (strcmp(tokens[4], "enable") == 0)) { 1327 cmd_thread_pipeline_enable(tokens, n_tokens, 1328 out, out_size, obj); 1329 return; 1330 } 1331 1332 if ((n_tokens >= 5) && 1333 (strcmp(tokens[4], "disable") == 0)) { 1334 cmd_thread_pipeline_disable(tokens, n_tokens, 1335 out, out_size, obj); 1336 return; 1337 } 1338 } 1339 1340 snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]); 1341 } 1342 1343 int 1344 cli_script_process(const char *file_name, 1345 size_t msg_in_len_max, 1346 size_t msg_out_len_max, 1347 void *obj) 1348 { 1349 char *msg_in = NULL, *msg_out = NULL; 1350 FILE *f = NULL; 1351 1352 /* Check input arguments */ 1353 if ((file_name == NULL) || 1354 (strlen(file_name) == 0) || 1355 (msg_in_len_max == 0) || 1356 (msg_out_len_max == 0)) 1357 return -EINVAL; 1358 1359 msg_in = malloc(msg_in_len_max + 1); 1360 msg_out = malloc(msg_out_len_max + 1); 1361 if ((msg_in == NULL) || 1362 (msg_out == NULL)) { 1363 free(msg_out); 1364 free(msg_in); 1365 return -ENOMEM; 1366 } 1367 1368 /* Open input file */ 1369 f = fopen(file_name, "r"); 1370 if (f == NULL) { 1371 free(msg_out); 1372 free(msg_in); 1373 return -EIO; 1374 } 1375 1376 /* Read file */ 1377 for ( ; ; ) { 1378 if (fgets(msg_in, msg_in_len_max + 1, f) == NULL) 1379 break; 1380 1381 printf("%s", msg_in); 1382 msg_out[0] = 0; 1383 1384 cli_process(msg_in, 1385 msg_out, 1386 msg_out_len_max, 1387 obj); 1388 1389 if (strlen(msg_out)) 1390 printf("%s", msg_out); 1391 } 1392 1393 /* Close file */ 1394 fclose(f); 1395 free(msg_out); 1396 free(msg_in); 1397 return 0; 1398 } 1399