1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2018 Intel Corporation 3 */ 4 5 #include <getopt.h> 6 #include <stdint.h> 7 #include <stdio.h> 8 #include <string.h> 9 #include <inttypes.h> 10 #include <stdlib.h> 11 #include <errno.h> 12 13 #include <rte_string_fns.h> 14 #include <rte_comp.h> 15 16 #include "comp_perf_options.h" 17 18 #define CPERF_PTEST_TYPE ("ptest") 19 #define CPERF_DRIVER_NAME ("driver-name") 20 #define CPERF_TEST_FILE ("input-file") 21 #define CPERF_SEG_SIZE ("seg-sz") 22 #define CPERF_BURST_SIZE ("burst-sz") 23 #define CPERF_EXTENDED_SIZE ("extended-input-sz") 24 #define CPERF_POOL_SIZE ("pool-sz") 25 #define CPERF_MAX_SGL_SEGS ("max-num-sgl-segs") 26 #define CPERF_NUM_ITER ("num-iter") 27 #define CPERF_OPTYPE ("operation") 28 #define CPERF_ALGO ("algo") 29 #define CPERF_HUFFMAN_ENC ("huffman-enc") 30 #define CPERF_LZ4_FLAGS ("lz4-flags") 31 #define CPERF_LEVEL ("compress-level") 32 #define CPERF_WINDOW_SIZE ("window-sz") 33 #define CPERF_EXTERNAL_MBUFS ("external-mbufs") 34 35 /* cyclecount-specific options */ 36 #define CPERF_CYCLECOUNT_DELAY_US ("cc-delay-us") 37 38 struct name_id_map { 39 const char *name; 40 uint32_t id; 41 }; 42 43 static void 44 usage(char *progname) 45 { 46 printf("%s [EAL options] --\n" 47 " --ptest throughput / verify / pmd-cyclecount\n" 48 " --driver-name NAME: compress driver to use\n" 49 " --input-file NAME: file to compress and decompress\n" 50 " --extended-input-sz N: extend file data up to this size (default: no extension)\n" 51 " --seg-sz N: size of segment to store the data (default: 2048)\n" 52 " --burst-sz N: compress operation burst size\n" 53 " --pool-sz N: mempool size for compress operations/mbufs\n" 54 " (default: 8192)\n" 55 " --max-num-sgl-segs N: maximum number of segments for each mbuf\n" 56 " (default: 16)\n" 57 " --num-iter N: number of times the file will be\n" 58 " compressed/decompressed (default: 10000)\n" 59 " --operation [comp/decomp/comp_and_decomp]: perform test on\n" 60 " compression, decompression or both operations\n" 61 " --algo [null/deflate/lzs/lz4]: perform test on algorithm\n" 62 " null(DMA), deflate, lzs or lz4 (default: deflate)\n" 63 " --huffman-enc [fixed/dynamic/default]: Huffman encoding\n" 64 " (default: dynamic)\n" 65 " --lz4-flags N: flags to configure LZ4 algorithm (default: 0)\n" 66 " --compress-level N: compression level, which could be a single value, list or range\n" 67 " (default: range between 1 and 9)\n" 68 " --window-sz N: base two log value of compression window size\n" 69 " (e.g.: 15 => 32k, default: max supported by PMD)\n" 70 " --external-mbufs: use memzones as external buffers instead of\n" 71 " keeping the data directly in mbuf area\n" 72 " --cc-delay-us N: delay between enqueue and dequeue operations in microseconds\n" 73 " valid only for cyclecount perf test (default: 500 us)\n" 74 " -h: prints this help\n", 75 progname); 76 } 77 78 static int 79 get_str_key_id_mapping(struct name_id_map *map, unsigned int map_len, 80 const char *str_key) 81 { 82 unsigned int i; 83 84 for (i = 0; i < map_len; i++) { 85 86 if (strcmp(str_key, map[i].name) == 0) 87 return map[i].id; 88 } 89 90 return -1; 91 } 92 93 static int 94 parse_cperf_test_type(struct comp_test_data *test_data, const char *arg) 95 { 96 struct name_id_map cperftest_namemap[] = { 97 { 98 comp_perf_test_type_strs[CPERF_TEST_TYPE_THROUGHPUT], 99 CPERF_TEST_TYPE_THROUGHPUT 100 }, 101 { 102 comp_perf_test_type_strs[CPERF_TEST_TYPE_VERIFY], 103 CPERF_TEST_TYPE_VERIFY 104 }, 105 { 106 comp_perf_test_type_strs[CPERF_TEST_TYPE_PMDCC], 107 CPERF_TEST_TYPE_PMDCC 108 } 109 }; 110 111 int id = get_str_key_id_mapping( 112 (struct name_id_map *)cperftest_namemap, 113 RTE_DIM(cperftest_namemap), arg); 114 if (id < 0) { 115 RTE_LOG(ERR, USER1, "failed to parse test type"); 116 return -1; 117 } 118 119 test_data->test = (enum cperf_test_type)id; 120 121 return 0; 122 } 123 124 static int 125 parse_uint32_t(uint32_t *value, const char *arg) 126 { 127 char *end = NULL; 128 unsigned long n = strtoul(arg, &end, 10); 129 130 if ((optarg[0] == '\0') || (end == NULL) || (*end != '\0')) 131 return -1; 132 133 if (n > UINT32_MAX) 134 return -ERANGE; 135 136 *value = (uint32_t) n; 137 138 return 0; 139 } 140 141 static int 142 parse_uint16_t(uint16_t *value, const char *arg) 143 { 144 uint32_t val = 0; 145 int ret = parse_uint32_t(&val, arg); 146 147 if (ret < 0) 148 return ret; 149 150 if (val > UINT16_MAX) 151 return -ERANGE; 152 153 *value = (uint16_t) val; 154 155 return 0; 156 } 157 158 static int 159 parse_uint8_t(uint8_t *value, const char *arg) 160 { 161 uint32_t val = 0; 162 int ret = parse_uint32_t(&val, arg); 163 164 if (ret < 0) 165 return ret; 166 167 if (val > UINT8_MAX) 168 return -ERANGE; 169 170 *value = (uint8_t) val; 171 172 return 0; 173 } 174 175 static int 176 parse_range(const char *arg, uint8_t *min, uint8_t *max, uint8_t *inc) 177 { 178 char *token; 179 uint8_t number; 180 181 char *copy_arg = strdup(arg); 182 183 if (copy_arg == NULL) 184 return -1; 185 186 errno = 0; 187 token = strtok(copy_arg, ":"); 188 189 /* Parse minimum value */ 190 if (token != NULL) { 191 number = strtoul(token, NULL, 10); 192 193 if (errno == EINVAL || errno == ERANGE) 194 goto err_range; 195 196 *min = number; 197 } else 198 goto err_range; 199 200 token = strtok(NULL, ":"); 201 202 /* Parse increment value */ 203 if (token != NULL) { 204 number = strtoul(token, NULL, 10); 205 206 if (errno == EINVAL || errno == ERANGE || 207 number == 0) 208 goto err_range; 209 210 *inc = number; 211 } else 212 goto err_range; 213 214 token = strtok(NULL, ":"); 215 216 /* Parse maximum value */ 217 if (token != NULL) { 218 number = strtoul(token, NULL, 10); 219 220 if (errno == EINVAL || errno == ERANGE || 221 number < *min) 222 goto err_range; 223 224 *max = number; 225 } else 226 goto err_range; 227 228 if (strtok(NULL, ":") != NULL) 229 goto err_range; 230 231 free(copy_arg); 232 return 0; 233 234 err_range: 235 free(copy_arg); 236 return -1; 237 } 238 239 static int 240 parse_list(const char *arg, uint8_t *list, uint8_t *min, uint8_t *max) 241 { 242 char *token; 243 uint32_t number; 244 uint8_t count = 0; 245 uint32_t temp_min; 246 uint32_t temp_max; 247 248 char *copy_arg = strdup(arg); 249 250 if (copy_arg == NULL) 251 return -1; 252 253 errno = 0; 254 token = strtok(copy_arg, ","); 255 256 /* Parse first value */ 257 if (token != NULL) { 258 number = strtoul(token, NULL, 10); 259 260 if (errno == EINVAL || errno == ERANGE) 261 goto err_list; 262 263 list[count++] = number; 264 temp_min = number; 265 temp_max = number; 266 } else 267 goto err_list; 268 269 token = strtok(NULL, ","); 270 271 while (token != NULL) { 272 if (count == MAX_LIST) { 273 RTE_LOG(WARNING, USER1, 274 "Using only the first %u sizes\n", 275 MAX_LIST); 276 break; 277 } 278 279 number = strtoul(token, NULL, 10); 280 281 if (errno == EINVAL || errno == ERANGE) 282 goto err_list; 283 284 list[count++] = number; 285 286 if (number < temp_min) 287 temp_min = number; 288 if (number > temp_max) 289 temp_max = number; 290 291 token = strtok(NULL, ","); 292 } 293 294 if (min) 295 *min = temp_min; 296 if (max) 297 *max = temp_max; 298 299 free(copy_arg); 300 return count; 301 302 err_list: 303 free(copy_arg); 304 return -1; 305 } 306 307 static int 308 parse_num_iter(struct comp_test_data *test_data, const char *arg) 309 { 310 int ret = parse_uint32_t(&test_data->num_iter, arg); 311 312 if (ret) { 313 RTE_LOG(ERR, USER1, "Failed to parse total iteration count\n"); 314 return -1; 315 } 316 317 if (test_data->num_iter == 0) { 318 RTE_LOG(ERR, USER1, 319 "Total number of iterations must be higher than 0\n"); 320 return -1; 321 } 322 323 return ret; 324 } 325 326 static int 327 parse_pool_sz(struct comp_test_data *test_data, const char *arg) 328 { 329 int ret = parse_uint32_t(&test_data->pool_sz, arg); 330 331 if (ret) { 332 RTE_LOG(ERR, USER1, "Failed to parse pool size"); 333 return -1; 334 } 335 336 if (test_data->pool_sz == 0) { 337 RTE_LOG(ERR, USER1, "Pool size must be higher than 0\n"); 338 return -1; 339 } 340 341 return ret; 342 } 343 344 static int 345 parse_burst_sz(struct comp_test_data *test_data, const char *arg) 346 { 347 int ret = parse_uint16_t(&test_data->burst_sz, arg); 348 349 if (ret) { 350 RTE_LOG(ERR, USER1, "Failed to parse burst size/s\n"); 351 return -1; 352 } 353 354 if (test_data->burst_sz == 0) { 355 RTE_LOG(ERR, USER1, "Burst size must be higher than 0\n"); 356 return -1; 357 } 358 359 return 0; 360 } 361 362 static int 363 parse_extended_input_sz(struct comp_test_data *test_data, const char *arg) 364 { 365 uint32_t tmp; 366 int ret = parse_uint32_t(&tmp, arg); 367 368 if (ret) { 369 RTE_LOG(ERR, USER1, "Failed to parse extended input size\n"); 370 return -1; 371 } 372 test_data->input_data_sz = tmp; 373 374 if (tmp == 0) { 375 RTE_LOG(ERR, USER1, 376 "Extended file size must be higher than 0\n"); 377 return -1; 378 } 379 return 0; 380 } 381 382 static int 383 parse_seg_sz(struct comp_test_data *test_data, const char *arg) 384 { 385 int ret = parse_uint16_t(&test_data->seg_sz, arg); 386 387 if (ret) { 388 RTE_LOG(ERR, USER1, "Failed to parse segment size\n"); 389 return -1; 390 } 391 392 if (test_data->seg_sz < MIN_COMPRESSED_BUF_SIZE) { 393 RTE_LOG(ERR, USER1, "Segment size must be higher than %d\n", 394 MIN_COMPRESSED_BUF_SIZE - 1); 395 return -1; 396 } 397 398 if (test_data->seg_sz > MAX_SEG_SIZE) { 399 RTE_LOG(ERR, USER1, "Segment size must be lower than %d\n", 400 MAX_SEG_SIZE + 1); 401 return -1; 402 } 403 404 return 0; 405 } 406 407 static int 408 parse_max_num_sgl_segs(struct comp_test_data *test_data, const char *arg) 409 { 410 int ret = parse_uint16_t(&test_data->max_sgl_segs, arg); 411 412 if (ret) { 413 RTE_LOG(ERR, USER1, 414 "Failed to parse max number of segments per mbuf chain\n"); 415 return -1; 416 } 417 418 if (test_data->max_sgl_segs == 0) { 419 RTE_LOG(ERR, USER1, "Max number of segments per mbuf chain " 420 "must be higher than 0\n"); 421 return -1; 422 } 423 424 return 0; 425 } 426 427 static int 428 parse_window_sz(struct comp_test_data *test_data, const char *arg) 429 { 430 uint16_t tmp; 431 int ret = parse_uint16_t(&tmp, arg); 432 433 if (ret) { 434 RTE_LOG(ERR, USER1, "Failed to parse window size\n"); 435 return -1; 436 } 437 test_data->window_sz = (int)tmp; 438 439 return 0; 440 } 441 442 static int 443 parse_driver_name(struct comp_test_data *test_data, const char *arg) 444 { 445 if (strlen(arg) > (sizeof(test_data->driver_name) - 1)) 446 return -1; 447 448 strlcpy(test_data->driver_name, arg, 449 sizeof(test_data->driver_name)); 450 451 return 0; 452 } 453 454 static int 455 parse_test_file(struct comp_test_data *test_data, const char *arg) 456 { 457 if (strlen(arg) > (sizeof(test_data->input_file) - 1)) 458 return -1; 459 460 strlcpy(test_data->input_file, arg, sizeof(test_data->input_file)); 461 462 return 0; 463 } 464 465 static int 466 parse_op_type(struct comp_test_data *test_data, const char *arg) 467 { 468 struct name_id_map optype_namemap[] = { 469 { 470 "comp", 471 COMPRESS 472 }, 473 { 474 "decomp", 475 DECOMPRESS 476 }, 477 { 478 "comp_and_decomp", 479 COMPRESS_DECOMPRESS 480 } 481 }; 482 483 int id = get_str_key_id_mapping(optype_namemap, 484 RTE_DIM(optype_namemap), arg); 485 if (id < 0) { 486 RTE_LOG(ERR, USER1, "Invalid operation type specified\n"); 487 return -1; 488 } 489 490 test_data->test_op = (enum comp_operation)id; 491 492 return 0; 493 } 494 495 static int 496 parse_algo(struct comp_test_data *test_data, const char *arg) 497 { 498 struct name_id_map algo_namemap[] = { 499 { 500 "null", 501 RTE_COMP_ALGO_NULL 502 }, 503 { 504 "deflate", 505 RTE_COMP_ALGO_DEFLATE 506 }, 507 { 508 "lzs", 509 RTE_COMP_ALGO_LZS 510 }, 511 { 512 "lz4", 513 RTE_COMP_ALGO_LZ4 514 } 515 }; 516 517 int id = get_str_key_id_mapping(algo_namemap, 518 RTE_DIM(algo_namemap), arg); 519 if (id < 0) { 520 RTE_LOG(ERR, USER1, "Invalid algorithm specified\n"); 521 return -1; 522 } 523 524 test_data->test_algo = (enum rte_comp_algorithm)id; 525 526 return 0; 527 } 528 529 static int 530 parse_huffman_enc(struct comp_test_data *test_data, const char *arg) 531 { 532 struct name_id_map huffman_namemap[] = { 533 { 534 "default", 535 RTE_COMP_HUFFMAN_DEFAULT 536 }, 537 { 538 "fixed", 539 RTE_COMP_HUFFMAN_FIXED 540 }, 541 { 542 "dynamic", 543 RTE_COMP_HUFFMAN_DYNAMIC 544 } 545 }; 546 547 int id = get_str_key_id_mapping(huffman_namemap, 548 RTE_DIM(huffman_namemap), arg); 549 if (id < 0) { 550 RTE_LOG(ERR, USER1, "Invalid Huffman encoding specified\n"); 551 return -1; 552 } 553 554 test_data->huffman_enc = (enum rte_comp_huffman)id; 555 556 return 0; 557 } 558 559 static int 560 parse_lz4_flags(struct comp_test_data *test_data, const char *arg) 561 { 562 int ret = parse_uint8_t(&test_data->lz4_flags, arg); 563 564 if (ret) { 565 RTE_LOG(ERR, USER1, "Failed to parse LZ4 flags\n"); 566 return -1; 567 } 568 569 return 0; 570 } 571 572 static int 573 parse_level(struct comp_test_data *test_data, const char *arg) 574 { 575 int ret; 576 577 /* 578 * Try parsing the argument as a range, if it fails, 579 * parse it as a list 580 */ 581 if (parse_range(arg, &test_data->level_lst.min, 582 &test_data->level_lst.max, 583 &test_data->level_lst.inc) < 0) { 584 ret = parse_list(arg, test_data->level_lst.list, 585 &test_data->level_lst.min, 586 &test_data->level_lst.max); 587 if (ret < 0) { 588 RTE_LOG(ERR, USER1, 589 "Failed to parse compression level/s\n"); 590 return -1; 591 } 592 test_data->level_lst.count = ret; 593 594 if (test_data->level_lst.max > RTE_COMP_LEVEL_MAX) { 595 RTE_LOG(ERR, USER1, "Level cannot be higher than %u\n", 596 RTE_COMP_LEVEL_MAX); 597 return -1; 598 } 599 } 600 601 return 0; 602 } 603 604 static int 605 parse_external_mbufs(struct comp_test_data *test_data, 606 const char *arg __rte_unused) 607 { 608 test_data->use_external_mbufs = 1; 609 return 0; 610 } 611 612 static int 613 parse_cyclecount_delay_us(struct comp_test_data *test_data, 614 const char *arg) 615 { 616 int ret = parse_uint32_t(&(test_data->cyclecount_delay), arg); 617 618 if (ret) { 619 RTE_LOG(ERR, USER1, "Failed to parse cyclecount delay\n"); 620 return -1; 621 } 622 return 0; 623 } 624 625 typedef int (*option_parser_t)(struct comp_test_data *test_data, 626 const char *arg); 627 628 struct long_opt_parser { 629 const char *lgopt_name; 630 option_parser_t parser_fn; 631 }; 632 633 static struct option lgopts[] = { 634 { CPERF_PTEST_TYPE, required_argument, 0, 0 }, 635 { CPERF_DRIVER_NAME, required_argument, 0, 0 }, 636 { CPERF_TEST_FILE, required_argument, 0, 0 }, 637 { CPERF_SEG_SIZE, required_argument, 0, 0 }, 638 { CPERF_BURST_SIZE, required_argument, 0, 0 }, 639 { CPERF_EXTENDED_SIZE, required_argument, 0, 0 }, 640 { CPERF_POOL_SIZE, required_argument, 0, 0 }, 641 { CPERF_MAX_SGL_SEGS, required_argument, 0, 0}, 642 { CPERF_NUM_ITER, required_argument, 0, 0 }, 643 { CPERF_OPTYPE, required_argument, 0, 0 }, 644 { CPERF_ALGO, required_argument, 0, 0 }, 645 { CPERF_HUFFMAN_ENC, required_argument, 0, 0 }, 646 { CPERF_LZ4_FLAGS, required_argument, 0, 0 }, 647 { CPERF_LEVEL, required_argument, 0, 0 }, 648 { CPERF_WINDOW_SIZE, required_argument, 0, 0 }, 649 { CPERF_EXTERNAL_MBUFS, 0, 0, 0 }, 650 { CPERF_CYCLECOUNT_DELAY_US, required_argument, 0, 0 }, 651 { NULL, 0, 0, 0 } 652 }; 653 654 static int 655 comp_perf_opts_parse_long(int opt_idx, struct comp_test_data *test_data) 656 { 657 struct long_opt_parser parsermap[] = { 658 { CPERF_PTEST_TYPE, parse_cperf_test_type }, 659 { CPERF_DRIVER_NAME, parse_driver_name }, 660 { CPERF_TEST_FILE, parse_test_file }, 661 { CPERF_SEG_SIZE, parse_seg_sz }, 662 { CPERF_BURST_SIZE, parse_burst_sz }, 663 { CPERF_EXTENDED_SIZE, parse_extended_input_sz }, 664 { CPERF_POOL_SIZE, parse_pool_sz }, 665 { CPERF_MAX_SGL_SEGS, parse_max_num_sgl_segs }, 666 { CPERF_NUM_ITER, parse_num_iter }, 667 { CPERF_OPTYPE, parse_op_type }, 668 { CPERF_ALGO, parse_algo }, 669 { CPERF_HUFFMAN_ENC, parse_huffman_enc }, 670 { CPERF_LZ4_FLAGS, parse_lz4_flags }, 671 { CPERF_LEVEL, parse_level }, 672 { CPERF_WINDOW_SIZE, parse_window_sz }, 673 { CPERF_EXTERNAL_MBUFS, parse_external_mbufs }, 674 { CPERF_CYCLECOUNT_DELAY_US, parse_cyclecount_delay_us }, 675 }; 676 unsigned int i; 677 678 for (i = 0; i < RTE_DIM(parsermap); i++) { 679 if (strncmp(lgopts[opt_idx].name, parsermap[i].lgopt_name, 680 strlen(lgopts[opt_idx].name)) == 0) 681 return parsermap[i].parser_fn(test_data, optarg); 682 } 683 684 return -EINVAL; 685 } 686 687 int 688 comp_perf_options_parse(struct comp_test_data *test_data, int argc, char **argv) 689 { 690 int opt, retval, opt_idx; 691 692 while ((opt = getopt_long(argc, argv, "h", lgopts, &opt_idx)) != EOF) { 693 switch (opt) { 694 case 'h': 695 usage(argv[0]); 696 exit(EXIT_SUCCESS); 697 break; 698 /* long options */ 699 case 0: 700 retval = comp_perf_opts_parse_long(opt_idx, test_data); 701 if (retval != 0) 702 return retval; 703 704 break; 705 706 default: 707 usage(argv[0]); 708 return -EINVAL; 709 } 710 } 711 712 return 0; 713 } 714 715 void 716 comp_perf_options_default(struct comp_test_data *test_data) 717 { 718 test_data->seg_sz = 2048; 719 test_data->burst_sz = 32; 720 test_data->pool_sz = 8192; 721 test_data->max_sgl_segs = 16; 722 test_data->num_iter = 10000; 723 test_data->lz4_flags = 0; 724 test_data->huffman_enc = RTE_COMP_HUFFMAN_DYNAMIC; 725 test_data->test_op = COMPRESS_DECOMPRESS; 726 test_data->test_algo = RTE_COMP_ALGO_DEFLATE; 727 test_data->window_sz = -1; 728 test_data->level_lst.min = RTE_COMP_LEVEL_MIN; 729 test_data->level_lst.max = RTE_COMP_LEVEL_MAX; 730 test_data->level_lst.inc = 1; 731 test_data->test = CPERF_TEST_TYPE_THROUGHPUT; 732 test_data->use_external_mbufs = 0; 733 test_data->cyclecount_delay = 500; 734 } 735 736 int 737 comp_perf_options_check(struct comp_test_data *test_data) 738 { 739 if (test_data->driver_name[0] == '\0') { 740 RTE_LOG(ERR, USER1, "Driver name has to be set\n"); 741 return -1; 742 } 743 744 if (test_data->input_file[0] == '\0') { 745 RTE_LOG(ERR, USER1, "Input file name has to be set\n"); 746 return -1; 747 } 748 749 return 0; 750 } 751