1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2018 Intel Corporation 3 */ 4 5 #include <rte_malloc.h> 6 #include <rte_eal.h> 7 #include <rte_log.h> 8 #include <rte_compressdev.h> 9 10 #include "comp_perf_options.h" 11 #include "comp_perf_test_verify.h" 12 #include "comp_perf_test_benchmark.h" 13 14 #define NUM_MAX_XFORMS 16 15 #define NUM_MAX_INFLIGHT_OPS 512 16 17 #define DIV_CEIL(a, b) ((a) / (b) + ((a) % (b) != 0)) 18 19 /* Cleanup state machine */ 20 static enum cleanup_st { 21 ST_CLEAR = 0, 22 ST_TEST_DATA, 23 ST_COMPDEV, 24 ST_INPUT_DATA, 25 ST_MEMORY_ALLOC, 26 ST_PREPARE_BUF, 27 ST_DURING_TEST 28 } cleanup = ST_CLEAR; 29 30 static int 31 param_range_check(uint16_t size, const struct rte_param_log2_range *range) 32 { 33 unsigned int next_size; 34 35 /* Check lower/upper bounds */ 36 if (size < range->min) 37 return -1; 38 39 if (size > range->max) 40 return -1; 41 42 /* If range is actually only one value, size is correct */ 43 if (range->increment == 0) 44 return 0; 45 46 /* Check if value is one of the supported sizes */ 47 for (next_size = range->min; next_size <= range->max; 48 next_size += range->increment) 49 if (size == next_size) 50 return 0; 51 52 return -1; 53 } 54 55 static int 56 comp_perf_check_capabilities(struct comp_test_data *test_data) 57 { 58 const struct rte_compressdev_capabilities *cap; 59 60 cap = rte_compressdev_capability_get(test_data->cdev_id, 61 RTE_COMP_ALGO_DEFLATE); 62 63 if (cap == NULL) { 64 RTE_LOG(ERR, USER1, 65 "Compress device does not support DEFLATE\n"); 66 return -1; 67 } 68 69 uint64_t comp_flags = cap->comp_feature_flags; 70 71 /* Huffman enconding */ 72 if (test_data->huffman_enc == RTE_COMP_HUFFMAN_FIXED && 73 (comp_flags & RTE_COMP_FF_HUFFMAN_FIXED) == 0) { 74 RTE_LOG(ERR, USER1, 75 "Compress device does not supported Fixed Huffman\n"); 76 return -1; 77 } 78 79 if (test_data->huffman_enc == RTE_COMP_HUFFMAN_DYNAMIC && 80 (comp_flags & RTE_COMP_FF_HUFFMAN_DYNAMIC) == 0) { 81 RTE_LOG(ERR, USER1, 82 "Compress device does not supported Dynamic Huffman\n"); 83 return -1; 84 } 85 86 /* Window size */ 87 if (test_data->window_sz != -1) { 88 if (param_range_check(test_data->window_sz, &cap->window_size) 89 < 0) { 90 RTE_LOG(ERR, USER1, 91 "Compress device does not support " 92 "this window size\n"); 93 return -1; 94 } 95 } else 96 /* Set window size to PMD maximum if none was specified */ 97 test_data->window_sz = cap->window_size.max; 98 99 /* Check if chained mbufs is supported */ 100 if (test_data->max_sgl_segs > 1 && 101 (comp_flags & RTE_COMP_FF_OOP_SGL_IN_SGL_OUT) == 0) { 102 RTE_LOG(INFO, USER1, "Compress device does not support " 103 "chained mbufs. Max SGL segments set to 1\n"); 104 test_data->max_sgl_segs = 1; 105 } 106 107 /* Level 0 support */ 108 if (test_data->level.min == 0 && 109 (comp_flags & RTE_COMP_FF_NONCOMPRESSED_BLOCKS) == 0) { 110 RTE_LOG(ERR, USER1, "Compress device does not support " 111 "level 0 (no compression)\n"); 112 return -1; 113 } 114 115 return 0; 116 } 117 118 static uint32_t 119 find_buf_size(uint32_t input_size) 120 { 121 uint32_t i; 122 123 /* From performance point of view the buffer size should be a 124 * power of 2 but also should be enough to store incompressible data 125 */ 126 127 /* We're looking for nearest power of 2 buffer size, which is greather 128 * than input_size 129 */ 130 uint32_t size = 131 !input_size ? MIN_COMPRESSED_BUF_SIZE : (input_size << 1); 132 133 for (i = UINT16_MAX + 1; !(i & size); i >>= 1) 134 ; 135 136 return i > ((UINT16_MAX + 1) >> 1) 137 ? (uint32_t)((float)input_size * EXPANSE_RATIO) 138 : i; 139 } 140 141 static int 142 comp_perf_allocate_memory(struct comp_test_data *test_data) 143 { 144 145 test_data->out_seg_sz = find_buf_size(test_data->seg_sz); 146 /* Number of segments for input and output 147 * (compression and decompression) 148 */ 149 uint32_t total_segs = DIV_CEIL(test_data->input_data_sz, 150 test_data->seg_sz); 151 test_data->comp_buf_pool = rte_pktmbuf_pool_create("comp_buf_pool", 152 total_segs, 153 0, 0, 154 test_data->out_seg_sz + RTE_PKTMBUF_HEADROOM, 155 rte_socket_id()); 156 if (test_data->comp_buf_pool == NULL) { 157 RTE_LOG(ERR, USER1, "Mbuf mempool could not be created\n"); 158 return -1; 159 } 160 161 cleanup = ST_MEMORY_ALLOC; 162 test_data->decomp_buf_pool = rte_pktmbuf_pool_create("decomp_buf_pool", 163 total_segs, 164 0, 0, test_data->seg_sz + RTE_PKTMBUF_HEADROOM, 165 rte_socket_id()); 166 if (test_data->decomp_buf_pool == NULL) { 167 RTE_LOG(ERR, USER1, "Mbuf mempool could not be created\n"); 168 return -1; 169 } 170 171 test_data->total_bufs = DIV_CEIL(total_segs, test_data->max_sgl_segs); 172 173 test_data->op_pool = rte_comp_op_pool_create("op_pool", 174 test_data->total_bufs, 175 0, 0, rte_socket_id()); 176 if (test_data->op_pool == NULL) { 177 RTE_LOG(ERR, USER1, "Comp op mempool could not be created\n"); 178 return -1; 179 } 180 181 /* 182 * Compressed data might be a bit larger than input data, 183 * if data cannot be compressed 184 */ 185 test_data->compressed_data = rte_zmalloc_socket(NULL, 186 test_data->input_data_sz * EXPANSE_RATIO 187 + MIN_COMPRESSED_BUF_SIZE, 0, 188 rte_socket_id()); 189 if (test_data->compressed_data == NULL) { 190 RTE_LOG(ERR, USER1, "Memory to hold the data from the input " 191 "file could not be allocated\n"); 192 return -1; 193 } 194 195 test_data->decompressed_data = rte_zmalloc_socket(NULL, 196 test_data->input_data_sz, 0, 197 rte_socket_id()); 198 if (test_data->decompressed_data == NULL) { 199 RTE_LOG(ERR, USER1, "Memory to hold the data from the input " 200 "file could not be allocated\n"); 201 return -1; 202 } 203 204 test_data->comp_bufs = rte_zmalloc_socket(NULL, 205 test_data->total_bufs * sizeof(struct rte_mbuf *), 206 0, rte_socket_id()); 207 if (test_data->comp_bufs == NULL) { 208 RTE_LOG(ERR, USER1, "Memory to hold the compression mbufs" 209 " could not be allocated\n"); 210 return -1; 211 } 212 213 test_data->decomp_bufs = rte_zmalloc_socket(NULL, 214 test_data->total_bufs * sizeof(struct rte_mbuf *), 215 0, rte_socket_id()); 216 if (test_data->decomp_bufs == NULL) { 217 RTE_LOG(ERR, USER1, "Memory to hold the decompression mbufs" 218 " could not be allocated\n"); 219 return -1; 220 } 221 return 0; 222 } 223 224 static int 225 comp_perf_dump_input_data(struct comp_test_data *test_data) 226 { 227 FILE *f = fopen(test_data->input_file, "r"); 228 int ret = -1; 229 230 if (f == NULL) { 231 RTE_LOG(ERR, USER1, "Input file could not be opened\n"); 232 return -1; 233 } 234 235 if (fseek(f, 0, SEEK_END) != 0) { 236 RTE_LOG(ERR, USER1, "Size of input could not be calculated\n"); 237 goto end; 238 } 239 size_t actual_file_sz = ftell(f); 240 /* If extended input data size has not been set, 241 * input data size = file size 242 */ 243 244 if (test_data->input_data_sz == 0) 245 test_data->input_data_sz = actual_file_sz; 246 247 if (fseek(f, 0, SEEK_SET) != 0) { 248 RTE_LOG(ERR, USER1, "Size of input could not be calculated\n"); 249 goto end; 250 } 251 252 test_data->input_data = rte_zmalloc_socket(NULL, 253 test_data->input_data_sz, 0, rte_socket_id()); 254 255 if (test_data->input_data == NULL) { 256 RTE_LOG(ERR, USER1, "Memory to hold the data from the input " 257 "file could not be allocated\n"); 258 goto end; 259 } 260 261 size_t remaining_data = test_data->input_data_sz; 262 uint8_t *data = test_data->input_data; 263 264 while (remaining_data > 0) { 265 size_t data_to_read = RTE_MIN(remaining_data, actual_file_sz); 266 267 if (fread(data, data_to_read, 1, f) != 1) { 268 RTE_LOG(ERR, USER1, "Input file could not be read\n"); 269 goto end; 270 } 271 if (fseek(f, 0, SEEK_SET) != 0) { 272 RTE_LOG(ERR, USER1, 273 "Size of input could not be calculated\n"); 274 goto end; 275 } 276 remaining_data -= data_to_read; 277 data += data_to_read; 278 } 279 280 if (test_data->input_data_sz > actual_file_sz) 281 RTE_LOG(INFO, USER1, 282 "%zu bytes read from file %s, extending the file %.2f times\n", 283 test_data->input_data_sz, test_data->input_file, 284 (double)test_data->input_data_sz/actual_file_sz); 285 else 286 RTE_LOG(INFO, USER1, 287 "%zu bytes read from file %s\n", 288 test_data->input_data_sz, test_data->input_file); 289 290 ret = 0; 291 292 end: 293 fclose(f); 294 return ret; 295 } 296 297 static int 298 comp_perf_initialize_compressdev(struct comp_test_data *test_data) 299 { 300 uint8_t enabled_cdev_count; 301 uint8_t enabled_cdevs[RTE_COMPRESS_MAX_DEVS]; 302 303 enabled_cdev_count = rte_compressdev_devices_get(test_data->driver_name, 304 enabled_cdevs, RTE_COMPRESS_MAX_DEVS); 305 if (enabled_cdev_count == 0) { 306 RTE_LOG(ERR, USER1, "No compress devices type %s available\n", 307 test_data->driver_name); 308 return -EINVAL; 309 } 310 311 if (enabled_cdev_count > 1) 312 RTE_LOG(INFO, USER1, 313 "Only the first compress device will be used\n"); 314 315 test_data->cdev_id = enabled_cdevs[0]; 316 317 if (comp_perf_check_capabilities(test_data) < 0) 318 return -1; 319 320 /* Configure compressdev (one device, one queue pair) */ 321 struct rte_compressdev_config config = { 322 .socket_id = rte_socket_id(), 323 .nb_queue_pairs = 1, 324 .max_nb_priv_xforms = NUM_MAX_XFORMS, 325 .max_nb_streams = 0 326 }; 327 328 if (rte_compressdev_configure(test_data->cdev_id, &config) < 0) { 329 RTE_LOG(ERR, USER1, "Device configuration failed\n"); 330 return -1; 331 } 332 333 if (rte_compressdev_queue_pair_setup(test_data->cdev_id, 0, 334 NUM_MAX_INFLIGHT_OPS, rte_socket_id()) < 0) { 335 RTE_LOG(ERR, USER1, "Queue pair setup failed\n"); 336 return -1; 337 } 338 339 if (rte_compressdev_start(test_data->cdev_id) < 0) { 340 RTE_LOG(ERR, USER1, "Device could not be started\n"); 341 return -1; 342 } 343 344 return 0; 345 } 346 347 static int 348 prepare_bufs(struct comp_test_data *test_data) 349 { 350 uint32_t remaining_data = test_data->input_data_sz; 351 uint8_t *input_data_ptr = test_data->input_data; 352 size_t data_sz; 353 uint8_t *data_addr; 354 uint32_t i, j; 355 356 for (i = 0; i < test_data->total_bufs; i++) { 357 /* Allocate data in input mbuf and copy data from input file */ 358 test_data->decomp_bufs[i] = 359 rte_pktmbuf_alloc(test_data->decomp_buf_pool); 360 if (test_data->decomp_bufs[i] == NULL) { 361 RTE_LOG(ERR, USER1, "Could not allocate mbuf\n"); 362 return -1; 363 } 364 365 cleanup = ST_PREPARE_BUF; 366 data_sz = RTE_MIN(remaining_data, test_data->seg_sz); 367 data_addr = (uint8_t *) rte_pktmbuf_append( 368 test_data->decomp_bufs[i], data_sz); 369 if (data_addr == NULL) { 370 RTE_LOG(ERR, USER1, "Could not append data\n"); 371 return -1; 372 } 373 rte_memcpy(data_addr, input_data_ptr, data_sz); 374 375 input_data_ptr += data_sz; 376 remaining_data -= data_sz; 377 378 /* Already one segment in the mbuf */ 379 uint16_t segs_per_mbuf = 1; 380 381 /* Chain mbufs if needed for input mbufs */ 382 while (segs_per_mbuf < test_data->max_sgl_segs 383 && remaining_data > 0) { 384 struct rte_mbuf *next_seg = 385 rte_pktmbuf_alloc(test_data->decomp_buf_pool); 386 387 if (next_seg == NULL) { 388 RTE_LOG(ERR, USER1, 389 "Could not allocate mbuf\n"); 390 return -1; 391 } 392 393 data_sz = RTE_MIN(remaining_data, test_data->seg_sz); 394 data_addr = (uint8_t *)rte_pktmbuf_append(next_seg, 395 data_sz); 396 397 if (data_addr == NULL) { 398 RTE_LOG(ERR, USER1, "Could not append data\n"); 399 return -1; 400 } 401 402 rte_memcpy(data_addr, input_data_ptr, data_sz); 403 input_data_ptr += data_sz; 404 remaining_data -= data_sz; 405 406 if (rte_pktmbuf_chain(test_data->decomp_bufs[i], 407 next_seg) < 0) { 408 RTE_LOG(ERR, USER1, "Could not chain mbufs\n"); 409 return -1; 410 } 411 segs_per_mbuf++; 412 } 413 414 /* Allocate data in output mbuf */ 415 test_data->comp_bufs[i] = 416 rte_pktmbuf_alloc(test_data->comp_buf_pool); 417 if (test_data->comp_bufs[i] == NULL) { 418 RTE_LOG(ERR, USER1, "Could not allocate mbuf\n"); 419 return -1; 420 } 421 data_addr = (uint8_t *) rte_pktmbuf_append( 422 test_data->comp_bufs[i], 423 test_data->out_seg_sz); 424 if (data_addr == NULL) { 425 RTE_LOG(ERR, USER1, "Could not append data\n"); 426 return -1; 427 } 428 429 /* Chain mbufs if needed for output mbufs */ 430 for (j = 1; j < segs_per_mbuf; j++) { 431 struct rte_mbuf *next_seg = 432 rte_pktmbuf_alloc(test_data->comp_buf_pool); 433 434 if (next_seg == NULL) { 435 RTE_LOG(ERR, USER1, 436 "Could not allocate mbuf\n"); 437 return -1; 438 } 439 440 data_addr = (uint8_t *)rte_pktmbuf_append(next_seg, 441 test_data->out_seg_sz); 442 443 if (data_addr == NULL) { 444 RTE_LOG(ERR, USER1, "Could not append data\n"); 445 return -1; 446 } 447 448 if (rte_pktmbuf_chain(test_data->comp_bufs[i], 449 next_seg) < 0) { 450 RTE_LOG(ERR, USER1, "Could not chain mbufs\n"); 451 return -1; 452 } 453 } 454 } 455 456 return 0; 457 } 458 459 static void 460 free_bufs(struct comp_test_data *test_data) 461 { 462 uint32_t i; 463 464 for (i = 0; i < test_data->total_bufs; i++) { 465 rte_pktmbuf_free(test_data->comp_bufs[i]); 466 rte_pktmbuf_free(test_data->decomp_bufs[i]); 467 } 468 } 469 470 471 472 int 473 main(int argc, char **argv) 474 { 475 uint8_t level, level_idx = 0; 476 int ret, i; 477 struct comp_test_data *test_data; 478 479 /* Initialise DPDK EAL */ 480 ret = rte_eal_init(argc, argv); 481 if (ret < 0) 482 rte_exit(EXIT_FAILURE, "Invalid EAL arguments!\n"); 483 argc -= ret; 484 argv += ret; 485 486 test_data = rte_zmalloc_socket(NULL, sizeof(struct comp_test_data), 487 0, rte_socket_id()); 488 489 if (test_data == NULL) 490 rte_exit(EXIT_FAILURE, "Cannot reserve memory in socket %d\n", 491 rte_socket_id()); 492 493 ret = EXIT_SUCCESS; 494 cleanup = ST_TEST_DATA; 495 comp_perf_options_default(test_data); 496 497 if (comp_perf_options_parse(test_data, argc, argv) < 0) { 498 RTE_LOG(ERR, USER1, 499 "Parsing one or more user options failed\n"); 500 ret = EXIT_FAILURE; 501 goto end; 502 } 503 504 if (comp_perf_options_check(test_data) < 0) { 505 ret = EXIT_FAILURE; 506 goto end; 507 } 508 509 if (comp_perf_initialize_compressdev(test_data) < 0) { 510 ret = EXIT_FAILURE; 511 goto end; 512 } 513 514 cleanup = ST_COMPDEV; 515 if (comp_perf_dump_input_data(test_data) < 0) { 516 ret = EXIT_FAILURE; 517 goto end; 518 } 519 520 cleanup = ST_INPUT_DATA; 521 if (comp_perf_allocate_memory(test_data) < 0) { 522 ret = EXIT_FAILURE; 523 goto end; 524 } 525 526 if (prepare_bufs(test_data) < 0) { 527 ret = EXIT_FAILURE; 528 goto end; 529 } 530 531 if (test_data->level.inc != 0) 532 level = test_data->level.min; 533 else 534 level = test_data->level.list[0]; 535 536 printf("Burst size = %u\n", test_data->burst_sz); 537 printf("File size = %zu\n", test_data->input_data_sz); 538 539 printf("%6s%12s%17s%19s%21s%15s%21s%23s%16s\n", 540 "Level", "Comp size", "Comp ratio [%]", 541 "Comp [Cycles/it]", "Comp [Cycles/Byte]", "Comp [Gbps]", 542 "Decomp [Cycles/it]", "Decomp [Cycles/Byte]", "Decomp [Gbps]"); 543 544 cleanup = ST_DURING_TEST; 545 while (level <= test_data->level.max) { 546 547 /* 548 * Run a first iteration, to verify compression and 549 * get the compression ratio for the level 550 */ 551 if (cperf_verification(test_data, level) != EXIT_SUCCESS) 552 break; 553 554 /* 555 * Run benchmarking test 556 */ 557 if (cperf_benchmark(test_data, level) != EXIT_SUCCESS) 558 break; 559 560 printf("%6u%12zu%17.2f%19"PRIu64"%21.2f" 561 "%15.2f%21"PRIu64"%23.2f%16.2f\n", 562 level, test_data->comp_data_sz, test_data->ratio, 563 test_data->comp_tsc_duration[level], 564 test_data->comp_tsc_byte, test_data->comp_gbps, 565 test_data->decomp_tsc_duration[level], 566 test_data->decomp_tsc_byte, test_data->decomp_gbps); 567 568 if (test_data->level.inc != 0) 569 level += test_data->level.inc; 570 else { 571 if (++level_idx == test_data->level.count) 572 break; 573 level = test_data->level.list[level_idx]; 574 } 575 } 576 577 end: 578 switch (cleanup) { 579 580 case ST_DURING_TEST: 581 case ST_PREPARE_BUF: 582 free_bufs(test_data); 583 /* fallthrough */ 584 case ST_MEMORY_ALLOC: 585 rte_free(test_data->decomp_bufs); 586 rte_free(test_data->comp_bufs); 587 rte_free(test_data->decompressed_data); 588 rte_free(test_data->compressed_data); 589 rte_mempool_free(test_data->op_pool); 590 rte_mempool_free(test_data->decomp_buf_pool); 591 rte_mempool_free(test_data->comp_buf_pool); 592 /* fallthrough */ 593 case ST_INPUT_DATA: 594 rte_free(test_data->input_data); 595 /* fallthrough */ 596 case ST_COMPDEV: 597 if (test_data->cdev_id != -1) 598 rte_compressdev_stop(test_data->cdev_id); 599 /* fallthrough */ 600 case ST_TEST_DATA: 601 rte_free(test_data); 602 /* fallthrough */ 603 case ST_CLEAR: 604 default: 605 i = rte_eal_cleanup(); 606 if (i) { 607 RTE_LOG(ERR, USER1, 608 "Error from rte_eal_cleanup(), %d\n", i); 609 ret = i; 610 } 611 break; 612 } 613 return ret; 614 } 615