[dpdk-dev,v7,5/7] test: add event timer adapter auto-test

Message ID 1520546046-6973-6-git-send-email-erik.g.carrillo@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Jerin Jacob
Headers

Checks

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

Commit Message

Carrillo, Erik G March 8, 2018, 9:54 p.m. UTC
  Signed-off-by: Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
---
 test/test/Makefile                   |    1 +
 test/test/test_event_timer_adapter.c | 1234 ++++++++++++++++++++++++++++++++++
 2 files changed, 1235 insertions(+)
 create mode 100644 test/test/test_event_timer_adapter.c
  

Comments

Pavan Nikhilesh March 14, 2018, 12:52 p.m. UTC | #1
Hi Gabriel,

On Thu, Mar 08, 2018 at 03:54:04PM -0600, Erik Gabriel Carrillo wrote:
> Signed-off-by: Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
> ---
>  test/test/Makefile                   |    1 +
>  test/test/test_event_timer_adapter.c | 1234 ++++++++++++++++++++++++++++++++++
>  2 files changed, 1235 insertions(+)
>  create mode 100644 test/test/test_event_timer_adapter.c
>
<snip>
> +
> +static int
> +configure_event_dev(void)
> +{
> +	const char *eventdev_name = "event_sw0";

Can this be made generic? instead of hardcoding to event_sw, check if
event_device is passed via --vdev (verify rte_event_dev_count()) and if it is
absent fallback to event_sw.

Thanks,
Pavan.

> +
> +	return TEST_SUCCESS;
> +}
<snip>
  
Pavan Nikhilesh March 14, 2018, 1:31 p.m. UTC | #2
Hi Gabriel,

Please make sure that the unit tests are generic, I could see that some places
it is verifying whether event port is used or service cores are used, but
doesn't verify if actually event port/service core are needed i.e.
INTERNAL_PORT capability.

