All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ferruh Yigit <ferruh.yigit@intel.com>
To: dev@dpdk.org, Thomas Monjalon <thomas@monjalon.net>,
	Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
Cc: Ferruh Yigit <ferruh.yigit@intel.com>
Subject: [dpdk-dev] [RFC 4/4] test: support ethdev
Date: Thu, 17 Jun 2021 09:14:49 +0100	[thread overview]
Message-ID: <20210617081449.2045195-4-ferruh.yigit@intel.com> (raw)
In-Reply-To: <20210617081449.2045195-1-ferruh.yigit@intel.com>

Added unit test for ethdev APIs, this unit test 'ethdev_api_autotest'
can run without physical device. If there are physical devices probed,
they will be ignored by the unit test.

A few issues fixed or some clarification added in the ehtdev library
with in this unit test patch.

Signed-off-by: Ferruh Yigit <ferruh.yigit@intel.com>
---
Notes:
* 'rte_eth_dev_owner_unset()' error message is misleading:
  "Cannot set owner to port 1 already owned by ..."
  Unset API error message is about setting.

* 'rte_eth_dev_owner_delete()' crashes, fixed here but it seems it is
  not used at all

* 'rte_eth_dev_configure()' is too complex, there still much more things
  to test in that API.

* Is there a way to get start/stop status of a port, should we add a new
  API, 'rte_eth_dev_is_started()', ?

* Need a way to get bus from ethdev. Current API requires "rte_device"
  which is internal information from ethdev perspective.

* Clarification added that PMD should implement 'dev_infos_get' for
  'rte_eth_dev_configure()' support.

* Tried to clarify dev_flags with more comments

* In configure, for default config, having only Rx or Tx queue number
  pass the test but it should fail, adding more checks to
  'rte_eth_dev_configure()' for it.

* Do we need a way to get device 'dev_conf.rxmode.max_rx_pkt_len' value?
---
 app/test/meson.build         |    1 +
 app/test/test.c              |    1 +
 app/test/test_ethdev.c       | 1160 ++++++++++++++++++++++++++++++++++
 lib/ethdev/ethdev_driver.h   |    6 +-
 lib/ethdev/rte_ethdev.c      |   19 +-
 lib/ethdev/rte_ethdev.h      |   14 +-
 lib/ethdev/rte_ethdev_core.h |    2 +-
 7 files changed, 1197 insertions(+), 6 deletions(-)
 create mode 100644 app/test/test_ethdev.c

diff --git a/app/test/meson.build b/app/test/meson.build
index 08c82d3d23a0..c55a7ac82bd8 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -41,6 +41,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',
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..69a2eaede1c3
--- /dev/null
+++ b/app/test/test_ethdev.c
@@ -0,0 +1,1160 @@
+/* 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;
+
+#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_BURST_SIZE,
+		.nb_queues = DEFAULT_QUEUE_NUMBER,
+	};
+	dev_info->default_rxportconf = portconfig;
+	dev_info->default_txportconf = portconfig;
+
+	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;
+
+		/* 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]);
+
+		/* 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 = 1;
+		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 = 1;
+		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 = 1;
+		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 = 1;
+		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 = 1;
+		nb_tx_q = 1;
+
+		/* request default queue number only for Rx or Tx */
+		nb_rx_q = 1;
+		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 = 1;
+		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 = 1;
+		nb_tx_q = 1;
+
+		/* 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]);
+
+		/* 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]);
+
+		/* 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 = 1;
+		nb_tx_q = 1;
+
+		/* 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;
+
+	}
+
+	// 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
+ethdev_rx_queue_setup(void)
+{
+	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 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_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 170a4e22a7c1..26e247f160d4 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 c607eabb5b0c..8e6e632dec9c 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);
@@ -1387,6 +1391,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 faf3bd901d75..a6ab64abf1df 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
@@ -2524,6 +2532,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 +2546,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 +3048,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 4679d948fa5e..43ab76760691 100644
--- a/lib/ethdev/rte_ethdev_core.h
+++ b/lib/ethdev/rte_ethdev_core.h
@@ -172,7 +172,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. */
-- 
2.31.1


  parent reply	other threads:[~2021-06-17  8:15 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-06-17  8:14 [dpdk-dev] [RFC 1/4] test/virtual_pmd: enable getting device operations Ferruh Yigit
2021-06-17  8:14 ` [dpdk-dev] [RFC 2/4] test/virtual_pmd: clean rings on close Ferruh Yigit
2021-06-17  8:14 ` [dpdk-dev] [RFC 3/4] test/virtual_pmd: enable updating device flags Ferruh Yigit
2021-06-17  8:14 ` Ferruh Yigit [this message]
2021-07-16 14:27 ` [dpdk-dev] [RFC v2 1/8] test/virtual_pmd: clean rings on close Ferruh Yigit
2021-07-16 14:27   ` [dpdk-dev] [RFC v2 2/8] test/virtual_pmd: enable getting device operations Ferruh Yigit
2023-08-22 21:10     ` Stephen Hemminger
2021-07-16 14:27   ` [dpdk-dev] [RFC v2 3/8] test/virtual_pmd: enable updating device flags Ferruh Yigit
2021-07-16 14:27   ` [dpdk-dev] [RFC v2 4/8] test/virtual_pmd: enable getting device data Ferruh Yigit
2021-07-16 14:27   ` [dpdk-dev] [RFC v2 5/8] test/virtual_pmd: support get queue info device ops Ferruh Yigit
2021-07-16 14:27   ` [dpdk-dev] [RFC v2 6/8] test/virtual_pmd: provide descriptor limit info Ferruh Yigit
2021-07-16 14:27   ` [dpdk-dev] [RFC v2 7/8] test/virtual_pmd: support queue start/stop Ferruh Yigit
2023-08-22 21:11     ` Stephen Hemminger
2021-07-16 14:28   ` [dpdk-dev] [RFC v2 8/8] test: support ethdev Ferruh Yigit
2023-08-22 21:14     ` Stephen Hemminger
2023-08-22 21:15   ` [dpdk-dev] [RFC v2 1/8] test/virtual_pmd: clean rings on close Stephen Hemminger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210617081449.2045195-4-ferruh.yigit@intel.com \
    --to=ferruh.yigit@intel.com \
    --cc=andrew.rybchenko@oktetlabs.ru \
    --cc=dev@dpdk.org \
    --cc=thomas@monjalon.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.