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