[dpdk-dev,5/7] test/eventdev: unit and functional tests

Message ID 1479319207-130646-6-git-send-email-harry.van.haaren@intel.com (mailing list archive)
State Superseded, archived
Headers

Checks

Context Check Description
checkpatch/checkpatch warning coding style issues

Commit Message

Van Haaren, Harry Nov. 16, 2016, 6 p.m. UTC
  This commit adds basic unit and functional tests for the eventdev
API. The test code is added in this commit, but not yet enabled until
the next commit.

Signed-off-by: Gage Eads <gage.eads@intel.com>
Signed-off-by: David Hunt <david.hunt@intel.com>
Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 app/test/test_eventdev_func.c | 1268 +++++++++++++++++++++++++++++++++++++++++
 app/test/test_eventdev_unit.c |  557 ++++++++++++++++++
 2 files changed, 1825 insertions(+)
 create mode 100644 app/test/test_eventdev_func.c
 create mode 100644 app/test/test_eventdev_unit.c
  

Comments

Jerin Jacob Nov. 23, 2016, 3:32 a.m. UTC | #1
On Wed, Nov 16, 2016 at 06:00:05PM +0000, Harry van Haaren wrote:
> This commit adds basic unit and functional tests for the eventdev
> API. The test code is added in this commit, but not yet enabled until
> the next commit.
> 
> Signed-off-by: Gage Eads <gage.eads@intel.com>
> Signed-off-by: David Hunt <david.hunt@intel.com>
> Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
> ---

A few comments on portability and usage perspective. See below,

> +#include <rte_eal.h>
> +#include <rte_per_lcore.h>
> +#include <rte_lcore.h>
> +#include <rte_debug.h>
> +#include <rte_ethdev.h>
> +#include <rte_cycles.h>
> +
> +#include <rte_eventdev.h>
> +#include "test.h"
> +
> +
> +static inline int
> +create_ports(struct test *t, int num_ports)
> +{
> +	int i;
> +	static const struct rte_event_port_conf conf = {
> +			.dequeue_queue_depth = 32,
> +			.enqueue_queue_depth = 64,
> +	};

Check the max supported through info get first.

> +
> +	for (i = 0; i < num_ports; i++) {
> +		if (rte_event_port_setup(t->ev, i, &conf) < 0) {
> +			printf("Error setting up port %d\n", i);
> +			return -1;
> +		}
> +		t->port[i] = i;
> +	}
> +
> +	return 0;
> +}
> +
> +
> +static int
> +run_prio_packet_test(struct test *t)

Run per event enqueue priority test if the platform supports
RTE_EVENT_DEV_CAP_EVENT_QOS


