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