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