diff --git a/app/test/meson.build b/app/test/meson.build
index a7611686adcb..7f41f2cf5ec0 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -48,6 +48,7 @@ test_sources = files(
         'test_efd.c',
         'test_efd_perf.c',
         'test_errno.c',
+        'test_ethdev.c',
         'test_ethdev_link.c',
         'test_event_crypto_adapter.c',
         'test_event_eth_rx_adapter.c',
@@ -215,6 +216,7 @@ fast_tests = [
         ['eal_fs_autotest', true],
         ['errno_autotest', true],
         ['ethdev_link_status', true],
+        ['ethdev_api_autotest', true],
         ['event_ring_autotest', true],
         ['fib_autotest', true],
         ['fib6_autotest', true],
diff --git a/app/test/test.c b/app/test/test.c
index 173d202e4774..82727e10b2be 100644
--- a/app/test/test.c
+++ b/app/test/test.c
@@ -222,6 +222,7 @@ main(int argc, char **argv)
 				break;
 		}
 		cmdline_free(cl);
+		printf("\n");
 		goto out;
 	} else {
 		/* if no DPDK_TEST env variable, go interactive */
diff --git a/app/test/test_ethdev.c b/app/test/test_ethdev.c
new file mode 100644
index 000000000000..3100d8bbc9b5
--- /dev/null
+++ b/app/test/test_ethdev.c
@@ -0,0 +1,1582 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#include <inttypes.h>
+
+#include <rte_ethdev.h>
+#include <ethdev_driver.h>
+
+#include "test.h"
+#include "virtual_pmd.h"
+
+#define MAX_PORT_NUMBER	2
+
+static uint16_t port_id[MAX_PORT_NUMBER];
+static struct eth_dev_ops *dev_ops[MAX_PORT_NUMBER];
+static uint16_t initial_port_number;
+static uint16_t port_number;
+static uint64_t port_owner_id;
+static uint16_t invalid_port_id = 999;
+static uint16_t default_nb_rx_q = 2;
+static uint16_t default_nb_tx_q = 2;
+
+#define TEST_PMD_NAME	"net_test"
+
+#define MAX_RX_PKTLEN	2048
+
+static int
+ethdev_api_setup(void)
+{
+	struct rte_ether_addr mac_addr = {
+		{ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x00 },
+	};
+	char name[RTE_ETH_NAME_MAX_LEN];
+	uint16_t local_port_id;
+	int ret;
+
+	if (port_number != 0)
+		return TEST_SUCCESS;
+
+	initial_port_number = rte_eth_dev_count_total();
+
+	snprintf(name, RTE_ETH_NAME_MAX_LEN, "%s%d", TEST_PMD_NAME, port_number);
+	ret = virtual_ethdev_create(name, &mac_addr, rte_socket_id(), 1);
+	TEST_ASSERT(ret >= 0, "Failed to create test PMD %s\n", name);
+	local_port_id = (uint16_t)ret;
+	dev_ops[port_number] = virtual_ethdev_ops_get(local_port_id);
+	port_id[port_number++] = local_port_id;
+
+	snprintf(name, RTE_ETH_NAME_MAX_LEN, "%s%d", TEST_PMD_NAME, port_number);
+	ret = virtual_ethdev_create(name, &mac_addr, rte_socket_id(), 1);
+	TEST_ASSERT(ret >= 0, "Failed to create test PMD %s\n", name);
+	local_port_id = (uint16_t)ret;
+	dev_ops[port_number] = virtual_ethdev_ops_get(local_port_id);
+	port_id[port_number++] = local_port_id;
+
+	return TEST_SUCCESS;
+}
+
+static void
+ethdev_api_teardown(void)
+{
+	int local_port_number = port_number;
+	char name[RTE_ETH_NAME_MAX_LEN];
+	int i;
+
+	for (i = 0; i < local_port_number; i++) {
+		rte_eth_dev_close(port_id[i]);
+		snprintf(name, RTE_ETH_NAME_MAX_LEN, "%s%d", TEST_PMD_NAME, i);
+		/* TODO: get bus from eth_dev */
+		rte_eal_hotplug_remove("pci", name);
+		port_number--;
+	}
+
+	/* reset global variables */
+	memset(port_id, 0, MAX_PORT_NUMBER * sizeof(port_id[0]));
+	memset(dev_ops, 0, MAX_PORT_NUMBER * sizeof(dev_ops[0]));
+	port_owner_id = RTE_ETH_DEV_NO_OWNER;
+}
+
+static int
+ethdev_count_avail(void)
+{
+	uint16_t count;
+
+	count = rte_eth_dev_count_avail();
+	TEST_ASSERT_EQUAL(count, port_number + initial_port_number,
+		"Failed to get available ethdev device count\n");
+
+	return TEST_SUCCESS;
+}
+
+static int
+ethdev_owner_get(void)
+{
+	char no_name[RTE_ETH_MAX_OWNER_NAME_LEN] = "";
+	struct rte_eth_dev_owner owner;
+	int ret;
+	int i;
+
+	for (i = 0; i < port_number; i++) {
+		ret = rte_eth_dev_owner_get(invalid_port_id, &owner);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Owner get accepted invalid port id %u\n",
+			invalid_port_id);
+
+		ret = rte_eth_dev_owner_get(port_id[i], NULL);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Owner get accepted null owner for port id %u\n",
+			port_id[i]);
+
+		ret = rte_eth_dev_owner_get(port_id[i], &owner);
+		RTE_TEST_ASSERT_SUCCESS(ret,
+			"Failed to get owner for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(owner.id, RTE_ETH_DEV_NO_OWNER,
+			"Received owner id doesn't match with no owner id port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_BUFFERS_ARE_EQUAL(owner.name, no_name,
+			RTE_ETH_MAX_OWNER_NAME_LEN,
+			"Received owner name doesn't match with no owner name port id %u\n",
+			port_id[i]);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+ethdev_owner_new(void)
+{
+	uint64_t local_port_owner_id;
+	int ret;
+
+	/* null owner id pointer */
+	ret = rte_eth_dev_owner_new(NULL);
+	RTE_TEST_ASSERT_FAIL(ret, "NULL owner argument accepted\n");
+
+	ret = rte_eth_dev_owner_new(&port_owner_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get new owner id\n");
+
+	/* Check not same owner ID received twice */
+	local_port_owner_id = port_owner_id;
+	ret = rte_eth_dev_owner_new(&port_owner_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get new owner id\n");
+	TEST_ASSERT_NOT_EQUAL(port_owner_id, local_port_owner_id,
+		"Existing owner id returned\n");
+
+	return TEST_SUCCESS;
+}
+
+static int
+ethdev_owner_set(void)
+{
+	struct rte_eth_dev_owner owner = {
+		.id = RTE_ETH_DEV_NO_OWNER,
+		.name = "TEST",
+	};
+	struct rte_eth_dev_owner owner_get;
+	uint16_t local_port_id = port_id[1];
+	const char *alternate_name = "TEST2";
+	int ret;
+
+	/* invalid port id */
+	ret = rte_eth_dev_owner_set(invalid_port_id, &owner);
+	RTE_TEST_ASSERT_FAIL(ret, "Owner set accepted invalid port id %u\n",
+		invalid_port_id);
+
+	/* null owner */
+	ret = rte_eth_dev_owner_set(local_port_id, NULL);
+	RTE_TEST_ASSERT_FAIL(ret, "Owner set accepted null owner for port id %u\n",
+		local_port_id);
+
+	/* no owner id */
+	ret = rte_eth_dev_owner_set(local_port_id, &owner);
+	RTE_TEST_ASSERT_FAIL(ret, "Accepted no owner id for port id %u\n",
+		local_port_id);
+
+	/* invalid owner id */
+	owner.id = port_owner_id + 1; /* 'rte_eth_dev_owner_new() called twice */
+	ret = rte_eth_dev_owner_set(local_port_id, &owner);
+	RTE_TEST_ASSERT_FAIL(ret, "Accepted invalid owner id for port id %u\n",
+		local_port_id);
+
+	/* set owner */
+	owner.id = port_owner_id;
+	ret = rte_eth_dev_owner_set(local_port_id, &owner);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to set owner for port id %u\n",
+		local_port_id);
+
+	/* get the owner back and verify */
+	ret = rte_eth_dev_owner_get(local_port_id, &owner_get);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get owner for port id %u\n",
+		local_port_id);
+	TEST_ASSERT_EQUAL(owner.id, owner_get.id,
+		"Received owner id doesn't match with set owner id port id %u\n",
+		local_port_id);
+	TEST_ASSERT_BUFFERS_ARE_EQUAL(owner.name, owner_get.name,
+		RTE_ETH_MAX_OWNER_NAME_LEN,
+		"Received owner name doesn't match with set owner name port id %u\n",
+		local_port_id);
+
+	/* set same owner */
+	ret = rte_eth_dev_owner_set(local_port_id, &owner);
+	RTE_TEST_ASSERT_FAIL(ret, "Accepted same owner for port id %u\n",
+		local_port_id);
+
+	/* no owner id after owner set */
+	owner.id = RTE_ETH_DEV_NO_OWNER;
+	ret = rte_eth_dev_owner_set(local_port_id, &owner);
+	RTE_TEST_ASSERT_FAIL(ret, "Accepted no owner id for port id %u\n",
+		local_port_id);
+
+	/* set owner with same owner id different owner name */
+	owner.id = port_owner_id;
+	strlcpy(owner.name, alternate_name, RTE_ETH_MAX_OWNER_NAME_LEN);
+	ret = rte_eth_dev_owner_set(local_port_id, &owner);
+	RTE_TEST_ASSERT_FAIL(ret,
+		"Accepted same owner id different owner name for port id %u\n",
+		local_port_id);
+
+	/* set owner with same owner name different owner id */
+	owner.id = port_owner_id - 1; /* Two owner ids received */
+	ret = rte_eth_dev_owner_set(local_port_id, &owner);
+	RTE_TEST_ASSERT_FAIL(ret,
+		"Accepted different owner id with same owner name for port id %u\n",
+		local_port_id);
+
+	/* Set owner with very large name */
+	ret = rte_eth_dev_owner_unset(local_port_id, port_owner_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to unset owner for port id %u\n",
+		local_port_id);
+
+	owner.id = port_owner_id;
+	memset(owner.name, 'x', RTE_ETH_MAX_OWNER_NAME_LEN);
+	ret = rte_eth_dev_owner_set(local_port_id, &owner);
+	RTE_TEST_ASSERT_SUCCESS(ret,
+		"Failed to set owner with large name for port id %u\n",
+		local_port_id);
+
+	/* Force printing the previously set large name */
+	ret = rte_eth_dev_owner_set(local_port_id, &owner);
+	RTE_TEST_ASSERT_FAIL(ret,
+		"Accepted same owner with large name for port id %u\n",
+		local_port_id);
+
+	return TEST_SUCCESS;
+}
+
+/* There must be two ethdev devices created at this point,
+ * But one of them has owner, so available and total device counts
+ * should differ.
+ */
+static int
+ethdev_count_total(void)
+{
+	uint16_t total_count;
+	uint16_t available_count;
+	uint16_t count;
+
+	total_count = rte_eth_dev_count_total();
+	TEST_ASSERT_EQUAL(total_count, initial_port_number + port_number,
+		"Failed to get total ethdev device count\n");
+
+	available_count = initial_port_number + port_number - 1; /* One has owner */
+	count = rte_eth_dev_count_avail();
+	TEST_ASSERT_EQUAL(count, available_count,
+		"Failed to get available ethdev device count after ownership\n");
+
+	return TEST_SUCCESS;
+}
+
+static int
+ethdev_owner_unset(void)
+{
+	char no_name[RTE_ETH_MAX_OWNER_NAME_LEN] = "";
+	uint16_t local_port_id = port_id[1];
+	struct rte_eth_dev_owner owner;
+	uint64_t invalid_owner_id;
+	int ret;
+
+	/* unset owner with invalid port id */
+	ret = rte_eth_dev_owner_unset(invalid_port_id, port_owner_id);
+	RTE_TEST_ASSERT_FAIL(ret, "Owner unset accepted invalid port id %u\n",
+		invalid_port_id);
+
+	/* unset owner with invalid owner id */
+	invalid_owner_id = port_owner_id - 1;
+	ret = rte_eth_dev_owner_unset(local_port_id, invalid_owner_id);
+	RTE_TEST_ASSERT_FAIL(ret,
+		"Owner unset accepted invalid owner id %" PRIu64 " for port id %u\n",
+		invalid_owner_id, local_port_id);
+
+	invalid_owner_id = port_owner_id + 1;
+	ret = rte_eth_dev_owner_unset(local_port_id, invalid_owner_id);
+	RTE_TEST_ASSERT_FAIL(ret,
+		"Owner unset accepted invalid owner id %" PRIu64 " for port id %u\n",
+		invalid_owner_id, local_port_id);
+
+	/* unset owner */
+	ret = rte_eth_dev_owner_unset(local_port_id, port_owner_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to unset owner for port id %u\n",
+		local_port_id);
+
+	/* verify owner unset */
+	ret = rte_eth_dev_owner_get(local_port_id, &owner);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get owner for port id %u\n",
+		local_port_id);
+	TEST_ASSERT_EQUAL(owner.id, RTE_ETH_DEV_NO_OWNER,
+		"Received owner id doesn't match with no owner id port id %u\n",
+		local_port_id);
+	TEST_ASSERT_BUFFERS_ARE_EQUAL(owner.name, no_name,
+		RTE_ETH_MAX_OWNER_NAME_LEN,
+		"Received owner name doesn't match with no owner name port id %u\n",
+		local_port_id);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ethdev_owner_delete(void)
+{
+	struct rte_eth_dev_owner owner = {
+		.id = port_owner_id,
+		.name = "TEST",
+	};
+	uint64_t invalid_owner_id;
+	int count;
+	int ret;
+	int i;
+
+	for (i = 0; i < port_number; i++) {
+		/* set owner */
+		ret = rte_eth_dev_owner_set(port_id[i], &owner);
+		RTE_TEST_ASSERT_SUCCESS(ret,
+			"Failed to set owner for port id %u\n",
+			port_id[i]);
+
+		/* delete owner with invalid owner id */
+		invalid_owner_id = port_owner_id - 1;
+		ret = rte_eth_dev_owner_unset(port_id[i], invalid_owner_id);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Owner delete accepted invalid owner id %" PRIu64 " for port id %u\n",
+			invalid_owner_id, port_id[i]);
+
+		invalid_owner_id = port_owner_id + 1;
+		ret = rte_eth_dev_owner_unset(port_id[i], invalid_owner_id);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Owner delete accepted invalid owner id %" PRIu64 " for port id %u\n",
+			invalid_owner_id, port_id[i]);
+	}
+
+	ret = rte_eth_dev_owner_delete(port_owner_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to delete owner id %" PRIu64 "\n",
+		port_owner_id);
+
+	count = rte_eth_dev_count_avail();
+	TEST_ASSERT_EQUAL(count, initial_port_number + port_number,
+		"Failed to delete owner id %" PRIu64 " from some ethdev devices\n",
+		port_owner_id);
+
+	return TEST_SUCCESS;
+}
+
+static int
+configure_fail(struct rte_eth_dev *dev __rte_unused)
+{
+	return -1;
+}
+
+static int
+info_get_default_config(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+#define DEFAULT_BURST_SIZE	99
+#define DEFAULT_RING_SIZE	129
+#define DEFAULT_QUEUE_NUMBER	333
+	struct rte_eth_dev_portconf portconfig = {
+		.burst_size = DEFAULT_BURST_SIZE,
+		.ring_size = DEFAULT_RING_SIZE,
+		.nb_queues = DEFAULT_QUEUE_NUMBER,
+	};
+	dev_info->default_rxportconf = portconfig;
+	dev_info->default_txportconf = portconfig;
+
+#define DEFAULT_RX_FREE_THRESH	48
+	dev_info->default_rxconf = (struct rte_eth_rxconf) {
+		.rx_free_thresh = DEFAULT_RX_FREE_THRESH,
+	};
+
+#define DEFAULT_TX_FREE_THRESH	54
+	dev_info->default_txconf = (struct rte_eth_txconf) {
+		.tx_free_thresh = DEFAULT_TX_FREE_THRESH,
+	};
+
+	dev_info->max_rx_queues = DEFAULT_QUEUE_NUMBER + 1;
+	dev_info->max_tx_queues = DEFAULT_QUEUE_NUMBER + 1;
+
+	return 0;
+}
+
+static int
+info_get_offload_jumbo(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+	dev_info->max_rx_pktlen = MAX_RX_PKTLEN;
+
+	dev_info->max_rx_queues = (uint16_t)128;
+	dev_info->max_tx_queues = (uint16_t)512;
+
+	dev_info->rx_offload_capa = DEV_RX_OFFLOAD_JUMBO_FRAME;
+
+	return 0;
+}
+
+static int
+info_get_min_max_mtu(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+	dev_info->max_rx_pktlen = MAX_RX_PKTLEN;
+
+	dev_info->max_rx_queues = (uint16_t)128;
+	dev_info->max_tx_queues = (uint16_t)512;
+
+	dev_info->rx_offload_capa = DEV_RX_OFFLOAD_JUMBO_FRAME;
+
+	dev_info->min_mtu = RTE_ETHER_MIN_MTU;
+	dev_info->max_mtu = MAX_RX_PKTLEN - 100;
+
+	return 0;
+}
+
+static int
+info_get_lro(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+	dev_info->max_rx_queues = (uint16_t)128;
+	dev_info->max_tx_queues = (uint16_t)512;
+
+	dev_info->rx_offload_capa = DEV_RX_OFFLOAD_TCP_LRO;
+
+	return 0;
+}
+
+static int
+info_get_lro_pkt_size(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+#define MAX_LRO_PKTLEN (MAX_RX_PKTLEN * 2)
+	dev_info->max_lro_pkt_size = MAX_LRO_PKTLEN;
+
+	dev_info->max_rx_queues = (uint16_t)128;
+	dev_info->max_tx_queues = (uint16_t)512;
+
+	dev_info->rx_offload_capa = DEV_RX_OFFLOAD_TCP_LRO;
+
+	return 0;
+}
+
+static int
+info_get_rss_hash_offload(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+	dev_info->max_rx_queues = (uint16_t)128;
+	dev_info->max_tx_queues = (uint16_t)512;
+
+	dev_info->rx_offload_capa = DEV_RX_OFFLOAD_RSS_HASH;
+
+	return 0;
+}
+
+static int
+ethdev_configure(void)
+{
+	struct eth_dev_ops *local_dev_ops;
+	struct eth_dev_ops backup_dev_ops;
+	struct rte_eth_dev_info dev_info;
+	struct rte_eth_conf dev_conf;
+	uint16_t nb_rx_q = 0;
+	uint16_t nb_tx_q = 0;
+	int ret;
+	int i;
+
+	memset(&dev_conf, 0, sizeof(dev_conf));
+
+	for (i = 0; i < port_number; i++) {
+		/* invalid port id */
+		ret = rte_eth_dev_configure(invalid_port_id, nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Configure accepted invalid port id %u\n",
+			invalid_port_id);
+
+		/* set NULL config */
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q, NULL);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted NULL configuration for port id %u\n",
+			port_id[i]);
+
+		/* no configure dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_configure = NULL;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted NULL configuration for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* no infos_get dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = NULL;
+		ret = rte_eth_dev_info_get(port_id[i], &dev_info);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted NULL info get dev_ops for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* failing dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_configure = configure_fail;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted failing device configuration for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* start before configure */
+		ret = rte_eth_dev_start(port_id[i]);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Started before configure for port id %u\n",
+			port_id[i]);
+
+		/* get device info for various tests below */
+		ret = rte_eth_dev_info_get(port_id[i], &dev_info);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get info for port id %u\n",
+			port_id[i]);
+
+		/* set big Rx queue number */
+		nb_rx_q = RTE_MAX_QUEUES_PER_PORT + 1;
+		nb_tx_q = default_nb_tx_q;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue number > RTE_MAX_QUEUES configuration for port id %u\n",
+			port_id[i]);
+
+		nb_rx_q = dev_info.max_rx_queues + 1;
+		nb_tx_q = default_nb_tx_q;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue number > max_rx_queues configuration for port id %u\n",
+			port_id[i]);
+
+		/* set big Tx queue number */
+		nb_rx_q = default_nb_rx_q;
+		nb_tx_q = RTE_MAX_QUEUES_PER_PORT + 1;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Tx queue number > RTE_MAX_QUEUES configuration for port id %u\n",
+			port_id[i]);
+
+		nb_rx_q = default_nb_rx_q;
+		nb_tx_q = dev_info.max_tx_queues + 1;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Tx queue number > max_tx_queues configuration for port id %u\n",
+			port_id[i]);
+		nb_rx_q = default_nb_rx_q;
+		nb_tx_q = default_nb_tx_q;
+
+		/* request default queue number only for Rx or Tx */
+		nb_rx_q = default_nb_rx_q;
+		nb_tx_q = 0;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted only Tx default queue number for port id %u\n",
+			port_id[i]);
+
+		nb_rx_q = 0;
+		nb_tx_q = default_nb_tx_q;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted only Rx default queue number for port id %u\n",
+			port_id[i]);
+		nb_rx_q = default_nb_rx_q;
+		nb_tx_q = default_nb_tx_q;
+
+		/* request not supported LSC */
+		dev_conf.intr_conf.lsc = 1;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted LSC interrupt config port id %u\n",
+			port_id[i]);
+		dev_conf.intr_conf.lsc = 0;
+
+		/* request not supported RMV */
+		dev_conf.intr_conf.rmv = 1;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted LSC interrupt config port id %u\n",
+			port_id[i]);
+		dev_conf.intr_conf.rmv = 0;
+
+		/* configure device */
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure port id %u\n",
+			port_id[i]);
+
+		/* configure after start */
+		ret = rte_eth_dev_start(port_id[i]);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to start port id %u\n",
+			port_id[i]);
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Configuring an already started port id %u\n",
+			port_id[i]);
+		ret = rte_eth_dev_stop(port_id[i]);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to stop port id %u\n",
+			port_id[i]);
+
+		/* requested supported device features */
+		virtual_ethdev_set_dev_flags(port_id[i],
+			RTE_ETH_DEV_INTR_LSC | RTE_ETH_DEV_INTR_RMV);
+		dev_conf.intr_conf.lsc = 1;
+		dev_conf.intr_conf.rmv = 1;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_SUCCESS(ret,
+			"Failed to configure with device flags for port id %u\n",
+			port_id[i]);
+		dev_conf.intr_conf.lsc = 0;
+		dev_conf.intr_conf.rmv = 0;
+
+		/* Use default Rx/Tx queue numbers */
+		nb_rx_q = 0;
+		nb_tx_q = 0;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure port id %u\n",
+			port_id[i]);
+		ret = rte_eth_dev_info_get(port_id[i], &dev_info);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get info for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(dev_info.nb_rx_queues,
+				RTE_ETH_DEV_FALLBACK_RX_NBQUEUES,
+			"Default Rx queue number is wrong for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(dev_info.nb_tx_queues,
+				RTE_ETH_DEV_FALLBACK_TX_NBQUEUES,
+			"Default Tx queue number is wrong for port id %u\n",
+			port_id[i]);
+		nb_rx_q = default_nb_rx_q;
+		nb_tx_q = default_nb_tx_q;
+
+		/* Use PMD provided Rx/Tx queue numbers */
+		nb_rx_q = 0;
+		nb_tx_q = 0;
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_default_config;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure port id %u\n",
+			port_id[i]);
+		ret = rte_eth_dev_info_get(port_id[i], &dev_info);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get info for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(dev_info.nb_rx_queues, DEFAULT_QUEUE_NUMBER,
+			"Default driver Rx queue number is wrong for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(dev_info.nb_tx_queues, DEFAULT_QUEUE_NUMBER,
+			"Default driver Tx queue number is wrong for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+		nb_rx_q = default_nb_rx_q;
+		nb_tx_q = default_nb_tx_q;
+
+		/* check max_rx_pkt_len without jumbo frame support */
+		uint16_t overhead_len;
+		struct rte_eth_dev *eth_dev = &rte_eth_devices[port_id[i]];
+		overhead_len = RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN;
+		dev_conf.rxmode.max_rx_pkt_len = RTE_ETHER_MTU + overhead_len + 1;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_NOT_EQUAL(eth_dev->data->dev_conf.rxmode.max_rx_pkt_len,
+				dev_conf.rxmode.max_rx_pkt_len,
+			"Accepted Rx packet length bigger than max MTU for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(eth_dev->data->dev_conf.rxmode.max_rx_pkt_len,
+				(uint32_t)(RTE_ETHER_MTU + overhead_len),
+			"Max Rx packet length calculated wrong for port id %u\n",
+			port_id[i]);
+		dev_conf.rxmode.max_rx_pkt_len = 0;
+
+		dev_conf.rxmode.max_rx_pkt_len = RTE_ETHER_MIN_MTU + overhead_len - 1;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_NOT_EQUAL(eth_dev->data->dev_conf.rxmode.max_rx_pkt_len,
+				dev_conf.rxmode.max_rx_pkt_len,
+			"Accepted Rx packet length less than min MTU for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(eth_dev->data->dev_conf.rxmode.max_rx_pkt_len,
+				(uint32_t)(RTE_ETHER_MTU + overhead_len),
+			"Max Rx packet length calculated wrong for port id %u\n",
+			port_id[i]);
+		dev_conf.rxmode.max_rx_pkt_len = 0;
+
+		/* check max_rx_pkt_len with jumbo frame support */
+		dev_conf.rxmode.max_rx_pkt_len = MAX_RX_PKTLEN + 1;
+		dev_conf.rxmode.offloads = DEV_RX_OFFLOAD_JUMBO_FRAME;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted Rx packet length bigger than supported by device for port id %u\n",
+			port_id[i]);
+		dev_conf.rxmode.max_rx_pkt_len = 0;
+		dev_conf.rxmode.offloads = 0;
+
+		dev_conf.rxmode.max_rx_pkt_len = RTE_ETHER_MIN_LEN - 1;
+		dev_conf.rxmode.offloads = DEV_RX_OFFLOAD_JUMBO_FRAME;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted Rx packet length less than min MTU for port id %u\n",
+			port_id[i]);
+		dev_conf.rxmode.max_rx_pkt_len = 0;
+		dev_conf.rxmode.offloads = 0;
+
+		uint16_t mtu;
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_offload_jumbo;
+		dev_conf.rxmode.max_rx_pkt_len = MAX_RX_PKTLEN;
+		dev_conf.rxmode.offloads = DEV_RX_OFFLOAD_JUMBO_FRAME;
+		overhead_len = RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure port id %u\n",
+			port_id[i]);
+		ret = rte_eth_dev_get_mtu(port_id[i], &mtu);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get MTU for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(dev_conf.rxmode.max_rx_pkt_len - overhead_len,
+				mtu,
+			"MTU calculated wrong on configure for port id %u\n",
+			port_id[i]);
+		dev_conf.rxmode.max_rx_pkt_len = 0;
+		dev_conf.rxmode.offloads = 0;
+		*local_dev_ops = backup_dev_ops;
+
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_offload_jumbo;
+		dev_conf.rxmode.max_rx_pkt_len = MAX_RX_PKTLEN;
+		dev_conf.rxmode.offloads = DEV_RX_OFFLOAD_JUMBO_FRAME;
+		overhead_len = RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure port id %u\n",
+			port_id[i]);
+		ret = rte_eth_dev_get_mtu(port_id[i], &mtu);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get MTU for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(dev_conf.rxmode.max_rx_pkt_len - overhead_len,
+				mtu,
+			"MTU calculated wrong on configure for port id %u\n",
+			port_id[i]);
+		dev_conf.rxmode.max_rx_pkt_len = 0;
+		dev_conf.rxmode.offloads = 0;
+		*local_dev_ops = backup_dev_ops;
+
+		/* max_rx_pkt_len with jumbo frame with min/max MTU */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_min_max_mtu;
+		dev_conf.rxmode.max_rx_pkt_len = MAX_RX_PKTLEN;
+		dev_conf.rxmode.offloads = DEV_RX_OFFLOAD_JUMBO_FRAME;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure port id %u\n",
+			port_id[i]);
+		ret = rte_eth_dev_info_get(port_id[i], &dev_info);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get info for port id %u\n",
+			port_id[i]);
+		overhead_len = dev_info.max_rx_pktlen - dev_info.max_mtu;
+		ret = rte_eth_dev_get_mtu(port_id[i], &mtu);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get MTU for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(dev_conf.rxmode.max_rx_pkt_len - overhead_len,
+				mtu,
+			"MTU calculated wrong on configure for port id %u\n",
+			port_id[i]);
+		dev_conf.rxmode.max_rx_pkt_len = 0;
+		dev_conf.rxmode.offloads = 0;
+		*local_dev_ops = backup_dev_ops;
+
+		/* LRO */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_lro;
+		dev_conf.rxmode.offloads = DEV_RX_OFFLOAD_TCP_LRO;
+		dev_conf.rxmode.max_lro_pkt_size = MAX_RX_PKTLEN * 2;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted different LRO packet size when driver limit is missing for port id %u\n",
+			port_id[i]);
+		dev_conf.rxmode.offloads = 0;
+		dev_conf.rxmode.max_lro_pkt_size = 0;
+		*local_dev_ops = backup_dev_ops;
+
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_lro_pkt_size;
+		dev_conf.rxmode.offloads = DEV_RX_OFFLOAD_TCP_LRO;
+		dev_conf.rxmode.max_lro_pkt_size = MAX_LRO_PKTLEN + 1;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted LRO packet size bigger than what device supports for port id %u\n",
+			port_id[i]);
+		dev_conf.rxmode.offloads = 0;
+		dev_conf.rxmode.max_lro_pkt_size = 0;
+		*local_dev_ops = backup_dev_ops;
+
+		/* offloads */
+		dev_conf.rxmode.offloads = DEV_RX_OFFLOAD_JUMBO_FRAME;
+		dev_conf.rxmode.max_rx_pkt_len = MAX_RX_PKTLEN;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted offload that is not in the capability for port id %u\n",
+			port_id[i]);
+		dev_conf.rxmode.max_rx_pkt_len = 0;
+		dev_conf.rxmode.offloads = 0;
+
+		/* RSS hash function */
+		dev_conf.rx_adv_conf.rss_conf.rss_hf = ETH_RSS_ETH;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted RSS hash function that is not in the capability for port id %u\n",
+			port_id[i]);
+		dev_conf.rx_adv_conf.rss_conf.rss_hf = 0;
+
+		/* RSS hash offload */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_rss_hash_offload;
+		dev_conf.rxmode.offloads = DEV_RX_OFFLOAD_RSS_HASH;
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted RSS hash offload without RSS for port id %u\n",
+			port_id[i]);
+		dev_conf.rxmode.offloads = 0;
+		*local_dev_ops = backup_dev_ops;
+
+		/* start after failed configure */
+		ret = rte_eth_dev_start(port_id[i]);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Started after failed configure for port id %u\n",
+			port_id[i]);
+
+		/* Need successful configure for start */
+		ret = rte_eth_dev_configure(port_id[i], nb_rx_q, nb_tx_q,
+			&dev_conf);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure port id %u\n",
+			port_id[i]);
+	}
+
+	// rss_hf src_only and dst_only
+	// eth_dev_tx_queue_config
+	// eth_dev_rx_queue_config
+	// RTE_ETHDEV_PROFILE_WITH_VTUNE
+	// eth_dev_validate_offloads
+	// restore config
+	// restore mtu
+
+	return TEST_SUCCESS;
+}
+
+
+static const char *virtual_ethdev_driver_name = "Virtual PMD";
+static int
+info_get_success(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+
+	dev_info->driver_name = virtual_ethdev_driver_name;
+	dev_info->max_mac_addrs = 1;
+
+	dev_info->max_rx_pktlen = MAX_RX_PKTLEN;
+
+	dev_info->max_rx_queues = (uint16_t)128;
+	dev_info->max_tx_queues = (uint16_t)512;
+
+	dev_info->min_rx_bufsize = 0;
+
+	return 0;
+}
+
+static int
+info_get_fail(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info __rte_unused)
+{
+	return -1;
+}
+
+static int
+info_get_max_queues(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+	dev_info->max_rx_queues = RTE_MAX_QUEUES_PER_PORT + 1;
+	dev_info->max_tx_queues = RTE_MAX_QUEUES_PER_PORT + 1;
+
+	return 0;
+}
+
+static int
+info_get_mtu(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+#define MIN_MTU	256
+#define MAX_MTU 512
+	dev_info->min_mtu = MIN_MTU;
+	dev_info->max_mtu = MAX_MTU;
+
+	return 0;
+}
+
+static int
+ethdev_info_get(void)
+{
+	struct eth_dev_ops *local_dev_ops;
+	struct eth_dev_ops backup_dev_ops;
+	struct rte_eth_dev_info dev_info;
+	int ret;
+	int i;
+
+	for (i = 0; i < port_number; i++) {
+		/* invalid port id */
+		ret = rte_eth_dev_info_get(invalid_port_id, &dev_info);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Getting info accepted invalid port id %u\n",
+			invalid_port_id);
+
+		/* NULL info */
+		ret = rte_eth_dev_info_get(port_id[i], NULL);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted NULL info struct for port id %u\n",
+			port_id[i]);
+
+		/* no infos_get dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = NULL;
+		ret = rte_eth_dev_info_get(port_id[i], &dev_info);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted NULL info get dev_ops for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* failing dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_fail;
+		ret = rte_eth_dev_info_get(port_id[i], &dev_info);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted failing device info get for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* get info */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_success;
+		ret = rte_eth_dev_info_get(port_id[i], &dev_info);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get info for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* big max queues number */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_max_queues;
+		ret = rte_eth_dev_info_get(port_id[i], &dev_info);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get info for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_NOT_EQUAL(dev_info.nb_rx_queues, RTE_MAX_QUEUES_PER_PORT + 1,
+			"Accepted big Rx queue number for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_NOT_EQUAL(dev_info.nb_tx_queues, RTE_MAX_QUEUES_PER_PORT + 1,
+			"Accepted big Tx queue number for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* min/max MTU */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_mtu;
+		ret = rte_eth_dev_info_get(port_id[i], &dev_info);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get info for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(dev_info.min_mtu, MIN_MTU,
+			"Received min MTU is wrong for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(dev_info.max_mtu, MAX_MTU,
+			"Received max MTU is wrong for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* verify dev_flags */
+#define DEV_FLAG 0xABCD
+		uint32_t local_dev_flag = DEV_FLAG;
+		virtual_ethdev_set_dev_flags(port_id[i], local_dev_flag);
+		ret = rte_eth_dev_info_get(port_id[i], &dev_info);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get info for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(*dev_info.dev_flags, local_dev_flag,
+			"Received device flags is wrong for port id %u\n",
+			port_id[i]);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+info_get_multi_rx_segment(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+	dev_info->rx_seg_capa.max_nseg = 3;
+	dev_info->rx_queue_offload_capa = RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT;
+	dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT;
+	dev_info->rx_seg_capa.multi_pools = 0;
+
+	return 0;
+}
+
+static int
+info_get_large_min_rx_bufsize(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+	dev_info->min_rx_bufsize = RTE_MBUF_DEFAULT_BUF_SIZE;
+
+	return 0;
+}
+
+static int
+info_get_runtime_rx_setup(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+	dev_info->dev_capa = RTE_ETH_DEV_CAPA_RUNTIME_RX_QUEUE_SETUP;
+
+	return 0;
+}
+
+static int
+ethdev_rx_queue_setup(void)
+{
+	struct rte_eth_rxconf rx_conf;
+	uint16_t rx_queue_id = 0;
+	uint16_t nb_rx_desc = 256;
+	unsigned int socket_id = SOCKET_ID_ANY;
+	struct rte_mempool *mp = NULL;
+	struct rte_mempool *first_seg_mp = NULL;
+	struct rte_mempool *second_seg_mp = NULL;
+	struct eth_dev_ops *local_dev_ops;
+	struct eth_dev_ops backup_dev_ops;
+	struct rte_eth_dev_info dev_info;
+	int ret;
+	int i;
+
+	memset(&rx_conf, 0, sizeof(struct rte_eth_rxconf));
+	mp = rte_pktmbuf_pool_create("test_ethdev", 128, 0, 0,
+			RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+
+	for (i = 0; i < port_number; i++) {
+		/* invalid port id */
+		ret = rte_eth_rx_queue_setup(invalid_port_id, rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted invalid port id %u\n",
+			invalid_port_id);
+
+		/* invalid queue id */
+		rx_queue_id = default_nb_rx_q;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted invalid port id %u\n",
+			port_id[i]);
+		rx_queue_id = 0;
+
+		/* no rx_queue_setup dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->rx_queue_setup = NULL;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted NULL Rx queue setup dev_ops for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* no infos_get dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = NULL;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted NULL info get dev_ops for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* failing infos_get dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_fail;
+		ret = rte_eth_dev_info_get(port_id[i], &dev_info);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted failing device info get for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* null mp with null rx_conf */
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, NULL, NULL);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted null mempool with null config for port id %u\n",
+			port_id[i]);
+
+		/* null mp with null rx_seg */
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, NULL);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted null mempool with null rx_seg for port id %u\n",
+			port_id[i]);
+
+		union rte_eth_rxseg rxseg[2];
+		memset(&rxseg, 0, sizeof(union rte_eth_rxseg) * 2);
+		/* null mp with zero rx_nseg */
+		rx_conf.rx_seg = &rxseg[0];
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, NULL);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted null mempool with zero rx_nseg for port id %u\n",
+			port_id[i]);
+		rx_conf.rx_seg = NULL;
+
+		/* null mp without RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT offload */
+		rx_conf.rx_seg = &rxseg[0];
+		rx_conf.rx_nseg = 1;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, NULL);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted null mempool without split offload for port id %u\n",
+			port_id[i]);
+		rx_conf.rx_seg = NULL;
+		rx_conf.rx_nseg = 0;
+
+		/* null mp with rx_nseg > seg_capa->max_nseg */
+		rx_conf.rx_seg = &rxseg[0];
+		rx_conf.rx_nseg = 1;
+		rx_conf.offloads |= RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, NULL);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted null mempool with segment "
+			"number bigger than supported for port id %u\n",
+			port_id[i]);
+		rx_conf.rx_seg = NULL;
+		rx_conf.rx_nseg = 0;
+		rx_conf.offloads = 0;
+
+		/* null mp with null segment mp */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_multi_rx_segment;
+		rx_conf.rx_seg = &rxseg[0];
+		rx_conf.rx_nseg = 1;
+		rx_conf.offloads |= RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, NULL);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted null mempool with null segment mempool for port id %u\n",
+			port_id[i]);
+		rx_conf.rx_seg = NULL;
+		rx_conf.rx_nseg = 0;
+		rx_conf.offloads = 0;
+		*local_dev_ops = backup_dev_ops;
+
+		/* null mp with segment mp are different when not supported */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_multi_rx_segment;
+		first_seg_mp = rte_pktmbuf_pool_create("test_ethdev1", 128, 0, 0,
+				RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+		second_seg_mp = rte_pktmbuf_pool_create("test_ethdev2", 128, 0, 0,
+				RTE_MBUF_DEFAULT_BUF_SIZE - 512, rte_socket_id());
+		rx_conf.rx_seg = rxseg;
+		rxseg[0].split.mp = first_seg_mp;
+		rxseg[0].split.length = 512;
+		//TODO: when rxseg.split.length is 0, API fails, check it
+		rxseg[1].split.mp = second_seg_mp;
+		rxseg[1].split.length = 512;
+		rx_conf.rx_nseg = 2;
+		rx_conf.offloads |= RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, NULL);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted null mempool with segment mp "
+			"are different when not supported for port id %u\n",
+			port_id[i]);
+		rx_conf.rx_seg = NULL;
+		rx_conf.rx_nseg = 0;
+		rx_conf.offloads = 0;
+		*local_dev_ops = backup_dev_ops;
+		memset(&rxseg, 0, sizeof(union rte_eth_rxseg) * 2);
+		rte_mempool_free(first_seg_mp);
+		first_seg_mp = NULL;
+		rte_mempool_free(second_seg_mp);
+		second_seg_mp = NULL;
+
+		//TODO: Add more segment Rx tests based on other capabilities
+
+		/* mp with non zero Rx segment number */
+		rx_conf.rx_seg = &rxseg[0];
+		rx_conf.rx_nseg = 1;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted mempool with non zero rx_nseg for port id %u\n",
+			port_id[i]);
+		rx_conf.rx_seg = NULL;
+		rx_conf.rx_nseg = 0;
+
+		/* mp with buffer size < min buffer size */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_large_min_rx_bufsize;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted mempool with buffer size < min_rx_bufsize for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* Use driver provided default Rx descriptor number */
+		struct rte_eth_rxq_info rxq_info;
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_default_config;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				0, socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_SUCCESS(ret,
+			"Rx queue setup failed for port id %u\n",
+			port_id[i]);
+		ret = rte_eth_rx_queue_info_get(port_id[i], rx_queue_id, &rxq_info);
+		RTE_TEST_ASSERT_SUCCESS(ret,
+			"Rx queue info get failed for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(rxq_info.nb_desc, DEFAULT_RING_SIZE,
+			"Not using default Rx desc number on Rx queue setup for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* Use library default Rx descriptor number */
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				0, socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_SUCCESS(ret,
+			"Rx queue setup failed for port id %u\n",
+			port_id[i]);
+		ret = rte_eth_rx_queue_info_get(port_id[i], rx_queue_id, &rxq_info);
+		RTE_TEST_ASSERT_SUCCESS(ret,
+			"Rx queue info get failed for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(rxq_info.nb_desc, RTE_ETH_DEV_FALLBACK_RX_RINGSIZE,
+			"Not using default Rx desc number on Rx queue setup for port id %u\n",
+			port_id[i]);
+
+		/* Rx descriptor less than min supported by driver */
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				VIRTUAL_ETHDEV_MIN_DESC_NUM - 1,
+				socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted Rx descriptor less than supported for port id %u\n",
+			port_id[i]);
+
+		/* Rx descriptor more than max supported by driver */
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				VIRTUAL_ETHDEV_MAX_DESC_NUM + 1,
+				socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted Rx descriptor more than supported for port id %u\n",
+			port_id[i]);
+
+		/* Rx descriptor number unaligned */
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc + VIRTUAL_ETHDEV_DESC_ALIGN + 1,
+				socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted unaligned number of Rx descriptor for port id %u\n",
+			port_id[i]);
+
+		/* Setup after port start */
+		ret = rte_eth_dev_start(port_id[i]);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to start port id %u\n",
+			port_id[i]);
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted after start for port id %u\n",
+			port_id[i]);
+		ret = rte_eth_dev_stop(port_id[i]);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to stop port id %u\n",
+			port_id[i]);
+
+		/* Setup with runtime setup capability but queue is not stopped */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_runtime_rx_setup;
+		ret = rte_eth_dev_start(port_id[i]);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to start port id %u\n",
+			port_id[i]);
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup with runtime setup capability accepted "
+			"started queue for port id %u\n",
+			port_id[i]);
+		ret = rte_eth_dev_stop(port_id[i]);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to stop port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* Setup Rx queue when queue is already allocated but there is
+		 * no rx_queue_release dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->rx_queue_release = NULL;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Rx queue setup accepted null queue release dev_ops when "
+			"queue is already allocated for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* Use driver provided Rx configuration */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_infos_get = info_get_default_config;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, NULL, mp);
+		RTE_TEST_ASSERT_SUCCESS(ret,
+			"Rx queue setup failed for port id %u\n",
+			port_id[i]);
+		ret = rte_eth_rx_queue_info_get(port_id[i], rx_queue_id, &rxq_info);
+		RTE_TEST_ASSERT_SUCCESS(ret,
+			"Rx queue info get failed for port id %u\n",
+			port_id[i]);
+		TEST_ASSERT_EQUAL(rxq_info.conf.rx_free_thresh, DEFAULT_RX_FREE_THRESH,
+			"Not using default Rx config on Rx queue setup for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* Request unsupported Rx queue offload */
+		/* expectation is there is no Rx queue specific offload
+		 * capability reported for device and following offload not
+		 * enabled in the port configure */
+		rx_conf.offloads = DEV_RX_OFFLOAD_VLAN_STRIP;
+		ret = rte_eth_rx_queue_setup(port_id[i], rx_queue_id,
+				nb_rx_desc, socket_id, &rx_conf, mp);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted unsupported Rx queue offload for port id %u\n",
+			port_id[i]);
+		rx_conf.offloads = 0;
+
+		//TODO: LRO
+
+		/* Check min Rx buffer size */
+		struct rte_eth_dev_data *data;
+		uint32_t size = RTE_MBUF_DEFAULT_BUF_SIZE;
+		for (int j = 0; j < default_nb_rx_q; j++) {
+			size -= 256;
+			first_seg_mp = rte_pktmbuf_pool_create("test_ethdev1",
+					128, 0, 0, size, rte_socket_id());
+			ret = rte_eth_rx_queue_setup(port_id[i], j, nb_rx_desc,
+					socket_id, &rx_conf, first_seg_mp);
+			RTE_TEST_ASSERT_SUCCESS(ret,
+				"Rx queue setup failed for port id %u\n",
+				port_id[i]);
+			rte_mempool_free(first_seg_mp);
+			first_seg_mp = NULL;
+		}
+		virtual_ethdev_get_dev_data(port_id[i], &data);
+		TEST_ASSERT_EQUAL(data->min_rx_buf_size, size,
+			"Rx queue setup set minimum Rx buffer size wrong for port id %u\n",
+			port_id[i]);
+		data = NULL;
+		size = 0;
+	}
+
+	rte_mempool_free(mp);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ethdev_tx_queue_setup(void)
+{
+	return TEST_SUCCESS;
+}
+
+static int
+start_fail(struct rte_eth_dev *dev __rte_unused)
+{
+	return -1;
+}
+
+static int
+ethdev_start(void)
+{
+	struct eth_dev_ops *local_dev_ops;
+	struct eth_dev_ops backup_dev_ops;
+	int ret;
+	int i;
+
+	for (i = 0; i < port_number; i++) {
+		/* invalid port id */
+		ret = rte_eth_dev_start(invalid_port_id);
+		RTE_TEST_ASSERT_FAIL(ret, "Start accepted invalid port id %u\n",
+			invalid_port_id);
+
+		/* no dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_start = NULL;
+		ret = rte_eth_dev_start(port_id[i]);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted NULL start dev_ops for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* failing dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_start = start_fail;
+		ret = rte_eth_dev_start(port_id[i]);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted failing device start for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		ret = rte_eth_dev_start(port_id[i]);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to start port id %u\n",
+			port_id[i]);
+
+		ret = rte_eth_dev_start(port_id[i]);
+		RTE_TEST_ASSERT_SUCCESS(ret,
+			"Failed to start already started port id %u\n",
+			port_id[i]);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+stop_fail(struct rte_eth_dev *dev __rte_unused)
+{
+	return -1;
+}
+
+static int
+ethdev_stop(void)
+{
+	struct eth_dev_ops *local_dev_ops;
+	struct eth_dev_ops backup_dev_ops;
+	int ret;
+	int i;
+
+	for (i = 0; i < port_number; i++) {
+		/* invalid port id */
+		ret = rte_eth_dev_stop(invalid_port_id);
+		RTE_TEST_ASSERT_FAIL(ret, "Stop accepted invalid port id %u\n",
+			invalid_port_id);
+
+		/* no dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_stop = NULL;
+		ret = rte_eth_dev_stop(port_id[i]);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted NULL stop dev_ops for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		/* failing dev_ops */
+		local_dev_ops = dev_ops[i];
+		backup_dev_ops = *local_dev_ops;
+		local_dev_ops->dev_stop = stop_fail;
+		ret = rte_eth_dev_stop(port_id[i]);
+		RTE_TEST_ASSERT_FAIL(ret,
+			"Accepted failing device stop for port id %u\n",
+			port_id[i]);
+		*local_dev_ops = backup_dev_ops;
+
+		ret = rte_eth_dev_stop(port_id[i]);
+		RTE_TEST_ASSERT_SUCCESS(ret, "Failed to stop port id %u\n",
+			port_id[i]);
+
+		ret = rte_eth_dev_stop(port_id[i]);
+		RTE_TEST_ASSERT_SUCCESS(ret,
+			"Failed to stop already stopped port id %u\n",
+			port_id[i]);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+ethdev_rx_queue_info_get(void)
+{
+	return TEST_SUCCESS;
+}
+
+static int
+ethdev_tx_queue_info_get(void)
+{
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite ethdev_api_testsuite = {
+	.suite_name = "ethdev API unit test suite",
+	.setup = ethdev_api_setup,
+	.teardown = ethdev_api_teardown,
+	.unit_test_cases = {
+		TEST_CASE(ethdev_count_avail),
+		TEST_CASE(ethdev_owner_get),
+		TEST_CASE(ethdev_owner_new),
+		TEST_CASE(ethdev_owner_set),
+		TEST_CASE(ethdev_count_total),
+		TEST_CASE(ethdev_owner_unset),
+		TEST_CASE(ethdev_owner_delete),
+		TEST_CASE(ethdev_configure),
+		TEST_CASE(ethdev_info_get),
+		TEST_CASE(ethdev_rx_queue_setup),
+		TEST_CASE(ethdev_tx_queue_setup),
+		TEST_CASE(ethdev_rx_queue_info_get),
+		TEST_CASE(ethdev_tx_queue_info_get),
+		TEST_CASE(ethdev_start),
+		TEST_CASE(ethdev_stop),
+		TEST_CASES_END(),
+	},
+};
+
+static int
+test_ethdev_api(void)
+{
+	return unit_test_suite_runner(&ethdev_api_testsuite);
+}
+
+REGISTER_TEST_COMMAND(ethdev_api_autotest, test_ethdev_api);
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 40e474aa7e7f..926bf96b719f 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -31,7 +31,11 @@ struct rte_hairpin_peer_info;
  */
 
 typedef int  (*eth_dev_configure_t)(struct rte_eth_dev *dev);
-/**< @internal Ethernet device configuration. */
+/**< @internal Ethernet device configuration.
+ *
+ * For ``rte_eth_dev_configure()`` API both ``eth_dev_configure_t`` and
+ * ``eth_dev_infos_get_t`` needs to be implemented by PMD.
+ * */
 
 typedef int  (*eth_dev_start_t)(struct rte_eth_dev *dev);
 /**< @internal Function used to start a configured Ethernet device. */
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index 9d95cd11e1b5..972d332e94d2 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -694,6 +694,7 @@ eth_dev_owner_set(const uint16_t port_id, const uint64_t old_owner_id,
 	}
 
 	/* can not truncate (same structure) */
+	memset(port_owner->name, 0, RTE_ETH_MAX_OWNER_NAME_LEN);
 	strlcpy(port_owner->name, new_owner->name, RTE_ETH_MAX_OWNER_NAME_LEN);
 
 	port_owner->id = new_owner->id;
@@ -748,10 +749,13 @@ rte_eth_dev_owner_delete(const uint64_t owner_id)
 	rte_spinlock_lock(&eth_dev_shared_data->ownership_lock);
 
 	if (eth_is_valid_owner_id(owner_id)) {
-		for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
-			if (rte_eth_devices[port_id].data->owner.id == owner_id)
-				memset(&rte_eth_devices[port_id].data->owner, 0,
+		for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) {
+			struct rte_eth_dev_data *data =
+				rte_eth_devices[port_id].data;
+			if (data != NULL && data->owner.id == owner_id)
+				memset(&data->owner, 0,
 				       sizeof(struct rte_eth_dev_owner));
+		}
 		RTE_ETHDEV_LOG(NOTICE,
 			"All port owners owned by %016"PRIx64" identifier have removed\n",
 			owner_id);
@@ -1394,6 +1398,15 @@ rte_eth_dev_configure(uint16_t port_id, uint16_t nb_rx_q, uint16_t nb_tx_q,
 	 * If driver does not provide any preferred valued, fall back on
 	 * EAL defaults.
 	 */
+	if ((nb_rx_q & nb_tx_q) == 0 && (nb_rx_q | nb_tx_q) != 0) {
+		RTE_ETHDEV_LOG(ERR,
+			"Ethdev port_id (%u), Rx queue number (%u) and Tx queue number (%u) "
+			"should be both zero or both non-zero\n",
+			port_id, nb_rx_q, nb_tx_q);
+		ret = -EINVAL;
+		goto rollback;
+	}
+
 	if (nb_rx_q == 0 && nb_tx_q == 0) {
 		nb_rx_q = dev_info.default_rxportconf.nb_queues;
 		if (nb_rx_q == 0)
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index d2b27c351fdb..6ab818b59f66 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -1837,6 +1837,10 @@ struct rte_eth_dev_owner {
 	char name[RTE_ETH_MAX_OWNER_NAME_LEN]; /**< The owner name. */
 };
 
+/**
+ * Device flags set on ``eth_dev->data->dev_flags`` by drivers.
+ * These values can be received via ``rte_eth_dev_info_get()``
+ */
 /** PMD supports thread-safe flow operations */
 #define RTE_ETH_DEV_FLOW_OPS_THREAD_SAFE  0x0001
 /** Device supports link state interrupt */
@@ -1980,6 +1984,10 @@ int rte_eth_dev_owner_new(uint64_t *owner_id);
  *
  * Set an Ethernet device owner.
  *
+ * Once an owner is set for an Ethernet device, setting owner again will fail,
+ * even it is exact same owner.
+ * Owner ids not obtained by ``rte_eth_dev_owner_new()`` are rejected.
+ *
  * @param	port_id
  *  The identifier of the port to own.
  * @param	owner
@@ -2212,6 +2220,8 @@ rte_eth_dev_is_removed(uint16_t port_id);
  *   - -ENOMEM: Unable to allocate the receive ring descriptors or to
  *      allocate network memory buffers from the memory pool when
  *      initializing receive descriptors.
+ *   - -ENOTSUP: The function or ``rte_eth_dev_info_get()`` is not supported by
+ *      driver.
  */
 int rte_eth_rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
 		uint16_t nb_rx_desc, unsigned int socket_id,
@@ -2524,6 +2534,8 @@ int rte_eth_dev_tx_queue_stop(uint16_t port_id, uint16_t tx_queue_id);
  * On success, all basic functions exported by the Ethernet API (link status,
  * receive/transmit, and so on) can be invoked.
  *
+ * Starting an already started port returns success.
+ *
  * @param port_id
  *   The port identifier of the Ethernet device.
  * @return
@@ -2536,6 +2548,8 @@ int rte_eth_dev_start(uint16_t port_id);
  * Stop an Ethernet device. The device can be restarted with a call to
  * rte_eth_dev_start()
  *
+ * Stopping an already stopped port returns success.
+ *
  * @param port_id
  *   The port identifier of the Ethernet device.
  * @return
@@ -3036,7 +3050,7 @@ int rte_eth_macaddr_get(uint16_t port_id, struct rte_ether_addr *mac_addr);
  * min_mtu = RTE_ETHER_MIN_MTU
  * max_mtu = UINT16_MAX
  *
- * The following fields will be populated if support for dev_infos_get()
+ *ops The following fields will be populated if support for dev_infos_get()
  * exists for the device and the rte_eth_dev 'dev' has been populated
  * successfully with a call to it:
  *
diff --git a/lib/ethdev/rte_ethdev_core.h b/lib/ethdev/rte_ethdev_core.h
index edf96de2dc2e..291b70a8cfc4 100644
--- a/lib/ethdev/rte_ethdev_core.h
+++ b/lib/ethdev/rte_ethdev_core.h
@@ -176,7 +176,7 @@ struct rte_eth_dev_data {
 		/**< Queues state: HAIRPIN(2) / STARTED(1) / STOPPED(0). */
 	uint8_t tx_queue_state[RTE_MAX_QUEUES_PER_PORT];
 		/**< Queues state: HAIRPIN(2) / STARTED(1) / STOPPED(0). */
-	uint32_t dev_flags;             /**< Capabilities. */
+	uint32_t dev_flags;		/**< Device flags */
 	int numa_node;                  /**< NUMA node connection. */
 	struct rte_vlan_filter_conf vlan_filter_conf;
 			/**< VLAN filter configuration. */
