diff mbox

[odp-lng,v0] test: performance: add crypto test

Message ID 1445239479-7406-1-git-send-email-alexandru.badicioiu@linaro.org
State New
Headers show

Commit Message

Alexandru Badicioiu Oct. 19, 2015, 7:24 a.m. UTC
From: Alexandru Badicioiu <alexandru.badicioiu@linaro.org>

Test program to measure crypto operation performance.
Measures the time required to launch and get the result
of ODP crypto operations in async and sync API mode with
configurable payload size, crypto algorithms and iteration
number.
Both asynchronous scheduled and polled are supported.
In scheduled mode a separate worker thread is used to
get the crypto completion events.
Output packet can be reused as the input of the next
operation or a new packet can be allocated for each
operation.
Based on a previous work :
https://lists.linaro.org/pipermail/lng-odp/2014-September/003251.html.

Signed-off-by: Alexandru Badicioiu <alexandru.badicioiu@linaro.org>
---
 test/performance/Makefile.am  |   5 +-
 test/performance/odp_cspeed.c | 946 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 950 insertions(+), 1 deletion(-)
 create mode 100644 test/performance/odp_cspeed.c

Comments

Mike Holmes Oct. 21, 2015, 3:25 p.m. UTC | #1
On 19 October 2015 at 03:24, <alexandru.badicioiu@linaro.org> wrote:

> From: Alexandru Badicioiu <alexandru.badicioiu@linaro.org>
>
> Test program to measure crypto operation performance.
> Measures the time required to launch and get the result
> of ODP crypto operations in async and sync API mode with
> configurable payload size, crypto algorithms and iteration
> number.
> Both asynchronous scheduled and polled are supported.
> In scheduled mode a separate worker thread is used to
> get the crypto completion events.
> Output packet can be reused as the input of the next
> operation or a new packet can be allocated for each
> operation.
> Based on a previous work :
> https://lists.linaro.org/pipermail/lng-odp/2014-September/003251.html.
>
> Signed-off-by: Alexandru Badicioiu <alexandru.badicioiu@linaro.org>
> ---
>  test/performance/Makefile.am  |   5 +-
>  test/performance/odp_cspeed.c | 946
> ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 950 insertions(+), 1 deletion(-)
>  create mode 100644 test/performance/odp_cspeed.c
>

I think odp_crypto might be a more descriptive name
In that directory we currently have the following that are more descriptive
of their purpose.
odp_atomic
odp_l2fwd
odp_scheduling
odp_pktio_perf


