DPDK patches and discussions
 help / color / mirror / Atom feed
* [PATCH] test: add cryptodev crosscheck suite
@ 2022-12-21 11:08 Volodymyr Fialko
  2023-02-09 12:34 ` [PATCH v2] " Volodymyr Fialko
  0 siblings, 1 reply; 11+ messages in thread
From: Volodymyr Fialko @ 2022-12-21 11:08 UTC (permalink / raw)
  To: dev, Akhil Goyal, Fan Zhang; +Cc: jerinj, anoobj, Volodymyr Fialko

Add new test suite that allow to crosscheck cryptodevs based on their
capabilities. Test suite will scan supported capabilities, determine
common and generate appropriate input, then suite will check if output
from all crpytodevs are the same.

Signed-off-by: Volodymyr Fialko <vfialko@marvell.com>
---
 app/test/meson.build                 |    1 +
 app/test/test_cryptodev_crosscheck.c | 1008 ++++++++++++++++++++++++++
 2 files changed, 1009 insertions(+)
 create mode 100644 app/test/test_cryptodev_crosscheck.c

diff --git a/app/test/meson.build b/app/test/meson.build
index 1b9ce355f8..f0bcf99fb1 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -33,6 +33,7 @@ test_sources = files(
         'test_cryptodev.c',
         'test_cryptodev_asym.c',
         'test_cryptodev_blockcipher.c',
+        'test_cryptodev_crosscheck.c',
         'test_cryptodev_security_ipsec.c',
         'test_cryptodev_security_pdcp.c',
         'test_cycles.c',
diff --git a/app/test/test_cryptodev_crosscheck.c b/app/test/test_cryptodev_crosscheck.c
new file mode 100644
index 0000000000..e5f841409c
--- /dev/null
+++ b/app/test/test_cryptodev_crosscheck.c
@@ -0,0 +1,1008 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2022 Marvell.
+ */
+#include <rte_cryptodev.h>
+#include <rte_malloc.h>
+
+#include "test.h"
+#include "test_cryptodev.h"
+
+#define MAX_NB_SESSIONS 1
+#define MAX_TEST_STRING_LEN 256
+
+/* Define list end for known algorithms */
+#define CRYPTO_AUTH_MAX_IDX (RTE_CRYPTO_AUTH_SHA3_512_HMAC)
+#define CRYPTO_CIPHER_MAX_IDX (RTE_CRYPTO_CIPHER_DES_DOCSISBPI)
+#define CRYPTO_AEAD_MAX_IDX (RTE_CRYPTO_AEAD_CHACHA20_POLY1305)
+#define CRYPTO_ALGOS_LEN (CRYPTO_AUTH_MAX_IDX + CRYPTO_CIPHER_MAX_IDX + CRYPTO_AEAD_MAX_IDX)
+
+static struct rte_cryptodev_symmetric_capability common_symm_capas[CRYPTO_ALGOS_LEN];
+
+static struct rte_cryptodev_symmetric_capability*
+common_capability_get(int algo, enum rte_crypto_sym_xform_type xform_type)
+{
+	const int type_to_offset[] = {
+		[RTE_CRYPTO_SYM_XFORM_AUTH] = 0,
+		[RTE_CRYPTO_SYM_XFORM_CIPHER] = CRYPTO_AUTH_MAX_IDX,
+		[RTE_CRYPTO_SYM_XFORM_AEAD] = CRYPTO_AUTH_MAX_IDX + CRYPTO_CIPHER_MAX_IDX,
+	};
+
+	return &common_symm_capas[type_to_offset[xform_type] + algo - 1];
+}
+
+enum capability_select_type {
+	CAPABILITY_TYPE_MIN,
+	CAPABILITY_TYPE_MAX,
+	CAPABILITY_TYPE_LAST,
+};
+
+static const char * const capability_select_strings[] = {
+	[CAPABILITY_TYPE_MIN] = "MIN",
+	[CAPABILITY_TYPE_MAX] = "MAX",
+};
+
+static size_t input_length[] = { 64, 256, 512 };
+
+static const char*
+algo_name_get(const struct rte_cryptodev_symmetric_capability *capa)
+{
+	switch (capa->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		return rte_crypto_auth_algorithm_strings[capa->auth.algo];
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		return rte_crypto_cipher_algorithm_strings[capa->cipher.algo];
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		return rte_crypto_aead_algorithm_strings[capa->aead.algo];
+	default:
+		return NULL;
+	}
+}
+
+/* Calculate number of test cases(combinations) per algorithm */
+#define NB_TEST_CASES_PER_ALGO (CAPABILITY_TYPE_LAST * RTE_DIM(input_length))
+
+enum crypto_op_type {
+	OP_ENCRYPT,
+	OP_DECRYPT,
+};
+
+struct crosscheck_test_profile {
+	char name[MAX_TEST_STRING_LEN];
+	size_t input_buf_len;
+	uint16_t block_size;
+	uint16_t key_size;
+	uint8_t *key;
+	uint16_t iv_size;
+	uint8_t *iv;
+
+	enum rte_crypto_sym_xform_type xform_type;
+	union {
+		struct {
+			enum rte_crypto_auth_algorithm algo;
+			uint16_t digest_size;
+		} auth;
+		struct {
+			enum rte_crypto_cipher_algorithm algo;
+			uint32_t dataunit_set;
+		} cipher;
+		struct {
+			enum rte_crypto_aead_algorithm algo;
+			uint16_t digest_size;
+			uint16_t aad_size;
+		} aead;
+	};
+};
+
+struct meta_test_suite {
+	char suite_name[MAX_TEST_STRING_LEN];
+	struct crosscheck_test_profile profile[NB_TEST_CASES_PER_ALGO];
+};
+
+static struct crypto_testsuite_params testsuite_params;
+
+static void
+incrementing_generate(uint8_t *dst, uint8_t start, uint16_t size)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		dst[i] = start + i;
+}
+
+static void
+pattern_fill(uint8_t *input, const char *pattern, uint16_t size)
+{
+	size_t pattern_len = strlen(pattern);
+	size_t filled_len = 0, to_fill;
+
+	while (filled_len < size) {
+		to_fill = RTE_MIN(pattern_len, size - filled_len);
+		rte_memcpy(input, pattern, to_fill);
+		filled_len += to_fill;
+		input += to_fill;
+	}
+}
+
+static struct crosscheck_test_profile
+profile_create(struct rte_cryptodev_symmetric_capability *capa,
+		enum capability_select_type capability_type, size_t input_len)
+{
+	struct crosscheck_test_profile profile;
+
+	memset(&profile, 0, sizeof(profile));
+	profile.xform_type = capa->xform_type;
+
+	switch (capa->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		profile.block_size = capa->auth.block_size;
+		profile.auth.algo = capa->auth.algo;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->auth.key_size.min;
+			profile.iv_size = capa->auth.iv_size.min;
+			profile.auth.digest_size = capa->auth.digest_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->auth.key_size.max;
+			profile.iv_size = capa->auth.iv_size.max;
+			profile.auth.digest_size = capa->auth.digest_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		profile.block_size = capa->cipher.block_size;
+		profile.cipher.algo = capa->cipher.algo;
+		profile.cipher.dataunit_set = capa->cipher.dataunit_set;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->cipher.key_size.min;
+			profile.iv_size = capa->cipher.iv_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->cipher.key_size.max;
+			profile.iv_size = capa->cipher.iv_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		profile.block_size = capa->aead.block_size;
+		profile.aead.algo = capa->aead.algo;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->aead.key_size.min;
+			profile.iv_size = capa->aead.iv_size.min;
+			profile.aead.digest_size = capa->aead.digest_size.min;
+			profile.aead.aad_size = capa->aead.aad_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->aead.key_size.max;
+			profile.iv_size = capa->aead.iv_size.max;
+			profile.aead.digest_size = capa->aead.digest_size.max;
+			profile.aead.aad_size = capa->aead.aad_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	default:
+		rte_panic("Wrong xform profile type: %i\n", capa->xform_type);
+		break;
+	}
+
+	profile.input_buf_len = RTE_ALIGN_CEIL(input_len, profile.block_size);
+
+	if (profile.key_size) {
+		profile.key = rte_zmalloc(NULL, profile.key_size, 0);
+		RTE_VERIFY(profile.key != NULL);
+		pattern_fill(profile.key, "*Secret key*", profile.key_size);
+	}
+
+	if (profile.iv_size) {
+		profile.iv = rte_zmalloc(NULL, profile.iv_size, 0);
+		RTE_VERIFY(profile.iv != NULL);
+		pattern_fill(profile.iv, "IV ", profile.iv_size);
+	}
+
+	snprintf(profile.name, MAX_TEST_STRING_LEN,
+			"'%s' - capabilities: '%s', input len: '%zu'",
+			algo_name_get(capa), capability_select_strings[capability_type],
+			input_len);
+
+	return profile;
+}
+
+static inline int
+common_range_set(struct rte_crypto_param_range *dst, const struct rte_crypto_param_range *src)
+{
+	/* Check if ranges overlaps */
+	if ((dst->min > src->max) && (dst->max < src->min))
+		return -1;
+	dst->min = RTE_MAX(dst->min, src->min);
+	dst->max = RTE_MIN(dst->max, src->max);
+
+	return 0;
+}
+
+static void
+capabilities_inspect(void)
+{
+	struct rte_cryptodev_sym_capability_idx cap_indexes[CRYPTO_ALGOS_LEN], *cap_idx;
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	const struct rte_cryptodev_symmetric_capability *sym_capa;
+	struct rte_cryptodev_symmetric_capability *common_capa;
+	uint32_t algo, i, dev_id, caps_idx;
+
+	caps_idx = 0;
+	/* Create capability idx for known algorithms*/
+	for (algo = 1; algo <= CRYPTO_AUTH_MAX_IDX; algo++) {
+		cap_idx = &cap_indexes[caps_idx++];
+		cap_idx->type = RTE_CRYPTO_SYM_XFORM_AUTH;
+		cap_idx->algo.auth = algo;
+	}
+	for (algo = 1; algo <= CRYPTO_CIPHER_MAX_IDX; algo++) {
+		cap_idx = &cap_indexes[caps_idx++];
+		cap_idx->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+		cap_idx->algo.cipher = algo;
+	}
+	for (algo = 1; algo <= CRYPTO_AEAD_MAX_IDX; algo++) {
+		cap_idx = &cap_indexes[caps_idx++];
+		cap_idx->type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		cap_idx->algo.aead = algo;
+	}
+
+	for (caps_idx = 0; caps_idx < CRYPTO_ALGOS_LEN; caps_idx++) {
+		/* Gather common capabilities */
+		common_capa = &common_symm_capas[caps_idx];
+		common_capa->xform_type = RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED;
+		for (i = 0; i < ts_params->valid_dev_count; i++) {
+			dev_id = ts_params->valid_devs[i];
+			sym_capa = rte_cryptodev_sym_capability_get(dev_id,
+					&cap_indexes[caps_idx]);
+			if (sym_capa == NULL) {
+				/* Capability not supported by one of devs, mark and skip */
+				goto next_algo;
+			}
+
+			if (common_capa->xform_type == RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED) {
+				/* First time initialization, copy data, go to next device  */
+				*common_capa = *sym_capa;
+				continue;
+			}
+
+			switch (sym_capa->xform_type) {
+			case RTE_CRYPTO_SYM_XFORM_AUTH:
+				if (common_capa->auth.algo != sym_capa->auth.algo)
+					goto next_algo;
+
+				if (common_capa->auth.block_size != sym_capa->auth.block_size)
+					goto next_algo;
+
+				if (common_range_set(&common_capa->auth.key_size,
+							&sym_capa->auth.key_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->auth.digest_size,
+							&sym_capa->auth.digest_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->auth.aad_size,
+							&sym_capa->auth.aad_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->auth.iv_size,
+							&sym_capa->auth.iv_size))
+					goto next_algo;
+				break;
+			case RTE_CRYPTO_SYM_XFORM_CIPHER:
+				if (common_capa->cipher.algo != sym_capa->cipher.algo)
+					goto next_algo;
+
+				if (common_capa->cipher.block_size != sym_capa->cipher.block_size)
+					goto next_algo;
+
+				if (common_range_set(&common_capa->cipher.key_size,
+							&sym_capa->cipher.key_size))
+					goto next_algo;
+
+				if (common_range_set(&common_capa->cipher.iv_size,
+							&sym_capa->cipher.iv_size))
+					goto next_algo;
+				if (common_capa->cipher.dataunit_set !=
+						sym_capa->cipher.dataunit_set)
+					goto next_algo;
+				break;
+			case RTE_CRYPTO_SYM_XFORM_AEAD:
+				if (common_capa->aead.algo != sym_capa->aead.algo)
+					goto next_algo;
+
+				if (common_capa->aead.block_size != sym_capa->aead.block_size)
+					goto next_algo;
+
+				if (common_range_set(&common_capa->aead.key_size,
+							&sym_capa->aead.key_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->aead.digest_size,
+							&sym_capa->aead.digest_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->aead.aad_size,
+							&sym_capa->aead.aad_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->aead.iv_size,
+							&sym_capa->aead.iv_size))
+					goto next_algo;
+				break;
+			default:
+				RTE_LOG(ERR, USER1, "Unsupported xform_type!\n");
+				goto next_algo;
+			}
+		}
+
+		continue;
+next_algo:
+		common_capa->xform_type = RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED;
+	}
+}
+
+static int
+crosscheck_init(void)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	const uint32_t nb_queue_pairs = 1;
+	struct rte_cryptodev_info info;
+	uint32_t session_priv_size = 0;
+	uint32_t nb_devs, dev_id;
+
+	memset(ts_params, 0, sizeof(*ts_params));
+
+	/* Create list of valid crypto devs */
+	nb_devs = rte_cryptodev_count();
+	for (dev_id = 0; dev_id < nb_devs; dev_id++) {
+		rte_cryptodev_info_get(dev_id, &info);
+
+		if (info.sym.max_nb_sessions != 0 && info.sym.max_nb_sessions < MAX_NB_SESSIONS)
+			continue;
+		if (info.max_nb_queue_pairs < nb_queue_pairs)
+			continue;
+		ts_params->valid_devs[ts_params->valid_dev_count++] = dev_id;
+		/* Obtaining configuration parameters, that will satisfy all cryptodevs */
+		session_priv_size = RTE_MAX(session_priv_size,
+				rte_cryptodev_sym_get_private_session_size(dev_id));
+	}
+
+	if (ts_params->valid_dev_count < 2) {
+		RTE_LOG(WARNING, USER1, "Min number of cryptodevs for test is 2, found (%d)\n",
+				ts_params->valid_dev_count);
+		return TEST_SKIPPED;
+	}
+
+	/* Create pools for mbufs, crypto operations and sessions */
+	ts_params->mbuf_pool = rte_pktmbuf_pool_create("CRYPTO_MBUFPOOL", NUM_MBUFS,
+			MBUF_CACHE_SIZE, 0, MBUF_SIZE, rte_socket_id());
+	if (ts_params->mbuf_pool == NULL) {
+		RTE_LOG(ERR, USER1, "Can't create CRYPTO_MBUFPOOL\n");
+		return TEST_FAILED;
+	}
+
+	ts_params->op_mpool = rte_crypto_op_pool_create("MBUF_CRYPTO_SYM_OP_POOL",
+			RTE_CRYPTO_OP_TYPE_SYMMETRIC, NUM_MBUFS, MBUF_CACHE_SIZE,
+			DEFAULT_NUM_XFORMS * sizeof(struct rte_crypto_sym_xform) +
+			MAXIMUM_IV_LENGTH, rte_socket_id());
+
+	if (ts_params->op_mpool == NULL) {
+		RTE_LOG(ERR, USER1, "Can't create CRYPTO_OP_POOL\n");
+		return TEST_FAILED;
+	}
+
+	ts_params->session_mpool = rte_cryptodev_sym_session_pool_create("test_sess_mp",
+			MAX_NB_SESSIONS, session_priv_size, 0, 0, SOCKET_ID_ANY);
+	TEST_ASSERT_NOT_NULL(ts_params->session_mpool, "session mempool allocation failed");
+
+	/* Setup queue pair conf params */
+	ts_params->conf.nb_queue_pairs = nb_queue_pairs;
+	ts_params->conf.socket_id = SOCKET_ID_ANY;
+	ts_params->conf.ff_disable = RTE_CRYPTODEV_FF_SECURITY;
+	ts_params->qp_conf.nb_descriptors = MAX_NUM_OPS_INFLIGHT;
+	ts_params->qp_conf.mp_session = ts_params->session_mpool;
+
+	capabilities_inspect();
+
+	return TEST_SUCCESS;
+}
+
+static int
+dev_configure_and_start(uint64_t ff_disable)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	uint8_t i, dev_id;
+	uint16_t qp_id;
+
+	/* Reconfigure device to default parameters */
+	ts_params->conf.ff_disable = ff_disable;
+
+	/* Configure cryptodevs */
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		TEST_ASSERT_SUCCESS(rte_cryptodev_configure(dev_id, &ts_params->conf),
+				"Failed to configure cryptodev %u with %u qps",
+				dev_id, ts_params->conf.nb_queue_pairs);
+
+
+		for (qp_id = 0; qp_id < ts_params->conf.nb_queue_pairs; qp_id++) {
+			TEST_ASSERT_SUCCESS(rte_cryptodev_queue_pair_setup(
+				dev_id, qp_id, &ts_params->qp_conf,
+				rte_cryptodev_socket_id(dev_id)),
+				"Failed to setup queue pair %u on cryptodev %u",
+				qp_id, dev_id);
+		}
+		rte_cryptodev_stats_reset(dev_id);
+
+		/* Start the device */
+		TEST_ASSERT_SUCCESS(rte_cryptodev_start(dev_id), "Failed to start cryptodev %u",
+				dev_id);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+crosscheck_suite_setup(void)
+{
+	dev_configure_and_start(RTE_CRYPTODEV_FF_SECURITY);
+
+	return 0;
+}
+
+static void
+crosscheck_suite_teardown(void)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	uint8_t i, dev_id;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		rte_cryptodev_stop(dev_id);
+	}
+}
+
+static struct rte_crypto_op *
+crypto_request_process(uint8_t dev_id, struct rte_crypto_op *op)
+{
+	struct rte_crypto_op *res = NULL;
+
+	if (rte_cryptodev_enqueue_burst(dev_id, 0, &op, 1) != 1) {
+		RTE_LOG(ERR, USER1, "Error sending packet for encryption\n");
+		return NULL;
+	}
+
+	while (rte_cryptodev_dequeue_burst(dev_id, 0, &res, 1) == 0)
+		rte_pause();
+
+	if (res->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
+		RTE_LOG(ERR, USER1, "Operation status %d\n", res->status);
+		return NULL;
+	}
+
+	if (res != op) {
+		RTE_LOG(ERR, USER1, "Unexpected operation received!\n");
+		rte_crypto_op_free(res);
+		return NULL;
+	}
+
+	return res;
+}
+
+/* Create free room at the start of segment, by moving existing data further */
+static uint8_t*
+mbuf_room_create(struct rte_mbuf *m, size_t size)
+{
+	uint8_t *data;
+
+	data = rte_pktmbuf_mtod(m, uint8_t*);
+	memmove(data + size, data, m->data_len);
+	m->data_len += size;
+	m->pkt_len += size;
+
+	return data;
+}
+
+static struct rte_cryptodev_sym_session*
+session_create(const struct crosscheck_test_profile *profile, uint8_t dev_id,
+		enum crypto_op_type op_type)
+{
+	struct rte_cryptodev_sym_session *session;
+	struct rte_crypto_sym_xform xform;
+
+	memset(&xform, 0, sizeof(xform));
+
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		xform.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+		xform.next = NULL;
+		xform.auth.algo = profile->auth.algo;
+		xform.auth.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_AUTH_OP_GENERATE :
+			RTE_CRYPTO_AUTH_OP_VERIFY;
+		xform.auth.digest_length = profile->auth.digest_size;
+		xform.auth.key.length = profile->key_size;
+		xform.auth.key.data = profile->key;
+		xform.auth.iv.length = profile->iv_size;
+		xform.auth.iv.offset = IV_OFFSET;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		xform.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+		xform.next = NULL;
+		xform.cipher.algo = profile->cipher.algo;
+		xform.cipher.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_CIPHER_OP_ENCRYPT :
+			RTE_CRYPTO_CIPHER_OP_DECRYPT;
+		xform.cipher.key.length = profile->key_size;
+		xform.cipher.key.data = profile->key;
+		xform.cipher.iv.length = profile->iv_size;
+		xform.cipher.iv.offset = IV_OFFSET;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		xform.type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		xform.next = NULL;
+		xform.aead.algo = profile->aead.algo;
+		xform.aead.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_AEAD_OP_ENCRYPT :
+			RTE_CRYPTO_AEAD_OP_DECRYPT;
+		xform.aead.digest_length = profile->aead.digest_size;
+		xform.aead.key.length = profile->key_size;
+		xform.aead.key.data = profile->key;
+		xform.aead.iv.length = profile->iv_size;
+		xform.aead.iv.offset = IV_OFFSET;
+		xform.aead.aad_length = profile->aead.aad_size;
+		break;
+	default:
+		return NULL;
+	}
+
+	session = rte_cryptodev_sym_session_create(dev_id, &xform, testsuite_params.session_mpool);
+
+	return session;
+}
+
+static struct rte_mbuf*
+mbuf_create(const uint8_t *input_buf, uint16_t input_len)
+{
+	struct rte_mbuf *pkt;
+	uint8_t *pkt_data;
+
+	pkt = rte_pktmbuf_alloc(testsuite_params.mbuf_pool);
+	if (pkt == NULL) {
+		RTE_LOG(ERR, USER1,  "Failed to allocate input buffer in mempool");
+		return NULL;
+	}
+
+	/* zeroing tailroom */
+	memset(rte_pktmbuf_mtod(pkt, uint8_t *), 0, rte_pktmbuf_tailroom(pkt));
+
+	pkt_data = (uint8_t *)rte_pktmbuf_append(pkt, input_len);
+	if (pkt_data == NULL) {
+		RTE_LOG(ERR, USER1, "no room to append data, len: %d", input_len);
+		goto error;
+	}
+	rte_memcpy(pkt_data, input_buf, input_len);
+
+	return pkt;
+error:
+	rte_pktmbuf_free(pkt);
+	return NULL;
+}
+
+static uint16_t
+profile_digest_get(const struct crosscheck_test_profile *profile)
+{
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		return profile->auth.digest_size;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		return profile->aead.digest_size;
+	default:
+		return 0;
+	}
+}
+
+static struct rte_crypto_op*
+operation_create(const struct crosscheck_test_profile *profile,
+		struct rte_mbuf *ibuf, enum crypto_op_type op_type)
+{
+	uint8_t *digest_data = NULL, *aad_data = NULL, *iv_ptr = NULL;
+	uint16_t aad_size = 0, digest_size = 0, digest_offset = 0;
+	uint16_t plaintext_len = profile->input_buf_len;
+	rte_iova_t digest_addr = 0, aad_addr = 0;
+	struct rte_crypto_sym_op *sym_op;
+	struct rte_crypto_op *op;
+
+	op = rte_crypto_op_alloc(testsuite_params.op_mpool, RTE_CRYPTO_OP_TYPE_SYMMETRIC);
+	if (op == NULL) {
+		RTE_LOG(ERR, USER1, "Failed to allocate symmetric crypto operation struct");
+		return NULL;
+	}
+
+	/* Obtain aad and digest sizes */
+	digest_size = profile_digest_get(profile);
+	if (profile->xform_type == RTE_CRYPTO_SYM_XFORM_AEAD)
+		aad_size = profile->aead.aad_size;
+
+	if (aad_size) {
+		if (op_type == OP_ENCRYPT) {
+			if (rte_pktmbuf_tailroom(ibuf) > plaintext_len + aad_size) {
+				/* Put aad to data segment */
+				aad_data = mbuf_room_create(ibuf, aad_size);
+			} else {
+				RTE_LOG(ERR, USER1, "No space for aad in single mbuf\n");
+				goto error;
+			}
+			pattern_fill(aad_data, "This is an aad.", aad_size);
+		} else {
+			aad_data = rte_pktmbuf_mtod(ibuf, uint8_t *);
+		}
+		aad_addr = rte_pktmbuf_iova(ibuf);
+	}
+
+	if (digest_size) {
+		digest_offset = aad_size + plaintext_len;
+		if (op_type == OP_ENCRYPT) {
+			digest_data = (uint8_t *)rte_pktmbuf_append(ibuf, digest_size);
+			if (digest_data == NULL) {
+				RTE_LOG(ERR, USER1, "No room to append digest\n");
+				goto error;
+			}
+		} else {
+			digest_data = rte_pktmbuf_mtod_offset(ibuf, uint8_t *, digest_offset);
+		}
+		digest_addr = rte_pktmbuf_iova_offset(ibuf, digest_offset);
+	}
+
+	sym_op = op->sym;
+	memset(sym_op, 0, sizeof(*sym_op));
+
+	iv_ptr = rte_crypto_op_ctod_offset(op, uint8_t *, IV_OFFSET);
+	rte_memcpy(iv_ptr, profile->iv, profile->iv_size);
+
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		sym_op->auth.digest.phys_addr = digest_addr;
+		sym_op->auth.digest.data = digest_data;
+		sym_op->auth.data.length = plaintext_len;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		sym_op->cipher.data.length = plaintext_len;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		sym_op->aead.aad.phys_addr = aad_addr;
+		sym_op->aead.aad.data = aad_data;
+		sym_op->aead.digest.phys_addr = digest_addr;
+		sym_op->aead.digest.data = digest_data;
+		sym_op->aead.data.offset = aad_size;
+		sym_op->aead.data.length = plaintext_len;
+		break;
+	default:
+		goto error;
+	}
+
+	sym_op->m_src = ibuf;
+
+	return op;
+
+error:
+	rte_crypto_op_free(op);
+	return NULL;
+}
+
+static void
+mbuf_to_buf_copy(const struct rte_mbuf *m, uint8_t *res_buf, uint16_t *len)
+{
+	const uint8_t *out;
+
+	*len = m->pkt_len;
+	out = rte_pktmbuf_read(m, 0, *len, res_buf);
+	/* Single segment buffer */
+	if (out != res_buf)
+		memcpy(res_buf, out, *len);
+}
+
+static int
+single_dev_process(const struct crosscheck_test_profile *profile, uint16_t dev_id, enum
+		crypto_op_type op_type, const uint8_t *input_buf, uint16_t input_len,
+		uint8_t *output_buf, uint16_t *output_len)
+{
+	struct rte_cryptodev_sym_session *session = NULL;
+	struct rte_mbuf *ibuf = NULL, *obuf = NULL;
+	struct rte_crypto_op *op = NULL;
+	int ret = -1;
+
+	session = session_create(profile, dev_id, op_type);
+	if (session == NULL)
+		goto error;
+
+	ibuf = mbuf_create(input_buf, input_len);
+	if (ibuf == NULL)
+		goto error;
+
+	op = operation_create(profile, ibuf, op_type);
+	if (op == NULL)
+		goto error;
+
+	debug_hexdump(stdout, "Input:", rte_pktmbuf_mtod(ibuf, uint8_t*), ibuf->pkt_len);
+
+	rte_crypto_op_attach_sym_session(op, session);
+
+	struct rte_crypto_op *res = crypto_request_process(dev_id, op);
+	if (res == NULL)
+		goto error;
+
+	obuf = op->sym->m_src;
+	if (obuf == NULL) {
+		RTE_LOG(ERR, USER1, "Invalid packet received\n");
+		goto error;
+	}
+	mbuf_to_buf_copy(obuf, output_buf, output_len);
+
+	ret = 0;
+
+error:
+	if (session != NULL) {
+		int sret;
+		sret = rte_cryptodev_sym_session_free(dev_id, session);
+		assert(sret == 0);
+	}
+	rte_pktmbuf_free(ibuf);
+	rte_crypto_op_free(op);
+	return ret;
+}
+
+static int
+crosscheck_all_devices(const struct crosscheck_test_profile *profile, enum crypto_op_type op_type,
+		const uint8_t *input_text, uint16_t input_len, uint8_t *output_text,
+		uint16_t *output_len)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	uint16_t len = 0, expected_len = 0;
+	uint8_t expected_text[MBUF_SIZE];
+	uint8_t i, dev_id;
+	int status;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		status = single_dev_process(profile, dev_id, op_type, input_text, input_len,
+				output_text, &len);
+		TEST_ASSERT_SUCCESS(status, "Error occurred during processing");
+
+		if (i == 0) {
+			/* First device, copy data for future comparisons */
+			memcpy(expected_text, output_text, len);
+			expected_len = len;
+		} else {
+			/* Compare output against expected(first) output */
+			TEST_ASSERT_EQUAL(len, expected_len, "Length mismatch %d != %d !\n",
+					len, expected_len);
+
+			if (memcmp(output_text, expected_text, len)) {
+				RTE_LOG(ERR, USER1, "Output mismatch between dev 0 and %i\n",
+						dev_id);
+				rte_hexdump(rte_log_get_stream(), "expected", expected_text, len);
+				rte_hexdump(rte_log_get_stream(), "received", output_text, len);
+				return TEST_FAILED;
+			}
+		}
+
+		RTE_LOG(DEBUG, USER1, "DEV ID: %u finished processing\n", dev_id);
+		debug_hexdump(stdout, "Output: ", output_text, len);
+	}
+
+	*output_len = len;
+
+	return TEST_SUCCESS;
+}
+
+static int
+check_negative_all_devices(const struct crosscheck_test_profile *profile,
+		enum crypto_op_type op_type, const uint8_t *input_text, uint16_t input_len)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	uint8_t output_text[MBUF_SIZE];
+	uint8_t i, dev_id;
+	uint16_t len;
+	int status;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		status = single_dev_process(profile, dev_id, op_type, input_text, input_len,
+				output_text, &len);
+		TEST_ASSERT_FAIL(status, "Error occurred during processing negative case");
+
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+crosscheck_with_profile_run(const struct crosscheck_test_profile *profile)
+{
+	uint8_t input_text[profile->input_buf_len];
+	uint16_t output_len, encrypted_len;
+	uint8_t encrypted_text[MBUF_SIZE];
+	uint8_t output_text[MBUF_SIZE];
+	int status;
+
+	/* Encrypt Stage */
+	RTE_LOG(DEBUG, USER1, "Executing encrypt stage\n");
+	/* Fill input with incrementing pattern */
+	incrementing_generate(input_text, 'a', profile->input_buf_len);
+	status = crosscheck_all_devices(profile, OP_ENCRYPT, input_text, profile->input_buf_len,
+			output_text, &output_len);
+	TEST_ASSERT_SUCCESS(status, "Error occurred during encryption");
+
+	/* Decrypt Stage */
+	RTE_LOG(DEBUG, USER1, "Executing decrypt stage\n");
+	/* Set up encrypted data as input */
+	encrypted_len = output_len;
+	memcpy(encrypted_text, output_text, output_len);
+	status = crosscheck_all_devices(profile, OP_DECRYPT, encrypted_text, encrypted_len,
+			output_text, &output_len);
+	TEST_ASSERT_SUCCESS(status, "Error occurred during decryption");
+
+	/* Negative Stage */
+	RTE_LOG(DEBUG, USER1, "Executing negative stage\n");
+	if (profile_digest_get(profile)) {
+		/* Corrupting one byte of digest */
+		encrypted_text[encrypted_len - 1] += 1;
+		status = check_negative_all_devices(profile, OP_DECRYPT, encrypted_text,
+				encrypted_len);
+		TEST_ASSERT_SUCCESS(status, "Error occurred during decryption");
+	}
+
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_crosscheck_unit(const void *ptr)
+{
+	const struct crosscheck_test_profile *profile = ptr;
+
+	if (profile->xform_type == RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED)
+		return TEST_SKIPPED;
+
+	return crosscheck_with_profile_run(profile);
+}
+
+static struct unit_test_suite*
+sym_unit_test_suite_create(int algo, enum rte_crypto_sym_xform_type xform_type)
+{
+	struct rte_cryptodev_symmetric_capability *capabilities;
+	size_t uts_size, total_size, input_sz;
+	struct meta_test_suite *meta_ts;
+	struct unit_test_suite *uts;
+	const char *suite_prefix = NULL;
+	const char *algo_name = NULL;
+	uint64_t test_case_idx = 0;
+	struct unit_test_case *utc;
+	int cap_type;
+	char *mem;
+
+	switch (xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		suite_prefix = "Algo AUTH ";
+		algo_name = rte_crypto_auth_algorithm_strings[algo];
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		suite_prefix = "Algo CIPHER";
+		algo_name = rte_crypto_cipher_algorithm_strings[algo];
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		suite_prefix = "Algo AEAD";
+		algo_name = rte_crypto_aead_algorithm_strings[algo];
+		break;
+	default:
+		return NULL;
+	}
+
+	/* Calculate size for test suite with all test cases +1 NULL case */
+	uts_size = sizeof(struct unit_test_suite) +
+		(NB_TEST_CASES_PER_ALGO + 1) * sizeof(struct unit_test_case);
+
+	/* Also allocate memory for suite meta data */
+	total_size = uts_size + sizeof(struct meta_test_suite);
+	mem = rte_zmalloc(NULL, total_size, 0);
+	if (mem == NULL)
+		return NULL;
+	uts = (struct unit_test_suite *) mem;
+	meta_ts = (struct meta_test_suite *) (mem + uts_size);
+
+	/* Initialize test suite */
+	snprintf(meta_ts->suite_name, MAX_TEST_STRING_LEN, "%s '%s'", suite_prefix, algo_name);
+	uts->suite_name = meta_ts->suite_name;
+
+	capabilities = common_capability_get(algo, xform_type);
+	if (capabilities->xform_type == 0) /* Skip case if current algo is not supported */
+		return uts;
+
+	/* Initialize test cases */
+
+	for (cap_type = 0; cap_type < CAPABILITY_TYPE_LAST; cap_type++) {
+		for (input_sz = 0; input_sz < RTE_DIM(input_length); input_sz++) {
+			meta_ts->profile[test_case_idx] = profile_create(
+					capabilities, cap_type, input_length[input_sz]);
+			utc = &uts->unit_test_cases[test_case_idx];
+			utc->name = meta_ts->profile[test_case_idx].name;
+			utc->data = (const void *) &meta_ts->profile[test_case_idx];
+			utc->testcase_with_data = test_crosscheck_unit;
+			utc->enabled = true;
+
+			test_case_idx += 1;
+			RTE_VERIFY(test_case_idx <= NB_TEST_CASES_PER_ALGO);
+		}
+	}
+
+	return uts;
+}
+
+static void
+sym_unit_test_suite_free(struct unit_test_suite *suite)
+{
+	struct crosscheck_test_profile profile;
+	struct unit_test_case *utc;
+	int i;
+
+	for (i = 0; suite->unit_test_cases[i].testcase_with_data; i++) {
+		utc = &suite->unit_test_cases[i];
+		profile = *(const struct crosscheck_test_profile *)utc->data;
+		rte_free(profile.key);
+		rte_free(profile.iv);
+	}
+
+	rte_free(suite);
+}
+
+static int
+test_crosscheck(void)
+{
+	int ret, i, ts_idx = 0;
+	static struct unit_test_suite ts = {
+		.suite_name = "Crosscheck Unit Test Suite",
+		.setup = crosscheck_suite_setup,
+		.teardown = crosscheck_suite_teardown,
+		.unit_test_cases = {TEST_CASES_END()}
+	};
+	struct unit_test_suite *test_suites[CRYPTO_ALGOS_LEN+1] = {0};/* 1 for NULL-end suite */
+
+	/* Allocate all required memory pools and crosscheck cryptodev capabilities */
+	ret = crosscheck_init();
+	if (ret)
+		return ret;
+
+	/* Create test suite for each known algorithm */
+	ts.unit_test_suites = test_suites;
+	for (i = 1; i <= CRYPTO_AUTH_MAX_IDX; i++) {
+		ts.unit_test_suites[ts_idx++] = sym_unit_test_suite_create(i,
+				RTE_CRYPTO_SYM_XFORM_AUTH);
+	}
+	for (i = 1; i <= CRYPTO_CIPHER_MAX_IDX; i++) {
+		ts.unit_test_suites[ts_idx++] = sym_unit_test_suite_create(i,
+				RTE_CRYPTO_SYM_XFORM_CIPHER);
+	}
+	for (i = 1; i <= CRYPTO_AEAD_MAX_IDX; i++) {
+		/* AES_CCM requires special handling due to api requirements, skip now */
+		if (i == RTE_CRYPTO_AEAD_AES_CCM)
+			continue;
+		ts.unit_test_suites[ts_idx++] = sym_unit_test_suite_create(i,
+				RTE_CRYPTO_SYM_XFORM_AEAD);
+	}
+
+	ret = unit_test_suite_runner(&ts);
+
+	for (i = 0; i < ts_idx; i++)
+		sym_unit_test_suite_free(ts.unit_test_suites[i]);
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(cryptodev_crosscheck, test_crosscheck);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v2] test: add cryptodev crosscheck suite
  2022-12-21 11:08 [PATCH] test: add cryptodev crosscheck suite Volodymyr Fialko
@ 2023-02-09 12:34 ` Volodymyr Fialko
  2023-02-09 14:34   ` [PATCH v3] " Volodymyr Fialko
                     ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Volodymyr Fialko @ 2023-02-09 12:34 UTC (permalink / raw)
  To: dev, Akhil Goyal, Fan Zhang
  Cc: jerinj, anoobj, hemant.agrawal, kai.ji, ciara.power, Volodymyr Fialko

Add a validation test suite that helps in verifying that the output
generated by two different cryptodevs match for a wide range of input
parameter combinations.

Crypto autotest performs a comprehensive testing of the cryptodev but since
it performs verification by comparing against known vectors, the extend to
which various parameters (like packet size) can be tested is limited. This
test suite attempts to simulate various cases by running same test case on
different cryptodevs and compares the output generated. The test suite
relies on capabilities to determine the combinations of tests to be
attempted.

A typical use case would be to compare outputs generated from a standard
driver such as openSSL PMD and a new cryptodev PMD. This test suite is to
compliment the testing coverage that crypto autotest provides.

Currently supported symmetric xforms(cipher, auth, aead) without chaining.

Example command:
DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
	-a <cryptodev> --vdev "crypto_openssl"

Signed-off-by: Volodymyr Fialko <vfialko@marvell.com>
---
V2:
- Updated commit message.

 app/test/meson.build                 |    1 +
 app/test/test_cryptodev_crosscheck.c | 1008 ++++++++++++++++++++++++++
 2 files changed, 1009 insertions(+)
 create mode 100644 app/test/test_cryptodev_crosscheck.c

diff --git a/app/test/meson.build b/app/test/meson.build
index 1b9ce355f8..f0bcf99fb1 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -33,6 +33,7 @@ test_sources = files(
         'test_cryptodev.c',
         'test_cryptodev_asym.c',
         'test_cryptodev_blockcipher.c',
+        'test_cryptodev_crosscheck.c',
         'test_cryptodev_security_ipsec.c',
         'test_cryptodev_security_pdcp.c',
         'test_cycles.c',
diff --git a/app/test/test_cryptodev_crosscheck.c b/app/test/test_cryptodev_crosscheck.c
new file mode 100644
index 0000000000..e5f841409c
--- /dev/null
+++ b/app/test/test_cryptodev_crosscheck.c
@@ -0,0 +1,1008 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2022 Marvell.
+ */
+#include <rte_cryptodev.h>
+#include <rte_malloc.h>
+
+#include "test.h"
+#include "test_cryptodev.h"
+
+#define MAX_NB_SESSIONS 1
+#define MAX_TEST_STRING_LEN 256
+
+/* Define list end for known algorithms */
+#define CRYPTO_AUTH_MAX_IDX (RTE_CRYPTO_AUTH_SHA3_512_HMAC)
+#define CRYPTO_CIPHER_MAX_IDX (RTE_CRYPTO_CIPHER_DES_DOCSISBPI)
+#define CRYPTO_AEAD_MAX_IDX (RTE_CRYPTO_AEAD_CHACHA20_POLY1305)
+#define CRYPTO_ALGOS_LEN (CRYPTO_AUTH_MAX_IDX + CRYPTO_CIPHER_MAX_IDX + CRYPTO_AEAD_MAX_IDX)
+
+static struct rte_cryptodev_symmetric_capability common_symm_capas[CRYPTO_ALGOS_LEN];
+
+static struct rte_cryptodev_symmetric_capability*
+common_capability_get(int algo, enum rte_crypto_sym_xform_type xform_type)
+{
+	const int type_to_offset[] = {
+		[RTE_CRYPTO_SYM_XFORM_AUTH] = 0,
+		[RTE_CRYPTO_SYM_XFORM_CIPHER] = CRYPTO_AUTH_MAX_IDX,
+		[RTE_CRYPTO_SYM_XFORM_AEAD] = CRYPTO_AUTH_MAX_IDX + CRYPTO_CIPHER_MAX_IDX,
+	};
+
+	return &common_symm_capas[type_to_offset[xform_type] + algo - 1];
+}
+
+enum capability_select_type {
+	CAPABILITY_TYPE_MIN,
+	CAPABILITY_TYPE_MAX,
+	CAPABILITY_TYPE_LAST,
+};
+
+static const char * const capability_select_strings[] = {
+	[CAPABILITY_TYPE_MIN] = "MIN",
+	[CAPABILITY_TYPE_MAX] = "MAX",
+};
+
+static size_t input_length[] = { 64, 256, 512 };
+
+static const char*
+algo_name_get(const struct rte_cryptodev_symmetric_capability *capa)
+{
+	switch (capa->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		return rte_crypto_auth_algorithm_strings[capa->auth.algo];
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		return rte_crypto_cipher_algorithm_strings[capa->cipher.algo];
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		return rte_crypto_aead_algorithm_strings[capa->aead.algo];
+	default:
+		return NULL;
+	}
+}
+
+/* Calculate number of test cases(combinations) per algorithm */
+#define NB_TEST_CASES_PER_ALGO (CAPABILITY_TYPE_LAST * RTE_DIM(input_length))
+
+enum crypto_op_type {
+	OP_ENCRYPT,
+	OP_DECRYPT,
+};
+
+struct crosscheck_test_profile {
+	char name[MAX_TEST_STRING_LEN];
+	size_t input_buf_len;
+	uint16_t block_size;
+	uint16_t key_size;
+	uint8_t *key;
+	uint16_t iv_size;
+	uint8_t *iv;
+
+	enum rte_crypto_sym_xform_type xform_type;
+	union {
+		struct {
+			enum rte_crypto_auth_algorithm algo;
+			uint16_t digest_size;
+		} auth;
+		struct {
+			enum rte_crypto_cipher_algorithm algo;
+			uint32_t dataunit_set;
+		} cipher;
+		struct {
+			enum rte_crypto_aead_algorithm algo;
+			uint16_t digest_size;
+			uint16_t aad_size;
+		} aead;
+	};
+};
+
+struct meta_test_suite {
+	char suite_name[MAX_TEST_STRING_LEN];
+	struct crosscheck_test_profile profile[NB_TEST_CASES_PER_ALGO];
+};
+
+static struct crypto_testsuite_params testsuite_params;
+
+static void
+incrementing_generate(uint8_t *dst, uint8_t start, uint16_t size)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		dst[i] = start + i;
+}
+
+static void
+pattern_fill(uint8_t *input, const char *pattern, uint16_t size)
+{
+	size_t pattern_len = strlen(pattern);
+	size_t filled_len = 0, to_fill;
+
+	while (filled_len < size) {
+		to_fill = RTE_MIN(pattern_len, size - filled_len);
+		rte_memcpy(input, pattern, to_fill);
+		filled_len += to_fill;
+		input += to_fill;
+	}
+}
+
+static struct crosscheck_test_profile
+profile_create(struct rte_cryptodev_symmetric_capability *capa,
+		enum capability_select_type capability_type, size_t input_len)
+{
+	struct crosscheck_test_profile profile;
+
+	memset(&profile, 0, sizeof(profile));
+	profile.xform_type = capa->xform_type;
+
+	switch (capa->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		profile.block_size = capa->auth.block_size;
+		profile.auth.algo = capa->auth.algo;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->auth.key_size.min;
+			profile.iv_size = capa->auth.iv_size.min;
+			profile.auth.digest_size = capa->auth.digest_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->auth.key_size.max;
+			profile.iv_size = capa->auth.iv_size.max;
+			profile.auth.digest_size = capa->auth.digest_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		profile.block_size = capa->cipher.block_size;
+		profile.cipher.algo = capa->cipher.algo;
+		profile.cipher.dataunit_set = capa->cipher.dataunit_set;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->cipher.key_size.min;
+			profile.iv_size = capa->cipher.iv_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->cipher.key_size.max;
+			profile.iv_size = capa->cipher.iv_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		profile.block_size = capa->aead.block_size;
+		profile.aead.algo = capa->aead.algo;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->aead.key_size.min;
+			profile.iv_size = capa->aead.iv_size.min;
+			profile.aead.digest_size = capa->aead.digest_size.min;
+			profile.aead.aad_size = capa->aead.aad_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->aead.key_size.max;
+			profile.iv_size = capa->aead.iv_size.max;
+			profile.aead.digest_size = capa->aead.digest_size.max;
+			profile.aead.aad_size = capa->aead.aad_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	default:
+		rte_panic("Wrong xform profile type: %i\n", capa->xform_type);
+		break;
+	}
+
+	profile.input_buf_len = RTE_ALIGN_CEIL(input_len, profile.block_size);
+
+	if (profile.key_size) {
+		profile.key = rte_zmalloc(NULL, profile.key_size, 0);
+		RTE_VERIFY(profile.key != NULL);
+		pattern_fill(profile.key, "*Secret key*", profile.key_size);
+	}
+
+	if (profile.iv_size) {
+		profile.iv = rte_zmalloc(NULL, profile.iv_size, 0);
+		RTE_VERIFY(profile.iv != NULL);
+		pattern_fill(profile.iv, "IV ", profile.iv_size);
+	}
+
+	snprintf(profile.name, MAX_TEST_STRING_LEN,
+			"'%s' - capabilities: '%s', input len: '%zu'",
+			algo_name_get(capa), capability_select_strings[capability_type],
+			input_len);
+
+	return profile;
+}
+
+static inline int
+common_range_set(struct rte_crypto_param_range *dst, const struct rte_crypto_param_range *src)
+{
+	/* Check if ranges overlaps */
+	if ((dst->min > src->max) && (dst->max < src->min))
+		return -1;
+	dst->min = RTE_MAX(dst->min, src->min);
+	dst->max = RTE_MIN(dst->max, src->max);
+
+	return 0;
+}
+
+static void
+capabilities_inspect(void)
+{
+	struct rte_cryptodev_sym_capability_idx cap_indexes[CRYPTO_ALGOS_LEN], *cap_idx;
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	const struct rte_cryptodev_symmetric_capability *sym_capa;
+	struct rte_cryptodev_symmetric_capability *common_capa;
+	uint32_t algo, i, dev_id, caps_idx;
+
+	caps_idx = 0;
+	/* Create capability idx for known algorithms*/
+	for (algo = 1; algo <= CRYPTO_AUTH_MAX_IDX; algo++) {
+		cap_idx = &cap_indexes[caps_idx++];
+		cap_idx->type = RTE_CRYPTO_SYM_XFORM_AUTH;
+		cap_idx->algo.auth = algo;
+	}
+	for (algo = 1; algo <= CRYPTO_CIPHER_MAX_IDX; algo++) {
+		cap_idx = &cap_indexes[caps_idx++];
+		cap_idx->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+		cap_idx->algo.cipher = algo;
+	}
+	for (algo = 1; algo <= CRYPTO_AEAD_MAX_IDX; algo++) {
+		cap_idx = &cap_indexes[caps_idx++];
+		cap_idx->type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		cap_idx->algo.aead = algo;
+	}
+
+	for (caps_idx = 0; caps_idx < CRYPTO_ALGOS_LEN; caps_idx++) {
+		/* Gather common capabilities */
+		common_capa = &common_symm_capas[caps_idx];
+		common_capa->xform_type = RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED;
+		for (i = 0; i < ts_params->valid_dev_count; i++) {
+			dev_id = ts_params->valid_devs[i];
+			sym_capa = rte_cryptodev_sym_capability_get(dev_id,
+					&cap_indexes[caps_idx]);
+			if (sym_capa == NULL) {
+				/* Capability not supported by one of devs, mark and skip */
+				goto next_algo;
+			}
+
+			if (common_capa->xform_type == RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED) {
+				/* First time initialization, copy data, go to next device  */
+				*common_capa = *sym_capa;
+				continue;
+			}
+
+			switch (sym_capa->xform_type) {
+			case RTE_CRYPTO_SYM_XFORM_AUTH:
+				if (common_capa->auth.algo != sym_capa->auth.algo)
+					goto next_algo;
+
+				if (common_capa->auth.block_size != sym_capa->auth.block_size)
+					goto next_algo;
+
+				if (common_range_set(&common_capa->auth.key_size,
+							&sym_capa->auth.key_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->auth.digest_size,
+							&sym_capa->auth.digest_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->auth.aad_size,
+							&sym_capa->auth.aad_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->auth.iv_size,
+							&sym_capa->auth.iv_size))
+					goto next_algo;
+				break;
+			case RTE_CRYPTO_SYM_XFORM_CIPHER:
+				if (common_capa->cipher.algo != sym_capa->cipher.algo)
+					goto next_algo;
+
+				if (common_capa->cipher.block_size != sym_capa->cipher.block_size)
+					goto next_algo;
+
+				if (common_range_set(&common_capa->cipher.key_size,
+							&sym_capa->cipher.key_size))
+					goto next_algo;
+
+				if (common_range_set(&common_capa->cipher.iv_size,
+							&sym_capa->cipher.iv_size))
+					goto next_algo;
+				if (common_capa->cipher.dataunit_set !=
+						sym_capa->cipher.dataunit_set)
+					goto next_algo;
+				break;
+			case RTE_CRYPTO_SYM_XFORM_AEAD:
+				if (common_capa->aead.algo != sym_capa->aead.algo)
+					goto next_algo;
+
+				if (common_capa->aead.block_size != sym_capa->aead.block_size)
+					goto next_algo;
+
+				if (common_range_set(&common_capa->aead.key_size,
+							&sym_capa->aead.key_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->aead.digest_size,
+							&sym_capa->aead.digest_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->aead.aad_size,
+							&sym_capa->aead.aad_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->aead.iv_size,
+							&sym_capa->aead.iv_size))
+					goto next_algo;
+				break;
+			default:
+				RTE_LOG(ERR, USER1, "Unsupported xform_type!\n");
+				goto next_algo;
+			}
+		}
+
+		continue;
+next_algo:
+		common_capa->xform_type = RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED;
+	}
+}
+
+static int
+crosscheck_init(void)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	const uint32_t nb_queue_pairs = 1;
+	struct rte_cryptodev_info info;
+	uint32_t session_priv_size = 0;
+	uint32_t nb_devs, dev_id;
+
+	memset(ts_params, 0, sizeof(*ts_params));
+
+	/* Create list of valid crypto devs */
+	nb_devs = rte_cryptodev_count();
+	for (dev_id = 0; dev_id < nb_devs; dev_id++) {
+		rte_cryptodev_info_get(dev_id, &info);
+
+		if (info.sym.max_nb_sessions != 0 && info.sym.max_nb_sessions < MAX_NB_SESSIONS)
+			continue;
+		if (info.max_nb_queue_pairs < nb_queue_pairs)
+			continue;
+		ts_params->valid_devs[ts_params->valid_dev_count++] = dev_id;
+		/* Obtaining configuration parameters, that will satisfy all cryptodevs */
+		session_priv_size = RTE_MAX(session_priv_size,
+				rte_cryptodev_sym_get_private_session_size(dev_id));
+	}
+
+	if (ts_params->valid_dev_count < 2) {
+		RTE_LOG(WARNING, USER1, "Min number of cryptodevs for test is 2, found (%d)\n",
+				ts_params->valid_dev_count);
+		return TEST_SKIPPED;
+	}
+
+	/* Create pools for mbufs, crypto operations and sessions */
+	ts_params->mbuf_pool = rte_pktmbuf_pool_create("CRYPTO_MBUFPOOL", NUM_MBUFS,
+			MBUF_CACHE_SIZE, 0, MBUF_SIZE, rte_socket_id());
+	if (ts_params->mbuf_pool == NULL) {
+		RTE_LOG(ERR, USER1, "Can't create CRYPTO_MBUFPOOL\n");
+		return TEST_FAILED;
+	}
+
+	ts_params->op_mpool = rte_crypto_op_pool_create("MBUF_CRYPTO_SYM_OP_POOL",
+			RTE_CRYPTO_OP_TYPE_SYMMETRIC, NUM_MBUFS, MBUF_CACHE_SIZE,
+			DEFAULT_NUM_XFORMS * sizeof(struct rte_crypto_sym_xform) +
+			MAXIMUM_IV_LENGTH, rte_socket_id());
+
+	if (ts_params->op_mpool == NULL) {
+		RTE_LOG(ERR, USER1, "Can't create CRYPTO_OP_POOL\n");
+		return TEST_FAILED;
+	}
+
+	ts_params->session_mpool = rte_cryptodev_sym_session_pool_create("test_sess_mp",
+			MAX_NB_SESSIONS, session_priv_size, 0, 0, SOCKET_ID_ANY);
+	TEST_ASSERT_NOT_NULL(ts_params->session_mpool, "session mempool allocation failed");
+
+	/* Setup queue pair conf params */
+	ts_params->conf.nb_queue_pairs = nb_queue_pairs;
+	ts_params->conf.socket_id = SOCKET_ID_ANY;
+	ts_params->conf.ff_disable = RTE_CRYPTODEV_FF_SECURITY;
+	ts_params->qp_conf.nb_descriptors = MAX_NUM_OPS_INFLIGHT;
+	ts_params->qp_conf.mp_session = ts_params->session_mpool;
+
+	capabilities_inspect();
+
+	return TEST_SUCCESS;
+}
+
+static int
+dev_configure_and_start(uint64_t ff_disable)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	uint8_t i, dev_id;
+	uint16_t qp_id;
+
+	/* Reconfigure device to default parameters */
+	ts_params->conf.ff_disable = ff_disable;
+
+	/* Configure cryptodevs */
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		TEST_ASSERT_SUCCESS(rte_cryptodev_configure(dev_id, &ts_params->conf),
+				"Failed to configure cryptodev %u with %u qps",
+				dev_id, ts_params->conf.nb_queue_pairs);
+
+
+		for (qp_id = 0; qp_id < ts_params->conf.nb_queue_pairs; qp_id++) {
+			TEST_ASSERT_SUCCESS(rte_cryptodev_queue_pair_setup(
+				dev_id, qp_id, &ts_params->qp_conf,
+				rte_cryptodev_socket_id(dev_id)),
+				"Failed to setup queue pair %u on cryptodev %u",
+				qp_id, dev_id);
+		}
+		rte_cryptodev_stats_reset(dev_id);
+
+		/* Start the device */
+		TEST_ASSERT_SUCCESS(rte_cryptodev_start(dev_id), "Failed to start cryptodev %u",
+				dev_id);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+crosscheck_suite_setup(void)
+{
+	dev_configure_and_start(RTE_CRYPTODEV_FF_SECURITY);
+
+	return 0;
+}
+
+static void
+crosscheck_suite_teardown(void)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	uint8_t i, dev_id;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		rte_cryptodev_stop(dev_id);
+	}
+}
+
+static struct rte_crypto_op *
+crypto_request_process(uint8_t dev_id, struct rte_crypto_op *op)
+{
+	struct rte_crypto_op *res = NULL;
+
+	if (rte_cryptodev_enqueue_burst(dev_id, 0, &op, 1) != 1) {
+		RTE_LOG(ERR, USER1, "Error sending packet for encryption\n");
+		return NULL;
+	}
+
+	while (rte_cryptodev_dequeue_burst(dev_id, 0, &res, 1) == 0)
+		rte_pause();
+
+	if (res->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
+		RTE_LOG(ERR, USER1, "Operation status %d\n", res->status);
+		return NULL;
+	}
+
+	if (res != op) {
+		RTE_LOG(ERR, USER1, "Unexpected operation received!\n");
+		rte_crypto_op_free(res);
+		return NULL;
+	}
+
+	return res;
+}
+
+/* Create free room at the start of segment, by moving existing data further */
+static uint8_t*
+mbuf_room_create(struct rte_mbuf *m, size_t size)
+{
+	uint8_t *data;
+
+	data = rte_pktmbuf_mtod(m, uint8_t*);
+	memmove(data + size, data, m->data_len);
+	m->data_len += size;
+	m->pkt_len += size;
+
+	return data;
+}
+
+static struct rte_cryptodev_sym_session*
+session_create(const struct crosscheck_test_profile *profile, uint8_t dev_id,
+		enum crypto_op_type op_type)
+{
+	struct rte_cryptodev_sym_session *session;
+	struct rte_crypto_sym_xform xform;
+
+	memset(&xform, 0, sizeof(xform));
+
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		xform.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+		xform.next = NULL;
+		xform.auth.algo = profile->auth.algo;
+		xform.auth.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_AUTH_OP_GENERATE :
+			RTE_CRYPTO_AUTH_OP_VERIFY;
+		xform.auth.digest_length = profile->auth.digest_size;
+		xform.auth.key.length = profile->key_size;
+		xform.auth.key.data = profile->key;
+		xform.auth.iv.length = profile->iv_size;
+		xform.auth.iv.offset = IV_OFFSET;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		xform.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+		xform.next = NULL;
+		xform.cipher.algo = profile->cipher.algo;
+		xform.cipher.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_CIPHER_OP_ENCRYPT :
+			RTE_CRYPTO_CIPHER_OP_DECRYPT;
+		xform.cipher.key.length = profile->key_size;
+		xform.cipher.key.data = profile->key;
+		xform.cipher.iv.length = profile->iv_size;
+		xform.cipher.iv.offset = IV_OFFSET;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		xform.type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		xform.next = NULL;
+		xform.aead.algo = profile->aead.algo;
+		xform.aead.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_AEAD_OP_ENCRYPT :
+			RTE_CRYPTO_AEAD_OP_DECRYPT;
+		xform.aead.digest_length = profile->aead.digest_size;
+		xform.aead.key.length = profile->key_size;
+		xform.aead.key.data = profile->key;
+		xform.aead.iv.length = profile->iv_size;
+		xform.aead.iv.offset = IV_OFFSET;
+		xform.aead.aad_length = profile->aead.aad_size;
+		break;
+	default:
+		return NULL;
+	}
+
+	session = rte_cryptodev_sym_session_create(dev_id, &xform, testsuite_params.session_mpool);
+
+	return session;
+}
+
+static struct rte_mbuf*
+mbuf_create(const uint8_t *input_buf, uint16_t input_len)
+{
+	struct rte_mbuf *pkt;
+	uint8_t *pkt_data;
+
+	pkt = rte_pktmbuf_alloc(testsuite_params.mbuf_pool);
+	if (pkt == NULL) {
+		RTE_LOG(ERR, USER1,  "Failed to allocate input buffer in mempool");
+		return NULL;
+	}
+
+	/* zeroing tailroom */
+	memset(rte_pktmbuf_mtod(pkt, uint8_t *), 0, rte_pktmbuf_tailroom(pkt));
+
+	pkt_data = (uint8_t *)rte_pktmbuf_append(pkt, input_len);
+	if (pkt_data == NULL) {
+		RTE_LOG(ERR, USER1, "no room to append data, len: %d", input_len);
+		goto error;
+	}
+	rte_memcpy(pkt_data, input_buf, input_len);
+
+	return pkt;
+error:
+	rte_pktmbuf_free(pkt);
+	return NULL;
+}
+
+static uint16_t
+profile_digest_get(const struct crosscheck_test_profile *profile)
+{
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		return profile->auth.digest_size;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		return profile->aead.digest_size;
+	default:
+		return 0;
+	}
+}
+
+static struct rte_crypto_op*
+operation_create(const struct crosscheck_test_profile *profile,
+		struct rte_mbuf *ibuf, enum crypto_op_type op_type)
+{
+	uint8_t *digest_data = NULL, *aad_data = NULL, *iv_ptr = NULL;
+	uint16_t aad_size = 0, digest_size = 0, digest_offset = 0;
+	uint16_t plaintext_len = profile->input_buf_len;
+	rte_iova_t digest_addr = 0, aad_addr = 0;
+	struct rte_crypto_sym_op *sym_op;
+	struct rte_crypto_op *op;
+
+	op = rte_crypto_op_alloc(testsuite_params.op_mpool, RTE_CRYPTO_OP_TYPE_SYMMETRIC);
+	if (op == NULL) {
+		RTE_LOG(ERR, USER1, "Failed to allocate symmetric crypto operation struct");
+		return NULL;
+	}
+
+	/* Obtain aad and digest sizes */
+	digest_size = profile_digest_get(profile);
+	if (profile->xform_type == RTE_CRYPTO_SYM_XFORM_AEAD)
+		aad_size = profile->aead.aad_size;
+
+	if (aad_size) {
+		if (op_type == OP_ENCRYPT) {
+			if (rte_pktmbuf_tailroom(ibuf) > plaintext_len + aad_size) {
+				/* Put aad to data segment */
+				aad_data = mbuf_room_create(ibuf, aad_size);
+			} else {
+				RTE_LOG(ERR, USER1, "No space for aad in single mbuf\n");
+				goto error;
+			}
+			pattern_fill(aad_data, "This is an aad.", aad_size);
+		} else {
+			aad_data = rte_pktmbuf_mtod(ibuf, uint8_t *);
+		}
+		aad_addr = rte_pktmbuf_iova(ibuf);
+	}
+
+	if (digest_size) {
+		digest_offset = aad_size + plaintext_len;
+		if (op_type == OP_ENCRYPT) {
+			digest_data = (uint8_t *)rte_pktmbuf_append(ibuf, digest_size);
+			if (digest_data == NULL) {
+				RTE_LOG(ERR, USER1, "No room to append digest\n");
+				goto error;
+			}
+		} else {
+			digest_data = rte_pktmbuf_mtod_offset(ibuf, uint8_t *, digest_offset);
+		}
+		digest_addr = rte_pktmbuf_iova_offset(ibuf, digest_offset);
+	}
+
+	sym_op = op->sym;
+	memset(sym_op, 0, sizeof(*sym_op));
+
+	iv_ptr = rte_crypto_op_ctod_offset(op, uint8_t *, IV_OFFSET);
+	rte_memcpy(iv_ptr, profile->iv, profile->iv_size);
+
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		sym_op->auth.digest.phys_addr = digest_addr;
+		sym_op->auth.digest.data = digest_data;
+		sym_op->auth.data.length = plaintext_len;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		sym_op->cipher.data.length = plaintext_len;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		sym_op->aead.aad.phys_addr = aad_addr;
+		sym_op->aead.aad.data = aad_data;
+		sym_op->aead.digest.phys_addr = digest_addr;
+		sym_op->aead.digest.data = digest_data;
+		sym_op->aead.data.offset = aad_size;
+		sym_op->aead.data.length = plaintext_len;
+		break;
+	default:
+		goto error;
+	}
+
+	sym_op->m_src = ibuf;
+
+	return op;
+
+error:
+	rte_crypto_op_free(op);
+	return NULL;
+}
+
+static void
+mbuf_to_buf_copy(const struct rte_mbuf *m, uint8_t *res_buf, uint16_t *len)
+{
+	const uint8_t *out;
+
+	*len = m->pkt_len;
+	out = rte_pktmbuf_read(m, 0, *len, res_buf);
+	/* Single segment buffer */
+	if (out != res_buf)
+		memcpy(res_buf, out, *len);
+}
+
+static int
+single_dev_process(const struct crosscheck_test_profile *profile, uint16_t dev_id, enum
+		crypto_op_type op_type, const uint8_t *input_buf, uint16_t input_len,
+		uint8_t *output_buf, uint16_t *output_len)
+{
+	struct rte_cryptodev_sym_session *session = NULL;
+	struct rte_mbuf *ibuf = NULL, *obuf = NULL;
+	struct rte_crypto_op *op = NULL;
+	int ret = -1;
+
+	session = session_create(profile, dev_id, op_type);
+	if (session == NULL)
+		goto error;
+
+	ibuf = mbuf_create(input_buf, input_len);
+	if (ibuf == NULL)
+		goto error;
+
+	op = operation_create(profile, ibuf, op_type);
+	if (op == NULL)
+		goto error;
+
+	debug_hexdump(stdout, "Input:", rte_pktmbuf_mtod(ibuf, uint8_t*), ibuf->pkt_len);
+
+	rte_crypto_op_attach_sym_session(op, session);
+
+	struct rte_crypto_op *res = crypto_request_process(dev_id, op);
+	if (res == NULL)
+		goto error;
+
+	obuf = op->sym->m_src;
+	if (obuf == NULL) {
+		RTE_LOG(ERR, USER1, "Invalid packet received\n");
+		goto error;
+	}
+	mbuf_to_buf_copy(obuf, output_buf, output_len);
+
+	ret = 0;
+
+error:
+	if (session != NULL) {
+		int sret;
+		sret = rte_cryptodev_sym_session_free(dev_id, session);
+		assert(sret == 0);
+	}
+	rte_pktmbuf_free(ibuf);
+	rte_crypto_op_free(op);
+	return ret;
+}
+
+static int
+crosscheck_all_devices(const struct crosscheck_test_profile *profile, enum crypto_op_type op_type,
+		const uint8_t *input_text, uint16_t input_len, uint8_t *output_text,
+		uint16_t *output_len)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	uint16_t len = 0, expected_len = 0;
+	uint8_t expected_text[MBUF_SIZE];
+	uint8_t i, dev_id;
+	int status;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		status = single_dev_process(profile, dev_id, op_type, input_text, input_len,
+				output_text, &len);
+		TEST_ASSERT_SUCCESS(status, "Error occurred during processing");
+
+		if (i == 0) {
+			/* First device, copy data for future comparisons */
+			memcpy(expected_text, output_text, len);
+			expected_len = len;
+		} else {
+			/* Compare output against expected(first) output */
+			TEST_ASSERT_EQUAL(len, expected_len, "Length mismatch %d != %d !\n",
+					len, expected_len);
+
+			if (memcmp(output_text, expected_text, len)) {
+				RTE_LOG(ERR, USER1, "Output mismatch between dev 0 and %i\n",
+						dev_id);
+				rte_hexdump(rte_log_get_stream(), "expected", expected_text, len);
+				rte_hexdump(rte_log_get_stream(), "received", output_text, len);
+				return TEST_FAILED;
+			}
+		}
+
+		RTE_LOG(DEBUG, USER1, "DEV ID: %u finished processing\n", dev_id);
+		debug_hexdump(stdout, "Output: ", output_text, len);
+	}
+
+	*output_len = len;
+
+	return TEST_SUCCESS;
+}
+
+static int
+check_negative_all_devices(const struct crosscheck_test_profile *profile,
+		enum crypto_op_type op_type, const uint8_t *input_text, uint16_t input_len)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	uint8_t output_text[MBUF_SIZE];
+	uint8_t i, dev_id;
+	uint16_t len;
+	int status;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		status = single_dev_process(profile, dev_id, op_type, input_text, input_len,
+				output_text, &len);
+		TEST_ASSERT_FAIL(status, "Error occurred during processing negative case");
+
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+crosscheck_with_profile_run(const struct crosscheck_test_profile *profile)
+{
+	uint8_t input_text[profile->input_buf_len];
+	uint16_t output_len, encrypted_len;
+	uint8_t encrypted_text[MBUF_SIZE];
+	uint8_t output_text[MBUF_SIZE];
+	int status;
+
+	/* Encrypt Stage */
+	RTE_LOG(DEBUG, USER1, "Executing encrypt stage\n");
+	/* Fill input with incrementing pattern */
+	incrementing_generate(input_text, 'a', profile->input_buf_len);
+	status = crosscheck_all_devices(profile, OP_ENCRYPT, input_text, profile->input_buf_len,
+			output_text, &output_len);
+	TEST_ASSERT_SUCCESS(status, "Error occurred during encryption");
+
+	/* Decrypt Stage */
+	RTE_LOG(DEBUG, USER1, "Executing decrypt stage\n");
+	/* Set up encrypted data as input */
+	encrypted_len = output_len;
+	memcpy(encrypted_text, output_text, output_len);
+	status = crosscheck_all_devices(profile, OP_DECRYPT, encrypted_text, encrypted_len,
+			output_text, &output_len);
+	TEST_ASSERT_SUCCESS(status, "Error occurred during decryption");
+
+	/* Negative Stage */
+	RTE_LOG(DEBUG, USER1, "Executing negative stage\n");
+	if (profile_digest_get(profile)) {
+		/* Corrupting one byte of digest */
+		encrypted_text[encrypted_len - 1] += 1;
+		status = check_negative_all_devices(profile, OP_DECRYPT, encrypted_text,
+				encrypted_len);
+		TEST_ASSERT_SUCCESS(status, "Error occurred during decryption");
+	}
+
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_crosscheck_unit(const void *ptr)
+{
+	const struct crosscheck_test_profile *profile = ptr;
+
+	if (profile->xform_type == RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED)
+		return TEST_SKIPPED;
+
+	return crosscheck_with_profile_run(profile);
+}
+
+static struct unit_test_suite*
+sym_unit_test_suite_create(int algo, enum rte_crypto_sym_xform_type xform_type)
+{
+	struct rte_cryptodev_symmetric_capability *capabilities;
+	size_t uts_size, total_size, input_sz;
+	struct meta_test_suite *meta_ts;
+	struct unit_test_suite *uts;
+	const char *suite_prefix = NULL;
+	const char *algo_name = NULL;
+	uint64_t test_case_idx = 0;
+	struct unit_test_case *utc;
+	int cap_type;
+	char *mem;
+
+	switch (xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		suite_prefix = "Algo AUTH ";
+		algo_name = rte_crypto_auth_algorithm_strings[algo];
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		suite_prefix = "Algo CIPHER";
+		algo_name = rte_crypto_cipher_algorithm_strings[algo];
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		suite_prefix = "Algo AEAD";
+		algo_name = rte_crypto_aead_algorithm_strings[algo];
+		break;
+	default:
+		return NULL;
+	}
+
+	/* Calculate size for test suite with all test cases +1 NULL case */
+	uts_size = sizeof(struct unit_test_suite) +
+		(NB_TEST_CASES_PER_ALGO + 1) * sizeof(struct unit_test_case);
+
+	/* Also allocate memory for suite meta data */
+	total_size = uts_size + sizeof(struct meta_test_suite);
+	mem = rte_zmalloc(NULL, total_size, 0);
+	if (mem == NULL)
+		return NULL;
+	uts = (struct unit_test_suite *) mem;
+	meta_ts = (struct meta_test_suite *) (mem + uts_size);
+
+	/* Initialize test suite */
+	snprintf(meta_ts->suite_name, MAX_TEST_STRING_LEN, "%s '%s'", suite_prefix, algo_name);
+	uts->suite_name = meta_ts->suite_name;
+
+	capabilities = common_capability_get(algo, xform_type);
+	if (capabilities->xform_type == 0) /* Skip case if current algo is not supported */
+		return uts;
+
+	/* Initialize test cases */
+
+	for (cap_type = 0; cap_type < CAPABILITY_TYPE_LAST; cap_type++) {
+		for (input_sz = 0; input_sz < RTE_DIM(input_length); input_sz++) {
+			meta_ts->profile[test_case_idx] = profile_create(
+					capabilities, cap_type, input_length[input_sz]);
+			utc = &uts->unit_test_cases[test_case_idx];
+			utc->name = meta_ts->profile[test_case_idx].name;
+			utc->data = (const void *) &meta_ts->profile[test_case_idx];
+			utc->testcase_with_data = test_crosscheck_unit;
+			utc->enabled = true;
+
+			test_case_idx += 1;
+			RTE_VERIFY(test_case_idx <= NB_TEST_CASES_PER_ALGO);
+		}
+	}
+
+	return uts;
+}
+
+static void
+sym_unit_test_suite_free(struct unit_test_suite *suite)
+{
+	struct crosscheck_test_profile profile;
+	struct unit_test_case *utc;
+	int i;
+
+	for (i = 0; suite->unit_test_cases[i].testcase_with_data; i++) {
+		utc = &suite->unit_test_cases[i];
+		profile = *(const struct crosscheck_test_profile *)utc->data;
+		rte_free(profile.key);
+		rte_free(profile.iv);
+	}
+
+	rte_free(suite);
+}
+
+static int
+test_crosscheck(void)
+{
+	int ret, i, ts_idx = 0;
+	static struct unit_test_suite ts = {
+		.suite_name = "Crosscheck Unit Test Suite",
+		.setup = crosscheck_suite_setup,
+		.teardown = crosscheck_suite_teardown,
+		.unit_test_cases = {TEST_CASES_END()}
+	};
+	struct unit_test_suite *test_suites[CRYPTO_ALGOS_LEN+1] = {0};/* 1 for NULL-end suite */
+
+	/* Allocate all required memory pools and crosscheck cryptodev capabilities */
+	ret = crosscheck_init();
+	if (ret)
+		return ret;
+
+	/* Create test suite for each known algorithm */
+	ts.unit_test_suites = test_suites;
+	for (i = 1; i <= CRYPTO_AUTH_MAX_IDX; i++) {
+		ts.unit_test_suites[ts_idx++] = sym_unit_test_suite_create(i,
+				RTE_CRYPTO_SYM_XFORM_AUTH);
+	}
+	for (i = 1; i <= CRYPTO_CIPHER_MAX_IDX; i++) {
+		ts.unit_test_suites[ts_idx++] = sym_unit_test_suite_create(i,
+				RTE_CRYPTO_SYM_XFORM_CIPHER);
+	}
+	for (i = 1; i <= CRYPTO_AEAD_MAX_IDX; i++) {
+		/* AES_CCM requires special handling due to api requirements, skip now */
+		if (i == RTE_CRYPTO_AEAD_AES_CCM)
+			continue;
+		ts.unit_test_suites[ts_idx++] = sym_unit_test_suite_create(i,
+				RTE_CRYPTO_SYM_XFORM_AEAD);
+	}
+
+	ret = unit_test_suite_runner(&ts);
+
+	for (i = 0; i < ts_idx; i++)
+		sym_unit_test_suite_free(ts.unit_test_suites[i]);
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(cryptodev_crosscheck, test_crosscheck);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v3] test: add cryptodev crosscheck suite
  2023-02-09 12:34 ` [PATCH v2] " Volodymyr Fialko
