diff mbox series

[v7,03/17] test/ring: add functional tests for rte_ring_xxx_elem APIs

Message ID 20191220044524.32910-4-honnappa.nagarahalli@arm.com
State New
Headers show
Series lib/ring: APIs to support custom element size | expand

Commit Message

Honnappa Nagarahalli Dec. 20, 2019, 4:45 a.m. UTC
Add basic infrastructure to test rte_ring_xxx_elem APIs. Add
test cases for testing burst and bulk tests.

Signed-off-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>

Reviewed-by: Gavin Hu <gavin.hu@arm.com>

---
 app/test/test_ring.c | 466 ++++++++++++++++++++-----------------------
 app/test/test_ring.h | 203 +++++++++++++++++++
 2 files changed, 419 insertions(+), 250 deletions(-)
 create mode 100644 app/test/test_ring.h

-- 
2.17.1

Comments

Ananyev, Konstantin Jan. 7, 2020, 4:03 p.m. UTC | #1
> > > Add basic infrastructure to test rte_ring_xxx_elem APIs. Add test

> > > cases for testing burst and bulk tests.

> > >

> > > Signed-off-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>

> > > Reviewed-by: Gavin Hu <gavin.hu@arm.com>

> > > ---

> > >  app/test/test_ring.c | 466

> > > ++++++++++++++++++++-----------------------

> > >  app/test/test_ring.h | 203 +++++++++++++++++++

> > >  2 files changed, 419 insertions(+), 250 deletions(-)  create mode

> > > 100644 app/test/test_ring.h

> > >

> > > diff --git a/app/test/test_ring.c b/app/test/test_ring.c index

> > > aaf1e70ad..e7a8b468b 100644

> > > --- a/app/test/test_ring.c

> > > +++ b/app/test/test_ring.c

> > > @@ -23,11 +23,13 @@

> > >  #include <rte_branch_prediction.h>

> > >  #include <rte_malloc.h>

> > >  #include <rte_ring.h>

> > > +#include <rte_ring_elem.h>

> > >  #include <rte_random.h>

> > >  #include <rte_errno.h>

> > >  #include <rte_hexdump.h>

> > >

> > >  #include "test.h"

> > > +#include "test_ring.h"

> > >

> > >  /*

> > >   * Ring

> > > @@ -67,6 +69,50 @@ static rte_atomic32_t synchro;

> > >

> > >  #define	TEST_RING_FULL_EMTPY_ITER	8

> > >

> > > +static int esize[] = {-1, 4, 8, 16};

> > > +

> > > +static void

> > > +test_ring_mem_init(void *obj, unsigned int count, int esize) {

> > > +	unsigned int i;

> > > +

> > > +	/* Legacy queue APIs? */

> > > +	if (esize == -1)

> > > +		for (i = 0; i < count; i++)

> > > +			((void **)obj)[i] = (void *)(unsigned long)i;

> > > +	else

> > > +		for (i = 0; i < (count * esize / sizeof(uint32_t)); i++)

> > > +			((uint32_t *)obj)[i] = i;

> > > +}

> > > +

> > > +static void

> > > +test_ring_print_test_string(const char *istr, unsigned int api_type,

> > > +int esize) {

> > > +	printf("\n%s: ", istr);

> > > +

> > > +	if (esize == -1)

> > > +		printf("legacy APIs: ");

> > > +	else

> > > +		printf("elem APIs: element size %dB ", esize);

> > > +

> > > +	if (api_type == TEST_RING_IGNORE_API_TYPE)

> > > +		return;

> > > +

> > > +	if ((api_type & TEST_RING_N) == TEST_RING_N)

> > > +		printf(": default enqueue/dequeue: ");

> > > +	else if ((api_type & TEST_RING_S) == TEST_RING_S)

> > > +		printf(": SP/SC: ");

> > > +	else if ((api_type & TEST_RING_M) == TEST_RING_M)

> > > +		printf(": MP/MC: ");

> > > +

> > > +	if ((api_type & TEST_RING_SL) == TEST_RING_SL)

> > > +		printf("single\n");

> > > +	else if ((api_type & TEST_RING_BL) == TEST_RING_BL)

> > > +		printf("bulk\n");

> > > +	else if ((api_type & TEST_RING_BR) == TEST_RING_BR)

> > > +		printf("burst\n");

> > > +}

> > > +

> > >  /*

> > >   * helper routine for test_ring_basic

> > >   */

> > > @@ -314,286 +360,203 @@ test_ring_basic(struct rte_ring *r)

> > >  	return -1;

> > >  }

> > >

> > > +/*

> > > + * Burst and bulk operations with sp/sc, mp/mc and default (during

> > > +creation)  */

> > >  static int

> > > -test_ring_burst_basic(struct rte_ring *r)

> > > +test_ring_burst_bulk_tests(unsigned int api_type)