> +{
> +	int err;
> +	const uint32_t MAGIC_SEQN[] = {4711, 1234};
> +	const uint32_t PRIORITY[] = {3, 0};
> +	unsigned i;
> +	for(i = 0; i < RTE_DIM(MAGIC_SEQN); i++) {
> +		/* generate pkt and enqueue */
> +		struct rte_event ev;
> +		struct rte_mbuf *arp = rte_gen_arp(0, t->mbuf_pool);
> +		if (!arp) {
> +			printf("%d: gen of pkt failed\n", __LINE__);
> +			return -1;
> +		}
> +		arp->seqn = MAGIC_SEQN[i];

For me, it make sense to don't touch any field in mbuf to make eventdev
model works. use private field to store test specific data

> +
> +		ev = (struct rte_event){
> +			.priority = PRIORITY[i],
> +			.operation = RTE_EVENT_OP_NEW,
> +			.queue_id = t->qid[0],
> +			.mbuf = arp
> +		};
> +		err = rte_event_enqueue(t->ev, t->port[0], &ev, 0);
> +		if (err < 0) {
> +			printf("%d: error failed to enqueue\n", __LINE__);
> +			return -1;
> +		}
> +	}
> +
> +	rte_event_schedule(t->ev);
> +
> +	struct rte_event_dev_stats stats;
> +	err = rte_event_dev_stats_get(t->ev, &stats);
> +	if (err) {
> +		printf("%d: error failed to get stats\n", __LINE__);
> +		return -1;
> +	}
> +
> +	if (stats.port_rx_pkts[t->port[0]] != 2) {
> +		printf("%d: error stats incorrect for directed port\n", __LINE__);
> +		rte_event_dev_dump(stdout, t->ev);
> +		return -1;
> +	}

rely on stats for functional verification may not work in all the
implementation. makes sense to have more concrete functional
verification without stats

> +
> +	struct rte_event ev, ev2;
> +	uint32_t deq_pkts;
> +	deq_pkts = rte_event_dequeue(t->ev, t->port[0], &ev, 0);
> +	if (deq_pkts != 1) {
> +		printf("%d: error failed to deq\n", __LINE__);
> +		rte_event_dev_dump(stdout, t->ev);
> +		return -1;
> +	}
> +	if(ev.mbuf->seqn != MAGIC_SEQN[1]) {
> +		printf("%d: first packet out not highest priority\n", __LINE__);
> +		rte_event_dev_dump(stdout, t->ev);
> +		return -1;
> +	}
> +	rte_pktmbuf_free(ev.mbuf);
> +
> +
> +static int
> +test_overload_trip(struct test *t)

overload tests wont fail in ddr backed systems.(ddr backed system will
mimic infinite size queue to application). So testing against failure
may not work at all in some implementation

> +{
> +	int err;
> +
> +	/* Create instance with 3 directed QIDs going to 3 ports */
> +	if (init(t, 1, 1) < 0 ||
> +			create_ports(t, 1) < 0 ||
> +			create_atomic_qids(t, 1) < 0)
> +		return -1;
> +
  

Patch

diff --git a/app/test/test_eventdev_func.c b/app/test/test_eventdev_func.c
new file mode 100644
index 0000000..d7fe481
--- /dev/null
+++ b/app/test/test_eventdev_func.c
@@ -0,0 +1,1268 @@ 
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_launch.h>
+#include <rte_eal.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+
+#include <rte_eventdev.h>
+#include "test.h"
+
+#define MAX_PORTS 16
+#define MAX_QIDS 16
+#define NUM_PACKETS (1<<18)
+
+struct test {
+	struct rte_mempool *mbuf_pool;
+	int ev;
+	int port[MAX_PORTS];
+	int qid[MAX_QIDS];
+	int nb_qids;
+};
+
+static inline struct rte_mbuf *
+rte_gen_arp(int portid, struct rte_mempool *mp)
+{
+	/*
+	* len = 14 + 46
+	* ARP, Request who-has 10.0.0.1 tell 10.0.0.2, length 46
+	*/
+	static const uint8_t arp_request[] = {
+		/*0x0000:*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xec, 0xa8,
+		0x6b, 0xfd, 0x02, 0x29, 0x08, 0x06, 0x00, 0x01,
+		/*0x0010:*/ 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0xec, 0xa8,
+		0x6b, 0xfd, 0x02, 0x29, 0x0a, 0x00, 0x00, 0x01,
+		/*0x0020:*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+		0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		/*0x0030:*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+	struct rte_mbuf *m;
+	int pkt_len = sizeof(arp_request) - 1;
+
+	m = rte_pktmbuf_alloc(mp);
+	if (!m)
+		return 0;
+
+	memcpy((void *)((uint64_t)m->buf_addr + m->data_off),
+		arp_request, pkt_len);
+	rte_pktmbuf_pkt_len(m) = pkt_len;
+	rte_pktmbuf_data_len(m) = pkt_len;
+
+	RTE_SET_USED(portid);
+	/*
+	 * Ignore MAC address for super-simple testing
+	 * struct ether_addr mac_addr;
+	 * rte_eth_macaddr_get(portid, &mac_addr);
+	 * memcpy((void *)((uint64_t)m->buf_addr + m->data_off + 6),
+	 * &mac_addr, 6);
+	 */
+
+	return m;
+}
+
+/* initialization and config */
+static inline int
+init(struct test *t, int nb_queues, int nb_ports)
+{
+	struct rte_event_dev_config config = {
+			.nb_event_queues = nb_queues,
+			.nb_event_ports = nb_ports,
+	};
+	int ret, nevdevs = rte_event_dev_count();
+
+	void *temp = t->mbuf_pool; /* save and restore mbuf pool */
+
+	memset(t, 0, sizeof(*t));
+	t->mbuf_pool = temp;
+
+	if (nevdevs < 1) {
+		printf("%d: No Eventdev Devices Found\n", __LINE__);
+		return -1;
+	}
+
+	const char *eventdev_name = "evdev_sw0";
+
+	t->ev = rte_event_dev_get_dev_id(eventdev_name);
+	if (t->ev < 0) {
+		printf("%d: Eventdev %s not found - quitting.\n", __LINE__, eventdev_name);
+		return -1;
+	}
+
+	ret = rte_event_dev_configure(t->ev, &config);
+	if (ret < 0)
+		printf("%d: Error configuring device\n", __LINE__);
+	return ret;
+};
+
+static inline int
+create_ports(struct test *t, int num_ports)
+{
+	int i;
+	static const struct rte_event_port_conf conf = {
+			.dequeue_queue_depth = 32,
+			.enqueue_queue_depth = 64,
+	};
+
+	for (i = 0; i < num_ports; i++) {
+		if (rte_event_port_setup(t->ev, i, &conf) < 0) {
+			printf("Error setting up port %d\n", i);
+			return -1;
+		}
+		t->port[i] = i;
+	}
+
+	return 0;
+}
+
+static inline int
+create_atomic_qids(struct test *t, int num_qids)
+{
+	int i;
+
+	/* Q creation */
+	static const struct rte_event_queue_conf conf = {
+			.priority = RTE_EVENT_QUEUE_PRIORITY_NORMAL,
+			.nb_atomic_flows = 1024,
+	};
+
+	for (i = t->nb_qids; i < t->nb_qids + num_qids; i++) {
+		if (rte_event_queue_setup(t->ev, i, &conf) < 0) {
+			printf("%d: error creating qid %d\n", __LINE__, i);
+			return -1;
+		}
+		t->qid[i] = i;
+	}
+	t->nb_qids += num_qids;
+
+	return 0;
+}
+
+static inline int
+create_ordered_qids(struct test *t, int num_qids)
+{
+	int i;
+
+	/* Q creation */
+	static const struct rte_event_queue_conf conf = {
+			.priority = RTE_EVENT_QUEUE_PRIORITY_NORMAL,
+			.nb_atomic_order_sequences = 1024,
+	};
+
+	for (i = t->nb_qids; i < t->nb_qids + num_qids; i++) {
+		if (rte_event_queue_setup(t->ev, i, &conf) < 0) {
+			printf("%d: error creating qid %d\n", __LINE__, i);
+			return -1;
+		}
+		t->qid[i] = i;
+	}
+	t->nb_qids += num_qids;
+
+	return 0;
+}
+
+static inline int
+create_unordered_qids(struct test *t, int num_qids)
+{
+	int i;
+
+	/* Q creation */
+	static const struct rte_event_queue_conf conf = {
+			.priority = RTE_EVENT_QUEUE_PRIORITY_NORMAL,
+	};
+
+	for (i = t->nb_qids; i < t->nb_qids + num_qids; i++) {
+		if (rte_event_queue_setup(t->ev, i, &conf) < 0) {
+			printf("%d: error creating qid %d\n", __LINE__, i);
+			return -1;
+		}
+		t->qid[i] = i;
+	}
+	t->nb_qids += num_qids;
+
+	return 0;
+}
+
+static inline int
+create_directed_qids(struct test *t, int num_qids, int ports[])
+{
+	int i;
+
+	/* Q creation */
+	static const struct rte_event_queue_conf conf = {
+			.priority = RTE_EVENT_QUEUE_PRIORITY_NORMAL,
+			.event_queue_cfg = RTE_EVENT_QUEUE_CFG_SINGLE_CONSUMER,
+	};
+
+	for (i = t->nb_qids; i < t->nb_qids + num_qids; i++) {
+		struct rte_event_queue_link link;
+
+		if (rte_event_queue_setup(t->ev, i, &conf) < 0) {
+			printf("%d: error creating qid %d\n", __LINE__, i);
+			return -1;
+		}
+		t->qid[i] = i;
+
+		link = (struct rte_event_queue_link){
+			t->qid[i],
+			RTE_EVENT_QUEUE_SERVICE_PRIORITY_NORMAL
+		};
+		if (rte_event_port_link(t->ev, ports[i - t->nb_qids], &link, 1) != 1) {
+			printf("%d: error creating link for qid %d\n",
+					__LINE__, i);
+			return -1;
+		}
+	}
+	t->nb_qids += num_qids;
+
+	return 0;
+}
+
+/* destruction */
+static inline int
+cleanup(struct test *t)
+{
+	rte_event_dev_stop(t->ev);
+	rte_event_dev_close(t->ev);
+	return 0;
+};
+
+/* run_prio_packet_test
+ * This performs a basic packet priority check on the test instance passed in.
+ * It is factored out of the main priority tests as the same tests must be
+ * performed to ensure prioritization of each type of QID.
+ *
+ * Requirements:
+ *  - An initialized test structure, including mempool
+ *  - t->port[0] is initialized for both Enq / Deq of packets to the QID
+ *  - t->qid[0] is the QID to be tested
+ *  - if LB QID, the CQ must be mapped to the QID.
+ */
+static int
+run_prio_packet_test(struct test *t)
+{
+	int err;
+	const uint32_t MAGIC_SEQN[] = {4711, 1234};
+	const uint32_t PRIORITY[] = {3, 0};
+	unsigned i;
+	for(i = 0; i < RTE_DIM(MAGIC_SEQN); i++) {
+		/* generate pkt and enqueue */
+		struct rte_event ev;
+		struct rte_mbuf *arp = rte_gen_arp(0, t->mbuf_pool);
+		if (!arp) {
+			printf("%d: gen of pkt failed\n", __LINE__);
+			return -1;
+		}
+		arp->seqn = MAGIC_SEQN[i];
+
+		ev = (struct rte_event){
+			.priority = PRIORITY[i],
+			.operation = RTE_EVENT_OP_NEW,
+			.queue_id = t->qid[0],
+			.mbuf = arp
+		};
+		err = rte_event_enqueue(t->ev, t->port[0], &ev, 0);
+		if (err < 0) {
+			printf("%d: error failed to enqueue\n", __LINE__);
+			return -1;
+		}
+	}
+
+	rte_event_schedule(t->ev);
+
+	struct rte_event_dev_stats stats;
+	err = rte_event_dev_stats_get(t->ev, &stats);
+	if (err) {
+		printf("%d: error failed to get stats\n", __LINE__);
+		return -1;
+	}
+
+	if (stats.port_rx_pkts[t->port[0]] != 2) {
+		printf("%d: error stats incorrect for directed port\n", __LINE__);
+		rte_event_dev_dump(stdout, t->ev);
+		return -1;
+	}
+
+	struct rte_event ev, ev2;
+	uint32_t deq_pkts;
+	deq_pkts = rte_event_dequeue(t->ev, t->port[0], &ev, 0);
+	if (deq_pkts != 1) {
+		printf("%d: error failed to deq\n", __LINE__);
+		rte_event_dev_dump(stdout, t->ev);
+		return -1;
+	}
+	if(ev.mbuf->seqn != MAGIC_SEQN[1]) {
+		printf("%d: first packet out not highest priority\n", __LINE__);
+		rte_event_dev_dump(stdout, t->ev);
+		return -1;
+	}
+	rte_pktmbuf_free(ev.mbuf);
+
+	deq_pkts = rte_event_dequeue(t->ev, t->port[0], &ev2, 0);
+	if (deq_pkts != 1) {
+		printf("%d: error failed to deq\n", __LINE__);
+		rte_event_dev_dump(stdout, t->ev);
+		return -1;
+	}
+	if(ev2.mbuf->seqn != MAGIC_SEQN[0]) {
+		printf("%d: second packet out not lower priority\n", __LINE__);
+		rte_event_dev_dump(stdout, t->ev);
+		return -1;
+	}
+	rte_pktmbuf_free(ev2.mbuf);
+
+	cleanup(t);
+	return 0;
+}
+
+static int
+test_single_directed_packet(struct test *t)
+{
+	const int rx_enq = 0;
+	const int wrk_enq = 2;
+	int err;
+
+	/* Create instance with 3 directed QIDs going to 3 ports */
+	if (init(t, 3, 3) < 0 ||
+			create_ports(t, 3) < 0 ||
+			create_directed_qids(t, 3, t->port) < 0)
+		return -1;
+
+	if (rte_event_dev_start(t->ev) < 0) {
+		printf("%d: Error with start call\n", __LINE__);
+		return -1;
+	}
+
+	/************** FORWARD ****************/
+	struct rte_mbuf *arp = rte_gen_arp(0, t->mbuf_pool);
+	struct rte_event ev = {
+			.operation = RTE_EVENT_OP_NEW,
+			.queue_id = wrk_enq,
+			.mbuf = arp,
+	};
+
+	if (!arp) {
+		printf("%d: gen of pkt failed\n", __LINE__);
+		return -1;
+	}
+
+	const uint32_t MAGIC_SEQN = 4711;
+	arp->seqn = MAGIC_SEQN;
+
+	/* generate pkt and enqueue */
+	err = rte_event_enqueue(t->ev, rx_enq, &ev, 0);
+	if (err < 0) {
+		printf("%d: error failed to enqueue\n", __LINE__);
+		return -1;
+	}
+
+	/* Run schedule() as dir packets may need to be re-ordered */
+	if (rte_event_schedule(t->ev) < 0) {
+		printf("%d: Error with schedule call\n", __LINE__);
+		return -1;
+	}
+
+	struct rte_event_dev_stats stats;
+	err = rte_event_dev_stats_get(t->ev, &stats);
+	if (err) {
+		printf("%d: error failed to get stats\n", __LINE__);
+		return -1;
+	}
+
+	if (stats.port_rx_pkts[rx_enq] != 1) {
+		printf("%d: error stats incorrect for directed port\n", __LINE__);
+		return -1;
+	}
+
+	uint32_t deq_pkts;
+	deq_pkts = rte_event_dequeue(t->ev, wrk_enq, &ev, 1);
+	if (deq_pkts != 1) {
+		printf("%d: error failed to deq\n", __LINE__);
+		return -1;
+	}
+
+	err = rte_event_dev_stats_get(t->ev, &stats);
+	if (stats.port_rx_pkts[wrk_enq] != 0 &&
+			stats.port_rx_pkts[wrk_enq] != 1) {
+		printf("%d: error directed stats post-dequeue\n", __LINE__);
+		return -1;
+	}
+
+	if (ev.mbuf->seqn != MAGIC_SEQN) {
+		printf("%d: error magic sequence number not dequeued\n", __LINE__);
+		return -1;
+	}
+
+	rte_pktmbuf_free(ev.mbuf);
+	cleanup(t);
+	return 0;
+}
+
+static int
+test_overload_trip(struct test *t)
+{
+	int err;
+
+	/* Create instance with 3 directed QIDs going to 3 ports */
+	if (init(t, 1, 1) < 0 ||
+			create_ports(t, 1) < 0 ||
+			create_atomic_qids(t, 1) < 0)
+		return -1;
+
+	struct rte_event_queue_link link = {t->qid[0],
+			RTE_EVENT_QUEUE_SERVICE_PRIORITY_NORMAL };
+	int ret = rte_event_port_link(t->ev, t->port[0], &link, 1);
+	if (ret != 1) {
+		printf("%d: error mapping lb qid0\n", __LINE__);
+		return -1;
+	}
+
+	if (rte_event_dev_start(t->ev) < 0) {
+		printf("%d: Error with start call\n", __LINE__);
+		return -1;
+	}
+
+	struct rte_mbuf *arp = rte_gen_arp(0, t->mbuf_pool);
+	if (!arp) {
+		printf("%d: gen of pkt failed\n", __LINE__);
+		return -1;
+	}
+
+	/* 512 packets is threshold
+	 * iters 0 - 511 is 512 packets, then overload will be flagged
+	 * iter 512 (the 513th pkt) is the first refused NEW packet */
+	const uint32_t THRES = (256+1);
+	uint32_t i;
+	for (i = 0; i < THRES; i++) {
+		struct rte_event ev = {
+				.operation = RTE_EVENT_OP_NEW,
+				.queue_id = t->qid[0],
+				.mbuf = arp,
+		};
+		err = rte_event_enqueue(t->ev, 0, &ev, 0);
+		if(i == THRES-1) {
+			if(err != -ENOSPC) {
+				printf("%d: overload trip didn't cause NEW pkt enq fail\n", __LINE__);
+				return -1;
+			}
+			else {
+				//printf("iter %d -ENOSPC returned for new enq as expected.\n", i);
+			}
+		} else {
+			if (err < 0) {
+				printf("%d: error failed to enqueue\n", __LINE__);
+				return -1;
+			}
+		}
+	}
+
+	for (i = 0; i < THRES; i++) {
+		if (rte_event_schedule(t->ev) < 0) {
+			printf("%d: Error with schedule call\n", __LINE__);
+			return -1;
+		}
+
+		uint32_t deq_pkts;
+		struct rte_event ev;
+		deq_pkts = rte_event_dequeue(t->ev, 0, &ev, 1);
+
+		/* i == THRES-1 *should* fail to deq, due to NEW pkt rejection
+		 * when enqueue is attempted in overload mode */
+		if (i == (THRES-1) && deq_pkts == 0)
+			break;
+
+		if (deq_pkts != 1) {
+			printf("%d: warning failed to deq event i = %d\n",
+					__LINE__, i);
+			//return -1;
+		}
+	}
+
+	rte_pktmbuf_free(arp);
+	cleanup(t);
+	return 0;
+}
+
+static int
+test_directed_overload(struct test *t)
+{
+	int err;
+
+	/* Create instance with 3 directed QIDs going to 3 ports */
+	if (init(t, 1, 1) < 0 ||
+			create_ports(t, 1) < 0 ||
+			create_directed_qids(t, 1, t->port) < 0)
+		return -1;
+
+	if (rte_event_dev_start(t->ev) < 0) {
+		printf("%d: Error with start call\n", __LINE__);
+		return -1;
+	}
+
+	/* 512 packets is threshold
+	 * iters 0 - 511 is 512 packets, then overload will be flagged
+	 * iter 512 (the 513th pkt) is the first refused NEW packet */
+	const uint32_t THRES = (256+1);
+	uint32_t i;
+	for (i = 0; i < THRES; i++) {
+		struct rte_event ev = {
+				.operation = RTE_EVENT_OP_NEW,
+				.queue_id = t->qid[0],
+				.event = (uintptr_t)i,
+		};
+		err = rte_event_enqueue(t->ev, 0, &ev, 0);
+		if(i == THRES-1) {
+			if(err != -ENOSPC) {
+				printf("%d: overload trip didn't cause NEW pkt enq fail\n", __LINE__);
+				//return -1;
+			}
+			else {
+				//printf("iter %d -ENOSPC returned for new enq as expected.\n", i);
+			}
+		} else {
+			if (err < 0) {
+				printf("%d: error failed to enqueue\n", __LINE__);
+				return -1;
+			}
+		}
+	}
+
+	if (rte_event_schedule(t->ev) < 0) {
+		printf("%d: Error with schedule call\n", __LINE__);
+		return -1;
+	}
+
+	uint32_t pkt_deq_cntr = 0;
+	for (i = 0; i < THRES; i++) {
+		if (rte_event_schedule(t->ev) < 0) {
+			printf("%d: Error with schedule call\n", __LINE__);
+			return -1;
+		}
+
+		int32_t deq_pkts;
+		struct rte_event ev;
+		deq_pkts = rte_event_dequeue(t->ev, 0, &ev, 1);
+
+		/* i == THRES-1 *should* fail to deq, due to NEW pkt rejection
+		 * when enqueue is attempted in overload mode */
+		if (i == (THRES-1) && deq_pkts == 0)
+			break;
+
+		if (deq_pkts != 1) {
+			printf("%d: warning failed to deq (iter = %d), ret %d. Dumping stats\n",
+					__LINE__, i, deq_pkts);
+			rte_event_dev_dump(stdout, t->ev);
+			return -1;
+		}
+		pkt_deq_cntr += deq_pkts;
+	}
+
+	cleanup(t);
+	return 0;
+}
+
+
+static int
+test_priority_directed(struct test *t)
+{
+	if (init(t, 1, 1) < 0 ||
+			create_ports(t, 1) < 0 ||
+			create_directed_qids(t, 1, t->port) < 0) {
+		printf("%d: Error initialising device\n", __LINE__);
+		return -1;
+	}
+
+	if (rte_event_dev_start(t->ev) < 0) {
+		printf("%d: Error with start call\n", __LINE__);
+		return -1;
+	}
+
+	return run_prio_packet_test(t);
+}
+
+static int
+test_priority_atomic(struct test *t)
+{
+	if (init(t, 1, 1) < 0 ||
+			create_ports(t, 1) < 0 ||
+			create_atomic_qids(t, 1) < 0) {
+		printf("%d: Error initialising device\n", __LINE__);
+		return -1;
+	}
+
+	/* map the QID */
+	struct rte_event_queue_link link = {t->qid[0],
+			RTE_EVENT_QUEUE_SERVICE_PRIORITY_NORMAL };
+	if (rte_event_port_link(t->ev, t->port[0], &link, 1) != 1) {
+		printf("%d: error mapping qid to port\n", __LINE__);
+		return -1;
+	}
+	if (rte_event_dev_start(t->ev) < 0) {
+		printf("%d: Error with start call\n", __LINE__);
+		return -1;
+	}
+
+	return run_prio_packet_test(t);
+}
+
+static int
+test_priority_ordered(struct test *t)
+{
+	if (init(t, 1, 1) < 0 ||
+			create_ports(t, 1) < 0 ||
+			create_ordered_qids(t, 1) < 0) {
+		printf("%d: Error initialising device\n", __LINE__);
+		return -1;
+	}
+
+	/* map the QID */
+	struct rte_event_queue_link link = {t->qid[0],
+			RTE_EVENT_QUEUE_SERVICE_PRIORITY_NORMAL };
+	if (rte_event_port_link(t->ev, t->port[0], &link, 1) != 1) {
+		printf("%d: error mapping qid to port\n", __LINE__);
+		return -1;
+	}
+	if (rte_event_dev_start(t->ev) < 0) {
+		printf("%d: Error with start call\n", __LINE__);
+		return -1;
+	}
+
+	return run_prio_packet_test(t);
+}
+
+static int
+test_priority_unordered(struct test *t)
+{
+	if (init(t, 1, 1) < 0 ||
+			create_ports(t, 1) < 0 ||
+			create_unordered_qids(t, 1) < 0) {
+		printf("%d: Error initialising device\n", __LINE__);
+		return -1;
+	}
+
+	/* map the QID */
+	struct rte_event_queue_link link = {t->qid[0],
+			RTE_EVENT_QUEUE_SERVICE_PRIORITY_NORMAL };
+	if (rte_event_port_link(t->ev, t->port[0], &link, 1) != 1) {
+		printf("%d: error mapping qid to port\n", __LINE__);
+		return -1;
+	}
+	if (rte_event_dev_start(t->ev) < 0) {
+		printf("%d: Error with start call\n", __LINE__);
+		return -1;
+	}
+
+	return run_prio_packet_test(t);
+}
+
+static int
+burst_packets(struct test *t)
+{
+	/************** CONFIG ****************/
+	uint32_t i;
+	int err;
+	int ret;
+
+	/* Create instance with 4 ports and 2 queues */
+	if (init(t, 2, 2) < 0 ||
+			create_ports(t, 2) < 0 ||
+			create_atomic_qids(t, 2) < 0) {
+		printf("%d: Error initialising device\n", __LINE__);
+		return -1;
+	}
+
+	/* CQ mapping to QID */
+	struct rte_event_queue_link link = {t->qid[0],
+			RTE_EVENT_QUEUE_SERVICE_PRIORITY_NORMAL };
+	ret = rte_event_port_link(t->ev, t->port[0], &link, 1);
+	if (ret != 1) {
+		printf("%d: error mapping lb qid0\n", __LINE__);
+		return -1;
+	}
+	link.queue_id = t->qid[1];
+	ret = rte_event_port_link(t->ev, t->port[1], &link, 1);
+	if (ret != 1) {
+		printf("%d: error mapping lb qid1\n", __LINE__);
+		return -1;
+	}
+
+	if (rte_event_dev_start(t->ev) < 0) {
+		printf("%d: Error with start call\n", __LINE__);
+		return -1;
+	}
+
+	/************** FORWARD ****************/
+	const uint32_t rx_port = 0;
+	const uint32_t NUM_PKTS = 2;
+
+	for (i = 0; i < NUM_PKTS; i++) {
+		struct rte_mbuf *arp = rte_gen_arp(0, t->mbuf_pool);
+		if (!arp) {
+			printf("%d: error generating pkt\n" , __LINE__);
+			return -1;
+		}
+
+		struct rte_event ev = {
+				.operation = RTE_EVENT_OP_NEW,
+				.queue_id = i % 2,
+				.flow_id = i % 3,
+				.mbuf = arp,
+		};
+		/* generate pkt and enqueue */
+		err = rte_event_enqueue(t->ev, t->port[rx_port], &ev, 0);
+		if (err < 1) {
+			printf("%d: Failed to enqueue\n", __LINE__);
+			return -1;
+		}
+	}
+	int16_t pkts = rte_event_schedule(t->ev);
+
+	RTE_SET_USED(pkts);
+
+	/* Check stats for all NUM_PKTS arrived to sched core */
+	struct rte_event_dev_stats stats;
+
+	err = rte_event_dev_stats_get(t->ev, &stats);
+	if (err) {
+		printf("%d: failed to get stats\n", __LINE__);
+		return -1;
+	}
+	if (stats.rx_pkts != NUM_PKTS || stats.tx_pkts != NUM_PKTS) {
+		printf("%d: Sched core didn't receive all %d pkts\n", __LINE__, NUM_PKTS);
+		rte_event_dev_dump(stdout, t->ev);
+		return -1;
+	}
+
+	uint32_t deq_pkts;
+	int p;
+
+	deq_pkts = 0;
+	/******** DEQ QID 1 *******/
+	do {
+		struct rte_event ev;
+		p = rte_event_dequeue(t->ev, t->port[0], &ev, 0);
+		deq_pkts += p;
+		rte_pktmbuf_free(ev.mbuf);
+	} while (p);
+
+	if (deq_pkts != NUM_PKTS/2) {
+		printf("%d: Half of NUM_PKTS didn't arrive at port 1\n", __LINE__);
+		return -1;
+	}
+
+	/******** DEQ QID 2 *******/
+	deq_pkts = 0;
+	do {
+		struct rte_event ev;
+		p = rte_event_dequeue(t->ev, t->port[1], &ev, 0);
+		deq_pkts += p;
+		rte_pktmbuf_free(ev.mbuf);
+	} while (p);
+	if (deq_pkts != NUM_PKTS/2) {
+		printf("%d: Half of NUM_PKTS didn't arrive at port 2\n", __LINE__);
+		return -1;
+	}
+
+	cleanup(t);
+	return 0;
+}
+
+static int
+load_balancing(struct test *t)
+{
+	const int rx_enq = 0;
+	int err;
+	uint32_t i;
+
+	if (init(t, 1, 4) < 0 ||
+			create_ports(t, 4) < 0 ||
+			create_atomic_qids(t, 1) < 0) {
+		printf("%d: Error initialising device\n", __LINE__);
+		return -1;
+	}
+
+	struct rte_event_queue_link link = {t->qid[0],
+			RTE_EVENT_QUEUE_SERVICE_PRIORITY_NORMAL };
+	for (i = 0; i < 3; i++) {
+		/* map port 1 - 3 inclusive */
+		if (rte_event_port_link(t->ev, t->port[i+1], &link, 1) != 1) {
+			printf("%d: error mapping qid to port %d\n", __LINE__, i);
+			return -1;
+		}
+	}
+
+	if (rte_event_dev_start(t->ev) < 0) {
+		printf("%d: Error with start call\n", __LINE__);
+		return -1;
+	}
+
+	/************** FORWARD ****************/
+	/*
+	 * Create a set of flows that test the load-balancing operation of the
+	 * implementation. Fill CQ 0 and 1 with flows 0 and 1, and test
+	 * with a new flow, which should be sent to the 3rd mapped CQ
+	 */
+	static uint32_t flows[] = {0, 1, 1, 0, 0, 2, 2, 0, 2};
+#define PKT_NUM (sizeof(flows) / sizeof(flows[0]))
+	for (i = 0; i < PKT_NUM; i++) {
+		struct rte_mbuf *arp = rte_gen_arp(0, t->mbuf_pool);
+		if (!arp) {
+			printf("%d: gen of pkt failed\n", __LINE__);
+			return -1;
+		}
+
+		struct rte_event ev = {
+				.operation = RTE_EVENT_OP_NEW,
+				.queue_id = t->qid[0],
+				.flow_id = flows[i],
+				.mbuf = arp,
+		};
+		/* generate pkt and enqueue */
+		err = rte_event_enqueue(t->ev, t->port[rx_enq], &ev, 0);
+		if (err < 1) {
+			printf("%d: Failed to enqueue\n", __LINE__);
+			return -1;
+		}
+	}
+
+	rte_event_schedule(t->ev);
+
+	struct rte_event_dev_stats stats;
+	err = rte_event_dev_stats_get(t->ev, &stats);
+	if (err) {
+		printf("%d: failed to get stats\n", __LINE__);
+		return -1;
+	}
+
+	if (stats.port_inflight[1] != 4) {
+		printf("%d:%s: port 1 inflight not correct\n", __LINE__, __func__);
+		return -1;
+	}
+	if (stats.port_inflight[2] != 2) {
+		printf("%d:%s: port 2 inflight not correct\n", __LINE__, __func__);
+		return -1;
+	}
+	if (stats.port_inflight[3] != 3) {
+		printf("%d:%s: port 3 inflight not correct\n", __LINE__, __func__);
+		return -1;
+	}
+
+	cleanup(t);
+	return 0;
+}
+
+static int
+invalid_qid(struct test *t)
+{
+	struct rte_event_dev_stats stats;
+	const int rx_enq = 0;
+	int err;
+	uint32_t i;
+
+	if (init(t, 1, 4) < 0 ||
+			create_ports(t, 4) < 0 ||
+			create_atomic_qids(t, 1) < 0) {
+		printf("%d: Error initialising device\n", __LINE__);
+		return -1;
+	}
+
+	/* CQ mapping to QID */
+	for(i = 0; i < 4; i++) {
+		struct rte_event_queue_link link = {t->qid[0],
+				RTE_EVENT_QUEUE_SERVICE_PRIORITY_NORMAL };
+		err = rte_event_port_link(t->ev, t->port[i], &link, 1);
+		if (err != 1) {
+			printf("%d: error mapping port 1 qid\n", __LINE__);
+			return -1;
+		}
+	}
+
+	if (rte_event_dev_start(t->ev) < 0) {
+		printf("%d: Error with start call\n", __LINE__);
+		return -1;
+	}
+
+	/*
+	 * Send in a packet with an invalid qid to the scheduler.
+	 * We should see the packed enqueued OK, but the inflights for
+	 * that packet should not be incremented, and the rx_dropped
+	 * should be incremented.
+	 */
+	static uint32_t flows1[] = {20};
+
+#define PKT_NUM1 (sizeof(flows1) / sizeof(flows1[0]))
+
+	for (i = 0; i < PKT_NUM1; i++) {
+		struct rte_mbuf *arp = rte_gen_arp(0, t->mbuf_pool);
+		if (!arp) {
+			printf("%d: gen of pkt failed\n", __LINE__);
+			return -1;
+		}
+
+		struct rte_event ev = {
+				.operation = RTE_EVENT_OP_NEW,
+				.queue_id = t->qid[0] + flows1[i],
+				.flow_id = i,
+				.mbuf = arp,
+		};
+		/* generate pkt and enqueue */
+		err = rte_event_enqueue(t->ev, t->port[rx_enq], &ev, 0);
+		if (err < 1) {
+			printf("%d: Failed to enqueue\n", __LINE__);
+			return -1;
+		}
+	}
+
+	/* call the scheduler */
+	int16_t pkts = rte_event_schedule(t->ev);
+	RTE_SET_USED(pkts);
+
+	err = rte_event_dev_stats_get(t->ev, &stats);
+	if (err) {
+		printf("%d: failed to get stats\n", __LINE__);
+		return -1;
+	}
+
+	/*
+	 * Now check the resulting inflights on the port, and the rx_dropped.
+	 */
+	if (stats.port_inflight[0] != 0) {
+		printf("%d:%s: port 1 inflight count not correct\n", __LINE__, __func__);
+		rte_event_dev_dump(stdout, 0);
+		return -1;
+	}
+	if (stats.port_rx_dropped[0] != 1) {
+		printf("%d:%s: port 1 drops\n", __LINE__, __func__);
+		rte_event_dev_dump(stdout, 0);
+		return -1;
+	}
+	/* each packet drop should only be counted in one place - port or dev */
+	if (stats.rx_dropped != 0) {
+		printf("%d:%s: port 1 dropped count not correct\n", __LINE__, __func__);
+		rte_event_dev_dump(stdout, 0);
+		return -1;
+	}
+
+	cleanup(t);
+	return 0;
+}
+
+static int
+worker_loopback_worker_fn(void *arg)
+{
+	struct test *t = arg;
+	uint8_t port = t->port[1];
+	int count = 0;
+	int err;
+
+	/*
+	 * Takes packets from the input port and then loops them back through
+	 * the Queue Manager. Each packet gets looped through QIDs 0-8, 16 times,
+	 * so each packet goes through 8*16 = 128 times.
+	 */
+	printf("%d: \tWorker function started\n", __LINE__);
+	while (count < NUM_PACKETS) {
+#define BURST_SIZE 32
+		struct rte_event ev[BURST_SIZE];
+		uint16_t i, nb_rx = rte_event_dequeue(t->ev, port, ev, BURST_SIZE);
+		if (nb_rx == 0) {
+			rte_pause();
+			continue;
+		}
+
+		for (i = 0; i < nb_rx; i++) {
+			ev[i].queue_id++;
+			if (ev[i].queue_id != 8) {
+				ev[i].operation = RTE_EVENT_OP_FORWARD;
+				err = rte_event_enqueue(t->ev, port, &ev[i], 0);
+				if (err <= 0) {
+					printf("%d: Can't enqueue FWD!!\n", __LINE__);
+					return -1;
+				}
+				continue;
+			}
+
+			ev[i].queue_id = 0;
+			ev[i].mbuf->udata64++;
+			if (ev[i].mbuf->udata64 != 16) {
+				ev[i].operation = RTE_EVENT_OP_FORWARD;
+				err = rte_event_enqueue(t->ev, port, &ev[i], 0);
+				if (err <= 0) {
+					printf("%d: Can't enqueue FWD!!\n", __LINE__);
+					return -1;
+				}
+				continue;
+			}
+			/* we have hit 16 iterations through system - drop */
+			rte_pktmbuf_free(ev[i].mbuf);
+			count++;
+			ev[i].operation = RTE_EVENT_OP_DROP;
+			err = rte_event_enqueue(t->ev, port, &ev[i], 0);
+			if(err != 1) {
+				printf("%d drop enqueue failed\n", __LINE__);
+				return -1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int
+worker_loopback_producer_fn(void *arg)
+{
+	struct test *t = arg;
+	uint8_t port = t->port[0];
+	uint64_t count = 0;
+
+	printf("%d: \tProducer function started\n", __LINE__);
+	while (count < NUM_PACKETS) {
+		struct rte_mbuf *m = rte_pktmbuf_alloc(t->mbuf_pool);
+		if (m == NULL) {
+			printf("%d: Error allocating mbuf\n", __LINE__);
+			return -1;
+		}
+		m->udata64 = 0;
+
+		struct rte_event ev = {
+				.operation = RTE_EVENT_OP_NEW,
+				.queue_id = t->qid[0],
+				.flow_id = (uintptr_t)m & 0xFFFF,
+				.mbuf = m,
+		};
+
+		while (rte_event_enqueue(t->ev, port, &ev, 0) != 1)
+			rte_pause();
+
+		count++;
+	}
+
+	return 0;
+}
+
+static int
+worker_loopback(struct test *t)
+{
+	/* use a single producer core, and a worker core to see what happens
+	 * if the worker loops packets back multiple times
+	 */
+	struct rte_event_dev_stats stats;
+	uint64_t print_cycles = 0, cycles = 0;
+	uint64_t tx_pkts = 0;
+	int err;
+	int w_lcore, p_lcore;
+	uint32_t i;
+
+	if (init(t, 8, 2) < 0 ||
+			create_ports(t, 2) < 0 ||
+			create_atomic_qids(t, 8) < 0) {
+		printf("%d: Error initialising device\n", __LINE__);
+		return -1;
+	}
+
+	/* CQ mapping to QID */
+	for(i = 0; i < 8; i++) {
+		struct rte_event_queue_link link = {t->qid[i],
+				RTE_EVENT_QUEUE_SERVICE_PRIORITY_NORMAL };
+		err = rte_event_port_link(t->ev, t->port[1], &link, 1);
+		if (err != 1) {
+			printf("%d: error mapping port 2 qid %d\n", __LINE__, i);
+			return -1;
+		}
+	}
+
+	if (rte_event_dev_start(t->ev) < 0) {
+		printf("%d: Error with start call\n", __LINE__);
+		return -1;
+	}
+
+	p_lcore = rte_get_next_lcore(
+			/* start core */ -1,
+			/* skip master */ 1,
+			/* wrap */ 0);
+	w_lcore = rte_get_next_lcore(p_lcore, 1, 0);
+
+	rte_eal_remote_launch(worker_loopback_producer_fn, t, p_lcore);
+	rte_eal_remote_launch(worker_loopback_worker_fn, t, w_lcore);
+
+	print_cycles = cycles = rte_get_timer_cycles();
+	while (rte_eal_get_lcore_state(p_lcore) != FINISHED ||
+			rte_eal_get_lcore_state(w_lcore) != FINISHED) {
+
+		rte_event_schedule(t->ev);
+
+		uint64_t new_cycles = rte_get_timer_cycles();
+
+		if (new_cycles - print_cycles > rte_get_timer_hz()) {
+			rte_event_dev_stats_get(t->ev, &stats);
+			printf("%d: \tSched Rx = %" PRIu64 ", Tx = %" PRIu64 "\n",
+					__LINE__, stats.rx_pkts, stats.tx_pkts);
+
+			print_cycles = new_cycles;
+		}
+		if (new_cycles - cycles > rte_get_timer_hz() * 3) {
+			rte_event_dev_stats_get(t->ev, &stats);
+			if (stats.tx_pkts == tx_pkts) {
+				rte_event_dev_dump(stdout, t->ev);
+				printf("%d: \nNo schedules for seconds, deadlock\n", __LINE__);
+				return -1;
+			}
+			tx_pkts = stats.tx_pkts;
+			cycles = new_cycles;
+		}
+	}
+
+	rte_eal_mp_wait_lcore();
+
+	//rte_event_dev_dump(stdout, 0);
+
+	cleanup(t);
+	return 0;
+}
+
+static struct rte_mempool *eventdev_func_mempool;
+
+static int
+test_eventdev(void)
+{
+	struct test *t = malloc(sizeof(struct test));
+	int ret;
+
+	/* Only create mbuf pool once, reuse for each test run */
+	if (!eventdev_func_mempool) {
+		eventdev_func_mempool = rte_pktmbuf_pool_create("EVDEV_SA_MBUF_POOL",
+				(1<<16), /* size */
+				32 /*MBUF_CACHE_SIZE*/,
+				0,
+				RTE_MBUF_DEFAULT_BUF_SIZE,
+				rte_socket_id());
+		if (!eventdev_func_mempool) {
+			printf("ERROR creating mempool\n");
+			return -1;
+		}
+	}
+	t->mbuf_pool = eventdev_func_mempool;
+
+	printf("*** Running Single Directed Packet test...\n");
+	ret = test_single_directed_packet(t);
+	if (ret != 0) {
+		printf("ERROR - Single Directed Packet test FAILED.\n");
+		return ret;
+	}
+	printf("*** Running Overload Trip test...\n");
+	ret = test_overload_trip(t);
+	if (ret != 0) {
+		printf("ERROR - Overload Trip test FAILED.\n");
+		return ret;
+	}
+	printf("*** Running Directed Overload test...\n");
+	ret = test_directed_overload(t);
+	if (ret != 0) {
+		printf("ERROR - Directed Overload test FAILED.\n");
+		return ret;
+	}
+	printf("*** Running Prioritized Directed test...\n");
+	ret = test_priority_directed(t);
+	if (ret != 0) {
+		printf("ERROR - Prioritized Directed test FAILED.\n");
+		return ret;
+	}
+	printf("*** Running Prioritized Atomic test...\n");
+	ret = test_priority_atomic(t);
+	if (ret != 0) {
+		printf("ERROR - Prioritized Atomic test FAILED.\n");
+		return ret;
+	}
+
+	printf("*** Running Prioritized Ordered test...\n");
+	ret = test_priority_ordered(t);
+	if (ret != 0) {
+		printf("ERROR - Prioritized Ordered test FAILED.\n");
+		return ret;
+	}
+	printf("*** Running Prioritized Unordered test...\n");
+	ret = test_priority_unordered(t);
+	if (ret != 0) {
+		printf("ERROR - Prioritized Unordered test FAILED.\n");
+		return ret;
+	}
+	printf("*** Running Burst Packets test...\n");
+	ret = burst_packets(t);
+	if (ret != 0) {
+		printf("ERROR - Burst Packets test FAILED.\n");
+		return ret;
+	}
+	printf("*** Running Load Balancing test...\n");
+	ret = load_balancing(t);
+	if (ret != 0) {
+		printf("ERROR - Load Balancing test FAILED.\n");
+		return ret;
+	}
+	printf("*** Running Invalid QID test...\n");
+	ret = invalid_qid(t);
+	if (ret != 0) {
+		printf("ERROR - Invalid QID test FAILED.\n");
+		return ret;
+	}
+	if (rte_lcore_count() >= 3) {
+		printf("*** Running Worker loopback test...\n");
+		ret = worker_loopback(t);
+		if (ret != 0) {
+			printf("ERROR - Worker loopback test FAILED.\n");
+			return ret;
+		}
+	} else {
+		printf("### Not enough cores for worker loopback test. \n");
+		printf("### Need at least 3 cores for test.\n");
+	}
+	/* Free test instance, leaving mempool initialized, and a pointer to it
+	 * in the static eventdev_func_mempool variable. It is re-used on re-runs */
+	free(t);
+
+	return 0;
+}
+
+REGISTER_TEST_COMMAND(eventdev_func_autotest, test_eventdev);
diff --git a/app/test/test_eventdev_unit.c b/app/test/test_eventdev_unit.c
new file mode 100644
index 0000000..c145401
--- /dev/null
+++ b/app/test/test_eventdev_unit.c
@@ -0,0 +1,557 @@ 
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include "test.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include <rte_eventdev.h>
+#include <rte_lcore.h>
+#include <rte_mbuf.h>
+
+typedef enum eventdev_api_ut_ids_s {
+	EVENTDEV_API_UT_001 = 1,
+	EVENTDEV_API_UT_002,
+	EVENTDEV_API_UT_003,
+	EVENTDEV_API_UT_004,
+	EVENTDEV_API_UT_005,
+	EVENTDEV_API_UT_006,
+	EVENTDEV_API_UT_007,
+	EVENTDEV_API_UT_008,
+	EVENTDEV_API_UT_009,
+	EVENTDEV_API_UT_010,
+	EVENTDEV_API_UT_011,
+	EVENTDEV_API_UT_012,
+	EVENTDEV_API_UT_013,
+	EVENTDEV_API_UT_014,
+	EVENTDEV_API_UT_015,
+	EVENTDEV_API_UT_016,
+	EVENTDEV_API_UT_017,
+	EVENTDEV_API_UT_MAX
+} eventdev_api_ut_ids_t;
+
+typedef enum eventdev_tc_status_s {
+	TC_FAILED,
+	TC_PASSED
+} eventdev_tc_status_t;
+
+typedef struct eventdev_api_ut_status_s {
+	bool executed;
+	eventdev_tc_status_t status;
+} eventdev_api_ut_status_t;
+
+eventdev_api_ut_status_t api_ut_status[EVENTDEV_API_UT_MAX];
+
+#define CONFIG_NB_EVENT_QUEUES 2
+#define CONFIG_NB_EVENT_PORTS 2
+#define CONFIG_NB_EVENT_LIMIT 128
+
+uint8_t queues[CONFIG_NB_EVENT_QUEUES];
+uint8_t ports[CONFIG_NB_EVENT_PORTS];
+
+/* FIXME: Check that dependent tests have executed */
+
+static int test_EVENTDEV_API_UT_001_rte_event_dev_count(void)
+{
+	uint8_t count = rte_event_dev_count();
+
+	if (count == 1) {
+		api_ut_status[EVENTDEV_API_UT_001].status = TC_PASSED;
+		return 0;
+	} else {
+		api_ut_status[EVENTDEV_API_UT_001].status = TC_FAILED;
+		return 1;
+	}
+}
+
+static int test_EVENTDEV_API_UT_002_rte_event_dev_get_dev_id(void)
+{
+	int8_t id;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	if (id < 0) {
+		api_ut_status[EVENTDEV_API_UT_002].status = TC_FAILED;
+		return 1;
+	}
+
+	id = rte_event_dev_get_dev_id("evdev_abcd123");
+
+	if (id >= 0) {
+		api_ut_status[EVENTDEV_API_UT_002].status = TC_FAILED;
+		return 1;
+	}
+
+	api_ut_status[EVENTDEV_API_UT_002].status = TC_PASSED;
+	return 0;
+}
+
+static int test_EVENTDEV_API_UT_003_rte_event_dev_info_get(void)
+{
+	struct rte_event_dev_info info;
+	int8_t id;
+	int ret;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	ret = rte_event_dev_info_get(id, &info);
+	if (ret)
+		goto fail;
+
+	if (strncmp(info.driver_name, "evdev_sw", sizeof("evdev_sw")) != 0)
+		goto fail;
+
+	/* FIXME: Add checks for remaining fields */
+
+	api_ut_status[EVENTDEV_API_UT_003].status = TC_PASSED;
+	return 0;
+
+fail:
+	api_ut_status[EVENTDEV_API_UT_003].status = TC_FAILED;
+	return 1;
+}
+
+static int test_EVENTDEV_API_UT_004_rte_event_dev_configure(void)
+{
+	struct rte_event_dev_config config;
+	int8_t id;
+	int ret;
+
+	api_ut_status[EVENTDEV_API_UT_004].executed = true;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	config.nb_event_queues = CONFIG_NB_EVENT_QUEUES; /* FIXME: Test max */
+	config.nb_event_ports = CONFIG_NB_EVENT_PORTS; /* FIXME: Test max */
+	config.nb_events_limit = CONFIG_NB_EVENT_LIMIT; /* FIXME: Test max */
+	config.dequeue_wait_ns = 0; /* FIXME: Test max */
+
+	ret = rte_event_dev_configure(id, &config);
+	if (ret)
+		goto fail;
+
+	api_ut_status[EVENTDEV_API_UT_004].status = TC_PASSED;
+	return 0;
+
+fail:
+	api_ut_status[EVENTDEV_API_UT_004].status = TC_FAILED;
+	return 1;
+}
+
+static int test_EVENTDEV_API_UT_005_rte_event_queue_count_pre(void)
+{
+	int8_t id;
+	uint8_t count;
+
+	if (!api_ut_status[EVENTDEV_API_UT_004].executed)
+		test_EVENTDEV_API_UT_004_rte_event_dev_configure();
+	if (api_ut_status[EVENTDEV_API_UT_004].status == TC_FAILED)
+		goto fail;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	count = rte_event_queue_count(id);
+	if (count != CONFIG_NB_EVENT_QUEUES)
+		goto fail;
+
+	api_ut_status[EVENTDEV_API_UT_005].status = TC_PASSED;
+	return 0;
+
+fail:
+	api_ut_status[EVENTDEV_API_UT_005].status = TC_FAILED;
+	return 1;
+}
+
+static int test_EVENTDEV_API_UT_006_rte_event_queue_setup(void)
+{
+	struct rte_event_queue_conf config;
+	int8_t id;
+	int ret;
+
+	api_ut_status[EVENTDEV_API_UT_006].executed = true;
+
+	if (!api_ut_status[EVENTDEV_API_UT_004].executed)
+		test_EVENTDEV_API_UT_004_rte_event_dev_configure();
+	if (api_ut_status[EVENTDEV_API_UT_004].status == TC_FAILED)
+		goto fail;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	config.event_queue_cfg = 0;
+	config.priority = 0;
+
+	queues[0] = 0;
+
+	ret = rte_event_queue_setup(id, queues[0], &config);
+	if (ret < 0)
+		goto fail;
+
+	config.event_queue_cfg = RTE_EVENT_QUEUE_CFG_SINGLE_CONSUMER;
+	config.priority = 0;
+
+	queues[1] = 1;
+
+	ret = rte_event_queue_setup(id, queues[1], &config);
+	if (ret < 0)
+		goto fail;
+
+	api_ut_status[EVENTDEV_API_UT_006].status = TC_PASSED;
+	return 0;
+
+fail:
+	api_ut_status[EVENTDEV_API_UT_006].status = TC_FAILED;
+	return 1;
+}
+
+static int test_EVENTDEV_API_UT_007_rte_event_queue_count_post(void)
+{
+	int8_t id;
+	uint8_t count;
+
+	if (!api_ut_status[EVENTDEV_API_UT_004].executed)
+		test_EVENTDEV_API_UT_004_rte_event_dev_configure();
+	if (api_ut_status[EVENTDEV_API_UT_004].status == TC_FAILED)
+		goto fail;
+
+	if (!api_ut_status[EVENTDEV_API_UT_006].executed)
+		test_EVENTDEV_API_UT_006_rte_event_queue_setup();
+	if (api_ut_status[EVENTDEV_API_UT_006].status == TC_FAILED)
+		goto fail;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	count = rte_event_queue_count(id);
+	if (count != CONFIG_NB_EVENT_QUEUES)
+		goto fail;
+
+	api_ut_status[EVENTDEV_API_UT_007].status = TC_PASSED;
+	return 0;
+
+fail:
+	api_ut_status[EVENTDEV_API_UT_007].status = TC_FAILED;
+	return 1;
+}
+
+static int test_EVENTDEV_API_UT_008_rte_event_port_count_pre(void)
+{
+	int8_t id;
+	uint8_t count;
+
+	if (!api_ut_status[EVENTDEV_API_UT_004].executed)
+		test_EVENTDEV_API_UT_004_rte_event_dev_configure();
+	if (api_ut_status[EVENTDEV_API_UT_004].status == TC_FAILED)
+		goto fail;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	count = rte_event_port_count(id);
+	if (count != CONFIG_NB_EVENT_PORTS)
+		goto fail;
+
+	api_ut_status[EVENTDEV_API_UT_008].status = TC_PASSED;
+	return 0;
+
+fail:
+	api_ut_status[EVENTDEV_API_UT_008].status = TC_FAILED;
+	return 1;
+}
+
+static int test_EVENTDEV_API_UT_009_rte_event_port_setup(void)
+{
+	struct rte_event_port_conf config;
+	int8_t id;
+	int ret;
+
+	if (!api_ut_status[EVENTDEV_API_UT_004].executed)
+		test_EVENTDEV_API_UT_004_rte_event_dev_configure();
+	if (api_ut_status[EVENTDEV_API_UT_004].status == TC_FAILED)
+		goto fail;
+
+	api_ut_status[EVENTDEV_API_UT_009].executed = true;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	config.dequeue_queue_depth = 4;
+	config.enqueue_queue_depth = 4;
+	config.new_event_threshold = 64;
+
+	ports[0] = 0;
+
+	ret = rte_event_port_setup(id, ports[0], &config);
+	if (ret < 0)
+		goto fail;
+
+	config.dequeue_queue_depth = 4;
+	config.enqueue_queue_depth = 4;
+	config.new_event_threshold = 64;
+
+	ports[1] = 1;
+
+	ret = rte_event_port_setup(id, ports[1], &config);
+	if (ret < 0)
+		goto fail;
+
+	api_ut_status[EVENTDEV_API_UT_009].status = TC_PASSED;
+	return 0;
+
+fail:
+	api_ut_status[EVENTDEV_API_UT_009].status = TC_FAILED;
+	return 1;
+}
+
+static int test_EVENTDEV_API_UT_010_rte_event_port_count_post(void)
+{
+	int8_t id;
+	uint8_t count;
+
+	if (!api_ut_status[EVENTDEV_API_UT_004].executed)
+		test_EVENTDEV_API_UT_004_rte_event_dev_configure();
+	if (api_ut_status[EVENTDEV_API_UT_004].status == TC_FAILED)
+		goto fail;
+
+	if (!api_ut_status[EVENTDEV_API_UT_009].executed)
+		test_EVENTDEV_API_UT_009_rte_event_port_setup();
+	if (api_ut_status[EVENTDEV_API_UT_009].status == TC_FAILED)
+		goto fail;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	count = rte_event_port_count(id);
+	if (count != CONFIG_NB_EVENT_PORTS)
+		goto fail;
+
+	api_ut_status[EVENTDEV_API_UT_010].status = TC_PASSED;
+	return 0;
+
+fail:
+	api_ut_status[EVENTDEV_API_UT_010].status = TC_FAILED;
+	return 1;
+}
+
+static int test_EVENTDEV_API_UT_011_rte_event_dev_start(void)
+{
+	int8_t id;
+	int ret;
+
+	if (!api_ut_status[EVENTDEV_API_UT_004].executed)
+		test_EVENTDEV_API_UT_004_rte_event_dev_configure();
+	if (api_ut_status[EVENTDEV_API_UT_004].status == TC_FAILED)
+		goto fail;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	ret = rte_event_dev_start(id);
+	if (ret != 0)
+		goto fail;
+
+	api_ut_status[EVENTDEV_API_UT_011].status = TC_PASSED;
+	return 0;
+
+fail:
+	api_ut_status[EVENTDEV_API_UT_011].status = TC_FAILED;
+	return 1;
+}
+
+static int test_EVENTDEV_API_UT_012_rte_event_port_link(void)
+{
+	struct rte_event_queue_link link;
+	int8_t id;
+	int ret;
+
+	if (!api_ut_status[EVENTDEV_API_UT_004].executed)
+		test_EVENTDEV_API_UT_004_rte_event_dev_configure();
+	if (api_ut_status[EVENTDEV_API_UT_004].status == TC_FAILED)
+		goto fail;
+
+	if (!api_ut_status[EVENTDEV_API_UT_006].executed)
+		test_EVENTDEV_API_UT_006_rte_event_queue_setup();
+	if (api_ut_status[EVENTDEV_API_UT_006].status == TC_FAILED)
+		goto fail;
+
+	if (!api_ut_status[EVENTDEV_API_UT_009].executed)
+		test_EVENTDEV_API_UT_009_rte_event_port_setup();
+	if (api_ut_status[EVENTDEV_API_UT_009].status == TC_FAILED)
+		goto fail;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	link.queue_id = queues[0];
+	link.priority = 0;
+
+	/* Connect port to previously configured scheduled queue */
+	ret = rte_event_port_link(id, ports[0], &link, 1);
+	if (ret != 1) {
+		printf("%d: failed here\n", __LINE__);
+		goto fail;
+	}
+
+	/* Check idempotency of re-linking port to queues[0] */
+	ret = rte_event_port_link(id, ports[0], &link, 1);
+	if (ret != 1) {
+		printf("%d: failed here\n", __LINE__);
+		goto fail;
+	}
+
+	link.queue_id = queues[1];
+	link.priority = 0;
+
+	/* Attempt to connect to FIFO queue as well */
+	ret = rte_event_port_link(id, ports[0], &link, 1);
+	if (ret == 1) {
+		printf("%d: failed here\n", __LINE__);
+		goto fail;
+	}
+
+	link.queue_id = queues[1];
+	link.priority = 0;
+
+	/* Connect port to previously configured FIFO queue */
+	ret = rte_event_port_link(id, ports[1], &link, 1);
+	if (ret != 1) {
+		printf("%d: failed here\n", __LINE__);
+		goto fail;
+	}
+
+	link.queue_id = queues[0];
+	link.priority = 0;
+
+	/* Attempt to connect to scheduled queue as well */
+	ret = rte_event_port_link(id, ports[1], &link, 1);
+	if (ret == 1) {
+		printf("%d: failed here\n", __LINE__);
+		goto fail;
+	}
+
+	/* link to 2nd queue, enabling start() to pass later */
+	link.queue_id = queues[1];
+	link.priority = 0;
+	ret = rte_event_port_link(id, ports[1], &link, 1);
+	if (ret == 1) {
+		printf("%d: failed here\n", __LINE__);
+		goto fail;
+	}
+
+	api_ut_status[EVENTDEV_API_UT_012].status = TC_PASSED;
+	return 0;
+
+fail:
+	api_ut_status[EVENTDEV_API_UT_012].status = TC_FAILED;
+	return 1;
+}
+
+static int test_EVENTDEV_API_UT_014_rte_event_dev_stop(void)
+{
+	int8_t id;
+
+	if (!api_ut_status[EVENTDEV_API_UT_004].executed)
+		test_EVENTDEV_API_UT_004_rte_event_dev_configure();
+	if (api_ut_status[EVENTDEV_API_UT_004].status == TC_FAILED)
+		return 1;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	rte_event_dev_stop(id);
+
+	api_ut_status[EVENTDEV_API_UT_014].status = TC_PASSED;
+	return 0;
+}
+
+static int test_EVENTDEV_API_UT_015_rte_event_dev_close(void)
+{
+	int8_t id;
+	int ret;
+
+	if (!api_ut_status[EVENTDEV_API_UT_004].executed)
+		test_EVENTDEV_API_UT_004_rte_event_dev_configure();
+	if (api_ut_status[EVENTDEV_API_UT_004].status == TC_FAILED)
+		goto fail;
+
+	id = rte_event_dev_get_dev_id("evdev_sw0");
+
+	ret = rte_event_dev_close(id);
+	if (ret != 0)
+		goto fail;
+
+	api_ut_status[EVENTDEV_API_UT_015].status = TC_PASSED;
+	return 0;
+
+fail:
+	api_ut_status[EVENTDEV_API_UT_015].status = TC_FAILED;
+	return 1;
+}
+
+static int
+test_setup(void)
+{
+	return 0;
+}
+
+static struct unit_test_suite eventdev_test_suite  = {
+	.setup = test_setup,
+	.suite_name = "Eventdev Test Suite",
+	.unit_test_cases = {
+		/* device aquisition and config */
+		TEST_CASE(test_EVENTDEV_API_UT_001_rte_event_dev_count),
+		TEST_CASE(test_EVENTDEV_API_UT_002_rte_event_dev_get_dev_id),
+		TEST_CASE(test_EVENTDEV_API_UT_003_rte_event_dev_info_get),
+		TEST_CASE(test_EVENTDEV_API_UT_004_rte_event_dev_configure),
+		/* queue config */
+		TEST_CASE(test_EVENTDEV_API_UT_005_rte_event_queue_count_pre),
+		TEST_CASE(test_EVENTDEV_API_UT_006_rte_event_queue_setup),
+		TEST_CASE(test_EVENTDEV_API_UT_007_rte_event_queue_count_post),
+		/* port config */
+		TEST_CASE(test_EVENTDEV_API_UT_008_rte_event_port_count_pre),
+		TEST_CASE(test_EVENTDEV_API_UT_009_rte_event_port_setup),
+		TEST_CASE(test_EVENTDEV_API_UT_010_rte_event_port_count_post),
+		TEST_CASE(test_EVENTDEV_API_UT_012_rte_event_port_link),
+		TEST_CASE(test_EVENTDEV_API_UT_011_rte_event_dev_start),
+		/* device cleanup */
+		TEST_CASE(test_EVENTDEV_API_UT_014_rte_event_dev_stop),
+		TEST_CASE(test_EVENTDEV_API_UT_015_rte_event_dev_close),
+		TEST_CASES_END()
+	}
+};
+
+static int
+test_eventdev_unit(void)
+{
+	return unit_test_suite_runner(&eventdev_test_suite);
+}
+
+REGISTER_TEST_COMMAND(eventdev_unit_autotest, test_eventdev_unit);