app/compress-perf: add benchmark test case
[dpdk.git] / app / test-compress-perf / main.c
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 #include "comp_perf.h"
14 #include "comp_perf_test_common.h"
15
16 #define NUM_MAX_XFORMS 16
17 #define NUM_MAX_INFLIGHT_OPS 512
18
19 __extension__
20 const char *cperf_test_type_strs[] = {
21         [CPERF_TEST_TYPE_BENCHMARK] = "benchmark",
22         [CPERF_TEST_TYPE_VERIFY] = "verify"
23 };
24
25 __extension__
26 static const struct cperf_test cperf_testmap[] = {
27         [CPERF_TEST_TYPE_BENCHMARK] = {
28                         cperf_benchmark_test_constructor,
29                         cperf_benchmark_test_runner,
30                         cperf_benchmark_test_destructor
31         },
32         [CPERF_TEST_TYPE_VERIFY] = {
33                         cperf_verify_test_constructor,
34                         cperf_verify_test_runner,
35                         cperf_verify_test_destructor
36         }
37 };
38
39 static int
40 comp_perf_check_capabilities(struct comp_test_data *test_data, uint8_t cdev_id)
41 {
42         const struct rte_compressdev_capabilities *cap;
43
44         cap = rte_compressdev_capability_get(cdev_id,
45                                              RTE_COMP_ALGO_DEFLATE);
46
47         if (cap == NULL) {
48                 RTE_LOG(ERR, USER1,
49                         "Compress device does not support DEFLATE\n");
50                 return -1;
51         }
52
53         uint64_t comp_flags = cap->comp_feature_flags;
54
55         /* Huffman enconding */
56         if (test_data->huffman_enc == RTE_COMP_HUFFMAN_FIXED &&
57                         (comp_flags & RTE_COMP_FF_HUFFMAN_FIXED) == 0) {
58                 RTE_LOG(ERR, USER1,
59                         "Compress device does not supported Fixed Huffman\n");
60                 return -1;
61         }
62
63         if (test_data->huffman_enc == RTE_COMP_HUFFMAN_DYNAMIC &&
64                         (comp_flags & RTE_COMP_FF_HUFFMAN_DYNAMIC) == 0) {
65                 RTE_LOG(ERR, USER1,
66                         "Compress device does not supported Dynamic Huffman\n");
67                 return -1;
68         }
69
70         /* Window size */
71         if (test_data->window_sz != -1) {
72                 if (param_range_check(test_data->window_sz, &cap->window_size)
73                                 < 0) {
74                         RTE_LOG(ERR, USER1,
75                                 "Compress device does not support "
76                                 "this window size\n");
77                         return -1;
78                 }
79         } else
80                 /* Set window size to PMD maximum if none was specified */
81                 test_data->window_sz = cap->window_size.max;
82
83         /* Check if chained mbufs is supported */
84         if (test_data->max_sgl_segs > 1  &&
85                         (comp_flags & RTE_COMP_FF_OOP_SGL_IN_SGL_OUT) == 0) {
86                 RTE_LOG(INFO, USER1, "Compress device does not support "
87                                 "chained mbufs. Max SGL segments set to 1\n");
88                 test_data->max_sgl_segs = 1;
89         }
90
91         /* Level 0 support */
92         if (test_data->level_lst.min == 0 &&
93                         (comp_flags & RTE_COMP_FF_NONCOMPRESSED_BLOCKS) == 0) {
94                 RTE_LOG(ERR, USER1, "Compress device does not support "
95                                 "level 0 (no compression)\n");
96                 return -1;
97         }
98
99         return 0;
100 }
101
102 static int
103 comp_perf_initialize_compressdev(struct comp_test_data *test_data,
104                                  uint8_t *enabled_cdevs)
105 {
106         uint8_t enabled_cdev_count, nb_lcores, cdev_id;
107         unsigned int i, j;
108         int ret;
109
110         enabled_cdev_count = rte_compressdev_devices_get(test_data->driver_name,
111                         enabled_cdevs, RTE_COMPRESS_MAX_DEVS);
112         if (enabled_cdev_count == 0) {
113                 RTE_LOG(ERR, USER1, "No compress devices type %s available\n",
114                                 test_data->driver_name);
115                 return -EINVAL;
116         }
117
118         nb_lcores = rte_lcore_count() - 1;
119         /*
120          * Use fewer devices,
121          * if there are more available than cores.
122          */
123         if (enabled_cdev_count > nb_lcores) {
124                 enabled_cdev_count = nb_lcores;
125                 RTE_LOG(INFO, USER1,
126                         " There's more available devices than cores!"
127                         " The number of devices has been aligned to %d cores\n",
128                         nb_lcores);
129         }
130
131         /*
132          * Calculate number of needed queue pairs, based on the amount
133          * of available number of logical cores and compression devices.
134          * For instance, if there are 4 cores and 2 compression devices,
135          * 2 queue pairs will be set up per device.
136          * One queue pair per one core.
137          * if e.g.: there're 3 cores and 2 compression devices,
138          * 2 queue pairs will be set up per device but one queue pair
139          * will left unused in the last one device
140          */
141         test_data->nb_qps = (nb_lcores % enabled_cdev_count) ?
142                                 (nb_lcores / enabled_cdev_count) + 1 :
143                                 nb_lcores / enabled_cdev_count;
144
145         for (i = 0; i < enabled_cdev_count &&
146                         i < RTE_COMPRESS_MAX_DEVS; i++,
147                                         nb_lcores -= test_data->nb_qps) {
148                 cdev_id = enabled_cdevs[i];
149
150                 struct rte_compressdev_info cdev_info;
151                 uint8_t socket_id = rte_compressdev_socket_id(cdev_id);
152
153                 rte_compressdev_info_get(cdev_id, &cdev_info);
154                 if (cdev_info.max_nb_queue_pairs &&
155                         test_data->nb_qps > cdev_info.max_nb_queue_pairs) {
156                         RTE_LOG(ERR, USER1,
157                                 "Number of needed queue pairs is higher "
158                                 "than the maximum number of queue pairs "
159                                 "per device.\n");
160                         RTE_LOG(ERR, USER1,
161                                 "Lower the number of cores or increase "
162                                 "the number of crypto devices\n");
163                         return -EINVAL;
164                 }
165
166                 if (comp_perf_check_capabilities(test_data, cdev_id) < 0)
167                         return -EINVAL;
168
169                 /* Configure compressdev */
170                 struct rte_compressdev_config config = {
171                         .socket_id = socket_id,
172                         .nb_queue_pairs = nb_lcores > test_data->nb_qps
173                                         ? test_data->nb_qps : nb_lcores,
174                         .max_nb_priv_xforms = NUM_MAX_XFORMS,
175                         .max_nb_streams = 0
176                 };
177
178                 if (rte_compressdev_configure(cdev_id, &config) < 0) {
179                         RTE_LOG(ERR, USER1, "Device configuration failed\n");
180                         return -EINVAL;
181                 }
182
183                 for (j = 0; j < test_data->nb_qps; j++) {
184                         ret = rte_compressdev_queue_pair_setup(cdev_id, j,
185                                         NUM_MAX_INFLIGHT_OPS, socket_id);
186                         if (ret < 0) {
187                                 RTE_LOG(ERR, USER1,
188                               "Failed to setup queue pair %u on compressdev %u",
189                                         j, cdev_id);
190                                 return -EINVAL;
191                         }
192                 }
193
194                 ret = rte_compressdev_start(cdev_id);
195                 if (ret < 0) {
196                         RTE_LOG(ERR, USER1,
197                                 "Failed to start device %u: error %d\n",
198                                 cdev_id, ret);
199                         return -EPERM;
200                 }
201         }
202
203         return enabled_cdev_count;
204 }
205
206 static int
207 comp_perf_dump_input_data(struct comp_test_data *test_data)
208 {
209         FILE *f = fopen(test_data->input_file, "r");
210         int ret = -1;
211
212         if (f == NULL) {
213                 RTE_LOG(ERR, USER1, "Input file could not be opened\n");
214                 return -1;
215         }
216
217         if (fseek(f, 0, SEEK_END) != 0) {
218                 RTE_LOG(ERR, USER1, "Size of input could not be calculated\n");
219                 goto end;
220         }
221         size_t actual_file_sz = ftell(f);
222         /* If extended input data size has not been set,
223          * input data size = file size
224          */
225
226         if (test_data->input_data_sz == 0)
227                 test_data->input_data_sz = actual_file_sz;
228
229         if (test_data->input_data_sz <= 0 || actual_file_sz <= 0 ||
230                         fseek(f, 0, SEEK_SET) != 0) {
231                 RTE_LOG(ERR, USER1, "Size of input could not be calculated\n");
232                 goto end;
233         }
234
235         test_data->input_data = rte_zmalloc_socket(NULL,
236                                 test_data->input_data_sz, 0, rte_socket_id());
237
238         if (test_data->input_data == NULL) {
239                 RTE_LOG(ERR, USER1, "Memory to hold the data from the input "
240                                 "file could not be allocated\n");
241                 goto end;
242         }
243
244         size_t remaining_data = test_data->input_data_sz;
245         uint8_t *data = test_data->input_data;
246
247         while (remaining_data > 0) {
248                 size_t data_to_read = RTE_MIN(remaining_data, actual_file_sz);
249
250                 if (fread(data, data_to_read, 1, f) != 1) {
251                         RTE_LOG(ERR, USER1, "Input file could not be read\n");
252                         goto end;
253                 }
254                 if (fseek(f, 0, SEEK_SET) != 0) {
255                         RTE_LOG(ERR, USER1,
256                                 "Size of input could not be calculated\n");
257                         goto end;
258                 }
259                 remaining_data -= data_to_read;
260                 data += data_to_read;
261         }
262
263         if (test_data->input_data_sz > actual_file_sz)
264                 RTE_LOG(INFO, USER1,
265                   "%zu bytes read from file %s, extending the file %.2f times\n",
266                         test_data->input_data_sz, test_data->input_file,
267                         (double)test_data->input_data_sz/actual_file_sz);
268         else
269                 RTE_LOG(INFO, USER1,
270                         "%zu bytes read from file %s\n",
271                         test_data->input_data_sz, test_data->input_file);
272
273         ret = 0;
274
275 end:
276         fclose(f);
277         return ret;
278 }
279
280 int
281 main(int argc, char **argv)
282 {
283         uint8_t level_idx = 0;
284         int ret, i;
285         struct comp_test_data *test_data;
286         void *ctx[RTE_MAX_LCORE] = {};
287         uint8_t enabled_cdevs[RTE_COMPRESS_MAX_DEVS];
288         int nb_compressdevs = 0;
289         uint16_t total_nb_qps = 0;
290         uint8_t cdev_id;
291         uint32_t lcore_id;
292
293         /* Initialise DPDK EAL */
294         ret = rte_eal_init(argc, argv);
295         if (ret < 0)
296                 rte_exit(EXIT_FAILURE, "Invalid EAL arguments!\n");
297         argc -= ret;
298         argv += ret;
299
300         test_data = rte_zmalloc_socket(NULL, sizeof(struct comp_test_data),
301                                         0, rte_socket_id());
302
303         if (test_data == NULL)
304                 rte_exit(EXIT_FAILURE, "Cannot reserve memory in socket %d\n",
305                                 rte_socket_id());
306
307         ret = EXIT_SUCCESS;
308         test_data->cleanup = ST_TEST_DATA;
309         comp_perf_options_default(test_data);
310
311         if (comp_perf_options_parse(test_data, argc, argv) < 0) {
312                 RTE_LOG(ERR, USER1,
313                         "Parsing one or more user options failed\n");
314                 ret = EXIT_FAILURE;
315                 goto end;
316         }
317
318         if (comp_perf_options_check(test_data) < 0) {
319                 ret = EXIT_FAILURE;
320                 goto end;
321         }
322
323         nb_compressdevs =
324                 comp_perf_initialize_compressdev(test_data, enabled_cdevs);
325
326         if (nb_compressdevs < 1) {
327                 ret = EXIT_FAILURE;
328                 goto end;
329         }
330
331         test_data->cleanup = ST_COMPDEV;
332         if (comp_perf_dump_input_data(test_data) < 0) {
333                 ret = EXIT_FAILURE;
334                 goto end;
335         }
336
337         test_data->cleanup = ST_INPUT_DATA;
338
339         if (test_data->level_lst.inc != 0)
340                 test_data->level = test_data->level_lst.min;
341         else
342                 test_data->level = test_data->level_lst.list[0];
343
344         printf("App uses socket: %u\n", rte_socket_id());
345         printf("Burst size = %u\n", test_data->burst_sz);
346         printf("File size = %zu\n", test_data->input_data_sz);
347
348         test_data->cleanup = ST_DURING_TEST;
349         total_nb_qps = nb_compressdevs * test_data->nb_qps;
350
351         i = 0;
352         uint8_t qp_id = 0, cdev_index = 0;
353
354         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
355
356                 if (i == total_nb_qps)
357                         break;
358
359                 cdev_id = enabled_cdevs[cdev_index];
360                 ctx[i] = cperf_testmap[test_data->test].constructor(
361                                                         cdev_id, qp_id,
362                                                         test_data);
363                 if (ctx[i] == NULL) {
364                         RTE_LOG(ERR, USER1, "Test run constructor failed\n");
365                         goto end;
366                 }
367                 qp_id = (qp_id + 1) % test_data->nb_qps;
368                 if (qp_id == 0)
369                         cdev_index++;
370                 i++;
371         }
372
373         while (test_data->level <= test_data->level_lst.max) {
374
375                 i = 0;
376                 RTE_LCORE_FOREACH_SLAVE(lcore_id) {
377
378                         if (i == total_nb_qps)
379                                 break;
380
381                         rte_eal_remote_launch(
382                                         cperf_testmap[test_data->test].runner,
383                                         ctx[i], lcore_id);
384                         i++;
385                 }
386                 i = 0;
387                 RTE_LCORE_FOREACH_SLAVE(lcore_id) {
388
389                         if (i == total_nb_qps)
390                                 break;
391                         ret |= rte_eal_wait_lcore(lcore_id);
392                         i++;
393                 }
394
395                 if (ret != EXIT_SUCCESS)
396                         break;
397
398                 if (test_data->level_lst.inc != 0)
399                         test_data->level += test_data->level_lst.inc;
400                 else {
401                         if (++level_idx == test_data->level_lst.count)
402                                 break;
403                         test_data->level = test_data->level_lst.list[level_idx];
404                 }
405         }
406
407 end:
408         switch (test_data->cleanup) {
409
410         case ST_DURING_TEST:
411                 i = 0;
412                 RTE_LCORE_FOREACH_SLAVE(lcore_id) {
413                         if (i == total_nb_qps)
414                                 break;
415
416                         if (ctx[i] && cperf_testmap[test_data->test].destructor)
417                                 cperf_testmap[test_data->test].destructor(
418                                                                         ctx[i]);
419                         i++;
420                 }
421                 /* fallthrough */
422         case ST_INPUT_DATA:
423                 rte_free(test_data->input_data);
424                 /* fallthrough */
425         case ST_COMPDEV:
426                 for (i = 0; i < nb_compressdevs &&
427                                 i < RTE_COMPRESS_MAX_DEVS; i++)
428                         rte_compressdev_stop(enabled_cdevs[i]);
429                 /* fallthrough */
430         case ST_TEST_DATA:
431                 rte_free(test_data);
432                 /* fallthrough */
433         case ST_CLEAR:
434         default:
435                 i = rte_eal_cleanup();
436                 if (i) {
437                         RTE_LOG(ERR, USER1,
438                                 "Error from rte_eal_cleanup(), %d\n", i);
439                         ret = i;
440                 }
441                 break;
442         }
443         return ret;
444 }
445
446 __rte_weak void *
447 cperf_benchmark_test_constructor(uint8_t dev_id __rte_unused,
448                                  uint16_t qp_id __rte_unused,
449                                  struct comp_test_data *options __rte_unused)
450 {
451         RTE_LOG(INFO, USER1, "Benchmark test is not supported yet\n");
452         return NULL;
453 }
454
455 __rte_weak void
456 cperf_benchmark_test_destructor(void *arg __rte_unused)
457 {
458
459 }
460
461 __rte_weak int
462 cperf_benchmark_test_runner(void *test_ctx __rte_unused)
463 {
464         return 0;
465 }
466 __rte_weak void *
467 cperf_verify_test_constructor(uint8_t dev_id __rte_unused,
468                                  uint16_t qp_id __rte_unused,
469                                  struct comp_test_data *options __rte_unused)
470 {
471         RTE_LOG(INFO, USER1, "Verify test is not supported yet\n");
472         return NULL;
473 }
474
475 __rte_weak void
476 cperf_verify_test_destructor(void *arg __rte_unused)
477 {
478
479 }
480
481 __rte_weak int
482 cperf_verify_test_runner(void *test_ctx __rte_unused)
483 {
484         return 0;
485 }