> > >  {

> > > +	struct rte_ring *r;

> > >  	void **src = NULL, **cur_src = NULL, **dst = NULL, **cur_dst = NULL;

> > >  	int ret;

> > > -	unsigned i;

> > > +	unsigned int i, j;

> > > +	unsigned int num_elems;

> > >

> > > -	/* alloc dummy object pointers */

> > > -	src = malloc(RING_SIZE*2*sizeof(void *));

> > > -	if (src == NULL)

> > > -		goto fail;

> > > -

> > > -	for (i = 0; i < RING_SIZE*2 ; i++) {

> > > -		src[i] = (void *)(unsigned long)i;

> > > -	}

> > > -	cur_src = src;

> > > +	for (i = 0; i < RTE_DIM(esize); i++) {

> > > +		test_ring_print_test_string("Test standard ring", api_type,

> > > +						esize[i]);

> > >

> > > -	/* alloc some room for copied objects */

> > > -	dst = malloc(RING_SIZE*2*sizeof(void *));

> > > -	if (dst == NULL)

> > > -		goto fail;

> > > +		/* Create the ring */

> > > +		TEST_RING_CREATE("test_ring_burst_bulk_tests", esize[i],

> > > +					RING_SIZE, SOCKET_ID_ANY, 0, r);

> > >

> > > -	memset(dst, 0, RING_SIZE*2*sizeof(void *));

> > > -	cur_dst = dst;

> > > -

> > > -	printf("Test SP & SC basic functions \n");

> > > -	printf("enqueue 1 obj\n");

> > > -	ret = rte_ring_sp_enqueue_burst(r, cur_src, 1, NULL);

> > > -	cur_src += 1;

> > > -	if (ret != 1)

> > > -		goto fail;

> > > -

> > > -	printf("enqueue 2 objs\n");

> > > -	ret = rte_ring_sp_enqueue_burst(r, cur_src, 2, NULL);

> > > -	cur_src += 2;

> > > -	if (ret != 2)

> > > -		goto fail;

> > > -

> > > -	printf("enqueue MAX_BULK objs\n");

> > > -	ret = rte_ring_sp_enqueue_burst(r, cur_src, MAX_BULK, NULL);

> > > -	cur_src += MAX_BULK;

> > > -	if (ret != MAX_BULK)

> > > -		goto fail;

> > > -

> > > -	printf("dequeue 1 obj\n");

> > > -	ret = rte_ring_sc_dequeue_burst(r, cur_dst, 1, NULL);

> > > -	cur_dst += 1;

> > > -	if (ret != 1)

> > > -		goto fail;

> > > -

> > > -	printf("dequeue 2 objs\n");

> > > -	ret = rte_ring_sc_dequeue_burst(r, cur_dst, 2, NULL);

> > > -	cur_dst += 2;

> > > -	if (ret != 2)

> > > -		goto fail;

> > > +		/* alloc dummy object pointers */

> > > +		src = test_ring_calloc(RING_SIZE * 2, esize[i]);

> > > +		if (src == NULL)

> > > +			goto fail;

> > > +		test_ring_mem_init(src, RING_SIZE * 2, esize[i]);

> > > +		cur_src = src;

> > >

> > > -	printf("dequeue MAX_BULK objs\n");

> > > -	ret = rte_ring_sc_dequeue_burst(r, cur_dst, MAX_BULK, NULL);

> > > -	cur_dst += MAX_BULK;

> > > -	if (ret != MAX_BULK)

> > > -		goto fail;

> > > +		/* alloc some room for copied objects */

> > > +		dst = test_ring_calloc(RING_SIZE * 2, esize[i]);

> > > +		if (dst == NULL)

> > > +			goto fail;

> > > +		cur_dst = dst;

> > >

> > > -	/* check data */

> > > -	if (memcmp(src, dst, cur_dst - dst)) {

> > > -		rte_hexdump(stdout, "src", src, cur_src - src);

> > > -		rte_hexdump(stdout, "dst", dst, cur_dst - dst);

> > > -		printf("data after dequeue is not the same\n");

> > > -		goto fail;

> > > -	}

> > > +		printf("enqueue 1 obj\n");

> > > +		TEST_RING_ENQUEUE(r, cur_src, esize[i], 1, ret, api_type);

> > > +		if (ret != 1)

> > > +			goto fail;

> > > +		TEST_RING_INCP(cur_src, esize[i], 1);

> > >

> > > -	cur_src = src;

> > > -	cur_dst = dst;

> > > +		printf("enqueue 2 objs\n");

> > > +		TEST_RING_ENQUEUE(r, cur_src, esize[i], 2, ret, api_type);

> > > +		if (ret != 2)

> > > +			goto fail;

> > > +		TEST_RING_INCP(cur_src, esize[i], 2);

> > >

> > > -	printf("Test enqueue without enough memory space \n");

> > > -	for (i = 0; i< (RING_SIZE/MAX_BULK - 1); i++) {

> > > -		ret = rte_ring_sp_enqueue_burst(r, cur_src, MAX_BULK,

> > NULL);

> > > -		cur_src += MAX_BULK;

> > > +		printf("enqueue MAX_BULK objs\n");

> > > +		TEST_RING_ENQUEUE(r, cur_src, esize[i], MAX_BULK, ret,

> > > +						api_type);

> > >  		if (ret != MAX_BULK)

> > >  			goto fail;

> > > -	}

> > > -

> > > -	printf("Enqueue 2 objects, free entries = MAX_BULK - 2  \n");

> > > -	ret = rte_ring_sp_enqueue_burst(r, cur_src, 2, NULL);

> > > -	cur_src += 2;

> > > -	if (ret != 2)

> > > -		goto fail;

> > > +		TEST_RING_INCP(cur_src, esize[i], MAX_BULK);

> > >

> > > -	printf("Enqueue the remaining entries = MAX_BULK - 2  \n");

> > > -	/* Always one free entry left */

> > > -	ret = rte_ring_sp_enqueue_burst(r, cur_src, MAX_BULK, NULL);

> > > -	cur_src += MAX_BULK - 3;

> > > -	if (ret != MAX_BULK - 3)

> > > -		goto fail;

> > > -

> > > -	printf("Test if ring is full  \n");

> > > -	if (rte_ring_full(r) != 1)

> > > -		goto fail;

> > > +		printf("dequeue 1 obj\n");

> > > +		TEST_RING_DEQUEUE(r, cur_dst, esize[i], 1, ret, api_type);

> > > +		if (ret != 1)

> > > +			goto fail;

> > > +		TEST_RING_INCP(cur_dst, esize[i], 1);

> > >

> > > -	printf("Test enqueue for a full entry  \n");

> > > -	ret = rte_ring_sp_enqueue_burst(r, cur_src, MAX_BULK, NULL);

> > > -	if (ret != 0)

> > > -		goto fail;

> > > +		printf("dequeue 2 objs\n");

> > > +		TEST_RING_DEQUEUE(r, cur_dst, esize[i], 2, ret, api_type);

> > > +		if (ret != 2)

> > > +			goto fail;

> > > +		TEST_RING_INCP(cur_dst, esize[i], 2);

> > >

> > > -	printf("Test dequeue without enough objects \n");

> > > -	for (i = 0; i<RING_SIZE/MAX_BULK - 1; i++) {

> > > -		ret = rte_ring_sc_dequeue_burst(r, cur_dst, MAX_BULK,

> > NULL);

> > > -		cur_dst += MAX_BULK;

> > > +		printf("dequeue MAX_BULK objs\n");

> > > +		TEST_RING_DEQUEUE(r, cur_dst, esize[i], MAX_BULK, ret,

> > > +						api_type);

> > >  		if (ret != MAX_BULK)

> > >  			goto fail;

> > > -	}

> > > -

> > > -	/* Available memory space for the exact MAX_BULK entries */

> > > -	ret = rte_ring_sc_dequeue_burst(r, cur_dst, 2, NULL);

> > > -	cur_dst += 2;

> > > -	if (ret != 2)

> > > -		goto fail;

> > > -

> > > -	ret = rte_ring_sc_dequeue_burst(r, cur_dst, MAX_BULK, NULL);

> > > -	cur_dst += MAX_BULK - 3;

> > > -	if (ret != MAX_BULK - 3)

> > > -		goto fail;

> > > -

> > > -	printf("Test if ring is empty \n");

> > > -	/* Check if ring is empty */

> > > -	if (1 != rte_ring_empty(r))

> > > -		goto fail;

> > > -

> > > -	/* check data */

> > > -	if (memcmp(src, dst, cur_dst - dst)) {

> > > -		rte_hexdump(stdout, "src", src, cur_src - src);

> > > -		rte_hexdump(stdout, "dst", dst, cur_dst - dst);

> > > -		printf("data after dequeue is not the same\n");

> > > -		goto fail;

> > > -	}

> > > +		TEST_RING_INCP(cur_dst, esize[i], MAX_BULK);

> > >

> > > -	cur_src = src;

> > > -	cur_dst = dst;

> > > -

> > > -	printf("Test MP & MC basic functions \n");

> > > -

> > > -	printf("enqueue 1 obj\n");

> > > -	ret = rte_ring_mp_enqueue_burst(r, cur_src, 1, NULL);

> > > -	cur_src += 1;

> > > -	if (ret != 1)

> > > -		goto fail;

> > > -

> > > -	printf("enqueue 2 objs\n");

> > > -	ret = rte_ring_mp_enqueue_burst(r, cur_src, 2, NULL);

> > > -	cur_src += 2;

> > > -	if (ret != 2)

> > > -		goto fail;

> > > -

> > > -	printf("enqueue MAX_BULK objs\n");

> > > -	ret = rte_ring_mp_enqueue_burst(r, cur_src, MAX_BULK, NULL);

> > > -	cur_src += MAX_BULK;

> > > -	if (ret != MAX_BULK)

> > > -		goto fail;

> > > -

> > > -	printf("dequeue 1 obj\n");

> > > -	ret = rte_ring_mc_dequeue_burst(r, cur_dst, 1, NULL);

> > > -	cur_dst += 1;

> > > -	if (ret != 1)

> > > -		goto fail;

> > > -

> > > -	printf("dequeue 2 objs\n");

> > > -	ret = rte_ring_mc_dequeue_burst(r, cur_dst, 2, NULL);

> > > -	cur_dst += 2;

> > > -	if (ret != 2)

> > > -		goto fail;

> > > -

> > > -	printf("dequeue MAX_BULK objs\n");

> > > -	ret = rte_ring_mc_dequeue_burst(r, cur_dst, MAX_BULK, NULL);

> > > -	cur_dst += MAX_BULK;

> > > -	if (ret != MAX_BULK)

> > > -		goto fail;

> > > -

> > > -	/* check data */

> > > -	if (memcmp(src, dst, cur_dst - dst)) {

> > > -		rte_hexdump(stdout, "src", src, cur_src - src);

> > > -		rte_hexdump(stdout, "dst", dst, cur_dst - dst);

> > > -		printf("data after dequeue is not the same\n");

> > > -		goto fail;

> > > -	}

> > > -

> > > -	cur_src = src;

> > > -	cur_dst = dst;

> > > +		/* check data */

> > > +		if (memcmp(src, dst, cur_dst - dst)) {

> > > +			rte_hexdump(stdout, "src", src, cur_src - src);

> > > +			rte_hexdump(stdout, "dst", dst, cur_dst - dst);

> > > +			printf("data after dequeue is not the same\n");

> > > +			goto fail;

> > > +		}

> > > +

> > > +		cur_src = src;

> > > +		cur_dst = dst;

> > > +

> > > +		printf("fill and empty the ring\n");

> > > +		for (j = 0; j < RING_SIZE / MAX_BULK; j++) {

> > > +			TEST_RING_ENQUEUE(r, cur_src, esize[i], MAX_BULK,

> > > +							ret, api_type);

> > > +			if (ret != MAX_BULK)

> > > +				goto fail;

> > > +			TEST_RING_INCP(cur_src, esize[i], MAX_BULK);

> > > +

> > > +			TEST_RING_DEQUEUE(r, cur_dst, esize[i], MAX_BULK,

> > > +							ret, api_type);

> > > +			if (ret != MAX_BULK)

> > > +				goto fail;

> > > +			TEST_RING_INCP(cur_dst, esize[i], MAX_BULK);

> > > +		}

> > >

> > > -	printf("fill and empty the ring\n");

> > > -	for (i = 0; i<RING_SIZE/MAX_BULK; i++) {

> > > -		ret = rte_ring_mp_enqueue_burst(r, cur_src, MAX_BULK,

> > NULL);

> > > -		cur_src += MAX_BULK;

> > > -		if (ret != MAX_BULK)

> > > +		/* check data */

> > > +		if (memcmp(src, dst, cur_dst - dst)) {

> > > +			rte_hexdump(stdout, "src", src, cur_src - src);

> > > +			rte_hexdump(stdout, "dst", dst, cur_dst - dst);

> > > +			printf("data after dequeue is not the same\n");

> > >  			goto fail;

> > > -		ret = rte_ring_mc_dequeue_burst(r, cur_dst, MAX_BULK,

> > NULL);

> > > -		cur_dst += MAX_BULK;

> > > -		if (ret != MAX_BULK)

> > > +		}

> > > +

> > > +		cur_src = src;

> > > +		cur_dst = dst;

> > > +

> > > +		printf("Test enqueue without enough memory space\n");

> > > +		for (j = 0; j < (RING_SIZE/MAX_BULK - 1); j++) {

> > > +			TEST_RING_ENQUEUE(r, cur_src, esize[i], MAX_BULK,

> > > +							ret, api_type);

> > > +			if (ret != MAX_BULK)

> > > +				goto fail;

> > > +			TEST_RING_INCP(cur_src, esize[i], MAX_BULK);

> > > +		}

> > > +

> > > +		printf("Enqueue 2 objects, free entries = MAX_BULK - 2\n");

> > > +		TEST_RING_ENQUEUE(r, cur_src, esize[i], 2, ret, api_type);

> > > +		if (ret != 2)

> > >  			goto fail;

> > > -	}

> > > -

> > > -	/* check data */

> > > -	if (memcmp(src, dst, cur_dst - dst)) {

> > > -		rte_hexdump(stdout, "src", src, cur_src - src);

> > > -		rte_hexdump(stdout, "dst", dst, cur_dst - dst);

> > > -		printf("data after dequeue is not the same\n");

> > > -		goto fail;

> > > -	}

> > > -

> > > -	cur_src = src;

> > > -	cur_dst = dst;

> > > -

> > > -	printf("Test enqueue without enough memory space \n");

> > > -	for (i = 0; i<RING_SIZE/MAX_BULK - 1; i++) {

> > > -		ret = rte_ring_mp_enqueue_burst(r, cur_src, MAX_BULK,

> > NULL);

> > > -		cur_src += MAX_BULK;

> > > -		if (ret != MAX_BULK)

> > > +		TEST_RING_INCP(cur_src, esize[i], 2);

> > > +

> > > +

> > > +		printf("Enqueue the remaining entries = MAX_BULK - 3\n");

> > > +		/* Bulk APIs enqueue exact number of elements */

> > > +		if ((api_type & TEST_RING_BL) == TEST_RING_BL)

> > > +			num_elems = MAX_BULK - 3;

> > > +		else

> > > +			num_elems = MAX_BULK;

> > > +		/* Always one free entry left */

> > > +		TEST_RING_ENQUEUE(r, cur_src, esize[i], num_elems,

> > > +						ret, api_type);

> > > +		if (ret != MAX_BULK - 3)

> > >  			goto fail;

> > > -	}

> > > -

> > > -	/* Available memory space for the exact MAX_BULK objects */

> > > -	ret = rte_ring_mp_enqueue_burst(r, cur_src, 2, NULL);

> > > -	cur_src += 2;

> > > -	if (ret != 2)

> > > -		goto fail;

> > > +		TEST_RING_INCP(cur_src, esize[i], MAX_BULK - 3);

> > >

> > > -	ret = rte_ring_mp_enqueue_burst(r, cur_src, MAX_BULK, NULL);

> > > -	cur_src += MAX_BULK - 3;

> > > -	if (ret != MAX_BULK - 3)

> > > -		goto fail;

> > > +		printf("Test if ring is full\n");

> > > +		if (rte_ring_full(r) != 1)

> > > +			goto fail;

> > >

> > > +		printf("Test enqueue for a full entry\n");

> > > +		TEST_RING_ENQUEUE(r, cur_src, esize[i], MAX_BULK,

> > > +						ret, api_type);

> > > +		if (ret != 0)

> > > +			goto fail;

> > >

> > > -	printf("Test dequeue without enough objects \n");

> > > -	for (i = 0; i<RING_SIZE/MAX_BULK - 1; i++) {

> > > -		ret = rte_ring_mc_dequeue_burst(r, cur_dst, MAX_BULK,

> > NULL);

> > > -		cur_dst += MAX_BULK;

> > > -		if (ret != MAX_BULK)

> > > +		printf("Test dequeue without enough objects\n");

> > > +		for (j = 0; j < RING_SIZE / MAX_BULK - 1; j++) {

> > > +			TEST_RING_DEQUEUE(r, cur_dst, esize[i], MAX_BULK,

> > > +							ret, api_type);

> > > +			if (ret != MAX_BULK)

> > > +				goto fail;

> > > +			TEST_RING_INCP(cur_dst, esize[i], MAX_BULK);

> > > +		}

> > > +

> > > +		/* Available memory space for the exact MAX_BULK entries

> > */

> > > +		TEST_RING_DEQUEUE(r, cur_dst, esize[i], 2, ret, api_type);

> > > +		if (ret != 2)

> > >  			goto fail;

> > > -	}

> > > +		TEST_RING_INCP(cur_dst, esize[i], 2);

> > > +

> > > +		/* Bulk APIs enqueue exact number of elements */

> > > +		if ((api_type & TEST_RING_BL) == TEST_RING_BL)

> > > +			num_elems = MAX_BULK - 3;

> > > +		else

> > > +			num_elems = MAX_BULK;

> > > +		TEST_RING_DEQUEUE(r, cur_dst, esize[i], num_elems,

> > > +						ret, api_type);

> > > +		if (ret != MAX_BULK - 3)

> > > +			goto fail;

> > > +		TEST_RING_INCP(cur_dst, esize[i], MAX_BULK - 3);

> > >

> > > -	/* Available objects - the exact MAX_BULK */

> > > -	ret = rte_ring_mc_dequeue_burst(r, cur_dst, 2, NULL);

> > > -	cur_dst += 2;

> > > -	if (ret != 2)

> > > -		goto fail;

> > > +		printf("Test if ring is empty\n");

> > > +		/* Check if ring is empty */

> > > +		if (rte_ring_empty(r) != 1)

> > > +			goto fail;

> > >

> > > -	ret = rte_ring_mc_dequeue_burst(r, cur_dst, MAX_BULK, NULL);

> > > -	cur_dst += MAX_BULK - 3;

> > > -	if (ret != MAX_BULK - 3)

> > > -		goto fail;

> > > +		/* check data */

> > > +		if (memcmp(src, dst, cur_dst - dst)) {

> > > +			rte_hexdump(stdout, "src", src, cur_src - src);

> > > +			rte_hexdump(stdout, "dst", dst, cur_dst - dst);

> > > +			printf("data after dequeue is not the same\n");

> > > +			goto fail;

> > > +		}

> > >

> > > -	/* check data */

> > > -	if (memcmp(src, dst, cur_dst - dst)) {

> > > -		rte_hexdump(stdout, "src", src, cur_src - src);

> > > -		rte_hexdump(stdout, "dst", dst, cur_dst - dst);

> > > -		printf("data after dequeue is not the same\n");

> > > -		goto fail;

> > > +		/* Free memory before test completed */

> > > +		rte_ring_free(r);

> > > +		rte_free(src);

> > > +		rte_free(dst);

> > >  	}

> > >

> > > -	cur_src = src;

> > > -	cur_dst = dst;

> > > -

> > > -	printf("Covering rte_ring_enqueue_burst functions \n");

> > > -

> > > -	ret = rte_ring_enqueue_burst(r, cur_src, 2, NULL);

> > > -	cur_src += 2;

> > > -	if (ret != 2)

> > > -		goto fail;

> > > -

> > > -	ret = rte_ring_dequeue_burst(r, cur_dst, 2, NULL);

> > > -	cur_dst += 2;

> > > -	if (ret != 2)

> > > -		goto fail;

> > > -

> > > -	/* Free memory before test completed */

> > > -	free(src);

> > > -	free(dst);

> > >  	return 0;

> > > -

> > > - fail:

> > > -	free(src);

> > > -	free(dst);

> > > +fail:

> > > +	rte_ring_free(r);

> > > +	rte_free(src);

> > > +	rte_free(dst);

> > >  	return -1;

> > >  }

> > >

> > > @@ -810,6 +773,7 @@ test_ring_with_exact_size(void)  static int

> > >  test_ring(void)

> > >  {

> > > +	unsigned int i, j;

> > >  	struct rte_ring *r = NULL;

> > >

> > >  	/* some more basic operations */

> > > @@ -828,9 +792,11 @@ test_ring(void)

> > >  		goto test_fail;

> > >  	}

> > >

> > > -	/* burst operations */

> > > -	if (test_ring_burst_basic(r) < 0)

> > > -		goto test_fail;

> > > +	/* Burst and bulk operations with sp/sc, mp/mc and default */

> > > +	for (j = TEST_RING_BL; j <= TEST_RING_BR; j <<= 1)

> > > +		for (i = TEST_RING_N; i <= TEST_RING_M; i <<= 1)

> > > +			if (test_ring_burst_bulk_tests(i | j) < 0)

> > > +				goto test_fail;

> > >

> > >  	/* basic operations */

> > >  	if (test_ring_basic(r) < 0)

> > > diff --git a/app/test/test_ring.h b/app/test/test_ring.h new file mode

> > > 100644 index 000000000..19ef1b399

> > > --- /dev/null

> > > +++ b/app/test/test_ring.h

> > > @@ -0,0 +1,203 @@

> > > +/* SPDX-License-Identifier: BSD-3-Clause

> > > + * Copyright(c) 2019 Arm Limited

> > > + */

> > > +

> > > +#include <rte_malloc.h>

> > > +#include <rte_ring.h>

> > > +#include <rte_ring_elem.h>

> > > +

> > > +/* API type to call

> > > + * N - Calls default APIs

> > > + * S - Calls SP or SC API

> > > + * M - Calls MP or MC API

> > > + */

> > > +#define TEST_RING_N 1

> > > +#define TEST_RING_S 2

> > > +#define TEST_RING_M 4

> > > +

> > > +/* API type to call

> > > + * SL - Calls single element APIs

> > > + * BL - Calls bulk APIs

> > > + * BR - Calls burst APIs

> > > + */

> > > +#define TEST_RING_SL 8

> > > +#define TEST_RING_BL 16

> > > +#define TEST_RING_BR 32

> > > +

> > > +#define TEST_RING_IGNORE_API_TYPE ~0U

> > > +

> > > +#define TEST_RING_INCP(obj, esize, n) do { \

> > > +	/* Legacy queue APIs? */ \

> > > +	if ((esize) == -1) \

> > > +		obj = ((void **)obj) + n; \

> > > +	else \

> > > +		obj = (void **)(((uint32_t *)obj) + \

> > > +					(n * esize / sizeof(uint32_t))); \ }