>
> diff --git a/test/performance/Makefile.am b/test/performance/Makefile.am
> index 721615b..351dc97 100644
> --- a/test/performance/Makefile.am
> +++ b/test/performance/Makefile.am
> @@ -2,7 +2,7 @@ include $(top_srcdir)/test/Makefile.inc
>
>  TESTS_ENVIRONMENT += TEST_DIR=${builddir}
>
> -EXECUTABLES = odp_atomic$(EXEEXT) odp_pktio_perf$(EXEEXT)
> +EXECUTABLES = odp_atomic$(EXEEXT) odp_pktio_perf$(EXEEXT)
> odp_cspeed$(EXEEXT)
>
>  COMPILE_ONLY = odp_l2fwd$(EXEEXT) \
>                odp_scheduling$(EXEEXT)
> @@ -20,6 +20,8 @@ odp_atomic_LDFLAGS = $(AM_LDFLAGS) -static
>  odp_atomic_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
>  odp_scheduling_LDFLAGS = $(AM_LDFLAGS) -static
>  odp_scheduling_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
> +odp_cspeed_LDFLAGS = $(AM_LDFLAGS) -static
> +odp_cspeed_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
>
>  noinst_HEADERS = \
>                   $(top_srcdir)/test/test_debug.h
> @@ -27,5 +29,6 @@ noinst_HEADERS = \
>  dist_odp_atomic_SOURCES = odp_atomic.c
>  dist_odp_scheduling_SOURCES = odp_scheduling.c
>  dist_odp_pktio_perf_SOURCES = odp_pktio_perf.c
> +dist_odp_cspeed_SOURCES = odp_cspeed.c
>
>  EXTRA_DIST = $(TESTSCRIPTS)
> diff --git a/test/performance/odp_cspeed.c b/test/performance/odp_cspeed.c
> new file mode 100644
> index 0000000..28ac1d5
> --- /dev/null
> +++ b/test/performance/odp_cspeed.c
> @@ -0,0 +1,946 @@
> +/* Copyright (c) 2015, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:    BSD-3-Clause
> + */
> +
> +#ifndef _GNU_SOURCE
> +#define _GNU_SOURCE
> +#endif /* _GNU_SOURCE */
> +
> +#include <stdlib.h>
> +#include <string.h>
> +#include <getopt.h>
> +#include <unistd.h>
> +#include <stdio.h>
> +#include <sys/time.h>
> +#include <sys/resource.h>
> +
> +#include <odp.h>
> +#include <odp/helper/linux.h>
> +
> +#define app_err(fmt, ...) \
> +       fprintf(stderr, "%s:%d:%s(): Error: " fmt, __FILE__, \
> +               __LINE__, __func__, ##__VA_ARGS__)
> +
> +/** @def SHM_PKT_POOL_SIZE
> + * @brief Size of the shared memory block
> + */
> +#define SHM_PKT_POOL_SIZE      (512 * 2048 * 2)
> +
> +/** @def SHM_PKT_POOL_BUF_SIZE
> + * @brief Buffer size of the packet pool buffer
> + */
> +#define SHM_PKT_POOL_BUF_SIZE  (1024 * 32)
> +
> +static uint8_t test_iv[8] = "01234567";
> +
> +static uint8_t test_key16[16] = { 0x01, 0x02, 0x03, 0x04, 0x05,
> +                                 0x06, 0x07, 0x08, 0x09, 0x0a,
> +                                 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
> +                                 0x10,
> +};
> +
> +static uint8_t test_key24[24] = { 0x01, 0x02, 0x03, 0x04, 0x05,
> +                                 0x06, 0x07, 0x08, 0x09, 0x0a,
> +                                 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
> +                                 0x10, 0x11, 0x12, 0x13, 0x14,
> +                                 0x15, 0x16, 0x17, 0x18
> +};
> +
> +/**
> + * Structure that holds template for session create call
> + * for different algorithms supported by test
> + */
> +typedef struct {
> +       const char *name;                     /**< Algorithm name */
> +       odp_crypto_session_params_t session;  /**< Prefilled crypto
> session params */
> +       unsigned int hash_adjust;             /**< Size of hash */
> +} cspeed_alg_config_t;
> +
> +/**
> + * Parsed command line cspeed arguments. Describes test configuration.
> + */
> +typedef struct {
> +       /**
> +        * If non zero prints content of packets. Enabled by -d or
> +        * --debug option.
> +        */
> +       int debug_packets;
> +
> +       /**
> +        * If non zero Try to run crypto operation in place. Note some
> +        * implementation may not support such mode. Enabled by -n or
> +        * --inplace option.
> +        */
> +       int in_place;
> +
> +       /**
> +        * If non zeor output of previous operation taken as input for
> +        * next encrypt operations. Enabled by -r or --reuse option.
> +        */
> +       int reuse_packet;
> +
> +       /**
> +        * Maximum number of outstanding encryption requests. Note code
> +        * poll for results over queue and if nothing is available it can
> +        * submit more encryption requests up to maximum number specified
> by
> +        * this option. Specified through -f or --flight option.
> +        */
> +       int in_flight;
> +
> +       /**
> +        * Number of core to run on. Currently is not used.
> +        */
> +       int core_count;
> +
> +       /**
> +        * Number of iteration to repeat crypto operation to get good
> +        * average number. Specified through -i or --terations option.
> +        * Default is 10000.
> +        */
> +       int iteration_count;
> +
> +       /**
> +        * Maximum sessions. Currently is not used.
> +        */
> +       int max_sessions;
> +
> +       /**
> +        * Payload size to test. If 0 set of predefined payload sizes
> +        * is tested. Specified through -p or --payload option.
> +        */
> +       int payload_length;
> +
> +       /**
> +        * Pointer to selected algorithm to test. If NULL all available
> +        * alogorthims are tested. Name of algorithm is passed through
> +        * -a or --algorithm option.
> +        */
> +       cspeed_alg_config_t *alg_config;
> +
> +       /**
> +        * Use scheduler to get completion events from crypto operation.
> +        * Specified through -s argument.
> +        * */
> +       int schedule;
> +
> +       /*
> +        * Poll completion queue for crypto completion events.
> +        * Specified through -p argument.
> +        */
> +       int poll;
> +} cspeed_args_t;
> +
> +/*
> + * Helper structure that holds averages for test of one algorithm
> + * for given payload size.
> + */
> +typedef struct {
> +       /**
> +        * Elapsed time for one crypto operation.
> +        */
> +       double elapsed;
> +
> +       /**
> +        * CPU time spent pre one crypto operation by whole process
> +        * i.e include current and all other threads in process.
> +        * It is filled with 'getrusage(RUSAGE_SELF, ...)' call.
> +        */
> +       double rusage_self;
> +
> +       /**
> +        * CPU time spent per one crypto operation by current thread
> +        * only. It is filled with 'getrusage(RUSAGE_THREAD, ...)'
> +        * call.
> +        */
> +       double rusage_thread;
> +} cspeed_run_result_t;
> +
> +/**
> + * Structure holds one snap to misc times of current process.
> + */
> +typedef struct {
> +       struct timeval tv;       /**< Elapsed time */
> +       struct rusage ru_self;   /**< Rusage value for whole process */
> +       struct rusage ru_thread; /**< Rusage value for current thread */
> +} time_record_t;
> +
> +static void parse_args(int argc, char *argv[], cspeed_args_t *cargs);
> +static void usage(char *progname);
> +
> +/**
> + * Set of predefined payloads. Make sure that maximum payload
> + * size is not bigger than SHM_PKT_POOL_BUF_SIZE. May relax when
> + * implementation start support segmented buffers/packets.
> + */
> +static unsigned int payloads[] = {
> +       16,
> +       64,
> +       256,
> +       1024,
> +       8192,
> +       16384
> +};
> +
> +/**
> + * Set of known algorithms to test
> + */
> +static cspeed_alg_config_t algs_config[] = {
> +       {
> +               .name = "3des-cbc-null",
> +               .session = {
> +                       .cipher_alg = ODP_CIPHER_ALG_3DES_CBC,
> +                       .cipher_key = {
> +                               .data = test_key24,
> +                               .length = sizeof(test_key24)
> +                       },
> +                       .iv = {
> +                               .data = test_iv,
> +                               .length = 8,
> +                       },
> +                       .auth_alg = ODP_AUTH_ALG_NULL
> +               },
> +       },
> +       {
> +               .name = "3des-cbc-hmac-md5-96",
> +               .session = {
> +                       .cipher_alg = ODP_CIPHER_ALG_3DES_CBC,
> +                       .cipher_key = {
> +                               .data = test_key24,
> +                               .length = sizeof(test_key24)
> +                       },
> +                       .iv = {
> +                               .data = test_iv,
> +                               .length = 8,
> +                       },
> +                       .auth_alg = ODP_AUTH_ALG_MD5_96,
> +                       .auth_key = {
> +                               .data = test_key16,
> +                               .length = sizeof(test_key16)
> +                       }
> +               },
> +               .hash_adjust = 12
> +       },
> +       {
> +               .name = "null-hmac-md5-96",
> +               .session = {
> +                       .cipher_alg = ODP_CIPHER_ALG_NULL,
> +                       .auth_alg = ODP_AUTH_ALG_MD5_96,
> +                       .auth_key = {
> +                               .data = test_key16,
> +                               .length = sizeof(test_key16)
> +                       }
> +               },
> +               .hash_adjust = 12
> +       },
> +};
> +
> +/**
> + * Find corresponding config for given name. Returns NULL
> + * if config for given name is not found.
> + */
> +static cspeed_alg_config_t *
> +find_config_by_name(const char *name) {
> +       unsigned int i;
> +       cspeed_alg_config_t *ret = NULL;
> +
> +       for (i = 0; i < (sizeof(algs_config) /
> sizeof(cspeed_alg_config_t));
> +            i++) {
> +               if (strcmp(algs_config[i].name, name) == 0) {
> +                       ret = algs_config + i;
> +                       break;
> +               }
> +       }
> +       return ret;
> +}
> +
> +/**
> + * Helper function that prints list of algorithms that this
> + * test understands.
> + */
> +static void
> +print_config_names(const char *prefix) {
> +       unsigned int i;
> +
> +       for (i = 0; i < (sizeof(algs_config) /
> sizeof(cspeed_alg_config_t));
> +            i++) {
> +               printf("%s %s\n", prefix, algs_config[i].name);
> +       }
> +}
> +
> +/**
> + * Snap current time values and put them into 'rec'.
> + */
> +static void
> +fill_time_record(time_record_t *rec)
> +{
> +       gettimeofday(&rec->tv, NULL);
> +       getrusage(RUSAGE_SELF, &rec->ru_self);
> +       getrusage(RUSAGE_THREAD, &rec->ru_thread);
> +}
> +
> +/**
> + * Calculated CPU time difference for given two rusage structures.
> + * Note it adds user space and system time together.
> + */
> +static unsigned long long
> +get_rusage_diff(struct rusage *start, struct rusage *end)
> +{
> +       unsigned long long rusage_diff;
> +       unsigned long long rusage_start;
> +       unsigned long long rusage_end;
> +
> +       rusage_start = (start->ru_utime.tv_sec * 1000000) +
> +                      (start->ru_utime.tv_usec);
> +       rusage_start += (start->ru_stime.tv_sec * 1000000) +
> +                       (start->ru_stime.tv_usec);
> +
> +       rusage_end = (end->ru_utime.tv_sec * 1000000) +
> +                    (end->ru_utime.tv_usec);
> +       rusage_end += (end->ru_stime.tv_sec * 1000000) +
> +                     (end->ru_stime.tv_usec);
> +
> +       rusage_diff = rusage_end - rusage_start;
> +
> +       return rusage_diff;
> +}
> +
> +/**
> + * Get diff for RUSAGE_SELF (whole process) between two time snap
> + * records.
> + */
> +static unsigned long long
> +get_rusage_self_diff(time_record_t *start, time_record_t *end)
> +{
> +       return get_rusage_diff(&start->ru_self, &end->ru_self);
> +}
> +
> +/**
> + * Get diff for RUSAGE_THREAD (current thread only) between two
> + * time snap records.
> + */
> +static unsigned long long
> +get_rusage_thread_diff(time_record_t *start, time_record_t *end)
> +{
> +       return get_rusage_diff(&start->ru_thread, &end->ru_thread);
> +}
> +
> +/**
> + * Get diff of elapsed time between two time snap records
> + */
> +static unsigned long long
> +get_elapsed_usec(time_record_t *start, time_record_t *end)
> +{
> +       unsigned long long s;
> +       unsigned long long e;
> +
> +       s = (start->tv.tv_sec * 1000000) +
> +           (start->tv.tv_usec);
> +       e = (end->tv.tv_sec * 1000000) +
> +           (end->tv.tv_usec);
> +
> +       return e - s;
> +}
> +
> +#define REPORT_HEADER      "\n%30.30s %15s %15s %15s %15s %15s %15s\n"
> +#define REPORT_LINE        "%30.30s %15d %15d %15.3f %15.3f %15.3f %15d\n"
> +
> +/**
> + * Print header line for our report.
> + */
> +static void
> +print_result_header(void)
> +{
> +       printf(REPORT_HEADER,
> +              "algorithm", "avg over #", "payload (bytes)", "elapsed
> (us)",
> +              "rusg self (us)", "rusg thrd (us)", "throughput (Kb)");
> +}
> +
> +/**
> + * Print one line of our report.
> + */
> +static void
> +print_result(cspeed_args_t *cargs,
> +            unsigned int payload_length,
> +            cspeed_alg_config_t *config,
> +            cspeed_run_result_t *result)
> +{
> +       unsigned int throughput;
> +
> +       throughput = (1000000.0 / result->elapsed) * payload_length / 1024;
> +       printf(REPORT_LINE,
> +              config->name, cargs->iteration_count, payload_length,
> +              result->elapsed, result->rusage_self, result->rusage_thread,
> +              throughput);
> +}
> +
> +/**
> + * Print piece of memory with given size.
> + */
> +static void
> +print_mem(const char *msg,
> +         const unsigned char *ptr,
> +         unsigned int len)
> +{
> +       unsigned i, j;
> +       char c;
> +       char line[81];
> +       char *p;
> +
> +       if (msg)
> +               printf("\n%s (bytes size = %d)", msg, len);
> +
> +       for (i = 0; i < len; i += 16) {
> +               p = line;
> +               sprintf(p, "\n%04x   ", i); p += 8;
> +
> +               for (j = 0; j < 16; j++) {
> +                       if (i + j == len)
> +                               break;
> +
> +                       sprintf(p, " %02x", (ptr)[i + j]); p += 3;
> +               }
> +
> +               for (; j < 16; j++) {
> +                       sprintf(p, "   "); p += 3;
> +               }
> +
> +               sprintf(p, "   "); p += 3;
> +
> +               for (j = 0; j < 16; j++) {
> +                       if (i + j == len)
> +                               break;
> +                       c = (ptr)[i + j];
> +                       *p++ = (' ' <= c && c <= '~') ? c : '.';
> +               }
> +
> +               *p = '\0';
> +               printf("%s", line);
> +       }
> +       printf("\n");
> +}
> +
> +/**
> + * Create ODP crypto session for given config.
> + */
> +static int
> +create_session_from_config(odp_crypto_session_t *session,
> +                          cspeed_alg_config_t *config,
> +                          cspeed_args_t *cargs)
> +{
> +       odp_crypto_session_params_t params;
> +       enum odp_crypto_ses_create_err ses_create_rc;
> +       odp_pool_t pkt_pool;
> +       odp_queue_t out_queue;
> +
> +       memcpy(&params, &config->session,
> sizeof(odp_crypto_session_params_t));
> +       params.op = ODP_CRYPTO_OP_ENCODE;
> +       params.pref_mode   = ODP_CRYPTO_SYNC;
> +
> +       /* Lookup the packet pool */
> +       pkt_pool = odp_pool_lookup("packet_pool");
> +       if (pkt_pool == ODP_POOL_INVALID) {
> +               app_err("packet_pool pool not found\n");
> +               return -1;
> +       }
> +       params.output_pool = pkt_pool;
> +
> +       if (cargs->schedule || cargs->poll) {
> +               out_queue = odp_queue_lookup("crypto-out");
> +               if (out_queue == ODP_QUEUE_INVALID) {
> +                       app_err("crypto-out queue not found\n");
> +                       return -1;
> +               }
> +               params.compl_queue = out_queue;
> +
> +       } else {
> +               params.compl_queue = ODP_QUEUE_INVALID;
> +       }
> +       if (odp_crypto_session_create(&params, session,
> +                                     &ses_create_rc)) {
> +               app_err("crypto session create failed.\n");
> +               return -1;
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * Run measurement iterations for given config and payload size.
> + * Result of run returned in 'result' out parameter.
> + */
> +static int
> +run_measure_one(cspeed_args_t *cargs,
> +               cspeed_alg_config_t *config,
> +               odp_crypto_session_t *session,
> +               unsigned int payload_length,
> +               cspeed_run_result_t *result)
> +{
> +       odp_crypto_op_params_t params;
> +
> +       odp_pool_t pkt_pool;
> +       odp_queue_t out_queue;
> +       odp_packet_t pkt;
> +       int rc = 0;
> +
> +       odp_bool_t posted = 0;
> +
> +       pkt_pool = odp_pool_lookup("packet_pool");
> +       if (pkt_pool == ODP_POOL_INVALID) {
> +               app_err("pkt_pool not found\n");
> +               return -1;
> +       }
> +
> +       out_queue = odp_queue_lookup("crypto-out");
> +       if (cargs->schedule || cargs->poll) {
> +               if (out_queue == ODP_QUEUE_INVALID) {
> +                       app_err("crypto-out queue not found\n");
> +                       return -1;
> +               }
> +       }
> +
> +       pkt = odp_packet_alloc(pkt_pool, payload_length);
> +       if (pkt == ODP_PACKET_INVALID) {
> +               app_err("failed to allocate buffer\n");
> +               return -1;
> +       }
> +
> +       void *mem = odp_packet_data(pkt);
> +
> +       memset(mem, 1, payload_length);
> +
> +       time_record_t start, end;
> +       int packets_sent = 0;
> +       int packets_received = 0;
> +
> +       /* Initialize parameters block */
> +       memset(&params, 0, sizeof(params));
> +       params.session = *session;
> +
> +       params.cipher_range.offset = 0;
> +       params.cipher_range.length = payload_length;
> +
> +       params.auth_range.offset = 0;
> +       params.auth_range.length = payload_length;
> +       params.hash_result_offset = payload_length;
> +
> +       if (cargs->reuse_packet) {
> +               params.pkt = pkt;
> +               params.out_pkt = cargs->in_place ? pkt :
> +                                ODP_PACKET_INVALID;
> +       }
> +
> +       fill_time_record(&start);
> +
> +       while ((packets_sent < cargs->iteration_count) ||
> +              (packets_received <  cargs->iteration_count)) {
> +               void *mem;
> +               odp_crypto_op_result_t result;
> +
> +               if ((packets_sent < cargs->iteration_count) &&
> +                   (packets_sent - packets_received <
> +                    cargs->in_flight)) {
> +                       if (!cargs->reuse_packet) {
> +                               /*
> +                                * For in place test we use just one
> +                                * statically allocated buffer.
> +                                * For now in place test we have to
> +                                * allocate and initialize packet
> +                                * every time.
> +                                * Note we leaked one packet here.
> +                                */
> +                               pkt = odp_packet_alloc(pkt_pool,
> +                                                      payload_length);
> +                               if (pkt == ODP_PACKET_INVALID) {
> +                                       app_err("failed to allocate
> buffer\n");
> +                                       rc = -1;
> +                               } else {
> +                                       void *mem = odp_packet_data(pkt);
> +
> +                                       memset(mem, 1, payload_length);
> +                               }
> +                               params.pkt = pkt;
> +                               params.out_pkt = cargs->in_place ? pkt :
> +                                                ODP_PACKET_INVALID;
> +                       }
> +
> +                       if (cargs->debug_packets) {
> +                               mem = odp_packet_data(params.pkt);
> +                               print_mem("Packet before encryption:",
> +                                         mem, payload_length);
> +                       }
> +
> +                       rc = odp_crypto_operation(&params, &posted,
> +                                                 &result);
> +                       if (rc)
> +                               app_err("failed odp_crypto_operation: rc =
> %d\n",
> +                                       rc);
> +                       else
> +                               packets_sent++;
> +               }
> +
> +               if (!posted) {
> +                       packets_received++;
> +                       if (cargs->debug_packets) {
> +                               mem = odp_packet_data(params.out_pkt);
> +                               print_mem("Imediately encrypted packet",
> mem,
> +                                         payload_length +
> +                                         config->hash_adjust);
> +                       }
> +                       if (!cargs->in_place) {
> +                               if (cargs->reuse_packet) {
> +                                       params.pkt = params.out_pkt;
> +                                       params.out_pkt =
> ODP_PACKET_INVALID;
> +                               } else {
> +                                       odp_packet_free(params.out_pkt);
> +                               }
> +                       }
> +               } else {
> +                       odp_event_t ev;
> +                       odp_crypto_compl_t compl;
> +                       odp_crypto_op_result_t result;
> +                       odp_packet_t out_pkt;
> +
> +                       if (cargs->schedule)
> +                               ev = odp_schedule(NULL,
> +                                                 ODP_SCHED_NO_WAIT);
> +                       else
> +                               ev = odp_queue_deq(out_queue);
> +
> +                       while (ev != ODP_EVENT_INVALID) {
> +                               compl = odp_crypto_compl_from_event(ev);
> +                               odp_crypto_compl_result(compl, &result);
> +                               odp_crypto_compl_free(compl);
> +                               out_pkt = result.pkt;
> +
> +                               if (cargs->debug_packets) {
> +                                       mem = odp_packet_data(out_pkt);
> +                                       print_mem("Receieved encrypted
> packet",
> +                                                 mem,
> +                                                 payload_length +
> +                                                 config->hash_adjust);
> +                               }
> +                               if (cargs->reuse_packet) {
> +                                       params.pkt = out_pkt;
> +                                       params.out_pkt =
> ODP_PACKET_INVALID;
> +                               } else {
> +                                       odp_packet_free(out_pkt);
> +                               }
> +                               packets_received++;
> +                               if (cargs->schedule)
> +                                       ev = odp_schedule(NULL,
> +
>  ODP_SCHED_NO_WAIT);
> +                               else
> +                                       ev = odp_queue_deq(out_queue);
> +                       };
> +               }
> +       }
> +
> +       fill_time_record(&end);
> +
> +       {
> +               double count;
> +
> +               count = get_elapsed_usec(&start, &end);
> +               result->elapsed = count /
> +                                 cargs->iteration_count;
> +
> +               count = get_rusage_self_diff(&start, &end);
> +               result->rusage_self = count /
> +                                     cargs->iteration_count;
> +
> +               count = get_rusage_thread_diff(&start, &end);
> +               result->rusage_thread = count /
> +                                       cargs->iteration_count;
> +       }
> +
> +       return rc;
> +}
> +
> +/**
> + * Process one algorithm. Note if paload size is specicified it is
> + * only one run. Or iterate over set of predefined payloads.
> + */
> +static int
> +run_measure_one_config(cspeed_args_t *cargs,
> +                      cspeed_alg_config_t *config)
> +{
> +       cspeed_run_result_t result;
> +       odp_crypto_session_t session;
> +       int rc = 0;
> +
> +       if (create_session_from_config(&session, config, cargs))
> +               rc = -1;
> +
> +       if (!rc) {
> +               if (cargs->payload_length) {
> +                       rc = run_measure_one(cargs, config, &session,
> +                                            cargs->payload_length,
> &result);
> +                       if (!rc) {
> +                               print_result_header();
> +                               print_result(cargs, cargs->payload_length,
> +                                            config, &result);
> +                       }
> +               } else {
> +                       unsigned int i;
> +
> +                       print_result_header();
> +                       for (i = 0;
> +                            i < (sizeof(payloads) / sizeof(unsigned int));
> +                            i++) {
> +                               rc = run_measure_one(cargs, config,
> &session,
> +                                                    payloads[i], &result);
> +                               if (rc)
> +                                       break;
> +                               print_result(cargs, payloads[i],
> +                                            config, &result);
> +                       }
> +               }
> +       }
> +
> +       if (session != ODP_CRYPTO_SESSION_INVALID)
> +               odp_crypto_session_destroy(session);
> +       return rc;
> +}
> +
> +typedef struct thr_arg {
> +       cspeed_args_t cspeed_args;
> +       cspeed_alg_config_t *cspeed_alg_config;
> +} thr_arg_t;
> +
> +static void *run_thr_func(void *arg)
> +{
> +       thr_arg_t *thr_args = (thr_arg_t *)arg;
> +
> +       run_measure_one_config(&thr_args->cspeed_args,
> +                              thr_args->cspeed_alg_config);
> +       return NULL;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +       cspeed_args_t cargs;
> +       odp_pool_t pool;
> +       odp_queue_param_t qparam;
> +       odp_pool_param_t params;
> +       odph_linux_pthread_t thr;
> +       odp_queue_t out_queue = ODP_QUEUE_INVALID;
> +       thr_arg_t thr_arg;
> +       int num_workers = 1;
> +       odp_cpumask_t cpumask;
> +       char cpumaskstr[ODP_CPUMASK_STR_SIZE];
> +
> +       /* Parse and store the application arguments */
> +       parse_args(argc, argv, &cargs);
> +
> +       /* Init ODP before calling anything else */
> +       if (odp_init_global(NULL, NULL)) {
> +               app_err("ODP global init failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* Init this thread */
> +       odp_init_local(ODP_THREAD_WORKER);
> +
> +       /* Create packet pool */
> +       memset(&params, 0, sizeof(params));
> +       params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
> +       params.pkt.len     = SHM_PKT_POOL_BUF_SIZE;
> +       params.pkt.num     = SHM_PKT_POOL_SIZE / SHM_PKT_POOL_BUF_SIZE;
> +       params.type        = ODP_POOL_PACKET;
> +       pool = odp_pool_create("packet_pool", &params);
> +
> +       if (pool == ODP_POOL_INVALID) {
> +               app_err("packet pool create failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +       odp_pool_print(pool);
> +
> +       if (cargs.schedule) {
> +               qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
> +               qparam.sched.sync  = ODP_SCHED_SYNC_NONE;
> +               qparam.sched.group = ODP_SCHED_GROUP_ALL;
> +               out_queue = odp_queue_create("crypto-out",
> +                                            ODP_QUEUE_TYPE_SCHED,
> &qparam);
> +       } else if (cargs.poll) {
> +               out_queue = odp_queue_create("crypto-out",
> +                                            ODP_QUEUE_TYPE_POLL, NULL);
> +       }
> +       if (cargs.schedule || cargs.poll) {
> +               if (out_queue == ODP_QUEUE_INVALID) {
> +                       app_err("crypto-out queue create failed.\n");
> +                       exit(EXIT_FAILURE);
> +               }
> +       }
> +
> +       if (cargs.schedule) {
> +               printf("Run in async scheduled mode\n");
> +
> +               thr_arg.cspeed_args = cargs;
> +               thr_arg.cspeed_alg_config = cargs.alg_config;
> +               num_workers = odp_cpumask_def_worker(&cpumask,
> +                                                    num_workers);
> +               (void)odp_cpumask_to_str(&cpumask, cpumaskstr,
> +                                        sizeof(cpumaskstr));
> +               printf("num worker threads:  %i\n",
> +                      num_workers);
> +               printf("first CPU:           %i\n",
> +                      odp_cpumask_first(&cpumask));
> +               printf("cpu mask:            %s\n",
> +                      cpumaskstr);
> +       } else if (cargs.poll) {
> +               printf("Run in async poll mode\n");
> +       } else {
> +               printf("Run in sync mode\n");
> +       }
> +
> +       if (cargs.alg_config) {
> +               if (cargs.schedule) {
> +                       odph_linux_pthread_create(&thr, &cpumask,
> +                                                 run_thr_func, &thr_arg);
> +                       odph_linux_pthread_join(&thr, num_workers);
> +               } else {
> +                       run_measure_one_config(&cargs, cargs.alg_config);
> +               }
> +       } else {
> +               unsigned int i;
> +
> +               for (i = 0;
> +                    i < (sizeof(algs_config) /
> sizeof(cspeed_alg_config_t));
> +                    i++) {
> +                       run_measure_one_config(&cargs, algs_config + i);
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static void parse_args(int argc, char *argv[], cspeed_args_t *cargs)
> +{
> +       int opt;
> +       int long_index;
> +       static struct option longopts[] = {
> +               {"algorithm", optional_argument, NULL, 'a'},
> +               {"count", optional_argument, NULL, 'c'},
> +               {"debug",  no_argument, NULL, 'd'},
> +               {"flight", optional_argument, NULL, 'f'},
> +               {"help", no_argument, NULL, 'h'},
> +               {"iterations", optional_argument, NULL, 'i'},
> +               {"inplace", no_argument, NULL, 'n'},
> +               {"payload", optional_argument, NULL, 'l'},
> +               {"sessions", optional_argument, NULL, 'm'},
> +               {"reuse", no_argument, NULL, 'r'},
> +               {"poll", no_argument, NULL, 'p'},
> +               {"schedule", no_argument, NULL, 's'},
> +               {NULL, 0, NULL, 0}
> +       };
> +
> +       cargs->in_place = 0;
> +       cargs->in_flight = 1;
> +       cargs->debug_packets = 0;
> +       cargs->core_count = 1;
> +       cargs->iteration_count = 10000;
> +       cargs->payload_length = 0;
> +       cargs->alg_config = NULL;
> +       cargs->reuse_packet = 0;
> +       cargs->schedule = 0;
> +
> +       while (1) {
> +               opt = getopt_long(argc, argv, "+a:c:df:hi:m:nl:spr",
> +                                 longopts, &long_index);
> +
> +               if (opt == -1)
> +                       break;  /* No more options */
> +
> +               switch (opt) {
> +               case 'a':
> +                       cargs->alg_config = find_config_by_name(optarg);
> +                       if (!cargs->alg_config) {
> +                               printf("cannot test crypto '%s'
> configuration\n",
> +                                      optarg);
> +                               usage(argv[0]);
> +                               exit(-1);
> +                       }
> +               case 'c':
> +                       cargs->core_count = atoi(optarg);
> +                       break;
> +               case 'd':
> +                       cargs->debug_packets = 1;
> +                       break;
> +               case 'i':
> +                       cargs->iteration_count = atoi(optarg);
> +                       break;
> +               case 'f':
> +                       cargs->in_flight = atoi(optarg);
> +                       break;
> +               case 'h':
> +                       usage(argv[0]);
> +                       exit(EXIT_SUCCESS);
> +                       break;
> +               case 'm':
> +                       cargs->max_sessions = atoi(optarg);
> +                       break;
> +               case 'n':
> +                       cargs->in_place = 1;
> +                       break;
> +               case 'l':
> +                       cargs->payload_length = atoi(optarg);
> +                       break;
> +               case 'r':
> +                       cargs->reuse_packet = 1;
> +                       break;
> +               case 's':
> +                       cargs->schedule = 1;
> +                       break;
> +               case 'p':
> +                       cargs->poll = 1;
> +                       break;
> +               default:
> +                       break;
> +               }
> +       }
> +
> +       optind = 1;             /* reset 'extern optind' from the getopt
> lib */
> +
> +       if ((cargs->in_flight > 1) && cargs->reuse_packet) {
> +               printf("-f (in flight > 1) and -r (reuse packet) options
> are not compatible\n");
> +               usage(argv[0]);
> +               exit(-1);
> +       }
> +       if (cargs->schedule && cargs->poll) {
> +               printf("-s (schedule) and -p (poll) options are not
> compatible\n");
> +               usage(argv[0]);
> +               exit(-1);
> +       }
> +}
> +
> +/**
> + * Prinf usage information
> + */
> +static void usage(char *progname)
> +{
> +       printf("\n"
> +              "Usage: %s OPTIONS\n"
> +              "  E.g. %s -i 100000\n"
> +              "\n"
> +              "OpenDataPlane crypto speed measure.\n"
> +              "Optional OPTIONS\n"
> +              "  -a, --algorithm <name> Specify algorithm name (default
> all)\n"
> +              "                         Supported values are:\n",
> +              progname, progname);
> +
> +       print_config_names("                                  ");
> +       printf("  -c, --count <number> Core count. (NOT used)\n"
>

I assume we should make a story up to revisit this option and have the
scheduler distribute work.


> +              "  -d, --debug          Enable dump of processed packets.\n"
> +              "  -f, --flight <number> Max number of packet processed in
> parallel (default 1)\n"
> +              "  -i, --iterations <number> Number of iterations.\n"
> +              "  -n, --inplace        Encrypt on place.\n"
> +              "  -l, --payload        Payload length.\n"
> +              "  -r, --reuse          Output encrypted packet is passed
> as input\n"
> +              "                       to next encrypt iteration.\n"
> +              "  -s, --schedule       Use scheduler for completion
> events.\n"
> +              "  -p, --poll           Poll completion queue for
> completion events.\n"
> +              "  -h, --help           Display help and exit.\n"
> +              "\n");
> +}
> --
> 1.9.3
>
> _______________________________________________
> lng-odp mailing list
> lng-odp@lists.linaro.org
> https://lists.linaro.org/mailman/listinfo/lng-odp
>
diff mbox

Patch

diff --git a/test/performance/Makefile.am b/test/performance/Makefile.am
index 721615b..351dc97 100644
--- a/test/performance/Makefile.am
+++ b/test/performance/Makefile.am
@@ -2,7 +2,7 @@  include $(top_srcdir)/test/Makefile.inc
 
 TESTS_ENVIRONMENT += TEST_DIR=${builddir}
 
-EXECUTABLES = odp_atomic$(EXEEXT) odp_pktio_perf$(EXEEXT)
+EXECUTABLES = odp_atomic$(EXEEXT) odp_pktio_perf$(EXEEXT) odp_cspeed$(EXEEXT)
 
 COMPILE_ONLY = odp_l2fwd$(EXEEXT) \
 	       odp_scheduling$(EXEEXT)
@@ -20,6 +20,8 @@  odp_atomic_LDFLAGS = $(AM_LDFLAGS) -static
 odp_atomic_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
 odp_scheduling_LDFLAGS = $(AM_LDFLAGS) -static
 odp_scheduling_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
+odp_cspeed_LDFLAGS = $(AM_LDFLAGS) -static
+odp_cspeed_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
 
 noinst_HEADERS = \
 		  $(top_srcdir)/test/test_debug.h
@@ -27,5 +29,6 @@  noinst_HEADERS = \
 dist_odp_atomic_SOURCES = odp_atomic.c
 dist_odp_scheduling_SOURCES = odp_scheduling.c
 dist_odp_pktio_perf_SOURCES = odp_pktio_perf.c
+dist_odp_cspeed_SOURCES = odp_cspeed.c
 
 EXTRA_DIST = $(TESTSCRIPTS)
diff --git a/test/performance/odp_cspeed.c b/test/performance/odp_cspeed.c
new file mode 100644
index 0000000..28ac1d5
--- /dev/null
+++ b/test/performance/odp_cspeed.c
@@ -0,0 +1,946 @@ 
+/* Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif /* _GNU_SOURCE */
+
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <odp.h>
+#include <odp/helper/linux.h>
+
+#define app_err(fmt, ...) \
+	fprintf(stderr, "%s:%d:%s(): Error: " fmt, __FILE__, \
+		__LINE__, __func__, ##__VA_ARGS__)
+
+/** @def SHM_PKT_POOL_SIZE
+ * @brief Size of the shared memory block
+ */
+#define SHM_PKT_POOL_SIZE      (512 * 2048 * 2)
+
+/** @def SHM_PKT_POOL_BUF_SIZE
+ * @brief Buffer size of the packet pool buffer
+ */
+#define SHM_PKT_POOL_BUF_SIZE  (1024 * 32)
+
+static uint8_t test_iv[8] = "01234567";
+
+static uint8_t test_key16[16] = { 0x01, 0x02, 0x03, 0x04, 0x05,
+				  0x06, 0x07, 0x08, 0x09, 0x0a,
+				  0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+				  0x10,
+};
+
+static uint8_t test_key24[24] = { 0x01, 0x02, 0x03, 0x04, 0x05,
+				  0x06, 0x07, 0x08, 0x09, 0x0a,
+				  0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+				  0x10, 0x11, 0x12, 0x13, 0x14,
+				  0x15, 0x16, 0x17, 0x18
+};
+
+/**
+ * Structure that holds template for session create call
+ * for different algorithms supported by test
+ */
+typedef struct {
+	const char *name;		      /**< Algorithm name */
+	odp_crypto_session_params_t session;  /**< Prefilled crypto session params */
+	unsigned int hash_adjust;	      /**< Size of hash */
+} cspeed_alg_config_t;
+
+/**
+ * Parsed command line cspeed arguments. Describes test configuration.
+ */
+typedef struct {
+	/**
+	 * If non zero prints content of packets. Enabled by -d or
+	 * --debug option.
+	 */
+	int debug_packets;
+
+	/**
+	 * If non zero Try to run crypto operation in place. Note some
+	 * implementation may not support such mode. Enabled by -n or
+	 * --inplace option.
+	 */
+	int in_place;
+
+	/**
+	 * If non zeor output of previous operation taken as input for
+	 * next encrypt operations. Enabled by -r or --reuse option.
+	 */
+	int reuse_packet;
+
+	/**
+	 * Maximum number of outstanding encryption requests. Note code
+	 * poll for results over queue and if nothing is available it can
+	 * submit more encryption requests up to maximum number specified by
+	 * this option. Specified through -f or --flight option.
+	 */
+	int in_flight;
+
+	/**
+	 * Number of core to run on. Currently is not used.
+	 */
+	int core_count;
+
+	/**
+	 * Number of iteration to repeat crypto operation to get good
+	 * average number. Specified through -i or --terations option.
+	 * Default is 10000.
+	 */
+	int iteration_count;
+
+	/**
+	 * Maximum sessions. Currently is not used.
+	 */
+	int max_sessions;
+
+	/**
+	 * Payload size to test. If 0 set of predefined payload sizes
+	 * is tested. Specified through -p or --payload option.
+	 */
+	int payload_length;
+
+	/**
+	 * Pointer to selected algorithm to test. If NULL all available
+	 * alogorthims are tested. Name of algorithm is passed through
+	 * -a or --algorithm option.
+	 */
+	cspeed_alg_config_t *alg_config;
+
+	/**
+	 * Use scheduler to get completion events from crypto operation.
+	 * Specified through -s argument.
+	 * */
+	int schedule;
+
+	/*
+	 * Poll completion queue for crypto completion events.
+	 * Specified through -p argument.
+	 */
+	int poll;
+} cspeed_args_t;
+
+/*
+ * Helper structure that holds averages for test of one algorithm
+ * for given payload size.
+ */
+typedef struct {
+	/**
+	 * Elapsed time for one crypto operation.
+	 */
+	double elapsed;
+
+	/**
+	 * CPU time spent pre one crypto operation by whole process
+	 * i.e include current and all other threads in process.
+	 * It is filled with 'getrusage(RUSAGE_SELF, ...)' call.
+	 */
+	double rusage_self;
+
+	/**
+	 * CPU time spent per one crypto operation by current thread
+	 * only. It is filled with 'getrusage(RUSAGE_THREAD, ...)'
+	 * call.
+	 */
+	double rusage_thread;
+} cspeed_run_result_t;
+
+/**
+ * Structure holds one snap to misc times of current process.
+ */
+typedef struct {
+	struct timeval tv;	 /**< Elapsed time */
+	struct rusage ru_self;	 /**< Rusage value for whole process */
+	struct rusage ru_thread; /**< Rusage value for current thread */
+} time_record_t;
+
+static void parse_args(int argc, char *argv[], cspeed_args_t *cargs);
+static void usage(char *progname);
+
+/**
+ * Set of predefined payloads. Make sure that maximum payload
+ * size is not bigger than SHM_PKT_POOL_BUF_SIZE. May relax when
+ * implementation start support segmented buffers/packets.
+ */
+static unsigned int payloads[] = {
+	16,
+	64,
+	256,
+	1024,
+	8192,
+	16384
+};
+
+/**
+ * Set of known algorithms to test
+ */
+static cspeed_alg_config_t algs_config[] = {
+	{
+		.name = "3des-cbc-null",
+		.session = {
+			.cipher_alg = ODP_CIPHER_ALG_3DES_CBC,
+			.cipher_key = {
+				.data = test_key24,
+				.length = sizeof(test_key24)
+			},
+			.iv = {
+				.data = test_iv,
+				.length = 8,
+			},
+			.auth_alg = ODP_AUTH_ALG_NULL
+		},
+	},
+	{
+		.name = "3des-cbc-hmac-md5-96",
+		.session = {
+			.cipher_alg = ODP_CIPHER_ALG_3DES_CBC,
+			.cipher_key = {
+				.data = test_key24,
+				.length = sizeof(test_key24)
+			},
+			.iv = {
+				.data = test_iv,
+				.length = 8,
+			},
+			.auth_alg = ODP_AUTH_ALG_MD5_96,
+			.auth_key = {
+				.data = test_key16,
+				.length = sizeof(test_key16)
+			}
+		},
+		.hash_adjust = 12
+	},
+	{
+		.name = "null-hmac-md5-96",
+		.session = {
+			.cipher_alg = ODP_CIPHER_ALG_NULL,
+			.auth_alg = ODP_AUTH_ALG_MD5_96,
+			.auth_key = {
+				.data = test_key16,
+				.length = sizeof(test_key16)
+			}
+		},
+		.hash_adjust = 12
+	},
+};
+
+/**
+ * Find corresponding config for given name. Returns NULL
+ * if config for given name is not found.
+ */
+static cspeed_alg_config_t *
+find_config_by_name(const char *name) {
+	unsigned int i;
+	cspeed_alg_config_t *ret = NULL;
+
+	for (i = 0; i < (sizeof(algs_config) / sizeof(cspeed_alg_config_t));
+	     i++) {
+		if (strcmp(algs_config[i].name, name) == 0) {
+			ret = algs_config + i;
+			break;
+		}
+	}
+	return ret;
+}
+
+/**
+ * Helper function that prints list of algorithms that this
+ * test understands.
+ */
+static void
+print_config_names(const char *prefix) {
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(algs_config) / sizeof(cspeed_alg_config_t));
+	     i++) {
+		printf("%s %s\n", prefix, algs_config[i].name);
+	}
+}
+
+/**
+ * Snap current time values and put them into 'rec'.
+ */
+static void
+fill_time_record(time_record_t *rec)
+{
+	gettimeofday(&rec->tv, NULL);
+	getrusage(RUSAGE_SELF, &rec->ru_self);
+	getrusage(RUSAGE_THREAD, &rec->ru_thread);
+}
+
+/**
+ * Calculated CPU time difference for given two rusage structures.
+ * Note it adds user space and system time together.
+ */
+static unsigned long long
+get_rusage_diff(struct rusage *start, struct rusage *end)
+{
+	unsigned long long rusage_diff;
+	unsigned long long rusage_start;
+	unsigned long long rusage_end;
+
+	rusage_start = (start->ru_utime.tv_sec * 1000000) +
+		       (start->ru_utime.tv_usec);
+	rusage_start += (start->ru_stime.tv_sec * 1000000) +
+			(start->ru_stime.tv_usec);
+
+	rusage_end = (end->ru_utime.tv_sec * 1000000) +
+		     (end->ru_utime.tv_usec);
+	rusage_end += (end->ru_stime.tv_sec * 1000000) +
+		      (end->ru_stime.tv_usec);
+
+	rusage_diff = rusage_end - rusage_start;
+
+	return rusage_diff;
+}
+
+/**
+ * Get diff for RUSAGE_SELF (whole process) between two time snap
+ * records.
+ */
+static unsigned long long
+get_rusage_self_diff(time_record_t *start, time_record_t *end)
+{
+	return get_rusage_diff(&start->ru_self, &end->ru_self);
+}
+
+/**
+ * Get diff for RUSAGE_THREAD (current thread only) between two
+ * time snap records.
+ */
+static unsigned long long
+get_rusage_thread_diff(time_record_t *start, time_record_t *end)
+{
+	return get_rusage_diff(&start->ru_thread, &end->ru_thread);
+}
+
+/**
+ * Get diff of elapsed time between two time snap records
+ */
+static unsigned long long
+get_elapsed_usec(time_record_t *start, time_record_t *end)
+{
+	unsigned long long s;
+	unsigned long long e;
+
+	s = (start->tv.tv_sec * 1000000) +
+	    (start->tv.tv_usec);
+	e = (end->tv.tv_sec * 1000000) +
+	    (end->tv.tv_usec);
+
+	return e - s;
+}
+
+#define REPORT_HEADER	    "\n%30.30s %15s %15s %15s %15s %15s %15s\n"
+#define REPORT_LINE	    "%30.30s %15d %15d %15.3f %15.3f %15.3f %15d\n"
+
+/**
+ * Print header line for our report.
+ */
+static void
+print_result_header(void)
+{
+	printf(REPORT_HEADER,
+	       "algorithm", "avg over #", "payload (bytes)", "elapsed (us)",
+	       "rusg self (us)", "rusg thrd (us)", "throughput (Kb)");
+}
+
+/**
+ * Print one line of our report.
+ */
+static void
+print_result(cspeed_args_t *cargs,
+	     unsigned int payload_length,
+	     cspeed_alg_config_t *config,
+	     cspeed_run_result_t *result)
+{
+	unsigned int throughput;
+
+	throughput = (1000000.0 / result->elapsed) * payload_length / 1024;
+	printf(REPORT_LINE,
+	       config->name, cargs->iteration_count, payload_length,
+	       result->elapsed, result->rusage_self, result->rusage_thread,
+	       throughput);
+}
+
+/**
+ * Print piece of memory with given size.
+ */
+static void
+print_mem(const char *msg,
+	  const unsigned char *ptr,
+	  unsigned int len)
+{
+	unsigned i, j;
+	char c;
+	char line[81];
+	char *p;
+
+	if (msg)
+		printf("\n%s (bytes size = %d)", msg, len);
+
+	for (i = 0; i < len; i += 16) {
+		p = line;
+		sprintf(p, "\n%04x   ", i); p += 8;
+
+		for (j = 0; j < 16; j++) {
+			if (i + j == len)
+				break;
+
+			sprintf(p, " %02x", (ptr)[i + j]); p += 3;
+		}
+
+		for (; j < 16; j++) {
+			sprintf(p, "   "); p += 3;
+		}
+
+		sprintf(p, "   "); p += 3;
+
+		for (j = 0; j < 16; j++) {
+			if (i + j == len)
+				break;
+			c = (ptr)[i + j];
+			*p++ = (' ' <= c && c <= '~') ? c : '.';
+		}
+
+		*p = '\0';
+		printf("%s", line);
+	}
+	printf("\n");
+}
+
+/**
+ * Create ODP crypto session for given config.
+ */
+static int
+create_session_from_config(odp_crypto_session_t *session,
+			   cspeed_alg_config_t *config,
+			   cspeed_args_t *cargs)
+{
+	odp_crypto_session_params_t params;
+	enum odp_crypto_ses_create_err ses_create_rc;
+	odp_pool_t pkt_pool;
+	odp_queue_t out_queue;
+
+	memcpy(&params, &config->session, sizeof(odp_crypto_session_params_t));
+	params.op = ODP_CRYPTO_OP_ENCODE;
+	params.pref_mode   = ODP_CRYPTO_SYNC;
+
+	/* Lookup the packet pool */
+	pkt_pool = odp_pool_lookup("packet_pool");
+	if (pkt_pool == ODP_POOL_INVALID) {
+		app_err("packet_pool pool not found\n");
+		return -1;
+	}
+	params.output_pool = pkt_pool;
+
+	if (cargs->schedule || cargs->poll) {
+		out_queue = odp_queue_lookup("crypto-out");
+		if (out_queue == ODP_QUEUE_INVALID) {
+			app_err("crypto-out queue not found\n");
+			return -1;
+		}
+		params.compl_queue = out_queue;
+
+	} else {
+		params.compl_queue = ODP_QUEUE_INVALID;
+	}
+	if (odp_crypto_session_create(&params, session,
+				      &ses_create_rc)) {
+		app_err("crypto session create failed.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * Run measurement iterations for given config and payload size.
+ * Result of run returned in 'result' out parameter.
+ */
+static int
+run_measure_one(cspeed_args_t *cargs,
+		cspeed_alg_config_t *config,
+		odp_crypto_session_t *session,
+		unsigned int payload_length,
+		cspeed_run_result_t *result)
+{
+	odp_crypto_op_params_t params;
+
+	odp_pool_t pkt_pool;
+	odp_queue_t out_queue;
+	odp_packet_t pkt;
+	int rc = 0;
+
+	odp_bool_t posted = 0;
+
+	pkt_pool = odp_pool_lookup("packet_pool");
+	if (pkt_pool == ODP_POOL_INVALID) {
+		app_err("pkt_pool not found\n");
+		return -1;
+	}
+
+	out_queue = odp_queue_lookup("crypto-out");
+	if (cargs->schedule || cargs->poll) {
+		if (out_queue == ODP_QUEUE_INVALID) {
+			app_err("crypto-out queue not found\n");
+			return -1;
+		}
+	}
+
+	pkt = odp_packet_alloc(pkt_pool, payload_length);
+	if (pkt == ODP_PACKET_INVALID) {
+		app_err("failed to allocate buffer\n");
+		return -1;
+	}
+
+	void *mem = odp_packet_data(pkt);
+
+	memset(mem, 1, payload_length);
+
+	time_record_t start, end;
+	int packets_sent = 0;
+	int packets_received = 0;
+
+	/* Initialize parameters block */
+	memset(&params, 0, sizeof(params));
+	params.session = *session;
+
+	params.cipher_range.offset = 0;
+	params.cipher_range.length = payload_length;
+
+	params.auth_range.offset = 0;
+	params.auth_range.length = payload_length;
+	params.hash_result_offset = payload_length;
+
+	if (cargs->reuse_packet) {
+		params.pkt = pkt;
+		params.out_pkt = cargs->in_place ? pkt :
+				 ODP_PACKET_INVALID;
+	}
+
+	fill_time_record(&start);
+
+	while ((packets_sent < cargs->iteration_count) ||
+	       (packets_received <  cargs->iteration_count)) {
+		void *mem;
+		odp_crypto_op_result_t result;
+
+		if ((packets_sent < cargs->iteration_count) &&
+		    (packets_sent - packets_received <
+		     cargs->in_flight)) {
+			if (!cargs->reuse_packet) {
+				/*
+				 * For in place test we use just one
+				 * statically allocated buffer.
+				 * For now in place test we have to
+				 * allocate and initialize packet
+				 * every time.
+				 * Note we leaked one packet here.
+				 */
+				pkt = odp_packet_alloc(pkt_pool,
+						       payload_length);
+				if (pkt == ODP_PACKET_INVALID) {
+					app_err("failed to allocate buffer\n");
+					rc = -1;
+				} else {
+					void *mem = odp_packet_data(pkt);
+
+					memset(mem, 1, payload_length);
+				}
+				params.pkt = pkt;
+				params.out_pkt = cargs->in_place ? pkt :
+						 ODP_PACKET_INVALID;
+			}
+
+			if (cargs->debug_packets) {
+				mem = odp_packet_data(params.pkt);
+				print_mem("Packet before encryption:",
+					  mem, payload_length);
+			}
+
+			rc = odp_crypto_operation(&params, &posted,
+						  &result);
+			if (rc)
+				app_err("failed odp_crypto_operation: rc = %d\n",
+					rc);
+			else
+				packets_sent++;
+		}
+
+		if (!posted) {
+			packets_received++;
+			if (cargs->debug_packets) {
+				mem = odp_packet_data(params.out_pkt);
+				print_mem("Imediately encrypted packet", mem,
+					  payload_length +
+					  config->hash_adjust);
+			}
+			if (!cargs->in_place) {
+				if (cargs->reuse_packet) {
+					params.pkt = params.out_pkt;
+					params.out_pkt = ODP_PACKET_INVALID;
+				} else {
+					odp_packet_free(params.out_pkt);
+				}
+			}
+		} else {
+			odp_event_t ev;
+			odp_crypto_compl_t compl;
+			odp_crypto_op_result_t result;
+			odp_packet_t out_pkt;
+
+			if (cargs->schedule)
+				ev = odp_schedule(NULL,
+						  ODP_SCHED_NO_WAIT);
+			else
+				ev = odp_queue_deq(out_queue);
+
+			while (ev != ODP_EVENT_INVALID) {
+				compl = odp_crypto_compl_from_event(ev);
+				odp_crypto_compl_result(compl, &result);
+				odp_crypto_compl_free(compl);
+				out_pkt = result.pkt;
+
+				if (cargs->debug_packets) {
+					mem = odp_packet_data(out_pkt);
+					print_mem("Receieved encrypted packet",
+						  mem,
+						  payload_length +
+						  config->hash_adjust);
+				}
+				if (cargs->reuse_packet) {
+					params.pkt = out_pkt;
+					params.out_pkt = ODP_PACKET_INVALID;
+				} else {
+					odp_packet_free(out_pkt);
+				}
+				packets_received++;
+				if (cargs->schedule)
+					ev = odp_schedule(NULL,
+							  ODP_SCHED_NO_WAIT);
+				else
+					ev = odp_queue_deq(out_queue);
+			};
+		}
+	}
+
+	fill_time_record(&end);
+
+	{
+		double count;
+
+		count = get_elapsed_usec(&start, &end);
+		result->elapsed = count /
+				  cargs->iteration_count;
+
+		count = get_rusage_self_diff(&start, &end);
+		result->rusage_self = count /
+				      cargs->iteration_count;
+
+		count = get_rusage_thread_diff(&start, &end);
+		result->rusage_thread = count /
+					cargs->iteration_count;
+	}
+
+	return rc;
+}
+
+/**
+ * Process one algorithm. Note if paload size is specicified it is
+ * only one run. Or iterate over set of predefined payloads.
+ */
+static int
+run_measure_one_config(cspeed_args_t *cargs,
+		       cspeed_alg_config_t *config)
+{
+	cspeed_run_result_t result;
+	odp_crypto_session_t session;
+	int rc = 0;
+
+	if (create_session_from_config(&session, config, cargs))
+		rc = -1;
+
+	if (!rc) {
+		if (cargs->payload_length) {
+			rc = run_measure_one(cargs, config, &session,
+					     cargs->payload_length, &result);
+			if (!rc) {
+				print_result_header();
+				print_result(cargs, cargs->payload_length,
+					     config, &result);
+			}
+		} else {
+			unsigned int i;
+
+			print_result_header();
+			for (i = 0;
+			     i < (sizeof(payloads) / sizeof(unsigned int));
+			     i++) {
+				rc = run_measure_one(cargs, config, &session,
+						     payloads[i], &result);
+				if (rc)
+					break;
+				print_result(cargs, payloads[i],
+					     config, &result);
+			}
+		}
+	}
+
+	if (session != ODP_CRYPTO_SESSION_INVALID)
+		odp_crypto_session_destroy(session);
+	return rc;
+}
+
+typedef struct thr_arg {
+	cspeed_args_t cspeed_args;
+	cspeed_alg_config_t *cspeed_alg_config;
+} thr_arg_t;
+
+static void *run_thr_func(void *arg)
+{
+	thr_arg_t *thr_args = (thr_arg_t *)arg;
+
+	run_measure_one_config(&thr_args->cspeed_args,
+			       thr_args->cspeed_alg_config);
+	return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+	cspeed_args_t cargs;
+	odp_pool_t pool;
+	odp_queue_param_t qparam;
+	odp_pool_param_t params;
+	odph_linux_pthread_t thr;
+	odp_queue_t out_queue = ODP_QUEUE_INVALID;
+	thr_arg_t thr_arg;
+	int num_workers = 1;
+	odp_cpumask_t cpumask;
+	char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+
+	/* Parse and store the application arguments */
+	parse_args(argc, argv, &cargs);
+
+	/* Init ODP before calling anything else */
+	if (odp_init_global(NULL, NULL)) {
+		app_err("ODP global init failed.\n");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Init this thread */
+	odp_init_local(ODP_THREAD_WORKER);
+
+	/* Create packet pool */
+	memset(&params, 0, sizeof(params));
+	params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
+	params.pkt.len	   = SHM_PKT_POOL_BUF_SIZE;
+	params.pkt.num	   = SHM_PKT_POOL_SIZE / SHM_PKT_POOL_BUF_SIZE;
+	params.type	   = ODP_POOL_PACKET;
+	pool = odp_pool_create("packet_pool", &params);
+
+	if (pool == ODP_POOL_INVALID) {
+		app_err("packet pool create failed.\n");
+		exit(EXIT_FAILURE);
+	}
+	odp_pool_print(pool);
+
+	if (cargs.schedule) {
+		qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
+		qparam.sched.sync  = ODP_SCHED_SYNC_NONE;
+		qparam.sched.group = ODP_SCHED_GROUP_ALL;
+		out_queue = odp_queue_create("crypto-out",
+					     ODP_QUEUE_TYPE_SCHED, &qparam);
+	} else if (cargs.poll) {
+		out_queue = odp_queue_create("crypto-out",
+					     ODP_QUEUE_TYPE_POLL, NULL);
+	}
+	if (cargs.schedule || cargs.poll) {
+		if (out_queue == ODP_QUEUE_INVALID) {
+			app_err("crypto-out queue create failed.\n");
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (cargs.schedule) {
+		printf("Run in async scheduled mode\n");
+
+		thr_arg.cspeed_args = cargs;
+		thr_arg.cspeed_alg_config = cargs.alg_config;
+		num_workers = odp_cpumask_def_worker(&cpumask,
+						     num_workers);
+		(void)odp_cpumask_to_str(&cpumask, cpumaskstr,
+					 sizeof(cpumaskstr));
+		printf("num worker threads:  %i\n",
+		       num_workers);
+		printf("first CPU:	     %i\n",
+		       odp_cpumask_first(&cpumask));
+		printf("cpu mask:	     %s\n",
+		       cpumaskstr);
+	} else if (cargs.poll) {
+		printf("Run in async poll mode\n");
+	} else {
+		printf("Run in sync mode\n");
+	}
+
+	if (cargs.alg_config) {
+		if (cargs.schedule) {
+			odph_linux_pthread_create(&thr, &cpumask,
+						  run_thr_func, &thr_arg);
+			odph_linux_pthread_join(&thr, num_workers);
+		} else {
+			run_measure_one_config(&cargs, cargs.alg_config);
+		}
+	} else {
+		unsigned int i;
+
+		for (i = 0;
+		     i < (sizeof(algs_config) / sizeof(cspeed_alg_config_t));
+		     i++) {
+			run_measure_one_config(&cargs, algs_config + i);
+		}
+	}
+
+	return 0;
+}
+
+static void parse_args(int argc, char *argv[], cspeed_args_t *cargs)
+{
+	int opt;
+	int long_index;
+	static struct option longopts[] = {
+		{"algorithm", optional_argument, NULL, 'a'},
+		{"count", optional_argument, NULL, 'c'},
+		{"debug",  no_argument, NULL, 'd'},
+		{"flight", optional_argument, NULL, 'f'},
+		{"help", no_argument, NULL, 'h'},
+		{"iterations", optional_argument, NULL, 'i'},
+		{"inplace", no_argument, NULL, 'n'},
+		{"payload", optional_argument, NULL, 'l'},
+		{"sessions", optional_argument, NULL, 'm'},
+		{"reuse", no_argument, NULL, 'r'},
+		{"poll", no_argument, NULL, 'p'},
+		{"schedule", no_argument, NULL, 's'},
+		{NULL, 0, NULL, 0}
+	};
+
+	cargs->in_place = 0;
+	cargs->in_flight = 1;
+	cargs->debug_packets = 0;
+	cargs->core_count = 1;
+	cargs->iteration_count = 10000;
+	cargs->payload_length = 0;
+	cargs->alg_config = NULL;
+	cargs->reuse_packet = 0;
+	cargs->schedule = 0;
+
+	while (1) {
+		opt = getopt_long(argc, argv, "+a:c:df:hi:m:nl:spr",
+				  longopts, &long_index);
+
+		if (opt == -1)
+			break;	/* No more options */
+
+		switch (opt) {
+		case 'a':
+			cargs->alg_config = find_config_by_name(optarg);
+			if (!cargs->alg_config) {
+				printf("cannot test crypto '%s' configuration\n",
+				       optarg);
+				usage(argv[0]);
+				exit(-1);
+			}
+		case 'c':
+			cargs->core_count = atoi(optarg);
+			break;
+		case 'd':
+			cargs->debug_packets = 1;
+			break;
+		case 'i':
+			cargs->iteration_count = atoi(optarg);
+			break;
+		case 'f':
+			cargs->in_flight = atoi(optarg);
+			break;
+		case 'h':
+			usage(argv[0]);
+			exit(EXIT_SUCCESS);
+			break;
+		case 'm':
+			cargs->max_sessions = atoi(optarg);
+			break;
+		case 'n':
+			cargs->in_place = 1;
+			break;
+		case 'l':
+			cargs->payload_length = atoi(optarg);
+			break;
+		case 'r':
+			cargs->reuse_packet = 1;
+			break;
+		case 's':
+			cargs->schedule = 1;
+			break;
+		case 'p':
+			cargs->poll = 1;
+			break;
+		default:
+			break;
+		}
+	}
+
+	optind = 1;		/* reset 'extern optind' from the getopt lib */
+
+	if ((cargs->in_flight > 1) && cargs->reuse_packet) {
+		printf("-f (in flight > 1) and -r (reuse packet) options are not compatible\n");
+		usage(argv[0]);
+		exit(-1);
+	}
+	if (cargs->schedule && cargs->poll) {
+		printf("-s (schedule) and -p (poll) options are not compatible\n");
+		usage(argv[0]);
+		exit(-1);
+	}
+}
+
+/**
+ * Prinf usage information
+ */
+static void usage(char *progname)
+{
+	printf("\n"
+	       "Usage: %s OPTIONS\n"
+	       "  E.g. %s -i 100000\n"
+	       "\n"
+	       "OpenDataPlane crypto speed measure.\n"
+	       "Optional OPTIONS\n"
+	       "  -a, --algorithm <name> Specify algorithm name (default all)\n"
+	       "			 Supported values are:\n",
+	       progname, progname);
+
+	print_config_names("				      ");
+	printf("  -c, --count <number> Core count. (NOT used)\n"
+	       "  -d, --debug	       Enable dump of processed packets.\n"
+	       "  -f, --flight <number> Max number of packet processed in parallel (default 1)\n"
+	       "  -i, --iterations <number> Number of iterations.\n"
+	       "  -n, --inplace	       Encrypt on place.\n"
+	       "  -l, --payload	       Payload length.\n"
+	       "  -r, --reuse	       Output encrypted packet is passed as input\n"
+	       "		       to next encrypt iteration.\n"
+	       "  -s, --schedule       Use scheduler for completion events.\n"
+	       "  -p, --poll           Poll completion queue for completion events.\n"
+	       "  -h, --help	       Display help and exit.\n"
+	       "\n");
+}