Message ID | 1445239479-7406-1-git-send-email-alexandru.badicioiu@linaro.org |
---|---|
State | New |
Headers | show |
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(¶ms, &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(¶ms, 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(¶ms, 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(¶ms, &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(¶ms, 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", ¶ms); > + > + 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 --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(¶ms, &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(¶ms, 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(¶ms, 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(¶ms, &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(¶ms, 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", ¶ms); + + 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"); +}