> > while (0)

> > > +

> > > +#define TEST_RING_CREATE(name, esize, count, socket_id, flags, r) do { \

> > > +	/* Legacy queue APIs? */ \

> > > +	if ((esize) == -1) \

> > > +		r = rte_ring_create((name), (count), (socket_id), (flags)); \

> > > +	else \

> > > +		r = rte_ring_create_elem((name), (esize), (count), \

> > > +						(socket_id), (flags)); \

> > > +} while (0)

> > > +

> > > +#define TEST_RING_ENQUEUE(r, obj, esize, n, ret, api_type) do { \

> > > +	/* Legacy queue APIs? */ \

> > > +	if ((esize) == -1) \

> > > +		switch (api_type) { \

> > > +		case (TEST_RING_N | TEST_RING_SL): \

> > > +			ret = rte_ring_enqueue(r, obj); \

> > > +			break; \

> > > +		case (TEST_RING_S | TEST_RING_SL): \

> > > +			ret = rte_ring_sp_enqueue(r, obj); \

> > > +			break; \

> > > +		case (TEST_RING_M | TEST_RING_SL): \

> > > +			ret = rte_ring_mp_enqueue(r, obj); \

> > > +			break; \

> > > +		case (TEST_RING_N | TEST_RING_BL): \

> > > +			ret = rte_ring_enqueue_bulk(r, obj, n, NULL); \

> > > +			break; \

> > > +		case (TEST_RING_S | TEST_RING_BL): \

> > > +			ret = rte_ring_sp_enqueue_bulk(r, obj, n, NULL); \

> > > +			break; \

> > > +		case (TEST_RING_M | TEST_RING_BL): \

> > > +			ret = rte_ring_mp_enqueue_bulk(r, obj, n, NULL); \

> > > +			break; \

> > > +		case (TEST_RING_N | TEST_RING_BR): \

> > > +			ret = rte_ring_enqueue_burst(r, obj, n, NULL); \

> > > +			break; \

> > > +		case (TEST_RING_S | TEST_RING_BR): \

> > > +			ret = rte_ring_sp_enqueue_burst(r, obj, n, NULL); \

> > > +			break; \

> > > +		case (TEST_RING_M | TEST_RING_BR): \

> > > +			ret = rte_ring_mp_enqueue_burst(r, obj, n, NULL); \

> > > +		} \

> > > +	else \

> > > +		switch (api_type) { \

> > > +		case (TEST_RING_N | TEST_RING_SL): \

> > > +			ret = rte_ring_enqueue_elem(r, obj, esize); \

> > > +			break; \

> > > +		case (TEST_RING_S | TEST_RING_SL): \

> > > +			ret = rte_ring_sp_enqueue_elem(r, obj, esize); \

> > > +			break; \

> > > +		case (TEST_RING_M | TEST_RING_SL): \

> > > +			ret = rte_ring_mp_enqueue_elem(r, obj, esize); \

> > > +			break; \

> > > +		case (TEST_RING_N | TEST_RING_BL): \

> > > +			ret = rte_ring_enqueue_bulk_elem(r, obj, esize, n, \

> > > +								NULL); \

> > > +			break; \

> > > +		case (TEST_RING_S | TEST_RING_BL): \

> > > +			ret = rte_ring_sp_enqueue_bulk_elem(r, obj, esize, n,

> > \

> > > +								NULL); \

> > > +			break; \

> > > +		case (TEST_RING_M | TEST_RING_BL): \

> > > +			ret = rte_ring_mp_enqueue_bulk_elem(r, obj, esize, n,

> > \

> > > +								NULL); \

> > > +			break; \

> > > +		case (TEST_RING_N | TEST_RING_BR): \

> > > +			ret = rte_ring_enqueue_burst_elem(r, obj, esize, n, \

> > > +								NULL); \

> > > +			break; \

> > > +		case (TEST_RING_S | TEST_RING_BR): \

> > > +			ret = rte_ring_sp_enqueue_burst_elem(r, obj, esize, n,

> > \

> > > +								NULL); \

> > > +			break; \

> > > +		case (TEST_RING_M | TEST_RING_BR): \

> > > +			ret = rte_ring_mp_enqueue_burst_elem(r, obj, esize,

> > n, \

> > > +								NULL); \

> > > +		} \

> > > +} while (0)

> > > +

> > > +#define TEST_RING_DEQUEUE(r, obj, esize, n, ret, api_type) do { \

> > > +	/* Legacy queue APIs? */ \

> > > +	if ((esize) == -1) \

