[3/7] test/stack: add stack test

Message ID 20190222160655.3346-4-gage.eads@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers
Series Subject: [PATCH ...] Add stack library and new mempool handler |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation fail Compilation issues

Commit Message

Eads, Gage Feb. 22, 2019, 4:06 p.m. UTC
  stack_autotest performs positive and negative testing of the stack API, and
exercises the push and pop datapath functions with all available lcores.

Signed-off-by: Gage Eads <gage.eads@intel.com>
---
 MAINTAINERS            |   1 +
 test/test/Makefile     |   2 +
 test/test/meson.build  |   3 +
 test/test/test_stack.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 400 insertions(+)
 create mode 100644 test/test/test_stack.c
  

Comments

Olivier Matz Feb. 25, 2019, 10:59 a.m. UTC | #1
On Fri, Feb 22, 2019 at 10:06:51AM -0600, Gage Eads wrote:
> stack_autotest performs positive and negative testing of the stack API, and
> exercises the push and pop datapath functions with all available lcores.
> 
> Signed-off-by: Gage Eads <gage.eads@intel.com>

[...]

> --- /dev/null
> +++ b/test/test/test_stack.c
> @@ -0,0 +1,394 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2019 Intel Corporation
> + */
> +
> +#include <string.h>
> +
> +#include <rte_lcore.h>
> +#include <rte_malloc.h>
> +#include <rte_random.h>
> +#include <rte_stack.h>
> +
> +#include "test.h"
> +
> +#define STACK_SIZE 4096
> +#define MAX_BULK 32
> +
> +static int
> +test_stack_push_pop(struct rte_stack *s, void **obj_table, unsigned int bulk_sz)
> +{
> +	void *popped_objs[STACK_SIZE];
> +	unsigned int i, ret;

Here, a dynamic sized table is used. In test_stack_basic() below, it
uses a heap-based allocation for the same purpose. I think it would be
more consistent to have the same method for both. I suggest to allocate
in heap to avoid a stack overflow if STACK_SIZE is increased in the
future.

[...]

> +static int
> +test_stack_basic(void)
> +{
> +	struct rte_stack *s = NULL;
> +	void **obj_table = NULL;
> +	int i, ret = -1;
> +
> +	obj_table = rte_calloc(NULL, STACK_SIZE, sizeof(void *), 0);
> +	if (obj_table == NULL) {
> +		printf("[%s():%u] failed to calloc %lu bytes\n",
> +		       __func__, __LINE__, STACK_SIZE * sizeof(void *));
> +		goto fail_test;
> +	}
> +
> +	for (i = 0; i < STACK_SIZE; i++)
> +		obj_table[i] = (void *)(uintptr_t)i;
> +
> +	s = rte_stack_create(__func__, STACK_SIZE, rte_socket_id(), 0);
> +	if (s == NULL) {
> +		printf("[%s():%u] failed to create a stack\n",
> +		       __func__, __LINE__);
> +		goto fail_test;
> +	}
> +
> +	if (rte_stack_lookup(__func__) != s) {
> +		printf("[%s():%u] failed to lookup a stack\n",
> +		       __func__, __LINE__);
> +		goto fail_test;
> +	}
> +
> +	if (rte_stack_count(s) != 0) {
> +		printf("[%s():%u] stack count: %u (expected 0)\n",
> +		       __func__, __LINE__, rte_stack_count(s));
> +		goto fail_test;
> +	}
> +
> +	if (rte_stack_free_count(s) != STACK_SIZE) {
> +		printf("[%s():%u] stack free count: %u (expected %u)\n",
> +		       __func__, __LINE__, rte_stack_count(s), STACK_SIZE);
> +		goto fail_test;
> +	}
> +
> +	ret = test_stack_push_pop(s, obj_table, 1);
> +	if (ret) {
> +		printf("[%s():%u] Single object push/pop failed\n",
> +		       __func__, __LINE__);
> +		goto fail_test;
> +	}
> +
> +	ret = test_stack_push_pop(s, obj_table, MAX_BULK);
> +	if (ret) {
> +		printf("[%s():%u] Bulk object push/pop failed\n",
> +		       __func__, __LINE__);
> +		goto fail_test;
> +	}
> +
> +	ret = rte_stack_push(s, obj_table, 2 * STACK_SIZE);
> +	if (ret != 0) {
> +		printf("[%s():%u] Excess objects push succeeded\n",
> +		       __func__, __LINE__);
> +		goto fail_test;
> +	}
> +
> +	ret = rte_stack_pop(s, obj_table, 1);
> +	if (ret != 0) {
> +		printf("[%s():%u] Empty stack pop succeeded\n",
> +		       __func__, __LINE__);
> +		goto fail_test;
> +	}
> +
> +	ret = 0;
> +
> +fail_test:
> +	rte_stack_free(s);
> +
> +	if (obj_table != NULL)
> +		rte_free(obj_table);
> +

The if can be removed.

> +static int
> +test_stack_name_length(void)
> +{
> +	char name[RTE_STACK_NAMESIZE + 1];
> +	struct rte_stack *s;
> +
> +	memset(name, 's', sizeof(name));
> +
> +	s = rte_stack_create(name, STACK_SIZE, rte_socket_id(), 0);
> +	if (s != NULL) {
> +		printf("[%s():%u] Failed to prevent long name\n",
> +		       __func__, __LINE__);
> +		return -1;
> +	}

Here, "name" is not a valid string (no \0 at the end). It does not hurt because
the length check is properly done in the lib, but we could imagine that the
wrong name is logged by the library on error, which would trigger a crash
here. So I suggest to pass a valid string instead.
  
Eads, Gage Feb. 28, 2019, 5:11 a.m. UTC | #2
> -----Original Message-----
> From: Olivier Matz [mailto:olivier.matz@6wind.com]
> Sent: Monday, February 25, 2019 4:59 AM
> To: Eads, Gage <gage.eads@intel.com>
> Cc: dev@dpdk.org; arybchenko@solarflare.com; Richardson, Bruce
> <bruce.richardson@intel.com>; Ananyev, Konstantin
> <konstantin.ananyev@intel.com>; gavin.hu@arm.com;
> Honnappa.Nagarahalli@arm.com; nd@arm.com; thomas@monjalon.net
> Subject: Re: [PATCH 3/7] test/stack: add stack test
> 
> On Fri, Feb 22, 2019 at 10:06:51AM -0600, Gage Eads wrote:
> > stack_autotest performs positive and negative testing of the stack
> > API, and exercises the push and pop datapath functions with all available
> lcores.
> >
> > Signed-off-by: Gage Eads <gage.eads@intel.com>
> 
> [...]
> 
> > --- /dev/null
> > +++ b/test/test/test_stack.c
> > @@ -0,0 +1,394 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2019 Intel Corporation  */
> > +
> > +#include <string.h>
> > +
> > +#include <rte_lcore.h>
> > +#include <rte_malloc.h>
> > +#include <rte_random.h>
> > +#include <rte_stack.h>
> > +
> > +#include "test.h"
> > +
> > +#define STACK_SIZE 4096
> > +#define MAX_BULK 32
> > +
> > +static int
> > +test_stack_push_pop(struct rte_stack *s, void **obj_table, unsigned
> > +int bulk_sz) {
> > +	void *popped_objs[STACK_SIZE];
> > +	unsigned int i, ret;
> 
> Here, a dynamic sized table is used. In test_stack_basic() below, it uses a heap-
> based allocation for the same purpose. I think it would be more consistent to
> have the same method for both. I suggest to allocate in heap to avoid a stack
> overflow if STACK_SIZE is increased in the future.
> 

Sure, I'll make popped_objs dynamically allocated.

> [...]
> 
> > +static int
> > +test_stack_basic(void)
> > +{
> > +	struct rte_stack *s = NULL;
> > +	void **obj_table = NULL;
> > +	int i, ret = -1;
> > +
> > +	obj_table = rte_calloc(NULL, STACK_SIZE, sizeof(void *), 0);
> > +	if (obj_table == NULL) {
> > +		printf("[%s():%u] failed to calloc %lu bytes\n",
> > +		       __func__, __LINE__, STACK_SIZE * sizeof(void *));
> > +		goto fail_test;
> > +	}
> > +
> > +	for (i = 0; i < STACK_SIZE; i++)
> > +		obj_table[i] = (void *)(uintptr_t)i;
> > +
> > +	s = rte_stack_create(__func__, STACK_SIZE, rte_socket_id(), 0);
> > +	if (s == NULL) {
> > +		printf("[%s():%u] failed to create a stack\n",
> > +		       __func__, __LINE__);
> > +		goto fail_test;
> > +	}
> > +
> > +	if (rte_stack_lookup(__func__) != s) {
> > +		printf("[%s():%u] failed to lookup a stack\n",
> > +		       __func__, __LINE__);
> > +		goto fail_test;
> > +	}
> > +
> > +	if (rte_stack_count(s) != 0) {
> > +		printf("[%s():%u] stack count: %u (expected 0)\n",
> > +		       __func__, __LINE__, rte_stack_count(s));
> > +		goto fail_test;
> > +	}
> > +
> > +	if (rte_stack_free_count(s) != STACK_SIZE) {
> > +		printf("[%s():%u] stack free count: %u (expected %u)\n",
> > +		       __func__, __LINE__, rte_stack_count(s), STACK_SIZE);
> > +		goto fail_test;
> > +	}
> > +
> > +	ret = test_stack_push_pop(s, obj_table, 1);
> > +	if (ret) {
> > +		printf("[%s():%u] Single object push/pop failed\n",
> > +		       __func__, __LINE__);
> > +		goto fail_test;
> > +	}
> > +
> > +	ret = test_stack_push_pop(s, obj_table, MAX_BULK);
> > +	if (ret) {
> > +		printf("[%s():%u] Bulk object push/pop failed\n",
> > +		       __func__, __LINE__);
> > +		goto fail_test;
> > +	}
> > +
> > +	ret = rte_stack_push(s, obj_table, 2 * STACK_SIZE);
> > +	if (ret != 0) {
> > +		printf("[%s():%u] Excess objects push succeeded\n",
> > +		       __func__, __LINE__);
> > +		goto fail_test;
> > +	}
> > +
> > +	ret = rte_stack_pop(s, obj_table, 1);
> > +	if (ret != 0) {
> > +		printf("[%s():%u] Empty stack pop succeeded\n",
> > +		       __func__, __LINE__);
> > +		goto fail_test;
> > +	}
> > +
> > +	ret = 0;
> > +
> > +fail_test:
> > +	rte_stack_free(s);
> > +
> > +	if (obj_table != NULL)
> > +		rte_free(obj_table);
> > +
> 
> The if can be removed.

Ah, I didn't know rte_free() checks for NULL. Will remove.

> 
> > +static int
> > +test_stack_name_length(void)
> > +{
> > +	char name[RTE_STACK_NAMESIZE + 1];
> > +	struct rte_stack *s;
> > +
> > +	memset(name, 's', sizeof(name));
> > +
> > +	s = rte_stack_create(name, STACK_SIZE, rte_socket_id(), 0);
> > +	if (s != NULL) {
> > +		printf("[%s():%u] Failed to prevent long name\n",
> > +		       __func__, __LINE__);
> > +		return -1;
> > +	}
> 
> Here, "name" is not a valid string (no \0 at the end). It does not hurt because the
> length check is properly done in the lib, but we could imagine that the wrong
> name is logged by the library on error, which would trigger a crash here. So I
> suggest to pass a valid string instead.

Good catch. Will fix.

Thanks,
Gage
  

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 7e64f63b6..58b438414 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -412,6 +412,7 @@  M: Olivier Matz <olivier.matz@6wind.com>
 F: lib/librte_stack/
 F: doc/guides/prog_guide/stack_lib.rst
 F: drivers/mempool/stack/
+F: test/test/*stack*
 
 
 Memory Pool Drivers
diff --git a/test/test/Makefile b/test/test/Makefile
index 89949c2bb..47cf98a3a 100644
--- a/test/test/Makefile
+++ b/test/test/Makefile
@@ -89,6 +89,8 @@  endif
 
 SRCS-y += test_rwlock.c
 
+SRCS-$(CONFIG_RTE_LIBRTE_STACK) += test_stack.c
+
 SRCS-$(CONFIG_RTE_LIBRTE_TIMER) += test_timer.c
 SRCS-$(CONFIG_RTE_LIBRTE_TIMER) += test_timer_perf.c
 SRCS-$(CONFIG_RTE_LIBRTE_TIMER) += test_timer_racecond.c
diff --git a/test/test/meson.build b/test/test/meson.build
index 05e5ddeb0..b00e1201a 100644
--- a/test/test/meson.build
+++ b/test/test/meson.build
@@ -95,6 +95,7 @@  test_sources = files('commands.c',
 	'test_sched.c',
 	'test_service_cores.c',
 	'test_spinlock.c',
+	'test_stack.c',
 	'test_string_fns.c',
 	'test_table.c',
 	'test_table_acl.c',
@@ -132,6 +133,7 @@  test_deps = ['acl',
 	'port',
 	'reorder',
 	'ring',
+	'stack',
 	'timer'
 ]
 
@@ -173,6 +175,7 @@  fast_parallel_test_names = [
         'rwlock_autotest',
         'sched_autotest',
         'spinlock_autotest',
+        'stack_autotest',
         'string_autotest',
         'table_autotest',
         'tailq_autotest',
diff --git a/test/test/test_stack.c b/test/test/test_stack.c
new file mode 100644
index 000000000..510c7aac1
--- /dev/null
+++ b/test/test/test_stack.c
@@ -0,0 +1,394 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <string.h>
+
+#include <rte_lcore.h>
+#include <rte_malloc.h>
+#include <rte_random.h>
+#include <rte_stack.h>
+
+#include "test.h"
+
+#define STACK_SIZE 4096
+#define MAX_BULK 32
+
+static int
+test_stack_push_pop(struct rte_stack *s, void **obj_table, unsigned int bulk_sz)
+{
+	void *popped_objs[STACK_SIZE];
+	unsigned int i, ret;
+
+	for (i = 0; i < STACK_SIZE; i += bulk_sz) {
+		ret = rte_stack_push(s, &obj_table[i], bulk_sz);
+
+		if (ret != bulk_sz) {
+			printf("[%s():%u] push returned: %d (expected %u)\n",
+			       __func__, __LINE__, ret, bulk_sz);
+			return -1;
+		}
+
+		if (rte_stack_count(s) != i + bulk_sz) {
+			printf("[%s():%u] stack count: %u (expected %u)\n",
+			       __func__, __LINE__, rte_stack_count(s),
+			       i + bulk_sz);
+			return -1;
+		}
+
+		if (rte_stack_free_count(s) != STACK_SIZE - i - bulk_sz) {
+			printf("[%s():%u] stack free count: %u (expected %u)\n",
+			       __func__, __LINE__, rte_stack_count(s),
+			       STACK_SIZE - i - bulk_sz);
+			return -1;
+		}
+	}
+
+	for (i = 0; i < STACK_SIZE; i += bulk_sz) {
+		ret = rte_stack_pop(s, &popped_objs[i], bulk_sz);
+
+		if (ret != bulk_sz) {
+			printf("[%s():%u] pop returned: %d (expected %u)\n",
+			       __func__, __LINE__, ret, bulk_sz);
+			return -1;
+		}
+
+		if (rte_stack_count(s) != STACK_SIZE - i - bulk_sz) {
+			printf("[%s():%u] stack count: %u (expected %u)\n",
+			       __func__, __LINE__, rte_stack_count(s),
+			       STACK_SIZE - i - bulk_sz);
+			return -1;
+		}
+
+		if (rte_stack_free_count(s) != i + bulk_sz) {
+			printf("[%s():%u] stack free count: %u (expected %u)\n",
+			       __func__, __LINE__, rte_stack_count(s),
+			       i + bulk_sz);
+			return -1;
+		}
+	}
+
+	for (i = 0; i < STACK_SIZE; i++) {
+		if (obj_table[i] != popped_objs[STACK_SIZE - i - 1]) {
+			printf("[%s():%u] Incorrect value %p at index 0x%x\n",
+			       __func__, __LINE__,
+			       popped_objs[STACK_SIZE - i - 1], i);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+test_stack_basic(void)
+{
+	struct rte_stack *s = NULL;
+	void **obj_table = NULL;
+	int i, ret = -1;
+
+	obj_table = rte_calloc(NULL, STACK_SIZE, sizeof(void *), 0);
+	if (obj_table == NULL) {
+		printf("[%s():%u] failed to calloc %lu bytes\n",
+		       __func__, __LINE__, STACK_SIZE * sizeof(void *));
+		goto fail_test;
+	}
+
+	for (i = 0; i < STACK_SIZE; i++)
+		obj_table[i] = (void *)(uintptr_t)i;
+
+	s = rte_stack_create(__func__, STACK_SIZE, rte_socket_id(), 0);
+	if (s == NULL) {
+		printf("[%s():%u] failed to create a stack\n",
+		       __func__, __LINE__);
+		goto fail_test;
+	}
+
+	if (rte_stack_lookup(__func__) != s) {
+		printf("[%s():%u] failed to lookup a stack\n",
+		       __func__, __LINE__);
+		goto fail_test;
+	}
+
+	if (rte_stack_count(s) != 0) {
+		printf("[%s():%u] stack count: %u (expected 0)\n",
+		       __func__, __LINE__, rte_stack_count(s));
+		goto fail_test;
+	}
+
+	if (rte_stack_free_count(s) != STACK_SIZE) {
+		printf("[%s():%u] stack free count: %u (expected %u)\n",
+		       __func__, __LINE__, rte_stack_count(s), STACK_SIZE);
+		goto fail_test;
+	}
+
+	ret = test_stack_push_pop(s, obj_table, 1);
+	if (ret) {
+		printf("[%s():%u] Single object push/pop failed\n",
+		       __func__, __LINE__);
+		goto fail_test;
+	}
+
+	ret = test_stack_push_pop(s, obj_table, MAX_BULK);
+	if (ret) {
+		printf("[%s():%u] Bulk object push/pop failed\n",
+		       __func__, __LINE__);
+		goto fail_test;
+	}
+
+	ret = rte_stack_push(s, obj_table, 2 * STACK_SIZE);
+	if (ret != 0) {
+		printf("[%s():%u] Excess objects push succeeded\n",
+		       __func__, __LINE__);
+		goto fail_test;
+	}
+
+	ret = rte_stack_pop(s, obj_table, 1);
+	if (ret != 0) {
+		printf("[%s():%u] Empty stack pop succeeded\n",
+		       __func__, __LINE__);
+		goto fail_test;
+	}
+
+	ret = 0;
+
+fail_test:
+	rte_stack_free(s);
+
+	if (obj_table != NULL)
+		rte_free(obj_table);
+
+	return ret;
+}
+
+static int
+test_stack_name_reuse(void)
+{
+	struct rte_stack *s[2];
+
+	s[0] = rte_stack_create("test", STACK_SIZE, rte_socket_id(), 0);
+	if (s[0] == NULL) {
+		printf("[%s():%u] Failed to create a stack\n",
+		       __func__, __LINE__);
+		return -1;
+	}
+
+	s[1] = rte_stack_create("test", STACK_SIZE, rte_socket_id(), 0);
+	if (s[1] != NULL) {
+		printf("[%s():%u] Failed to detect re-used name\n",
+		       __func__, __LINE__);
+		return -1;
+	}
+
+	rte_stack_free(s[0]);
+
+	return 0;
+}
+
+static int
+test_stack_name_length(void)
+{
+	char name[RTE_STACK_NAMESIZE + 1];
+	struct rte_stack *s;
+
+	memset(name, 's', sizeof(name));
+
+	s = rte_stack_create(name, STACK_SIZE, rte_socket_id(), 0);
+	if (s != NULL) {
+		printf("[%s():%u] Failed to prevent long name\n",
+		       __func__, __LINE__);
+		return -1;
+	}
+
+	if (rte_errno != ENAMETOOLONG) {
+		printf("[%s():%u] rte_stack failed to set correct errno on failed lookup\n",
+		       __func__, __LINE__);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_lookup_null(void)
+{
+	struct rte_stack *s = rte_stack_lookup("stack_not_found");
+
+	if (s != NULL) {
+		printf("[%s():%u] rte_stack found a non-existent stack\n",
+		       __func__, __LINE__);
+		return -1;
+	}
+
+	if (rte_errno != ENOENT) {
+		printf("[%s():%u] rte_stack failed to set correct errno on failed lookup\n",
+		       __func__, __LINE__);
+		return -1;
+	}
+
+	s = rte_stack_lookup(NULL);
+
+	if (s != NULL) {
+		printf("[%s():%u] rte_stack found a non-existent stack\n",
+		       __func__, __LINE__);
+		return -1;
+	}
+
+	if (rte_errno != EINVAL) {
+		printf("[%s():%u] rte_stack failed to set correct errno on failed lookup\n",
+		       __func__, __LINE__);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_free_null(void)
+{
+	/* Check whether the library proper handles a NULL pointer */
+	rte_stack_free(NULL);
+
+	return 0;
+}
+
+#define NUM_ITERS_PER_THREAD 100000
+
+struct test_args {
+	struct rte_stack *s;
+	rte_atomic64_t *sz;
+};
+
+static int
+stack_thread_push_pop(void *args)
+{
+	struct test_args *t = args;
+	void **obj_table;
+	int i;
+
+	obj_table = rte_calloc(NULL, STACK_SIZE, sizeof(void *), 0);
+	if (obj_table == NULL) {
+		printf("[%s():%u] failed to calloc %lu bytes\n",
+		       __func__, __LINE__, STACK_SIZE * sizeof(void *));
+		return -1;
+	}
+
+	for (i = 0; i < NUM_ITERS_PER_THREAD; i++) {
+		unsigned int success, num;
+
+		/* Reserve up to min(MAX_BULK, available slots) stack entries,
+		 * then push and pop those stack entries.
+		 */
+		do {
+			uint64_t sz = rte_atomic64_read(t->sz);
+			volatile uint64_t *sz_addr;
+
+			sz_addr = (volatile uint64_t *)&t->sz;
+
+			num = RTE_MIN(rte_rand() % MAX_BULK, STACK_SIZE - sz);
+
+			success = rte_atomic64_cmpset(sz_addr, sz, sz + num);
+		} while (success == 0);
+
+		if (rte_stack_push(t->s, obj_table, num) != num) {
+			printf("[%s():%u] Failed to push %u pointers\n",
+			       __func__, __LINE__, num);
+			rte_free(obj_table);
+			return -1;
+		}
+
+		if (rte_stack_pop(t->s, obj_table, num) != num) {
+			printf("[%s():%u] Failed to pop %u pointers\n",
+			       __func__, __LINE__, num);
+			rte_free(obj_table);
+			return -1;
+		}
+
+		rte_atomic64_sub(t->sz, num);
+	}
+
+	rte_free(obj_table);
+	return 0;
+}
+
+static int
+test_stack_multithreaded(void)
+{
+	struct test_args *args;
+	unsigned int lcore_id;
+	struct rte_stack *s;
+	rte_atomic64_t size;
+
+	printf("[%s():%u] Running with %u lcores\n",
+	       __func__, __LINE__, rte_lcore_count());
+
+	if (rte_lcore_count() < 2)
+		return 0;
+
+	args = rte_malloc(NULL, sizeof(struct test_args) * RTE_MAX_LCORE, 0);
+	if (args == NULL) {
+		printf("[%s():%u] failed to malloc %lu bytes\n",
+		       __func__, __LINE__,
+		       sizeof(struct test_args) * RTE_MAX_LCORE);
+		return -1;
+	}
+
+	s = rte_stack_create("test", STACK_SIZE, rte_socket_id(), 0);
+	if (s == NULL) {
+		printf("[%s():%u] Failed to create a stack\n",
+		       __func__, __LINE__);
+		rte_free(args);
+		return -1;
+	}
+
+	rte_atomic64_init(&size);
+
+	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
+		args[lcore_id].s = s;
+		args[lcore_id].sz = &size;
+
+		if (rte_eal_remote_launch(stack_thread_push_pop,
+					  &args[lcore_id], lcore_id))
+			rte_panic("Failed to launch lcore %d\n", lcore_id);
+	}
+
+	lcore_id = rte_lcore_id();
+
+	args[lcore_id].s = s;
+	args[lcore_id].sz = &size;
+
+	stack_thread_push_pop(&args[lcore_id]);
+
+	rte_eal_mp_wait_lcore();
+
+	rte_stack_free(s);
+	rte_free(args);
+
+	return 0;
+}
+
+static int
+test_stack(void)
+{
+	if (test_stack_basic() < 0)
+		return -1;
+
+	if (test_lookup_null() < 0)
+		return -1;
+
+	if (test_free_null() < 0)
+		return -1;
+
+	if (test_stack_name_reuse() < 0)
+		return -1;
+
+	if (test_stack_name_length() < 0)
+		return -1;
+
+	if (test_stack_multithreaded() < 0)
+		return -1;
+
+	return 0;
+}
+
+REGISTER_TEST_COMMAND(stack_autotest, test_stack);