xref: /dpdk/app/test-crypto-perf/cperf_test_throughput.c (revision dc348f2e81a94dd3b8a32c2f882483227796905d)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2016-2017 Intel Corporation
3  */
4 
5 #include <stdlib.h>
6 
7 #include <rte_malloc.h>
8 #include <rte_cycles.h>
9 #include <rte_crypto.h>
10 #include <rte_cryptodev.h>
11 
12 #include "cperf_test_throughput.h"
13 #include "cperf_ops.h"
14 #include "cperf_test_common.h"
15 
16 struct cperf_throughput_ctx {
17 	uint8_t dev_id;
18 	uint16_t qp_id;
19 	uint8_t lcore_id;
20 
21 	struct rte_mempool *pool;
22 
23 	void *sess;
24 
25 	cperf_populate_ops_t populate_ops;
26 
27 	uint32_t src_buf_offset;
28 	uint32_t dst_buf_offset;
29 
30 	const struct cperf_options *options;
31 	const struct cperf_test_vector *test_vector;
32 };
33 
34 static void
35 cperf_throughput_test_free(struct cperf_throughput_ctx *ctx)
36 {
37 	if (!ctx)
38 		return;
39 	if (ctx->sess) {
40 		if (ctx->options->op_type == CPERF_ASYM_MODEX)
41 			rte_cryptodev_asym_session_free(ctx->dev_id,
42 					(void *)ctx->sess);
43 #ifdef RTE_LIB_SECURITY
44 		else if (ctx->options->op_type == CPERF_PDCP ||
45 			 ctx->options->op_type == CPERF_DOCSIS ||
46 			 ctx->options->op_type == CPERF_IPSEC) {
47 			struct rte_security_ctx *sec_ctx =
48 				(struct rte_security_ctx *)
49 					rte_cryptodev_get_sec_ctx(ctx->dev_id);
50 			rte_security_session_destroy(
51 				sec_ctx,
52 				(void *)ctx->sess);
53 		}
54 #endif
55 		else
56 			rte_cryptodev_sym_session_free(ctx->dev_id, ctx->sess);
57 	}
58 	rte_mempool_free(ctx->pool);
59 
60 	rte_free(ctx);
61 }
62 
63 void *
64 cperf_throughput_test_constructor(struct rte_mempool *sess_mp,
65 		uint8_t dev_id, uint16_t qp_id,
66 		const struct cperf_options *options,
67 		const struct cperf_test_vector *test_vector,
68 		const struct cperf_op_fns *op_fns)
69 {
70 	struct cperf_throughput_ctx *ctx = NULL;
71 
72 	ctx = rte_malloc(NULL, sizeof(struct cperf_throughput_ctx), 0);
73 	if (ctx == NULL)
74 		goto err;
75 
76 	ctx->dev_id = dev_id;
77 	ctx->qp_id = qp_id;
78 
79 	ctx->populate_ops = op_fns->populate_ops;
80 	ctx->options = options;
81 	ctx->test_vector = test_vector;
82 
83 	/* IV goes at the end of the crypto operation */
84 	uint16_t iv_offset = sizeof(struct rte_crypto_op) +
85 		sizeof(struct rte_crypto_sym_op);
86 
87 	ctx->sess = op_fns->sess_create(sess_mp, dev_id, options,
88 			test_vector, iv_offset);
89 	if (ctx->sess == NULL)
90 		goto err;
91 
92 	if (cperf_alloc_common_memory(options, test_vector, dev_id, qp_id, 0,
93 			&ctx->src_buf_offset, &ctx->dst_buf_offset,
94 			&ctx->pool) < 0)
95 		goto err;
96 
97 	return ctx;
98 err:
99 	cperf_throughput_test_free(ctx);
100 
101 	return NULL;
102 }
103 
104 int
105 cperf_throughput_test_runner(void *test_ctx)
106 {
107 	struct cperf_throughput_ctx *ctx = test_ctx;
108 	uint16_t test_burst_size;
109 	uint8_t burst_size_idx = 0;
110 	uint32_t imix_idx = 0;
111 
112 	static uint16_t display_once;
113 
114 	struct rte_crypto_op *ops[ctx->options->max_burst_size];
115 	struct rte_crypto_op *ops_processed[ctx->options->max_burst_size];
116 	uint64_t i;
117 
118 	uint32_t lcore = rte_lcore_id();
119 
120 #ifdef CPERF_LINEARIZATION_ENABLE
121 	struct rte_cryptodev_info dev_info;
122 	int linearize = 0;
123 
124 	/* Check if source mbufs require coalescing */
125 	if ((ctx->options->op_type != CPERF_ASYM_MODEX) &&
126 	    (ctx->options->segment_sz < ctx->options->max_buffer_size)) {
127 		rte_cryptodev_info_get(ctx->dev_id, &dev_info);
128 		if ((dev_info.feature_flags &
129 				RTE_CRYPTODEV_FF_MBUF_SCATTER_GATHER) == 0)
130 			linearize = 1;
131 	}
132 #endif /* CPERF_LINEARIZATION_ENABLE */
133 
134 	ctx->lcore_id = lcore;
135 
136 	/* Warm up the host CPU before starting the test */
137 	for (i = 0; i < ctx->options->total_ops; i++)
138 		rte_cryptodev_enqueue_burst(ctx->dev_id, ctx->qp_id, NULL, 0);
139 
140 	/* Get first size from range or list */
141 	if (ctx->options->inc_burst_size != 0)
142 		test_burst_size = ctx->options->min_burst_size;
143 	else
144 		test_burst_size = ctx->options->burst_size_list[0];
145 
146 	uint16_t iv_offset = sizeof(struct rte_crypto_op) +
147 		sizeof(struct rte_crypto_sym_op);
148 
149 	while (test_burst_size <= ctx->options->max_burst_size) {
150 		uint64_t ops_enqd = 0, ops_enqd_total = 0, ops_enqd_failed = 0;
151 		uint64_t ops_deqd = 0, ops_deqd_total = 0, ops_deqd_failed = 0;
152 
153 		uint64_t tsc_start, tsc_end, tsc_duration;
154 
155 		uint16_t ops_unused = 0;
156 
157 		tsc_start = rte_rdtsc_precise();
158 
159 		while (ops_enqd_total < ctx->options->total_ops) {
160 
161 			uint16_t burst_size = ((ops_enqd_total + test_burst_size)
162 					<= ctx->options->total_ops) ?
163 							test_burst_size :
164 							ctx->options->total_ops -
165 							ops_enqd_total;
166 
167 			uint16_t ops_needed = burst_size - ops_unused;
168 
169 			/* Allocate objects containing crypto operations and mbufs */
170 			if (rte_mempool_get_bulk(ctx->pool, (void **)ops,
171 						ops_needed) != 0) {
172 				RTE_LOG(ERR, USER1,
173 					"Failed to allocate more crypto operations "
174 					"from the crypto operation pool.\n"
175 					"Consider increasing the pool size "
176 					"with --pool-sz\n");
177 				return -1;
178 			}
179 
180 			/* Setup crypto op, attach mbuf etc */
181 			(ctx->populate_ops)(ops, ctx->src_buf_offset,
182 					ctx->dst_buf_offset,
183 					ops_needed, ctx->sess,
184 					ctx->options, ctx->test_vector,
185 					iv_offset, &imix_idx, &tsc_start);
186 
187 			/**
188 			 * When ops_needed is smaller than ops_enqd, the
189 			 * unused ops need to be moved to the front for
190 			 * next round use.
191 			 */
192 			if (unlikely(ops_enqd > ops_needed)) {
193 				size_t nb_b_to_mov = ops_unused * sizeof(
194 						struct rte_crypto_op *);
195 
196 				memmove(&ops[ops_needed], &ops[ops_enqd],
197 					nb_b_to_mov);
198 			}
199 
200 #ifdef CPERF_LINEARIZATION_ENABLE
201 			if (linearize) {
202 				/* PMD doesn't support scatter-gather and source buffer
203 				 * is segmented.
204 				 * We need to linearize it before enqueuing.
205 				 */
206 				for (i = 0; i < burst_size; i++)
207 					rte_pktmbuf_linearize(
208 						ops[i]->sym->m_src);
209 			}
210 #endif /* CPERF_LINEARIZATION_ENABLE */
211 
212 			/* Enqueue burst of ops on crypto device */
213 			ops_enqd = rte_cryptodev_enqueue_burst(ctx->dev_id, ctx->qp_id,
214 					ops, burst_size);
215 			if (ops_enqd < burst_size)
216 				ops_enqd_failed++;
217 
218 			/**
219 			 * Calculate number of ops not enqueued (mainly for hw
220 			 * accelerators whose ingress queue can fill up).
221 			 */
222 			ops_unused = burst_size - ops_enqd;
223 			ops_enqd_total += ops_enqd;
224 
225 
226 			/* Dequeue processed burst of ops from crypto device */
227 			ops_deqd = rte_cryptodev_dequeue_burst(ctx->dev_id, ctx->qp_id,
228 					ops_processed, test_burst_size);
229 
230 			if (likely(ops_deqd))  {
231 				/* Free crypto ops so they can be reused. */
232 				rte_mempool_put_bulk(ctx->pool,
233 						(void **)ops_processed, ops_deqd);
234 
235 				ops_deqd_total += ops_deqd;
236 			} else {
237 				/**
238 				 * Count dequeue polls which didn't return any
239 				 * processed operations. This statistic is mainly
240 				 * relevant to hw accelerators.
241 				 */
242 				ops_deqd_failed++;
243 			}
244 
245 		}
246 
247 		/* Dequeue any operations still in the crypto device */
248 
249 		while (ops_deqd_total < ctx->options->total_ops) {
250 			/* Sending 0 length burst to flush sw crypto device */
251 			rte_cryptodev_enqueue_burst(ctx->dev_id, ctx->qp_id, NULL, 0);
252 
253 			/* dequeue burst */
254 			ops_deqd = rte_cryptodev_dequeue_burst(ctx->dev_id, ctx->qp_id,
255 					ops_processed, test_burst_size);
256 			if (ops_deqd == 0)
257 				ops_deqd_failed++;
258 			else {
259 				rte_mempool_put_bulk(ctx->pool,
260 						(void **)ops_processed, ops_deqd);
261 				ops_deqd_total += ops_deqd;
262 			}
263 		}
264 
265 		tsc_end = rte_rdtsc_precise();
266 		tsc_duration = (tsc_end - tsc_start);
267 
268 		/* Calculate average operations processed per second */
269 		double ops_per_second = ((double)ctx->options->total_ops /
270 				tsc_duration) * rte_get_tsc_hz();
271 
272 		/* Calculate average throughput (Gbps) in bits per second */
273 		double throughput_gbps = ((ops_per_second *
274 				ctx->options->test_buffer_size * 8) / 1000000000);
275 
276 		/* Calculate average cycles per packet */
277 		double cycles_per_packet = ((double)tsc_duration /
278 				ctx->options->total_ops);
279 
280 		uint16_t exp = 0;
281 		if (!ctx->options->csv) {
282 			if (__atomic_compare_exchange_n(&display_once, &exp, 1, 0,
283 					__ATOMIC_RELAXED, __ATOMIC_RELAXED))
284 				printf("%12s%12s%12s%12s%12s%12s%12s%12s%12s%12s\n\n",
285 					"lcore id", "Buf Size", "Burst Size",
286 					"Enqueued", "Dequeued", "Failed Enq",
287 					"Failed Deq", "MOps", "Gbps",
288 					"Cycles/Buf");
289 
290 			printf("%12u%12u%12u%12"PRIu64"%12"PRIu64"%12"PRIu64
291 					"%12"PRIu64"%12.4f%12.4f%12.2f\n",
292 					ctx->lcore_id,
293 					ctx->options->test_buffer_size,
294 					test_burst_size,
295 					ops_enqd_total,
296 					ops_deqd_total,
297 					ops_enqd_failed,
298 					ops_deqd_failed,
299 					ops_per_second/1000000,
300 					throughput_gbps,
301 					cycles_per_packet);
302 		} else {
303 			if (__atomic_compare_exchange_n(&display_once, &exp, 1, 0,
304 					__ATOMIC_RELAXED, __ATOMIC_RELAXED))
305 				printf("#lcore id,Buffer Size(B),"
306 					"Burst Size,Enqueued,Dequeued,Failed Enq,"
307 					"Failed Deq,Ops(Millions),Throughput(Gbps),"
308 					"Cycles/Buf\n\n");
309 
310 			printf("%u,%u,%u,%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64","
311 					"%.3f,%.3f,%.3f\n",
312 					ctx->lcore_id,
313 					ctx->options->test_buffer_size,
314 					test_burst_size,
315 					ops_enqd_total,
316 					ops_deqd_total,
317 					ops_enqd_failed,
318 					ops_deqd_failed,
319 					ops_per_second/1000000,
320 					throughput_gbps,
321 					cycles_per_packet);
322 		}
323 
324 		/* Get next size from range or list */
325 		if (ctx->options->inc_burst_size != 0)
326 			test_burst_size += ctx->options->inc_burst_size;
327 		else {
328 			if (++burst_size_idx == ctx->options->burst_size_count)
329 				break;
330 			test_burst_size = ctx->options->burst_size_list[burst_size_idx];
331 		}
332 
333 	}
334 
335 	return 0;
336 }
337 
338 
339 void
340 cperf_throughput_test_destructor(void *arg)
341 {
342 	struct cperf_throughput_ctx *ctx = arg;
343 
344 	if (ctx == NULL)
345 		return;
346 
347 	cperf_throughput_test_free(ctx);
348 }
349