> > > +		switch (api_type) { \

> > > +		case (TEST_RING_N | TEST_RING_SL): \

> > > +			ret = rte_ring_dequeue(r, obj); \

> > > +			break; \

> > > +		case (TEST_RING_S | TEST_RING_SL): \

> > > +			ret = rte_ring_sc_dequeue(r, obj); \

> > > +			break; \

> > > +		case (TEST_RING_M | TEST_RING_SL): \

> > > +			ret = rte_ring_mc_dequeue(r, obj); \

> > > +			break; \

> > > +		case (TEST_RING_N | TEST_RING_BL): \

> > > +			ret = rte_ring_dequeue_bulk(r, obj, n, NULL); \

> > > +			break; \

> > > +		case (TEST_RING_S | TEST_RING_BL): \

> > > +			ret = rte_ring_sc_dequeue_bulk(r, obj, n, NULL); \

> > > +			break; \

> > > +		case (TEST_RING_M | TEST_RING_BL): \

> > > +			ret = rte_ring_mc_dequeue_bulk(r, obj, n, NULL); \

> > > +			break; \

> > > +		case (TEST_RING_N | TEST_RING_BR): \

> > > +			ret = rte_ring_dequeue_burst(r, obj, n, NULL); \

> > > +			break; \

> > > +		case (TEST_RING_S | TEST_RING_BR): \

> > > +			ret = rte_ring_sc_dequeue_burst(r, obj, n, NULL); \

> > > +			break; \

> > > +		case (TEST_RING_M | TEST_RING_BR): \

> > > +			ret = rte_ring_mc_dequeue_burst(r, obj, n, NULL); \

> > > +		} \

> > > +	else \

> > > +		switch (api_type) { \

> > > +		case (TEST_RING_N | TEST_RING_SL): \

> > > +			ret = rte_ring_dequeue_elem(r, obj, esize); \

> > > +			break; \

> > > +		case (TEST_RING_S | TEST_RING_SL): \

> > > +			ret = rte_ring_sc_dequeue_elem(r, obj, esize); \

> > > +			break; \

> > > +		case (TEST_RING_M | TEST_RING_SL): \

> > > +			ret = rte_ring_mc_dequeue_elem(r, obj, esize); \

> > > +			break; \

> > > +		case (TEST_RING_N | TEST_RING_BL): \

> > > +			ret = rte_ring_dequeue_bulk_elem(r, obj, esize, n, \

> > > +								NULL); \

> > > +			break; \

> > > +		case (TEST_RING_S | TEST_RING_BL): \

> > > +			ret = rte_ring_sc_dequeue_bulk_elem(r, obj, esize, n,

> > \

> > > +								NULL); \

> > > +			break; \

> > > +		case (TEST_RING_M | TEST_RING_BL): \

> > > +			ret = rte_ring_mc_dequeue_bulk_elem(r, obj, esize, n,

> > \

> > > +								NULL); \

> > > +			break; \

> > > +		case (TEST_RING_N | TEST_RING_BR): \

> > > +			ret = rte_ring_dequeue_burst_elem(r, obj, esize, n, \

> > > +								NULL); \

> > > +			break; \

> > > +		case (TEST_RING_S | TEST_RING_BR): \

> > > +			ret = rte_ring_sc_dequeue_burst_elem(r, obj, esize, n,

> > \

> > > +								NULL); \

> > > +			break; \

> > > +		case (TEST_RING_M | TEST_RING_BR): \

> > > +			ret = rte_ring_mc_dequeue_burst_elem(r, obj, esize,

> > n, \

> > > +								NULL); \

> > > +		} \

> > > +} while (0)

> >

> >

> > My thought to avoid test-code duplication was a bit different.

> Yes, this can be done multiple ways. My implementation is not complicated either.

> 

> > Instead of adding extra enums/parameters and then do switch on them, my

> The switch statement should be removed by the compiler for the performance tests.


I am sure the compiler will do its job properly.
My concern is that with all these extra flags, it is really hard to
read and understand what exactly function we are calling and what we are trying to test.
Might be just me, but let say in original version for enqueue_bulk() we have:

        const uint64_t sp_start = rte_rdtsc();
        for (i = 0; i < iterations; i++)
                while (rte_ring_sp_enqueue_bulk(r, burst, size, NULL) == 0)
                        rte_pause();
        const uint64_t sp_end = rte_rdtsc();

        const uint64_t mp_start = rte_rdtsc();
        for (i = 0; i < iterations; i++)
                while (rte_ring_mp_enqueue_bulk(r, burst, size, NULL) == 0)
                        rte_pause();
        const uint64_t mp_end = rte_rdtsc();

Simple and easy to understand.
Same code after the patch doesn't that straightforward anymore:

 const uint64_t sp_start = rte_rdtsc();
        for (i = 0; i < iterations; i++)
                do {
                        if (flag == 0)
                                TEST_RING_ENQUEUE(r, burst, esize, bsize, ret,
                                                TEST_RING_S | TEST_RING_BL);
                        else if (flag == 1)
                                TEST_RING_DEQUEUE(r, burst, esize, bsize, ret,
                                                TEST_RING_S | TEST_RING_BL);
                        if (ret == 0)
                                rte_pause();
                } while (!ret);
 const uint64_t sp_end = rte_rdtsc();

Another thing - if tomorrow we'll want to add perf tests
for elem_size==4/8, etc. - we'll need to do copy/paste
for all test-case invocations, as you did for 16B
(or some code reorg). 

> 

> > intention was something like that:

> >

> > 1. mv  test_ring_perf.c test_ring_perf.h 2. Inside test_ring_perf.h change

> > rte_ring_ create/enqueue/dequeue function

> >     calls to some not-defined function/macros invocations.

> >    With similar name, same number of parameters, and same semantics.

> >    Also change 'void *burst[..]' to 'RING_ELEM[...]'; 3. For each test

> > configuration we want to have (default, 4B, 8B, 16B)

> >     create a new .c file where we:

> >     - define used in test_ring_perf.h macros(/function)

> >    - include test_ring_perf.h

> >    -  REGISTER_TEST_COMMAND(<test_name>, test_ring_perf);

> >

> > As an example:

> > test_ring_perf.h:

> > ...

> > static int

> > enqueue_bulk(void *p)

> > {

> >         ...

> >         RING_ELEM burst[MAX_BURST];

> >

> >         memset(burst, 0, sizeof(burst));

> >         ....

> >         const uint64_t sp_start = rte_rdtsc();

> >         for (i = 0; i < iterations; i++)

> >                 while (RING_SP_ENQUEUE_BULK(r, burst, size, NULL) == 0)

> >                         rte_pause();

> >         const uint64_t sp_end = rte_rdtsc();

> >

> >         const uint64_t mp_start = rte_rdtsc();

> >         for (i = 0; i < iterations; i++)

> >                 while (RING_MP_ENQUEUE_BULK(r, burst, size, NULL) == 0)

> >                         rte_pause();

> >         const uint64_t mp_end = rte_rdtsc();

> >         ....

> >

> > Then in test_ring_perf.c:

> >

> > ....

> > #define RING_ELEM	void *

> > ...

> > #define RING_SP_ENQUEUE_BULK(ring, buf, size, spc)  \

> >        rte_ring_sp_enqueue_bulk(ring, buf, size, spc) ....

> >

> > #include "test_ring_perf.h"

> > REGISTER_TEST_COMMAND(ring_perf_autotest, test_ring_perf);

> >

> >

> > In test_ring_elem16B_perf.c:

> > ....

> > #define RING_ELEM	__uint128_t

> > #define RING_SP_ENQUEUE_BULK(ring, buf, size, spc)  \

> > 	rte_ring_sp_enqueue_bulk_elem(ring, buf, sizeof(RING_ELEM), size,

> > spc) ....

> > #include "test_ring_perf.h"

> > REGISTER_TEST_COMMAND(ring_perf_elem16B_autotest, test_ring_perf);

> >

> > In test_ring_elem4B_per.c:

> >

> > ....

> > #define RING_ELEM	uint32_t

> > #define RING_SP_ENQUEUE_BULK(ring, buf, size, spc)  \

> > 	rte_ring_sp_enqueue_bulk_elem(ring, buf, sizeof(RING_ELEM), size,

> > spc) ....

> > #include "test_ring_perf.h"

> > REGISTER_TEST_COMMAND(ring_perf_elem4B_autotest, test_ring_perf);

> >

> > And so on.

> >

> > > +

> > > +/* This function is placed here as it is required for both

> > > + * performance and functional tests.

> > > + */

> > > +static __rte_always_inline void *

> > > +test_ring_calloc(unsigned int rsize, int esize) {

> > > +	unsigned int sz;

> > > +	void *p;

> > > +

> > > +	/* Legacy queue APIs? */

> > > +	if (esize == -1)

> > > +		sz = sizeof(void *);

> > > +	else

> > > +		sz = esize;

> > > +

> > > +	p = rte_zmalloc(NULL, rsize * sz, RTE_CACHE_LINE_SIZE);

> > > +	if (p == NULL)

> > > +		printf("Failed to allocate memory\n");

> > > +

> > > +	return p;

> > > +}

> > > --

> > > 2.17.1
Honnappa Nagarahalli Jan. 9, 2020, 5:15 a.m. UTC | #2
<snip>

> Subject: RE: [PATCH v7 03/17] test/ring: add functional tests for

> rte_ring_xxx_elem APIs

> 

> > > > Add basic infrastructure to test rte_ring_xxx_elem APIs. Add test

> > > > cases for testing burst and bulk tests.

> > > >

> > > > Signed-off-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>

> > > > Reviewed-by: Gavin Hu <gavin.hu@arm.com>

> > > > ---

<snip>

> > > > diff --git a/app/test/test_ring.h b/app/test/test_ring.h new file

> > > > mode

> > > > 100644 index 000000000..19ef1b399

> > > > --- /dev/null

> > > > +++ b/app/test/test_ring.h

> > > > @@ -0,0 +1,203 @@

> > > > +/* SPDX-License-Identifier: BSD-3-Clause

> > > > + * Copyright(c) 2019 Arm Limited

> > > > + */

> > > > +

> > > > +#include <rte_malloc.h>

> > > > +#include <rte_ring.h>

> > > > +#include <rte_ring_elem.h>

> > > > +

> > > > +/* API type to call

> > > > + * N - Calls default APIs

> > > > + * S - Calls SP or SC API

> > > > + * M - Calls MP or MC API

> > > > + */

> > > > +#define TEST_RING_N 1

> > > > +#define TEST_RING_S 2

> > > > +#define TEST_RING_M 4

> > > > +

> > > > +/* API type to call

> > > > + * SL - Calls single element APIs

> > > > + * BL - Calls bulk APIs

> > > > + * BR - Calls burst APIs

> > > > + */

> > > > +#define TEST_RING_SL 8

> > > > +#define TEST_RING_BL 16

> > > > +#define TEST_RING_BR 32

> > > > +

> > > > +#define TEST_RING_IGNORE_API_TYPE ~0U

> > > > +

> > > > +#define TEST_RING_INCP(obj, esize, n) do { \

> > > > +	/* Legacy queue APIs? */ \

> > > > +	if ((esize) == -1) \

> > > > +		obj = ((void **)obj) + n; \

> > > > +	else \

> > > > +		obj = (void **)(((uint32_t *)obj) + \

> > > > +					(n * esize / sizeof(uint32_t))); \ }