@ 2023-02-09 14:34   ` Volodymyr Fialko
  2023-03-14 13:32     ` [PATCH v4] " Volodymyr Fialko
  2023-03-08 17:30   ` [PATCH v2] " Power, Ciara
  2023-03-08 18:03   ` Power, Ciara
  2 siblings, 1 reply; 11+ messages in thread
From: Volodymyr Fialko @ 2023-02-09 14:34 UTC (permalink / raw)
  To: dev, Akhil Goyal, Fan Zhang
  Cc: jerinj, anoobj, hemant.agrawal, kai.ji, ciara.power, Volodymyr Fialko

Add a validation test suite that helps in verifying that the output
generated by two different cryptodevs match for a wide range of input
parameter combinations.

Crypto autotest performs a comprehensive testing of the cryptodev but since
it performs verification by comparing against known vectors, the extend to
which various parameters (like packet size) can be tested is limited. This
test suite attempts to simulate various cases by running same test case on
different cryptodevs and compares the output generated. The test suite
relies on capabilities to determine the combinations of tests to be
attempted.

A typical use case would be to compare outputs generated from a standard
driver such as openSSL PMD and a new cryptodev PMD. This test suite is to
compliment the testing coverage that crypto autotest provides.

Currently supported symmetric xforms(cipher, auth, aead) without chaining.