On Thu, Mar 08, 2018 at 03:54:04PM -0600, Erik Gabriel Carrillo wrote:
> Signed-off-by: Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
> ---
>  test/test/Makefile                   |    1 +
>  test/test/test_event_timer_adapter.c | 1234 ++++++++++++++++++++++++++++++++++
>  2 files changed, 1235 insertions(+)
>  create mode 100644 test/test/test_event_timer_adapter.c
>
<snip>
> +
> +/* This port conf callback is used by the max adapter instance creation test.
> + * Because that test may be limited by the number of ports available in the
> + * event device, this callback allocates just one port and returns it each
> + * time a port is requested.
> + */
> +static int
> +test_port_conf_cb(uint16_t id, uint8_t event_dev_id, uint8_t *event_port_id,
> +		  void *conf_arg)
> +{
> +	struct rte_eventdev *dev;
> +	struct rte_event_dev_config dev_conf;
> +	struct rte_event_port_conf *port_conf, def_port_conf = {0};
> +	int started;
> +	static int port_allocated;
> +	static uint8_t port_id;
> +	uint8_t dev_id;
> +	int ret;
> +
> +	if (port_allocated) {
> +		*event_port_id = port_id;
> +		return 0;
> +	}
> +
> +	RTE_SET_USED(id);
> +
> +	dev = &rte_eventdevs[event_dev_id];

I don't think this is the correct way of accessing event dev information i.e.
accessing the global rte_eventdevs structure from a application.

> +	dev_id = dev->data->dev_id;
> +	dev_conf = dev->data->dev_conf;
> +
> +	started = dev->data->dev_started;
> +	if (started)
> +		rte_event_dev_stop(dev_id);
> +
> +	port_id = dev_conf.nb_event_ports;
> +	dev_conf.nb_event_ports += 1;
> +	ret = rte_event_dev_configure(dev_id, &dev_conf);
> +	if (ret < 0) {
> +		if (started)
> +			rte_event_dev_start(dev_id);
Shouldn't this be !started ?. The same pattern repeats a few places.

> +
> +		return ret;
> +	}
> +
> +	if (conf_arg != NULL)
> +		port_conf = conf_arg;
> +	else {
> +		port_conf = &def_port_conf;
> +		ret = rte_event_port_default_conf_get(dev_id, port_id,
> +						      port_conf);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	ret = rte_event_port_setup(dev_id, port_id, port_conf);
> +	if (ret < 0)
> +		return ret;
> +
> +	*event_port_id = port_id;
> +
> +	if (started)
> +		rte_event_dev_start(dev_id);
> +
> +	port_allocated = 1;
> +
> +	return 0;
> +}
<snip>

Thanks,
Pavan.
  
Carrillo, Erik G March 14, 2018, 9:40 p.m. UTC | #3
Hi Pavan,

> -----Original Message-----
> From: Pavan Nikhilesh [mailto:pbhagavatula@caviumnetworks.com]
> Sent: Wednesday, March 14, 2018 8:31 AM
> To: Carrillo, Erik G <erik.g.carrillo@intel.com>;
> jerin.jacob@caviumnetworks.com; nipun.gupta@nxp.com;
> hemant.agrawal@nxp.com
> Cc: dev@dpdk.org
> Subject: Re: [PATCH v7 5/7] test: add event timer adapter auto-test
> 
> Hi Gabriel,
> 
> Please make sure that the unit tests are generic, I could see that some places
> it is verifying whether event port is used or service cores are used, but
> doesn't verify if actually event port/service core are needed i.e.
> INTERNAL_PORT capability.

Good point... I'll make these updates.

Thanks for reviewing,
Gabriel

> 
> On Thu, Mar 08, 2018 at 03:54:04PM -0600, Erik Gabriel Carrillo wrote:
> > Signed-off-by: Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
> > ---
> >  test/test/Makefile                   |    1 +
> >  test/test/test_event_timer_adapter.c | 1234
> > ++++++++++++++++++++++++++++++++++
> >  2 files changed, 1235 insertions(+)
> >  create mode 100644 test/test/test_event_timer_adapter.c
> >
> <snip>
> > +
> > +/* This port conf callback is used by the max adapter instance creation
> test.
> > + * Because that test may be limited by the number of ports available
> > +in the
> > + * event device, this callback allocates just one port and returns it
> > +each
> > + * time a port is requested.
> > + */
> > +static int
> > +test_port_conf_cb(uint16_t id, uint8_t event_dev_id, uint8_t
> *event_port_id,
> > +		  void *conf_arg)
> > +{
> > +	struct rte_eventdev *dev;
> > +	struct rte_event_dev_config dev_conf;
> > +	struct rte_event_port_conf *port_conf, def_port_conf = {0};
> > +	int started;
> > +	static int port_allocated;
> > +	static uint8_t port_id;
> > +	uint8_t dev_id;
> > +	int ret;
> > +
> > +	if (port_allocated) {
> > +		*event_port_id = port_id;
> > +		return 0;
> > +	}
> > +
> > +	RTE_SET_USED(id);
> > +
> > +	dev = &rte_eventdevs[event_dev_id];
> 
> I don't think this is the correct way of accessing event dev information i.e.
> accessing the global rte_eventdevs structure from a application.
> 
> > +	dev_id = dev->data->dev_id;
> > +	dev_conf = dev->data->dev_conf;
> > +
> > +	started = dev->data->dev_started;
> > +	if (started)
> > +		rte_event_dev_stop(dev_id);
> > +
> > +	port_id = dev_conf.nb_event_ports;
> > +	dev_conf.nb_event_ports += 1;
> > +	ret = rte_event_dev_configure(dev_id, &dev_conf);
> > +	if (ret < 0) {
> > +		if (started)
> > +			rte_event_dev_start(dev_id);
> Shouldn't this be !started ?. The same pattern repeats a few places.
> 
> > +
> > +		return ret;
> > +	}
> > +
> > +	if (conf_arg != NULL)
> > +		port_conf = conf_arg;
> > +	else {
> > +		port_conf = &def_port_conf;
> > +		ret = rte_event_port_default_conf_get(dev_id, port_id,
> > +						      port_conf);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> > +	ret = rte_event_port_setup(dev_id, port_id, port_conf);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	*event_port_id = port_id;
> > +
> > +	if (started)
> > +		rte_event_dev_start(dev_id);
> > +
> > +	port_allocated = 1;
> > +
> > +	return 0;
> > +}
> <snip>
> 
> Thanks,
> Pavan.
  
Carrillo, Erik G March 14, 2018, 9:42 p.m. UTC | #4
Hi Pavan,

> -----Original Message-----
> From: Pavan Nikhilesh [mailto:pbhagavatula@caviumnetworks.com]
> Sent: Wednesday, March 14, 2018 7:53 AM
> To: Carrillo, Erik G <erik.g.carrillo@intel.com>;
> jerin.jacob@caviumnetworks.com; nipun.gupta@nxp.com;
> hemant.agrawal@nxp.com
> Cc: dev@dpdk.org
> Subject: Re: [PATCH v7 5/7] test: add event timer adapter auto-test
> 
> Hi Gabriel,
> 
> On Thu, Mar 08, 2018 at 03:54:04PM -0600, Erik Gabriel Carrillo wrote:
> > Signed-off-by: Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
> > ---
> >  test/test/Makefile                   |    1 +
> >  test/test/test_event_timer_adapter.c | 1234
> > ++++++++++++++++++++++++++++++++++
> >  2 files changed, 1235 insertions(+)
> >  create mode 100644 test/test/test_event_timer_adapter.c
> >
> <snip>
> > +
> > +static int
> > +configure_event_dev(void)
> > +{
> > +	const char *eventdev_name = "event_sw0";
> 
> Can this be made generic? instead of hardcoding to event_sw, check if
> event_device is passed via --vdev (verify rte_event_dev_count()) and if it is
> absent fallback to event_sw.
> 

Sure, I'll look into doing this.

Thanks,
Gabriel

> Thanks,
> Pavan.
> 
> > +
> > +	return TEST_SUCCESS;
> > +}
> <snip>
  

Patch

diff --git a/test/test/Makefile b/test/test/Makefile
index a88cc38..c9c007c9 100644
--- a/test/test/Makefile
+++ b/test/test/Makefile
@@ -185,6 +185,7 @@  ifeq ($(CONFIG_RTE_LIBRTE_EVENTDEV),y)
 SRCS-y += test_eventdev.c
 SRCS-y += test_event_ring.c
 SRCS-y += test_event_eth_rx_adapter.c
+SRCS-y += test_event_timer_adapter.c
 endif
 
 ifeq ($(CONFIG_RTE_LIBRTE_RAWDEV),y)
diff --git a/test/test/test_event_timer_adapter.c b/test/test/test_event_timer_adapter.c
new file mode 100644
index 0000000..936e662
--- /dev/null
+++ b/test/test/test_event_timer_adapter.c
@@ -0,0 +1,1234 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2017 Intel Corporation
+ */
+
+#include <string.h>
+#include <math.h>
+#include <stdbool.h>
+
+#include <rte_eventdev.h>
+#include <rte_dev.h>
+#include <rte_bus_vdev.h>
+#include <rte_event_timer_adapter.h>
+#include <rte_mempool.h>
+#include <rte_errno.h>
+#include <rte_service_component.h>
+#include <rte_cycles.h>
+
+#include "test.h"
+
+#define NB_TEST_EVENT_TIMERS    2050
+#define NB_TEST_PORTS		1
+#define NB_TEST_QUEUES		1
+#define TEST_PORT_ID		0
+#define ADAPTER_PORT_ID		1
+#define TEST_QUEUE_ID		0
+#define TEST_ADAPTER_ID		0
+
+#define NSECPERSEC		1E9
+
+#define BATCH_SIZE 16
+
+/* Handle log statements in same manner as test macros */
+#define LOG_DBG(...)	RTE_LOG(DEBUG, EAL, __VA_ARGS__)
+
+static int evdev;
+static struct rte_mempool *g_event_timer_pool;
+static struct rte_event_timer_adapter *g_adapter;
+static uint32_t slcore_id;
+
+static struct rte_event_timer_adapter_conf g_adapter_conf = {
+	.event_dev_id = 0,
+	.timer_adapter_id = TEST_ADAPTER_ID,
+	.clk_src = RTE_EVENT_TIMER_ADAPTER_CPU_CLK,
+	.timer_tick_ns = NSECPERSEC / 10,  // 100 milliseconds, 10 ticks/sec
+	.max_tmo_ns = 180 * NSECPERSEC,	   // 2 minutes
+	.nb_timers = NB_TEST_EVENT_TIMERS,
+	.flags = 0,
+};
+
+static inline void
+devconf_set_test_values(struct rte_event_dev_config *dev_conf,
+				  struct rte_event_dev_info *info)
+{
+	memset(dev_conf, 0, sizeof(struct rte_event_dev_config));
+	dev_conf->dequeue_timeout_ns = info->min_dequeue_timeout_ns;
+	dev_conf->nb_event_ports = NB_TEST_PORTS;
+	dev_conf->nb_event_queues = NB_TEST_QUEUES;
+	dev_conf->nb_event_queue_flows = info->max_event_queue_flows;
+	dev_conf->nb_event_port_dequeue_depth =
+			info->max_event_port_dequeue_depth;
+	dev_conf->nb_event_port_enqueue_depth =
+			info->max_event_port_enqueue_depth;
+	dev_conf->nb_event_port_enqueue_depth =
+			info->max_event_port_enqueue_depth;
+	dev_conf->nb_events_limit =
+			info->max_num_events;
+}
+
+static int
+configure_event_dev(void)
+{
+	const char *eventdev_name = "event_sw0";
+	int ret;
+	struct rte_event_dev_config devconf;
+	struct rte_event_dev_info info;
+
+	evdev = rte_event_dev_get_dev_id(eventdev_name);
+	if (evdev < 0) {
+		if (rte_vdev_init(eventdev_name, NULL) < 0) {
+			LOG_DBG("Error creating eventdev\n");
+			return TEST_FAILED;
+		}
+		evdev = rte_event_dev_get_dev_id(eventdev_name);
+		if (evdev < 0) {
+			LOG_DBG("Error finding newly created eventdev\n");
+			return TEST_FAILED;
+		}
+	}
+
+	ret = rte_event_dev_info_get(evdev, &info);
+	TEST_ASSERT_SUCCESS(ret, "Failed to get event dev info");
+
+	devconf_set_test_values(&devconf, &info);
+
+	ret = rte_event_dev_configure(evdev, &devconf);
+	TEST_ASSERT_SUCCESS(ret, "Failed to configure eventdev");
+
+	/* Set up event queue */
+	uint32_t queue_count;
+	TEST_ASSERT_SUCCESS(rte_event_dev_attr_get(evdev,
+			    RTE_EVENT_DEV_ATTR_QUEUE_COUNT, &queue_count),
+			    "Queue count get failed");
+	TEST_ASSERT_EQUAL(queue_count, 1, "Unexpected queue count");
+	ret = rte_event_queue_setup(evdev, TEST_QUEUE_ID, NULL);
+	TEST_ASSERT_SUCCESS(ret, "Failed to setup queue=%d", TEST_QUEUE_ID);
+
+	/* Set up event port */
+	uint32_t port_count;
+	uint8_t qid = TEST_QUEUE_ID;
+	TEST_ASSERT_SUCCESS(rte_event_dev_attr_get(evdev,
+			    RTE_EVENT_DEV_ATTR_PORT_COUNT,
+			    &port_count), "Port count get failed");
+	TEST_ASSERT_EQUAL(port_count, 1, "Unexpected port count");
+	ret = rte_event_port_setup(evdev, TEST_PORT_ID, NULL);
+	TEST_ASSERT_SUCCESS(ret, "Failed to setup port=%d", TEST_PORT_ID);
+	ret = rte_event_port_link(evdev, TEST_PORT_ID, &qid, NULL, 1);
+	TEST_ASSERT(ret >= 0, "Failed to link queue port=%d", TEST_PORT_ID);
+
+	return TEST_SUCCESS;
+}
+
+/* ----------------    Setup / Teardown   --------------------*/
+
+static int
+testsuite_setup(void)
+{
+	slcore_id = rte_get_next_lcore(-1, 1, 0);
+
+	TEST_ASSERT_NOT_EQUAL(slcore_id, RTE_MAX_LCORE, "At least 2 lcores "
+			"are required to run this autotest");
+
+	/* Setup and start event device. */
+	TEST_ASSERT_SUCCESS(configure_event_dev(), "Failed to configure event "
+			"dev");
+
+	/* Create a mempool of event timers. */
+	g_event_timer_pool = rte_mempool_create("event_timer_mempool",
+			NB_TEST_EVENT_TIMERS * 2,
+			sizeof(struct rte_event_timer), 0, 0, NULL, NULL, NULL,
+			NULL, rte_socket_id(), 0);
+
+	TEST_ASSERT_NOT_NULL(g_event_timer_pool, "Failed to configure event "
+			"timer mempool: %s\n", rte_strerror(rte_errno));
+
+	return TEST_SUCCESS;
+}
+
+static void
+testsuite_teardown(void)
+{
+	rte_mempool_free(g_event_timer_pool);
+}
+
+static int
+g_adapter_create(void)
+{
+	uint8_t event_dev_id;
+	int ret, started;
+	struct rte_eventdev *event_dev;
+	struct rte_event_dev_config event_dev_conf;
+
+	/* Re-configure the event device ports to release ports allocated by
+	 * previous instantiations of event timer adapters.
+	 */
+	event_dev = &rte_eventdevs[g_adapter_conf.event_dev_id];
+	event_dev_id = event_dev->data->dev_id;
+	event_dev_conf = event_dev->data->dev_conf;
+
+	started = event_dev->data->dev_started;
+	if (started)
+		rte_event_dev_stop(event_dev_id);
+
+	event_dev_conf.nb_event_ports = NB_TEST_PORTS;
+	ret = rte_event_dev_configure(event_dev_id, &event_dev_conf);
+
+	if (started) {
+		ret = rte_event_dev_start(event_dev_id);
+		if (ret < 0) {
+			LOG_DBG("failed to start event device after "
+				"reconfiguration\n");
+			return TEST_FAILED;
+		}
+	}
+
+	/* Now create adapter with default port creation callback */
+	g_adapter = rte_event_timer_adapter_create(&g_adapter_conf);
+	TEST_ASSERT_NOT_NULL(g_adapter, "Failed to create event timer adapter");
+
+	return TEST_SUCCESS;
+}
+static void
+g_adapter_free(void)
+{
+	uint16_t n;
+	int ret;
+	struct rte_event evs[BATCH_SIZE];
+
+	ret = rte_event_timer_adapter_free(g_adapter);
+	if (ret != 0)
+		LOG_DBG("%s: Failed to free adapter\n", __func__);
+
+	/* Drain the eventdev of events, so subsequent tests are not affected */
+	while ((n = rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs,
+					    RTE_DIM(evs), 0)) > 0)
+		rte_delay_ms(10);
+}
+
+static int
+g_adapter_create_start(void)
+{
+	uint32_t evdev_service_id, adapter_service_id;
+
+	g_adapter_create();
+
+	/* retrieve service ids */
+	TEST_ASSERT_SUCCESS(rte_event_dev_service_id_get(evdev,
+			&evdev_service_id), "Failed to get event device "
+			"service id");
+	TEST_ASSERT_SUCCESS(rte_event_timer_adapter_service_id_get(g_adapter,
+			&adapter_service_id), "Failed to get event timer "
+			"adapter service id");
+
+	/* add a service core and start it */
+	TEST_ASSERT_SUCCESS(rte_service_lcore_add(slcore_id),
+			"Failed to add service core");
+	TEST_ASSERT_SUCCESS(rte_service_lcore_start(slcore_id),
+			"Failed to start service core");
+
+	/* map services to it */
+	TEST_ASSERT_SUCCESS(rte_service_map_lcore_set(evdev_service_id,
+			slcore_id, 1), "Failed to map evdev service");
+	TEST_ASSERT_SUCCESS(rte_service_map_lcore_set(adapter_service_id,
+			slcore_id, 1), "Failed to map adapter service");
+
+	/* set services to running */
+	TEST_ASSERT_SUCCESS(rte_service_runstate_set(evdev_service_id, 1),
+			"Failed to start evdev service");
+	TEST_ASSERT_SUCCESS(rte_service_runstate_set(adapter_service_id, 1),
+			"Failed to start event timer adapter service");
+
+	/* start the eventdev */
+	TEST_ASSERT_SUCCESS(rte_event_dev_start(evdev),
+			"Failed to start event device");
+
+	/* start the timer event adapter */
+	TEST_ASSERT_SUCCESS(rte_event_timer_adapter_start(g_adapter),
+			"Failed to start event timer adapter");
+
+	return TEST_SUCCESS;
+}
+
+static void
+g_adapter_stop_free(void)
+{
+	int ret;
+	uint32_t evdev_service_id, adapter_service_id;
+
+	/* Stop the event timer adapter */
+	ret = rte_event_timer_adapter_stop(g_adapter);
+	if (ret != 0)
+		LOG_DBG("%s: Failed to stop adapter, ret = %d\n", __func__,
+			ret);
+
+	/* Stop the eventdev */
+	rte_event_dev_stop(evdev);
+
+	/* set services to stopped */
+	ret = rte_event_dev_service_id_get(evdev, &evdev_service_id);
+	if (ret != 0)
+		LOG_DBG("%s: Failed to get event device service id\n",
+			__func__);
+
+	ret = rte_event_timer_adapter_service_id_get(g_adapter,
+						     &adapter_service_id);
+	if (ret != 0)
+		LOG_DBG("%s: Failed to get event timer adapter service id\n",
+		       __func__);
+
+	ret = rte_service_runstate_set(evdev_service_id, 0);
+	if (ret != 0)
+		LOG_DBG("%s: Failed to stop evdev service\n", __func__);
+
+	ret = rte_service_runstate_set(adapter_service_id, 0);
+	if (ret != 0)
+		LOG_DBG("%s: Failed to stop event timer adapter service\n",
+		       __func__);
+
+	g_adapter_free();
+
+	/* unmap all the services and service cores */
+	rte_service_lcore_reset_all();
+}
+
+/* ----------------    Tests  --------------------*/
+
+/* Check that the adapter can be created correctly */
+static int
+adapter_create(void)
+{
+	int adapter_id = 0;
+	struct rte_event_timer_adapter *adapter, *adapter2;
+
+	struct rte_event_timer_adapter_conf conf = {
+		.event_dev_id = evdev + 1,  // invalid event dev id
+		.timer_adapter_id = adapter_id,
+		.clk_src = RTE_EVENT_TIMER_ADAPTER_CPU_CLK,
+		.timer_tick_ns = NSECPERSEC / 10,
+		.max_tmo_ns = 180 * NSECPERSEC,
+		.nb_timers = NB_TEST_EVENT_TIMERS,
+		.flags = 0,
+	};
+
+	/* Test invalid conf */
+	adapter = rte_event_timer_adapter_create(&conf);
+	TEST_ASSERT_NULL(adapter, "Created adapter with invalid "
+			"event device id");
+	TEST_ASSERT_EQUAL(rte_errno, EINVAL, "Incorrect errno value for "
+			"invalid event device id");
+
+	/* Test valid conf */
+	conf.event_dev_id = evdev;
+	adapter = rte_event_timer_adapter_create(&conf);
+	TEST_ASSERT_NOT_NULL(adapter, "Failed to create adapter with valid "
+			"configuration");
+
+	/* Test existing id */
+	adapter2 = rte_event_timer_adapter_create(&conf);
+	TEST_ASSERT_NULL(adapter2, "Created adapter with in-use id");
+	TEST_ASSERT(rte_errno == EEXIST, "Incorrect errno value for existing "
+			"id");
+
+	TEST_ASSERT_SUCCESS(rte_event_timer_adapter_free(adapter),
+			"Failed to free adapter");
+
+	return TEST_SUCCESS;
+}
+
+/* This port conf callback is used by the max adapter instance creation test.
+ * Because that test may be limited by the number of ports available in the
+ * event device, this callback allocates just one port and returns it each
+ * time a port is requested.
+ */
+static int
+test_port_conf_cb(uint16_t id, uint8_t event_dev_id, uint8_t *event_port_id,
+		  void *conf_arg)
+{
+	struct rte_eventdev *dev;
+	struct rte_event_dev_config dev_conf;
+	struct rte_event_port_conf *port_conf, def_port_conf = {0};
+	int started;
+	static int port_allocated;
+	static uint8_t port_id;
+	uint8_t dev_id;
+	int ret;
+
+	if (port_allocated) {
+		*event_port_id = port_id;
+		return 0;
+	}
+
+	RTE_SET_USED(id);
+
+	dev = &rte_eventdevs[event_dev_id];
+	dev_id = dev->data->dev_id;
+	dev_conf = dev->data->dev_conf;
+
+	started = dev->data->dev_started;
+	if (started)
+		rte_event_dev_stop(dev_id);
+
+	port_id = dev_conf.nb_event_ports;
+	dev_conf.nb_event_ports += 1;
+	ret = rte_event_dev_configure(dev_id, &dev_conf);
+	if (ret < 0) {
+		if (started)
+			rte_event_dev_start(dev_id);
+
+		return ret;
+	}
+
+	if (conf_arg != NULL)
+		port_conf = conf_arg;
+	else {
+		port_conf = &def_port_conf;
+		ret = rte_event_port_default_conf_get(dev_id, port_id,
+						      port_conf);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = rte_event_port_setup(dev_id, port_id, port_conf);
+	if (ret < 0)
+		return ret;
+
+	*event_port_id = port_id;
+
+	if (started)
+		rte_event_dev_start(dev_id);
+
+	port_allocated = 1;
+
+	return 0;
+}
+
+static int
+adapter_create_max(void)
+{
+	int i;
+	uint32_t svc_start_count, svc_end_count;
+	struct rte_event_timer_adapter *adapters[
+					RTE_EVENT_TIMER_ADAPTER_NUM_MAX + 1];
+
+	struct rte_event_timer_adapter_conf conf = {
+		.event_dev_id = evdev,
+		// timer_adapter_id set in loop
+		.clk_src = RTE_EVENT_TIMER_ADAPTER_CPU_CLK,
+		.timer_tick_ns = NSECPERSEC / 10,
+		.max_tmo_ns = 180 * NSECPERSEC,
+		.nb_timers = NB_TEST_EVENT_TIMERS,
+		.flags = 0,
+	};
+
+	svc_start_count = rte_service_get_count();
+
+	/* This test expects that there are sufficient service IDs available
+	 * to be allocated. I.e., RTE_EVENT_TIMER_ADAPTER_NUM_MAX may need to
+	 * be less than RTE_SERVICE_NUM_MAX if anything else uses a service
+	 * (the SW event device, for example).
+	 */
+	for (i = 0; i < RTE_EVENT_TIMER_ADAPTER_NUM_MAX; i++) {
+		conf.timer_adapter_id = i;
+		adapters[i] = rte_event_timer_adapter_create_ext(&conf,
+				test_port_conf_cb, NULL);
+		TEST_ASSERT_NOT_NULL(adapters[i], "Failed to create adapter "
+				"%d", i);
+	}
+
+	conf.timer_adapter_id = i;
+	adapters[i] = rte_event_timer_adapter_create(&conf);
+	TEST_ASSERT_NULL(adapters[i], "Created too many adapters");
+
+	/* Check that at least RTE_EVENT_TIMER_ADAPTER_NUM_MAX services
+	 * have been created
+	 */
+	svc_end_count = rte_service_get_count();
+	TEST_ASSERT_EQUAL(svc_end_count - svc_start_count,
+			RTE_EVENT_TIMER_ADAPTER_NUM_MAX,
+			"Failed to create expected number of services");
+
+	for (i = 0; i < RTE_EVENT_TIMER_ADAPTER_NUM_MAX; i++)
+		TEST_ASSERT_SUCCESS(rte_event_timer_adapter_free(adapters[i]),
+				"Failed to free adapter %d", i);
+
+	/* Check that service count is back to where it was at start */
+	svc_end_count = rte_service_get_count();
+	TEST_ASSERT_EQUAL(svc_start_count, svc_end_count, "Failed to release "
+			  "correct number of services");
+
+	return TEST_SUCCESS;
+}
+
+/* Test that adapter can be freed correctly. */
+static int
+adapter_free(void)
+{
+	TEST_ASSERT_SUCCESS(rte_event_timer_adapter_free(g_adapter),
+			"Failed to free valid adapter");
+
+	/* Test free of already freed adapter */
+	TEST_ASSERT_FAIL(rte_event_timer_adapter_free(g_adapter),
+			"Freed adapter that was already freed");
+
+	/* Test free of null adapter */
+	g_adapter = NULL;
+	TEST_ASSERT_FAIL(rte_event_timer_adapter_free(g_adapter),
+			"Freed null adapter");
+
+	return TEST_SUCCESS;
+}
+
+/* Test that adapter info can be retrieved and is correct. */
+static int
+adapter_get_info(void)
+{
+	struct rte_event_timer_adapter_info info;
+
+	TEST_ASSERT_SUCCESS(rte_event_timer_adapter_get_info(g_adapter, &info),
+			"Failed to get adapter info");
+
+	TEST_ASSERT_EQUAL(info.event_dev_port_id, 1, "Expected port id = 1,"
+			"got port id = %d", info.event_dev_port_id);
+
+	return TEST_SUCCESS;
+}
+
+/* Test adapter lookup via adapter ID. */
+static int
+adapter_lookup(void)
+{
+	struct rte_event_timer_adapter *adapter;
+
+	adapter = rte_event_timer_adapter_lookup(TEST_ADAPTER_ID);
+	TEST_ASSERT_NOT_NULL(adapter, "Failed to lookup adapter");
+
+	return TEST_SUCCESS;
+}
+
+/* Test that adapter starts correctly. */
+static int
+adapter_start(void)
+{
+	uint32_t evdev_service_id, adapter_service_id;
+	struct rte_event_timer_adapter *l_adapter = NULL;
+
+	TEST_ASSERT_FAIL(rte_event_timer_adapter_start(l_adapter),
+			 "Erroneously started null adapter");
+
+	/* Check that we fail when no service core is mapped */
+	TEST_ASSERT_FAIL(rte_event_timer_adapter_start(g_adapter),
+			 "Erroneously started adapter with no service core "
+			 "mapped");
+
+	/* retrieve service ids */
+	TEST_ASSERT_SUCCESS(rte_event_dev_service_id_get(evdev,
+			&evdev_service_id), "Failed to get event device "
+			"service id");
+	TEST_ASSERT_SUCCESS(rte_event_timer_adapter_service_id_get(g_adapter,
+			&adapter_service_id), "Failed to get event timer "
+			"adapter service id");
+
+	/* add a service core and start it */
+	TEST_ASSERT_SUCCESS(rte_service_lcore_add(slcore_id),
+			"Failed to add service core");
+	TEST_ASSERT_SUCCESS(rte_service_lcore_start(slcore_id),
+			"Failed to start service core");
+
+	/* map services to it */
+	TEST_ASSERT_SUCCESS(rte_service_map_lcore_set(evdev_service_id,
+			slcore_id, 1), "Failed to map evdev service");
+	TEST_ASSERT_SUCCESS(rte_service_map_lcore_set(adapter_service_id,
+			slcore_id, 1), "Failed to map adapter service");
+
+	/* set services to running */
+	TEST_ASSERT_SUCCESS(rte_service_runstate_set(evdev_service_id, 1),
+			"Failed to start evdev service");
+	TEST_ASSERT_SUCCESS(rte_service_runstate_set(adapter_service_id, 1),
+			"Failed to start event timer adapter service");
+
+	/* start the eventdev */
+	TEST_ASSERT_SUCCESS(rte_event_dev_start(evdev),
+			"Failed to start event device");
+
+	/* test adapter start */
+	TEST_ASSERT_SUCCESS(rte_event_timer_adapter_start(g_adapter),
+			"Failed to start event timer adapter");
+
+	TEST_ASSERT_SUCCESS(rte_event_timer_adapter_start(g_adapter),
+			"Failed to repeatedly start adapter");
+
+	return TEST_SUCCESS;
+}
+
+/* Test that adapter stops correctly. */
+static int
+adapter_stop(void)
+{
+	uint32_t evdev_service_id, adapter_service_id;
+	struct rte_event_timer_adapter *l_adapter = NULL;
+
+	/* retrieve service ids */
+	TEST_ASSERT_SUCCESS(rte_event_dev_service_id_get(evdev,
+			&evdev_service_id), "Failed to get event device "
+			"service id");
+	TEST_ASSERT_SUCCESS(rte_event_timer_adapter_service_id_get(g_adapter,
+			&adapter_service_id), "Failed to get event timer "
+			"adapter service id");
+
+	/* Test adapter stop */
+	TEST_ASSERT_SUCCESS(rte_event_timer_adapter_stop(g_adapter),
+			"Failed to stop event adapter");
+
+	TEST_ASSERT_FAIL(rte_event_timer_adapter_stop(l_adapter),
+			"Erroneously stopped null event adapter");
+
+	/* Stop the eventdev */
+	rte_event_dev_stop(evdev);
+
+	/* set services to stopped */
+	TEST_ASSERT_SUCCESS(rte_service_runstate_set(evdev_service_id, 0),
+			"Failed to stop evdev service");
+	TEST_ASSERT_SUCCESS(rte_service_runstate_set(adapter_service_id, 0),
+			"Failed to stop event timer adapter service");
+
+	/* Undo all service and core mappings */
+	rte_service_lcore_reset_all();
+
+	return TEST_SUCCESS;
+}
+
+/* Test increment and reset of ev_enq_count stat */
+static int
+stat_inc_reset_ev_enq(void)
+{
+	int ret, i, n;
+	int num_evtims = NB_TEST_EVENT_TIMERS;
+	struct rte_event_timer *evtims[num_evtims];
+	struct rte_event evs[BATCH_SIZE];
+	struct rte_event_timer_adapter_stats stats;
+
+	ret = rte_mempool_get_bulk(g_event_timer_pool, (void **)evtims,
+				   num_evtims);
+	TEST_ASSERT_EQUAL(ret, 0, "Failed to get array of timer objs: ret = %d",
+			  ret);
+
+	for (i = 0; i < num_evtims; i++) {
+		rte_event_timer_init(evtims[i]);
+		evtims[i]->ev.event_ptr = evtims[i];
+		evtims[i]->ev.queue_id = TEST_QUEUE_ID;
+		evtims[i]->ev.sched_type = RTE_SCHED_TYPE_ATOMIC;
+		evtims[i]->timeout_ticks = 5;  // expire in .5 sec
+	}
+
+	ret = rte_event_timer_adapter_stats_get(g_adapter, &stats);
+	TEST_ASSERT_EQUAL(ret, 0, "Failed to get stats");
+	TEST_ASSERT_EQUAL((int)stats.ev_enq_count, 0, "Stats not clear at "
+			  "startup");
+
+	/* Test with the max value for the adapter */
+	ret = rte_event_timer_arm_burst(g_adapter, evtims, num_evtims);
+	TEST_ASSERT_EQUAL(ret, num_evtims,
+			  "Failed to arm all event timers: attempted = %d, "
+			  "succeeded = %d, rte_errno = %s",
+			  num_evtims, ret, rte_strerror(rte_errno));
+
+	rte_delay_ms(1000);
+
+#define MAX_TRIES 1000
+	int sum = 0;
+	int tries = 0;
+	bool done = false;
+	while (!done) {
+		sum += rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs,
+					       RTE_DIM(evs), 10);
+		if (sum >= num_evtims || ++tries >= MAX_TRIES)
+			done = true;
+
+		rte_delay_ms(10);
+	}
+
+	TEST_ASSERT_EQUAL(sum, num_evtims, "Expected %d timer expiry events, "
+			  "got %d", num_evtims, sum);
+
+	TEST_ASSERT(tries < MAX_TRIES, "Exceeded max tries");
+
+	rte_delay_ms(100);
+
+	/* Make sure the eventdev is still empty */
+	n = rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs, RTE_DIM(evs),
+				      10);
+
+	TEST_ASSERT_EQUAL(n, 0, "Dequeued unexpected number of timer expiry "
+			  "events from event device");
+
+	/* Check stats again */
+	ret = rte_event_timer_adapter_stats_get(g_adapter, &stats);
+	TEST_ASSERT_EQUAL(ret, 0, "Failed to get stats");
+	TEST_ASSERT_EQUAL((int)stats.ev_enq_count, num_evtims,
+			  "Expected enqueue stat = %d; got %d", num_evtims,
+			  (int)stats.ev_enq_count);
+
+	/* Reset and check again */
+	ret = rte_event_timer_adapter_stats_reset(g_adapter);
+	TEST_ASSERT_EQUAL(ret, 0, "Failed to reset stats");
+
+	ret = rte_event_timer_adapter_stats_get(g_adapter, &stats);
+	TEST_ASSERT_EQUAL(ret, 0, "Failed to get stats");
+	TEST_ASSERT_EQUAL((int)stats.ev_enq_count, 0,
+			  "Expected enqueue stat = %d; got %d", 0,
+			  (int)stats.ev_enq_count);
+
+	rte_mempool_put_bulk(g_event_timer_pool, (void **)evtims, num_evtims);
+
+	return TEST_SUCCESS;
+}
+
+/* Test various cases in arming timers */
+static int
+event_timer_arm(void)
+{
+	uint16_t n;
+	int ret;
+	struct rte_event_timer_adapter *adapter = g_adapter;
+	struct rte_event_timer *evtim = NULL;
+	struct rte_event evs[BATCH_SIZE];
+
+	rte_mempool_get(g_event_timer_pool, (void **)&evtim);
+	if (evtim == NULL) {
+		/* Failed to get an event timer object */
+		return TEST_FAILED;
+	}
+
+	/* Set up a timer */
+	rte_event_timer_init(evtim);
+	evtim->ev.event_ptr = evtim;
+	evtim->ev.queue_id = TEST_QUEUE_ID;
+	evtim->ev.sched_type = RTE_SCHED_TYPE_ATOMIC;
+	evtim->timeout_ticks = 5;  // expire in 0.5 sec
+
+	/* Test single timer arm succeeds */
+	ret = rte_event_timer_arm_burst(adapter, &evtim, 1);
+	TEST_ASSERT_EQUAL(ret, 1, "Failed to arm event timer: %s\n",
+			  rte_strerror(rte_errno));
+	TEST_ASSERT_EQUAL(evtim->state, RTE_EVENT_TIMER_ARMED, "Event timer "
+			  "in incorrect state");
+
+	/* Test arm of armed timer fails */
+	ret = rte_event_timer_arm_burst(adapter, &evtim, 1);
+	TEST_ASSERT_EQUAL(ret, 0, "expected return value from "
+			  "rte_event_timer_arm_burst: 0, got: %d", ret);
+	TEST_ASSERT_EQUAL(rte_errno, EALREADY, "Unexpected rte_errno value "
+			  "after arming already armed timer");
+
+	/* Let timer expire */
+	rte_delay_ms(1000);
+
+	n = rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs, RTE_DIM(evs), 0);
+	TEST_ASSERT_EQUAL(n, 1, "Failed to dequeue expected number of expiry "
+			  "events from event device");
+
+	rte_mempool_put(g_event_timer_pool, evtim);
+
+	return TEST_SUCCESS;
+}
+
+/* This test checks that repeated references to the same event timer in the
+ * arm request work as expected; only the first one through should succeed.
+ */
+static int
+event_timer_arm_double(void)
+{
+	uint16_t n;
+	int ret;
+	struct rte_event_timer_adapter *adapter = g_adapter;
+	struct rte_event_timer *evtim = NULL;
+	struct rte_event evs[BATCH_SIZE];
+
+	rte_mempool_get(g_event_timer_pool, (void **)&evtim);
+	if (evtim == NULL) {
+		/* Failed to get an event timer object */
+		return TEST_FAILED;
+	}
+
+	/* Set up a timer */
+	rte_event_timer_init(evtim);
+	evtim->ev.event_ptr = evtim;
+	evtim->ev.queue_id = TEST_QUEUE_ID;
+	evtim->ev.sched_type = RTE_SCHED_TYPE_ATOMIC;
+	evtim->timeout_ticks = 5;  // expire in 0.5 sec
+
+	struct rte_event_timer *evtim_arr[] = {evtim, evtim};
+	ret = rte_event_timer_arm_burst(adapter, evtim_arr, RTE_DIM(evtim_arr));
+	TEST_ASSERT_EQUAL(ret, 1, "Unexpected return value from "
+			  "rte_event_timer_arm_burst");
+	TEST_ASSERT_EQUAL(rte_errno, EALREADY, "Unexpected rte_errno value "
+			  "after double-arm");
+
+	/* Let timer expire */
+	rte_delay_ms(600);
+
+	n = rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs, RTE_DIM(evs), 0);
+	TEST_ASSERT_EQUAL(n, 1, "Dequeued incorrect number of expiry events - "
+			  "expected: 1, actual: %d", n);
+
+	rte_mempool_put(g_event_timer_pool, evtim);
+
+	return TEST_SUCCESS;
+}
+
+/* Test the timer expiry event is generated at the expected time.  */
+static int
+event_timer_arm_expiry(void)
+{
+	uint16_t n;
+	int ret;
+	struct rte_event_timer_adapter *adapter = g_adapter;
+	struct rte_event_timer *evtim = NULL;
+	struct rte_event_timer *evtim2 = NULL;
+	struct rte_event evs[BATCH_SIZE];
+
+	/* Set up an event timer */
+	rte_mempool_get(g_event_timer_pool, (void **)&evtim);
+	if (evtim == NULL) {
+		/* Failed to get an event timer object */
+		return TEST_FAILED;
+	}
+
+	rte_event_timer_init(evtim);
+	evtim->ev.event_ptr = evtim;
+	evtim->ev.queue_id = TEST_QUEUE_ID;
+	evtim->ev.sched_type = RTE_SCHED_TYPE_ATOMIC;
+	evtim->timeout_ticks = 30;  // expire in 3 sec
+
+	ret = rte_event_timer_arm_burst(adapter, &evtim, 1);
+	TEST_ASSERT_EQUAL(ret, 1, "Failed to arm event timer: %s",
+			  rte_strerror(rte_errno));
+	TEST_ASSERT_EQUAL(evtim->state, RTE_EVENT_TIMER_ARMED, "Event "
+			  "timer in incorrect state");
+
+	rte_delay_ms(2999);
+
+	n = rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs, RTE_DIM(evs), 0);
+	TEST_ASSERT_EQUAL(n, 0, "Dequeued unexpected timer expiry event");
+
+	/* Delay 100 ms to account for the adapter tick window - should let us
+	 * dequeue one event
+	 */
+	rte_delay_ms(100);
+
+	n = rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs, RTE_DIM(evs), 0);
+	TEST_ASSERT_EQUAL(n, 1, "Dequeued incorrect number (%d) of timer "
+			  "expiry events", n);
+	TEST_ASSERT_EQUAL(evs[0].event_type, RTE_EVENT_TYPE_TIMER,
+			  "Dequeued unexpected type of event");
+
+	/* Check that we recover the original event timer and then free it */
+	evtim2 = evs[0].event_ptr;
+	TEST_ASSERT_EQUAL(evtim, evtim2,
+			  "Failed to recover pointer to original event timer");
+	TEST_ASSERT_EQUAL(evtim->state, RTE_EVENT_TIMER_NOT_ARMED, "Event "
+			  "timer in incorrect state");
+	rte_mempool_put(g_event_timer_pool, evtim2);
+
+	return TEST_SUCCESS;
+}
+
+/* Check that rearming a timer works as expected. */
+static int
+event_timer_arm_rearm(void)
+{
+	uint16_t n;
+	int ret;
+	struct rte_event_timer *evtim = NULL;
+	struct rte_event_timer *evtim2 = NULL;
+	struct rte_event evs[BATCH_SIZE];
+
+	rte_mempool_get(g_event_timer_pool, (void **)&evtim);
+	if (evtim == NULL) {
+		/* Failed to get an event timer object */
+		return TEST_FAILED;
+	}
+
+	/* Set up a timer */
+	rte_event_timer_init(evtim);
+	evtim->ev.event_ptr = evtim;
+	evtim->ev.queue_id = TEST_QUEUE_ID;
+	evtim->ev.sched_type = RTE_SCHED_TYPE_ATOMIC;
+	evtim->timeout_ticks = 1;  // expire in 0.1 sec
+
+	/* Arm it */
+	ret = rte_event_timer_arm_burst(g_adapter, &evtim, 1);
+	TEST_ASSERT_EQUAL(ret, 1, "Failed to arm event timer: %s\n",
+			  rte_strerror(rte_errno));
+
+	/* Add 100ms to account for the adapter tick window */
+	rte_delay_ms(100 + 100);
+
+	n = rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs, RTE_DIM(evs), 0);
+	TEST_ASSERT_EQUAL(n, 1, "Failed to dequeue expected number of expiry "
+			  "events from event device");
+
+	/* Recover the timer through the event that was dequeued. */
+	evtim2 = evs[0].event_ptr;
+	TEST_ASSERT_EQUAL(evtim, evtim2,
+			  "Failed to recover pointer to original event timer");
+
+	/* Rearm it */
+	ret = rte_event_timer_arm_burst(g_adapter, &evtim2, 1);
+	TEST_ASSERT_EQUAL(ret, 1, "Failed to arm event timer: %s\n",
+			  rte_strerror(rte_errno));
+
+	/* Add 100ms to account for the adapter tick window */
+	rte_delay_ms(100 + 100);
+
+	n = rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs, RTE_DIM(evs), 0);
+	TEST_ASSERT_EQUAL(n, 1, "Failed to dequeue expected number of expiry "
+			  "events from event device");
+
+	/* Free it */
+	evtim2 = evs[0].event_ptr;
+	TEST_ASSERT_EQUAL(evtim, evtim2,
+			  "Failed to recover pointer to original event timer");
+	rte_mempool_put(g_event_timer_pool, evtim2);
+
+	return TEST_SUCCESS;
+}
+
+/* Check that creating an event timer with incorrect event sched type fails. */
+static int
+event_timer_arm_invalid_sched_type(void)
+{
+	int ret;
+	struct rte_event_timer *evtim = NULL;
+
+	rte_mempool_get(g_event_timer_pool, (void **)&evtim);
+	if (evtim == NULL) {
+		/* Failed to get an event timer object */
+		return TEST_FAILED;
+	}
+
+	rte_event_timer_init(evtim);
+	evtim->ev.event_ptr = evtim;
+	evtim->ev.queue_id = TEST_QUEUE_ID;
+	evtim->ev.sched_type = RTE_SCHED_TYPE_PARALLEL; // bad sched type
+	evtim->timeout_ticks = 5;  // expire in 0.5 sec
+
+	ret = rte_event_timer_arm_burst(g_adapter, &evtim, 1);
+	TEST_ASSERT_EQUAL(ret, 0, "Expected to fail timer arm with invalid "
+			  "sched type, but didn't");
+	TEST_ASSERT_EQUAL(rte_errno, EINVAL, "Unexpected rte_errno value after"
+			  " arm fail with invalid queue");
+
+	rte_mempool_put(g_event_timer_pool, &evtim);
+
+	return TEST_SUCCESS;
+}
+
+/* Check that creating an event timer with a timeout value that is too small or
+ * too big fails.
+ */
+static int
+event_timer_arm_invalid_timeout(void)
+{
+	int ret;
+	struct rte_event_timer *evtim = NULL;
+
+	rte_mempool_get(g_event_timer_pool, (void **)&evtim);
+	if (evtim == NULL) {
+		/* Failed to get an event timer object */
+		return TEST_FAILED;
+	}
+
+	rte_event_timer_init(evtim);
+	evtim->ev.event_ptr = evtim;
+	evtim->ev.queue_id = TEST_QUEUE_ID;
+	evtim->ev.sched_type = RTE_SCHED_TYPE_ATOMIC;
+	evtim->timeout_ticks = 0;  // timeout too small
+
+	ret = rte_event_timer_arm_burst(g_adapter, &evtim, 1);
+	TEST_ASSERT_EQUAL(ret, 0, "Expected to fail timer arm with invalid "
+			  "timeout, but didn't");
+	TEST_ASSERT_EQUAL(rte_errno, EINVAL, "Unexpected rte_errno value after"
+			  " arm fail with invalid timeout");
+	TEST_ASSERT_EQUAL(evtim->state, RTE_EVENT_TIMER_ERROR_TOOEARLY,
+			  "Unexpected event timer state");
+
+	rte_event_timer_init(evtim);
+	evtim->ev.event_ptr = evtim;
+	evtim->ev.queue_id = TEST_QUEUE_ID;
+	evtim->ev.sched_type = RTE_SCHED_TYPE_ATOMIC;
+	evtim->timeout_ticks = 1801;  // timeout too big
+
+	ret = rte_event_timer_arm_burst(g_adapter, &evtim, 1);
+	TEST_ASSERT_EQUAL(ret, 0, "Expected to fail timer arm with invalid "
+			  "timeout, but didn't");
+	TEST_ASSERT_EQUAL(rte_errno, EINVAL, "Unexpected rte_errno value after"
+			  " arm fail with invalid timeout");
+	TEST_ASSERT_EQUAL(evtim->state, RTE_EVENT_TIMER_ERROR_TOOLATE,
+			  "Unexpected event timer state");
+
+	rte_mempool_put(g_event_timer_pool, evtim);
+
+	return TEST_SUCCESS;
+}
+
+/* Check that the adapter handles the max specified number of timers as
+ * expected.
+ */
+static int
+event_timer_arm_max(void)
+{
+	int ret, i, n;
+	int num_evtims = NB_TEST_EVENT_TIMERS;
+	struct rte_event_timer *evtims[num_evtims];
+	struct rte_event evs[BATCH_SIZE];
+
+	ret = rte_mempool_get_bulk(g_event_timer_pool, (void **)evtims,
+				   num_evtims);
+	TEST_ASSERT_EQUAL(ret, 0, "Failed to get array of timer objs: ret = %d",
+			  ret);
+
+	for (i = 0; i < num_evtims; i++) {
+		rte_event_timer_init(evtims[i]);
+		evtims[i]->ev.event_ptr = evtims[i];
+		evtims[i]->ev.queue_id = TEST_QUEUE_ID;
+		evtims[i]->ev.sched_type = RTE_SCHED_TYPE_ATOMIC;
+		evtims[i]->timeout_ticks = 5;  // expire in .5 sec
+	}
+
+	/* Test with the max value for the adapter */
+	ret = rte_event_timer_arm_burst(g_adapter, evtims, num_evtims);
+	TEST_ASSERT_EQUAL(ret, num_evtims,
+			  "Failed to arm all event timers: attempted = %d, "
+			  "succeeded = %d, rte_errno = %s",
+			  num_evtims, ret, rte_strerror(rte_errno));
+
+	rte_delay_ms(1000);
+
+#define MAX_TRIES 1000
+	int sum = 0;
+	int tries = 0;
+	bool done = false;
+	while (!done) {
+		sum += rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs,
+					       RTE_DIM(evs), 10);
+		if (sum >= num_evtims || ++tries >= MAX_TRIES)
+			done = true;
+
+		rte_delay_ms(10);
+	}
+
+	TEST_ASSERT_EQUAL(sum, num_evtims, "Expected %d timer expiry events, "
+			  "got %d", num_evtims, sum);
+
+	TEST_ASSERT(tries < MAX_TRIES, "Exceeded max tries");
+
+	rte_delay_ms(100);
+
+	/* Make sure the eventdev is still empty */
+	n = rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs, RTE_DIM(evs),
+				      10);
+
+	TEST_ASSERT_EQUAL(n, 0, "Dequeued unexpected number of timer expiry "
+			  "events from event device");
+
+	rte_mempool_put_bulk(g_event_timer_pool, (void **)evtims, num_evtims);
+
+	return TEST_SUCCESS;
+}
+
+static int
+event_timer_cancel(void)
+{
+	uint16_t n;
+	int ret;
+	struct rte_event_timer_adapter *adapter = g_adapter;
+	struct rte_event_timer *evtim = NULL;
+	struct rte_event evs[BATCH_SIZE];
+
+	rte_mempool_get(g_event_timer_pool, (void **)&evtim);
+	if (evtim == NULL) {
+		/* Failed to get an event timer object */
+		return TEST_FAILED;
+	}
+
+	/* Check that cancelling an uninited timer fails */
+	ret = rte_event_timer_cancel_burst(adapter, &evtim, 1);
+	TEST_ASSERT_EQUAL(ret, 0, "Succeeded unexpectedly in canceling "
+			  "uninited timer");
+	TEST_ASSERT_EQUAL(rte_errno, EINVAL, "Unexpected rte_errno value after "
+			  "cancelling uninited timer");
+
+	/* Set up a timer */
+	rte_event_timer_init(evtim);
+	evtim->ev.event_ptr = evtim;
+	evtim->ev.queue_id = TEST_QUEUE_ID;
+	evtim->ev.sched_type = RTE_SCHED_TYPE_ATOMIC;
+	evtim->timeout_ticks = 30;  // expire in 3 sec
+
+	/* Check that cancelling an inited but unarmed timer fails */
+	ret = rte_event_timer_cancel_burst(adapter, &evtim, 1);
+	TEST_ASSERT_EQUAL(ret, 0, "Succeeded unexpectedly in canceling "
+			  "unarmed timer");
+	TEST_ASSERT_EQUAL(rte_errno, EINVAL, "Unexpected rte_errno value after "
+			  "cancelling unarmed timer");
+
+	ret = rte_event_timer_arm_burst(adapter, &evtim, 1);
+	TEST_ASSERT_EQUAL(ret, 1, "Failed to arm event timer: %s\n",
+			  rte_strerror(rte_errno));
+	TEST_ASSERT_EQUAL(evtim->state, RTE_EVENT_TIMER_ARMED,
+			  "evtim in incorrect state");
+
+	/* Delay 1 sec */
+	rte_delay_ms(1000);
+
+	ret = rte_event_timer_cancel_burst(adapter, &evtim, 1);
+	TEST_ASSERT_EQUAL(ret, 1, "Failed to cancel event_timer: %s\n",
+			  rte_strerror(rte_errno));
+	TEST_ASSERT_EQUAL(evtim->state, RTE_EVENT_TIMER_CANCELED,
+			  "evtim in incorrect state");
+
+	rte_delay_ms(3000);
+
+	/* Make sure that no expiry event was generated */
+	n = rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs, RTE_DIM(evs), 0);
+	TEST_ASSERT_EQUAL(n, 0, "Dequeued unexpected timer expiry event\n");
+
+	return TEST_SUCCESS;
+}
+
+static int
+event_timer_cancel_double(void)
+{
+	uint16_t n;
+	int ret;
+	struct rte_event_timer_adapter *adapter = g_adapter;
+	struct rte_event_timer *evtim = NULL;
+	struct rte_event evs[BATCH_SIZE];
+
+	rte_mempool_get(g_event_timer_pool, (void **)&evtim);
+	if (evtim == NULL) {
+		/* Failed to get an event timer object */
+		return TEST_FAILED;
+	}
+
+	/* Set up a timer */
+	rte_event_timer_init(evtim);
+	evtim->ev.event_ptr = evtim;
+	evtim->ev.queue_id = TEST_QUEUE_ID;
+	evtim->ev.sched_type = RTE_SCHED_TYPE_ATOMIC;
+	evtim->timeout_ticks = 30;  // expire in 3 sec
+
+	ret = rte_event_timer_arm_burst(adapter, &evtim, 1);
+	TEST_ASSERT_EQUAL(ret, 1, "Failed to arm event timer: %s\n",
+			  rte_strerror(rte_errno));
+	TEST_ASSERT_EQUAL(evtim->state, RTE_EVENT_TIMER_ARMED,
+			  "timer in unexpected state");
+
+	/* Now, test that referencing the same timer twice in the same call
+	 * fails
+	 */
+	struct rte_event_timer *evtim_arr[] = {evtim, evtim};
+	ret = rte_event_timer_cancel_burst(adapter, evtim_arr,
+					   RTE_DIM(evtim_arr));
+
+	/* Two requests to cancel same timer, only one should succeed */
+	TEST_ASSERT_EQUAL(ret, 1, "Succeeded unexpectedly in canceling timer "
+			  "twice");
+
+	TEST_ASSERT_EQUAL(rte_errno, EALREADY, "Unexpected rte_errno value "
+			  "after double-cancel: rte_errno = %d", rte_errno);
+
+	rte_delay_ms(3000);
+
+	/* Still make sure that no expiry event was generated */
+	n = rte_event_dequeue_burst(evdev, TEST_PORT_ID, evs, RTE_DIM(evs), 0);
+	TEST_ASSERT_EQUAL(n, 0, "Dequeued unexpected timer expiry event\n");
+
+	return TEST_SUCCESS;
+}
+
+/* Check that event timer adapter tick resolution works as expected by testing
+ * the number of adapter ticks that occur within a particular time interval.
+ */
+static int
+adapter_tick_resolution(void)
+{
+	struct rte_event_timer_adapter_stats stats;
+	uint64_t adapter_tick_count;
+
+	TEST_ASSERT_SUCCESS(rte_event_timer_adapter_stats_get(g_adapter,
+			&stats), "Failed to get adapter stats");
+	TEST_ASSERT_EQUAL(stats.adapter_tick_count, 0, "Adapter tick count "
+			"not zeroed out");
+
+	/* Delay 1 second; should let at least 10 ticks occur with the default
+	 * adapter configuration used by this test.
+	 */
+	rte_delay_ms(1000);
+
+	TEST_ASSERT_SUCCESS(rte_event_timer_adapter_stats_get(g_adapter,
+			&stats), "Failed to get adapter stats");
+
+	adapter_tick_count = stats.adapter_tick_count;
+	TEST_ASSERT(adapter_tick_count >= 10 && adapter_tick_count <= 12,
+			"Expected 10-12 adapter ticks, got %"PRIu64"\n",
+			adapter_tick_count);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite adapter_tests  = {
+	.suite_name = "event timer adapter test suite",
+	.setup = testsuite_setup,
+	.teardown = testsuite_teardown,
+	.unit_test_cases = {
+		TEST_CASE(adapter_create),
+		TEST_CASE_ST(g_adapter_create, NULL, adapter_free),
+		TEST_CASE_ST(g_adapter_create, g_adapter_free,
+				adapter_get_info),
+		TEST_CASE_ST(g_adapter_create, g_adapter_free,
+				adapter_lookup),
+		TEST_CASE_ST(g_adapter_create, g_adapter_stop_free,
+				adapter_start),
+		TEST_CASE_ST(g_adapter_create_start, g_adapter_free,
+				adapter_stop),
+		TEST_CASE_ST(g_adapter_create_start, g_adapter_stop_free,
+				stat_inc_reset_ev_enq),
+		TEST_CASE_ST(g_adapter_create_start, g_adapter_stop_free,
+				event_timer_arm),
+		TEST_CASE_ST(g_adapter_create_start, g_adapter_stop_free,
+				event_timer_arm_double),
+		TEST_CASE_ST(g_adapter_create_start, g_adapter_stop_free,
+				event_timer_arm_expiry),
+		TEST_CASE_ST(g_adapter_create_start, g_adapter_stop_free,
+				event_timer_arm_rearm),
+		TEST_CASE_ST(g_adapter_create_start, g_adapter_stop_free,
+				event_timer_arm_max),
+		TEST_CASE_ST(g_adapter_create_start, g_adapter_stop_free,
+				event_timer_arm_invalid_sched_type),
+		TEST_CASE_ST(g_adapter_create_start, g_adapter_stop_free,
+				event_timer_arm_invalid_timeout),
+		TEST_CASE_ST(g_adapter_create_start, g_adapter_stop_free,
+				event_timer_cancel),
+		TEST_CASE_ST(g_adapter_create_start, g_adapter_stop_free,
+				event_timer_cancel_double),
+		TEST_CASE_ST(g_adapter_create_start,
+				g_adapter_stop_free,
+				adapter_tick_resolution),
+		/* This test is last because it can influence tests that follow
+		 * it
+		 */
+		TEST_CASE(adapter_create_max),
+		TEST_CASES_END() /**< NULL terminate unit test array */
+	}
+};
+
+static int
+test_event_timer_adapter_common(void)
+{
+	return unit_test_suite_runner(&adapter_tests);
+}
+
+REGISTER_TEST_COMMAND(event_timer_adapter_autotest,
+		      test_event_timer_adapter_common);