> > > while (0)

> > > > +

> > > > +#define TEST_RING_CREATE(name, esize, count, socket_id, flags, r) do

> { \

> > > > +	/* Legacy queue APIs? */ \

> > > > +	if ((esize) == -1) \

> > > > +		r = rte_ring_create((name), (count), (socket_id), (flags)); \

> > > > +	else \

> > > > +		r = rte_ring_create_elem((name), (esize), (count), \

> > > > +						(socket_id), (flags)); \

> > > > +} while (0)

> > > > +

> > > > +#define TEST_RING_ENQUEUE(r, obj, esize, n, ret, api_type) do { \

> > > > +	/* Legacy queue APIs? */ \

> > > > +	if ((esize) == -1) \

> > > > +		switch (api_type) { \

> > > > +		case (TEST_RING_N | TEST_RING_SL): \

> > > > +			ret = rte_ring_enqueue(r, obj); \

> > > > +			break; \

> > > > +		case (TEST_RING_S | TEST_RING_SL): \

> > > > +			ret = rte_ring_sp_enqueue(r, obj); \

> > > > +			break; \

> > > > +		case (TEST_RING_M | TEST_RING_SL): \

> > > > +			ret = rte_ring_mp_enqueue(r, obj); \

> > > > +			break; \

> > > > +		case (TEST_RING_N | TEST_RING_BL): \

> > > > +			ret = rte_ring_enqueue_bulk(r, obj, n, NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_S | TEST_RING_BL): \

> > > > +			ret = rte_ring_sp_enqueue_bulk(r, obj, n, NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_M | TEST_RING_BL): \

> > > > +			ret = rte_ring_mp_enqueue_bulk(r, obj, n, NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_N | TEST_RING_BR): \

> > > > +			ret = rte_ring_enqueue_burst(r, obj, n, NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_S | TEST_RING_BR): \

> > > > +			ret = rte_ring_sp_enqueue_burst(r, obj, n, NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_M | TEST_RING_BR): \

> > > > +			ret = rte_ring_mp_enqueue_burst(r, obj, n, NULL); \

> > > > +		} \

> > > > +	else \

> > > > +		switch (api_type) { \

> > > > +		case (TEST_RING_N | TEST_RING_SL): \

> > > > +			ret = rte_ring_enqueue_elem(r, obj, esize); \

> > > > +			break; \

> > > > +		case (TEST_RING_S | TEST_RING_SL): \

> > > > +			ret = rte_ring_sp_enqueue_elem(r, obj, esize); \

> > > > +			break; \

> > > > +		case (TEST_RING_M | TEST_RING_SL): \

> > > > +			ret = rte_ring_mp_enqueue_elem(r, obj, esize); \

> > > > +			break; \

> > > > +		case (TEST_RING_N | TEST_RING_BL): \

> > > > +			ret = rte_ring_enqueue_bulk_elem(r, obj, esize, n, \

> > > > +								NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_S | TEST_RING_BL): \

> > > > +			ret = rte_ring_sp_enqueue_bulk_elem(r, obj, esize, n,

> > > \

> > > > +								NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_M | TEST_RING_BL): \

> > > > +			ret = rte_ring_mp_enqueue_bulk_elem(r, obj, esize, n,

> > > \

> > > > +								NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_N | TEST_RING_BR): \

> > > > +			ret = rte_ring_enqueue_burst_elem(r, obj, esize, n, \

> > > > +								NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_S | TEST_RING_BR): \

> > > > +			ret = rte_ring_sp_enqueue_burst_elem(r, obj, esize, n,

> > > \

> > > > +								NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_M | TEST_RING_BR): \

> > > > +			ret = rte_ring_mp_enqueue_burst_elem(r, obj, esize,

> > > n, \

> > > > +								NULL); \

> > > > +		} \

> > > > +} while (0)

> > > > +

> > > > +#define TEST_RING_DEQUEUE(r, obj, esize, n, ret, api_type) do { \

> > > > +	/* Legacy queue APIs? */ \

> > > > +	if ((esize) == -1) \

> > > > +		switch (api_type) { \

> > > > +		case (TEST_RING_N | TEST_RING_SL): \

> > > > +			ret = rte_ring_dequeue(r, obj); \

> > > > +			break; \

> > > > +		case (TEST_RING_S | TEST_RING_SL): \

> > > > +			ret = rte_ring_sc_dequeue(r, obj); \

> > > > +			break; \

> > > > +		case (TEST_RING_M | TEST_RING_SL): \

> > > > +			ret = rte_ring_mc_dequeue(r, obj); \

> > > > +			break; \

> > > > +		case (TEST_RING_N | TEST_RING_BL): \

> > > > +			ret = rte_ring_dequeue_bulk(r, obj, n, NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_S | TEST_RING_BL): \

> > > > +			ret = rte_ring_sc_dequeue_bulk(r, obj, n, NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_M | TEST_RING_BL): \

> > > > +			ret = rte_ring_mc_dequeue_bulk(r, obj, n, NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_N | TEST_RING_BR): \

> > > > +			ret = rte_ring_dequeue_burst(r, obj, n, NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_S | TEST_RING_BR): \

> > > > +			ret = rte_ring_sc_dequeue_burst(r, obj, n, NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_M | TEST_RING_BR): \

> > > > +			ret = rte_ring_mc_dequeue_burst(r, obj, n, NULL); \

> > > > +		} \

> > > > +	else \

> > > > +		switch (api_type) { \

> > > > +		case (TEST_RING_N | TEST_RING_SL): \

> > > > +			ret = rte_ring_dequeue_elem(r, obj, esize); \

> > > > +			break; \

> > > > +		case (TEST_RING_S | TEST_RING_SL): \

> > > > +			ret = rte_ring_sc_dequeue_elem(r, obj, esize); \

> > > > +			break; \

> > > > +		case (TEST_RING_M | TEST_RING_SL): \

> > > > +			ret = rte_ring_mc_dequeue_elem(r, obj, esize); \

> > > > +			break; \

> > > > +		case (TEST_RING_N | TEST_RING_BL): \

> > > > +			ret = rte_ring_dequeue_bulk_elem(r, obj, esize, n, \

> > > > +								NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_S | TEST_RING_BL): \

> > > > +			ret = rte_ring_sc_dequeue_bulk_elem(r, obj, esize, n,

> > > \

> > > > +								NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_M | TEST_RING_BL): \

> > > > +			ret = rte_ring_mc_dequeue_bulk_elem(r, obj, esize, n,

> > > \

> > > > +								NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_N | TEST_RING_BR): \

> > > > +			ret = rte_ring_dequeue_burst_elem(r, obj, esize, n, \

> > > > +								NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_S | TEST_RING_BR): \

> > > > +			ret = rte_ring_sc_dequeue_burst_elem(r, obj, esize, n,

> > > \

> > > > +								NULL); \

> > > > +			break; \

> > > > +		case (TEST_RING_M | TEST_RING_BR): \

> > > > +			ret = rte_ring_mc_dequeue_burst_elem(r, obj, esize,

> > > n, \

> > > > +								NULL); \

> > > > +		} \

> > > > +} while (0)

> > >

> > >

> > > My thought to avoid test-code duplication was a bit different.

> > Yes, this can be done multiple ways. My implementation is not complicated

> either.

> >

> > > Instead of adding extra enums/parameters and then do switch on them,

> > > my

> > The switch statement should be removed by the compiler for the

> performance tests.

> 

> I am sure the compiler will do its job properly.

> My concern is that with all these extra flags, it is really hard to read and

> understand what exactly function we are calling and what we are trying to

> test.

There are just 2 flags - 1) representing single/bulk/burst 2) representing default/single/multiple threads. This is the way the rte_ring APIs are also organized (rte_ring_<sp/mp or sc/mc>_enqueue_<bulk/burst>).
If we want to keep the code flexible, we have to keep these 2 flags that can be varied.
Your proposal considers only the element size as a variable. It does not consider the above mentioned variables. This results in code duplication. This is visible in patch 10/17.

> Might be just me, but let say in original version for enqueue_bulk() we have:

> 

>         const uint64_t sp_start = rte_rdtsc();

>         for (i = 0; i < iterations; i++)

>                 while (rte_ring_sp_enqueue_bulk(r, burst, size, NULL) == 0)

>                         rte_pause();

>         const uint64_t sp_end = rte_rdtsc();

> 

>         const uint64_t mp_start = rte_rdtsc();

>         for (i = 0; i < iterations; i++)

>                 while (rte_ring_mp_enqueue_bulk(r, burst, size, NULL) == 0)

>                         rte_pause();

>         const uint64_t mp_end = rte_rdtsc();

> 

> Simple and easy to understand.

> Same code after the patch doesn't that straightforward anymore:

> 

>  const uint64_t sp_start = rte_rdtsc();

>         for (i = 0; i < iterations; i++)

>                 do {

>                         if (flag == 0)

>                                 TEST_RING_ENQUEUE(r, burst, esize, bsize, ret,

>                                                 TEST_RING_S | TEST_RING_BL);

>                         else if (flag == 1)

>                                 TEST_RING_DEQUEUE(r, burst, esize, bsize, ret,

>                                                 TEST_RING_S | TEST_RING_BL);

Would it help if the #define names are better?
May be convert

TEST_RING_SL to TEST_ELEM_SINGLE
TEST_RING_BL to TEST_ELEM_BULK
TEST_RING_BR to TEST_ELEM_BURST

and

TEST_RING_N to TEST_THREAD_DEFAULT
TEST_RING_S to TEST_THREAD_SPSC
TEST_RING_M to TEST_THREAD_MPMC

>                         if (ret == 0)

>                                 rte_pause();

>                 } while (!ret);

>  const uint64_t sp_end = rte_rdtsc();

> 

> Another thing - if tomorrow we'll want to add perf tests for elem_size==4/8,

> etc. - we'll need to do copy/paste for all test-case invocations, as you did for

> 16B (or some code reorg).

This is a mistake on my side. Looking at the code, 'test_ring_perf' can be simplified to avoid the copy/paste. 'test_ring_perf' can be changed to call another function (that contains the test cases) with different element sizes. I will make this change.
The only issue would be the wrappers 'dequeue_bulk', 'dequeue_bulk_16B' etc. However, the wrappers are simple enough to maintain.

> 

> >

> > > intention was something like that:

> > >

> > > 1. mv  test_ring_perf.c test_ring_perf.h 2. Inside test_ring_perf.h

> > > change rte_ring_ create/enqueue/dequeue function

> > >     calls to some not-defined function/macros invocations.

> > >    With similar name, same number of parameters, and same semantics.

> > >    Also change 'void *burst[..]' to 'RING_ELEM[...]'; 3. For each

> > > test configuration we want to have (default, 4B, 8B, 16B)

> > >     create a new .c file where we:

> > >     - define used in test_ring_perf.h macros(/function)

> > >    - include test_ring_perf.h

> > >    -  REGISTER_TEST_COMMAND(<test_name>, test_ring_perf);

> > >

> > > As an example:

> > > test_ring_perf.h:

