xref: /dpdk/app/test-crypto-perf/cperf_test_latency.c (revision 0857b942113874c69dc3db5df11a828ee3cc9b6b)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
5  *
6  *   Redistribution and use in source and binary forms, with or without
7  *   modification, are permitted provided that the following conditions
8  *   are met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above copyright
13  *       notice, this list of conditions and the following disclaimer in
14  *       the documentation and/or other materials provided with the
15  *       distribution.
16  *     * Neither the name of Intel Corporation nor the names of its
17  *       contributors may be used to endorse or promote products derived
18  *       from this software without specific prior written permission.
19  *
20  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <rte_malloc.h>
34 #include <rte_cycles.h>
35 #include <rte_crypto.h>
36 #include <rte_cryptodev.h>
37 
38 #include "cperf_test_latency.h"
39 #include "cperf_ops.h"
40 
41 
42 struct cperf_latency_results {
43 
44 	uint64_t ops_failed;
45 
46 	uint64_t enqd_tot;
47 	uint64_t enqd_max;
48 	uint64_t enqd_min;
49 
50 	uint64_t deqd_tot;
51 	uint64_t deqd_max;
52 	uint64_t deqd_min;
53 
54 	uint64_t cycles_tot;
55 	uint64_t cycles_max;
56 	uint64_t cycles_min;
57 
58 	uint64_t burst_num;
59 	uint64_t num;
60 };
61 
62 struct cperf_op_result {
63 	uint64_t tsc_start;
64 	uint64_t tsc_end;
65 	enum rte_crypto_op_status status;
66 };
67 
68 struct cperf_latency_ctx {
69 	uint8_t dev_id;
70 	uint16_t qp_id;
71 	uint8_t lcore_id;
72 
73 	struct rte_mempool *pkt_mbuf_pool_in;
74 	struct rte_mempool *pkt_mbuf_pool_out;
75 	struct rte_mbuf **mbufs_in;
76 	struct rte_mbuf **mbufs_out;
77 
78 	struct rte_mempool *crypto_op_pool;
79 
80 	struct rte_cryptodev_sym_session *sess;
81 
82 	cperf_populate_ops_t populate_ops;
83 	cperf_verify_crypto_op_t verify_op_output;
84 
85 	const struct cperf_options *options;
86 	const struct cperf_test_vector *test_vector;
87 	struct cperf_op_result *res;
88 	struct cperf_latency_results results;
89 };
90 
91 #define max(a, b) (a > b ? (uint64_t)a : (uint64_t)b)
92 #define min(a, b) (a < b ? (uint64_t)a : (uint64_t)b)
93 
94 static void
95 cperf_latency_test_free(struct cperf_latency_ctx *ctx, uint32_t mbuf_nb)
96 {
97 	uint32_t i;
98 
99 	if (ctx) {
100 		if (ctx->sess)
101 			rte_cryptodev_sym_session_free(ctx->dev_id, ctx->sess);
102 
103 		if (ctx->mbufs_in) {
104 			for (i = 0; i < mbuf_nb; i++)
105 				rte_pktmbuf_free(ctx->mbufs_in[i]);
106 
107 			rte_free(ctx->mbufs_in);
108 		}
109 
110 		if (ctx->mbufs_out) {
111 			for (i = 0; i < mbuf_nb; i++) {
112 				if (ctx->mbufs_out[i] != NULL)
113 					rte_pktmbuf_free(ctx->mbufs_out[i]);
114 			}
115 
116 			rte_free(ctx->mbufs_out);
117 		}
118 
119 		if (ctx->pkt_mbuf_pool_in)
120 			rte_mempool_free(ctx->pkt_mbuf_pool_in);
121 
122 		if (ctx->pkt_mbuf_pool_out)
123 			rte_mempool_free(ctx->pkt_mbuf_pool_out);
124 
125 		if (ctx->crypto_op_pool)
126 			rte_mempool_free(ctx->crypto_op_pool);
127 
128 		rte_free(ctx->res);
129 		rte_free(ctx);
130 	}
131 }
132 
133 static struct rte_mbuf *
134 cperf_mbuf_create(struct rte_mempool *mempool,
135 		uint32_t segments_nb,
136 		const struct cperf_options *options,
137 		const struct cperf_test_vector *test_vector)
138 {
139 	struct rte_mbuf *mbuf;
140 	uint32_t segment_sz = options->buffer_sz / segments_nb;
141 	uint32_t last_sz = options->buffer_sz % segments_nb;
142 	uint8_t *mbuf_data;
143 	uint8_t *test_data =
144 			(options->cipher_op == RTE_CRYPTO_CIPHER_OP_ENCRYPT) ?
145 					test_vector->plaintext.data :
146 					test_vector->ciphertext.data;
147 
148 	mbuf = rte_pktmbuf_alloc(mempool);
149 	if (mbuf == NULL)
150 		goto error;
151 
152 	mbuf_data = (uint8_t *)rte_pktmbuf_append(mbuf, segment_sz);
153 	if (mbuf_data == NULL)
154 		goto error;
155 
156 	memcpy(mbuf_data, test_data, segment_sz);
157 	test_data += segment_sz;
158 	segments_nb--;
159 
160 	while (segments_nb) {
161 		struct rte_mbuf *m;
162 
163 		m = rte_pktmbuf_alloc(mempool);
164 		if (m == NULL)
165 			goto error;
166 
167 		rte_pktmbuf_chain(mbuf, m);
168 
169 		mbuf_data = (uint8_t *)rte_pktmbuf_append(mbuf, segment_sz);
170 		if (mbuf_data == NULL)
171 			goto error;
172 
173 		memcpy(mbuf_data, test_data, segment_sz);
174 		test_data += segment_sz;
175 		segments_nb--;
176 	}
177 
178 	if (last_sz) {
179 		mbuf_data = (uint8_t *)rte_pktmbuf_append(mbuf, last_sz);
180 		if (mbuf_data == NULL)
181 			goto error;
182 
183 		memcpy(mbuf_data, test_data, last_sz);
184 	}
185 
186 	mbuf_data = (uint8_t *)rte_pktmbuf_append(mbuf,
187 			options->auth_digest_sz);
188 	if (mbuf_data == NULL)
189 		goto error;
190 
191 	if (options->op_type == CPERF_AEAD) {
192 		uint8_t *aead = (uint8_t *)rte_pktmbuf_prepend(mbuf,
193 			RTE_ALIGN_CEIL(options->auth_aad_sz, 16));
194 
195 		if (aead == NULL)
196 			goto error;
197 
198 		memcpy(aead, test_vector->aad.data, test_vector->aad.length);
199 	}
200 
201 	return mbuf;
202 error:
203 	if (mbuf != NULL)
204 		rte_pktmbuf_free(mbuf);
205 
206 	return NULL;
207 }
208 
209 void *
210 cperf_latency_test_constructor(uint8_t dev_id, uint16_t qp_id,
211 		const struct cperf_options *options,
212 		const struct cperf_test_vector *test_vector,
213 		const struct cperf_op_fns *op_fns)
214 {
215 	struct cperf_latency_ctx *ctx = NULL;
216 	unsigned int mbuf_idx = 0;
217 	char pool_name[32] = "";
218 
219 	ctx = rte_malloc(NULL, sizeof(struct cperf_latency_ctx), 0);
220 	if (ctx == NULL)
221 		goto err;
222 
223 	ctx->dev_id = dev_id;
224 	ctx->qp_id = qp_id;
225 
226 	ctx->populate_ops = op_fns->populate_ops;
227 	ctx->options = options;
228 	ctx->test_vector = test_vector;
229 
230 	ctx->sess = op_fns->sess_create(dev_id, options, test_vector);
231 	if (ctx->sess == NULL)
232 		goto err;
233 
234 	snprintf(pool_name, sizeof(pool_name), "cperf_pool_in_cdev_%d",
235 				dev_id);
236 
237 	ctx->pkt_mbuf_pool_in = rte_pktmbuf_pool_create(pool_name,
238 			options->pool_sz * options->segments_nb, 0, 0,
239 			RTE_PKTMBUF_HEADROOM +
240 			RTE_CACHE_LINE_ROUNDUP(
241 				(options->buffer_sz / options->segments_nb) +
242 				(options->buffer_sz % options->segments_nb) +
243 					options->auth_digest_sz),
244 			rte_socket_id());
245 
246 	if (ctx->pkt_mbuf_pool_in == NULL)
247 		goto err;
248 
249 	/* Generate mbufs_in with plaintext populated for test */
250 	if (ctx->options->pool_sz % ctx->options->burst_sz)
251 		goto err;
252 
253 	ctx->mbufs_in = rte_malloc(NULL,
254 			(sizeof(struct rte_mbuf *) *
255 			ctx->options->pool_sz), 0);
256 
257 	for (mbuf_idx = 0; mbuf_idx < options->pool_sz; mbuf_idx++) {
258 		ctx->mbufs_in[mbuf_idx] = cperf_mbuf_create(
259 				ctx->pkt_mbuf_pool_in, options->segments_nb,
260 				options, test_vector);
261 		if (ctx->mbufs_in[mbuf_idx] == NULL)
262 			goto err;
263 	}
264 
265 	if (options->out_of_place == 1)	{
266 
267 		snprintf(pool_name, sizeof(pool_name),
268 				"cperf_pool_out_cdev_%d",
269 				dev_id);
270 
271 		ctx->pkt_mbuf_pool_out = rte_pktmbuf_pool_create(
272 				pool_name, options->pool_sz, 0, 0,
273 				RTE_PKTMBUF_HEADROOM +
274 				RTE_CACHE_LINE_ROUNDUP(
275 					options->buffer_sz +
276 					options->auth_digest_sz),
277 				rte_socket_id());
278 
279 		if (ctx->pkt_mbuf_pool_out == NULL)
280 			goto err;
281 	}
282 
283 	ctx->mbufs_out = rte_malloc(NULL,
284 			(sizeof(struct rte_mbuf *) *
285 			ctx->options->pool_sz), 0);
286 
287 	for (mbuf_idx = 0; mbuf_idx < options->pool_sz; mbuf_idx++) {
288 		if (options->out_of_place == 1)	{
289 			ctx->mbufs_out[mbuf_idx] = cperf_mbuf_create(
290 					ctx->pkt_mbuf_pool_out, 1,
291 					options, test_vector);
292 			if (ctx->mbufs_out[mbuf_idx] == NULL)
293 				goto err;
294 		} else {
295 			ctx->mbufs_out[mbuf_idx] = NULL;
296 		}
297 	}
298 
299 	snprintf(pool_name, sizeof(pool_name), "cperf_op_pool_cdev_%d",
300 			dev_id);
301 
302 	ctx->crypto_op_pool = rte_crypto_op_pool_create(pool_name,
303 			RTE_CRYPTO_OP_TYPE_SYMMETRIC, options->pool_sz, 0, 0,
304 			rte_socket_id());
305 	if (ctx->crypto_op_pool == NULL)
306 		goto err;
307 
308 	ctx->res = rte_malloc(NULL, sizeof(struct cperf_op_result) *
309 			ctx->options->total_ops, 0);
310 
311 	if (ctx->res == NULL)
312 		goto err;
313 
314 	return ctx;
315 err:
316 	cperf_latency_test_free(ctx, mbuf_idx);
317 
318 	return NULL;
319 }
320 
321 static int
322 cperf_latency_test_verifier(struct rte_mbuf *mbuf,
323 		const struct cperf_options *options,
324 		const struct cperf_test_vector *vector)
325 {
326 	const struct rte_mbuf *m;
327 	uint32_t len;
328 	uint16_t nb_segs;
329 	uint8_t *data;
330 	uint32_t cipher_offset, auth_offset;
331 	uint8_t	cipher, auth;
332 	int res = 0;
333 
334 	m = mbuf;
335 	nb_segs = m->nb_segs;
336 	len = 0;
337 	while (m && nb_segs != 0) {
338 		len += m->data_len;
339 		m = m->next;
340 		nb_segs--;
341 	}
342 
343 	data = rte_malloc(NULL, len, 0);
344 	if (data == NULL)
345 		return 1;
346 
347 	m = mbuf;
348 	nb_segs = m->nb_segs;
349 	len = 0;
350 	while (m && nb_segs != 0) {
351 		memcpy(data + len, rte_pktmbuf_mtod(m, uint8_t *),
352 				m->data_len);
353 		len += m->data_len;
354 		m = m->next;
355 		nb_segs--;
356 	}
357 
358 	switch (options->op_type) {
359 	case CPERF_CIPHER_ONLY:
360 		cipher = 1;
361 		cipher_offset = 0;
362 		auth = 0;
363 		auth_offset = 0;
364 		break;
365 	case CPERF_CIPHER_THEN_AUTH:
366 		cipher = 1;
367 		cipher_offset = 0;
368 		auth = 1;
369 		auth_offset = vector->plaintext.length;
370 		break;
371 	case CPERF_AUTH_ONLY:
372 		cipher = 0;
373 		cipher_offset = 0;
374 		auth = 1;
375 		auth_offset = vector->plaintext.length;
376 		break;
377 	case CPERF_AUTH_THEN_CIPHER:
378 		cipher = 1;
379 		cipher_offset = 0;
380 		auth = 1;
381 		auth_offset = vector->plaintext.length;
382 		break;
383 	case CPERF_AEAD:
384 		cipher = 1;
385 		cipher_offset = vector->aad.length;
386 		auth = 1;
387 		auth_offset = vector->aad.length + vector->plaintext.length;
388 		break;
389 	}
390 
391 	if (cipher == 1) {
392 		if (options->cipher_op == RTE_CRYPTO_CIPHER_OP_ENCRYPT)
393 			res += memcmp(data + cipher_offset,
394 					vector->ciphertext.data,
395 					vector->ciphertext.length);
396 		else
397 			res += memcmp(data + cipher_offset,
398 					vector->plaintext.data,
399 					vector->plaintext.length);
400 	}
401 
402 	if (auth == 1) {
403 		if (options->auth_op == RTE_CRYPTO_AUTH_OP_GENERATE)
404 			res += memcmp(data + auth_offset,
405 					vector->digest.data,
406 					vector->digest.length);
407 	}
408 
409 	if (res != 0)
410 		res = 1;
411 
412 	return res;
413 }
414 
415 int
416 cperf_latency_test_runner(void *arg)
417 {
418 	struct cperf_latency_ctx *ctx = arg;
419 	struct cperf_op_result *pres;
420 
421 	if (ctx == NULL)
422 		return 0;
423 
424 	struct rte_crypto_op *ops[ctx->options->burst_sz];
425 	struct rte_crypto_op *ops_processed[ctx->options->burst_sz];
426 	uint64_t ops_enqd = 0, ops_deqd = 0;
427 	uint16_t ops_unused = 0;
428 	uint64_t m_idx = 0, b_idx = 0, i;
429 
430 	uint64_t tsc_val, tsc_end, tsc_start;
431 	uint64_t tsc_max = 0, tsc_min = ~0UL, tsc_tot = 0, tsc_idx = 0;
432 	uint64_t enqd_max = 0, enqd_min = ~0UL, enqd_tot = 0;
433 	uint64_t deqd_max = 0, deqd_min = ~0UL, deqd_tot = 0;
434 
435 	uint32_t lcore = rte_lcore_id();
436 
437 #ifdef CPERF_LINEARIZATION_ENABLE
438 	struct rte_cryptodev_info dev_info;
439 	int linearize = 0;
440 
441 	/* Check if source mbufs require coalescing */
442 	if (ctx->options->segments_nb > 1) {
443 		rte_cryptodev_info_get(ctx->dev_id, &dev_info);
444 		if ((dev_info.feature_flags &
445 				RTE_CRYPTODEV_FF_MBUF_SCATTER_GATHER) == 0)
446 			linearize = 1;
447 	}
448 #endif /* CPERF_LINEARIZATION_ENABLE */
449 
450 	ctx->lcore_id = lcore;
451 
452 	/* Warm up the host CPU before starting the test */
453 	for (i = 0; i < ctx->options->total_ops; i++)
454 		rte_cryptodev_enqueue_burst(ctx->dev_id, ctx->qp_id, NULL, 0);
455 
456 	while (enqd_tot < ctx->options->total_ops) {
457 
458 		uint16_t burst_size = ((enqd_tot + ctx->options->burst_sz)
459 				<= ctx->options->total_ops) ?
460 						ctx->options->burst_sz :
461 						ctx->options->total_ops -
462 						enqd_tot;
463 		uint16_t ops_needed = burst_size - ops_unused;
464 
465 		/* Allocate crypto ops from pool */
466 		if (ops_needed != rte_crypto_op_bulk_alloc(
467 				ctx->crypto_op_pool,
468 				RTE_CRYPTO_OP_TYPE_SYMMETRIC,
469 				ops, ops_needed))
470 			return -1;
471 
472 		/* Setup crypto op, attach mbuf etc */
473 		(ctx->populate_ops)(ops, &ctx->mbufs_in[m_idx],
474 				&ctx->mbufs_out[m_idx],
475 				ops_needed, ctx->sess, ctx->options,
476 				ctx->test_vector);
477 
478 		tsc_start = rte_rdtsc_precise();
479 
480 #ifdef CPERF_LINEARIZATION_ENABLE
481 		if (linearize) {
482 			/* PMD doesn't support scatter-gather and source buffer
483 			 * is segmented.
484 			 * We need to linearize it before enqueuing.
485 			 */
486 			for (i = 0; i < burst_size; i++)
487 				rte_pktmbuf_linearize(ops[i]->sym->m_src);
488 		}
489 #endif /* CPERF_LINEARIZATION_ENABLE */
490 
491 		/* Enqueue burst of ops on crypto device */
492 		ops_enqd = rte_cryptodev_enqueue_burst(ctx->dev_id, ctx->qp_id,
493 				ops, burst_size);
494 
495 		/* Dequeue processed burst of ops from crypto device */
496 		ops_deqd = rte_cryptodev_dequeue_burst(ctx->dev_id, ctx->qp_id,
497 				ops_processed, ctx->options->burst_sz);
498 
499 		tsc_end = rte_rdtsc_precise();
500 
501 		for (i = 0; i < ops_needed; i++) {
502 			ctx->res[tsc_idx].tsc_start = tsc_start;
503 			ops[i]->opaque_data = (void *)&ctx->res[tsc_idx];
504 			tsc_idx++;
505 		}
506 
507 		/*
508 		 * Calculate number of ops not enqueued (mainly for hw
509 		 * accelerators whose ingress queue can fill up).
510 		 */
511 		ops_unused = burst_size - ops_enqd;
512 
513 		if (likely(ops_deqd))  {
514 			/*
515 			 * free crypto ops so they can be reused. We don't free
516 			 * the mbufs here as we don't want to reuse them as
517 			 * the crypto operation will change the data and cause
518 			 * failures.
519 			 */
520 			for (i = 0; i < ops_deqd; i++) {
521 				pres = (struct cperf_op_result *)
522 						(ops_processed[i]->opaque_data);
523 				pres->status = ops_processed[i]->status;
524 				pres->tsc_end = tsc_end;
525 
526 				rte_crypto_op_free(ops_processed[i]);
527 			}
528 
529 			deqd_tot += ops_deqd;
530 			deqd_max = max(ops_deqd, deqd_max);
531 			deqd_min = min(ops_deqd, deqd_min);
532 		}
533 
534 		enqd_tot += ops_enqd;
535 		enqd_max = max(ops_enqd, enqd_max);
536 		enqd_min = min(ops_enqd, enqd_min);
537 
538 		m_idx += ops_needed;
539 		m_idx = m_idx + ctx->options->burst_sz > ctx->options->pool_sz ?
540 				0 : m_idx;
541 		b_idx++;
542 	}
543 
544 	/* Dequeue any operations still in the crypto device */
545 	while (deqd_tot < ctx->options->total_ops) {
546 		/* Sending 0 length burst to flush sw crypto device */
547 		rte_cryptodev_enqueue_burst(ctx->dev_id, ctx->qp_id, NULL, 0);
548 
549 		/* dequeue burst */
550 		ops_deqd = rte_cryptodev_dequeue_burst(ctx->dev_id, ctx->qp_id,
551 				ops_processed, ctx->options->burst_sz);
552 
553 		tsc_end = rte_rdtsc_precise();
554 
555 		if (ops_deqd != 0) {
556 			for (i = 0; i < ops_deqd; i++) {
557 				pres = (struct cperf_op_result *)
558 						(ops_processed[i]->opaque_data);
559 				pres->status = ops_processed[i]->status;
560 				pres->tsc_end = tsc_end;
561 
562 				rte_crypto_op_free(ops_processed[i]);
563 			}
564 
565 			deqd_tot += ops_deqd;
566 			deqd_max = max(ops_deqd, deqd_max);
567 			deqd_min = min(ops_deqd, deqd_min);
568 		}
569 	}
570 
571 	for (i = 0; i < tsc_idx; i++) {
572 		tsc_val = ctx->res[i].tsc_end - ctx->res[i].tsc_start;
573 		tsc_max = max(tsc_val, tsc_max);
574 		tsc_min = min(tsc_val, tsc_min);
575 		tsc_tot += tsc_val;
576 	}
577 
578 	if (ctx->options->verify) {
579 		struct rte_mbuf **mbufs;
580 
581 		if (ctx->options->out_of_place == 1)
582 			mbufs = ctx->mbufs_out;
583 		else
584 			mbufs = ctx->mbufs_in;
585 
586 		for (i = 0; i < ctx->options->total_ops; i++) {
587 
588 			if (ctx->res[i].status != RTE_CRYPTO_OP_STATUS_SUCCESS
589 					|| cperf_latency_test_verifier(mbufs[i],
590 							ctx->options,
591 							ctx->test_vector)) {
592 
593 				ctx->results.ops_failed++;
594 			}
595 		}
596 	}
597 
598 	ctx->results.enqd_tot = enqd_tot;
599 	ctx->results.enqd_max = enqd_max;
600 	ctx->results.enqd_min = enqd_min;
601 
602 	ctx->results.deqd_tot = deqd_tot;
603 	ctx->results.deqd_max = deqd_max;
604 	ctx->results.deqd_min = deqd_min;
605 
606 	ctx->results.cycles_tot = tsc_tot;
607 	ctx->results.cycles_max = tsc_max;
608 	ctx->results.cycles_min = tsc_min;
609 
610 	ctx->results.burst_num = b_idx;
611 	ctx->results.num = tsc_idx;
612 
613 	return 0;
614 }
615 
616 void
617 cperf_latency_test_destructor(void *arg)
618 {
619 	struct cperf_latency_ctx *ctx = arg;
620 	uint64_t i;
621 	if (ctx == NULL)
622 		return;
623 	static int only_once;
624 	uint64_t etot, eavg, emax, emin;
625 	uint64_t dtot, davg, dmax, dmin;
626 	uint64_t ctot, cavg, cmax, cmin;
627 	double ttot, tavg, tmax, tmin;
628 
629 	const uint64_t tunit = 1000000; /* us */
630 	const uint64_t tsc_hz = rte_get_tsc_hz();
631 
632 	etot = ctx->results.enqd_tot;
633 	eavg = ctx->results.enqd_tot / ctx->results.burst_num;
634 	emax = ctx->results.enqd_max;
635 	emin = ctx->results.enqd_min;
636 
637 	dtot = ctx->results.deqd_tot;
638 	davg = ctx->results.deqd_tot / ctx->results.burst_num;
639 	dmax = ctx->results.deqd_max;
640 	dmin = ctx->results.deqd_min;
641 
642 	ctot = ctx->results.cycles_tot;
643 	cavg = ctx->results.cycles_tot / ctx->results.num;
644 	cmax = ctx->results.cycles_max;
645 	cmin = ctx->results.cycles_min;
646 
647 	ttot = tunit*(double)(ctot) / tsc_hz;
648 	tavg = tunit*(double)(cavg) / tsc_hz;
649 	tmax = tunit*(double)(cmax) / tsc_hz;
650 	tmin = tunit*(double)(cmin) / tsc_hz;
651 
652 	if (ctx->options->csv) {
653 		if (!only_once)
654 			printf("\n# lcore, Pakt Seq #, Packet Size, cycles,"
655 					" time (us)");
656 
657 		for (i = 0; i < ctx->options->total_ops; i++) {
658 
659 			printf("\n%u;%"PRIu64";%"PRIu64";%.3f",
660 				ctx->lcore_id, i + 1,
661 				ctx->res[i].tsc_end - ctx->res[i].tsc_start,
662 				tunit * (double) (ctx->res[i].tsc_end
663 						- ctx->res[i].tsc_start)
664 					/ tsc_hz);
665 
666 		}
667 		only_once = 1;
668 	} else {
669 		printf("\n# Device %d on lcore %u\n", ctx->dev_id,
670 			ctx->lcore_id);
671 		printf("\n# total operations: %u", ctx->options->total_ops);
672 		printf("\n#  verified failed: %"PRIu64,
673 				ctx->results.ops_failed);
674 		printf("\n#     burst number: %"PRIu64,
675 				ctx->results.burst_num);
676 		printf("\n#");
677 		printf("\n#          \t       Total\t   Average\t   Maximum\t "
678 				"  Minimum");
679 		printf("\n#  enqueued\t%12"PRIu64"\t%10"PRIu64"\t%10"PRIu64"\t"
680 				"%10"PRIu64, etot, eavg, emax, emin);
681 		printf("\n#  dequeued\t%12"PRIu64"\t%10"PRIu64"\t%10"PRIu64"\t"
682 				"%10"PRIu64, dtot, davg, dmax, dmin);
683 		printf("\n#    cycles\t%12"PRIu64"\t%10"PRIu64"\t%10"PRIu64"\t"
684 				"%10"PRIu64, ctot, cavg, cmax, cmin);
685 		printf("\n# time [us]\t%12.0f\t%10.3f\t%10.3f\t%10.3f", ttot,
686 			tavg, tmax, tmin);
687 		printf("\n\n");
688 
689 	}
690 	cperf_latency_test_free(ctx, ctx->options->pool_sz);
691 
692 }
693