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