> > > ...

> > > static int

> > > enqueue_bulk(void *p)

> > > {

> > >         ...

> > >         RING_ELEM burst[MAX_BURST];

> > >

> > >         memset(burst, 0, sizeof(burst));

> > >         ....

> > >         const uint64_t sp_start = rte_rdtsc();

> > >         for (i = 0; i < iterations; i++)

> > >                 while (RING_SP_ENQUEUE_BULK(r, burst, size, NULL) == 0)

> > >                         rte_pause();

> > >         const uint64_t sp_end = rte_rdtsc();

> > >

> > >         const uint64_t mp_start = rte_rdtsc();

> > >         for (i = 0; i < iterations; i++)

> > >                 while (RING_MP_ENQUEUE_BULK(r, burst, size, NULL) == 0)

> > >                         rte_pause();

> > >         const uint64_t mp_end = rte_rdtsc();

> > >         ....

> > >

> > > Then in test_ring_perf.c:

> > >

> > > ....

> > > #define RING_ELEM	void *

> > > ...

> > > #define RING_SP_ENQUEUE_BULK(ring, buf, size, spc)  \

> > >        rte_ring_sp_enqueue_bulk(ring, buf, size, spc) ....

> > >

> > > #include "test_ring_perf.h"

> > > REGISTER_TEST_COMMAND(ring_perf_autotest, test_ring_perf);

> > >

> > >

> > > In test_ring_elem16B_perf.c:

> > > ....

> > > #define RING_ELEM	__uint128_t

> > > #define RING_SP_ENQUEUE_BULK(ring, buf, size, spc)  \

> > > 	rte_ring_sp_enqueue_bulk_elem(ring, buf, sizeof(RING_ELEM), size,

> > > spc) ....

> > > #include "test_ring_perf.h"

> > > REGISTER_TEST_COMMAND(ring_perf_elem16B_autotest, test_ring_perf);

> > >

> > > In test_ring_elem4B_per.c:

> > >

> > > ....

> > > #define RING_ELEM	uint32_t

> > > #define RING_SP_ENQUEUE_BULK(ring, buf, size, spc)  \

> > > 	rte_ring_sp_enqueue_bulk_elem(ring, buf, sizeof(RING_ELEM), size,

> > > spc) ....

> > > #include "test_ring_perf.h"

> > > REGISTER_TEST_COMMAND(ring_perf_elem4B_autotest, test_ring_perf);

> > >

> > > And so on.

This will result in additional test files.

> > >

> > > > +

> > > > +/* This function is placed here as it is required for both

> > > > + * performance and functional tests.

> > > > + */

> > > > +static __rte_always_inline void * test_ring_calloc(unsigned int

> > > > +rsize, int esize) {

> > > > +	unsigned int sz;

> > > > +	void *p;

> > > > +

> > > > +	/* Legacy queue APIs? */

> > > > +	if (esize == -1)

> > > > +		sz = sizeof(void *);

> > > > +	else

> > > > +		sz = esize;

> > > > +

> > > > +	p = rte_zmalloc(NULL, rsize * sz, RTE_CACHE_LINE_SIZE);

> > > > +	if (p == NULL)

> > > > +		printf("Failed to allocate memory\n");

> > > > +

> > > > +	return p;

> > > > +}

> > > > --

> > > > 2.17.1
diff mbox series

Patch

diff --git a/app/test/test_ring.c b/app/test/test_ring.c
index aaf1e70ad..e7a8b468b 100644
--- a/app/test/test_ring.c
+++ b/app/test/test_ring.c
@@ -23,11 +23,13 @@ 
 #include <rte_branch_prediction.h>
 #include <rte_malloc.h>
 #include <rte_ring.h>
+#include <rte_ring_elem.h>
 #include <rte_random.h>
 #include <rte_errno.h>
 #include <rte_hexdump.h>
 
 #include "test.h"
+#include "test_ring.h"
 
 /*
  * Ring
@@ -67,6 +69,50 @@  static rte_atomic32_t synchro;
 
 #define	TEST_RING_FULL_EMTPY_ITER	8
 
+static int esize[] = {-1, 4, 8, 16};
+
+static void
+test_ring_mem_init(void *obj, unsigned int count, int esize)
+{
+	unsigned int i;
+
+	/* Legacy queue APIs? */
+	if (esize == -1)
+		for (i = 0; i < count; i++)
+			((void **)obj)[i] = (void *)(unsigned long)i;
+	else
+		for (i = 0; i < (count * esize / sizeof(uint32_t)); i++)
+			((uint32_t *)obj)[i] = i;
+}
+
+static void
+test_ring_print_test_string(const char *istr, unsigned int api_type, int esize)
+{
+	printf("\n%s: ", istr);
+
+	if (esize == -1)
+		printf("legacy APIs: ");
+	else
+		printf("elem APIs: element size %dB ", esize);
+
+	if (api_type == TEST_RING_IGNORE_API_TYPE)
+		return;
+
+	if ((api_type & TEST_RING_N) == TEST_RING_N)
+		printf(": default enqueue/dequeue: ");
+	else if ((api_type & TEST_RING_S) == TEST_RING_S)
+		printf(": SP/SC: ");
+	else if ((api_type & TEST_RING_M) == TEST_RING_M)
+		printf(": MP/MC: ");
+
+	if ((api_type & TEST_RING_SL) == TEST_RING_SL)
+		printf("single\n");
+	else if ((api_type & TEST_RING_BL) == TEST_RING_BL)
+		printf("bulk\n");
+	else if ((api_type & TEST_RING_BR) == TEST_RING_BR)
+		printf("burst\n");
+}
+
 /*
  * helper routine for test_ring_basic
  */
@@ -314,286 +360,203 @@  test_ring_basic(struct rte_ring *r)
 	return -1;
 }
 
+/*
+ * Burst and bulk operations with sp/sc, mp/mc and default (during creation)
+ */
 static int
