xref: /dpdk/app/test-security-perf/test_security_perf.c (revision 54140461b60485941da282d8da2db2f2bc19e281)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2022, Marvell
3  */
4 
5 #include <getopt.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 
9 #include <rte_common.h>
10 #include <rte_cryptodev.h>
11 #include <rte_eal.h>
12 #include <rte_lcore.h>
13 #include <rte_malloc.h>
14 #include <rte_security.h>
15 
16 #include <app/test/test_cryptodev.h>
17 #include <app/test/test_cryptodev_security_ipsec.h>
18 #include <app/test/test_cryptodev_security_ipsec_test_vectors.h>
19 
20 #define NB_DESC 4096
21 #define DEF_NB_SESSIONS (16 * 10 * 1024) /* 16 * 10K tunnels */
22 
23 struct lcore_conf {
24 	struct rte_crypto_sym_xform cipher_xform;
25 	struct rte_crypto_sym_xform auth_xform;
26 	struct rte_crypto_sym_xform aead_xform;
27 	uint8_t dev_id;
28 	uint8_t qp_id;
29 	struct test_ctx *ctx;
30 };
31 
32 struct test_ctx {
33 	struct lcore_conf lconf[RTE_MAX_LCORE];
34 	void *sec_ctx;
35 	struct rte_mempool *sess_mp;
36 	struct ipsec_test_data *td;
37 	int nb_sess;
38 	unsigned long td_idx;
39 	uint8_t nb_lcores;
40 	uint8_t nb_cryptodevs;
41 	uint8_t enabled_cdevs[RTE_CRYPTO_MAX_DEVS];
42 	bool is_inbound;
43 };
44 
45 static struct test_ctx ctx;
46 
47 static int
48 cryptodev_init(struct test_ctx *ctx, uint8_t nb_lcores)
49 {
50 	const char dev_names[][RTE_CRYPTODEV_NAME_MAX_LEN] = {
51 		"crypto_cn10k",
52 		"crypto_cn9k",
53 		"crypto_dpaa_sec",
54 		"crypto_dpaa2_sec",
55 	};
56 	struct rte_cryptodev_qp_conf qp_conf;
57 	struct rte_cryptodev_info dev_info;
58 	struct rte_cryptodev_config config;
59 	unsigned int j, nb_qp, qps_reqd;
60 	uint8_t socket_id;
61 	uint32_t dev_cnt;
62 	int ret, core_id;
63 	void *sec_ctx;
64 	uint64_t i;
65 
66 	i = 0;
67 	do {
68 		dev_cnt = rte_cryptodev_devices_get(dev_names[i],
69 						     ctx->enabled_cdevs,
70 						     RTE_CRYPTO_MAX_DEVS);
71 		i++;
72 	} while (dev_cnt == 0 && i < RTE_DIM(dev_names));
73 
74 	if (dev_cnt == 0)
75 		return -1;
76 
77 	/* Check first device for capabilities */
78 	rte_cryptodev_info_get(0, &dev_info);
79 	if (!(dev_info.feature_flags & RTE_CRYPTODEV_FF_SECURITY)) {
80 		RTE_LOG(ERR, USER1,
81 			"Security not supported by the cryptodev\n");
82 		return -1;
83 	}
84 
85 	sec_ctx = rte_cryptodev_get_sec_ctx(0);
86 	ctx->sec_ctx = sec_ctx;
87 
88 	socket_id = rte_socket_id();
89 	qps_reqd = nb_lcores;
90 	core_id = 0;
91 	i = 0;
92 
93 	do {
94 		rte_cryptodev_info_get(i, &dev_info);
95 		qps_reqd = RTE_MIN(dev_info.max_nb_queue_pairs, qps_reqd);
96 
97 		for (j = 0; j < qps_reqd; j++) {
98 			ctx->lconf[core_id].dev_id = i;
99 			ctx->lconf[core_id].qp_id = j;
100 			ctx->lconf[core_id].ctx = ctx;
101 			core_id++;
102 			if (core_id == RTE_MAX_LCORE)
103 				break;
104 		}
105 
106 		nb_qp = j;
107 
108 		memset(&config, 0, sizeof(config));
109 		config.nb_queue_pairs = nb_qp;
110 		config.socket_id = socket_id;
111 
112 		ret = rte_cryptodev_configure(i, &config);
113 		if (ret < 0) {
114 			RTE_LOG(ERR, USER1,
115 				"Could not configure cryptodev - %" PRIu64 "\n",
116 				i);
117 			return -1;
118 		}
119 
120 		memset(&qp_conf, 0, sizeof(qp_conf));
121 		qp_conf.nb_descriptors = NB_DESC;
122 
123 		for (j = 0; j < nb_qp; j++) {
124 			ret = rte_cryptodev_queue_pair_setup(i, j, &qp_conf,
125 							     socket_id);
126 			if (ret < 0) {
127 				RTE_LOG(ERR, USER1,
128 					"Could not configure queue pair:"
129 					" %" PRIu64 " - %d\n", i, j);
130 				return -1;
131 			}
132 		}
133 
134 		ret = rte_cryptodev_start(i);
135 		if (ret < 0) {
136 			RTE_LOG(ERR, USER1, "Could not start cryptodev\n");
137 			return -1;
138 		}
139 
140 		i++;
141 		qps_reqd -= j;
142 
143 	} while (i < dev_cnt && core_id < RTE_MAX_LCORE);
144 
145 	ctx->nb_cryptodevs = i;
146 
147 	return 0;
148 }
149 
150 static int
151 cryptodev_fini(struct test_ctx *ctx)
152 {
153 	int i, ret = 0;
154 
155 	for (i = 0; i < ctx->nb_cryptodevs &&
156 			i < RTE_CRYPTO_MAX_DEVS; i++) {
157 		rte_cryptodev_stop(ctx->enabled_cdevs[i]);
158 		ret = rte_cryptodev_close(ctx->enabled_cdevs[i]);
159 		if (ret)
160 			RTE_LOG(ERR, USER1,
161 					"Crypto device close error %d\n", ret);
162 	}
163 
164 	return ret;
165 }
166 
167 static int
168 mempool_init(struct test_ctx *ctx, uint8_t nb_lcores)
169 {
170 	struct rte_mempool *sess_mpool;
171 	unsigned int sec_sess_sz;
172 	int nb_sess_total;
173 
174 	nb_sess_total = ctx->nb_sess + RTE_MEMPOOL_CACHE_MAX_SIZE * nb_lcores;
175 
176 	sec_sess_sz = rte_security_session_get_size(ctx->sec_ctx);
177 
178 	sess_mpool = rte_cryptodev_sym_session_pool_create("test_sess_mp",
179 			nb_sess_total, sec_sess_sz, RTE_MEMPOOL_CACHE_MAX_SIZE,
180 			0, SOCKET_ID_ANY);
181 	if (sess_mpool == NULL) {
182 		RTE_LOG(ERR, USER1, "Could not create mempool\n");
183 		return -1;
184 	}
185 
186 	ctx->sess_mp = sess_mpool;
187 
188 	return 0;
189 }
190 
191 static int
192 mempool_fini(struct test_ctx *ctx)
193 {
194 	rte_mempool_free(ctx->sess_mp);
195 
196 	return 0;
197 }
198 
199 static int
200 sec_conf_init(struct lcore_conf *conf,
201 	      struct rte_security_session_conf *sess_conf,
202 	      struct rte_security_ipsec_xform *ipsec_xform,
203 	      const struct ipsec_test_data *td)
204 {
205 	uint16_t v6_src[8] = {0x2607, 0xf8b0, 0x400c, 0x0c03, 0x0000, 0x0000,
206 				0x0000, 0x001a};
207 	uint16_t v6_dst[8] = {0x2001, 0x0470, 0xe5bf, 0xdead, 0x4957, 0x2174,
208 				0xe82c, 0x4887};
209 	const struct rte_ipv4_hdr *ipv4 =
210 			(const struct rte_ipv4_hdr *)td->output_text.data;
211 	struct rte_security_capability_idx sec_cap_idx;
212 	const struct rte_security_capability *sec_cap;
213 	enum rte_security_ipsec_sa_direction dir;
214 	uint32_t src, dst;
215 	int salt_len;
216 
217 	/* Copy IPsec xform */
218 	memcpy(ipsec_xform, &td->ipsec_xform, sizeof(*ipsec_xform));
219 
220 	dir = ipsec_xform->direction;
221 
222 	memcpy(&src, &ipv4->src_addr, sizeof(ipv4->src_addr));
223 	memcpy(&dst, &ipv4->dst_addr, sizeof(ipv4->dst_addr));
224 
225 	if (td->ipsec_xform.mode == RTE_SECURITY_IPSEC_SA_MODE_TUNNEL) {
226 		if (td->ipsec_xform.tunnel.type ==
227 				RTE_SECURITY_IPSEC_TUNNEL_IPV4) {
228 			memcpy(&ipsec_xform->tunnel.ipv4.src_ip, &src,
229 			       sizeof(src));
230 			memcpy(&ipsec_xform->tunnel.ipv4.dst_ip, &dst,
231 			       sizeof(dst));
232 
233 		} else {
234 			memcpy(&ipsec_xform->tunnel.ipv6.src_addr, &v6_src,
235 			       sizeof(v6_src));
236 			memcpy(&ipsec_xform->tunnel.ipv6.dst_addr, &v6_dst,
237 			       sizeof(v6_dst));
238 		}
239 	}
240 
241 	sec_cap_idx.action = RTE_SECURITY_ACTION_TYPE_LOOKASIDE_PROTOCOL;
242 	sec_cap_idx.protocol = RTE_SECURITY_PROTOCOL_IPSEC;
243 	sec_cap_idx.ipsec.proto = ipsec_xform->proto;
244 	sec_cap_idx.ipsec.mode = ipsec_xform->mode;
245 	sec_cap_idx.ipsec.direction = ipsec_xform->direction;
246 
247 	sec_cap = rte_security_capability_get(conf->ctx->sec_ctx, &sec_cap_idx);
248 	if (sec_cap == NULL) {
249 		RTE_LOG(ERR, USER1, "Could not get capabilities\n");
250 		return -1;
251 	}
252 
253 	/* Copy cipher session parameters */
254 	if (td[0].aead) {
255 		memcpy(&conf->aead_xform, &td[0].xform.aead,
256 		       sizeof(conf->aead_xform));
257 		conf->aead_xform.aead.key.data = td[0].key.data;
258 		conf->aead_xform.aead.iv.offset = IV_OFFSET;
259 
260 		/* Verify crypto capabilities */
261 		if (test_ipsec_crypto_caps_aead_verify(
262 				sec_cap,
263 				&conf->aead_xform) != 0) {
264 			RTE_LOG(ERR, USER1,
265 				"Crypto capabilities not supported\n");
266 			return -1;
267 		}
268 	} else if (td[0].auth_only) {
269 		memcpy(&conf->auth_xform, &td[0].xform.chain.auth,
270 		       sizeof(conf->auth_xform));
271 		conf->auth_xform.auth.key.data = td[0].auth_key.data;
272 
273 		if (test_ipsec_crypto_caps_auth_verify(
274 				sec_cap,
275 				&conf->auth_xform) != 0) {
276 			RTE_LOG(INFO, USER1,
277 				"Auth crypto capabilities not supported\n");
278 			return -1;
279 		}
280 	} else {
281 		memcpy(&conf->cipher_xform, &td[0].xform.chain.cipher,
282 		       sizeof(conf->cipher_xform));
283 		memcpy(&conf->auth_xform, &td[0].xform.chain.auth,
284 		       sizeof(conf->auth_xform));
285 		conf->cipher_xform.cipher.key.data = td[0].key.data;
286 		conf->cipher_xform.cipher.iv.offset = IV_OFFSET;
287 		conf->auth_xform.auth.key.data = td[0].auth_key.data;
288 
289 		/* Verify crypto capabilities */
290 
291 		if (test_ipsec_crypto_caps_cipher_verify(
292 				sec_cap,
293 				&conf->cipher_xform) != 0) {
294 			RTE_LOG(ERR, USER1,
295 				"Cipher crypto capabilities not supported\n");
296 			return -1;
297 		}
298 
299 		if (test_ipsec_crypto_caps_auth_verify(
300 				sec_cap,
301 				&conf->auth_xform) != 0) {
302 			RTE_LOG(ERR, USER1,
303 				"Auth crypto capabilities not supported\n");
304 			return -1;
305 		}
306 	}
307 
308 	if (test_ipsec_sec_caps_verify(ipsec_xform, sec_cap, 0) != 0)
309 		return -1;
310 
311 	sess_conf->action_type = RTE_SECURITY_ACTION_TYPE_LOOKASIDE_PROTOCOL;
312 	sess_conf->protocol = RTE_SECURITY_PROTOCOL_IPSEC;
313 
314 	if (td[0].aead || td[0].aes_gmac) {
315 		salt_len = RTE_MIN(sizeof(ipsec_xform->salt), td[0].salt.len);
316 		memcpy(&ipsec_xform->salt, td[0].salt.data, salt_len);
317 	}
318 
319 	if (td[0].aead) {
320 		sess_conf->ipsec = *ipsec_xform;
321 		sess_conf->crypto_xform = &conf->aead_xform;
322 	} else if (td[0].auth_only) {
323 		sess_conf->ipsec = *ipsec_xform;
324 		sess_conf->crypto_xform = &conf->auth_xform;
325 	} else {
326 		sess_conf->ipsec = *ipsec_xform;
327 		if (dir == RTE_SECURITY_IPSEC_SA_DIR_EGRESS) {
328 			sess_conf->crypto_xform = &conf->cipher_xform;
329 			conf->cipher_xform.next = &conf->auth_xform;
330 		} else {
331 			sess_conf->crypto_xform = &conf->auth_xform;
332 			conf->auth_xform.next = &conf->cipher_xform;
333 		}
334 	}
335 
336 	return 0;
337 }
338 
339 static int
340 test_security_session_perf(void *arg)
341 {
342 	uint64_t tsc_start, tsc_mid, tsc_end, tsc_setup_dur, tsc_destroy_dur;
343 	struct rte_security_ipsec_xform ipsec_xform;
344 	struct rte_security_session_conf sess_conf;
345 	int i, ret, nb_sessions, nb_sess_total;
346 	struct rte_security_session **sess;
347 	void *sec_ctx;
348 	double setup_rate, destroy_rate;
349 	uint64_t setup_ms, destroy_ms;
350 	struct lcore_conf *conf = arg;
351 	struct rte_mempool *sess_mp;
352 	uint8_t nb_lcores;
353 
354 	nb_lcores = conf->ctx->nb_lcores;
355 	nb_sess_total = conf->ctx->nb_sess;
356 	sec_ctx = conf->ctx->sec_ctx;
357 	sess_mp = conf->ctx->sess_mp;
358 
359 	nb_sessions = nb_sess_total / nb_lcores;
360 
361 	if (conf->qp_id == 0)
362 		nb_sessions += (nb_sess_total - nb_sessions * nb_lcores);
363 
364 	ret = sec_conf_init(conf, &sess_conf, &ipsec_xform,
365 			    &ctx.td[ctx.td_idx]);
366 	if (ret) {
367 		RTE_LOG(ERR, USER1, "Could not initialize session conf\n");
368 		return EXIT_FAILURE;
369 	}
370 
371 	sess = rte_zmalloc(NULL, sizeof(void *) * nb_sessions, 0);
372 
373 	tsc_start = rte_rdtsc_precise();
374 
375 	for (i = 0; i < nb_sessions; i++) {
376 		sess[i] = rte_security_session_create(sec_ctx,
377 						      &sess_conf,
378 						      sess_mp);
379 		if (unlikely(sess[i] == NULL)) {
380 			RTE_LOG(ERR, USER1, "Could not create session\n");
381 			return EXIT_FAILURE;
382 		}
383 	}
384 
385 	tsc_mid = rte_rdtsc_precise();
386 
387 	for (i = 0; i < nb_sessions; i++) {
388 		ret = rte_security_session_destroy(sec_ctx, sess[i]);
389 		if (unlikely(ret < 0)) {
390 			RTE_LOG(ERR, USER1, "Could not destroy session\n");
391 			return EXIT_FAILURE;
392 		}
393 	}
394 
395 	tsc_end = rte_rdtsc_precise();
396 
397 	tsc_setup_dur = tsc_mid - tsc_start;
398 	tsc_destroy_dur = tsc_end - tsc_mid;
399 
400 	setup_ms = tsc_setup_dur * 1000 / rte_get_tsc_hz();
401 	destroy_ms = tsc_destroy_dur * 1000 / rte_get_tsc_hz();
402 
403 	setup_rate = (double)nb_sessions * rte_get_tsc_hz() / tsc_setup_dur;
404 	destroy_rate = (double)nb_sessions * rte_get_tsc_hz() / tsc_destroy_dur;
405 
406 	printf("%20u%20u%20"PRIu64"%20"PRIu64"%20.2f%20.2f\n",
407 			rte_lcore_id(),
408 			nb_sessions,
409 			setup_ms,
410 			destroy_ms,
411 			setup_rate,
412 			destroy_rate);
413 
414 	return EXIT_SUCCESS;
415 }
416 
417 static void
418 usage(char *progname)
419 {
420 	printf("\nusage: %s\n", progname);
421 	printf("  --help     : display this message and exit\n"
422 	       "  --inbound  : test for inbound direction\n"
423 		"           default outbound direction is tested\n"
424 	       "  --nb-sess=N: to set the number of sessions\n"
425 		"           to be created, default is %d\n", DEF_NB_SESSIONS);
426 }
427 
428 static void
429 args_parse(int argc, char **argv)
430 {
431 	char **argvopt;
432 	int n, opt;
433 	int opt_idx;
434 
435 	static const struct option lgopts[] = {
436 		/* Control */
437 		{ "help",    0, 0, 0 },
438 		{ "inbound", 0, 0, 0 },
439 		{ "nb-sess", 1, 0, 0 },
440 		{ NULL, 0, 0, 0 }
441 	};
442 
443 	argvopt = argv;
444 
445 	while ((opt = getopt_long(argc, argvopt, "",
446 				lgopts, &opt_idx)) != EOF) {
447 		switch (opt) {
448 		case 0:
449 			if (strcmp(lgopts[opt_idx].name, "help") == 0) {
450 				usage(argv[0]);
451 				exit(EXIT_SUCCESS);
452 			}
453 
454 			if (strcmp(lgopts[opt_idx].name, "nb-sess") == 0) {
455 				n = atoi(optarg);
456 				if (n >= 0)
457 					ctx.nb_sess = n;
458 				else
459 					rte_exit(EXIT_FAILURE,
460 						"nb-sess should be >= 0\n");
461 				printf("nb-sess %d / ", ctx.nb_sess);
462 			} else if (strcmp(lgopts[opt_idx].name, "inbound") ==
463 				   0) {
464 				ctx.is_inbound = true;
465 				printf("inbound / ");
466 			}
467 
468 			break;
469 
470 		default:
471 			usage(argv[0]);
472 			rte_exit(EXIT_FAILURE, "Invalid option: %s\n",
473 					argv[opt_idx - 1]);
474 			break;
475 		}
476 	}
477 
478 	printf("\n\n");
479 }
480 
481 int
482 main(int argc, char **argv)
483 {
484 	struct ipsec_test_data td_outb[RTE_DIM(alg_list)];
485 	struct ipsec_test_data td_inb[RTE_DIM(alg_list)];
486 	struct ipsec_test_flags flags;
487 	uint32_t lcore_id;
488 	uint8_t nb_lcores;
489 	unsigned long i;
490 	int ret;
491 
492 	memset(&ctx, 0, sizeof(struct test_ctx));
493 	memset(&flags, 0, sizeof(flags));
494 
495 	ret = rte_eal_init(argc, argv);
496 	if (ret < 0)
497 		rte_exit(EXIT_FAILURE, "Invalid EAL arguments!\n");
498 	argc -= ret;
499 	argv += ret;
500 
501 	nb_lcores = rte_lcore_count() - 1;
502 	if (nb_lcores < 1) {
503 		RTE_LOG(ERR, USER1,
504 			"Number of worker cores need to be higher than 1\n");
505 		return -EINVAL;
506 	}
507 
508 	ctx.nb_sess = DEF_NB_SESSIONS + RTE_MEMPOOL_CACHE_MAX_SIZE * nb_lcores;
509 
510 	if (argc > 1)
511 		args_parse(argc, argv);
512 
513 	ctx.nb_lcores = nb_lcores;
514 
515 	ret = cryptodev_init(&ctx, nb_lcores);
516 	if (ret)
517 		goto exit;
518 
519 	ret = mempool_init(&ctx, nb_lcores);
520 	if (ret)
521 		goto cryptodev_fini;
522 
523 	test_ipsec_alg_list_populate();
524 
525 	for (i = 0; i < RTE_DIM(alg_list); i++) {
526 		test_ipsec_td_prepare(alg_list[i].param1,
527 				      alg_list[i].param2,
528 				      &flags,
529 				      &td_outb[i],
530 				      1);
531 		if (ctx.is_inbound)
532 			test_ipsec_td_in_from_out(&td_outb[i], &td_inb[i]);
533 	}
534 
535 	ctx.td = td_outb;
536 	if (ctx.is_inbound)
537 		ctx.td = td_inb;
538 
539 	for (ctx.td_idx = 0; ctx.td_idx < RTE_DIM(alg_list); ctx.td_idx++) {
540 
541 		printf("\n\n    Algorithm combination:");
542 		test_ipsec_display_alg(alg_list[ctx.td_idx].param1,
543 				       alg_list[ctx.td_idx].param2);
544 		printf("    ----------------------");
545 
546 		printf("\n%20s%20s%20s%20s%20s%20s\n\n",
547 			"lcore id", "nb_sessions",
548 			"Setup time(ms)", "Destroy time(ms)",
549 			"Setup rate(sess/s)",
550 			"Destroy rate(sess/sec)");
551 
552 		i = 0;
553 		RTE_LCORE_FOREACH_WORKER(lcore_id) {
554 			rte_eal_remote_launch(test_security_session_perf,
555 					      &ctx.lconf[i],
556 					      lcore_id);
557 			i++;
558 		}
559 
560 		RTE_LCORE_FOREACH_WORKER(lcore_id) {
561 			ret |= rte_eal_wait_lcore(lcore_id);
562 		}
563 
564 	}
565 
566 	cryptodev_fini(&ctx);
567 	mempool_fini(&ctx);
568 
569 	return EXIT_SUCCESS;
570 cryptodev_fini:
571 	cryptodev_fini(&ctx);
572 exit:
573 	return EXIT_FAILURE;
574 
575 }
576