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