-test_ring_burst_basic(struct rte_ring *r)
+test_ring_burst_bulk_tests(unsigned int api_type)
 {
+	struct rte_ring *r;
 	void **src = NULL, **cur_src = NULL, **dst = NULL, **cur_dst = NULL;
 	int ret;
-	unsigned i;
+	unsigned int i, j;
+	unsigned int num_elems;
 
-	/* alloc dummy object pointers */
-	src = malloc(RING_SIZE*2*sizeof(void *));
-	if (src == NULL)
-		goto fail;
-
-	for (i = 0; i < RING_SIZE*2 ; i++) {
-		src[i] = (void *)(unsigned long)i;
-	}
-	cur_src = src;
+	for (i = 0; i < RTE_DIM(esize); i++) {
+		test_ring_print_test_string("Test standard ring", api_type,
+						esize[i]);
 
-	/* alloc some room for copied objects */
-	dst = malloc(RING_SIZE*2*sizeof(void *));
-	if (dst == NULL)
-		goto fail;
+		/* Create the ring */
+		TEST_RING_CREATE("test_ring_burst_bulk_tests", esize[i],
+					RING_SIZE, SOCKET_ID_ANY, 0, r);
 
-	memset(dst, 0, RING_SIZE*2*sizeof(void *));
-	cur_dst = dst;
-
-	printf("Test SP & SC basic functions \n");
-	printf("enqueue 1 obj\n");
-	ret = rte_ring_sp_enqueue_burst(r, cur_src, 1, NULL);
-	cur_src += 1;
-	if (ret != 1)
-		goto fail;
-
-	printf("enqueue 2 objs\n");
-	ret = rte_ring_sp_enqueue_burst(r, cur_src, 2, NULL);
-	cur_src += 2;
-	if (ret != 2)
-		goto fail;
-
-	printf("enqueue MAX_BULK objs\n");
-	ret = rte_ring_sp_enqueue_burst(r, cur_src, MAX_BULK, NULL);
-	cur_src += MAX_BULK;
-	if (ret != MAX_BULK)
-		goto fail;
-
-	printf("dequeue 1 obj\n");
-	ret = rte_ring_sc_dequeue_burst(r, cur_dst, 1, NULL);
-	cur_dst += 1;
-	if (ret != 1)
-		goto fail;
-
-	printf("dequeue 2 objs\n");
-	ret = rte_ring_sc_dequeue_burst(r, cur_dst, 2, NULL);
-	cur_dst += 2;
-	if (ret != 2)
-		goto fail;
+		/* alloc dummy object pointers */
+		src = test_ring_calloc(RING_SIZE * 2, esize[i]);
+		if (src == NULL)
+			goto fail;
+		test_ring_mem_init(src, RING_SIZE * 2, esize[i]);
+		cur_src = src;
 
-	printf("dequeue MAX_BULK objs\n");
-	ret = rte_ring_sc_dequeue_burst(r, cur_dst, MAX_BULK, NULL);
-	cur_dst += MAX_BULK;
-	if (ret != MAX_BULK)
-		goto fail;
+		/* alloc some room for copied objects */
+		dst = test_ring_calloc(RING_SIZE * 2, esize[i]);
+		if (dst == NULL)
+			goto fail;
+		cur_dst = dst;
 
-	/* check data */
-	if (memcmp(src, dst, cur_dst - dst)) {
-		rte_hexdump(stdout, "src", src, cur_src - src);
-		rte_hexdump(stdout, "dst", dst, cur_dst - dst);
-		printf("data after dequeue is not the same\n");
-		goto fail;
-	}
+		printf("enqueue 1 obj\n");
+		TEST_RING_ENQUEUE(r, cur_src, esize[i], 1, ret, api_type);
+		if (ret != 1)
+			goto fail;
+		TEST_RING_INCP(cur_src, esize[i], 1);
 
-	cur_src = src;
-	cur_dst = dst;
+		printf("enqueue 2 objs\n");
+		TEST_RING_ENQUEUE(r, cur_src, esize[i], 2, ret, api_type);
+		if (ret != 2)
+			goto fail;
+		TEST_RING_INCP(cur_src, esize[i], 2);
 
-	printf("Test enqueue without enough memory space \n");
-	for (i = 0; i< (RING_SIZE/MAX_BULK - 1); i++) {
-		ret = rte_ring_sp_enqueue_burst(r, cur_src, MAX_BULK, NULL);
-		cur_src += MAX_BULK;
+		printf("enqueue MAX_BULK objs\n");
+		TEST_RING_ENQUEUE(r, cur_src, esize[i], MAX_BULK, ret,
+						api_type);
 		if (ret != MAX_BULK)
 			goto fail;
-	}
-
-	printf("Enqueue 2 objects, free entries = MAX_BULK - 2  \n");
-	ret = rte_ring_sp_enqueue_burst(r, cur_src, 2, NULL);
-	cur_src += 2;
-	if (ret != 2)
-		goto fail;
+		TEST_RING_INCP(cur_src, esize[i], MAX_BULK);
 
-	printf("Enqueue the remaining entries = MAX_BULK - 2  \n");
-	/* Always one free entry left */
-	ret = rte_ring_sp_enqueue_burst(r, cur_src, MAX_BULK, NULL);
-	cur_src += MAX_BULK - 3;
-	if (ret != MAX_BULK - 3)
-		goto fail;
-
-	printf("Test if ring is full  \n");
-	if (rte_ring_full(r) != 1)
-		goto fail;
+		printf("dequeue 1 obj\n");
+		TEST_RING_DEQUEUE(r, cur_dst, esize[i], 1, ret, api_type);
+		if (ret != 1)
+			goto fail;
+		TEST_RING_INCP(cur_dst, esize[i], 1);
 
-	printf("Test enqueue for a full entry  \n");
-	ret = rte_ring_sp_enqueue_burst(r, cur_src, MAX_BULK, NULL);
-	if (ret != 0)
-		goto fail;
+		printf("dequeue 2 objs\n");
+		TEST_RING_DEQUEUE(r, cur_dst, esize[i], 2, ret, api_type);
+		if (ret != 2)
+			goto fail;
+		TEST_RING_INCP(cur_dst, esize[i], 2);
 
-	printf("Test dequeue without enough objects \n");
-	for (i = 0; i<RING_SIZE/MAX_BULK - 1; i++) {
-		ret = rte_ring_sc_dequeue_burst(r, cur_dst, MAX_BULK, NULL);
-		cur_dst += MAX_BULK;
+		printf("dequeue MAX_BULK objs\n");
+		TEST_RING_DEQUEUE(r, cur_dst, esize[i], MAX_BULK, ret,
+						api_type);
 		if (ret != MAX_BULK)
 			goto fail;
-	}
-
-	/* Available memory space for the exact MAX_BULK entries */
-	ret = rte_ring_sc_dequeue_burst(r, cur_dst, 2, NULL);
-	cur_dst += 2;
-	if (ret != 2)
-		goto fail;
-
-	ret = rte_ring_sc_dequeue_burst(r, cur_dst, MAX_BULK, NULL);
-	cur_dst += MAX_BULK - 3;
-	if (ret != MAX_BULK - 3)
-		goto fail;
-
-	printf("Test if ring is empty \n");
-	/* Check if ring is empty */
-	if (1 != rte_ring_empty(r))
-		goto fail;
-
-	/* check data */
-	if (memcmp(src, dst, cur_dst - dst)) {
-		rte_hexdump(stdout, "src", src, cur_src - src);
-		rte_hexdump(stdout, "dst", dst, cur_dst - dst);
-		printf("data after dequeue is not the same\n");
-		goto fail;
-	}
+		TEST_RING_INCP(cur_dst, esize[i], MAX_BULK);
 
-	cur_src = src;
-	cur_dst = dst;
-
-	printf("Test MP & MC basic functions \n");
-
-	printf("enqueue 1 obj\n");
-	ret = rte_ring_mp_enqueue_burst(r, cur_src, 1, NULL);
-	cur_src += 1;
-	if (ret != 1)
-		goto fail;
-
-	printf("enqueue 2 objs\n");
-	ret = rte_ring_mp_enqueue_burst(r, cur_src, 2, NULL);
-	cur_src += 2;
-	if (ret != 2)
-		goto fail;
-
-	printf("enqueue MAX_BULK objs\n");
-	ret = rte_ring_mp_enqueue_burst(r, cur_src, MAX_BULK, NULL);
-	cur_src += MAX_BULK;
-	if (ret != MAX_BULK)
-		goto fail;
-
-	printf("dequeue 1 obj\n");
-	ret = rte_ring_mc_dequeue_burst(r, cur_dst, 1, NULL);
-	cur_dst += 1;
-	if (ret != 1)
-		goto fail;
-
-	printf("dequeue 2 objs\n");
-	ret = rte_ring_mc_dequeue_burst(r, cur_dst, 2, NULL);
-	cur_dst += 2;
-	if (ret != 2)
-		goto fail;
-
-	printf("dequeue MAX_BULK objs\n");
-	ret = rte_ring_mc_dequeue_burst(r, cur_dst, MAX_BULK, NULL);
-	cur_dst += MAX_BULK;
-	if (ret != MAX_BULK)
-		goto fail;
-
-	/* check data */
-	if (memcmp(src, dst, cur_dst - dst)) {
-		rte_hexdump(stdout, "src", src, cur_src - src);
-		rte_hexdump(stdout, "dst", dst, cur_dst - dst);
-		printf("data after dequeue is not the same\n");
-		goto fail;
-	}
-
-	cur_src = src;
-	cur_dst = dst;
+		/* check data */
+		if (memcmp(src, dst, cur_dst - dst)) {
+			rte_hexdump(stdout, "src", src, cur_src - src);
+			rte_hexdump(stdout, "dst", dst, cur_dst - dst);
+			printf("data after dequeue is not the same\n");
+			goto fail;
+		}
+
+		cur_src = src;
+		cur_dst = dst;
+
+		printf("fill and empty the ring\n");
+		for (j = 0; j < RING_SIZE / MAX_BULK; j++) {
+			TEST_RING_ENQUEUE(r, cur_src, esize[i], MAX_BULK,
+							ret, api_type);
+			if (ret != MAX_BULK)
+				goto fail;
+			TEST_RING_INCP(cur_src, esize[i], MAX_BULK);
+
+			TEST_RING_DEQUEUE(r, cur_dst, esize[i], MAX_BULK,
+							ret, api_type);
+			if (ret != MAX_BULK)
+				goto fail;
+			TEST_RING_INCP(cur_dst, esize[i], MAX_BULK);
+		}
 
-	printf("fill and empty the ring\n");
-	for (i = 0; i<RING_SIZE/MAX_BULK; i++) {
-		ret = rte_ring_mp_enqueue_burst(r, cur_src, MAX_BULK, NULL);
-		cur_src += MAX_BULK;
-		if (ret != MAX_BULK)
+		/* check data */
+		if (memcmp(src, dst, cur_dst - dst)) {
+			rte_hexdump(stdout, "src", src, cur_src - src);
+			rte_hexdump(stdout, "dst", dst, cur_dst - dst);
+			printf("data after dequeue is not the same\n");
 			goto fail;
-		ret = rte_ring_mc_dequeue_burst(r, cur_dst, MAX_BULK, NULL);
-		cur_dst += MAX_BULK;
-		if (ret != MAX_BULK)
+		}
+
+		cur_src = src;
+		cur_dst = dst;
+
+		printf("Test enqueue without enough memory space\n");
+		for (j = 0; j < (RING_SIZE/MAX_BULK - 1); j++) {
+			TEST_RING_ENQUEUE(r, cur_src, esize[i], MAX_BULK,
+							ret, api_type);
+			if (ret != MAX_BULK)
+				goto fail;
+			TEST_RING_INCP(cur_src, esize[i], MAX_BULK);
+		}
+
+		printf("Enqueue 2 objects, free entries = MAX_BULK - 2\n");
+		TEST_RING_ENQUEUE(r, cur_src, esize[i], 2, ret, api_type);
+		if (ret != 2)
 			goto fail;
-	}
-
-	/* check data */
-	if (memcmp(src, dst, cur_dst - dst)) {
-		rte_hexdump(stdout, "src", src, cur_src - src);
-		rte_hexdump(stdout, "dst", dst, cur_dst - dst);
-		printf("data after dequeue is not the same\n");
-		goto fail;
-	}
-
-	cur_src = src;
-	cur_dst = dst;
-
-	printf("Test enqueue without enough memory space \n");
-	for (i = 0; i<RING_SIZE/MAX_BULK - 1; i++) {
-		ret = rte_ring_mp_enqueue_burst(r, cur_src, MAX_BULK, NULL);
-		cur_src += MAX_BULK;
-		if (ret != MAX_BULK)
+		TEST_RING_INCP(cur_src, esize[i], 2);
+
+
+		printf("Enqueue the remaining entries = MAX_BULK - 3\n");
+		/* Bulk APIs enqueue exact number of elements */
+		if ((api_type & TEST_RING_BL) == TEST_RING_BL)
+			num_elems = MAX_BULK - 3;
+		else
+			num_elems = MAX_BULK;
+		/* Always one free entry left */
+		TEST_RING_ENQUEUE(r, cur_src, esize[i], num_elems,
+						ret, api_type);
+		if (ret != MAX_BULK - 3)
 			goto fail;
-	}
-
-	/* Available memory space for the exact MAX_BULK objects */
-	ret = rte_ring_mp_enqueue_burst(r, cur_src, 2, NULL);
-	cur_src += 2;
-	if (ret != 2)
-		goto fail;
+		TEST_RING_INCP(cur_src, esize[i], MAX_BULK - 3);
 
-	ret = rte_ring_mp_enqueue_burst(r, cur_src, MAX_BULK, NULL);
-	cur_src += MAX_BULK - 3;
-	if (ret != MAX_BULK - 3)
-		goto fail;
+		printf("Test if ring is full\n");
+		if (rte_ring_full(r) != 1)
+			goto fail;
 
+		printf("Test enqueue for a full entry\n");
+		TEST_RING_ENQUEUE(r, cur_src, esize[i], MAX_BULK,
+						ret, api_type);
+		if (ret != 0)
+			goto fail;
 
-	printf("Test dequeue without enough objects \n");
-	for (i = 0; i<RING_SIZE/MAX_BULK - 1; i++) {
-		ret = rte_ring_mc_dequeue_burst(r, cur_dst, MAX_BULK, NULL);
-		cur_dst += MAX_BULK;
-		if (ret != MAX_BULK)
+		printf("Test dequeue without enough objects\n");
+		for (j = 0; j < RING_SIZE / MAX_BULK - 1; j++) {
+			TEST_RING_DEQUEUE(r, cur_dst, esize[i], MAX_BULK,
+							ret, api_type);
+			if (ret != MAX_BULK)
+				goto fail;
+			TEST_RING_INCP(cur_dst, esize[i], MAX_BULK);
+		}
+
+		/* Available memory space for the exact MAX_BULK entries */
+		TEST_RING_DEQUEUE(r, cur_dst, esize[i], 2, ret, api_type);
+		if (ret != 2)
 			goto fail;
-	}
+		TEST_RING_INCP(cur_dst, esize[i], 2);
+
+		/* Bulk APIs enqueue exact number of elements */
+		if ((api_type & TEST_RING_BL) == TEST_RING_BL)
+			num_elems = MAX_BULK - 3;
+		else
+			num_elems = MAX_BULK;
+		TEST_RING_DEQUEUE(r, cur_dst, esize[i], num_elems,
+						ret, api_type);
+		if (ret != MAX_BULK - 3)
+			goto fail;
+		TEST_RING_INCP(cur_dst, esize[i], MAX_BULK - 3);
 
-	/* Available objects - the exact MAX_BULK */
-	ret = rte_ring_mc_dequeue_burst(r, cur_dst, 2, NULL);
-	cur_dst += 2;
-	if (ret != 2)
-		goto fail;
+		printf("Test if ring is empty\n");
+		/* Check if ring is empty */
+		if (rte_ring_empty(r) != 1)
+			goto fail;
 
-	ret = rte_ring_mc_dequeue_burst(r, cur_dst, MAX_BULK, NULL);
-	cur_dst += MAX_BULK - 3;
-	if (ret != MAX_BULK - 3)
-		goto fail;
+		/* check data */
+		if (memcmp(src, dst, cur_dst - dst)) {
+			rte_hexdump(stdout, "src", src, cur_src - src);
+			rte_hexdump(stdout, "dst", dst, cur_dst - dst);
+			printf("data after dequeue is not the same\n");
+			goto fail;
+		}
 
-	/* check data */
-	if (memcmp(src, dst, cur_dst - dst)) {
-		rte_hexdump(stdout, "src", src, cur_src - src);
-		rte_hexdump(stdout, "dst", dst, cur_dst - dst);
-		printf("data after dequeue is not the same\n");
-		goto fail;
+		/* Free memory before test completed */
+		rte_ring_free(r);
+		rte_free(src);
+		rte_free(dst);
 	}
 
-	cur_src = src;
-	cur_dst = dst;
-
-	printf("Covering rte_ring_enqueue_burst functions \n");
-
-	ret = rte_ring_enqueue_burst(r, cur_src, 2, NULL);
-	cur_src += 2;
-	if (ret != 2)
-		goto fail;
-
-	ret = rte_ring_dequeue_burst(r, cur_dst, 2, NULL);
-	cur_dst += 2;
-	if (ret != 2)
-		goto fail;
-
-	/* Free memory before test completed */
-	free(src);
-	free(dst);
 	return 0;
-
- fail:
-	free(src);
-	free(dst);
+fail:
+	rte_ring_free(r);
+	rte_free(src);
+	rte_free(dst);
 	return -1;
 }
 
@@ -810,6 +773,7 @@  test_ring_with_exact_size(void)
 static int
 test_ring(void)
 {
+	unsigned int i, j;
 	struct rte_ring *r = NULL;
 
 	/* some more basic operations */
@@ -828,9 +792,11 @@  test_ring(void)
 		goto test_fail;
 	}
 
-	/* burst operations */
-	if (test_ring_burst_basic(r) < 0)
-		goto test_fail;
+	/* Burst and bulk operations with sp/sc, mp/mc and default */
+	for (j = TEST_RING_BL; j <= TEST_RING_BR; j <<= 1)
+		for (i = TEST_RING_N; i <= TEST_RING_M; i <<= 1)
+			if (test_ring_burst_bulk_tests(i | j) < 0)
+				goto test_fail;
 
 	/* basic operations */
 	if (test_ring_basic(r) < 0)
diff --git a/app/test/test_ring.h b/app/test/test_ring.h
new file mode 100644
index 000000000..19ef1b399
--- /dev/null
+++ b/app/test/test_ring.h
@@ -0,0 +1,203 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#include <rte_malloc.h>
+#include <rte_ring.h>
+#include <rte_ring_elem.h>
+
+/* API type to call
+ * N - Calls default APIs
+ * S - Calls SP or SC API
+ * M - Calls MP or MC API
+ */
+#define TEST_RING_N 1
+#define TEST_RING_S 2
+#define TEST_RING_M 4
+
+/* API type to call
+ * SL - Calls single element APIs
+ * BL - Calls bulk APIs
+ * BR - Calls burst APIs
+ */
+#define TEST_RING_SL 8
+#define TEST_RING_BL 16
+#define TEST_RING_BR 32
+
+#define TEST_RING_IGNORE_API_TYPE ~0U
+
+#define TEST_RING_INCP(obj, esize, n) do { \
+	/* Legacy queue APIs? */ \
+	if ((esize) == -1) \
+		obj = ((void **)obj) + n; \
+	else \
+		obj = (void **)(((uint32_t *)obj) + \
+					(n * esize / sizeof(uint32_t))); \
+} while (0)
+
+#define TEST_RING_CREATE(name, esize, count, socket_id, flags, r) do { \
+	/* Legacy queue APIs? */ \
+	if ((esize) == -1) \
+		r = rte_ring_create((name), (count), (socket_id), (flags)); \
+	else \
+		r = rte_ring_create_elem((name), (esize), (count), \
+						(socket_id), (flags)); \
+} while (0)
+
+#define TEST_RING_ENQUEUE(r, obj, esize, n, ret, api_type) do { \
+	/* Legacy queue APIs? */ \
+	if ((esize) == -1) \
+		switch (api_type) { \
+		case (TEST_RING_N | TEST_RING_SL): \
+			ret = rte_ring_enqueue(r, obj); \
+			break; \
+		case (TEST_RING_S | TEST_RING_SL): \
+			ret = rte_ring_sp_enqueue(r, obj); \
+			break; \
+		case (TEST_RING_M | TEST_RING_SL): \
+			ret = rte_ring_mp_enqueue(r, obj); \
+			break; \
+		case (TEST_RING_N | TEST_RING_BL): \
+			ret = rte_ring_enqueue_bulk(r, obj, n, NULL); \
+			break; \
+		case (TEST_RING_S | TEST_RING_BL): \
+			ret = rte_ring_sp_enqueue_bulk(r, obj, n, NULL); \
+			break; \
+		case (TEST_RING_M | TEST_RING_BL): \
+			ret = rte_ring_mp_enqueue_bulk(r, obj, n, NULL); \
+			break; \
+		case (TEST_RING_N | TEST_RING_BR): \
+			ret = rte_ring_enqueue_burst(r, obj, n, NULL); \
+			break; \
+		case (TEST_RING_S | TEST_RING_BR): \
+			ret = rte_ring_sp_enqueue_burst(r, obj, n, NULL); \
+			break; \
+		case (TEST_RING_M | TEST_RING_BR): \
+			ret = rte_ring_mp_enqueue_burst(r, obj, n, NULL); \
+		} \
+	else \
+		switch (api_type) { \
+		case (TEST_RING_N | TEST_RING_SL): \
+			ret = rte_ring_enqueue_elem(r, obj, esize); \
+			break; \
+		case (TEST_RING_S | TEST_RING_SL): \
+			ret = rte_ring_sp_enqueue_elem(r, obj, esize); \
+			break; \
+		case (TEST_RING_M | TEST_RING_SL): \
+			ret = rte_ring_mp_enqueue_elem(r, obj, esize); \
+			break; \
+		case (TEST_RING_N | TEST_RING_BL): \
+			ret = rte_ring_enqueue_bulk_elem(r, obj, esize, n, \
+								NULL); \
+			break; \
+		case (TEST_RING_S | TEST_RING_BL): \
+			ret = rte_ring_sp_enqueue_bulk_elem(r, obj, esize, n, \
+								NULL); \
+			break; \
+		case (TEST_RING_M | TEST_RING_BL): \
+			ret = rte_ring_mp_enqueue_bulk_elem(r, obj, esize, n, \
+								NULL); \
+			break; \
+		case (TEST_RING_N | TEST_RING_BR): \
+			ret = rte_ring_enqueue_burst_elem(r, obj, esize, n, \
+								NULL); \
+			break; \
+		case (TEST_RING_S | TEST_RING_BR): \
+			ret = rte_ring_sp_enqueue_burst_elem(r, obj, esize, n, \
+								NULL); \
+			break; \
+		case (TEST_RING_M | TEST_RING_BR): \
+			ret = rte_ring_mp_enqueue_burst_elem(r, obj, esize, n, \
+								NULL); \
+		} \
+} while (0)
+
+#define TEST_RING_DEQUEUE(r, obj, esize, n, ret, api_type) do { \
+	/* Legacy queue APIs? */ \
+	if ((esize) == -1) \
+		switch (api_type) { \
+		case (TEST_RING_N | TEST_RING_SL): \
+			ret = rte_ring_dequeue(r, obj); \
+			break; \
+		case (TEST_RING_S | TEST_RING_SL): \
+			ret = rte_ring_sc_dequeue(r, obj); \
+			break; \
+		case (TEST_RING_M | TEST_RING_SL): \
+			ret = rte_ring_mc_dequeue(r, obj); \
+			break; \
+		case (TEST_RING_N | TEST_RING_BL): \
+			ret = rte_ring_dequeue_bulk(r, obj, n, NULL); \
+			break; \
+		case (TEST_RING_S | TEST_RING_BL): \
+			ret = rte_ring_sc_dequeue_bulk(r, obj, n, NULL); \
+			break; \
+		case (TEST_RING_M | TEST_RING_BL): \
+			ret = rte_ring_mc_dequeue_bulk(r, obj, n, NULL); \
+			break; \
+		case (TEST_RING_N | TEST_RING_BR): \
+			ret = rte_ring_dequeue_burst(r, obj, n, NULL); \
+			break; \
+		case (TEST_RING_S | TEST_RING_BR): \
+			ret = rte_ring_sc_dequeue_burst(r, obj, n, NULL); \
+			break; \
+		case (TEST_RING_M | TEST_RING_BR): \
+			ret = rte_ring_mc_dequeue_burst(r, obj, n, NULL); \
+		} \
+	else \
+		switch (api_type) { \
+		case (TEST_RING_N | TEST_RING_SL): \
+			ret = rte_ring_dequeue_elem(r, obj, esize); \
+			break; \
+		case (TEST_RING_S | TEST_RING_SL): \
+			ret = rte_ring_sc_dequeue_elem(r, obj, esize); \
+			break; \
+		case (TEST_RING_M | TEST_RING_SL): \
+			ret = rte_ring_mc_dequeue_elem(r, obj, esize); \
+			break; \
+		case (TEST_RING_N | TEST_RING_BL): \
+			ret = rte_ring_dequeue_bulk_elem(r, obj, esize, n, \
+								NULL); \
+			break; \
+		case (TEST_RING_S | TEST_RING_BL): \
+			ret = rte_ring_sc_dequeue_bulk_elem(r, obj, esize, n, \
+								NULL); \
+			break; \
+		case (TEST_RING_M | TEST_RING_BL): \
+			ret = rte_ring_mc_dequeue_bulk_elem(r, obj, esize, n, \
+								NULL); \
+			break; \
+		case (TEST_RING_N | TEST_RING_BR): \
+			ret = rte_ring_dequeue_burst_elem(r, obj, esize, n, \
+								NULL); \
+			break; \
+		case (TEST_RING_S | TEST_RING_BR): \
+			ret = rte_ring_sc_dequeue_burst_elem(r, obj, esize, n, \
+								NULL); \
+			break; \
+		case (TEST_RING_M | TEST_RING_BR): \
+			ret = rte_ring_mc_dequeue_burst_elem(r, obj, esize, n, \
+								NULL); \
+		} \
+} while (0)
+
+/* This function is placed here as it is required for both
+ * performance and functional tests.
+ */
+static __rte_always_inline void *
+test_ring_calloc(unsigned int rsize, int esize)
+{
+	unsigned int sz;
+	void *p;
+
+	/* Legacy queue APIs? */
+	if (esize == -1)
+		sz = sizeof(void *);
+	else
+		sz = esize;
+
+	p = rte_zmalloc(NULL, rsize * sz, RTE_CACHE_LINE_SIZE);
+	if (p == NULL)
+		printf("Failed to allocate memory\n");
+
+	return p;
+}