Example command:
DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
	-a <cryptodev> --vdev "crypto_openssl"

Signed-off-by: Volodymyr Fialko <vfialko@marvell.com>
---
V2:
- Updated commit message.
V3:
- Removed usage of deprecated crypto strings API.

 app/test/meson.build                 |    1 +
 app/test/test_cryptodev_crosscheck.c | 1008 ++++++++++++++++++++++++++
 2 files changed, 1009 insertions(+)
 create mode 100644 app/test/test_cryptodev_crosscheck.c

diff --git a/app/test/meson.build b/app/test/meson.build
index f34d19e3c3..906dea47c3 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -33,6 +33,7 @@ test_sources = files(
         'test_cryptodev.c',
         'test_cryptodev_asym.c',
         'test_cryptodev_blockcipher.c',
+        'test_cryptodev_crosscheck.c',
         'test_cryptodev_security_ipsec.c',
         'test_cryptodev_security_pdcp.c',
         'test_cycles.c',
diff --git a/app/test/test_cryptodev_crosscheck.c b/app/test/test_cryptodev_crosscheck.c
new file mode 100644
index 0000000000..5da2671685
--- /dev/null
+++ b/app/test/test_cryptodev_crosscheck.c
@@ -0,0 +1,1008 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2022 Marvell.
+ */
+#include <rte_cryptodev.h>
+#include <rte_malloc.h>
+
+#include "test.h"
+#include "test_cryptodev.h"
+
+#define MAX_NB_SESSIONS 1
+#define MAX_TEST_STRING_LEN 256
+
+/* Define list end for known algorithms */
+#define CRYPTO_AUTH_MAX_IDX (RTE_CRYPTO_AUTH_SHA3_512_HMAC)
+#define CRYPTO_CIPHER_MAX_IDX (RTE_CRYPTO_CIPHER_DES_DOCSISBPI)
+#define CRYPTO_AEAD_MAX_IDX (RTE_CRYPTO_AEAD_CHACHA20_POLY1305)
+#define CRYPTO_ALGOS_LEN (CRYPTO_AUTH_MAX_IDX + CRYPTO_CIPHER_MAX_IDX + CRYPTO_AEAD_MAX_IDX)
+
+static struct rte_cryptodev_symmetric_capability common_symm_capas[CRYPTO_ALGOS_LEN];
+
+static struct rte_cryptodev_symmetric_capability*
+common_capability_get(int algo, enum rte_crypto_sym_xform_type xform_type)
+{
+	const int type_to_offset[] = {
+		[RTE_CRYPTO_SYM_XFORM_AUTH] = 0,
+		[RTE_CRYPTO_SYM_XFORM_CIPHER] = CRYPTO_AUTH_MAX_IDX,
+		[RTE_CRYPTO_SYM_XFORM_AEAD] = CRYPTO_AUTH_MAX_IDX + CRYPTO_CIPHER_MAX_IDX,
+	};
+
+	return &common_symm_capas[type_to_offset[xform_type] + algo - 1];
+}
+
+enum capability_select_type {
+	CAPABILITY_TYPE_MIN,
+	CAPABILITY_TYPE_MAX,
+	CAPABILITY_TYPE_LAST,
+};
+
+static const char * const capability_select_strings[] = {
+	[CAPABILITY_TYPE_MIN] = "MIN",
+	[CAPABILITY_TYPE_MAX] = "MAX",
+};
+
+static size_t input_length[] = { 64, 256, 512 };
+
+static const char*
+algo_name_get(const struct rte_cryptodev_symmetric_capability *capa)
+{
+	switch (capa->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		return rte_cryptodev_get_auth_algo_string(capa->auth.algo);
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		return rte_cryptodev_get_cipher_algo_string(capa->cipher.algo);
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		return rte_cryptodev_get_aead_algo_string(capa->aead.algo);
+	default:
+		return NULL;
+	}
+}
+
+/* Calculate number of test cases(combinations) per algorithm */
+#define NB_TEST_CASES_PER_ALGO (CAPABILITY_TYPE_LAST * RTE_DIM(input_length))
+
+enum crypto_op_type {
+	OP_ENCRYPT,
+	OP_DECRYPT,
+};
+
+struct crosscheck_test_profile {
+	char name[MAX_TEST_STRING_LEN];
+	size_t input_buf_len;
+	uint16_t block_size;
+	uint16_t key_size;
+	uint8_t *key;
+	uint16_t iv_size;
+	uint8_t *iv;
+
+	enum rte_crypto_sym_xform_type xform_type;
+	union {
+		struct {
+			enum rte_crypto_auth_algorithm algo;
+			uint16_t digest_size;
+		} auth;
+		struct {
+			enum rte_crypto_cipher_algorithm algo;
+			uint32_t dataunit_set;
+		} cipher;
+		struct {
+			enum rte_crypto_aead_algorithm algo;
+			uint16_t digest_size;
+			uint16_t aad_size;
+		} aead;
+	};
+};
+
+struct meta_test_suite {
+	char suite_name[MAX_TEST_STRING_LEN];
+	struct crosscheck_test_profile profile[NB_TEST_CASES_PER_ALGO];
+};
+
+static struct crypto_testsuite_params testsuite_params;
+
+static void
+incrementing_generate(uint8_t *dst, uint8_t start, uint16_t size)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		dst[i] = start + i;
+}
+
+static void
+pattern_fill(uint8_t *input, const char *pattern, uint16_t size)
+{
+	size_t pattern_len = strlen(pattern);
+	size_t filled_len = 0, to_fill;
+
+	while (filled_len < size) {
+		to_fill = RTE_MIN(pattern_len, size - filled_len);
+		rte_memcpy(input, pattern, to_fill);
+		filled_len += to_fill;
+		input += to_fill;
+	}
+}
+
+static struct crosscheck_test_profile
+profile_create(struct rte_cryptodev_symmetric_capability *capa,
+		enum capability_select_type capability_type, size_t input_len)
+{
+	struct crosscheck_test_profile profile;
+
+	memset(&profile, 0, sizeof(profile));
+	profile.xform_type = capa->xform_type;
+
+	switch (capa->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		profile.block_size = capa->auth.block_size;
+		profile.auth.algo = capa->auth.algo;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->auth.key_size.min;
+			profile.iv_size = capa->auth.iv_size.min;
+			profile.auth.digest_size = capa->auth.digest_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->auth.key_size.max;
+			profile.iv_size = capa->auth.iv_size.max;
+			profile.auth.digest_size = capa->auth.digest_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		profile.block_size = capa->cipher.block_size;
+		profile.cipher.algo = capa->cipher.algo;
+		profile.cipher.dataunit_set = capa->cipher.dataunit_set;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->cipher.key_size.min;
+			profile.iv_size = capa->cipher.iv_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->cipher.key_size.max;
+			profile.iv_size = capa->cipher.iv_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		profile.block_size = capa->aead.block_size;
+		profile.aead.algo = capa->aead.algo;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->aead.key_size.min;
+			profile.iv_size = capa->aead.iv_size.min;
+			profile.aead.digest_size = capa->aead.digest_size.min;
+			profile.aead.aad_size = capa->aead.aad_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->aead.key_size.max;
+			profile.iv_size = capa->aead.iv_size.max;
+			profile.aead.digest_size = capa->aead.digest_size.max;
+			profile.aead.aad_size = capa->aead.aad_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	default:
+		rte_panic("Wrong xform profile type: %i\n", capa->xform_type);
+		break;
+	}
+
+	profile.input_buf_len = RTE_ALIGN_CEIL(input_len, profile.block_size);
+
+	if (profile.key_size) {
+		profile.key = rte_zmalloc(NULL, profile.key_size, 0);
+		RTE_VERIFY(profile.key != NULL);
+		pattern_fill(profile.key, "*Secret key*", profile.key_size);
+	}
+
+	if (profile.iv_size) {
+		profile.iv = rte_zmalloc(NULL, profile.iv_size, 0);
+		RTE_VERIFY(profile.iv != NULL);
+		pattern_fill(profile.iv, "IV ", profile.iv_size);
+	}
+
+	snprintf(profile.name, MAX_TEST_STRING_LEN,
+			"'%s' - capabilities: '%s', input len: '%zu'",
+			algo_name_get(capa), capability_select_strings[capability_type],
+			input_len);
+
+	return profile;
+}
+
+static inline int
+common_range_set(struct rte_crypto_param_range *dst, const struct rte_crypto_param_range *src)
+{
+	/* Check if ranges overlaps */
+	if ((dst->min > src->max) && (dst->max < src->min))
+		return -1;
+	dst->min = RTE_MAX(dst->min, src->min);
+	dst->max = RTE_MIN(dst->max, src->max);
+
+	return 0;
+}
+
+static void
+capabilities_inspect(void)
+{
+	struct rte_cryptodev_sym_capability_idx cap_indexes[CRYPTO_ALGOS_LEN], *cap_idx;
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	const struct rte_cryptodev_symmetric_capability *sym_capa;
+	struct rte_cryptodev_symmetric_capability *common_capa;
+	uint32_t algo, i, dev_id, caps_idx;
+
+	caps_idx = 0;
+	/* Create capability idx for known algorithms*/
+	for (algo = 1; algo <= CRYPTO_AUTH_MAX_IDX; algo++) {
+		cap_idx = &cap_indexes[caps_idx++];
+		cap_idx->type = RTE_CRYPTO_SYM_XFORM_AUTH;
+		cap_idx->algo.auth = algo;
+	}
+	for (algo = 1; algo <= CRYPTO_CIPHER_MAX_IDX; algo++) {
+		cap_idx = &cap_indexes[caps_idx++];
+		cap_idx->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+		cap_idx->algo.cipher = algo;
+	}
+	for (algo = 1; algo <= CRYPTO_AEAD_MAX_IDX; algo++) {
+		cap_idx = &cap_indexes[caps_idx++];
+		cap_idx->type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		cap_idx->algo.aead = algo;
+	}
+
+	for (caps_idx = 0; caps_idx < CRYPTO_ALGOS_LEN; caps_idx++) {
+		/* Gather common capabilities */
+		common_capa = &common_symm_capas[caps_idx];
+		common_capa->xform_type = RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED;
+		for (i = 0; i < ts_params->valid_dev_count; i++) {
+			dev_id = ts_params->valid_devs[i];
+			sym_capa = rte_cryptodev_sym_capability_get(dev_id,
+					&cap_indexes[caps_idx]);
+			if (sym_capa == NULL) {
+				/* Capability not supported by one of devs, mark and skip */
+				goto next_algo;
+			}
+
+			if (common_capa->xform_type == RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED) {
+				/* First time initialization, copy data, go to next device  */
+				*common_capa = *sym_capa;
+				continue;
+			}
+
+			switch (sym_capa->xform_type) {
+			case RTE_CRYPTO_SYM_XFORM_AUTH:
+				if (common_capa->auth.algo != sym_capa->auth.algo)
+					goto next_algo;
+
+				if (common_capa->auth.block_size != sym_capa->auth.block_size)
+					goto next_algo;
+
+				if (common_range_set(&common_capa->auth.key_size,
+							&sym_capa->auth.key_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->auth.digest_size,
+							&sym_capa->auth.digest_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->auth.aad_size,
+							&sym_capa->auth.aad_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->auth.iv_size,
+							&sym_capa->auth.iv_size))
+					goto next_algo;
+				break;
+			case RTE_CRYPTO_SYM_XFORM_CIPHER:
+				if (common_capa->cipher.algo != sym_capa->cipher.algo)
+					goto next_algo;
+
+				if (common_capa->cipher.block_size != sym_capa->cipher.block_size)
+					goto next_algo;
+
+				if (common_range_set(&common_capa->cipher.key_size,
+							&sym_capa->cipher.key_size))
+					goto next_algo;
+
+				if (common_range_set(&common_capa->cipher.iv_size,
+							&sym_capa->cipher.iv_size))
+					goto next_algo;
+				if (common_capa->cipher.dataunit_set !=
+						sym_capa->cipher.dataunit_set)
+					goto next_algo;
+				break;
+			case RTE_CRYPTO_SYM_XFORM_AEAD:
+				if (common_capa->aead.algo != sym_capa->aead.algo)
+					goto next_algo;
+
+				if (common_capa->aead.block_size != sym_capa->aead.block_size)
+					goto next_algo;
+
+				if (common_range_set(&common_capa->aead.key_size,
+							&sym_capa->aead.key_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->aead.digest_size,
+							&sym_capa->aead.digest_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->aead.aad_size,
+							&sym_capa->aead.aad_size))
+					goto next_algo;
+				if (common_range_set(&common_capa->aead.iv_size,
+							&sym_capa->aead.iv_size))
+					goto next_algo;
+				break;
+			default:
+				RTE_LOG(ERR, USER1, "Unsupported xform_type!\n");
+				goto next_algo;
+			}
+		}
+
+		continue;
+next_algo:
+		common_capa->xform_type = RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED;
+	}
+}
+
+static int
+crosscheck_init(void)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	const uint32_t nb_queue_pairs = 1;
+	struct rte_cryptodev_info info;
+	uint32_t session_priv_size = 0;
+	uint32_t nb_devs, dev_id;
+
+	memset(ts_params, 0, sizeof(*ts_params));
+
+	/* Create list of valid crypto devs */
+	nb_devs = rte_cryptodev_count();
+	for (dev_id = 0; dev_id < nb_devs; dev_id++) {
+		rte_cryptodev_info_get(dev_id, &info);
+
+		if (info.sym.max_nb_sessions != 0 && info.sym.max_nb_sessions < MAX_NB_SESSIONS)
+			continue;
+		if (info.max_nb_queue_pairs < nb_queue_pairs)
+			continue;
+		ts_params->valid_devs[ts_params->valid_dev_count++] = dev_id;
+		/* Obtaining configuration parameters, that will satisfy all cryptodevs */
+		session_priv_size = RTE_MAX(session_priv_size,
+				rte_cryptodev_sym_get_private_session_size(dev_id));
+	}
+
+	if (ts_params->valid_dev_count < 2) {
+		RTE_LOG(WARNING, USER1, "Min number of cryptodevs for test is 2, found (%d)\n",
+				ts_params->valid_dev_count);
+		return TEST_SKIPPED;
+	}
+
+	/* Create pools for mbufs, crypto operations and sessions */
+	ts_params->mbuf_pool = rte_pktmbuf_pool_create("CRYPTO_MBUFPOOL", NUM_MBUFS,
+			MBUF_CACHE_SIZE, 0, MBUF_SIZE, rte_socket_id());
+	if (ts_params->mbuf_pool == NULL) {
+		RTE_LOG(ERR, USER1, "Can't create CRYPTO_MBUFPOOL\n");
+		return TEST_FAILED;
+	}
+
+	ts_params->op_mpool = rte_crypto_op_pool_create("MBUF_CRYPTO_SYM_OP_POOL",
+			RTE_CRYPTO_OP_TYPE_SYMMETRIC, NUM_MBUFS, MBUF_CACHE_SIZE,
+			DEFAULT_NUM_XFORMS * sizeof(struct rte_crypto_sym_xform) +
+			MAXIMUM_IV_LENGTH, rte_socket_id());
+
+	if (ts_params->op_mpool == NULL) {
+		RTE_LOG(ERR, USER1, "Can't create CRYPTO_OP_POOL\n");
+		return TEST_FAILED;
+	}
+
+	ts_params->session_mpool = rte_cryptodev_sym_session_pool_create("test_sess_mp",
+			MAX_NB_SESSIONS, session_priv_size, 0, 0, SOCKET_ID_ANY);
+	TEST_ASSERT_NOT_NULL(ts_params->session_mpool, "session mempool allocation failed");
+
+	/* Setup queue pair conf params */
+	ts_params->conf.nb_queue_pairs = nb_queue_pairs;
+	ts_params->conf.socket_id = SOCKET_ID_ANY;
+	ts_params->conf.ff_disable = RTE_CRYPTODEV_FF_SECURITY;
+	ts_params->qp_conf.nb_descriptors = MAX_NUM_OPS_INFLIGHT;
+	ts_params->qp_conf.mp_session = ts_params->session_mpool;
+
+	capabilities_inspect();
+
+	return TEST_SUCCESS;
+}
+
+static int
+dev_configure_and_start(uint64_t ff_disable)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	uint8_t i, dev_id;
+	uint16_t qp_id;
+
+	/* Reconfigure device to default parameters */
+	ts_params->conf.ff_disable = ff_disable;
+
+	/* Configure cryptodevs */
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		TEST_ASSERT_SUCCESS(rte_cryptodev_configure(dev_id, &ts_params->conf),
+				"Failed to configure cryptodev %u with %u qps",
+				dev_id, ts_params->conf.nb_queue_pairs);
+
+
+		for (qp_id = 0; qp_id < ts_params->conf.nb_queue_pairs; qp_id++) {
+			TEST_ASSERT_SUCCESS(rte_cryptodev_queue_pair_setup(
+				dev_id, qp_id, &ts_params->qp_conf,
+				rte_cryptodev_socket_id(dev_id)),
+				"Failed to setup queue pair %u on cryptodev %u",
+				qp_id, dev_id);
+		}
+		rte_cryptodev_stats_reset(dev_id);
+
+		/* Start the device */
+		TEST_ASSERT_SUCCESS(rte_cryptodev_start(dev_id), "Failed to start cryptodev %u",
+				dev_id);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+crosscheck_suite_setup(void)
+{
+	dev_configure_and_start(RTE_CRYPTODEV_FF_SECURITY);
+
+	return 0;
+}
+
+static void
+crosscheck_suite_teardown(void)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	uint8_t i, dev_id;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		rte_cryptodev_stop(dev_id);
+	}
+}
+
+static struct rte_crypto_op *
+crypto_request_process(uint8_t dev_id, struct rte_crypto_op *op)
+{
+	struct rte_crypto_op *res = NULL;
+
+	if (rte_cryptodev_enqueue_burst(dev_id, 0, &op, 1) != 1) {
+		RTE_LOG(ERR, USER1, "Error sending packet for encryption\n");
+		return NULL;
+	}
+
+	while (rte_cryptodev_dequeue_burst(dev_id, 0, &res, 1) == 0)
+		rte_pause();
+
+	if (res->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
+		RTE_LOG(ERR, USER1, "Operation status %d\n", res->status);
+		return NULL;
+	}
+
+	if (res != op) {
+		RTE_LOG(ERR, USER1, "Unexpected operation received!\n");
+		rte_crypto_op_free(res);
+		return NULL;
+	}
+
+	return res;
+}
+
+/* Create free room at the start of segment, by moving existing data further */
+static uint8_t*
+mbuf_room_create(struct rte_mbuf *m, size_t size)
+{
+	uint8_t *data;
+
+	data = rte_pktmbuf_mtod(m, uint8_t*);
+	memmove(data + size, data, m->data_len);
+	m->data_len += size;
+	m->pkt_len += size;
+
+	return data;
+}
+
+static struct rte_cryptodev_sym_session*
+session_create(const struct crosscheck_test_profile *profile, uint8_t dev_id,
+		enum crypto_op_type op_type)
+{
+	struct rte_cryptodev_sym_session *session;
+	struct rte_crypto_sym_xform xform;
+
+	memset(&xform, 0, sizeof(xform));
+
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		xform.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+		xform.next = NULL;
+		xform.auth.algo = profile->auth.algo;
+		xform.auth.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_AUTH_OP_GENERATE :
+			RTE_CRYPTO_AUTH_OP_VERIFY;
+		xform.auth.digest_length = profile->auth.digest_size;
+		xform.auth.key.length = profile->key_size;
+		xform.auth.key.data = profile->key;
+		xform.auth.iv.length = profile->iv_size;
+		xform.auth.iv.offset = IV_OFFSET;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		xform.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+		xform.next = NULL;
+		xform.cipher.algo = profile->cipher.algo;
+		xform.cipher.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_CIPHER_OP_ENCRYPT :
+			RTE_CRYPTO_CIPHER_OP_DECRYPT;
+		xform.cipher.key.length = profile->key_size;
+		xform.cipher.key.data = profile->key;
+		xform.cipher.iv.length = profile->iv_size;
+		xform.cipher.iv.offset = IV_OFFSET;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		xform.type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		xform.next = NULL;
+		xform.aead.algo = profile->aead.algo;
+		xform.aead.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_AEAD_OP_ENCRYPT :
+			RTE_CRYPTO_AEAD_OP_DECRYPT;
+		xform.aead.digest_length = profile->aead.digest_size;
+		xform.aead.key.length = profile->key_size;
+		xform.aead.key.data = profile->key;
+		xform.aead.iv.length = profile->iv_size;
+		xform.aead.iv.offset = IV_OFFSET;
+		xform.aead.aad_length = profile->aead.aad_size;
+		break;
+	default:
+		return NULL;
+	}
+
+	session = rte_cryptodev_sym_session_create(dev_id, &xform, testsuite_params.session_mpool);
+
+	return session;
+}
+
+static struct rte_mbuf*
+mbuf_create(const uint8_t *input_buf, uint16_t input_len)
+{
+	struct rte_mbuf *pkt;
+	uint8_t *pkt_data;
+
+	pkt = rte_pktmbuf_alloc(testsuite_params.mbuf_pool);
+	if (pkt == NULL) {
+		RTE_LOG(ERR, USER1,  "Failed to allocate input buffer in mempool");
+		return NULL;
+	}
+
+	/* zeroing tailroom */
+	memset(rte_pktmbuf_mtod(pkt, uint8_t *), 0, rte_pktmbuf_tailroom(pkt));
+
+	pkt_data = (uint8_t *)rte_pktmbuf_append(pkt, input_len);
+	if (pkt_data == NULL) {
+		RTE_LOG(ERR, USER1, "no room to append data, len: %d", input_len);
+		goto error;
+	}
+	rte_memcpy(pkt_data, input_buf, input_len);
+
+	return pkt;
+error:
+	rte_pktmbuf_free(pkt);
+	return NULL;
+}
+
+static uint16_t
+profile_digest_get(const struct crosscheck_test_profile *profile)
+{
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		return profile->auth.digest_size;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		return profile->aead.digest_size;
+	default:
+		return 0;
+	}
+}
+
+static struct rte_crypto_op*
+operation_create(const struct crosscheck_test_profile *profile,
+		struct rte_mbuf *ibuf, enum crypto_op_type op_type)
+{
+	uint8_t *digest_data = NULL, *aad_data = NULL, *iv_ptr = NULL;
+	uint16_t aad_size = 0, digest_size = 0, digest_offset = 0;
+	uint16_t plaintext_len = profile->input_buf_len;
+	rte_iova_t digest_addr = 0, aad_addr = 0;
+	struct rte_crypto_sym_op *sym_op;
+	struct rte_crypto_op *op;
+
+	op = rte_crypto_op_alloc(testsuite_params.op_mpool, RTE_CRYPTO_OP_TYPE_SYMMETRIC);
+	if (op == NULL) {
+		RTE_LOG(ERR, USER1, "Failed to allocate symmetric crypto operation struct");
+		return NULL;
+	}
+
+	/* Obtain aad and digest sizes */
+	digest_size = profile_digest_get(profile);
+	if (profile->xform_type == RTE_CRYPTO_SYM_XFORM_AEAD)
+		aad_size = profile->aead.aad_size;
+
+	if (aad_size) {
+		if (op_type == OP_ENCRYPT) {
+			if (rte_pktmbuf_tailroom(ibuf) > plaintext_len + aad_size) {
+				/* Put aad to data segment */
+				aad_data = mbuf_room_create(ibuf, aad_size);
+			} else {
+				RTE_LOG(ERR, USER1, "No space for aad in single mbuf\n");
+				goto error;
+			}
+			pattern_fill(aad_data, "This is an aad.", aad_size);
+		} else {
+			aad_data = rte_pktmbuf_mtod(ibuf, uint8_t *);
+		}
+		aad_addr = rte_pktmbuf_iova(ibuf);
+	}
+
+	if (digest_size) {
+		digest_offset = aad_size + plaintext_len;
+		if (op_type == OP_ENCRYPT) {
+			digest_data = (uint8_t *)rte_pktmbuf_append(ibuf, digest_size);
+			if (digest_data == NULL) {
+				RTE_LOG(ERR, USER1, "No room to append digest\n");
+				goto error;
+			}
+		} else {
+			digest_data = rte_pktmbuf_mtod_offset(ibuf, uint8_t *, digest_offset);
+		}
+		digest_addr = rte_pktmbuf_iova_offset(ibuf, digest_offset);
+	}
+
+	sym_op = op->sym;
+	memset(sym_op, 0, sizeof(*sym_op));
+
+	iv_ptr = rte_crypto_op_ctod_offset(op, uint8_t *, IV_OFFSET);
+	rte_memcpy(iv_ptr, profile->iv, profile->iv_size);
+
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		sym_op->auth.digest.phys_addr = digest_addr;
+		sym_op->auth.digest.data = digest_data;
+		sym_op->auth.data.length = plaintext_len;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		sym_op->cipher.data.length = plaintext_len;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		sym_op->aead.aad.phys_addr = aad_addr;
+		sym_op->aead.aad.data = aad_data;
+		sym_op->aead.digest.phys_addr = digest_addr;
+		sym_op->aead.digest.data = digest_data;
+		sym_op->aead.data.offset = aad_size;
+		sym_op->aead.data.length = plaintext_len;
+		break;
+	default:
+		goto error;
+	}
+
+	sym_op->m_src = ibuf;
+
+	return op;
+
+error:
+	rte_crypto_op_free(op);
+	return NULL;
+}
+
+static void
+mbuf_to_buf_copy(const struct rte_mbuf *m, uint8_t *res_buf, uint16_t *len)
+{
+	const uint8_t *out;
+
+	*len = m->pkt_len;
+	out = rte_pktmbuf_read(m, 0, *len, res_buf);
+	/* Single segment buffer */
+	if (out != res_buf)
+		memcpy(res_buf, out, *len);
+}
+
+static int
+single_dev_process(const struct crosscheck_test_profile *profile, uint16_t dev_id, enum
+		crypto_op_type op_type, const uint8_t *input_buf, uint16_t input_len,
+		uint8_t *output_buf, uint16_t *output_len)
+{
+	struct rte_cryptodev_sym_session *session = NULL;
+	struct rte_mbuf *ibuf = NULL, *obuf = NULL;
+	struct rte_crypto_op *op = NULL;
+	int ret = -1;
+
+	session = session_create(profile, dev_id, op_type);
+	if (session == NULL)
+		goto error;
+
+	ibuf = mbuf_create(input_buf, input_len);
+	if (ibuf == NULL)
+		goto error;
+
+	op = operation_create(profile, ibuf, op_type);
+	if (op == NULL)
+		goto error;
+
+	debug_hexdump(stdout, "Input:", rte_pktmbuf_mtod(ibuf, uint8_t*), ibuf->pkt_len);
+
+	rte_crypto_op_attach_sym_session(op, session);
+
+	struct rte_crypto_op *res = crypto_request_process(dev_id, op);
+	if (res == NULL)
+		goto error;
+
+	obuf = op->sym->m_src;
+	if (obuf == NULL) {
+		RTE_LOG(ERR, USER1, "Invalid packet received\n");
+		goto error;
+	}
+	mbuf_to_buf_copy(obuf, output_buf, output_len);
+
+	ret = 0;
+
+error:
+	if (session != NULL) {
+		int sret;
+		sret = rte_cryptodev_sym_session_free(dev_id, session);
+		assert(sret == 0);
+	}
+	rte_pktmbuf_free(ibuf);
+	rte_crypto_op_free(op);
+	return ret;
+}
+
+static int
+crosscheck_all_devices(const struct crosscheck_test_profile *profile, enum crypto_op_type op_type,
+		const uint8_t *input_text, uint16_t input_len, uint8_t *output_text,
+		uint16_t *output_len)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	uint16_t len = 0, expected_len = 0;
+	uint8_t expected_text[MBUF_SIZE];
+	uint8_t i, dev_id;
+	int status;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		status = single_dev_process(profile, dev_id, op_type, input_text, input_len,
+				output_text, &len);
+		TEST_ASSERT_SUCCESS(status, "Error occurred during processing");
+
+		if (i == 0) {
+			/* First device, copy data for future comparisons */
+			memcpy(expected_text, output_text, len);
+			expected_len = len;
+		} else {
+			/* Compare output against expected(first) output */
+			TEST_ASSERT_EQUAL(len, expected_len, "Length mismatch %d != %d !\n",
+					len, expected_len);
+
+			if (memcmp(output_text, expected_text, len)) {
+				RTE_LOG(ERR, USER1, "Output mismatch between dev 0 and %i\n",
+						dev_id);
+				rte_hexdump(rte_log_get_stream(), "expected", expected_text, len);
+				rte_hexdump(rte_log_get_stream(), "received", output_text, len);
+				return TEST_FAILED;
+			}
+		}
+
+		RTE_LOG(DEBUG, USER1, "DEV ID: %u finished processing\n", dev_id);
+		debug_hexdump(stdout, "Output: ", output_text, len);
+	}
+
+	*output_len = len;
+
+	return TEST_SUCCESS;
+}
+
+static int
+check_negative_all_devices(const struct crosscheck_test_profile *profile,
+		enum crypto_op_type op_type, const uint8_t *input_text, uint16_t input_len)
+{
+	struct crypto_testsuite_params *ts_params = &testsuite_params;
+	uint8_t output_text[MBUF_SIZE];
+	uint8_t i, dev_id;
+	uint16_t len;
+	int status;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		status = single_dev_process(profile, dev_id, op_type, input_text, input_len,
+				output_text, &len);
+		TEST_ASSERT_FAIL(status, "Error occurred during processing negative case");
+
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+crosscheck_with_profile_run(const struct crosscheck_test_profile *profile)
+{
+	uint8_t input_text[profile->input_buf_len];
+	uint16_t output_len, encrypted_len;
+	uint8_t encrypted_text[MBUF_SIZE];
+	uint8_t output_text[MBUF_SIZE];
+	int status;
+
+	/* Encrypt Stage */
+	RTE_LOG(DEBUG, USER1, "Executing encrypt stage\n");
+	/* Fill input with incrementing pattern */
+	incrementing_generate(input_text, 'a', profile->input_buf_len);
+	status = crosscheck_all_devices(profile, OP_ENCRYPT, input_text, profile->input_buf_len,
+			output_text, &output_len);
+	TEST_ASSERT_SUCCESS(status, "Error occurred during encryption");
+
+	/* Decrypt Stage */
+	RTE_LOG(DEBUG, USER1, "Executing decrypt stage\n");
+	/* Set up encrypted data as input */
+	encrypted_len = output_len;
+	memcpy(encrypted_text, output_text, output_len);
+	status = crosscheck_all_devices(profile, OP_DECRYPT, encrypted_text, encrypted_len,
+			output_text, &output_len);
+	TEST_ASSERT_SUCCESS(status, "Error occurred during decryption");
+
+	/* Negative Stage */
+	RTE_LOG(DEBUG, USER1, "Executing negative stage\n");
+	if (profile_digest_get(profile)) {
+		/* Corrupting one byte of digest */
+		encrypted_text[encrypted_len - 1] += 1;
+		status = check_negative_all_devices(profile, OP_DECRYPT, encrypted_text,
+				encrypted_len);
+		TEST_ASSERT_SUCCESS(status, "Error occurred during decryption");
+	}
+
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_crosscheck_unit(const void *ptr)
+{
+	const struct crosscheck_test_profile *profile = ptr;
+
+	if (profile->xform_type == RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED)
+		return TEST_SKIPPED;
+
+	return crosscheck_with_profile_run(profile);
+}
+
+static struct unit_test_suite*
+sym_unit_test_suite_create(int algo, enum rte_crypto_sym_xform_type xform_type)
+{
+	struct rte_cryptodev_symmetric_capability *capabilities;
+	size_t uts_size, total_size, input_sz;
+	struct meta_test_suite *meta_ts;
+	struct unit_test_suite *uts;
+	const char *suite_prefix = NULL;
+	const char *algo_name = NULL;
+	uint64_t test_case_idx = 0;
+	struct unit_test_case *utc;
+	int cap_type;
+	char *mem;
+
+	switch (xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		suite_prefix = "Algo AUTH ";
+		algo_name = rte_cryptodev_get_auth_algo_string(algo);
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		suite_prefix = "Algo CIPHER";
+		algo_name = rte_cryptodev_get_cipher_algo_string(algo);
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		suite_prefix = "Algo AEAD";
+		algo_name = rte_cryptodev_get_aead_algo_string(algo);
+		break;
+	default:
+		return NULL;
+	}
+
+	/* Calculate size for test suite with all test cases +1 NULL case */
+	uts_size = sizeof(struct unit_test_suite) +
+		(NB_TEST_CASES_PER_ALGO + 1) * sizeof(struct unit_test_case);
+
+	/* Also allocate memory for suite meta data */
+	total_size = uts_size + sizeof(struct meta_test_suite);
+	mem = rte_zmalloc(NULL, total_size, 0);
+	if (mem == NULL)
+		return NULL;
+	uts = (struct unit_test_suite *) mem;
+	meta_ts = (struct meta_test_suite *) (mem + uts_size);
+
+	/* Initialize test suite */
+	snprintf(meta_ts->suite_name, MAX_TEST_STRING_LEN, "%s '%s'", suite_prefix, algo_name);
+	uts->suite_name = meta_ts->suite_name;
+
+	capabilities = common_capability_get(algo, xform_type);
+	if (capabilities->xform_type == 0) /* Skip case if current algo is not supported */
+		return uts;
+
+	/* Initialize test cases */
+
+	for (cap_type = 0; cap_type < CAPABILITY_TYPE_LAST; cap_type++) {
+		for (input_sz = 0; input_sz < RTE_DIM(input_length); input_sz++) {
+			meta_ts->profile[test_case_idx] = profile_create(
+					capabilities, cap_type, input_length[input_sz]);
+			utc = &uts->unit_test_cases[test_case_idx];
+			utc->name = meta_ts->profile[test_case_idx].name;
+			utc->data = (const void *) &meta_ts->profile[test_case_idx];
+			utc->testcase_with_data = test_crosscheck_unit;
+			utc->enabled = true;
+
+			test_case_idx += 1;
+			RTE_VERIFY(test_case_idx <= NB_TEST_CASES_PER_ALGO);
+		}
+	}
+
+	return uts;
+}
+
+static void
+sym_unit_test_suite_free(struct unit_test_suite *suite)
+{
+	struct crosscheck_test_profile profile;
+	struct unit_test_case *utc;
+	int i;
+
+	for (i = 0; suite->unit_test_cases[i].testcase_with_data; i++) {
+		utc = &suite->unit_test_cases[i];
+		profile = *(const struct crosscheck_test_profile *)utc->data;
+		rte_free(profile.key);
+		rte_free(profile.iv);
+	}
+
+	rte_free(suite);
+}
+
+static int
+test_crosscheck(void)
+{
+	int ret, i, ts_idx = 0;
+	static struct unit_test_suite ts = {
+		.suite_name = "Crosscheck Unit Test Suite",
+		.setup = crosscheck_suite_setup,
+		.teardown = crosscheck_suite_teardown,
+		.unit_test_cases = {TEST_CASES_END()}
+	};
+	struct unit_test_suite *test_suites[CRYPTO_ALGOS_LEN+1] = {0};/* 1 for NULL-end suite */
+
+	/* Allocate all required memory pools and crosscheck cryptodev capabilities */
+	ret = crosscheck_init();
+	if (ret)
+		return ret;
+
+	/* Create test suite for each known algorithm */
+	ts.unit_test_suites = test_suites;
+	for (i = 1; i <= CRYPTO_AUTH_MAX_IDX; i++) {
+		ts.unit_test_suites[ts_idx++] = sym_unit_test_suite_create(i,
+				RTE_CRYPTO_SYM_XFORM_AUTH);
+	}
+	for (i = 1; i <= CRYPTO_CIPHER_MAX_IDX; i++) {
+		ts.unit_test_suites[ts_idx++] = sym_unit_test_suite_create(i,
+				RTE_CRYPTO_SYM_XFORM_CIPHER);
+	}
+	for (i = 1; i <= CRYPTO_AEAD_MAX_IDX; i++) {
+		/* AES_CCM requires special handling due to api requirements, skip now */
+		if (i == RTE_CRYPTO_AEAD_AES_CCM)
+			continue;
+		ts.unit_test_suites[ts_idx++] = sym_unit_test_suite_create(i,
+				RTE_CRYPTO_SYM_XFORM_AEAD);
+	}
+
+	ret = unit_test_suite_runner(&ts);
+
+	for (i = 0; i < ts_idx; i++)
+		sym_unit_test_suite_free(ts.unit_test_suites[i]);
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(cryptodev_crosscheck, test_crosscheck);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: [PATCH v2] test: add cryptodev crosscheck suite
  2023-02-09 12:34 ` [PATCH v2] " Volodymyr Fialko
  2023-02-09 14:34   ` [PATCH v3] " Volodymyr Fialko
@ 2023-03-08 17:30   ` Power, Ciara
  2023-03-14 13:33     ` Volodymyr Fialko
  2023-03-08 18:03   ` Power, Ciara
  2 siblings, 1 reply; 11+ messages in thread
From: Power, Ciara @ 2023-03-08 17:30 UTC (permalink / raw)
  To: Volodymyr Fialko, dev, Akhil Goyal, Fan Zhang
  Cc: jerinj, anoobj, hemant.agrawal, Ji,  Kai

Hi Volodymyr,


> -----Original Message-----
> From: Volodymyr Fialko <vfialko@marvell.com>
> Sent: Thursday 9 February 2023 12:34
> To: dev@dpdk.org; Akhil Goyal <gakhil@marvell.com>; Fan Zhang
> <fanzhang.oss@gmail.com>
> Cc: jerinj@marvell.com; anoobj@marvell.com; hemant.agrawal@nxp.com; Ji,
> Kai <kai.ji@intel.com>; Power, Ciara <ciara.power@intel.com>; Volodymyr
> Fialko <vfialko@marvell.com>
> Subject: [PATCH v2] test: add cryptodev crosscheck suite
> 
> Add a validation test suite that helps in verifying that the output generated
> by two different cryptodevs match for a wide range of input parameter
> combinations.
> 
> Crypto autotest performs a comprehensive testing of the cryptodev but
> since it performs verification by comparing against known vectors, the
> extend to which various parameters (like packet size) can be tested is
> limited. This test suite attempts to simulate various cases by running same
> test case on different cryptodevs and compares the output generated. The
> test suite relies on capabilities to determine the combinations of tests to be
> attempted.
> 
> A typical use case would be to compare outputs generated from a standard
> driver such as openSSL PMD and a new cryptodev PMD. This test suite is to
> compliment the testing coverage that crypto autotest provides.
> 
> Currently supported symmetric xforms(cipher, auth, aead) without chaining.
> 
> Example command:
> DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
> 	-a <cryptodev> --vdev "crypto_openssl"
> 
> Signed-off-by: Volodymyr Fialko <vfialko@marvell.com>
> ---
> V2:
> - Updated commit message.
> 
<snip>
> +
> +static void
> +capabilities_inspect(void)
> +{
> +	struct rte_cryptodev_sym_capability_idx
> cap_indexes[CRYPTO_ALGOS_LEN], *cap_idx;
> +	struct crypto_testsuite_params *ts_params = &testsuite_params;
> +	const struct rte_cryptodev_symmetric_capability *sym_capa;
> +	struct rte_cryptodev_symmetric_capability *common_capa;
> +	uint32_t algo, i, dev_id, caps_idx;
> +
> +	caps_idx = 0;
> +	/* Create capability idx for known algorithms*/
> +	for (algo = 1; algo <= CRYPTO_AUTH_MAX_IDX; algo++) {
> +		cap_idx = &cap_indexes[caps_idx++];
> +		cap_idx->type = RTE_CRYPTO_SYM_XFORM_AUTH;
> +		cap_idx->algo.auth = algo;
> +	}
> +	for (algo = 1; algo <= CRYPTO_CIPHER_MAX_IDX; algo++) {
> +		cap_idx = &cap_indexes[caps_idx++];
> +		cap_idx->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
> +		cap_idx->algo.cipher = algo;
> +	}
> +	for (algo = 1; algo <= CRYPTO_AEAD_MAX_IDX; algo++) {
> +		cap_idx = &cap_indexes[caps_idx++];
> +		cap_idx->type = RTE_CRYPTO_SYM_XFORM_AEAD;
> +		cap_idx->algo.aead = algo;
> +	}
> +
> +	for (caps_idx = 0; caps_idx < CRYPTO_ALGOS_LEN; caps_idx++) {
> +		/* Gather common capabilities */
> +		common_capa = &common_symm_capas[caps_idx];
> +		common_capa->xform_type =
> RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED;
> +		for (i = 0; i < ts_params->valid_dev_count; i++) {
> +			dev_id = ts_params->valid_devs[i];
> +			sym_capa =
> rte_cryptodev_sym_capability_get(dev_id,
> +					&cap_indexes[caps_idx]);
> +			if (sym_capa == NULL) {
> +				/* Capability not supported by one of devs,
> mark and skip */
> +				goto next_algo;
> +			}
> +
> +			if (common_capa->xform_type ==
> RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED) {
> +				/* First time initialization, copy data, go to
> next device  */
> +				*common_capa = *sym_capa;
> +				continue;
> +			}
[CP] 

This function - from what I understand after review,  is looping through all algorithms in DPDK crypto, and then checking if each is supported by each device?
Could we instead just take the capabilities list from one device as the starting point - That will be the max list of capabilities, they will only get knocked out if not supported on the 2nd/3rd device etc.


<snip>

Some trials I did with this patch showed some issues.

1. For QAT + AESNI_MB I get all skipped tests. I would have expected some common algs here.
+ ------------------------------------------------------- +
 + Test Suite Summary : Crosscheck Unit Test Suite
 + ------------------------------------------------------- +
 + Algo AUTH  'null' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'aes-cbc-mac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'aes-cmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'aes-gmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'aes-xcbc-mac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'kasumi-f9' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'md5' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'md5-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha1' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha1-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha2-224' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha2-224-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha2-256' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha2-256-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha2-384' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha2-384-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha2-512' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha2-512-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'snow3g-uia2' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'zuc-eia3' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha3-224' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha3-224-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha3-256' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha3-256-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha3-384' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha3-384-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha3-512' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AUTH  'sha3-512-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'null' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER '3des-cbc' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER '3des-ctr' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER '3des-ecb' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'aes-cbc' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'aes-ctr' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'aes-ecb' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'aes-f8' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'aes-xts' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'arc4' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'kasumi-f8' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'snow3g-uea2' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'zuc-eea3' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'des-cbc' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'aes-docsisbpi' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo CIPHER 'des-docsisbpi' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AEAD 'aes-gcm' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + Algo AEAD 'chacha20-poly1305' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
 + ------------------------------------------------------- +
 + Sub Testsuites Total :     46
 + Sub Testsuites Skipped :   46
 + Sub Testsuites Passed :     0
 + Sub Testsuites Failed :     0
 + ------------------------------------------------------- +
 + Tests Total :        0
 + Tests Skipped :      0
 + Tests Executed :     0
 + Tests Unsupported:   0
 + Tests Passed :       0
 + Tests Failed :       0
 + ------------------------------------------------------- +
Test Skipped


2. For 2 openssl devices I get 3 failures, which is curious as they are the same. Seems to be a space issue:
USER1: Operation status 2
USER1: No space for aad in single mbuf
EAL: Test assert crosscheck_all_devices line 776 failed: Error occurred during processing
EAL: Test assert crosscheck_with_profile_run line 841 failed: Error occurred during encryption


Thanks,
Ciara

^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: [PATCH v2] test: add cryptodev crosscheck suite
  2023-02-09 12:34 ` [PATCH v2] " Volodymyr Fialko
  2023-02-09 14:34   ` [PATCH v3] " Volodymyr Fialko
  2023-03-08 17:30   ` [PATCH v2] " Power, Ciara
@ 2023-03-08 18:03   ` Power, Ciara
  2 siblings, 0 replies; 11+ messages in thread
From: Power, Ciara @ 2023-03-08 18:03 UTC (permalink / raw)
  To: Volodymyr Fialko, dev, Akhil Goyal, Fan Zhang
  Cc: jerinj, anoobj, hemant.agrawal, Ji,  Kai



> -----Original Message-----
> From: Power, Ciara
> Sent: Wednesday 8 March 2023 17:31
> To: Volodymyr Fialko <vfialko@marvell.com>; dev@dpdk.org; Akhil Goyal
> <gakhil@marvell.com>; Fan Zhang <fanzhang.oss@gmail.com>
> Cc: jerinj@marvell.com; anoobj@marvell.com; hemant.agrawal@nxp.com; Ji,
> Kai <kai.ji@intel.com>
> Subject: RE: [PATCH v2] test: add cryptodev crosscheck suite
> 
> Hi Volodymyr,
> 
> 
> > -----Original Message-----
> > From: Volodymyr Fialko <vfialko@marvell.com>
> > Sent: Thursday 9 February 2023 12:34
> > To: dev@dpdk.org; Akhil Goyal <gakhil@marvell.com>; Fan Zhang
> > <fanzhang.oss@gmail.com>
> > Cc: jerinj@marvell.com; anoobj@marvell.com; hemant.agrawal@nxp.com;
> > Ji, Kai <kai.ji@intel.com>; Power, Ciara <ciara.power@intel.com>;
> > Volodymyr Fialko <vfialko@marvell.com>
> > Subject: [PATCH v2] test: add cryptodev crosscheck suite
> >
> > Add a validation test suite that helps in verifying that the output
> > generated by two different cryptodevs match for a wide range of input
> > parameter combinations.
> >
> > Crypto autotest performs a comprehensive testing of the cryptodev but
> > since it performs verification by comparing against known vectors, the
> > extend to which various parameters (like packet size) can be tested is
> > limited. This test suite attempts to simulate various cases by running
> > same test case on different cryptodevs and compares the output
> > generated. The test suite relies on capabilities to determine the
> > combinations of tests to be attempted.
> >
> > A typical use case would be to compare outputs generated from a
> > standard driver such as openSSL PMD and a new cryptodev PMD. This test
> > suite is to compliment the testing coverage that crypto autotest provides.
> >
> > Currently supported symmetric xforms(cipher, auth, aead) without
> chaining.
> >
> > Example command:
> > DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
> > 	-a <cryptodev> --vdev "crypto_openssl"
> >
> > Signed-off-by: Volodymyr Fialko <vfialko@marvell.com>
> > ---
> > V2:
> > - Updated commit message.
> >
> <snip>
> > +
> > +static void
> > +capabilities_inspect(void)
> > +{
> > +	struct rte_cryptodev_sym_capability_idx
> > cap_indexes[CRYPTO_ALGOS_LEN], *cap_idx;
> > +	struct crypto_testsuite_params *ts_params = &testsuite_params;
> > +	const struct rte_cryptodev_symmetric_capability *sym_capa;
> > +	struct rte_cryptodev_symmetric_capability *common_capa;
> > +	uint32_t algo, i, dev_id, caps_idx;
> > +
> > +	caps_idx = 0;
> > +	/* Create capability idx for known algorithms*/
> > +	for (algo = 1; algo <= CRYPTO_AUTH_MAX_IDX; algo++) {
> > +		cap_idx = &cap_indexes[caps_idx++];
> > +		cap_idx->type = RTE_CRYPTO_SYM_XFORM_AUTH;
> > +		cap_idx->algo.auth = algo;
> > +	}
> > +	for (algo = 1; algo <= CRYPTO_CIPHER_MAX_IDX; algo++) {
> > +		cap_idx = &cap_indexes[caps_idx++];
> > +		cap_idx->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
> > +		cap_idx->algo.cipher = algo;
> > +	}
> > +	for (algo = 1; algo <= CRYPTO_AEAD_MAX_IDX; algo++) {
> > +		cap_idx = &cap_indexes[caps_idx++];
> > +		cap_idx->type = RTE_CRYPTO_SYM_XFORM_AEAD;
> > +		cap_idx->algo.aead = algo;
> > +	}
> > +
> > +	for (caps_idx = 0; caps_idx < CRYPTO_ALGOS_LEN; caps_idx++) {
> > +		/* Gather common capabilities */
> > +		common_capa = &common_symm_capas[caps_idx];
> > +		common_capa->xform_type =
> > RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED;
> > +		for (i = 0; i < ts_params->valid_dev_count; i++) {
> > +			dev_id = ts_params->valid_devs[i];
> > +			sym_capa =
> > rte_cryptodev_sym_capability_get(dev_id,
> > +					&cap_indexes[caps_idx]);
> > +			if (sym_capa == NULL) {
> > +				/* Capability not supported by one of devs,
> > mark and skip */
> > +				goto next_algo;
> > +			}
> > +
> > +			if (common_capa->xform_type ==
> > RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED) {
> > +				/* First time initialization, copy data, go to
> > next device  */
> > +				*common_capa = *sym_capa;
> > +				continue;
> > +			}
> [CP]
> 
> This function - from what I understand after review,  is looping through all
> algorithms in DPDK crypto, and then checking if each is supported by each
> device?
> Could we instead just take the capabilities list from one device as the starting
> point - That will be the max list of capabilities, they will only get knocked out
> if not supported on the 2nd/3rd device etc.
> 
> 
> <snip>
> 
> Some trials I did with this patch showed some issues.
> 
> 1. For QAT + AESNI_MB I get all skipped tests. I would have expected some
> common algs here.
> + ------------------------------------------------------- +
>  + Test Suite Summary : Crosscheck Unit Test Suite  + -----------------------------
> -------------------------- +  + Algo AUTH  'null' : 0/0 passed, 0/0 skipped, 0/0
> failed, 0/0 unsupported  + Algo AUTH  'aes-cbc-mac' : 0/0 passed, 0/0
> skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'aes-cmac' : 0/0 passed,
> 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'aes-gmac' : 0/0
> passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'aes-xcbc-
> mac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH
> 'kasumi-f9' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo
> AUTH  'md5' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo
> AUTH  'md5-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  +
> Algo AUTH  'sha1' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  +
> Algo AUTH  'sha1-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0
> unsupported  + Algo AUTH  'sha2-224' : 0/0 passed, 0/0 skipped, 0/0 failed,
> 0/0 unsupported  + Algo AUTH  'sha2-224-hmac' : 0/0 passed, 0/0 skipped, 0/0
> failed, 0/0 unsupported  + Algo AUTH  'sha2-256' : 0/0 passed, 0/0 skipped,
> 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha2-256-hmac' : 0/0 passed, 0/0
> skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha2-384' : 0/0 passed,
> 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha2-384-hmac' : 0/0
> passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha2-512' :
> 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha2-512-
> hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH
> 'snow3g-uia2' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo
> AUTH  'zuc-eia3' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo
> AUTH  'sha3-224' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  +
> Algo AUTH  'sha3-224-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0
> unsupported  + Algo AUTH  'sha3-256' : 0/0 passed, 0/0 skipped, 0/0 failed,
> 0/0 unsupported  + Algo AUTH  'sha3-256-hmac' : 0/0 passed, 0/0 skipped, 0/0
> failed, 0/0 unsupported  + Algo AUTH  'sha3-384' : 0/0 passed, 0/0 skipped,
> 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha3-384-hmac' : 0/0 passed, 0/0
> skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha3-512' : 0/0 passed,
> 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha3-512-hmac' : 0/0
> passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER 'null' : 0/0
> passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER '3des-cbc' :
> 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER '3des-ctr'
> : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER '3des-
> ecb' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER
> 'aes-cbc' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo
> CIPHER 'aes-ctr' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo
> CIPHER 'aes-ecb' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  +
> Algo CIPHER 'aes-f8' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  +
> Algo CIPHER 'aes-xts' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  +
> Algo CIPHER 'arc4' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  +
> Algo CIPHER 'kasumi-f8' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0
> unsupported  + Algo CIPHER 'snow3g-uea2' : 0/0 passed, 0/0 skipped, 0/0
> failed, 0/0 unsupported  + Algo CIPHER 'zuc-eea3' : 0/0 passed, 0/0 skipped,
> 0/0 failed, 0/0 unsupported  + Algo CIPHER 'des-cbc' : 0/0 passed, 0/0
> skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER 'aes-docsisbpi' : 0/0
> passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER 'des-
> docsisbpi' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AEAD
> 'aes-gcm' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AEAD
> 'chacha20-poly1305' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  +
> ------------------------------------------------------- +
>  + Sub Testsuites Total :     46
>  + Sub Testsuites Skipped :   46
>  + Sub Testsuites Passed :     0
>  + Sub Testsuites Failed :     0
>  + ------------------------------------------------------- +
>  + Tests Total :        0
>  + Tests Skipped :      0
>  + Tests Executed :     0
>  + Tests Unsupported:   0
>  + Tests Passed :       0
>  + Tests Failed :       0
>  + ------------------------------------------------------- + Test Skipped
> 
> 
> 2. For 2 openssl devices I get 3 failures, which is curious as they are the same.
> Seems to be a space issue:
> USER1: Operation status 2
> USER1: No space for aad in single mbuf
> EAL: Test assert crosscheck_all_devices line 776 failed: Error occurred during
> processing
> EAL: Test assert crosscheck_with_profile_run line 841 failed: Error occurred
> during encryption
> 
> 
> Thanks,
> Ciara
[CP] 

Comments above are based on V3 version of patch ... replied to the wrong email.
-Ciara

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v4] test: add cryptodev crosscheck suite
  2023-02-09 14:34   ` [PATCH v3] " Volodymyr Fialko
@ 2023-03-14 13:32     ` Volodymyr Fialko
  2023-03-14 13:56       ` [PATCH v5] " Volodymyr Fialko
  0 siblings, 1 reply; 11+ messages in thread
From: Volodymyr Fialko @ 2023-03-14 13:32 UTC (permalink / raw)
  To: dev, Akhil Goyal, Fan Zhang
  Cc: jerinj, anoobj, hemant.agrawal, kai.ji, ciara.power, Volodymyr Fialko

Add a validation test suite that helps in verifying that the output
generated by two different cryptodevs match for a wide range of input
parameter combinations.

Crypto autotest performs a comprehensive testing of the cryptodev but since
it performs verification by comparing against known vectors, the extend to
which various parameters (like packet size) can be tested is limited. This
test suite attempts to simulate various cases by running same test case on
different cryptodevs and compares the output generated. The test suite
relies on capabilities to determine the combinations of tests to be
attempted.

A typical use case would be to compare outputs generated from a standard
driver such as openSSL PMD and a new cryptodev PMD. This test suite is to
compliment the testing coverage that crypto autotest provides.

Currently supported symmetric xforms(cipher, auth, aead) without chaining.

Example commands:
DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
	--vdev "crypto_openssl0" --vdev "crypto_openssl1"
DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
	-a <cryptodev> --vdev "crypto_openssl"

Signed-off-by: Volodymyr Fialko <vfialko@marvell.com>
---
V2:
- Updated commit message.
V3:
- Removed usage of deprecated crypto strings API.
V4:
- Reworked cryptodev capabilities scan procedure
- Moved AAD and digest from rte_mbuf to separated memory
- Released allocated memory

 app/test/meson.build                 |    1 +
 app/test/test_cryptodev_crosscheck.c | 1050 ++++++++++++++++++++++++++
 2 files changed, 1051 insertions(+)
 create mode 100644 app/test/test_cryptodev_crosscheck.c

diff --git a/app/test/meson.build b/app/test/meson.build
index 1b9ce355f8..f0bcf99fb1 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -33,6 +33,7 @@ test_sources = files(
         'test_cryptodev.c',
         'test_cryptodev_asym.c',
         'test_cryptodev_blockcipher.c',
+        'test_cryptodev_crosscheck.c',
         'test_cryptodev_security_ipsec.c',
         'test_cryptodev_security_pdcp.c',
         'test_cycles.c',
diff --git a/app/test/test_cryptodev_crosscheck.c b/app/test/test_cryptodev_crosscheck.c
new file mode 100644
index 0000000000..f031c86fdd
--- /dev/null
+++ b/app/test/test_cryptodev_crosscheck.c
@@ -0,0 +1,1050 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+#include <rte_cryptodev.h>
+#include <rte_malloc.h>
+
+#include "test.h"
+#include "test_cryptodev.h"
+
+#define MAX_NB_SESSIONS 1
+#define MAX_TEST_STRING_LEN 256
+
+/*
+ * The test suite will iterate through the capabilities of each probed cryptodev to identify the
+ * common ones. Once the common capabilities are determined, the test suite will generate potential
+ * valid inputs and crosscheck (compare) the output results from all cryptodevs.
+ */
+static struct rte_cryptodev_symmetric_capability *common_symm_capas;
+static uint16_t nb_common_sym_caps;
+
+/* Policies of capabilities selection */
+enum capability_select_type {
+	CAPABILITY_TYPE_MIN,
+	CAPABILITY_TYPE_MAX,
+	CAPABILITY_TYPE_LAST,
+};
+
+static const char * const capability_select_strings[] = {
+	[CAPABILITY_TYPE_MIN] = "MIN",
+	[CAPABILITY_TYPE_MAX] = "MAX",
+};
+
+/* Length of input text to be encrypted */
+static size_t input_length[] = { 64, 256, 512 };
+
+/* Calculate number of test cases(combinations) per algorithm */
+#define NB_TEST_CASES_PER_ALGO (CAPABILITY_TYPE_LAST * RTE_DIM(input_length))
+
+enum crypto_op_type {
+	OP_ENCRYPT,
+	OP_DECRYPT,
+};
+
+struct crosscheck_test_profile {
+	char name[MAX_TEST_STRING_LEN];
+	size_t input_buf_len;
+	enum rte_crypto_sym_xform_type xform_type;
+	int algo;
+	uint16_t block_size;
+	uint16_t key_size;
+	uint16_t iv_size;
+	uint16_t digest_size;
+	uint16_t aad_size;
+	uint32_t dataunit_set;
+};
+
+struct meta_test_suite {
+	char suite_name[MAX_TEST_STRING_LEN];
+	struct crosscheck_test_profile profile[NB_TEST_CASES_PER_ALGO];
+};
+
+struct memory_segment {
+	uint8_t *mem;
+	uint16_t len;
+};
+
+struct crosscheck_testsuite_params {
+	struct rte_mempool *mbuf_pool;
+	struct rte_mempool *op_mpool;
+	struct rte_mempool *session_mpool;
+	struct rte_cryptodev_config conf;
+	struct rte_cryptodev_qp_conf qp_conf;
+
+	uint8_t valid_devs[RTE_CRYPTO_MAX_DEVS];
+	uint8_t valid_dev_count;
+
+	struct memory_segment key;
+	struct memory_segment digest;
+	struct memory_segment aad;
+	struct memory_segment iv;
+
+	struct memory_segment expected_digest;
+	struct memory_segment expected_aad;
+};
+
+static struct crosscheck_testsuite_params testsuite_params;
+
+static const char*
+algo_name_get(const struct rte_cryptodev_symmetric_capability *capa)
+{
+	switch (capa->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		return rte_cryptodev_get_auth_algo_string(capa->auth.algo);
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		return rte_cryptodev_get_cipher_algo_string(capa->cipher.algo);
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		return rte_cryptodev_get_aead_algo_string(capa->aead.algo);
+	default:
+		return NULL;
+	}
+}
+
+static void
+incrementing_generate(uint8_t *dst, uint8_t start, uint16_t size)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		dst[i] = start + i;
+}
+
+static void
+pattern_fill(uint8_t *input, const char *pattern, uint16_t size)
+{
+	size_t pattern_len = strlen(pattern);
+	size_t filled_len = 0, to_fill;
+
+	while (filled_len < size) {
+		to_fill = RTE_MIN(pattern_len, size - filled_len);
+		rte_memcpy(input, pattern, to_fill);
+		filled_len += to_fill;
+		input += to_fill;
+	}
+}
+
+static struct crosscheck_test_profile
+profile_create(const struct rte_cryptodev_symmetric_capability *capa,
+	       enum capability_select_type capability_type, size_t input_len)
+{
+	struct crosscheck_test_profile profile;
+
+	memset(&profile, 0, sizeof(profile));
+	profile.xform_type = capa->xform_type;
+
+	switch (capa->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		profile.block_size = capa->auth.block_size;
+		profile.algo = capa->auth.algo;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->auth.key_size.min;
+			profile.iv_size = capa->auth.iv_size.min;
+			profile.digest_size = capa->auth.digest_size.min;
+			profile.aad_size = capa->auth.aad_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->auth.key_size.max;
+			profile.iv_size = capa->auth.iv_size.max;
+			profile.digest_size = capa->auth.digest_size.max;
+			profile.aad_size = capa->auth.aad_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		profile.block_size = capa->cipher.block_size;
+		profile.algo = capa->cipher.algo;
+		profile.dataunit_set = capa->cipher.dataunit_set;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->cipher.key_size.min;
+			profile.iv_size = capa->cipher.iv_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->cipher.key_size.max;
+			profile.iv_size = capa->cipher.iv_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		profile.block_size = capa->aead.block_size;
+		profile.algo = capa->aead.algo;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->aead.key_size.min;
+			profile.iv_size = capa->aead.iv_size.min;
+			profile.digest_size = capa->aead.digest_size.min;
+			profile.aad_size = capa->aead.aad_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->aead.key_size.max;
+			profile.iv_size = capa->aead.iv_size.max;
+			profile.digest_size = capa->aead.digest_size.max;
+			profile.aad_size = capa->aead.aad_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	default:
+		rte_panic("Wrong xform profile type: %i\n", capa->xform_type);
+		break;
+	}
+
+	profile.input_buf_len = RTE_ALIGN_CEIL(input_len, profile.block_size);
+
+	snprintf(profile.name, MAX_TEST_STRING_LEN,
+			"'%s' - capabilities: '%s', input len: '%zu'",
+			algo_name_get(capa), capability_select_strings[capability_type],
+			input_len);
+
+	return profile;
+}
+
+static inline int
+common_range_set(struct rte_crypto_param_range *dst, const struct rte_crypto_param_range *src)
+{
+	/* Check if ranges overlaps */
+	if ((dst->min > src->max) && (dst->max < src->min))
+		return -1;
+	dst->min = RTE_MAX(dst->min, src->min);
+	dst->max = RTE_MIN(dst->max, src->max);
+
+	return 0;
+}
+
+static uint16_t
+nb_sym_capabilities_get(const struct rte_cryptodev_capabilities *cap)
+{
+	uint16_t nb_caps = 0;
+
+	for (; cap->op != RTE_CRYPTO_OP_TYPE_UNDEFINED; cap++) {
+		if (cap->op == RTE_CRYPTO_OP_TYPE_SYMMETRIC)
+			nb_caps += 1;
+	}
+
+	return nb_caps;
+}
+
+static struct rte_cryptodev_sym_capability_idx
+sym_capability_to_idx(const struct rte_cryptodev_symmetric_capability *cap)
+{
+	struct rte_cryptodev_sym_capability_idx cap_idx;
+
+	cap_idx.type = cap->xform_type;
+	switch (cap_idx.type) {
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		cap_idx.algo.auth = cap->auth.algo;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		cap_idx.algo.cipher = cap->cipher.algo;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		cap_idx.algo.aead = cap->aead.algo;
+		break;
+	default:
+		rte_panic("Wrong capability profile type: %i\n", cap_idx.type);
+		break;
+	}
+
+	return cap_idx;
+}
+
+/* Set the biggest common range for all capability fields */
+static int
+commmon_capability_set(struct rte_cryptodev_symmetric_capability *dst,
+		       const struct rte_cryptodev_symmetric_capability *src)
+{
+	switch (src->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		if (dst->auth.algo != src->auth.algo)
+			return -ENOENT;
+		if (dst->auth.block_size != src->auth.block_size)
+			return -ENOENT;
+		if (common_range_set(&dst->auth.key_size, &src->auth.key_size))
+			return -ENOENT;
+		if (common_range_set(&dst->auth.digest_size, &src->auth.digest_size))
+			return -ENOENT;
+		if (common_range_set(&dst->auth.aad_size, &src->auth.aad_size))
+			return -ENOENT;
+		if (common_range_set(&dst->auth.iv_size, &src->auth.iv_size))
+			return -ENOENT;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		if (dst->cipher.algo != src->cipher.algo)
+			return -ENOENT;
+		if (dst->cipher.block_size != src->cipher.block_size)
+			return -ENOENT;
+		if (common_range_set(&dst->cipher.key_size, &src->cipher.key_size))
+			return -ENOENT;
+		if (common_range_set(&dst->cipher.iv_size, &src->cipher.iv_size))
+			return -ENOENT;
+		if (dst->cipher.dataunit_set != src->cipher.dataunit_set)
+			return -ENOENT;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		if (dst->aead.algo != src->aead.algo)
+			return -ENOENT;
+		if (dst->aead.block_size != src->aead.block_size)
+			return -ENOENT;
+		if (common_range_set(&dst->aead.key_size, &src->aead.key_size))
+			return -ENOENT;
+		if (common_range_set(&dst->aead.digest_size, &src->aead.digest_size))
+			return -ENOENT;
+		if (common_range_set(&dst->aead.aad_size, &src->aead.aad_size))
+			return -ENOENT;
+		if (common_range_set(&dst->aead.iv_size, &src->aead.iv_size))
+			return -ENOENT;
+		break;
+	default:
+		RTE_LOG(ERR, USER1, "Unsupported xform_type!\n");
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static int
+capabilities_inspect(void)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	const struct rte_cryptodev_symmetric_capability *next_dev_cap;
+	struct rte_cryptodev_symmetric_capability common_cap;
+	struct rte_cryptodev_sym_capability_idx cap_idx;
+	const struct rte_cryptodev_capabilities *cap;
+	struct rte_cryptodev_info dev_info;
+	uint16_t nb_caps, cap_i = 0;
+	uint8_t cdev_id, i;
+
+	/* Get list of capabilities of first device */
+	cdev_id = ts_params->valid_devs[0];
+	rte_cryptodev_info_get(cdev_id, &dev_info);
+	cap = dev_info.capabilities;
+	nb_caps = nb_sym_capabilities_get(cap);
+	common_symm_capas = rte_calloc(NULL, nb_caps,
+				       sizeof(struct rte_cryptodev_symmetric_capability), 0);
+	if (common_symm_capas == NULL)
+		return -ENOMEM;
+
+	for (; cap->op != RTE_CRYPTO_OP_TYPE_UNDEFINED; cap++) {
+		/* Skip non symmetric capabilities */
+		if (cap->op != RTE_CRYPTO_OP_TYPE_SYMMETRIC)
+			continue;
+		/* AES_CCM requires special handling due to api requirements, skip now */
+		if (cap->sym.xform_type == RTE_CRYPTO_SYM_XFORM_AEAD &&
+				cap->sym.aead.algo == RTE_CRYPTO_AEAD_AES_CCM)
+			continue;
+
+		cap_idx = sym_capability_to_idx(&cap->sym);
+		common_cap = cap->sym;
+		for (i = 1; i < ts_params->valid_dev_count; i++) {
+			cdev_id = ts_params->valid_devs[i];
+			next_dev_cap = rte_cryptodev_sym_capability_get(cdev_id, &cap_idx);
+			/* Capability not supported by one of devs, skip */
+			if (next_dev_cap == NULL)
+				goto skip;
+			/* Check if capabilities have a common range of values */
+			if (commmon_capability_set(&common_cap, next_dev_cap) != 0)
+				goto skip;
+		}
+
+		/* If capability reach this point - it's support by all cryptodevs */
+		common_symm_capas[cap_i++] = common_cap;
+skip:;
+	}
+	nb_common_sym_caps = cap_i;
+
+	return 0;
+}
+
+static int
+crosscheck_init(void)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	const struct rte_cryptodev_symmetric_capability *cap;
+	const uint32_t nb_queue_pairs = 1;
+	struct rte_cryptodev_info info;
+	uint32_t session_priv_size = 0;
+	uint32_t nb_devs, dev_id;
+	uint8_t i;
+
+	memset(ts_params, 0, sizeof(*ts_params));
+
+	/* Create list of valid crypto devs */
+	nb_devs = rte_cryptodev_count();
+	for (dev_id = 0; dev_id < nb_devs; dev_id++) {
+		rte_cryptodev_info_get(dev_id, &info);
+
+		if (info.sym.max_nb_sessions != 0 && info.sym.max_nb_sessions < MAX_NB_SESSIONS)
+			continue;
+		if (info.max_nb_queue_pairs < nb_queue_pairs)
+			continue;
+		ts_params->valid_devs[ts_params->valid_dev_count++] = dev_id;
+		/* Obtaining configuration parameters, that will satisfy all cryptodevs */
+		session_priv_size = RTE_MAX(session_priv_size,
+					    rte_cryptodev_sym_get_private_session_size(dev_id));
+	}
+
+	if (ts_params->valid_dev_count < 2) {
+		RTE_LOG(WARNING, USER1, "Min number of cryptodevs for test is 2, found (%d)\n",
+			ts_params->valid_dev_count);
+		return TEST_SKIPPED;
+	}
+
+	/* Create pools for mbufs, crypto operations and sessions */
+	ts_params->mbuf_pool = rte_pktmbuf_pool_create("CRYPTO_MBUFPOOL", NUM_MBUFS,
+			MBUF_CACHE_SIZE, 0, MBUF_SIZE, rte_socket_id());
+	if (ts_params->mbuf_pool == NULL) {
+		RTE_LOG(ERR, USER1, "Can't create CRYPTO_MBUFPOOL\n");
+		return TEST_FAILED;
+	}
+
+	ts_params->op_mpool = rte_crypto_op_pool_create("MBUF_CRYPTO_SYM_OP_POOL",
+			RTE_CRYPTO_OP_TYPE_SYMMETRIC, NUM_MBUFS, MBUF_CACHE_SIZE,
+			DEFAULT_NUM_XFORMS * sizeof(struct rte_crypto_sym_xform) +
+			MAXIMUM_IV_LENGTH, rte_socket_id());
+
+	if (ts_params->op_mpool == NULL) {
+		RTE_LOG(ERR, USER1, "Can't create CRYPTO_OP_POOL\n");
+		return TEST_FAILED;
+	}
+
+	ts_params->session_mpool = rte_cryptodev_sym_session_pool_create("test_sess_mp",
+			MAX_NB_SESSIONS, session_priv_size, 0, 0, SOCKET_ID_ANY);
+	TEST_ASSERT_NOT_NULL(ts_params->session_mpool, "session mempool allocation failed");
+
+	/* Setup queue pair conf params */
+	ts_params->conf.nb_queue_pairs = nb_queue_pairs;
+	ts_params->conf.socket_id = SOCKET_ID_ANY;
+	ts_params->conf.ff_disable = RTE_CRYPTODEV_FF_SECURITY;
+	ts_params->qp_conf.nb_descriptors = MAX_NUM_OPS_INFLIGHT;
+	ts_params->qp_conf.mp_session = ts_params->session_mpool;
+
+	if (capabilities_inspect() != 0)
+		return TEST_FAILED;
+
+	/* Allocate memory based on max supported capabilities */
+	for (i = 0; i < nb_common_sym_caps; i++) {
+		cap = &common_symm_capas[i];
+		switch (cap->xform_type) {
+		case RTE_CRYPTO_SYM_XFORM_AUTH:
+			ts_params->key.len = RTE_MAX(ts_params->key.len, cap->auth.key_size.max);
+			ts_params->digest.len = RTE_MAX(ts_params->digest.len,
+							cap->auth.digest_size.max);
+			ts_params->aad.len = RTE_MAX(ts_params->aad.len, cap->auth.aad_size.max);
+			ts_params->iv.len = RTE_MAX(ts_params->iv.len, cap->auth.iv_size.max);
+			break;
+		case RTE_CRYPTO_SYM_XFORM_CIPHER:
+			ts_params->key.len = RTE_MAX(ts_params->key.len, cap->cipher.key_size.max);
+			ts_params->iv.len = RTE_MAX(ts_params->iv.len, cap->cipher.iv_size.max);
+			break;
+		case RTE_CRYPTO_SYM_XFORM_AEAD:
+			ts_params->key.len = RTE_MAX(ts_params->key.len, cap->aead.key_size.max);
+			ts_params->digest.len = RTE_MAX(ts_params->digest.len,
+							cap->aead.digest_size.max);
+			ts_params->aad.len = RTE_MAX(ts_params->aad.len, cap->aead.aad_size.max);
+			ts_params->iv.len = RTE_MAX(ts_params->iv.len, cap->aead.iv_size.max);
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", cap->xform_type);
+			break;
+		}
+	}
+
+	if (ts_params->key.len) {
+		ts_params->key.mem = rte_zmalloc(NULL, ts_params->key.len, 0);
+		TEST_ASSERT_NOT_NULL(ts_params->key.mem, "Key mem allocation failed\n");
+		pattern_fill(ts_params->key.mem, "*Secret key*", ts_params->key.len);
+	}
+	if (ts_params->digest.len) {
+		ts_params->digest.mem = rte_zmalloc(NULL, ts_params->digest.len, 16);
+		TEST_ASSERT_NOT_NULL(ts_params->digest.mem, "digest mem allocation failed\n");
+		ts_params->expected_digest.len = ts_params->digest.len;
+		ts_params->expected_digest.mem = rte_zmalloc(NULL, ts_params->digest.len, 0);
+		TEST_ASSERT_NOT_NULL(ts_params->expected_digest.mem,
+				     "Expected digest allocation failed\n");
+	}
+	if (ts_params->aad.len) {
+		ts_params->aad.mem = rte_zmalloc(NULL, ts_params->aad.len, 16);
+		TEST_ASSERT_NOT_NULL(ts_params->aad.mem, "aad mem allocation failed\n");
+		ts_params->expected_aad.len = ts_params->aad.len;
+		ts_params->expected_aad.mem = rte_zmalloc(NULL, ts_params->expected_aad.len, 0);
+		TEST_ASSERT_NOT_NULL(ts_params->expected_aad.mem,
+				     "Expected aad allocation failed\n");
+	}
+	if (ts_params->iv.len) {
+		ts_params->iv.mem = rte_zmalloc(NULL, ts_params->iv.len, 0);
+		TEST_ASSERT_NOT_NULL(ts_params->iv.mem, "iv mem allocation failed\n");
+		pattern_fill(ts_params->iv.mem, "IV", ts_params->iv.len);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static void
+crosscheck_fini(void)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+
+	rte_mempool_free(ts_params->mbuf_pool);
+	rte_mempool_free(ts_params->op_mpool);
+	rte_mempool_free(ts_params->session_mpool);
+	rte_free(ts_params->key.mem);
+	rte_free(ts_params->digest.mem);
+	rte_free(ts_params->aad.mem);
+	rte_free(ts_params->iv.mem);
+}
+
+static int
+dev_configure_and_start(uint64_t ff_disable)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	uint8_t i, dev_id;
+	uint16_t qp_id;
+
+	/* Reconfigure device to default parameters */
+	ts_params->conf.ff_disable = ff_disable;
+
+	/* Configure cryptodevs */
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		TEST_ASSERT_SUCCESS(rte_cryptodev_configure(dev_id, &ts_params->conf),
+				    "Failed to configure cryptodev %u with %u qps",
+				    dev_id, ts_params->conf.nb_queue_pairs);
+
+		for (qp_id = 0; qp_id < ts_params->conf.nb_queue_pairs; qp_id++) {
+			TEST_ASSERT_SUCCESS(rte_cryptodev_queue_pair_setup(
+				dev_id, qp_id, &ts_params->qp_conf,
+				rte_cryptodev_socket_id(dev_id)),
+				"Failed to setup queue pair %u on cryptodev %u",
+				qp_id, dev_id);
+		}
+		rte_cryptodev_stats_reset(dev_id);
+
+		/* Start the device */
+		TEST_ASSERT_SUCCESS(rte_cryptodev_start(dev_id), "Failed to start cryptodev %u",
+				    dev_id);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+crosscheck_suite_setup(void)
+{
+	dev_configure_and_start(RTE_CRYPTODEV_FF_SECURITY);
+
+	return 0;
+}
+
+static void
+crosscheck_suite_teardown(void)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	uint8_t i, dev_id;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		rte_cryptodev_stop(dev_id);
+	}
+}
+
+static struct rte_crypto_op *
+crypto_request_process(uint8_t dev_id, struct rte_crypto_op *op)
+{
+	struct rte_crypto_op *res = NULL;
+
+	if (rte_cryptodev_enqueue_burst(dev_id, 0, &op, 1) != 1) {
+		RTE_LOG(ERR, USER1, "Error sending packet for encryption\n");
+		return NULL;
+	}
+
+	while (rte_cryptodev_dequeue_burst(dev_id, 0, &res, 1) == 0)
+		rte_pause();
+
+	if (res->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
+		RTE_LOG(ERR, USER1, "Operation status %d\n", res->status);
+		return NULL;
+	}
+
+	if (res != op) {
+		RTE_LOG(ERR, USER1, "Unexpected operation received!\n");
+		rte_crypto_op_free(res);
+		return NULL;
+	}
+
+	return res;
+}
+
+static struct rte_cryptodev_sym_session*
+session_create(const struct crosscheck_test_profile *profile, uint8_t dev_id,
+	       enum crypto_op_type op_type)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	struct rte_cryptodev_sym_session *session;
+	struct rte_crypto_sym_xform xform;
+
+	memset(&xform, 0, sizeof(xform));
+
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		xform.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+		xform.next = NULL;
+		xform.auth.algo = profile->algo;
+		xform.auth.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_AUTH_OP_GENERATE :
+			RTE_CRYPTO_AUTH_OP_VERIFY;
+		xform.auth.digest_length = profile->digest_size;
+		xform.auth.key.length = profile->key_size;
+		xform.auth.key.data = ts_params->key.mem;
+		xform.auth.iv.length = profile->iv_size;
+		xform.auth.iv.offset = IV_OFFSET;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		xform.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+		xform.next = NULL;
+		xform.cipher.algo = profile->algo;
+		xform.cipher.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_CIPHER_OP_ENCRYPT :
+			RTE_CRYPTO_CIPHER_OP_DECRYPT;
+		xform.cipher.key.length = profile->key_size;
+		xform.cipher.key.data = ts_params->key.mem;
+		xform.cipher.iv.length = profile->iv_size;
+		xform.cipher.iv.offset = IV_OFFSET;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		xform.type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		xform.next = NULL;
+		xform.aead.algo = profile->algo;
+		xform.aead.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_AEAD_OP_ENCRYPT :
+			RTE_CRYPTO_AEAD_OP_DECRYPT;
+		xform.aead.digest_length = profile->digest_size;
+		xform.aead.key.length = profile->key_size;
+		xform.aead.key.data = ts_params->key.mem;
+		xform.aead.iv.length = profile->iv_size;
+		xform.aead.iv.offset = IV_OFFSET;
+		xform.aead.aad_length = profile->aad_size;
+		break;
+	default:
+		return NULL;
+	}
+
+	session = rte_cryptodev_sym_session_create(dev_id, &xform, testsuite_params.session_mpool);
+
+	return session;
+}
+
+static struct rte_mbuf*
+mbuf_create(const uint8_t *input_buf, uint16_t input_len)
+{
+	struct rte_mbuf *pkt;
+	uint8_t *pkt_data;
+
+	pkt = rte_pktmbuf_alloc(testsuite_params.mbuf_pool);
+	if (pkt == NULL) {
+		RTE_LOG(ERR, USER1,  "Failed to allocate input buffer in mempool");
+		return NULL;
+	}
+
+	/* zeroing tailroom */
+	memset(rte_pktmbuf_mtod(pkt, uint8_t *), 0, rte_pktmbuf_tailroom(pkt));
+
+	pkt_data = (uint8_t *)rte_pktmbuf_append(pkt, input_len);
+	if (pkt_data == NULL) {
+		RTE_LOG(ERR, USER1, "no room to append data, len: %d", input_len);
+		goto error;
+	}
+	rte_memcpy(pkt_data, input_buf, input_len);
+
+	return pkt;
+error:
+	rte_pktmbuf_free(pkt);
+	return NULL;
+}
+
+static struct rte_crypto_op*
+operation_create(const struct crosscheck_test_profile *profile,
+		 struct rte_mbuf *ibuf, enum crypto_op_type op_type)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	uint8_t *digest_data = NULL, *aad_data = NULL, *iv_ptr = NULL;
+	uint16_t aad_size, digest_size, plaintext_len;
+	struct rte_crypto_sym_op *sym_op;
+	struct rte_crypto_op *op;
+
+	op = rte_crypto_op_alloc(ts_params->op_mpool, RTE_CRYPTO_OP_TYPE_SYMMETRIC);
+	if (op == NULL) {
+		RTE_LOG(ERR, USER1, "Failed to allocate symmetric crypto operation struct");
+		return NULL;
+	}
+
+	plaintext_len = profile->input_buf_len;
+	aad_size = profile->aad_size;
+	digest_size = profile->digest_size;
+
+	if (aad_size) {
+		aad_data = ts_params->aad.mem;
+		if (op_type == OP_ENCRYPT)
+			pattern_fill(aad_data, "This is an aad.", aad_size);
+	}
+
+	if (digest_size) {
+		digest_data = ts_params->digest.mem;
+		if (op_type == OP_ENCRYPT)
+			memset(digest_data, 0, sizeof(digest_size));
+	}
+
+	sym_op = op->sym;
+	memset(sym_op, 0, sizeof(*sym_op));
+
+	iv_ptr = rte_crypto_op_ctod_offset(op, uint8_t *, IV_OFFSET);
+	rte_memcpy(iv_ptr, ts_params->iv.mem, profile->iv_size);
+
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		sym_op->auth.digest.data = digest_data;
+		sym_op->auth.digest.phys_addr = rte_malloc_virt2iova(sym_op->auth.digest.data);
+		sym_op->auth.data.length = plaintext_len;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		sym_op->cipher.data.length = plaintext_len;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		sym_op->aead.aad.data = aad_data;
+		sym_op->aead.aad.phys_addr = rte_malloc_virt2iova(sym_op->aead.aad.data);
+		sym_op->aead.digest.data = digest_data;
+		sym_op->aead.digest.phys_addr = rte_malloc_virt2iova(sym_op->aead.digest.data);
+		sym_op->aead.data.offset = 0;
+		sym_op->aead.data.length = plaintext_len;
+		break;
+	default:
+		goto error;
+	}
+
+	sym_op->m_src = ibuf;
+
+	return op;
+
+error:
+	rte_crypto_op_free(op);
+	return NULL;
+}
+
+static void
+mbuf_to_buf_copy(const struct rte_mbuf *m, uint8_t *res_buf, uint16_t *len)
+{
+	const uint8_t *out;
+
+	*len = m->pkt_len;
+	out = rte_pktmbuf_read(m, 0, *len, res_buf);
+	/* Single segment buffer */
+	if (out != res_buf)
+		memcpy(res_buf, out, *len);
+}
+
+static int
+single_dev_process(const struct crosscheck_test_profile *profile, uint16_t dev_id, enum
+		   crypto_op_type op_type, const uint8_t *input_buf, uint16_t input_len,
+		   uint8_t *output_buf, uint16_t *output_len)
+{
+	struct rte_cryptodev_sym_session *session = NULL;
+	struct rte_mbuf *ibuf = NULL, *obuf = NULL;
+	struct rte_crypto_op *op = NULL;
+	int ret = -1;
+
+	session = session_create(profile, dev_id, op_type);
+	if (session == NULL)
+		goto error;
+
+	ibuf = mbuf_create(input_buf, input_len);
+	if (ibuf == NULL)
+		goto error;
+
+	op = operation_create(profile, ibuf, op_type);
+	if (op == NULL)
+		goto error;
+
+	debug_hexdump(stdout, "Input:", rte_pktmbuf_mtod(ibuf, uint8_t*), ibuf->pkt_len);
+
+	rte_crypto_op_attach_sym_session(op, session);
+
+	struct rte_crypto_op *res = crypto_request_process(dev_id, op);
+	if (res == NULL)
+		goto error;
+
+	obuf = op->sym->m_src;
+	if (obuf == NULL) {
+		RTE_LOG(ERR, USER1, "Invalid packet received\n");
+		goto error;
+	}
+	mbuf_to_buf_copy(obuf, output_buf, output_len);
+
+	ret = 0;
+
+error:
+	if (session != NULL) {
+		int sret;
+		sret = rte_cryptodev_sym_session_free(dev_id, session);
+		RTE_VERIFY(sret == 0);
+	}
+	rte_pktmbuf_free(ibuf);
+	rte_crypto_op_free(op);
+	return ret;
+}
+
+static int
+buffers_compare(const uint8_t *expected, uint16_t expected_len,
+		const uint8_t *received, uint16_t received_len)
+{
+	TEST_ASSERT_EQUAL(expected_len, received_len, "Length mismatch %d != %d !\n",
+			  expected_len, received_len);
+
+	if (memcmp(expected, received, expected_len)) {
+		rte_hexdump(rte_log_get_stream(), "expected", expected, expected_len);
+		rte_hexdump(rte_log_get_stream(), "received", received, expected_len);
+		return TEST_FAILED;
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+crosscheck_all_devices(const struct crosscheck_test_profile *profile, enum crypto_op_type op_type,
+		       const uint8_t *input_text, uint16_t input_len, uint8_t *output_text,
+		       uint16_t *output_len)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	uint16_t len = 0, expected_len = 0;
+	uint8_t expected_text[MBUF_SIZE];
+	uint8_t i, dev_id;
+	int status;
+
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		status = single_dev_process(profile, dev_id, op_type, input_text, input_len,
+					    output_text, &len);
+		TEST_ASSERT_SUCCESS(status, "Error occurred during processing");
+
+		if (i == 0) {
+			/* First device, copy data for future comparisons */
+			memcpy(expected_text, output_text, len);
+			memcpy(ts_params->expected_digest.mem, ts_params->digest.mem,
+			       profile->digest_size);
+			memcpy(ts_params->expected_aad.mem, ts_params->aad.mem, profile->aad_size);
+			expected_len = len;
+		} else {
+			/* Compare output against expected(first) output */
+			TEST_ASSERT_SUCCESS(buffers_compare(expected_text, expected_len,
+					output_text, len),
+					"Text mismatch occurred on dev %i\n", dev_id);
+			TEST_ASSERT_SUCCESS(buffers_compare(ts_params->expected_digest.mem,
+					profile->digest_size, ts_params->digest.mem,
+					profile->digest_size),
+					"Digest mismatch occurred on dev %i\n", dev_id);
+			TEST_ASSERT_SUCCESS(buffers_compare(ts_params->expected_aad.mem,
+					profile->aad_size, ts_params->aad.mem, profile->aad_size),
+					"AAD mismatch occurred on dev %i\n", dev_id);
+		}
+
+		RTE_LOG(DEBUG, USER1, "DEV ID: %u finished processing\n", dev_id);
+		debug_hexdump(stdout, "Output: ", output_text, len);
+		if (profile->digest_size)
+			debug_hexdump(stdout, "Digest: ", ts_params->digest.mem,
+				      profile->digest_size);
+	}
+
+	*output_len = len;
+
+	return TEST_SUCCESS;
+}
+
+static int
+check_negative_all_devices(const struct crosscheck_test_profile *profile,
+			   enum crypto_op_type op_type, const uint8_t *input_text,
+			   uint16_t input_len)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+
+	uint8_t output_text[MBUF_SIZE];
+	uint8_t i, dev_id;
+	uint16_t len;
+	int status;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		status = single_dev_process(profile, dev_id, op_type, input_text, input_len,
+					    output_text, &len);
+		TEST_ASSERT_FAIL(status, "Error occurred during processing negative case");
+
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+crosscheck_with_profile_run(const struct crosscheck_test_profile *profile)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	uint8_t input_text[profile->input_buf_len];
+	uint16_t output_len, encrypted_len;
+	uint8_t encrypted_text[MBUF_SIZE];
+	uint8_t output_text[MBUF_SIZE];
+	int status;
+
+	memset(ts_params->digest.mem, 0, ts_params->digest.len);
+	memset(ts_params->aad.mem, 0, ts_params->aad.len);
+
+	/* Encrypt Stage */
+	RTE_LOG(DEBUG, USER1, "Executing encrypt stage\n");
+	/* Fill input with incrementing pattern */
+	incrementing_generate(input_text, 'a', profile->input_buf_len);
+	status = crosscheck_all_devices(profile, OP_ENCRYPT, input_text, profile->input_buf_len,
+					output_text, &output_len);
+	TEST_ASSERT_SUCCESS(status, "Error occurred during encryption");
+
+	/* Decrypt Stage */
+	RTE_LOG(DEBUG, USER1, "Executing decrypt stage\n");
+	/* Set up encrypted data as input */
+	encrypted_len = output_len;
+	memcpy(encrypted_text, output_text, output_len);
+	status = crosscheck_all_devices(profile, OP_DECRYPT, encrypted_text, encrypted_len,
+					output_text, &output_len);
+	TEST_ASSERT_SUCCESS(status, "Error occurred during decryption");
+
+	/* Negative Stage */
+	RTE_LOG(DEBUG, USER1, "Executing negative stage\n");
+	if (profile->digest_size) {
+		/* Corrupting one byte of digest */
+		ts_params->digest.mem[profile->digest_size - 1] += 1;
+		status = check_negative_all_devices(profile, OP_DECRYPT, encrypted_text,
+						    encrypted_len);
+		TEST_ASSERT_SUCCESS(status, "Error occurred during decryption");
+	}
+
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_crosscheck_unit(const void *ptr)
+{
+	const struct crosscheck_test_profile *profile = ptr;
+
+	if (profile->xform_type == RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED)
+		return TEST_SKIPPED;
+
+	return crosscheck_with_profile_run(profile);
+}
+
+static struct unit_test_suite*
+sym_unit_test_suite_create(const struct rte_cryptodev_symmetric_capability *capa)
+{
+	size_t uts_size, total_size, input_sz;
+	struct meta_test_suite *meta_ts;
+	const char *suite_prefix = NULL;
+	const char *algo_name = NULL;
+	struct unit_test_suite *uts;
+	uint64_t test_case_idx = 0;
+	struct unit_test_case *utc;
+	int cap_type;
+	char *mem;
+
+	const char * const suite_prefix_strings[] = {
+		[RTE_CRYPTO_SYM_XFORM_AUTH] = "Algo AUTH ",
+		[RTE_CRYPTO_SYM_XFORM_CIPHER] = "Algo CIPHER ",
+		[RTE_CRYPTO_SYM_XFORM_AEAD] = "Algo AEAD ",
+	};
+
+	suite_prefix = suite_prefix_strings[capa->xform_type];
+	algo_name = algo_name_get(capa);
+
+	/* Calculate size for test suite with all test cases +1 NULL case */
+	uts_size = sizeof(struct unit_test_suite) +
+		(NB_TEST_CASES_PER_ALGO + 1) * sizeof(struct unit_test_case);
+
+	/* Also allocate memory for suite meta data */
+	total_size = uts_size + sizeof(struct meta_test_suite);
+	mem = rte_zmalloc(NULL, total_size, 0);
+	if (mem == NULL)
+		return NULL;
+	uts = (struct unit_test_suite *) mem;
+	meta_ts = (struct meta_test_suite *) (mem + uts_size);
+
+	/* Initialize test suite */
+	snprintf(meta_ts->suite_name, MAX_TEST_STRING_LEN, "%s '%s'", suite_prefix, algo_name);
+	uts->suite_name = meta_ts->suite_name;
+
+	/* Initialize test cases */
+	for (cap_type = 0; cap_type < CAPABILITY_TYPE_LAST; cap_type++) {
+		for (input_sz = 0; input_sz < RTE_DIM(input_length); input_sz++) {
+			meta_ts->profile[test_case_idx] = profile_create(
+					capa, cap_type, input_length[input_sz]);
+			utc = &uts->unit_test_cases[test_case_idx];
+			utc->name = meta_ts->profile[test_case_idx].name;
+			utc->data = (const void *) &meta_ts->profile[test_case_idx];
+			utc->testcase_with_data = test_crosscheck_unit;
+			utc->enabled = true;
+
+			test_case_idx += 1;
+			RTE_VERIFY(test_case_idx <= NB_TEST_CASES_PER_ALGO);
+		}
+	}
+
+	return uts;
+}
+
+static int
+test_crosscheck(void)
+{
+	struct unit_test_suite **test_suites = NULL;
+	int ret, i;
+
+	static struct unit_test_suite ts = {
+		.suite_name = "Crosscheck Unit Test Suite",
+		.setup = crosscheck_suite_setup,
+		.teardown = crosscheck_suite_teardown,
+		.unit_test_cases = {TEST_CASES_END()}
+	};
+
+	ret = crosscheck_init();
+	if (ret)
+		goto exit;
+
+	if (nb_common_sym_caps == 0) {
+		RTE_LOG(WARNING, USER1, "Cryptodevs don't have common capabilities\n");
+		ret = TEST_SKIPPED;
+		goto exit;
+	}
+
+	/* + 1 for NULL-end suite */
+	test_suites = rte_calloc(NULL, nb_common_sym_caps + 1, sizeof(struct unit_test_suite *), 0);
+	TEST_ASSERT_NOT_NULL(test_suites, "test_suites allocation failed");
+
+	/* Create test suite for each supported algorithm */
+	ts.unit_test_suites = test_suites;
+	for (i = 0; i < nb_common_sym_caps; i++)
+		ts.unit_test_suites[i] = sym_unit_test_suite_create(&common_symm_capas[i]);
+
+	ret = unit_test_suite_runner(&ts);
+
+	for (i = 0; i < nb_common_sym_caps; i++)
+		rte_free(ts.unit_test_suites[i]);
+
+	rte_free(test_suites);
+
+exit:
+	crosscheck_fini();
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(cryptodev_crosscheck, test_crosscheck);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: [PATCH v2] test: add cryptodev crosscheck suite
  2023-03-08 17:30   ` [PATCH v2] " Power, Ciara
@ 2023-03-14 13:33     ` Volodymyr Fialko
  0 siblings, 0 replies; 11+ messages in thread
From: Volodymyr Fialko @ 2023-03-14 13:33 UTC (permalink / raw)
  To: Power, Ciara, dev, Akhil Goyal, Fan Zhang
  Cc: Jerin Jacob Kollanukkaran, Anoob Joseph, hemant.agrawal, Ji, Kai

Hi Ciara,

> -----Original Message-----
> From: Power, Ciara <ciara.power@intel.com>
> Sent: Wednesday, March 8, 2023 6:31 PM
> To: Volodymyr Fialko <vfialko@marvell.com>; dev@dpdk.org; Akhil Goyal <gakhil@marvell.com>; Fan
> Zhang <fanzhang.oss@gmail.com>
> Cc: Jerin Jacob Kollanukkaran <jerinj@marvell.com>; Anoob Joseph <anoobj@marvell.com>;
> hemant.agrawal@nxp.com; Ji, Kai <kai.ji@intel.com>
> Subject: [EXT] RE: [PATCH v2] test: add cryptodev crosscheck suite
> 
> External Email
> 
> ----------------------------------------------------------------------
> Hi Volodymyr,
> 
> 
> > -----Original Message-----
> > From: Volodymyr Fialko <vfialko@marvell.com>
> > Sent: Thursday 9 February 2023 12:34
> > To: dev@dpdk.org; Akhil Goyal <gakhil@marvell.com>; Fan Zhang
> > <fanzhang.oss@gmail.com>
> > Cc: jerinj@marvell.com; anoobj@marvell.com; hemant.agrawal@nxp.com;
> > Ji, Kai <kai.ji@intel.com>; Power, Ciara <ciara.power@intel.com>;
> > Volodymyr Fialko <vfialko@marvell.com>
> > Subject: [PATCH v2] test: add cryptodev crosscheck suite
> >
> > Add a validation test suite that helps in verifying that the output
> > generated by two different cryptodevs match for a wide range of input
> > parameter combinations.
> >
> > Crypto autotest performs a comprehensive testing of the cryptodev but
> > since it performs verification by comparing against known vectors, the
> > extend to which various parameters (like packet size) can be tested is
> > limited. This test suite attempts to simulate various cases by running
> > same test case on different cryptodevs and compares the output
> > generated. The test suite relies on capabilities to determine the
> > combinations of tests to be attempted.
> >
> > A typical use case would be to compare outputs generated from a
> > standard driver such as openSSL PMD and a new cryptodev PMD. This test
> > suite is to compliment the testing coverage that crypto autotest provides.
> >
> > Currently supported symmetric xforms(cipher, auth, aead) without chaining.
> >
> > Example command:
> > DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
> > 	-a <cryptodev> --vdev "crypto_openssl"
> >
> > Signed-off-by: Volodymyr Fialko <vfialko@marvell.com>
> > ---
> > V2:
> > - Updated commit message.
> >
> <snip>
> > +
> > +static void
> > +capabilities_inspect(void)
> > +{
> > +	struct rte_cryptodev_sym_capability_idx
> > cap_indexes[CRYPTO_ALGOS_LEN], *cap_idx;
> > +	struct crypto_testsuite_params *ts_params = &testsuite_params;
> > +	const struct rte_cryptodev_symmetric_capability *sym_capa;
> > +	struct rte_cryptodev_symmetric_capability *common_capa;
> > +	uint32_t algo, i, dev_id, caps_idx;
> > +
> > +	caps_idx = 0;
> > +	/* Create capability idx for known algorithms*/
> > +	for (algo = 1; algo <= CRYPTO_AUTH_MAX_IDX; algo++) {
> > +		cap_idx = &cap_indexes[caps_idx++];
> > +		cap_idx->type = RTE_CRYPTO_SYM_XFORM_AUTH;
> > +		cap_idx->algo.auth = algo;
> > +	}
> > +	for (algo = 1; algo <= CRYPTO_CIPHER_MAX_IDX; algo++) {
> > +		cap_idx = &cap_indexes[caps_idx++];
> > +		cap_idx->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
> > +		cap_idx->algo.cipher = algo;
> > +	}
> > +	for (algo = 1; algo <= CRYPTO_AEAD_MAX_IDX; algo++) {
> > +		cap_idx = &cap_indexes[caps_idx++];
> > +		cap_idx->type = RTE_CRYPTO_SYM_XFORM_AEAD;
> > +		cap_idx->algo.aead = algo;
> > +	}
> > +
> > +	for (caps_idx = 0; caps_idx < CRYPTO_ALGOS_LEN; caps_idx++) {
> > +		/* Gather common capabilities */
> > +		common_capa = &common_symm_capas[caps_idx];
> > +		common_capa->xform_type =
> > RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED;
> > +		for (i = 0; i < ts_params->valid_dev_count; i++) {
> > +			dev_id = ts_params->valid_devs[i];
> > +			sym_capa =
> > rte_cryptodev_sym_capability_get(dev_id,
> > +					&cap_indexes[caps_idx]);
> > +			if (sym_capa == NULL) {
> > +				/* Capability not supported by one of devs,
> > mark and skip */
> > +				goto next_algo;
> > +			}
> > +
> > +			if (common_capa->xform_type ==
> > RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED) {
> > +				/* First time initialization, copy data, go to
> > next device  */
> > +				*common_capa = *sym_capa;
> > +				continue;
> > +			}
> [CP]
> 
> This function - from what I understand after review,  is looping through all algorithms in DPDK crypto,
> and then checking if each is supported by each device?
> Could we instead just take the capabilities list from one device as the starting point - That will be the
> max list of capabilities, they will only get knocked out if not supported on the 2nd/3rd device etc.
> 
Agreed, that's a good idea. I was misled by the lack of an API like rte_cryptodev_sym_capability_get_all().
But after taking closer look, I found  that it's possible to retrieve all capabilities via dev_info. Reworked in V4.

> 
> <snip>
> 
> Some trials I did with this patch showed some issues.
> 
> 1. For QAT + AESNI_MB I get all skipped tests. I would have expected some common algs here.
> + ------------------------------------------------------- +
>  + Test Suite Summary : Crosscheck Unit Test Suite  + ------------------------------------------------------- +  +
> Algo AUTH  'null' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'aes-cbc-mac' :
> 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'aes-cmac' : 0/0 passed, 0/0
> skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'aes-gmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0
> unsupported  + Algo AUTH  'aes-xcbc-mac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  +
> Algo AUTH  'kasumi-f9' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'md5' : 0/0
> passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'md5-hmac' : 0/0 passed, 0/0 skipped,
> 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha1' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
> + Algo AUTH  'sha1-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha2-
> 224' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha2-224-hmac' : 0/0 passed,
> 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha2-256' : 0/0 passed, 0/0 skipped, 0/0 failed,
> 0/0 unsupported  + Algo AUTH  'sha2-256-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
> + Algo AUTH  'sha2-384' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha2-384-
> hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha2-512' : 0/0 passed, 0/0
> skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha2-512-hmac' : 0/0 passed, 0/0 skipped, 0/0
> failed, 0/0 unsupported  + Algo AUTH  'snow3g-uia2' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0
> unsupported  + Algo AUTH  'zuc-eia3' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo
> AUTH  'sha3-224' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha3-224-hmac' :
> 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha3-256' : 0/0 passed, 0/0
> skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha3-256-hmac' : 0/0 passed, 0/0 skipped, 0/0
> failed, 0/0 unsupported  + Algo AUTH  'sha3-384' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
> + Algo AUTH  'sha3-384-hmac' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH
> 'sha3-512' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo AUTH  'sha3-512-hmac' : 0/0
> passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER 'null' : 0/0 passed, 0/0 skipped, 0/0
> failed, 0/0 unsupported  + Algo CIPHER '3des-cbc' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0
> unsupported  + Algo CIPHER '3des-ctr' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo
> CIPHER '3des-ecb' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER 'aes-cbc' : 0/0
> passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER 'aes-ctr' : 0/0 passed, 0/0 skipped, 0/0
> failed, 0/0 unsupported  + Algo CIPHER 'aes-ecb' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
> + Algo CIPHER 'aes-f8' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER 'aes-xts' :
> 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER 'arc4' : 0/0 passed, 0/0 skipped,
> 0/0 failed, 0/0 unsupported  + Algo CIPHER 'kasumi-f8' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0
> unsupported  + Algo CIPHER 'snow3g-uea2' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  +
> Algo CIPHER 'zuc-eea3' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER 'des-cbc' :
> 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER 'aes-docsisbpi' : 0/0 passed, 0/0
> skipped, 0/0 failed, 0/0 unsupported  + Algo CIPHER 'des-docsisbpi' : 0/0 passed, 0/0 skipped, 0/0
> failed, 0/0 unsupported  + Algo AEAD 'aes-gcm' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported
> + Algo AEAD 'chacha20-poly1305' : 0/0 passed, 0/0 skipped, 0/0 failed, 0/0 unsupported  + ---------------
> ---------------------------------------- +
>  + Sub Testsuites Total :     46
>  + Sub Testsuites Skipped :   46
>  + Sub Testsuites Passed :     0
>  + Sub Testsuites Failed :     0
>  + ------------------------------------------------------- +
>  + Tests Total :        0
>  + Tests Skipped :      0
>  + Tests Executed :     0
>  + Tests Unsupported:   0
>  + Tests Passed :       0
>  + Tests Failed :       0
>  + ------------------------------------------------------- + Test Skipped
> 
> 
Unfortunately, I do not have access to QAT device to investigate this issue.
However, I was able to run AESNI_MB with openssl. Let me know if issue persists in the next version.

> 2. For 2 openssl devices I get 3 failures, which is curious as they are the same. Seems to be a space
> issue:
> USER1: Operation status 2
> USER1: No space for aad in single mbuf
> EAL: Test assert crosscheck_all_devices line 776 failed: Error occurred during processing
> EAL: Test assert crosscheck_with_profile_run line 841 failed: Error occurred during encryption
> 
Good catch. Instead of including everything in rte_mbuf, I separated aad/digest into their own memory allocations.
> 
> Thanks,
> Ciara

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v5] test: add cryptodev crosscheck suite
  2023-03-14 13:32     ` [PATCH v4] " Volodymyr Fialko
@ 2023-03-14 13:56       ` Volodymyr Fialko
  2023-03-16  9:44         ` Akhil Goyal
  0 siblings, 1 reply; 11+ messages in thread
From: Volodymyr Fialko @ 2023-03-14 13:56 UTC (permalink / raw)
  To: dev, Akhil Goyal, Fan Zhang
  Cc: jerinj, anoobj, hemant.agrawal, kai.ji, ciara.power, Volodymyr Fialko

Add a validation test suite that helps in verifying that the output
generated by two different cryptodevs match for a wide range of input
parameter combinations.

Crypto autotest performs a comprehensive testing of the cryptodev but since
it performs verification by comparing against known vectors, the extend to
which various parameters (like packet size) can be tested is limited. This
test suite attempts to simulate various cases by running same test case on
different cryptodevs and compares the output generated. The test suite
relies on capabilities to determine the combinations of tests to be
attempted.

A typical use case would be to compare outputs generated from a standard
driver such as openSSL PMD and a new cryptodev PMD. This test suite is to
compliment the testing coverage that crypto autotest provides.

Currently supported symmetric xforms(cipher, auth, aead) without chaining.

Example commands:
DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
	--vdev "crypto_openssl0" --vdev "crypto_openssl1"
DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
	-a <cryptodev> --vdev "crypto_openssl"

Signed-off-by: Volodymyr Fialko <vfialko@marvell.com>
---
V2:
- Updated commit message.
V3:
- Removed usage of deprecated crypto strings API.
V4:
- Reworked cryptodev capabilities scan procedure
- Moved AAD and digest from rte_mbuf to separated memory
- Released allocated memory
V5:
- Fixed coding style issues

 app/test/meson.build                 |    1 +
 app/test/test_cryptodev_crosscheck.c | 1050 ++++++++++++++++++++++++++
 2 files changed, 1051 insertions(+)
 create mode 100644 app/test/test_cryptodev_crosscheck.c

diff --git a/app/test/meson.build b/app/test/meson.build
index 1b9ce355f8..f0bcf99fb1 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -33,6 +33,7 @@ test_sources = files(
         'test_cryptodev.c',
         'test_cryptodev_asym.c',
         'test_cryptodev_blockcipher.c',
+        'test_cryptodev_crosscheck.c',
         'test_cryptodev_security_ipsec.c',
         'test_cryptodev_security_pdcp.c',
         'test_cycles.c',
diff --git a/app/test/test_cryptodev_crosscheck.c b/app/test/test_cryptodev_crosscheck.c
new file mode 100644
index 0000000000..c29b19cf6b
--- /dev/null
+++ b/app/test/test_cryptodev_crosscheck.c
@@ -0,0 +1,1050 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+#include <rte_cryptodev.h>
+#include <rte_malloc.h>
+
+#include "test.h"
+#include "test_cryptodev.h"
+
+#define MAX_NB_SESSIONS 1
+#define MAX_TEST_STRING_LEN 256
+
+/*
+ * The test suite will iterate through the capabilities of each probed cryptodev to identify the
+ * common ones. Once the common capabilities are determined, the test suite will generate potential
+ * valid inputs and crosscheck (compare) the output results from all cryptodevs.
+ */
+static struct rte_cryptodev_symmetric_capability *common_symm_capas;
+static uint16_t nb_common_sym_caps;
+
+/* Policies of capabilities selection */
+enum capability_select_type {
+	CAPABILITY_TYPE_MIN,
+	CAPABILITY_TYPE_MAX,
+	CAPABILITY_TYPE_LAST,
+};
+
+static const char * const capability_select_strings[] = {
+	[CAPABILITY_TYPE_MIN] = "MIN",
+	[CAPABILITY_TYPE_MAX] = "MAX",
+};
+
+/* Length of input text to be encrypted */
+static size_t input_length[] = { 64, 256, 512 };
+
+/* Calculate number of test cases(combinations) per algorithm */
+#define NB_TEST_CASES_PER_ALGO (CAPABILITY_TYPE_LAST * RTE_DIM(input_length))
+
+enum crypto_op_type {
+	OP_ENCRYPT,
+	OP_DECRYPT,
+};
+
+struct crosscheck_test_profile {
+	char name[MAX_TEST_STRING_LEN];
+	size_t input_buf_len;
+	enum rte_crypto_sym_xform_type xform_type;
+	int algo;
+	uint16_t block_size;
+	uint16_t key_size;
+	uint16_t iv_size;
+	uint16_t digest_size;
+	uint16_t aad_size;
+	uint32_t dataunit_set;
+};
+
+struct meta_test_suite {
+	char suite_name[MAX_TEST_STRING_LEN];
+	struct crosscheck_test_profile profile[NB_TEST_CASES_PER_ALGO];
+};
+
+struct memory_segment {
+	uint8_t *mem;
+	uint16_t len;
+};
+
+struct crosscheck_testsuite_params {
+	struct rte_mempool *mbuf_pool;
+	struct rte_mempool *op_mpool;
+	struct rte_mempool *session_mpool;
+	struct rte_cryptodev_config conf;
+	struct rte_cryptodev_qp_conf qp_conf;
+
+	uint8_t valid_devs[RTE_CRYPTO_MAX_DEVS];
+	uint8_t valid_dev_count;
+
+	struct memory_segment key;
+	struct memory_segment digest;
+	struct memory_segment aad;
+	struct memory_segment iv;
+
+	struct memory_segment expected_digest;
+	struct memory_segment expected_aad;
+};
+
+static struct crosscheck_testsuite_params testsuite_params;
+
+static const char*
+algo_name_get(const struct rte_cryptodev_symmetric_capability *capa)
+{
+	switch (capa->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		return rte_cryptodev_get_auth_algo_string(capa->auth.algo);
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		return rte_cryptodev_get_cipher_algo_string(capa->cipher.algo);
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		return rte_cryptodev_get_aead_algo_string(capa->aead.algo);
+	default:
+		return NULL;
+	}
+}
+
+static void
+incrementing_generate(uint8_t *dst, uint8_t start, uint16_t size)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		dst[i] = start + i;
+}
+
+static void
+pattern_fill(uint8_t *input, const char *pattern, uint16_t size)
+{
+	size_t pattern_len = strlen(pattern);
+	size_t filled_len = 0, to_fill;
+
+	while (filled_len < size) {
+		to_fill = RTE_MIN(pattern_len, size - filled_len);
+		rte_memcpy(input, pattern, to_fill);
+		filled_len += to_fill;
+		input += to_fill;
+	}
+}
+
+static struct crosscheck_test_profile
+profile_create(const struct rte_cryptodev_symmetric_capability *capa,
+	       enum capability_select_type capability_type, size_t input_len)
+{
+	struct crosscheck_test_profile profile;
+
+	memset(&profile, 0, sizeof(profile));
+	profile.xform_type = capa->xform_type;
+
+	switch (capa->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		profile.block_size = capa->auth.block_size;
+		profile.algo = capa->auth.algo;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->auth.key_size.min;
+			profile.iv_size = capa->auth.iv_size.min;
+			profile.digest_size = capa->auth.digest_size.min;
+			profile.aad_size = capa->auth.aad_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->auth.key_size.max;
+			profile.iv_size = capa->auth.iv_size.max;
+			profile.digest_size = capa->auth.digest_size.max;
+			profile.aad_size = capa->auth.aad_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		profile.block_size = capa->cipher.block_size;
+		profile.algo = capa->cipher.algo;
+		profile.dataunit_set = capa->cipher.dataunit_set;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->cipher.key_size.min;
+			profile.iv_size = capa->cipher.iv_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->cipher.key_size.max;
+			profile.iv_size = capa->cipher.iv_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		profile.block_size = capa->aead.block_size;
+		profile.algo = capa->aead.algo;
+
+		switch (capability_type) {
+		case CAPABILITY_TYPE_MIN:
+			profile.key_size = capa->aead.key_size.min;
+			profile.iv_size = capa->aead.iv_size.min;
+			profile.digest_size = capa->aead.digest_size.min;
+			profile.aad_size = capa->aead.aad_size.min;
+			break;
+		case CAPABILITY_TYPE_MAX:
+			profile.key_size = capa->aead.key_size.max;
+			profile.iv_size = capa->aead.iv_size.max;
+			profile.digest_size = capa->aead.digest_size.max;
+			profile.aad_size = capa->aead.aad_size.max;
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", capability_type);
+			break;
+		}
+		break;
+	default:
+		rte_panic("Wrong xform profile type: %i\n", capa->xform_type);
+		break;
+	}
+
+	profile.input_buf_len = RTE_ALIGN_CEIL(input_len, profile.block_size);
+
+	snprintf(profile.name, MAX_TEST_STRING_LEN,
+			"'%s' - capabilities: '%s', input len: '%zu'",
+			algo_name_get(capa), capability_select_strings[capability_type],
+			input_len);
+
+	return profile;
+}
+
+static inline int
+common_range_set(struct rte_crypto_param_range *dst, const struct rte_crypto_param_range *src)
+{
+	/* Check if ranges overlaps */
+	if ((dst->min > src->max) && (dst->max < src->min))
+		return -1;
+	dst->min = RTE_MAX(dst->min, src->min);
+	dst->max = RTE_MIN(dst->max, src->max);
+
+	return 0;
+}
+
+static uint16_t
+nb_sym_capabilities_get(const struct rte_cryptodev_capabilities *cap)
+{
+	uint16_t nb_caps = 0;
+
+	for (; cap->op != RTE_CRYPTO_OP_TYPE_UNDEFINED; cap++) {
+		if (cap->op == RTE_CRYPTO_OP_TYPE_SYMMETRIC)
+			nb_caps += 1;
+	}
+
+	return nb_caps;
+}
+
+static struct rte_cryptodev_sym_capability_idx
+sym_capability_to_idx(const struct rte_cryptodev_symmetric_capability *cap)
+{
+	struct rte_cryptodev_sym_capability_idx cap_idx;
+
+	cap_idx.type = cap->xform_type;
+	switch (cap_idx.type) {
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		cap_idx.algo.auth = cap->auth.algo;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		cap_idx.algo.cipher = cap->cipher.algo;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		cap_idx.algo.aead = cap->aead.algo;
+		break;
+	default:
+		rte_panic("Wrong capability profile type: %i\n", cap_idx.type);
+		break;
+	}
+
+	return cap_idx;
+}
+
+/* Set the biggest common range for all capability fields */
+static int
+common_capability_set(struct rte_cryptodev_symmetric_capability *dst,
+		       const struct rte_cryptodev_symmetric_capability *src)
+{
+	switch (src->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		if (dst->auth.algo != src->auth.algo)
+			return -ENOENT;
+		if (dst->auth.block_size != src->auth.block_size)
+			return -ENOENT;
+		if (common_range_set(&dst->auth.key_size, &src->auth.key_size))
+			return -ENOENT;
+		if (common_range_set(&dst->auth.digest_size, &src->auth.digest_size))
+			return -ENOENT;
+		if (common_range_set(&dst->auth.aad_size, &src->auth.aad_size))
+			return -ENOENT;
+		if (common_range_set(&dst->auth.iv_size, &src->auth.iv_size))
+			return -ENOENT;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		if (dst->cipher.algo != src->cipher.algo)
+			return -ENOENT;
+		if (dst->cipher.block_size != src->cipher.block_size)
+			return -ENOENT;
+		if (common_range_set(&dst->cipher.key_size, &src->cipher.key_size))
+			return -ENOENT;
+		if (common_range_set(&dst->cipher.iv_size, &src->cipher.iv_size))
+			return -ENOENT;
+		if (dst->cipher.dataunit_set != src->cipher.dataunit_set)
+			return -ENOENT;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		if (dst->aead.algo != src->aead.algo)
+			return -ENOENT;
+		if (dst->aead.block_size != src->aead.block_size)
+			return -ENOENT;
+		if (common_range_set(&dst->aead.key_size, &src->aead.key_size))
+			return -ENOENT;
+		if (common_range_set(&dst->aead.digest_size, &src->aead.digest_size))
+			return -ENOENT;
+		if (common_range_set(&dst->aead.aad_size, &src->aead.aad_size))
+			return -ENOENT;
+		if (common_range_set(&dst->aead.iv_size, &src->aead.iv_size))
+			return -ENOENT;
+		break;
+	default:
+		RTE_LOG(ERR, USER1, "Unsupported xform_type!\n");
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static int
+capabilities_inspect(void)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	const struct rte_cryptodev_symmetric_capability *next_dev_cap;
+	struct rte_cryptodev_symmetric_capability common_cap;
+	struct rte_cryptodev_sym_capability_idx cap_idx;
+	const struct rte_cryptodev_capabilities *cap;
+	struct rte_cryptodev_info dev_info;
+	uint16_t nb_caps, cap_i = 0;
+	uint8_t cdev_id, i;
+
+	/* Get list of capabilities of first device */
+	cdev_id = ts_params->valid_devs[0];
+	rte_cryptodev_info_get(cdev_id, &dev_info);
+	cap = dev_info.capabilities;
+	nb_caps = nb_sym_capabilities_get(cap);
+	common_symm_capas = rte_calloc(NULL, nb_caps,
+				       sizeof(struct rte_cryptodev_symmetric_capability), 0);
+	if (common_symm_capas == NULL)
+		return -ENOMEM;
+
+	for (; cap->op != RTE_CRYPTO_OP_TYPE_UNDEFINED; cap++) {
+		/* Skip non symmetric capabilities */
+		if (cap->op != RTE_CRYPTO_OP_TYPE_SYMMETRIC)
+			continue;
+		/* AES_CCM requires special handling due to api requirements, skip now */
+		if (cap->sym.xform_type == RTE_CRYPTO_SYM_XFORM_AEAD &&
+				cap->sym.aead.algo == RTE_CRYPTO_AEAD_AES_CCM)
+			continue;
+
+		cap_idx = sym_capability_to_idx(&cap->sym);
+		common_cap = cap->sym;
+		for (i = 1; i < ts_params->valid_dev_count; i++) {
+			cdev_id = ts_params->valid_devs[i];
+			next_dev_cap = rte_cryptodev_sym_capability_get(cdev_id, &cap_idx);
+			/* Capability not supported by one of devs, skip */
+			if (next_dev_cap == NULL)
+				goto skip;
+			/* Check if capabilities have a common range of values */
+			if (common_capability_set(&common_cap, next_dev_cap) != 0)
+				goto skip;
+		}
+
+		/* If capability reach this point - it's support by all cryptodevs */
+		common_symm_capas[cap_i++] = common_cap;
+skip:;
+	}
+	nb_common_sym_caps = cap_i;
+
+	return 0;
+}
+
+static int
+crosscheck_init(void)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	const struct rte_cryptodev_symmetric_capability *cap;
+	const uint32_t nb_queue_pairs = 1;
+	struct rte_cryptodev_info info;
+	uint32_t session_priv_size = 0;
+	uint32_t nb_devs, dev_id;
+	uint8_t i;
+
+	memset(ts_params, 0, sizeof(*ts_params));
+
+	/* Create list of valid crypto devs */
+	nb_devs = rte_cryptodev_count();
+	for (dev_id = 0; dev_id < nb_devs; dev_id++) {
+		rte_cryptodev_info_get(dev_id, &info);
+
+		if (info.sym.max_nb_sessions != 0 && info.sym.max_nb_sessions < MAX_NB_SESSIONS)
+			continue;
+		if (info.max_nb_queue_pairs < nb_queue_pairs)
+			continue;
+		ts_params->valid_devs[ts_params->valid_dev_count++] = dev_id;
+		/* Obtaining configuration parameters, that will satisfy all cryptodevs */
+		session_priv_size = RTE_MAX(session_priv_size,
+					    rte_cryptodev_sym_get_private_session_size(dev_id));
+	}
+
+	if (ts_params->valid_dev_count < 2) {
+		RTE_LOG(WARNING, USER1, "Min number of cryptodevs for test is 2, found (%d)\n",
+			ts_params->valid_dev_count);
+		return TEST_SKIPPED;
+	}
+
+	/* Create pools for mbufs, crypto operations and sessions */
+	ts_params->mbuf_pool = rte_pktmbuf_pool_create("CRYPTO_MBUFPOOL", NUM_MBUFS,
+			MBUF_CACHE_SIZE, 0, MBUF_SIZE, rte_socket_id());
+	if (ts_params->mbuf_pool == NULL) {
+		RTE_LOG(ERR, USER1, "Can't create CRYPTO_MBUFPOOL\n");
+		return TEST_FAILED;
+	}
+
+	ts_params->op_mpool = rte_crypto_op_pool_create("MBUF_CRYPTO_SYM_OP_POOL",
+			RTE_CRYPTO_OP_TYPE_SYMMETRIC, NUM_MBUFS, MBUF_CACHE_SIZE,
+			DEFAULT_NUM_XFORMS * sizeof(struct rte_crypto_sym_xform) +
+			MAXIMUM_IV_LENGTH, rte_socket_id());
+
+	if (ts_params->op_mpool == NULL) {
+		RTE_LOG(ERR, USER1, "Can't create CRYPTO_OP_POOL\n");
+		return TEST_FAILED;
+	}
+
+	ts_params->session_mpool = rte_cryptodev_sym_session_pool_create("test_sess_mp",
+			MAX_NB_SESSIONS, session_priv_size, 0, 0, SOCKET_ID_ANY);
+	TEST_ASSERT_NOT_NULL(ts_params->session_mpool, "session mempool allocation failed");
+
+	/* Setup queue pair conf params */
+	ts_params->conf.nb_queue_pairs = nb_queue_pairs;
+	ts_params->conf.socket_id = SOCKET_ID_ANY;
+	ts_params->conf.ff_disable = RTE_CRYPTODEV_FF_SECURITY;
+	ts_params->qp_conf.nb_descriptors = MAX_NUM_OPS_INFLIGHT;
+	ts_params->qp_conf.mp_session = ts_params->session_mpool;
+
+	if (capabilities_inspect() != 0)
+		return TEST_FAILED;
+
+	/* Allocate memory based on max supported capabilities */
+	for (i = 0; i < nb_common_sym_caps; i++) {
+		cap = &common_symm_capas[i];
+		switch (cap->xform_type) {
+		case RTE_CRYPTO_SYM_XFORM_AUTH:
+			ts_params->key.len = RTE_MAX(ts_params->key.len, cap->auth.key_size.max);
+			ts_params->digest.len = RTE_MAX(ts_params->digest.len,
+							cap->auth.digest_size.max);
+			ts_params->aad.len = RTE_MAX(ts_params->aad.len, cap->auth.aad_size.max);
+			ts_params->iv.len = RTE_MAX(ts_params->iv.len, cap->auth.iv_size.max);
+			break;
+		case RTE_CRYPTO_SYM_XFORM_CIPHER:
+			ts_params->key.len = RTE_MAX(ts_params->key.len, cap->cipher.key_size.max);
+			ts_params->iv.len = RTE_MAX(ts_params->iv.len, cap->cipher.iv_size.max);
+			break;
+		case RTE_CRYPTO_SYM_XFORM_AEAD:
+			ts_params->key.len = RTE_MAX(ts_params->key.len, cap->aead.key_size.max);
+			ts_params->digest.len = RTE_MAX(ts_params->digest.len,
+							cap->aead.digest_size.max);
+			ts_params->aad.len = RTE_MAX(ts_params->aad.len, cap->aead.aad_size.max);
+			ts_params->iv.len = RTE_MAX(ts_params->iv.len, cap->aead.iv_size.max);
+			break;
+		default:
+			rte_panic("Wrong capability profile type: %i\n", cap->xform_type);
+			break;
+		}
+	}
+
+	if (ts_params->key.len) {
+		ts_params->key.mem = rte_zmalloc(NULL, ts_params->key.len, 0);
+		TEST_ASSERT_NOT_NULL(ts_params->key.mem, "Key mem allocation failed\n");
+		pattern_fill(ts_params->key.mem, "*Secret key*", ts_params->key.len);
+	}
+	if (ts_params->digest.len) {
+		ts_params->digest.mem = rte_zmalloc(NULL, ts_params->digest.len, 16);
+		TEST_ASSERT_NOT_NULL(ts_params->digest.mem, "digest mem allocation failed\n");
+		ts_params->expected_digest.len = ts_params->digest.len;
+		ts_params->expected_digest.mem = rte_zmalloc(NULL, ts_params->digest.len, 0);
+		TEST_ASSERT_NOT_NULL(ts_params->expected_digest.mem,
+				     "Expected digest allocation failed\n");
+	}
+	if (ts_params->aad.len) {
+		ts_params->aad.mem = rte_zmalloc(NULL, ts_params->aad.len, 16);
+		TEST_ASSERT_NOT_NULL(ts_params->aad.mem, "aad mem allocation failed\n");
+		ts_params->expected_aad.len = ts_params->aad.len;
+		ts_params->expected_aad.mem = rte_zmalloc(NULL, ts_params->expected_aad.len, 0);
+		TEST_ASSERT_NOT_NULL(ts_params->expected_aad.mem,
+				     "Expected aad allocation failed\n");
+	}
+	if (ts_params->iv.len) {
+		ts_params->iv.mem = rte_zmalloc(NULL, ts_params->iv.len, 0);
+		TEST_ASSERT_NOT_NULL(ts_params->iv.mem, "iv mem allocation failed\n");
+		pattern_fill(ts_params->iv.mem, "IV", ts_params->iv.len);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static void
+crosscheck_fini(void)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+
+	rte_mempool_free(ts_params->mbuf_pool);
+	rte_mempool_free(ts_params->op_mpool);
+	rte_mempool_free(ts_params->session_mpool);
+	rte_free(ts_params->key.mem);
+	rte_free(ts_params->digest.mem);
+	rte_free(ts_params->aad.mem);
+	rte_free(ts_params->iv.mem);
+}
+
+static int
+dev_configure_and_start(uint64_t ff_disable)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	uint8_t i, dev_id;
+	uint16_t qp_id;
+
+	/* Reconfigure device to default parameters */
+	ts_params->conf.ff_disable = ff_disable;
+
+	/* Configure cryptodevs */
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		TEST_ASSERT_SUCCESS(rte_cryptodev_configure(dev_id, &ts_params->conf),
+				    "Failed to configure cryptodev %u with %u qps",
+				    dev_id, ts_params->conf.nb_queue_pairs);
+
+		for (qp_id = 0; qp_id < ts_params->conf.nb_queue_pairs; qp_id++) {
+			TEST_ASSERT_SUCCESS(rte_cryptodev_queue_pair_setup(
+				dev_id, qp_id, &ts_params->qp_conf,
+				rte_cryptodev_socket_id(dev_id)),
+				"Failed to setup queue pair %u on cryptodev %u",
+				qp_id, dev_id);
+		}
+		rte_cryptodev_stats_reset(dev_id);
+
+		/* Start the device */
+		TEST_ASSERT_SUCCESS(rte_cryptodev_start(dev_id), "Failed to start cryptodev %u",
+				    dev_id);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+crosscheck_suite_setup(void)
+{
+	dev_configure_and_start(RTE_CRYPTODEV_FF_SECURITY);
+
+	return 0;
+}
+
+static void
+crosscheck_suite_teardown(void)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	uint8_t i, dev_id;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		rte_cryptodev_stop(dev_id);
+	}
+}
+
+static struct rte_crypto_op *
+crypto_request_process(uint8_t dev_id, struct rte_crypto_op *op)
+{
+	struct rte_crypto_op *res = NULL;
+
+	if (rte_cryptodev_enqueue_burst(dev_id, 0, &op, 1) != 1) {
+		RTE_LOG(ERR, USER1, "Error sending packet for encryption\n");
+		return NULL;
+	}
+
+	while (rte_cryptodev_dequeue_burst(dev_id, 0, &res, 1) == 0)
+		rte_pause();
+
+	if (res->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
+		RTE_LOG(ERR, USER1, "Operation status %d\n", res->status);
+		return NULL;
+	}
+
+	if (res != op) {
+		RTE_LOG(ERR, USER1, "Unexpected operation received!\n");
+		rte_crypto_op_free(res);
+		return NULL;
+	}
+
+	return res;
+}
+
+static struct rte_cryptodev_sym_session*
+session_create(const struct crosscheck_test_profile *profile, uint8_t dev_id,
+	       enum crypto_op_type op_type)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	struct rte_cryptodev_sym_session *session;
+	struct rte_crypto_sym_xform xform;
+
+	memset(&xform, 0, sizeof(xform));
+
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		xform.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+		xform.next = NULL;
+		xform.auth.algo = profile->algo;
+		xform.auth.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_AUTH_OP_GENERATE :
+			RTE_CRYPTO_AUTH_OP_VERIFY;
+		xform.auth.digest_length = profile->digest_size;
+		xform.auth.key.length = profile->key_size;
+		xform.auth.key.data = ts_params->key.mem;
+		xform.auth.iv.length = profile->iv_size;
+		xform.auth.iv.offset = IV_OFFSET;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		xform.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+		xform.next = NULL;
+		xform.cipher.algo = profile->algo;
+		xform.cipher.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_CIPHER_OP_ENCRYPT :
+			RTE_CRYPTO_CIPHER_OP_DECRYPT;
+		xform.cipher.key.length = profile->key_size;
+		xform.cipher.key.data = ts_params->key.mem;
+		xform.cipher.iv.length = profile->iv_size;
+		xform.cipher.iv.offset = IV_OFFSET;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		xform.type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		xform.next = NULL;
+		xform.aead.algo = profile->algo;
+		xform.aead.op = op_type == OP_ENCRYPT ? RTE_CRYPTO_AEAD_OP_ENCRYPT :
+			RTE_CRYPTO_AEAD_OP_DECRYPT;
+		xform.aead.digest_length = profile->digest_size;
+		xform.aead.key.length = profile->key_size;
+		xform.aead.key.data = ts_params->key.mem;
+		xform.aead.iv.length = profile->iv_size;
+		xform.aead.iv.offset = IV_OFFSET;
+		xform.aead.aad_length = profile->aad_size;
+		break;
+	default:
+		return NULL;
+	}
+
+	session = rte_cryptodev_sym_session_create(dev_id, &xform, testsuite_params.session_mpool);
+
+	return session;
+}
+
+static struct rte_mbuf*
+mbuf_create(const uint8_t *input_buf, uint16_t input_len)
+{
+	struct rte_mbuf *pkt;
+	uint8_t *pkt_data;
+
+	pkt = rte_pktmbuf_alloc(testsuite_params.mbuf_pool);
+	if (pkt == NULL) {
+		RTE_LOG(ERR, USER1,  "Failed to allocate input buffer in mempool");
+		return NULL;
+	}
+
+	/* zeroing tailroom */
+	memset(rte_pktmbuf_mtod(pkt, uint8_t *), 0, rte_pktmbuf_tailroom(pkt));
+
+	pkt_data = (uint8_t *)rte_pktmbuf_append(pkt, input_len);
+	if (pkt_data == NULL) {
+		RTE_LOG(ERR, USER1, "no room to append data, len: %d", input_len);
+		goto error;
+	}
+	rte_memcpy(pkt_data, input_buf, input_len);
+
+	return pkt;
+error:
+	rte_pktmbuf_free(pkt);
+	return NULL;
+}
+
+static struct rte_crypto_op*
+operation_create(const struct crosscheck_test_profile *profile,
+		 struct rte_mbuf *ibuf, enum crypto_op_type op_type)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	uint8_t *digest_data = NULL, *aad_data = NULL, *iv_ptr = NULL;
+	uint16_t aad_size, digest_size, plaintext_len;
+	struct rte_crypto_sym_op *sym_op;
+	struct rte_crypto_op *op;
+
+	op = rte_crypto_op_alloc(ts_params->op_mpool, RTE_CRYPTO_OP_TYPE_SYMMETRIC);
+	if (op == NULL) {
+		RTE_LOG(ERR, USER1, "Failed to allocate symmetric crypto operation struct");
+		return NULL;
+	}
+
+	plaintext_len = profile->input_buf_len;
+	aad_size = profile->aad_size;
+	digest_size = profile->digest_size;
+
+	if (aad_size) {
+		aad_data = ts_params->aad.mem;
+		if (op_type == OP_ENCRYPT)
+			pattern_fill(aad_data, "This is an aad.", aad_size);
+	}
+
+	if (digest_size) {
+		digest_data = ts_params->digest.mem;
+		if (op_type == OP_ENCRYPT)
+			memset(digest_data, 0, sizeof(digest_size));
+	}
+
+	sym_op = op->sym;
+	memset(sym_op, 0, sizeof(*sym_op));
+
+	iv_ptr = rte_crypto_op_ctod_offset(op, uint8_t *, IV_OFFSET);
+	rte_memcpy(iv_ptr, ts_params->iv.mem, profile->iv_size);
+
+	switch (profile->xform_type) {
+	case RTE_CRYPTO_SYM_XFORM_AUTH:
+		sym_op->auth.digest.data = digest_data;
+		sym_op->auth.digest.phys_addr = rte_malloc_virt2iova(sym_op->auth.digest.data);
+		sym_op->auth.data.length = plaintext_len;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_CIPHER:
+		sym_op->cipher.data.length = plaintext_len;
+		break;
+	case RTE_CRYPTO_SYM_XFORM_AEAD:
+		sym_op->aead.aad.data = aad_data;
+		sym_op->aead.aad.phys_addr = rte_malloc_virt2iova(sym_op->aead.aad.data);
+		sym_op->aead.digest.data = digest_data;
+		sym_op->aead.digest.phys_addr = rte_malloc_virt2iova(sym_op->aead.digest.data);
+		sym_op->aead.data.offset = 0;
+		sym_op->aead.data.length = plaintext_len;
+		break;
+	default:
+		goto error;
+	}
+
+	sym_op->m_src = ibuf;
+
+	return op;
+
+error:
+	rte_crypto_op_free(op);
+	return NULL;
+}
+
+static void
+mbuf_to_buf_copy(const struct rte_mbuf *m, uint8_t *res_buf, uint16_t *len)
+{
+	const uint8_t *out;
+
+	*len = m->pkt_len;
+	out = rte_pktmbuf_read(m, 0, *len, res_buf);
+	/* Single segment buffer */
+	if (out != res_buf)
+		memcpy(res_buf, out, *len);
+}
+
+static int
+single_dev_process(const struct crosscheck_test_profile *profile, uint16_t dev_id, enum
+		   crypto_op_type op_type, const uint8_t *input_buf, uint16_t input_len,
+		   uint8_t *output_buf, uint16_t *output_len)
+{
+	struct rte_cryptodev_sym_session *session = NULL;
+	struct rte_mbuf *ibuf = NULL, *obuf = NULL;
+	struct rte_crypto_op *op = NULL;
+	int ret = -1;
+
+	session = session_create(profile, dev_id, op_type);
+	if (session == NULL)
+		goto error;
+
+	ibuf = mbuf_create(input_buf, input_len);
+	if (ibuf == NULL)
+		goto error;
+
+	op = operation_create(profile, ibuf, op_type);
+	if (op == NULL)
+		goto error;
+
+	debug_hexdump(stdout, "Input:", rte_pktmbuf_mtod(ibuf, uint8_t*), ibuf->pkt_len);
+
+	rte_crypto_op_attach_sym_session(op, session);
+
+	struct rte_crypto_op *res = crypto_request_process(dev_id, op);
+	if (res == NULL)
+		goto error;
+
+	obuf = op->sym->m_src;
+	if (obuf == NULL) {
+		RTE_LOG(ERR, USER1, "Invalid packet received\n");
+		goto error;
+	}
+	mbuf_to_buf_copy(obuf, output_buf, output_len);
+
+	ret = 0;
+
+error:
+	if (session != NULL) {
+		int sret;
+		sret = rte_cryptodev_sym_session_free(dev_id, session);
+		RTE_VERIFY(sret == 0);
+	}
+	rte_pktmbuf_free(ibuf);
+	rte_crypto_op_free(op);
+	return ret;
+}
+
+static int
+buffers_compare(const uint8_t *expected, uint16_t expected_len,
+		const uint8_t *received, uint16_t received_len)
+{
+	TEST_ASSERT_EQUAL(expected_len, received_len, "Length mismatch %d != %d !\n",
+			  expected_len, received_len);
+
+	if (memcmp(expected, received, expected_len)) {
+		rte_hexdump(rte_log_get_stream(), "expected", expected, expected_len);
+		rte_hexdump(rte_log_get_stream(), "received", received, expected_len);
+		return TEST_FAILED;
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+crosscheck_all_devices(const struct crosscheck_test_profile *profile, enum crypto_op_type op_type,
+		       const uint8_t *input_text, uint16_t input_len, uint8_t *output_text,
+		       uint16_t *output_len)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	uint16_t len = 0, expected_len = 0;
+	uint8_t expected_text[MBUF_SIZE];
+	uint8_t i, dev_id;
+	int status;
+
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		status = single_dev_process(profile, dev_id, op_type, input_text, input_len,
+					    output_text, &len);
+		TEST_ASSERT_SUCCESS(status, "Error occurred during processing");
+
+		if (i == 0) {
+			/* First device, copy data for future comparisons */
+			memcpy(expected_text, output_text, len);
+			memcpy(ts_params->expected_digest.mem, ts_params->digest.mem,
+			       profile->digest_size);
+			memcpy(ts_params->expected_aad.mem, ts_params->aad.mem, profile->aad_size);
+			expected_len = len;
+		} else {
+			/* Compare output against expected(first) output */
+			TEST_ASSERT_SUCCESS(buffers_compare(expected_text, expected_len,
+					output_text, len),
+					"Text mismatch occurred on dev %i\n", dev_id);
+			TEST_ASSERT_SUCCESS(buffers_compare(ts_params->expected_digest.mem,
+					profile->digest_size, ts_params->digest.mem,
+					profile->digest_size),
+					"Digest mismatch occurred on dev %i\n", dev_id);
+			TEST_ASSERT_SUCCESS(buffers_compare(ts_params->expected_aad.mem,
+					profile->aad_size, ts_params->aad.mem, profile->aad_size),
+					"AAD mismatch occurred on dev %i\n", dev_id);
+		}
+
+		RTE_LOG(DEBUG, USER1, "DEV ID: %u finished processing\n", dev_id);
+		debug_hexdump(stdout, "Output: ", output_text, len);
+		if (profile->digest_size)
+			debug_hexdump(stdout, "Digest: ", ts_params->digest.mem,
+				      profile->digest_size);
+	}
+
+	*output_len = len;
+
+	return TEST_SUCCESS;
+}
+
+static int
+check_negative_all_devices(const struct crosscheck_test_profile *profile,
+			   enum crypto_op_type op_type, const uint8_t *input_text,
+			   uint16_t input_len)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+
+	uint8_t output_text[MBUF_SIZE];
+	uint8_t i, dev_id;
+	uint16_t len;
+	int status;
+
+	for (i = 0; i < ts_params->valid_dev_count; i++) {
+		dev_id = ts_params->valid_devs[i];
+		status = single_dev_process(profile, dev_id, op_type, input_text, input_len,
+					    output_text, &len);
+		TEST_ASSERT_FAIL(status, "Error occurred during processing negative case");
+
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+crosscheck_with_profile_run(const struct crosscheck_test_profile *profile)
+{
+	struct crosscheck_testsuite_params *ts_params = &testsuite_params;
+	uint8_t input_text[profile->input_buf_len];
+	uint16_t output_len, encrypted_len;
+	uint8_t encrypted_text[MBUF_SIZE];
+	uint8_t output_text[MBUF_SIZE];
+	int status;
+
+	memset(ts_params->digest.mem, 0, ts_params->digest.len);
+	memset(ts_params->aad.mem, 0, ts_params->aad.len);
+
+	/* Encrypt Stage */
+	RTE_LOG(DEBUG, USER1, "Executing encrypt stage\n");
+	/* Fill input with incrementing pattern */
+	incrementing_generate(input_text, 'a', profile->input_buf_len);
+	status = crosscheck_all_devices(profile, OP_ENCRYPT, input_text, profile->input_buf_len,
+					output_text, &output_len);
+	TEST_ASSERT_SUCCESS(status, "Error occurred during encryption");
+
+	/* Decrypt Stage */
+	RTE_LOG(DEBUG, USER1, "Executing decrypt stage\n");
+	/* Set up encrypted data as input */
+	encrypted_len = output_len;
+	memcpy(encrypted_text, output_text, output_len);
+	status = crosscheck_all_devices(profile, OP_DECRYPT, encrypted_text, encrypted_len,
+					output_text, &output_len);
+	TEST_ASSERT_SUCCESS(status, "Error occurred during decryption");
+
+	/* Negative Stage */
+	RTE_LOG(DEBUG, USER1, "Executing negative stage\n");
+	if (profile->digest_size) {
+		/* Corrupting one byte of digest */
+		ts_params->digest.mem[profile->digest_size - 1] += 1;
+		status = check_negative_all_devices(profile, OP_DECRYPT, encrypted_text,
+						    encrypted_len);
+		TEST_ASSERT_SUCCESS(status, "Error occurred during decryption");
+	}
+
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_crosscheck_unit(const void *ptr)
+{
+	const struct crosscheck_test_profile *profile = ptr;
+
+	if (profile->xform_type == RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED)
+		return TEST_SKIPPED;
+
+	return crosscheck_with_profile_run(profile);
+}
+
+static struct unit_test_suite*
+sym_unit_test_suite_create(const struct rte_cryptodev_symmetric_capability *capa)
+{
+	size_t uts_size, total_size, input_sz;
+	struct meta_test_suite *meta_ts;
+	const char *suite_prefix = NULL;
+	const char *algo_name = NULL;
+	struct unit_test_suite *uts;
+	uint64_t test_case_idx = 0;
+	struct unit_test_case *utc;
+	int cap_type;
+	char *mem;
+
+	const char * const suite_prefix_strings[] = {
+		[RTE_CRYPTO_SYM_XFORM_AUTH] = "Algo AUTH ",
+		[RTE_CRYPTO_SYM_XFORM_CIPHER] = "Algo CIPHER ",
+		[RTE_CRYPTO_SYM_XFORM_AEAD] = "Algo AEAD ",
+	};
+
+	suite_prefix = suite_prefix_strings[capa->xform_type];
+	algo_name = algo_name_get(capa);
+
+	/* Calculate size for test suite with all test cases +1 NULL case */
+	uts_size = sizeof(struct unit_test_suite) +
+		(NB_TEST_CASES_PER_ALGO + 1) * sizeof(struct unit_test_case);
+
+	/* Also allocate memory for suite meta data */
+	total_size = uts_size + sizeof(struct meta_test_suite);
+	mem = rte_zmalloc(NULL, total_size, 0);
+	if (mem == NULL)
+		return NULL;
+	uts = (struct unit_test_suite *) mem;
+	meta_ts = (struct meta_test_suite *) (mem + uts_size);
+
+	/* Initialize test suite */
+	snprintf(meta_ts->suite_name, MAX_TEST_STRING_LEN, "%s '%s'", suite_prefix, algo_name);
+	uts->suite_name = meta_ts->suite_name;
+
+	/* Initialize test cases */
+	for (cap_type = 0; cap_type < CAPABILITY_TYPE_LAST; cap_type++) {
+		for (input_sz = 0; input_sz < RTE_DIM(input_length); input_sz++) {
+			meta_ts->profile[test_case_idx] = profile_create(
+					capa, cap_type, input_length[input_sz]);
+			utc = &uts->unit_test_cases[test_case_idx];
+			utc->name = meta_ts->profile[test_case_idx].name;
+			utc->data = (const void *) &meta_ts->profile[test_case_idx];
+			utc->testcase_with_data = test_crosscheck_unit;
+			utc->enabled = true;
+
+			test_case_idx += 1;
+			RTE_VERIFY(test_case_idx <= NB_TEST_CASES_PER_ALGO);
+		}
+	}
+
+	return uts;
+}
+
+static int
+test_crosscheck(void)
+{
+	struct unit_test_suite **test_suites = NULL;
+	int ret, i;
+
+	static struct unit_test_suite ts = {
+		.suite_name = "Crosscheck Unit Test Suite",
+		.setup = crosscheck_suite_setup,
+		.teardown = crosscheck_suite_teardown,
+		.unit_test_cases = {TEST_CASES_END()}
+	};
+
+	ret = crosscheck_init();
+	if (ret)
+		goto exit;
+
+	if (nb_common_sym_caps == 0) {
+		RTE_LOG(WARNING, USER1, "Cryptodevs don't have common capabilities\n");
+		ret = TEST_SKIPPED;
+		goto exit;
+	}
+
+	/* + 1 for NULL-end suite */
+	test_suites = rte_calloc(NULL, nb_common_sym_caps + 1, sizeof(struct unit_test_suite *), 0);
+	TEST_ASSERT_NOT_NULL(test_suites, "test_suites allocation failed");
+
+	/* Create test suite for each supported algorithm */
+	ts.unit_test_suites = test_suites;
+	for (i = 0; i < nb_common_sym_caps; i++)
+		ts.unit_test_suites[i] = sym_unit_test_suite_create(&common_symm_capas[i]);
+
+	ret = unit_test_suite_runner(&ts);
+
+	for (i = 0; i < nb_common_sym_caps; i++)
+		rte_free(ts.unit_test_suites[i]);
+
+	rte_free(test_suites);
+
+exit:
+	crosscheck_fini();
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(cryptodev_crosscheck, test_crosscheck);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: [PATCH v5] test: add cryptodev crosscheck suite
  2023-03-14 13:56       ` [PATCH v5] " Volodymyr Fialko
@ 2023-03-16  9:44         ` Akhil Goyal
  2023-03-16 15:46           ` Power, Ciara
  0 siblings, 1 reply; 11+ messages in thread
From: Akhil Goyal @ 2023-03-16  9:44 UTC (permalink / raw)
  To: Volodymyr Fialko, dev, Fan Zhang, ciara.power
  Cc: Jerin Jacob Kollanukkaran, Anoob Joseph, hemant.agrawal, kai.ji,
	Volodymyr Fialko


> Subject: [PATCH v5] test: add cryptodev crosscheck suite
> 
> Add a validation test suite that helps in verifying that the output
> generated by two different cryptodevs match for a wide range of input
> parameter combinations.
> 
> Crypto autotest performs a comprehensive testing of the cryptodev but since
> it performs verification by comparing against known vectors, the extend to
> which various parameters (like packet size) can be tested is limited. This
> test suite attempts to simulate various cases by running same test case on
> different cryptodevs and compares the output generated. The test suite
> relies on capabilities to determine the combinations of tests to be
> attempted.
> 
> A typical use case would be to compare outputs generated from a standard
> driver such as openSSL PMD and a new cryptodev PMD. This test suite is to
> compliment the testing coverage that crypto autotest provides.
> 
> Currently supported symmetric xforms(cipher, auth, aead) without chaining.
> 
> Example commands:
> DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
> 	--vdev "crypto_openssl0" --vdev "crypto_openssl1"
> DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
> 	-a <cryptodev> --vdev "crypto_openssl"
> 
> Signed-off-by: Volodymyr Fialko <vfialko@marvell.com>
> ---
> V2:
> - Updated commit message.
> V3:
> - Removed usage of deprecated crypto strings API.
> V4:
> - Reworked cryptodev capabilities scan procedure
> - Moved AAD and digest from rte_mbuf to separated memory
> - Released allocated memory
> V5:
> - Fixed coding style issues
Hi Ciara,

Can you check if this version works for you and ack if all good?

Regards,
Akhil

^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: [PATCH v5] test: add cryptodev crosscheck suite
  2023-03-16  9:44         ` Akhil Goyal
@ 2023-03-16 15:46           ` Power, Ciara
  2023-03-16 18:35             ` Akhil Goyal
  0 siblings, 1 reply; 11+ messages in thread
From: Power, Ciara @ 2023-03-16 15:46 UTC (permalink / raw)
  To: Akhil Goyal, Volodymyr Fialko, dev, Fan Zhang, Ji, Kai
  Cc: Jerin Jacob Kollanukkaran, Anoob Joseph, hemant.agrawal, Ji,
	 Kai, Volodymyr Fialko

Hi Akhil, Volodymyr

> -----Original Message-----
> From: Akhil Goyal <gakhil@marvell.com>
> Sent: Thursday 16 March 2023 09:45
> To: Volodymyr Fialko <vfialko@marvell.com>; dev@dpdk.org; Fan Zhang
> <fanzhang.oss@gmail.com>; Power, Ciara <ciara.power@intel.com>
> Cc: Jerin Jacob Kollanukkaran <jerinj@marvell.com>; Anoob Joseph
> <anoobj@marvell.com>; hemant.agrawal@nxp.com; Ji, Kai <kai.ji@intel.com>;
> Volodymyr Fialko <vfialko@marvell.com>
> Subject: RE: [PATCH v5] test: add cryptodev crosscheck suite
> 
> 
> > Subject: [PATCH v5] test: add cryptodev crosscheck suite
> >
> > Add a validation test suite that helps in verifying that the output
> > generated by two different cryptodevs match for a wide range of input
> > parameter combinations.
> >
> > Crypto autotest performs a comprehensive testing of the cryptodev but
> > since it performs verification by comparing against known vectors, the
> > extend to which various parameters (like packet size) can be tested is
> > limited. This test suite attempts to simulate various cases by running
> > same test case on different cryptodevs and compares the output
> > generated. The test suite relies on capabilities to determine the
> > combinations of tests to be attempted.
> >
> > A typical use case would be to compare outputs generated from a
> > standard driver such as openSSL PMD and a new cryptodev PMD. This test
> > suite is to compliment the testing coverage that crypto autotest provides.
> >
> > Currently supported symmetric xforms(cipher, auth, aead) without chaining.
> >
> > Example commands:
> > DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
> > 	--vdev "crypto_openssl0" --vdev "crypto_openssl1"
> > DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
> > 	-a <cryptodev> --vdev "crypto_openssl"
> >
> > Signed-off-by: Volodymyr Fialko <vfialko@marvell.com>
> > ---
> > V2:
> > - Updated commit message.
> > V3:
> > - Removed usage of deprecated crypto strings API.
> > V4:
> > - Reworked cryptodev capabilities scan procedure
> > - Moved AAD and digest from rte_mbuf to separated memory
> > - Released allocated memory
> > V5:
> > - Fixed coding style issues
> Hi Ciara,
> 
> Can you check if this version works for you and ack if all good?
> 
> Regards,
> Akhil

Thanks for those fixes in v4 - the openssl issues I saw previously are fixed now.

When I try openssl/QAT or aesni_mb/QAT, the tests skip with no common capabilities.
I think this might be an error on QAT side, that capabilities aren't retrieved correctly maybe.
Needs further debugging on our side for that, but we have a bank holiday in Ireland tomorrow so it may be delayed.

Happy to ack this patch anyway, seeing as the tests look good for the other devices I tested.

Thanks,
Acked-by: Ciara Power <ciara.power@intel.com>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: [PATCH v5] test: add cryptodev crosscheck suite
  2023-03-16 15:46           ` Power, Ciara
@ 2023-03-16 18:35             ` Akhil Goyal
  0 siblings, 0 replies; 11+ messages in thread
From: Akhil Goyal @ 2023-03-16 18:35 UTC (permalink / raw)
  To: Power, Ciara, Volodymyr Fialko, dev, Fan Zhang, Ji, Kai
  Cc: Jerin Jacob Kollanukkaran, Anoob Joseph, hemant.agrawal, Ji, Kai,
	Volodymyr Fialko

> Hi Akhil, Volodymyr
> 
> > Subject: RE: [PATCH v5] test: add cryptodev crosscheck suite
> >
> >
> > > Subject: [PATCH v5] test: add cryptodev crosscheck suite
> > >
> > > Add a validation test suite that helps in verifying that the output
> > > generated by two different cryptodevs match for a wide range of input
> > > parameter combinations.
> > >
> > > Crypto autotest performs a comprehensive testing of the cryptodev but
> > > since it performs verification by comparing against known vectors, the
> > > extend to which various parameters (like packet size) can be tested is
> > > limited. This test suite attempts to simulate various cases by running
> > > same test case on different cryptodevs and compares the output
> > > generated. The test suite relies on capabilities to determine the
> > > combinations of tests to be attempted.
> > >
> > > A typical use case would be to compare outputs generated from a
> > > standard driver such as openSSL PMD and a new cryptodev PMD. This test
> > > suite is to compliment the testing coverage that crypto autotest provides.
> > >
> > > Currently supported symmetric xforms(cipher, auth, aead) without chaining.
> > >
> > > Example commands:
> > > DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
> > > 	--vdev "crypto_openssl0" --vdev "crypto_openssl1"
> > > DPDK_TEST=cryptodev_crosscheck ./dpdk-test \
> > > 	-a <cryptodev> --vdev "crypto_openssl"
> > >
> > > Signed-off-by: Volodymyr Fialko <vfialko@marvell.com>
> > > ---
> > > V2:
> > > - Updated commit message.
> > > V3:
> > > - Removed usage of deprecated crypto strings API.
> > > V4:
> > > - Reworked cryptodev capabilities scan procedure
> > > - Moved AAD and digest from rte_mbuf to separated memory
> > > - Released allocated memory
> > > V5:
> > > - Fixed coding style issues
> > Hi Ciara,
> >
> > Can you check if this version works for you and ack if all good?
> >
> > Regards,
> > Akhil
> 
> Thanks for those fixes in v4 - the openssl issues I saw previously are fixed now.
> 
> When I try openssl/QAT or aesni_mb/QAT, the tests skip with no common
> capabilities.
> I think this might be an error on QAT side, that capabilities aren't retrieved
> correctly maybe.
> Needs further debugging on our side for that, but we have a bank holiday in
> Ireland tomorrow so it may be delayed.
> 
> Happy to ack this patch anyway, seeing as the tests look good for the other
> devices I tested.
> 
> Thanks,
> Acked-by: Ciara Power <ciara.power@intel.com>

Acked-by: Akhil Goyal <gakhil@marvell.com>

Applied to dpdk-next-crypto
Title updated to " test/crypto: add device crosscheck suite"

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2023-03-16 18:35 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-21 11:08 [PATCH] test: add cryptodev crosscheck suite Volodymyr Fialko
2023-02-09 12:34 ` [PATCH v2] " Volodymyr Fialko
2023-02-09 14:34   ` [PATCH v3] " Volodymyr Fialko
2023-03-14 13:32     ` [PATCH v4] " Volodymyr Fialko
2023-03-14 13:56       ` [PATCH v5] " Volodymyr Fialko
2023-03-16  9:44         ` Akhil Goyal
2023-03-16 15:46           ` Power, Ciara
2023-03-16 18:35             ` Akhil Goyal
2023-03-08 17:30   ` [PATCH v2] " Power, Ciara
2023-03-14 13:33     ` Volodymyr Fialko
2023-03-08 18:03   ` Power, Ciara

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).