DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver
@ 2020-12-18  7:47 Chenbo Xia
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 1/8] lib: introduce emudev library Chenbo Xia
                   ` (9 more replies)
  0 siblings, 10 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-18  7:47 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This series introduces a new device abstraction called emudev for emulated
devices. A new library (librte_emudev) is implemented. The first emudev
driver is also introduced, which emulates Intel Adaptive Virtual Function
(iavf) as a software network device.

This series has a dependency on librte_vfio_user patch series:
http://patchwork.dpdk.org/cover/85389/

Background & Motivation 
-----------------------
The disaggregated/multi-process QEMU is using VFIO-over-socket/vfio-user
as the main transport mechanism to disaggregate IO services from QEMU.
Therefore, librte_vfio_user is introduced in DPDK to accommodate
emulated devices for high performance I/O. Although vfio-user library
provides possibility of emulating devices in DPDK, DPDK does not have
a device abstraction for emulated devices. A good device abstraction will
be useful for applications or high performance data path driver. With
this consideration, emudev library is designed and implemented. It also
make it possbile to keep modular design on emulated devices by implementing
data path related logic in a standalone driver (e.g., an ethdev driver)
and keeps the unrelated logic in the emudev driver.

Design overview
---------------

                    +---------------------------------------+
                    |   +---------------+    +-----------+  |
                    |   |  iavf_emudev  |<-->| data path |  |
                    |   |    driver     |    |   driver  |  |
                    |   +---------------+    +-----------+  |
                    |           |                           |
                    | --------------------------- VDEV BUS  |
                    |           |                           |
                    |   +---------------+                   |
+--------------+    |   | vdev:         |                   |
| +----------+ |    |   | /path/to/vfio |                   |
| | Generic  | |    |   +---------------+                   |
| | vfio-dev | |    |           |                           |
| +----------+ |    |           |                           |
| +----------+ |    |      +----------+                     |
| | vfio-user| |    |      | vfio-user|                     |
| | client   | |<---|----->| server   |                     |
| +----------+ |    |      +----------+                     |
| QEMU/DPDK    |    | DPDK                                  |
+--------------+    +---------------------------------------+

- Generic vfio-dev/vfio-user client/vfio-user server
  Above concepts are all introduced in librte_vfio_user patch series:
  http://patchwork.dpdk.org/cover/85389/

- vdev:/path/to/vfio.
  It binds to vdev bus driver. The vdev device is defined by DPDK applications
  through command line as '--vdev=emu_iavf, path=/path/to/socket' in iavf_emudev
  case. Parameters in command line include device name (emu_iavf) which is used
  to identify corresponding driver (in this case, iavf_emudev driver),
  path=/path/to/socket which is used to open the transport interface to vfio-user
  client in QEMU/DPDK.

- data path driver.
  The data path handling is splited to another standalone driver for modular
  design.


Chenbo Xia (8):
  lib: introduce emudev library
  doc: add emudev library guide
  emu: introduce emulated iavf driver
  emu/iavf: add vfio-user device register and unregister
  emu/iavf: add resource management and internal logic of iavf
  emu/iavf: add emudev operations to fit in emudev framework
  test/emudev: introduce functional test
  doc: update release notes for iavf emudev driver

 MAINTAINERS                            |   12 +
 app/test/meson.build                   |    5 +-
 app/test/test_emudev.c                 |   29 +
 doc/guides/prog_guide/emudev.rst       |  122 +++
 doc/guides/prog_guide/index.rst        |    1 +
 doc/guides/rel_notes/release_21_02.rst |   16 +
 drivers/emu/iavf/iavf_emu.c            |  250 ++++++
 drivers/emu/iavf/iavf_emu_internal.h   |   69 ++
 drivers/emu/iavf/iavf_emu_test.c       |  174 ++++
 drivers/emu/iavf/iavf_emudev.c         |  237 ++++++
 drivers/emu/iavf/iavf_vfio_user.c      | 1053 ++++++++++++++++++++++++
 drivers/emu/iavf/iavf_vfio_user.h      |   57 ++
 drivers/emu/iavf/meson.build           |   17 +
 drivers/emu/iavf/rte_iavf_emu.h        |  119 +++
 drivers/emu/iavf/version.map           |    3 +
 drivers/emu/meson.build                |    6 +
 drivers/meson.build                    |    1 +
 lib/librte_emudev/meson.build          |    5 +
 lib/librte_emudev/rte_emudev.c         |  486 +++++++++++
 lib/librte_emudev/rte_emudev.h         |  410 +++++++++
 lib/librte_emudev/rte_emudev_vdev.h    |   53 ++
 lib/librte_emudev/version.map          |   27 +
 lib/meson.build                        |    2 +-
 23 files changed, 3152 insertions(+), 2 deletions(-)
 create mode 100644 app/test/test_emudev.c
 create mode 100644 doc/guides/prog_guide/emudev.rst
 create mode 100644 drivers/emu/iavf/iavf_emu.c
 create mode 100644 drivers/emu/iavf/iavf_emu_internal.h
 create mode 100644 drivers/emu/iavf/iavf_emu_test.c
 create mode 100644 drivers/emu/iavf/iavf_emudev.c
 create mode 100644 drivers/emu/iavf/iavf_vfio_user.c
 create mode 100644 drivers/emu/iavf/iavf_vfio_user.h
 create mode 100644 drivers/emu/iavf/meson.build
 create mode 100644 drivers/emu/iavf/rte_iavf_emu.h
 create mode 100644 drivers/emu/iavf/version.map
 create mode 100644 drivers/emu/meson.build
 create mode 100644 lib/librte_emudev/meson.build
 create mode 100644 lib/librte_emudev/rte_emudev.c
 create mode 100644 lib/librte_emudev/rte_emudev.h
 create mode 100644 lib/librte_emudev/rte_emudev_vdev.h
 create mode 100644 lib/librte_emudev/version.map

-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH 1/8] lib: introduce emudev library
  2020-12-18  7:47 [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver Chenbo Xia
@ 2020-12-18  7:47 ` Chenbo Xia
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 2/8] doc: add emudev library guide Chenbo Xia
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-18  7:47 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This patch introduces the emudev library. Emudev library is used
to abstract an emulated device, whose type could be general
(e.g., network, crypto and etc.). Several device-level APIs are
implemented to use or manipulate the device. It can be attached
to another data path driver (e.g., ethdev driver) to plug in its
high performance data path.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
Signed-off-by: Miao Li <miao.li@intel.com>
---
 MAINTAINERS                         |   5 +
 lib/librte_emudev/meson.build       |   5 +
 lib/librte_emudev/rte_emudev.c      | 486 ++++++++++++++++++++++++++++
 lib/librte_emudev/rte_emudev.h      | 410 +++++++++++++++++++++++
 lib/librte_emudev/rte_emudev_vdev.h |  53 +++
 lib/librte_emudev/version.map       |  27 ++
 lib/meson.build                     |   2 +-
 7 files changed, 987 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_emudev/meson.build
 create mode 100644 lib/librte_emudev/rte_emudev.c
 create mode 100644 lib/librte_emudev/rte_emudev.h
 create mode 100644 lib/librte_emudev/rte_emudev_vdev.h
 create mode 100644 lib/librte_emudev/version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index 5fb4880758..1b395e181d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1545,6 +1545,11 @@ M: Chenbo Xia <chenbo.xia@intel.com>
 M: Xiuchun Lu <xiuchun.lu@intel.com>
 F: lib/librte_vfio_user/
 
+Emudev - EXPERIMENTAL
+M: Chenbo Xia <chenbo.xia@intel.com>
+M: Xiuchun Lu <xiuchun.lu@intel.com>
+F: lib/librte_emudev/
+
 Test Applications
 -----------------
 
diff --git a/lib/librte_emudev/meson.build b/lib/librte_emudev/meson.build
new file mode 100644
index 0000000000..4e16cecbaf
--- /dev/null
+++ b/lib/librte_emudev/meson.build
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation
+
+sources = files('rte_emudev.c')
+headers = files('rte_emudev.h', 'rte_emudev_vdev.h')
diff --git a/lib/librte_emudev/rte_emudev.c b/lib/librte_emudev/rte_emudev.c
new file mode 100644
index 0000000000..2bbf3970d8
--- /dev/null
+++ b/lib/librte_emudev/rte_emudev.c
@@ -0,0 +1,486 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <string.h>
+
+#include <rte_malloc.h>
+#include <rte_memcpy.h>
+
+#include "rte_emudev.h"
+
+#define RTE_MAX_EMU_DEV 1024
+struct rte_emudev rte_emu_devices[RTE_MAX_EMU_DEV];
+
+static struct rte_emudev_global emu_dev_globals = {
+	.nb_devs = 0
+};
+
+static inline uint16_t rte_emu_alloc_dev_id(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < RTE_MAX_EMU_DEV; i++) {
+		if (rte_emu_devices[i].name[0] == '\0')
+			return i;
+	}
+	return RTE_MAX_EMU_DEV;
+}
+
+uint8_t
+rte_emudev_count(void)
+{
+	return emu_dev_globals.nb_devs;
+}
+
+int
+rte_emudev_get_dev_id(const char *name)
+{
+	uint16_t i;
+
+	if (!name) {
+		RTE_EMUDEV_LOG(ERR, "Failed to get device ID: "
+			"NULL device name\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < emu_dev_globals.nb_devs; i++)
+		if (!strncmp(rte_emu_devices[i].name, name,
+			RTE_EMU_NAME_MAX_LEN))
+			return i;
+
+	return -ENODEV;
+}
+
+struct rte_emudev *
+rte_emudev_allocate(const char *name)
+{
+	uint16_t dev_id;
+	struct rte_emudev *emu_dev = NULL;
+	size_t name_len;
+
+	if (!name) {
+		RTE_EMUDEV_LOG(ERR, "Failed to allocate emudev: "
+			"NULL device name\n");
+		return NULL;
+	}
+
+	name_len = strnlen(name, RTE_EMU_NAME_MAX_LEN);
+	if (!name_len) {
+		RTE_EMUDEV_LOG(ERR, "Emulated device name has zero length\n");
+		return NULL;
+	}
+
+	if (name_len >= RTE_EMU_NAME_MAX_LEN) {
+		RTE_EMUDEV_LOG(ERR, "Emulated device name too long\n");
+		return NULL;
+	}
+
+	if (rte_emudev_allocated(name) != NULL) {
+		RTE_EMUDEV_LOG(ERR,
+			"Emulated device with name %s already exists\n",
+			name);
+		return NULL;
+	}
+
+	dev_id = rte_emu_alloc_dev_id();
+	if (dev_id == RTE_MAX_EMU_DEV) {
+		RTE_EMUDEV_LOG(ERR, "Reached max number of Emulated device\n");
+		return NULL;
+	}
+
+	emu_dev = &rte_emu_devices[dev_id];
+	strncpy(emu_dev->name, name, sizeof(emu_dev->name));
+	emu_dev->dev_id = dev_id;
+	emu_dev_globals.nb_devs++;
+
+	return emu_dev;
+}
+
+int
+rte_emudev_release(struct rte_emudev *dev)
+{
+	if (!dev)
+		return -EINVAL;
+
+	if (dev->priv_data) {
+		rte_free(dev->priv_data);
+		dev->priv_data = NULL;
+	}
+
+	memset(dev, 0, sizeof(*dev));
+	emu_dev_globals.nb_devs--;
+	return 0;
+}
+
+struct rte_emudev *
+rte_emudev_allocated(const char *name)
+{
+	unsigned int i;
+
+	if (!name) {
+		RTE_EMUDEV_LOG(ERR, "Failed to find emudev: "
+			"NULL device name\n");
+		return NULL;
+	}
+
+	for (i = 0; i < RTE_MAX_EMU_DEV; i++) {
+		if (rte_emu_devices[i].dev_ops != NULL &&
+		    strcmp(rte_emu_devices[i].device->name, name) == 0)
+			return &rte_emu_devices[i];
+	}
+	return NULL;
+}
+
+int rte_emudev_is_valid_id(uint16_t dev_id)
+{
+	if (dev_id >= RTE_MAX_EMU_DEV ||
+		rte_emu_devices[dev_id].name[0] == '\0')
+		return 0;
+	else
+		return 1;
+}
+
+int
+rte_emudev_selftest(uint16_t dev_id)
+{
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	struct rte_emudev *dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_selftest, -ENOTSUP);
+
+	return (*dev->dev_ops->dev_selftest)(dev_id);
+}
+
+
+int rte_emudev_start(uint16_t dev_id)
+{
+	struct rte_emudev *dev;
+	int ret;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (dev->started) {
+		RTE_EMUDEV_LOG(ERR, "Device %u already started\n", dev_id);
+		return 0;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_start, -ENOTSUP);
+
+	ret = (*dev->dev_ops->dev_start)(dev);
+	if (ret)
+		return ret;
+
+	dev->started = 1;
+	return 0;
+}
+
+void rte_emudev_stop(uint16_t dev_id)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID(dev_id);
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (!dev->started) {
+		RTE_EMUDEV_LOG(ERR, "Device %u already stopped\n", dev_id);
+		return;
+	}
+
+	RTE_FUNC_PTR_OR_RET(*dev->dev_ops->dev_stop);
+
+	(*dev->dev_ops->dev_stop)(dev);
+
+	dev->started = 0;
+}
+
+int rte_emudev_configure(uint16_t dev_id, struct rte_emudev_info *dev_conf)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (!dev_conf)
+		return -EINVAL;
+
+	if (dev->started) {
+		RTE_EMUDEV_LOG(ERR, "Device %u must be stopped "
+			"before configure\n", dev_id);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_configure, -ENOTSUP);
+
+	if (strcmp(dev_conf->dev_type, dev->dev_info.dev_type)) {
+		RTE_EMUDEV_LOG(ERR, "Wrong device type to configure"
+			" for device %u\n", dev_id);
+		return -EINVAL;
+	}
+
+	return (*dev->dev_ops->dev_configure)(dev, dev_conf);
+}
+
+int rte_emudev_close(uint16_t dev_id)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (dev->started) {
+		RTE_EMUDEV_LOG(ERR, "Device %u must be stopped "
+			"before close\n", dev_id);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_close, -ENOTSUP);
+
+	(*dev->dev_ops->dev_close)(dev);
+
+	rte_emudev_release(dev);
+	return 0;
+}
+
+int rte_emudev_subscribe_event(uint16_t dev_id,
+		const rte_emudev_event_chnl_t ev_chnl)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!ev_chnl) {
+		RTE_EMUDEV_LOG(ERR, "Failed to subscribe because of NULL"
+			" event channel for device %u\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->subscribe_event, -ENOTSUP);
+
+	return (*dev->dev_ops->subscribe_event)(dev, ev_chnl);
+}
+
+int rte_emudev_unsubscribe_event(uint16_t dev_id,
+		const rte_emudev_event_chnl_t ev_chnl)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!ev_chnl) {
+		RTE_EMUDEV_LOG(ERR, "Failed to unsubscribe because of NULL"
+			" event channel for device %u\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->unsubscribe_event, -ENOTSUP);
+
+	return (*dev->dev_ops->unsubscribe_event)(dev, ev_chnl);
+}
+
+int rte_emudev_get_dev_info(uint16_t dev_id, struct rte_emudev_info *info)
+{
+	struct rte_emudev *dev;
+	struct rte_emudev_info *dev_info;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!info) {
+		RTE_EMUDEV_LOG(ERR, "NULL device info for device %u\n",
+			dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+	dev_info = &dev->dev_info;
+
+	strcpy(info->dev_type, dev_info->dev_type);
+	info->max_qp_num = dev_info->max_qp_num;
+	info->region_num = dev_info->region_num;
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_info_get, -ENOTSUP);
+
+	return (*dev->dev_ops->dev_info_get)(dev, info->dev_priv);
+}
+
+int rte_emudev_get_mem_table(uint16_t dev_id, rte_emudev_mem_table_t *tb)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!tb) {
+		RTE_EMUDEV_LOG(ERR, "NULL memory table for device %u\n",
+			dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_mem_table, -ENOTSUP);
+
+	return (*dev->dev_ops->get_mem_table)(dev, tb);
+}
+
+int rte_emudev_get_queue_info(uint16_t dev_id, uint32_t queue,
+	struct rte_emudev_q_info *info)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!info) {
+		RTE_EMUDEV_LOG(ERR, "NULL queue info for queue %d"
+			" of device %u\n", queue, dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (queue >= dev->dev_info.max_qp_num * 2) {
+		RTE_EMUDEV_LOG(ERR, "Queue index of device %u exceeds max\n",
+			dev_id);
+		return -EINVAL;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_queue_info, -ENOTSUP);
+
+	memset(info, 0, sizeof(*info));
+
+	return (*dev->dev_ops->get_queue_info)(dev, queue, info);
+}
+
+int rte_emudev_get_irq_info(uint16_t dev_id, uint32_t vector,
+	struct rte_emudev_irq_info *info)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!info) {
+		RTE_EMUDEV_LOG(ERR, "NULL irq info for vector %u"
+			" of device %u\n", vector, dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_irq_info, -ENOTSUP);
+
+	memset(info, 0, sizeof(*info));
+
+	return (*dev->dev_ops->get_irq_info)(dev, vector, info);
+}
+
+int rte_emudev_get_db_info(uint16_t dev_id, uint32_t doorbell,
+	struct rte_emudev_db_info *info)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!info) {
+		RTE_EMUDEV_LOG(ERR, "NULL doorbell info of device %u"
+			" for id %u\n", dev_id, doorbell);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_db_info, -ENOTSUP);
+
+	memset(info, 0, sizeof(*info));
+
+	return (*dev->dev_ops->get_db_info)(dev, doorbell, info);
+}
+
+int rte_emudev_set_attr(uint16_t dev_id, const char *attr_name,
+	rte_emudev_attr_t attr)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!attr_name) {
+		RTE_EMUDEV_LOG(ERR, "NULL attribute name of device %u "
+			"for set\n", dev_id);
+		return -EINVAL;
+	}
+
+	if (!attr) {
+		RTE_EMUDEV_LOG(ERR, "NULL attribute of device %u "
+			"for set_attr\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->set_attr, -ENOTSUP);
+
+	return (*dev->dev_ops->set_attr)(dev, attr_name, attr);
+}
+
+int rte_emudev_get_attr(uint16_t dev_id, const char *attr_name,
+	rte_emudev_attr_t attr)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!attr_name) {
+		RTE_EMUDEV_LOG(ERR, "NULL attribute name of device %u "
+			"for get\n", dev_id);
+		return -EINVAL;
+	}
+
+	if (!attr) {
+		RTE_EMUDEV_LOG(ERR, "NULL attribute of device %u "
+			"for get_attr\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_attr, -ENOTSUP);
+
+	return (*dev->dev_ops->get_attr)(dev, attr_name, attr);
+}
+
+int rte_emudev_region_map(uint16_t dev_id, uint32_t index,
+	uint64_t *region_size, uint64_t *base_addr)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!region_size || !base_addr)
+		return -EINVAL;
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (index >= dev->dev_info.region_num) {
+		RTE_EMUDEV_LOG(ERR, "Wrong region index for device %u\n",
+			dev_id);
+		return -EINVAL;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->region_map, -ENOTSUP);
+
+	memset(region_size, 0, sizeof(*region_size));
+	memset(base_addr, 0, sizeof(*base_addr));
+
+	return (*dev->dev_ops->region_map)(dev, index, region_size,
+		base_addr);
+}
+
+RTE_LOG_REGISTER(rte_emudev_logtype, lib.emudev, INFO);
diff --git a/lib/librte_emudev/rte_emudev.h b/lib/librte_emudev/rte_emudev.h
new file mode 100644
index 0000000000..df31c631b7
--- /dev/null
+++ b/lib/librte_emudev/rte_emudev.h
@@ -0,0 +1,410 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _RTE_EMUDEV_H_
+#define _RTE_EMUDEV_H_
+
+#include <rte_config.h>
+#include <rte_dev.h>
+#include <rte_compat.h>
+
+#define RTE_EMU_NAME_MAX_LEN RTE_DEV_NAME_MAX_LEN
+
+extern int rte_emudev_logtype;
+
+#define RTE_EMUDEV_LOG(level, ...) \
+	rte_log(RTE_LOG_ ## level, rte_emudev_logtype, "" __VA_ARGS__)
+
+/* Macros to check for valid dev id */
+#define RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id) do { \
+	if (!rte_emudev_is_valid_id(dev_id)) { \
+		RTE_EMUDEV_LOG(ERR, "Invalid dev_id=%u\n", dev_id); \
+		return -ENODEV; \
+	} \
+} while (0)
+
+#define RTE_EMU_CHECK_VALID_DEVID(dev_id) do { \
+	if (!rte_emudev_is_valid_id(dev_id)) { \
+		RTE_EMUDEV_LOG(ERR, "Invalid dev_id=%u\n", dev_id); \
+		return; \
+	} \
+} while (0)
+
+typedef void *rte_emudev_obj_t;
+typedef void *rte_emudev_attr_t;
+typedef void *rte_emudev_mem_table_t;
+typedef void *rte_emudev_event_chnl_t;
+
+struct rte_emudev;
+
+/** 
+ * Global structure used for maintaining state
+ * of allocated emu devices
+ */
+struct rte_emudev_global {
+	uint8_t nb_devs;	/**< Number of devices found */
+};
+
+struct rte_emudev_info {
+	char dev_type[RTE_EMU_NAME_MAX_LEN];
+	uint32_t region_num;
+	uint32_t max_qp_num;
+	rte_emudev_obj_t dev_priv;
+};
+
+struct rte_emudev_q_info {
+	uint64_t base;
+	uint64_t size;
+	int doorbell_id;
+	int irq_vector;
+};
+
+struct rte_emudev_irq_info {
+	uint32_t vector;
+	bool enable;
+	int eventfd;
+};
+
+struct rte_emudev_db_info {
+	uint32_t id;
+	uint32_t flag;
+#define RTE_EMUDEV_DB_FD	(0x1 << 0)
+#define RTE_EMUDEV_DB_MEM	(0x1 << 1)
+	union {
+		int eventfd;
+		struct {
+			uint64_t base;
+			uint64_t size;
+		} mem;
+	} data;
+};
+
+struct rte_emudev_ops {
+	int (*dev_start)(struct rte_emudev *dev);
+	void (*dev_stop)(struct rte_emudev *dev);
+	int (*dev_configure)(struct rte_emudev *dev,
+		struct rte_emudev_info *conf);
+	int (*dev_close)(struct rte_emudev *dev);
+	int (*dev_info_get)(struct rte_emudev *dev, rte_emudev_obj_t info);
+	int (*subscribe_event)(struct rte_emudev *dev,
+		rte_emudev_event_chnl_t ev_chnl);
+	int (*unsubscribe_event)(struct rte_emudev *dev,
+		rte_emudev_event_chnl_t ev_chnl);
+	int (*get_mem_table)(struct rte_emudev *dev,
+		rte_emudev_mem_table_t *tb);
+	int (*get_queue_info)(struct rte_emudev *dev, uint32_t queue,
+		struct rte_emudev_q_info *info);
+	int (*get_irq_info)(struct rte_emudev *dev, uint32_t vector,
+		struct rte_emudev_irq_info *info);
+	int (*get_db_info)(struct rte_emudev *dev, uint32_t doorbell,
+		struct rte_emudev_db_info *info);
+	int (*get_attr)(struct rte_emudev *dev, const char *attr_name,
+		rte_emudev_attr_t attr);
+	int (*set_attr)(struct rte_emudev *dev, const char *attr_name,
+		rte_emudev_attr_t attr);
+	int (*region_map)(struct rte_emudev *dev, uint32_t index,
+		uint64_t *region_size, uint64_t *base_addr);
+	int (*dev_selftest)(uint16_t dev_id);
+};
+
+struct rte_emudev {
+	char name[RTE_EMU_NAME_MAX_LEN];
+	uint16_t dev_id;
+	int numa_node;
+	int started;
+	struct rte_device *device;
+	struct rte_emudev_info dev_info;
+	const struct rte_emudev_ops *dev_ops;
+	void *priv_data;
+	void *backend_priv;
+} __rte_cache_aligned;
+
+/**
+ * Note that 'rte_emudev_allocate', 'rte_emudev_release' and
+ * 'rte_emudev_allocated' should be called by emulated device
+ * provider.
+ */
+
+/**
+ * Allocate a new emudev for an emulation device and returns the pointer
+ * to the emudev.
+ *
+ * @param name
+ *   Name of the emudev
+ * @return
+ *   Pointer to rte_emudev on success, NULL on failure
+ */
+__rte_experimental
+struct rte_emudev *rte_emudev_allocate(const char *name);
+
+/**
+ * Release the emudev.
+ *
+ * @param dev
+ *   The emulated device
+ * @return
+ *   - 0: Success, device release
+ *   - <0: Failure on release
+ */
+__rte_experimental
+int rte_emudev_release(struct rte_emudev *dev);
+
+/**
+ * Find an emudev using name.
+ *
+ * @param name
+ *   Name of the emudev
+ * @return
+ *   Pointer to rte_emudev on success, NULL on failure
+ */
+__rte_experimental
+struct rte_emudev *rte_emudev_allocated(const char *name);
+
+/**
+ * Start an emulation device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @return
+ *   - 0: Success, device start
+ *   - <0: Failure on start
+ */
+__rte_experimental
+int rte_emudev_start(uint16_t dev_id);
+
+/**
+ * Stop an emulation device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ */
+__rte_experimental
+void rte_emudev_stop(uint16_t dev_id);
+
+/**
+ * Configure an emulation device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param dev_conf
+ *   Device configure info
+ * @return
+ *   - 0: Success, device configured
+ *   - <0: Failure on configure
+ */
+__rte_experimental
+int rte_emudev_configure(uint16_t dev_id, struct rte_emudev_info *dev_conf);
+
+/**
+ * Close an emulation device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @return
+ *   - 0: Success, device close
+ *   - <0: Failure on close
+ */
+__rte_experimental
+int rte_emudev_close(uint16_t dev_id);
+
+/* Note that below APIs should only be called by back-end (data path) driver */
+
+/**
+ * Back-end driver subscribes events of the emulated device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param ev_chnl
+ *   Event channel that events should be passed to
+ * @return
+ *   - 0: Success, event subscribed
+ *   - <0: Failure on subscribe
+ */
+__rte_experimental
+int rte_emudev_subscribe_event(uint16_t dev_id,
+		const rte_emudev_event_chnl_t ev_chnl);
+
+/**
+ * Back-end driver unsubscribes events of the emulated device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param set
+ *   Event channel that events should be passed to
+ * @return
+ *   - 0: Success, event unsubscribed
+ *   - <0: Failure on unsubscribe
+ */
+__rte_experimental
+int rte_emudev_unsubscribe_event(uint16_t dev_id,
+		const rte_emudev_event_chnl_t ev_chnl);
+
+/**
+ * Get the total number of emulated devices that have been
+ * successfully initialised.
+ *
+ * @return
+ *   The total number of usable emudev.
+ */
+__rte_experimental
+uint8_t rte_emudev_count(void);
+
+/**
+ * Get the device identifier for the named emulated device.
+ *
+ * @param name
+ *   Emulated device name to select the device identifier.
+ *
+ * @return
+ *   - 0: Success, emulated device identifier returned
+ *   - <0: Failure on unsubscribe
+ */
+__rte_experimental
+int rte_emudev_get_dev_id(const char *name);
+
+/**
+ * Back-end driver gets the device info of the emulated device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @return
+ *   - 0: Success, emulated device info updated
+ *   - <0: Failure on get device information
+ */
+__rte_experimental
+int rte_emudev_get_dev_info(uint16_t dev_id, struct rte_emudev_info *info);
+
+/**
+ * Get the memory table content and operations of the emulated device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @return
+ *   - 0: Success, memory table of emulated device updated
+ *   - <0: Failure on get memory table
+ */
+__rte_experimental
+int rte_emudev_get_mem_table(uint16_t dev_id, rte_emudev_mem_table_t *tb);
+
+/**
+ * Get queue info of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param queue
+ *   Queue ID of emudev
+ * @return
+ *   - 0: Success, queue information of emulated device updated
+ *   - <0: Failure on get queue information
+ */
+__rte_experimental
+int rte_emudev_get_queue_info(uint16_t dev_id, uint32_t queue,
+	struct rte_emudev_q_info *info);
+
+/**
+ * Get irq info of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param vector
+ *   Interrupt vector
+ * @return
+ *   - 0: Success, irq information of emulated device updated
+ *   - <0: Failure on get irq information
+ */
+__rte_experimental
+int rte_emudev_get_irq_info(uint16_t dev_id, uint32_t vector,
+	struct rte_emudev_irq_info *info);
+
+/**
+ * Get doorbell info of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param doorbell
+ *   Doorbell ID
+ * @return
+ *   - 0: Success, doorbell information of emulated device updated
+ *   - <0: Failure on get doorbell information
+ */
+__rte_experimental
+int rte_emudev_get_db_info(uint16_t dev_id, uint32_t doorbell,
+	struct rte_emudev_db_info *info);
+
+/**
+ * Set attribute of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param attr_name
+ *   Opaque object representing an attribute in implementation.
+ * @param attr
+ *   Pointer to attribute
+ * @return
+ *   - 0: Success, attribute set
+ *   - <0: Failure on attribute set
+ */
+__rte_experimental
+int rte_emudev_set_attr(uint16_t dev_id, const char *attr_name,
+	rte_emudev_attr_t attr);
+
+/**
+ * Get attribute of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param attr_name
+ *   Opaque object representing an attribute in implementation.
+ * @return
+ *   - 0: Success, attribute of emulated device updated
+ *   - <0: Failure on attribute get
+ */
+__rte_experimental
+int rte_emudev_get_attr(uint16_t dev_id, const char *attr_name,
+	rte_emudev_attr_t attr);
+
+/**
+ * Back-end driver maps a region to the emulated device.
+ * Region name identifies the meaning of the region and the emulated
+ * device and the back-end driver should have the same definition of
+ * region name and its meaning.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param index
+ *   Region index
+ * @param attr
+ *   Pointer to attribute
+ * @return
+ *   - 0: Success, region mapped
+ *   - <0: Failure on region map
+ */
+__rte_experimental
+int rte_emudev_region_map(uint16_t dev_id, uint32_t index,
+	uint64_t *region_size, uint64_t *base_addr);
+
+/**
+ * Trigger the emudev self test.
+ *
+ * @param dev_id
+ *   The identifier of the device
+ * @return
+ *   - 0: Selftest successful
+ *   - <0: Failure on selftest
+ */
+__rte_experimental
+int rte_emudev_selftest(uint16_t dev_id);
+
+/**
+ * Check if an emudev device ID is valid.
+ *
+ * @param dev_id
+ *   The identifier of the device
+ * @return
+ *   0 on failure, 1 on success
+ */
+__rte_experimental
+int rte_emudev_is_valid_id(uint16_t dev_id);
+
+#endif /* _RTE_EMUDEV_H_ */
diff --git a/lib/librte_emudev/rte_emudev_vdev.h b/lib/librte_emudev/rte_emudev_vdev.h
new file mode 100644
index 0000000000..85f534b4bd
--- /dev/null
+++ b/lib/librte_emudev/rte_emudev_vdev.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _RTE_EMUDEV_VDEV_H_
+#define _RTE_EMUDEV_VDEV_H_
+
+#include <rte_bus_vdev.h>
+#include <rte_malloc.h>
+
+#include "rte_emudev.h"
+
+/**
+ * @internal
+ * Allocates a new emudev instance for an emulated device and
+ * returns the pointer to that instance for the driver to use.
+ *
+ * @param dev
+ *	Pointer to virtual device
+ *
+ * @param private_data_size
+ *	Size of private data structure
+ *
+ * @return
+ *	A pointer to a rte_emudev or NULL if allocation failed.
+ */
+static inline struct rte_emudev *
+rte_emu_vdev_allocate(struct rte_vdev_device *dev, size_t private_data_size)
+{
+	struct rte_emudev *emu_dev;
+	const char *name = rte_vdev_device_name(dev);
+
+	emu_dev = rte_emudev_allocate(name);
+	if (!emu_dev)
+		return NULL;
+
+	if (private_data_size) {
+		emu_dev->priv_data = rte_zmalloc_socket(name,
+			private_data_size, RTE_CACHE_LINE_SIZE,
+			dev->device.numa_node);
+		if (!emu_dev->priv_data) {
+			rte_emudev_release(emu_dev);
+			return NULL;
+		}
+	}
+
+	emu_dev->device = &dev->device;
+	emu_dev->numa_node = dev->device.numa_node;
+
+	return emu_dev;
+}
+
+#endif /* _RTE_EMUDEV_VDEV_H_ */
diff --git a/lib/librte_emudev/version.map b/lib/librte_emudev/version.map
new file mode 100644
index 0000000000..f800b4c21c
--- /dev/null
+++ b/lib/librte_emudev/version.map
@@ -0,0 +1,27 @@
+EXPERIMENTAL {
+	global:
+
+	rte_emudev_allocate;
+	rte_emudev_release;
+	rte_emudev_allocated;
+	rte_emudev_start;
+	rte_emudev_stop;
+	rte_emudev_configure;
+	rte_emudev_close;
+	rte_emudev_subscribe_event;
+	rte_emudev_unsubscribe_event;
+	rte_emudev_count;
+	rte_emudev_get_dev_id;
+	rte_emudev_get_dev_info;
+	rte_emudev_get_mem_table;
+	rte_emudev_get_queue_info;
+	rte_emudev_get_irq_info;
+	rte_emudev_get_db_info;
+	rte_emudev_set_attr;
+	rte_emudev_get_attr;
+	rte_emudev_region_map;
+	rte_emudev_selftest;
+	rte_emudev_is_valid_id;
+
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index b7fbfcc95b..6dd07fb73e 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -28,7 +28,7 @@ libraries = [
 	'rib', 'reorder', 'sched', 'security', 'stack', 'vhost',
 	# ipsec lib depends on net, crypto and security
 	'ipsec',
-	'vfio_user',
+	'vfio_user', 'emudev',
 	#fib lib depends on rib
 	'fib',
 	# add pkt framework libs which use other libs from above
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH 2/8] doc: add emudev library guide
  2020-12-18  7:47 [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver Chenbo Xia
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 1/8] lib: introduce emudev library Chenbo Xia
@ 2020-12-18  7:47 ` Chenbo Xia
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 3/8] emu: introduce emulated iavf driver Chenbo Xia
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-18  7:47 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

Add emudev library guide and update release notes.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
---
 doc/guides/prog_guide/emudev.rst       | 122 +++++++++++++++++++++++++
 doc/guides/prog_guide/index.rst        |   1 +
 doc/guides/rel_notes/release_21_02.rst |  12 +++
 3 files changed, 135 insertions(+)
 create mode 100644 doc/guides/prog_guide/emudev.rst

diff --git a/doc/guides/prog_guide/emudev.rst b/doc/guides/prog_guide/emudev.rst
new file mode 100644
index 0000000000..91ad520de7
--- /dev/null
+++ b/doc/guides/prog_guide/emudev.rst
@@ -0,0 +1,122 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2020 Intel Corporation.
+
+Emulated Device Library
+=================
+
+Introduction
+------------
+
+The DPDK Emudev library is an abstraction for emulated device. This library
+provides a generic set of APIs for device provider, data path provider and
+applications to use.
+
+A device provider could be implemented as a driver on vdev bus. It should
+expose itself as an emudev for applications to use. It is responsible for the
+device resource management and the device's internal logic. All specifics of a
+device, except data path handling, should be implemented in the device
+provider. The device provider uses emudev APIs mainly for create/destroy an
+emudev instance. The device provider should also use a tranport to communicate
+with device consumer (e.g., virtual machine monitor or container). A potential
+choice could be vfio-user library, which implements the vfio-user protocol for
+emulating devices outside of a virtual machine monitor.
+
+A data path provider could be implemented as any type of driver on vdev bus.
+If the device you want to emulate is a network device, you could implement
+it as an ethdev driver. It is responsible for all data path handling. The data
+path provider uses emudev APIs mainly for getting device-related information
+from the device provider.
+
+Applications uses emudev APIs for device lifecycle management and configuration.
+
+Design
+------------
+
+Some key objects are designed in emudev.
+
+  ``Regions`` are the device layout exposed to the data path provider.
+
+  ``Queues`` are the data path queues that the data path provider needs. Queue
+  information includes queue base address, queue size, queue-related doorbell
+  and interrupt information.
+
+  ``Memory Table`` is the DMA mapping table. The data path provider could use
+  it to perform DMA read/write on device consumer's memory.
+
+Information of above key objects could be acquired through emudev APIs. The
+following will introduce the emudev APIs which are used by data path provider
+and applications. The APIs for device provider to use are allocate/release APIs
+and will not be listed because it's similar to other device abstraction.
+
+There are five categories of APIs:
+
+1. Lifecycle management
+
+* ``rte_emu_dev_start(dev_id)``
+* ``rte_emu_dev_stop(dev_id)``
+* ``rte_emu_dev_configure(dev_id)``
+* ``rte_emu_dev_close(dev_id)``
+
+  Above APIs are respectively for device start/stop/configure/close and mainly
+  for applications to use.
+
+  ``dev_id`` is the emudev device ID.
+
+2. Notification
+
+* ``rte_emu_subscribe_event(dev_id, ev_chnl)``
+* ``rte_emu_unsubscribe_event(dev_id, ev_chnl)``
+
+  Above APIs are for data path provider and applications to register events.
+  The mechanism of event notification could be different in different device
+  providers. A possbile implementation could be event callbacks.
+
+  ``ev_chnl`` is the event channel pointer. The definition varies between
+  different devices.
+
+3. Region-related
+
+* ``rte_emu_region_map(dev_id, index, region_size, base_addr)``
+* ``rte_emu_get_attr(dev_id, attr_name, attr)``
+* ``rte_emu_set_attr(dev_id, attr_name, attr)``
+
+  Above APIs are for data path provider and applications to read/write regions.
+  ``rte_emu_region_map`` is for directly mapping the region and use the mapped
+  address to read/write it. ``rte_emu_get_attr`` and ``rte_emu_set_attr`` are
+  respectively for getting/setting certain attributes in all regions.
+
+  Applications will set attributes or write regions for device configuration.
+
+  In ``rte_emu_region_map``:
+  - ``index`` is the region index.
+  - ``region_size`` is for saving the size of mapped region.
+  - ``base_addr`` is for saving the address of mapped region.
+
+  In ``rte_emu_get_attr`` and ``rte_emu_set_attr``:
+  - ``attr_name`` is the name of attribute. Note that attribute names are aligned
+  between device provider and data path provider for the same device.
+  - ``attr`` is the attribute value.
+
+4. Queue-related
+
+* ``rte_emu_get_queue_info(dev_id, queue, info)``
+* ``rte_emu_get_irq_info(dev_id, irq, info)``
+* ``rte_emu_get_db_info(dev_id, doorbell, info)``
+
+  Above APIs are for data path provider to get queue/interrupt/doorbell information.
+
+  - ``queue``, ``irq`` and ``doorbell`` are respectively the queue/interrupt/doorbell
+  index.
+  - ``info`` is for saving the queue/interrupt/doorbell info.
+
+5. Direct Memory Access
+
+* ``rte_emu_get_mem_table(dev_id, tb)``
+
+  Above APIs are for data path provider to get the information of DMA memory table.
+  The memory table implementation varies between different devices and memory table
+  operations should better be helper functions exposed by device provider. Because
+  address translation make a difference in data path performance, the memory table
+  implementation should have high efficiency.
+
+  ``tb`` is for saving the DMA memory table.
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index f9847b1058..0ed15a0995 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -71,3 +71,4 @@ Programmer's Guide
     profile_app
     glossary
     vfio_user_lib
+    emudev
diff --git a/doc/guides/rel_notes/release_21_02.rst b/doc/guides/rel_notes/release_21_02.rst
index 6fbb6e8c39..3d26b6b580 100644
--- a/doc/guides/rel_notes/release_21_02.rst
+++ b/doc/guides/rel_notes/release_21_02.rst
@@ -67,6 +67,18 @@ New Features
 
   See :doc:`../prog_guide/vfio_user_lib` for more information.
 
+* **Added emudev Library.**
+
+  Added an experimental library ``librte_emudev`` to provide device abstraction
+  for an emulated device.
+
+  The library abstracts an emulated device and provides several categories of
+  device-level APIs. The specific device type could be general (e.g, network,
+  crypto and etc.). It can be attached to another data path driver (e.g, ethdev
+  driver) to leverage the high performance of DPDK data path driver.
+
+  See :doc:`../prog_guide/emudev` for more information.
+
 Removed Items
 -------------
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH 3/8] emu: introduce emulated iavf driver
  2020-12-18  7:47 [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver Chenbo Xia
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 1/8] lib: introduce emudev library Chenbo Xia
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 2/8] doc: add emudev library guide Chenbo Xia
@ 2020-12-18  7:47 ` Chenbo Xia
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 4/8] emu/iavf: add vfio-user device register and unregister Chenbo Xia
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-18  7:47 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This patch introduces emulated iavf driver. It is a vdev driver
emulating all iavf device behavior except data path handling.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
---
 MAINTAINERS                          |   7 +
 drivers/emu/iavf/iavf_emu.c          |  29 ++++
 drivers/emu/iavf/iavf_emu_internal.h |  49 +++++++
 drivers/emu/iavf/iavf_emudev.c       | 207 +++++++++++++++++++++++++++
 drivers/emu/iavf/meson.build         |   8 ++
 drivers/emu/iavf/rte_iavf_emu.h      |  43 ++++++
 drivers/emu/iavf/version.map         |   3 +
 drivers/emu/meson.build              |   6 +
 drivers/meson.build                  |   1 +
 9 files changed, 353 insertions(+)
 create mode 100644 drivers/emu/iavf/iavf_emu.c
 create mode 100644 drivers/emu/iavf/iavf_emu_internal.h
 create mode 100644 drivers/emu/iavf/iavf_emudev.c
 create mode 100644 drivers/emu/iavf/meson.build
 create mode 100644 drivers/emu/iavf/rte_iavf_emu.h
 create mode 100644 drivers/emu/iavf/version.map
 create mode 100644 drivers/emu/meson.build

diff --git a/MAINTAINERS b/MAINTAINERS
index 1b395e181d..bca206ba8f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1275,6 +1275,13 @@ F: doc/guides/rawdevs/ntb.rst
 F: examples/ntb/
 F: doc/guides/sample_app_ug/ntb.rst
 
+Emudev Drivers
+--------------
+
+Intel iavf
+M: Chenbo Xia <chenbo.xia@intel.com>
+M: Xiuchun Lu <xiuchun.lu@intel.com>
+F: drivers/emulation/iavf/
 
 Packet processing
 -----------------
diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
new file mode 100644
index 0000000000..68d2c440e3
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include "iavf_emu_internal.h"
+
+static int iavf_emu_dev_close(struct rte_emudev *dev)
+{
+	struct iavf_emudev *iavf;
+
+	/* For now, we don't support device close when data
+	 * path driver is attached
+	 */
+	if (dev->backend_priv) {
+		EMU_IAVF_LOG(ERR, "Close failed because of "
+			"data path attached\n");
+		return -EPERM;
+	}
+
+	iavf = (struct iavf_emudev *)dev->priv_data;
+	iavf_emu_uninit_device(iavf);
+	dev->priv_data = NULL;
+
+	return 0;
+}
+
+struct rte_emudev_ops emu_iavf_ops = {
+	.dev_close = iavf_emu_dev_close,
+};
diff --git a/drivers/emu/iavf/iavf_emu_internal.h b/drivers/emu/iavf/iavf_emu_internal.h
new file mode 100644
index 0000000000..a726bfe577
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emu_internal.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IAVF_EMU_ITNL_H
+#define _IAVF_EMU_ITNL_H
+
+#include <stdint.h>
+
+#include <rte_log.h>
+
+#include "rte_iavf_emu.h"
+
+extern struct rte_emudev_ops emu_iavf_ops;
+
+extern int emu_iavf_logtype;
+#define EMU_IAVF_LOG(level, ...) \
+	rte_log(RTE_LOG_ ## level, emu_iavf_logtype, "EMU_IAVF: " __VA_ARGS__)
+
+struct iavf_emu_intr_info {
+	int enable;
+	int fd;
+};
+
+struct iavf_emu_intr {
+	uint32_t intr_num;
+	struct iavf_emu_intr_info info[RTE_IAVF_EMU_MAX_INTR];
+};
+
+struct iavf_emu_lanQ {
+	uint16_t db_size;
+	void *doorbell;
+};
+
+struct iavf_emudev {
+	struct rte_emudev *edev;
+	/* Maximum LANQ queue pair that this emulated iavf has */
+	uint16_t max_lanqp;
+	/* Maximum LANQ queue pair number that back-end driver can use */
+	uint16_t max_be_lanqp;
+	unsigned int numa_node;
+	char *sock_addr;
+	struct rte_iavf_emu_mem *mem;
+	struct iavf_emu_intr *intr;
+	struct iavf_emu_lanQ *lanq;
+};
+
+void iavf_emu_uninit_device(struct iavf_emudev *dev);
+#endif
diff --git a/drivers/emu/iavf/iavf_emudev.c b/drivers/emu/iavf/iavf_emudev.c
new file mode 100644
index 0000000000..a4cd2deb06
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emudev.c
@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <rte_kvargs.h>
+#include <rte_emudev.h>
+#include <rte_emudev_vdev.h>
+
+#include "iavf_emu_internal.h"
+
+#define EMU_IAVF_SOCK_ARG "sock"
+#define EMU_IAVF_QUEUES_ARG "queues"
+
+static const char * const emu_iavf_valid_arg[] = {
+	EMU_IAVF_SOCK_ARG,
+	EMU_IAVF_QUEUES_ARG,
+	NULL
+};
+
+static inline int
+save_sockaddr(const char *key __rte_unused, const char *value,
+	void *extra_args)
+{
+	const char **sock_addr = extra_args;
+
+	if (value == NULL)
+		return -1;
+
+	*sock_addr = value;
+
+	return 0;
+}
+
+static inline int
+save_int(const char *key __rte_unused, const char *value, void *extra_args)
+{
+	uint16_t *n = extra_args;
+
+	if (value == NULL || extra_args == NULL)
+		return -EINVAL;
+
+	*n = (uint16_t)strtoul(value, NULL, 0);
+	if (*n == USHRT_MAX && errno == ERANGE)
+		return -1;
+
+	return 0;
+}
+
+static int iavf_emu_init_device(struct iavf_emudev *dev,
+	char *sock_addr, uint16_t queues, unsigned int numa_node)
+{
+	dev->sock_addr = rte_malloc_socket("sock_addr",
+		strlen(sock_addr) + 1, 0, numa_node);
+	if (!dev->sock_addr) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc sock addr\n");
+		goto exit;
+	}
+	strcpy(dev->sock_addr, sock_addr);
+
+	dev->mem = rte_zmalloc_socket("iavf_emu_mem",
+			sizeof(struct rte_iavf_emu_mem),
+			0, numa_node);
+	if (!dev->mem) {
+		EMU_IAVF_LOG(ERR, "Unable to alloc iavf_emu_mem.\n");
+		goto err_mem;
+	}
+
+	dev->intr = rte_zmalloc_socket("iavf_emu_intr",
+			sizeof(struct iavf_emu_intr),
+			0, numa_node);
+	if (!dev->intr) {
+		EMU_IAVF_LOG(ERR, "Unable to alloc iavf_emu_intr.\n");
+		goto err_intr;
+	}
+
+	dev->lanq = rte_zmalloc_socket("iavf_emu_lanQ",
+			sizeof(struct iavf_emu_lanQ) * queues * 2,
+			0, numa_node);
+	if (!dev->lanq) {
+		EMU_IAVF_LOG(ERR, "Unable to alloc iavf_emu_lanQ.\n");
+		goto err_lanq;
+	}
+
+	dev->numa_node = numa_node;
+
+	return 0;
+
+err_lanq:
+	rte_free(dev->lanq);
+err_intr:
+	rte_free(dev->intr);
+err_mem:
+	rte_free(dev->sock_addr);
+exit:
+	return -1;
+}
+
+void iavf_emu_uninit_device(struct iavf_emudev *dev)
+{
+	rte_free(dev->sock_addr);
+	rte_free(dev->mem);
+	rte_free(dev->intr);
+	rte_free(dev->lanq);
+}
+
+static int
+rte_emu_iavf_probe(struct rte_vdev_device *dev)
+{
+	struct rte_kvargs *kvlist = NULL;
+	struct rte_emudev *edev;
+	struct iavf_emudev *iavf;
+	char *sock_addr;
+	uint16_t queues;
+	int ret = 0;
+
+	kvlist = rte_kvargs_parse(rte_vdev_device_args(dev),
+		emu_iavf_valid_arg);
+	if (kvlist == NULL)
+		return -1;
+
+	if (rte_kvargs_count(kvlist, EMU_IAVF_SOCK_ARG) == 1) {
+		ret = rte_kvargs_process(kvlist, EMU_IAVF_SOCK_ARG,
+					 &save_sockaddr, &sock_addr);
+		if (ret < 0)
+			goto err;
+	} else {
+		ret = -1;
+		goto err;
+	}
+
+	if (rte_kvargs_count(kvlist, EMU_IAVF_QUEUES_ARG) == 1) {
+		ret = rte_kvargs_process(kvlist, EMU_IAVF_QUEUES_ARG,
+					 &save_int, &queues);
+		if (ret < 0 || queues > RTE_MAX_QUEUES_PER_PORT ||
+			queues > RTE_IAVF_EMU_MAX_QP_NUM)
+			goto err;
+
+	} else
+		queues = 1;
+
+	if (dev->device.numa_node == SOCKET_ID_ANY)
+		dev->device.numa_node = rte_socket_id();
+
+	edev = rte_emu_vdev_allocate(dev, sizeof(*iavf));
+	if (!edev) {
+		EMU_IAVF_LOG(ERR, "Failed to allocate emu_vdev\n");
+		ret = -1;
+		goto err;
+	}
+	edev->dev_ops = &emu_iavf_ops;
+	edev->dev_info.region_num = RTE_IAVF_EMU_MAPPABLE_REG_NUM;
+	edev->dev_info.max_qp_num = queues + RTE_IAVF_EMU_ADMINQ_NUM / 2;
+
+	strcpy(edev->dev_info.dev_type, RTE_IAVF_EMUDEV_TYPE);
+
+	iavf = (struct iavf_emudev *)edev->priv_data;
+	ret = iavf_emu_init_device(iavf, sock_addr, queues,
+		dev->device.numa_node);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to init new iavf device\n");
+		ret = -1;
+		goto err_ndev;
+	}
+
+	iavf->edev = edev;
+	/* If not configured, we assume back-end driver
+	 * can use all queues of emulated iavf
+	 */
+	iavf->max_be_lanqp = queues;
+	iavf->max_lanqp = queues;
+	edev->priv_data = (void *)iavf;
+
+	edev->started = 1;
+	rte_kvargs_free(kvlist);
+	return 0;
+
+err_ndev:
+	rte_emudev_release(edev);
+err:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
+static int
+rte_emu_iavf_remove(struct rte_vdev_device *dev)
+{
+	struct rte_emudev *emu_dev;
+
+	/* Find the emudev entry */
+	emu_dev = rte_emudev_allocated(rte_vdev_device_name(dev));
+	if (!emu_dev)
+		return 0;
+
+	return rte_emudev_close(emu_dev->dev_id);
+}
+
+static struct rte_vdev_driver emu_iavf_drv = {
+	.probe = rte_emu_iavf_probe,
+	.remove = rte_emu_iavf_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(emu_iavf, emu_iavf_drv);
+RTE_PMD_REGISTER_PARAM_STRING(emu_iavf,
+	"sock=<path> "
+	"queues=<int> ");
+
+RTE_LOG_REGISTER(emu_iavf_logtype, emu.iavf, INFO);
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
new file mode 100644
index 0000000000..58c2a90383
--- /dev/null
+++ b/drivers/emu/iavf/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation
+
+sources = files('iavf_emu.c', 'iavf_emudev.c')
+
+deps += ['bus_vdev', 'emudev']
+
+headers = files('rte_iavf_emu.h')
diff --git a/drivers/emu/iavf/rte_iavf_emu.h b/drivers/emu/iavf/rte_iavf_emu.h
new file mode 100644
index 0000000000..623c3c5d99
--- /dev/null
+++ b/drivers/emu/iavf/rte_iavf_emu.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IAVF_EMU_H
+#define _IAVF_EMU_H
+
+#include <stdint.h>
+
+#include <rte_emudev.h>
+
+#define RTE_IAVF_EMUDEV_TYPE "iavf"
+#define RTE_IAVF_EMU_MAX_MEM_REGIONS 256
+#define RTE_IAVF_EMU_MAX_QP_NUM 256
+#define RTE_IAVF_EMU_MAX_INTR 32
+
+enum {
+	RTE_IAVF_EMU_ADMINQ_TXQ = 0,
+	RTE_IAVF_EMU_ADMINQ_RXQ = 1,
+	RTE_IAVF_EMU_ADMINQ_NUM = 2,
+};
+
+enum {
+	RTE_IAVF_EMU_MAPPABLE_REG_BAR0 = 0,
+	RTE_IAVF_EMU_MAPPABLE_REG_BAR3 = 1,
+	RTE_IAVF_EMU_MAPPABLE_REG_NUM = 2,
+};
+
+struct rte_iavf_emu_mem_reg {
+	uint64_t guest_phys_addr;
+	uint64_t host_user_addr;
+	uint64_t size;
+	void	 *mmap_addr;
+	uint64_t mmap_size;
+	int fd;
+};
+
+struct rte_iavf_emu_mem {
+	uint32_t region_num;
+	struct rte_iavf_emu_mem_reg regions[RTE_IAVF_EMU_MAX_MEM_REGIONS];
+};
+
+#endif
diff --git a/drivers/emu/iavf/version.map b/drivers/emu/iavf/version.map
new file mode 100644
index 0000000000..4a76d1d52d
--- /dev/null
+++ b/drivers/emu/iavf/version.map
@@ -0,0 +1,3 @@
+DPDK_21 {
+	local: *;
+};
diff --git a/drivers/emu/meson.build b/drivers/emu/meson.build
new file mode 100644
index 0000000000..acc8c395ef
--- /dev/null
+++ b/drivers/emu/meson.build
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation
+
+drivers = ['iavf']
+std_deps = ['emudev']
+config_flag_fmt = 'RTE_LIBRTE_PMD_@0@_EMUDEV'
diff --git a/drivers/meson.build b/drivers/meson.build
index f9febc579e..4579832459 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -16,6 +16,7 @@ subdirs = [
 	'vdpa',    # depends on common, bus and mempool.
 	'event',   # depends on common, bus, mempool and net.
 	'baseband', # depends on common and bus.
+	'emu', # depends on common and bus.
 ]
 
 disabled_drivers = run_command(list_dir_globs, get_option('disable_drivers'),
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH 4/8] emu/iavf: add vfio-user device register and unregister
  2020-12-18  7:47 [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver Chenbo Xia
                   ` (2 preceding siblings ...)
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 3/8] emu: introduce emulated iavf driver Chenbo Xia
@ 2020-12-18  7:47 ` Chenbo Xia
  2021-01-07  7:18   ` Xing, Beilei
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 5/8] emu/iavf: add resource management and internal logic of iavf Chenbo Xia
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 44+ messages in thread
From: Chenbo Xia @ 2020-12-18  7:47 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This patch adds vfio-user APIs call in driver probe and remove.
rte_vfio_user_register() and rte_vfio_user_unregister() are called
to create/destroy a vfio-user device. Notify callbacks that
libvfio_user defines are also implemented.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Miao Li <miao.li@intel.com>
---
 drivers/emu/iavf/iavf_emu.c          |   3 +-
 drivers/emu/iavf/iavf_emu_internal.h |  19 ++
 drivers/emu/iavf/iavf_emudev.c       |  12 +-
 drivers/emu/iavf/iavf_vfio_user.c    | 384 +++++++++++++++++++++++++++
 drivers/emu/iavf/iavf_vfio_user.h    |  16 ++
 drivers/emu/iavf/meson.build         |   5 +-
 drivers/emu/iavf/rte_iavf_emu.h      |  17 ++
 7 files changed, 452 insertions(+), 4 deletions(-)
 create mode 100644 drivers/emu/iavf/iavf_vfio_user.c
 create mode 100644 drivers/emu/iavf/iavf_vfio_user.h

diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index 68d2c440e3..dfd9796920 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -2,7 +2,7 @@
  * Copyright(c) 2020 Intel Corporation
  */
 
-#include "iavf_emu_internal.h"
+#include "iavf_vfio_user.h"
 
 static int iavf_emu_dev_close(struct rte_emudev *dev)
 {
@@ -18,6 +18,7 @@ static int iavf_emu_dev_close(struct rte_emudev *dev)
 	}
 
 	iavf = (struct iavf_emudev *)dev->priv_data;
+	iavf_emu_unregister_vfio_user(iavf);
 	iavf_emu_uninit_device(iavf);
 	dev->priv_data = NULL;
 
diff --git a/drivers/emu/iavf/iavf_emu_internal.h b/drivers/emu/iavf/iavf_emu_internal.h
index a726bfe577..10197c00ba 100644
--- a/drivers/emu/iavf/iavf_emu_internal.h
+++ b/drivers/emu/iavf/iavf_emu_internal.h
@@ -17,6 +17,13 @@ extern int emu_iavf_logtype;
 #define EMU_IAVF_LOG(level, ...) \
 	rte_log(RTE_LOG_ ## level, emu_iavf_logtype, "EMU_IAVF: " __VA_ARGS__)
 
+struct iavf_emu_vfio_user {
+	int dev_id;
+	struct vfio_device_info *dev_info;
+	struct rte_vfio_user_regions *reg;
+	struct rte_vfio_user_irq_info *irq;
+};
+
 struct iavf_emu_intr_info {
 	int enable;
 	int fd;
@@ -27,6 +34,14 @@ struct iavf_emu_intr {
 	struct iavf_emu_intr_info info[RTE_IAVF_EMU_MAX_INTR];
 };
 
+struct iavf_emu_adminQ {
+	uint32_t *ring_addr_lo;
+	uint32_t *ring_addr_hi;
+	uint32_t *ring_sz;
+	uint16_t db_size;
+	void *doorbell;
+};
+
 struct iavf_emu_lanQ {
 	uint16_t db_size;
 	void *doorbell;
@@ -34,14 +49,18 @@ struct iavf_emu_lanQ {
 
 struct iavf_emudev {
 	struct rte_emudev *edev;
+	struct iavf_emu_vfio_user *vfio;
 	/* Maximum LANQ queue pair that this emulated iavf has */
 	uint16_t max_lanqp;
 	/* Maximum LANQ queue pair number that back-end driver can use */
 	uint16_t max_be_lanqp;
 	unsigned int numa_node;
+	int ready;
 	char *sock_addr;
+	struct rte_iavf_emu_notify_ops *ops;
 	struct rte_iavf_emu_mem *mem;
 	struct iavf_emu_intr *intr;
+	struct iavf_emu_adminQ adq[RTE_IAVF_EMU_ADMINQ_NUM];
 	struct iavf_emu_lanQ *lanq;
 };
 
diff --git a/drivers/emu/iavf/iavf_emudev.c b/drivers/emu/iavf/iavf_emudev.c
index a4cd2deb06..fbbe3d95a7 100644
--- a/drivers/emu/iavf/iavf_emudev.c
+++ b/drivers/emu/iavf/iavf_emudev.c
@@ -6,7 +6,7 @@
 #include <rte_emudev.h>
 #include <rte_emudev_vdev.h>
 
-#include "iavf_emu_internal.h"
+#include "iavf_vfio_user.h"
 
 #define EMU_IAVF_SOCK_ARG "sock"
 #define EMU_IAVF_QUEUES_ARG "queues"
@@ -170,10 +170,20 @@ rte_emu_iavf_probe(struct rte_vdev_device *dev)
 	iavf->max_lanqp = queues;
 	edev->priv_data = (void *)iavf;
 
+	ret = iavf_emu_register_vfio_user(iavf);
+	if (ret) {
+		EMU_IAVF_LOG(ERR,
+			"Emulated iavf failed to register vfio user.\n");
+		ret = -1;
+		goto err_reg;
+	}
+
 	edev->started = 1;
 	rte_kvargs_free(kvlist);
 	return 0;
 
+err_reg:
+	iavf_emu_uninit_device(iavf);
 err_ndev:
 	rte_emudev_release(edev);
 err:
diff --git a/drivers/emu/iavf/iavf_vfio_user.c b/drivers/emu/iavf/iavf_vfio_user.c
new file mode 100644
index 0000000000..aae47de9f3
--- /dev/null
+++ b/drivers/emu/iavf/iavf_vfio_user.c
@@ -0,0 +1,384 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <pthread.h>
+
+#include <rte_malloc.h>
+
+#include "iavf_vfio_user.h"
+#include <iavf_type.h>
+
+struct iavf_emu_sock_list {
+	TAILQ_ENTRY(iavf_emu_sock_list) next;
+	struct rte_emudev *emu_dev;
+};
+
+TAILQ_HEAD(iavf_emu_sock_list_head, iavf_emu_sock_list);
+
+static struct iavf_emu_sock_list_head sock_list =
+	TAILQ_HEAD_INITIALIZER(sock_list);
+
+static pthread_mutex_t sock_list_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static int iavf_emu_setup_irq(struct iavf_emudev *dev)
+{
+	struct iavf_emu_intr *intr;
+	struct rte_vfio_user_irq_info *irq;
+	int *fds = NULL;
+	uint32_t i, count;
+
+	irq = dev->vfio->irq;
+	if (!irq)
+		return -1;
+
+	count = irq->irq_info[VFIO_PCI_MSIX_IRQ_INDEX].count;
+	if (count) {
+		fds = rte_zmalloc("irq_fds", sizeof(int) * count, 0);
+		if (!fds) {
+			EMU_IAVF_LOG(ERR,
+				"Failed to alloc irq fds.\n");
+			return -1;
+		}
+	}
+
+	if (rte_vfio_user_get_irq(dev->vfio->dev_id,
+			VFIO_PCI_MSIX_IRQ_INDEX, count, fds)) {
+		EMU_IAVF_LOG(ERR, "Failed to get irqfds from vfio-user.\n");
+		return -1;
+	}
+
+	intr = dev->intr;
+	intr->intr_num = irq->irq_info[VFIO_PCI_MSIX_IRQ_INDEX].count;
+
+	for (i = 0; i < count; i++) {
+		intr->info[i].fd = fds[i];
+		intr->info[i].enable = 0;
+	}
+
+	rte_free(fds);
+
+	return 0;
+}
+
+static inline void iavf_emu_reset_irq(struct iavf_emudev *dev)
+{
+	struct iavf_emu_intr *intr = dev->intr;
+	uint32_t i;
+
+	for (i = 0; i < intr->intr_num; i++) {
+		intr->info[i].enable = 0;
+		intr->info[i].fd = -1;
+	}
+}
+
+static inline void iavf_emu_reset_regions(struct iavf_emudev *dev)
+{
+	struct rte_vfio_user_regions *reg = dev->vfio->reg;
+	struct rte_vfio_user_reg_info *vinfo;
+	uint32_t i;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		vinfo = &reg->reg_info[i];
+		if (vinfo->info->size && vinfo->base)
+			memset(vinfo->base, 0, vinfo->info->size);
+	}
+}
+
+static int iavf_emu_setup_mem_table(struct iavf_emudev *dev)
+{
+	const struct rte_vfio_user_mem *vfio_mem;
+	const struct rte_vfio_user_mtb_entry *entry;
+	struct rte_iavf_emu_mem *mem;
+	uint32_t i;
+
+	vfio_mem = rte_vfio_user_get_mem_table(dev->vfio->dev_id);
+	if (!vfio_mem) {
+		EMU_IAVF_LOG(ERR, "Unable to get vfio mem table.\n");
+		return -1;
+	}
+
+	mem = dev->mem;
+
+	mem->region_num = vfio_mem->entry_num;
+	if (mem->region_num > RTE_IAVF_EMU_MAX_MEM_REGIONS) {
+		EMU_IAVF_LOG(ERR, "Failed to set up mem table,"
+			"exceed max region num.\n");
+		return -1;
+	}
+
+	for (i = 0; i < vfio_mem->entry_num; i++) {
+		entry = &vfio_mem->entry[i];
+
+		mem->regions[i].guest_phys_addr = entry->gpa;
+		mem->regions[i].host_user_addr = entry->host_user_addr;
+		mem->regions[i].mmap_addr = entry->mmap_addr;
+		mem->regions[i].mmap_size = entry->mmap_size;
+		mem->regions[i].size = entry->size;
+		mem->regions[i].fd = entry->fd;
+	}
+
+	return 0;
+}
+
+static inline void iavf_emu_reset_mem_table(struct iavf_emudev *dev)
+{
+	struct rte_iavf_emu_mem *mem = dev->mem;
+	uint32_t i;
+
+	for (i = 0; i < mem->region_num; i++) {
+		mem->regions[i].guest_phys_addr = 0;
+		mem->regions[i].host_user_addr = 0;
+		mem->regions[i].mmap_addr = 0;
+		mem->regions[i].mmap_size = 0;
+		mem->regions[i].size = 0;
+		mem->regions[i].fd = -1;
+	}
+}
+
+static int iavf_emu_setup_queues(struct iavf_emudev *dev)
+{
+	struct iavf_emu_adminQ *asq, *arq;
+	struct rte_vfio_user_reg_info *info;
+	uint16_t i;
+
+	info = &dev->vfio->reg->reg_info[0];
+	asq = &dev->adq[RTE_IAVF_EMU_ADMINQ_TXQ];
+	arq = &dev->adq[RTE_IAVF_EMU_ADMINQ_RXQ];
+
+	asq->doorbell = (uint8_t *)info->base + IAVF_VF_ATQT1;
+	asq->db_size = 4;
+	asq->ring_addr_lo = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ATQBAL1);
+	asq->ring_addr_hi = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ATQBAH1);
+	asq->ring_sz = (uint32_t *)((uint8_t *)info->base + IAVF_VF_ATQLEN1);
+
+	arq->doorbell = (uint8_t *)info->base + IAVF_VF_ARQT1;
+	arq->db_size = 4;
+	arq->ring_addr_lo = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ARQBAL1);
+	arq->ring_addr_hi = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ARQBAH1);
+	arq->ring_sz = (uint32_t *)((uint8_t *)info->base + IAVF_VF_ARQLEN1);
+
+	for (i = 0; i < dev->max_lanqp; i++) {
+		dev->lanq[i * 2].doorbell = (uint8_t *)info->base +
+				IAVF_QTX_TAIL1(i);
+		dev->lanq[i * 2].db_size = 4;
+		dev->lanq[i * 2 + 1].doorbell = (uint8_t *)info->base +
+				IAVF_QRX_TAIL1(i);
+		dev->lanq[i * 2 + 1].db_size = 4;
+	}
+
+	return 0;
+}
+
+static inline void iavf_emu_reset_queues(struct iavf_emudev *dev)
+{
+	memset(&dev->adq, 0, RTE_IAVF_EMU_ADMINQ_NUM *
+		sizeof(struct iavf_emu_adminQ));
+
+	memset(dev->lanq, 0, dev->max_lanqp * 2 *
+		sizeof(struct iavf_emu_lanQ));
+}
+
+static void iavf_emu_reset_all_resources(struct iavf_emudev *dev)
+{
+	iavf_emu_reset_mem_table(dev);
+	iavf_emu_reset_irq(dev);
+	iavf_emu_reset_queues(dev);
+	iavf_emu_reset_regions(dev);
+}
+
+static inline struct iavf_emu_sock_list *
+iavf_emu_find_sock_list(char *sock_addr)
+{
+	struct iavf_emu_sock_list *list;
+	struct iavf_emudev *dev;
+	int list_exist;
+
+	if (!sock_addr)
+		return NULL;
+
+	pthread_mutex_lock(&sock_list_lock);
+
+	TAILQ_FOREACH(list, &sock_list, next) {
+		dev = (struct iavf_emudev *)list->emu_dev->priv_data;
+
+		if (!strcmp(dev->sock_addr, sock_addr)) {
+			list_exist = 1;
+			break;
+		}
+		break;
+	}
+
+	pthread_mutex_unlock(&sock_list_lock);
+
+	if (!list_exist)
+		return NULL;
+
+	return list;
+}
+
+static struct iavf_emudev *find_iavf_with_dev_id(int vfio_dev_id)
+{
+	struct iavf_emu_sock_list *list;
+	char sock_addr[PATH_MAX];
+	int ret;
+
+	ret = rte_vfio_get_sock_addr(vfio_dev_id, sock_addr,
+		sizeof(sock_addr));
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Can not find vfio device %d "
+			"sock_addr.\n", vfio_dev_id);
+		return NULL;
+	}
+
+	list = iavf_emu_find_sock_list(sock_addr);
+	if (!list) {
+		EMU_IAVF_LOG(ERR, "Can not find sock list.\n");
+		return NULL;
+	}
+
+	return (struct iavf_emudev *)list->emu_dev->priv_data;
+}
+
+static int iavf_emu_new_device(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+	int ret;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	dev->vfio->dev_id = vfio_dev_id;
+
+	ret = iavf_emu_setup_mem_table(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up memtable for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = iavf_emu_setup_irq(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up irq for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = iavf_emu_setup_queues(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up queues for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = dev->ops->device_ready(dev->edev);
+	if (ret)
+		return ret;
+
+	dev->ready = 1;
+	return 0;
+}
+
+static void iavf_emu_destroy_device(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return;
+
+	iavf_emu_reset_all_resources(dev);
+
+	dev->ops->device_destroy(dev->edev);
+}
+
+static int iavf_emu_update_status(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+	int ret;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	ret = iavf_emu_setup_mem_table(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up memtable for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = iavf_emu_setup_irq(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up irq for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	dev->ops->update_status(dev->edev);
+
+	return 0;
+}
+
+static int iavf_emu_lock_datapath(int vfio_dev_id, int lock)
+{
+	struct iavf_emudev *dev;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	return dev->ops->lock_dp(dev->edev, lock);
+}
+
+static int iavf_emu_reset_device(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	iavf_emu_reset_all_resources(dev);
+
+	return dev->ops->reset_device(dev->edev);
+}
+
+struct rte_vfio_user_notify_ops vfio_ops = {
+	.new_device = iavf_emu_new_device,
+	.destroy_device = iavf_emu_destroy_device,
+	.update_status = iavf_emu_update_status,
+	.lock_dp = iavf_emu_lock_datapath,
+	.reset_device = iavf_emu_reset_device,
+};
+
+int iavf_emu_register_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+
+	ret = rte_vfio_user_register(dev->sock_addr, &vfio_ops);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Register vfio_user failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+int iavf_emu_unregister_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+
+	ret = rte_vfio_user_unregister(dev->sock_addr);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Unregister vfio_user failed\n");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/drivers/emu/iavf/iavf_vfio_user.h b/drivers/emu/iavf/iavf_vfio_user.h
new file mode 100644
index 0000000000..aa2f3edc87
--- /dev/null
+++ b/drivers/emu/iavf/iavf_vfio_user.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IAVF_VFIO_USER_H
+#define _IAVF_VFIO_USER_H
+
+#include <rte_vfio_user.h>
+
+#include "iavf_emu_internal.h"
+
+int iavf_emu_register_vfio_user(struct iavf_emudev *dev);
+
+int iavf_emu_unregister_vfio_user(struct iavf_emudev *dev);
+
+#endif
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
index 58c2a90383..4f651258c2 100644
--- a/drivers/emu/iavf/meson.build
+++ b/drivers/emu/iavf/meson.build
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2020 Intel Corporation
 
-sources = files('iavf_emu.c', 'iavf_emudev.c')
+sources = files('iavf_emu.c', 'iavf_vfio_user.c',
+	'iavf_emudev.c')
 
-deps += ['bus_vdev', 'emudev']
+deps += ['bus_vdev', 'emudev', 'vfio_user', 'common_iavf']
 
 headers = files('rte_iavf_emu.h')
diff --git a/drivers/emu/iavf/rte_iavf_emu.h b/drivers/emu/iavf/rte_iavf_emu.h
index 623c3c5d99..6de0989f0b 100644
--- a/drivers/emu/iavf/rte_iavf_emu.h
+++ b/drivers/emu/iavf/rte_iavf_emu.h
@@ -40,4 +40,21 @@ struct rte_iavf_emu_mem {
 	struct rte_iavf_emu_mem_reg regions[RTE_IAVF_EMU_MAX_MEM_REGIONS];
 };
 
+struct rte_iavf_emu_notify_ops {
+	/* Device is ready */
+	int (*device_ready)(struct rte_emudev *dev);
+	/* Device is destroyed */
+	void (*device_destroy)(struct rte_emudev *dev);
+	/* Update device status */
+	int (*update_status)(struct rte_emudev *dev);
+	/* Start device */
+	int (*device_start)(struct rte_emudev *dev);
+	/* Stop device */
+	int (*device_stop)(struct rte_emudev *dev);
+	/* Lock or unlock data path */
+	int (*lock_dp)(struct rte_emudev *dev, int lock);
+	/* Reset device */
+	int (*reset_device)(struct rte_emudev *dev);
+};
+
 #endif
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH 5/8] emu/iavf: add resource management and internal logic of iavf
  2020-12-18  7:47 [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver Chenbo Xia
                   ` (3 preceding siblings ...)
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 4/8] emu/iavf: add vfio-user device register and unregister Chenbo Xia
@ 2020-12-18  7:47 ` Chenbo Xia
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 6/8] emu/iavf: add emudev operations to fit in emudev framework Chenbo Xia
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-18  7:47 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This patch adds the allocation and release of device resources.
Device resources include PCI BARs' memory and interrupt related
resources. Device internal logic is also added.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
---
 drivers/emu/iavf/iavf_emu.c       |   1 +
 drivers/emu/iavf/iavf_emudev.c    |  20 +
 drivers/emu/iavf/iavf_vfio_user.c | 669 ++++++++++++++++++++++++++++++
 drivers/emu/iavf/iavf_vfio_user.h |  41 ++
 drivers/emu/iavf/meson.build      |   8 +
 5 files changed, 739 insertions(+)

diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index dfd9796920..c1a702d744 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -18,6 +18,7 @@ static int iavf_emu_dev_close(struct rte_emudev *dev)
 	}
 
 	iavf = (struct iavf_emudev *)dev->priv_data;
+	iavf_emu_uninit_vfio_user(iavf);
 	iavf_emu_unregister_vfio_user(iavf);
 	iavf_emu_uninit_device(iavf);
 	dev->priv_data = NULL;
diff --git a/drivers/emu/iavf/iavf_emudev.c b/drivers/emu/iavf/iavf_emudev.c
index fbbe3d95a7..70cf558eef 100644
--- a/drivers/emu/iavf/iavf_emudev.c
+++ b/drivers/emu/iavf/iavf_emudev.c
@@ -178,10 +178,30 @@ rte_emu_iavf_probe(struct rte_vdev_device *dev)
 		goto err_reg;
 	}
 
+	ret = iavf_emu_init_vfio_user(iavf);
+	if (ret) {
+		EMU_IAVF_LOG(ERR,
+			"Emulated iavf failed to init vfio user.\n");
+		ret = -1;
+		goto err_init;
+	}
+
+	ret = iavf_emu_start_vfio_user(iavf);
+	if (ret) {
+		EMU_IAVF_LOG(ERR,
+			"Emulated iavf failed to start vfio user.\n");
+		ret = -1;
+		goto err_start;
+	}
+
 	edev->started = 1;
 	rte_kvargs_free(kvlist);
 	return 0;
 
+err_start:
+	iavf_emu_uninit_vfio_user(iavf);
+err_init:
+	iavf_emu_unregister_vfio_user(iavf);
 err_reg:
 	iavf_emu_uninit_device(iavf);
 err_ndev:
diff --git a/drivers/emu/iavf/iavf_vfio_user.c b/drivers/emu/iavf/iavf_vfio_user.c
index aae47de9f3..a4208de618 100644
--- a/drivers/emu/iavf/iavf_vfio_user.c
+++ b/drivers/emu/iavf/iavf_vfio_user.c
@@ -2,13 +2,36 @@
  * Copyright(c) 2020 Intel Corporation
  */
 
+#include <linux/pci.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
 #include <pthread.h>
+#include <sys/types.h>
 
 #include <rte_malloc.h>
+#include <rte_emudev.h>
+#include <rte_memcpy.h>
 
 #include "iavf_vfio_user.h"
 #include <iavf_type.h>
 
+#define STORE_LE16(addr, val)   (*(__u16 *)addr = val)
+#define STORE_LE32(addr, val)   (*(__u32 *)addr = val)
+
+#define IAVF_EMU_BAR0_SIZE 0x10000
+#define IAVF_EMU_BAR3_SIZE 0x1000
+#define IAVF_EMU_BAR_SIZE_MASK 0xffffffff
+#define IAVF_EMU_BAR_MASK(sz) (~(sz) + 1)
+#define IAVF_EMU_MSIX_TABLE_SIZE 0x5
+
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_SUBDEVICE_ID 0x1100
+#define PCI_CLASS_ETHERNET 0x0200
+
 struct iavf_emu_sock_list {
 	TAILQ_ENTRY(iavf_emu_sock_list) next;
 	struct rte_emudev *emu_dev;
@@ -174,6 +197,14 @@ static int iavf_emu_setup_queues(struct iavf_emudev *dev)
 	return 0;
 }
 
+static inline void iavf_emu_cleanup_queues(struct iavf_emudev *dev)
+{
+	memset(&dev->adq, 0, RTE_IAVF_EMU_ADMINQ_NUM *
+		sizeof(struct iavf_emu_adminQ));
+
+	rte_free(dev->lanq);
+}
+
 static inline void iavf_emu_reset_queues(struct iavf_emudev *dev)
 {
 	memset(&dev->adq, 0, RTE_IAVF_EMU_ADMINQ_NUM *
@@ -191,6 +222,576 @@ static void iavf_emu_reset_all_resources(struct iavf_emudev *dev)
 	iavf_emu_reset_regions(dev);
 }
 
+static int iavf_emu_init_dev(struct iavf_emudev *dev)
+{
+	struct iavf_emu_vfio_user *vfio;
+	struct vfio_device_info *dev_info;
+	struct rte_vfio_user_regions *reg;
+	struct rte_vfio_user_irq_info *irq;
+	struct vfio_region_info_cap_sparse_mmap *sparse;
+	int ret;
+	uint32_t i, j;
+
+	vfio = rte_zmalloc_socket("vfio", sizeof(*vfio), 0, dev->numa_node);
+	if (!vfio) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc iavf_emu_vfio_user\n");
+		ret = -1;
+		goto exit;
+	}
+
+	dev_info = rte_zmalloc_socket("vfio_dev_info",
+		sizeof(*dev_info), 0, dev->numa_node);
+	if (!dev_info) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc vfio dev_info\n");
+		ret = -1;
+		goto err_info;
+	}
+	dev_info->argsz = sizeof(*dev_info);
+	dev_info->flags = VFIO_DEVICE_FLAGS_PCI | VFIO_DEVICE_FLAGS_RESET;
+	dev_info->num_regions = VFIO_PCI_NUM_REGIONS;
+	dev_info->num_irqs = VFIO_PCI_NUM_IRQS;
+
+	reg = rte_zmalloc_socket("vfio_user_regions",
+		sizeof(*reg) + dev_info->num_regions *
+		sizeof(struct rte_vfio_user_reg_info), 0, dev->numa_node);
+	if (!reg) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc vfio_user_regions\n");
+		ret = -1;
+		goto err_reg;
+	}
+	reg->reg_num = dev_info->num_regions;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		struct rte_vfio_user_reg_info *vinfo = &reg->reg_info[i];
+		size_t sz = sizeof(struct vfio_region_info);
+
+		/* BAR0 has two sparse mmap area */
+		if (i == VFIO_PCI_BAR0_REGION_INDEX)
+			sz += sizeof(*sparse) + 2 * sizeof(*sparse->areas);
+
+		vinfo->info = rte_zmalloc_socket("vfio_region_info",
+			sz, 0, dev->numa_node);
+		if (!vinfo->info) {
+			EMU_IAVF_LOG(ERR, "Failed to alloc region info "
+				"for region %d\n", i);
+			ret = -1;
+			goto err_reg_alloc;
+		}
+
+		vinfo->info->index = i;
+
+		switch (i) {
+		case VFIO_PCI_CONFIG_REGION_INDEX:
+			vinfo->info->argsz = sz;
+			vinfo->info->offset = 0;
+			vinfo->info->size = IAVF_EMU_CFG_SPACE_SIZE;
+			vinfo->info->flags = VFIO_REGION_INFO_FLAG_READ |
+				       VFIO_REGION_INFO_FLAG_WRITE;
+			break;
+		case VFIO_PCI_BAR0_REGION_INDEX:
+			vinfo->info->argsz = sz;
+			vinfo->info->offset = 0;
+			vinfo->info->size = IAVF_EMU_BAR0_SIZE;
+			vinfo->info->flags  = VFIO_REGION_INFO_FLAG_READ |
+				       VFIO_REGION_INFO_FLAG_WRITE |
+				       VFIO_REGION_INFO_FLAG_MMAP |
+				       VFIO_REGION_INFO_FLAG_CAPS;
+			vinfo->info->cap_offset =
+						sizeof(struct vfio_region_info);
+
+			sparse = (struct vfio_region_info_cap_sparse_mmap *)
+						((uint8_t *)vinfo->info +
+						vinfo->info->cap_offset);
+			sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
+			sparse->header.version = 1;
+			sparse->nr_areas = 2;
+			sparse->areas[0].offset = 0;
+			sparse->areas[0].size = 0x3000;
+			sparse->areas[1].offset = 0x6000;
+			sparse->areas[1].size = IAVF_EMU_BAR0_SIZE - 0x6000;
+
+			break;
+		case VFIO_PCI_BAR3_REGION_INDEX:
+			vinfo->info->argsz = sz;
+			vinfo->info->offset = 0;
+			vinfo->info->size = IAVF_EMU_BAR3_SIZE;
+			vinfo->info->flags  = VFIO_REGION_INFO_FLAG_READ |
+				       VFIO_REGION_INFO_FLAG_WRITE |
+				       VFIO_REGION_INFO_FLAG_MMAP;
+			break;
+		default:
+			vinfo->info->argsz = sz;
+			vinfo->info->offset = 0;
+			vinfo->info->size = 0;
+			vinfo->info->flags = 0;
+		}
+	}
+
+	irq = rte_zmalloc_socket("vfio_user_irq_info", sizeof(*irq) +
+		VFIO_PCI_NUM_IRQS * sizeof(struct vfio_irq_info),
+		0, dev->numa_node);
+	if (!irq) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc vfio_user_irqs\n");
+		ret = -1;
+		goto err_irq;
+	}
+	irq->irq_num = VFIO_PCI_NUM_IRQS;
+
+	for (i = 0; i < VFIO_PCI_NUM_IRQS; i++) {
+		irq->irq_info[i].index = i;
+		irq->irq_info[i].flags =
+			VFIO_IRQ_INFO_EVENTFD | VFIO_IRQ_INFO_NORESIZE;
+		if (i == VFIO_PCI_MSIX_IRQ_INDEX)
+			irq->irq_info[i].count =
+				IAVF_EMU_MSIX_TABLE_SIZE + 1;
+		else if (i == VFIO_PCI_ERR_IRQ_INDEX)
+			irq->irq_info[i].count = 1;
+		else
+			irq->irq_info[i].count = 0;
+	}
+
+	vfio->dev_info = dev_info;
+	vfio->reg = reg;
+	vfio->irq = irq;
+	dev->vfio = vfio;
+
+	return 0;
+
+err_irq:
+err_reg_alloc:
+	for (j = 0; j < i; j++)
+		rte_free(reg->reg_info[j].info);
+	rte_free(reg);
+err_reg:
+	rte_free(dev_info);
+err_info:
+	rte_free(vfio);
+exit:
+	return ret;
+}
+
+static int iavf_emu_uninit_dev(struct iavf_emudev *dev)
+{
+	struct iavf_emu_vfio_user *vfio;
+	struct rte_vfio_user_regions *reg;
+	uint32_t i;
+
+	if (!dev->vfio)
+		return -1;
+
+	vfio = dev->vfio;
+	rte_free(vfio->dev_info);
+
+	reg = vfio->reg;
+	for (i = 0; i < reg->reg_num; i++)
+		rte_free(reg->reg_info[i].info);
+	rte_free(reg);
+
+	rte_free(vfio->irq);
+	rte_free(vfio);
+
+	return 0;
+}
+
+static int handle_pci_cmd_write(struct iavf_emu_pci_hdr *hdr,
+	char *buf, size_t count)
+{
+	/* Below are all R/W bits in command register */
+	uint16_t rw_bitmask = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+		PCI_COMMAND_MASTER | PCI_COMMAND_PARITY |
+		PCI_COMMAND_SERR | PCI_COMMAND_INTX_DISABLE;
+	uint16_t val;
+
+	if (count != 2) {
+		EMU_IAVF_LOG(ERR, "Wrong write count (%lu) for PCI_COMMAND\n",
+			count);
+		return -1;
+	}
+
+	val = *(uint16_t *)buf;
+	/* Only write the R/W bits */
+	hdr->cmd = (rw_bitmask & val) | (~rw_bitmask & hdr->cmd);
+
+	return 2;
+}
+
+static int handle_pci_status_write(struct iavf_emu_pci_hdr *hdr,
+	char *buf, size_t count)
+{
+	/* Below are all write-1-to-clear bits in status register */
+	uint16_t rw1c_bitmask = PCI_STATUS_PARITY |
+		PCI_STATUS_SIG_TARGET_ABORT |
+		PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT |
+		PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY;
+	uint16_t val;
+
+	if (count != 2) {
+		EMU_IAVF_LOG(ERR, "Wrong write count (%lu) for PCI_STATUS\n",
+			count);
+		return -1;
+	}
+
+	val = *(uint16_t *)buf;
+	/* Clear the write-1-to-clear bits*/
+	hdr->status = ~(rw1c_bitmask & val) & hdr->status;
+
+	return 2;
+}
+
+static int handle_pci_bar_write(struct iavf_emu_pci_hdr *hdr,
+	char *buf, size_t count, loff_t pos)
+{
+	uint32_t val, size;
+	uint8_t idx;
+
+	if (count != 4) {
+		EMU_IAVF_LOG(ERR, "Wrong write count (%lu) for "
+			"Base Address Register\n",
+			count);
+		return -1;
+	}
+
+	val = *(uint32_t *)buf;
+
+	if (pos == PCI_BASE_ADDRESS_0)
+		size = IAVF_EMU_BAR0_SIZE;
+	else if (pos == PCI_BASE_ADDRESS_3)
+		size = IAVF_EMU_BAR3_SIZE;
+	else
+		size = 0;
+
+	if (val == IAVF_EMU_BAR_SIZE_MASK)
+		val &= IAVF_EMU_BAR_MASK(size);
+
+	idx = (pos - PCI_BASE_ADDRESS_0) / 0x4;
+	hdr->bar[idx] |= val & ~PCI_BASE_ADDRESS_MEM_MASK;
+
+	return 4;
+}
+
+static int handle_cfg_write(struct iavf_emu_pci_hdr *hdr,
+	char *buf, size_t count, loff_t pos)
+{
+	int ret = count;
+
+	switch (pos) {
+	case PCI_COMMAND:
+		ret = handle_pci_cmd_write(hdr, buf, count);
+		break;
+	case PCI_STATUS:
+		ret = handle_pci_status_write(hdr, buf, count);
+		break;
+	case PCI_INTERRUPT_LINE:
+		if (count != 1) {
+			EMU_IAVF_LOG(ERR, "Wrong write count (%lu)"
+				"for PCI_INTERRUPT_LINE\n",
+				count);
+			return -1;
+		}
+		hdr->intrl = *(uint8_t *)buf;
+		ret = 1;
+		break;
+	case PCI_BASE_ADDRESS_0:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_1:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_2:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_3:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_4:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_5:
+		ret = handle_pci_bar_write(hdr, buf, count, pos);
+		break;
+	default:
+		EMU_IAVF_LOG(INFO, "Write request for cfg (pos: %ld) ignored\n",
+			pos);
+		break;
+	}
+
+	return ret;
+}
+
+static ssize_t iavf_emu_cfg_rw(struct rte_vfio_user_reg_info *reg, char *buf,
+	size_t count, loff_t pos, bool iswrite)
+{
+	struct iavf_emu_cfg_space *cfg;
+	char *reg_pos;
+	int ret = 0;
+
+	if (!reg->base) {
+		EMU_IAVF_LOG(ERR, "Config space not exist\n");
+		return -EFAULT;
+	}
+
+	if (pos + count > reg->info->size) {
+		EMU_IAVF_LOG(ERR, "Access exceeds config space size\n");
+		return -EINVAL;
+	}
+
+	cfg = (struct iavf_emu_cfg_space *)reg->base;
+	reg_pos = (char *)reg->base + pos;
+
+	if (!iswrite) {
+		rte_memcpy(buf, reg_pos, count);
+		ret = count;
+	} else {
+		ret = handle_cfg_write(&cfg->hdr, buf, count, pos);
+		if (ret < 0) {
+			EMU_IAVF_LOG(ERR, "Failed to write cfg space\n");
+			return -EINVAL;
+		}
+	}
+
+	return ret;
+}
+
+static int iavf_emu_init_cfg_space(struct rte_vfio_user_reg_info *vinfo,
+	unsigned int numa_node)
+{
+	char *v_cfg;
+
+	vinfo->base = rte_zmalloc_socket("cfg space",
+		IAVF_EMU_CFG_SPACE_SIZE,
+		0, numa_node);
+	if (!vinfo->base) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc cfg space\n");
+		return -1;
+	}
+	vinfo->rw = iavf_emu_cfg_rw;
+	vinfo->fd = -1;
+	vinfo->priv = NULL;
+
+	v_cfg = (char *)vinfo->base;
+
+	STORE_LE16((uint16_t *)&v_cfg[PCI_VENDOR_ID],
+		PCI_VENDOR_ID_INTEL);
+	STORE_LE16((uint16_t *)&v_cfg[PCI_DEVICE_ID],
+		IAVF_DEV_ID_ADAPTIVE_VF);
+	STORE_LE16((uint16_t *)&v_cfg[PCI_SUBSYSTEM_VENDOR_ID],
+		PCI_VENDOR_ID_INTEL);
+	STORE_LE16((uint16_t *)&v_cfg[PCI_SUBSYSTEM_ID],
+		   PCI_SUBDEVICE_ID);
+
+	STORE_LE16((uint16_t *)&v_cfg[PCI_COMMAND],
+		   PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+	STORE_LE16((uint16_t *)&v_cfg[PCI_CLASS_DEVICE],
+		   PCI_CLASS_ETHERNET);
+	v_cfg[PCI_CLASS_REVISION] = 0x01;
+
+	STORE_LE16((uint16_t *)&v_cfg[PCI_STATUS],
+			   PCI_STATUS_CAP_LIST);
+
+	STORE_LE32((uint32_t *)&v_cfg[PCI_BASE_ADDRESS_0],
+		   PCI_BASE_ADDRESS_SPACE_MEMORY |
+		   PCI_BASE_ADDRESS_MEM_TYPE_32	 |
+		   PCI_BASE_ADDRESS_MEM_PREFETCH);
+
+	STORE_LE32((uint32_t *)&v_cfg[PCI_BASE_ADDRESS_3],
+		   PCI_BASE_ADDRESS_SPACE_MEMORY |
+		   PCI_BASE_ADDRESS_MEM_TYPE_32);
+
+	STORE_LE16((uint16_t *)&v_cfg[PCI_CAPABILITY_LIST],
+			   0x70);
+	STORE_LE16((uint16_t *)&v_cfg[0x70],
+			   PCI_CAP_ID_MSIX);
+
+	STORE_LE16((uint16_t *)&v_cfg[0x70 + PCI_MSIX_FLAGS],
+			(IAVF_EMU_MSIX_TABLE_SIZE & PCI_MSIX_FLAGS_QSIZE) |
+			PCI_MSIX_FLAGS_ENABLE);
+
+	STORE_LE32((uint32_t *)&v_cfg[0x70 + PCI_MSIX_TABLE],
+			   (0x3 & PCI_MSIX_TABLE_BIR));
+
+	STORE_LE32((uint32_t *)&v_cfg[0x70 + PCI_MSIX_PBA],
+			(0x3 & PCI_MSIX_PBA_BIR) |
+			(0x100 & PCI_MSIX_PBA_OFFSET));
+
+	return 0;
+}
+
+static inline void iavf_emu_uninit_cfg_space(
+	struct rte_vfio_user_reg_info *vinfo)
+{
+	rte_free(vinfo->base);
+	vinfo->rw = NULL;
+	vinfo->info->size = 0;
+	vinfo->fd = -1;
+}
+static ssize_t iavf_emu_bar0_rw(struct rte_vfio_user_reg_info *reg, char *buf,
+	size_t count, loff_t pos, bool iswrite)
+{
+	struct iavf_emudev *dev = (struct iavf_emudev *)reg->priv;
+	char *reg_pos;
+
+	if (!reg->base) {
+		EMU_IAVF_LOG(ERR, "BAR 0 does not exist\n");
+		return -EFAULT;
+	}
+
+	if (pos + count > reg->info->size) {
+		EMU_IAVF_LOG(ERR, "Access exceeds BAR 0 size\n");
+		return -EINVAL;
+	}
+
+	reg_pos = (char *)reg->base + pos;
+
+	if (!iswrite) {
+		rte_memcpy(buf, reg_pos, count);
+	} else {
+		int tmp;
+		uint32_t val;
+		int idx = -1;
+
+		if (count != 4)
+			return -EINVAL;
+
+		val = *(uint32_t *)buf;
+		/* Only handle interrupt enable/disable for now */
+		if (pos == IAVF_VFINT_DYN_CTL01) {
+			tmp = val & IAVF_VFINT_DYN_CTL01_INTENA_MASK;
+			idx = 0;
+		} else if ((pos >= IAVF_VFINT_DYN_CTLN1(0)) && pos <=
+			IAVF_VFINT_DYN_CTLN1(RTE_IAVF_EMU_MAX_INTR - 1)) {
+			tmp = val & IAVF_VFINT_DYN_CTLN1_INTENA_MASK;
+			idx = pos - IAVF_VFINT_DYN_CTLN1(0);
+			if (idx % 4)
+				return -EINVAL;
+			idx = idx / 4;
+		}
+
+		if (idx != -1 &&
+			tmp != dev->intr->info[idx].enable && dev->ready) {
+			dev->ops->update_status(dev->edev);
+			dev->intr->info[idx].enable = tmp;
+		}
+
+		rte_memcpy(reg_pos, buf, count);
+	}
+
+	return count;
+}
+
+static int iavf_emu_alloc_reg(struct iavf_emudev *dev)
+{
+	struct rte_vfio_user_regions *reg = dev->vfio->reg;
+	struct rte_vfio_user_reg_info *vinfo;
+	char shm_str[64];
+	uint32_t i;
+	int ret;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		vinfo = &reg->reg_info[i];
+
+		switch (i) {
+		case VFIO_PCI_CONFIG_REGION_INDEX:
+			ret = iavf_emu_init_cfg_space(vinfo, dev->numa_node);
+			if (ret)
+				return ret;
+			break;
+		case VFIO_PCI_BAR0_REGION_INDEX:
+		case VFIO_PCI_BAR3_REGION_INDEX:
+			sprintf(shm_str, "AVF%d_BAR%d",
+				dev->edev->dev_id, i);
+			vinfo->fd = shm_open(shm_str,
+				O_RDWR|O_CREAT, 0700);
+			if (vinfo->fd == -1) {
+				EMU_IAVF_LOG(ERR,
+					"Failed to open shm for BAR %d\n", i);
+				goto exit;
+			}
+
+			if (ftruncate(vinfo->fd, vinfo->info->size) == -1) {
+				EMU_IAVF_LOG(ERR,
+					"Failed to ftruncate BAR %d\n", i);
+				ret = -1;
+				goto exit;
+			}
+
+			vinfo->base = mmap(NULL, vinfo->info->size,
+					PROT_READ | PROT_WRITE,
+					MAP_SHARED, vinfo->fd, 0);
+			memset(vinfo->base, 0, vinfo->info->size);
+			if (vinfo->base == MAP_FAILED) {
+				EMU_IAVF_LOG(ERR,
+					"Failed to mmap BAR %d\n", i);
+				ret = -1;
+				goto exit;
+			}
+			vinfo->priv = (void *)dev;
+			if (i == VFIO_PCI_BAR0_REGION_INDEX)
+				vinfo->rw = iavf_emu_bar0_rw;
+			else
+				vinfo->rw = NULL;
+			break;
+		default:
+			vinfo->base = NULL;
+			vinfo->rw = NULL;
+			vinfo->fd = -1;
+			vinfo->priv = NULL;
+			break;
+		}
+	}
+
+	return 0;
+
+exit:
+	for (;; i--) {
+		vinfo = &reg->reg_info[i];
+
+		if (i == VFIO_PCI_CONFIG_REGION_INDEX)
+			iavf_emu_uninit_cfg_space(vinfo);
+
+		if (!vinfo->info->size) {
+			if (!vinfo->base)
+				munmap(vinfo->base, vinfo->info->size);
+			if (vinfo->fd > 0) {
+				close(vinfo->fd);
+				sprintf(shm_str, "AVF%d_BAR%d",
+					dev->edev->dev_id, i);
+				shm_unlink(shm_str);
+				vinfo->fd = -1;
+			}
+		}
+
+		if (i == 0)
+			break;
+	}
+	return ret;
+}
+
+static void iavf_emu_free_reg(struct iavf_emudev *dev)
+{
+	struct rte_vfio_user_regions *reg = dev->vfio->reg;
+	struct rte_vfio_user_reg_info *vinfo;
+	char shm_str[64];
+	uint32_t i;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		vinfo = &reg->reg_info[i];
+
+		switch (i) {
+		case VFIO_PCI_CONFIG_REGION_INDEX:
+			iavf_emu_uninit_cfg_space(vinfo);
+			break;
+		case VFIO_PCI_BAR0_REGION_INDEX:
+			/* FALLTHROUGH */
+		case VFIO_PCI_BAR3_REGION_INDEX:
+			munmap(vinfo->base, vinfo->info->size);
+			close(vinfo->fd);
+			vinfo->fd = -1;
+			sprintf(shm_str, "AVF%d_BAR%d",
+				dev->edev->dev_id, i);
+			shm_unlink(shm_str);
+			vinfo->info->size = 0;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
 static inline struct iavf_emu_sock_list *
 iavf_emu_find_sock_list(char *sock_addr)
 {
@@ -382,3 +983,71 @@ int iavf_emu_unregister_vfio_user(struct iavf_emudev *dev)
 
 	return 0;
 }
+
+int iavf_emu_init_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+	struct iavf_emu_sock_list *list;
+
+	if (iavf_emu_init_dev(dev)) {
+		EMU_IAVF_LOG(ERR, "Emulated iavf dev init failed.\n");
+		ret = -1;
+		goto exit;
+	}
+
+	if (iavf_emu_alloc_reg(dev)) {
+		EMU_IAVF_LOG(ERR, "Emulated iavf alloc region failed.\n");
+		ret = -1;
+		goto err_alloc_reg;
+	}
+
+	ret = rte_vfio_user_set_dev_info(dev->sock_addr, dev->vfio->dev_info);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set vfio dev info\n");
+		goto err_set;
+	}
+
+	ret = rte_vfio_user_set_reg_info(dev->sock_addr, dev->vfio->reg);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set vfio region info\n");
+		goto err_set;
+	}
+
+	ret = rte_vfio_user_set_irq_info(dev->sock_addr, dev->vfio->irq);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set vfio irq info\n");
+		goto err_set;
+	}
+
+	list = rte_zmalloc_socket("list", sizeof(*list), 0, dev->numa_node);
+	list->emu_dev = dev->edev;
+	pthread_mutex_lock(&sock_list_lock);
+	TAILQ_INSERT_TAIL(&sock_list, list, next);
+	pthread_mutex_unlock(&sock_list_lock);
+
+	return 0;
+
+err_set:
+	iavf_emu_free_reg(dev);
+err_alloc_reg:
+	iavf_emu_uninit_dev(dev);
+exit:
+	return ret;
+}
+
+void iavf_emu_uninit_vfio_user(struct iavf_emudev *dev)
+{
+	iavf_emu_free_reg(dev);
+	iavf_emu_uninit_dev(dev);
+}
+
+int iavf_emu_start_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+
+	ret = rte_vfio_user_start(dev->sock_addr);
+	if (ret)
+		EMU_IAVF_LOG(ERR, "Start vfio user failed.\n");
+
+	return ret;
+}
diff --git a/drivers/emu/iavf/iavf_vfio_user.h b/drivers/emu/iavf/iavf_vfio_user.h
index aa2f3edc87..2ccb04eb48 100644
--- a/drivers/emu/iavf/iavf_vfio_user.h
+++ b/drivers/emu/iavf/iavf_vfio_user.h
@@ -5,12 +5,53 @@
 #ifndef _IAVF_VFIO_USER_H
 #define _IAVF_VFIO_USER_H
 
+#include <linux/pci_regs.h>
+
 #include <rte_vfio_user.h>
 
 #include "iavf_emu_internal.h"
 
+#define IAVF_EMU_CFG_SPACE_SIZE 0x100
+
+struct iavf_emu_pci_hdr {
+	uint16_t vid;		/* Vendor ID */
+	uint16_t did;		/* Device ID */
+	uint16_t cmd;		/* Command */
+	uint16_t status;	/* Status */
+	uint8_t rid;		/* Revision ID */
+	uint8_t cc_pi;		/* Program I/F in Class Code*/
+	uint8_t cc_sub;		/* Sub-Class Code */
+	uint8_t cc_base;	/* Base Class Code */
+	uint8_t cl_size;	/* Cache Line Size*/
+	uint8_t lt_timer;	/* Latency Timer */
+	uint8_t hdr_type;	/* Header Type */
+	uint8_t bist;		/* BIST */
+	uint32_t bar[6];	/* Base Address Registers */
+	uint32_t ccp;		/* Cardbus CIC Pointer */
+	uint16_t sub_vid;	/* Subsystem Vendor ID */
+	uint16_t sub_sid;	/* Subsystem ID */
+	uint32_t rom;		/* Expansion ROM Base Address */
+	uint8_t cap;		/* Capabilities Pointer */
+	uint8_t rsvd[7];	/* Reserved */
+	uint8_t intrl;		/* Interrupt Line */
+	uint8_t intrp;		/* Interrupt Pin */
+	uint8_t min_gnt;	/* Min_Gnt Register */
+	uint8_t max_lat;	/* Max_Lat Register */
+} __attribute((packed));
+
+struct iavf_emu_cfg_space {
+	struct iavf_emu_pci_hdr hdr;
+	uint8_t cfg_non_std[IAVF_EMU_CFG_SPACE_SIZE - PCI_STD_HEADER_SIZEOF];
+} __attribute((packed));
+
 int iavf_emu_register_vfio_user(struct iavf_emudev *dev);
 
 int iavf_emu_unregister_vfio_user(struct iavf_emudev *dev);
 
+int iavf_emu_init_vfio_user(struct iavf_emudev *dev);
+
+void iavf_emu_uninit_vfio_user(struct iavf_emudev *dev);
+
+int iavf_emu_start_vfio_user(struct iavf_emudev *dev);
+
 #endif
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
index 4f651258c2..3cab2226b7 100644
--- a/drivers/emu/iavf/meson.build
+++ b/drivers/emu/iavf/meson.build
@@ -1,6 +1,14 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2020 Intel Corporation
 
+librt = cc.find_library('rt', required: false)
+if not librt.found()
+	build = false
+	subdir_done()
+endif
+
+ext_deps += librt
+
 sources = files('iavf_emu.c', 'iavf_vfio_user.c',
 	'iavf_emudev.c')
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH 6/8] emu/iavf: add emudev operations to fit in emudev framework
  2020-12-18  7:47 [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver Chenbo Xia
                   ` (4 preceding siblings ...)
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 5/8] emu/iavf: add resource management and internal logic of iavf Chenbo Xia
@ 2020-12-18  7:47 ` Chenbo Xia
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 7/8] test/emudev: introduce functional test Chenbo Xia
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-18  7:47 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This patch implements emudev opertions to make emulated iavf
fit into rte_emudev framework. Lifecycle related and device
resource related operations are both implemented.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
---
 drivers/emu/iavf/iavf_emu.c     | 218 ++++++++++++++++++++++++++++++++
 drivers/emu/iavf/rte_iavf_emu.h |  59 +++++++++
 2 files changed, 277 insertions(+)

diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index c1a702d744..9ad371ca98 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -2,7 +2,72 @@
  * Copyright(c) 2020 Intel Corporation
  */
 
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rte_malloc.h>
+#include <rte_emudev.h>
+
 #include "iavf_vfio_user.h"
+#include <iavf_type.h>
+
+static int iavf_emu_dev_start(struct rte_emudev *dev)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if (iavf->ops != NULL && iavf->ops->device_start != NULL)
+		iavf->ops->device_start(dev);
+
+	return 0;
+}
+
+static void iavf_emu_dev_stop(struct rte_emudev *dev)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if (iavf->ops != NULL && iavf->ops->device_stop != NULL)
+		iavf->ops->device_stop(dev);
+}
+
+static int iavf_emu_dev_configure(struct rte_emudev *dev,
+		struct rte_emudev_info *dev_conf)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+	struct rte_iavf_emu_config *conf =
+		(struct rte_iavf_emu_config *)dev_conf->dev_priv;
+
+	if (!dev_conf->dev_priv)
+		return -EINVAL;
+
+	/* Currently emulated iavf does not support max_qp_num
+	 * and region num configuration
+	 */
+	if (dev->dev_info.max_qp_num != dev_conf->max_qp_num ||
+		dev->dev_info.region_num != dev_conf->region_num) {
+		EMU_IAVF_LOG(ERR,
+			"Configure max_qp_num/region num not supported\n");
+		return -ENOTSUP;
+	}
+
+	if (conf->qp_num >  RTE_MAX_QUEUES_PER_PORT ||
+		conf->qp_num > RTE_IAVF_EMU_MAX_QP_NUM) {
+		EMU_IAVF_LOG(ERR, "Queue pair num exceeds max\n");
+		return -EINVAL;
+	}
+
+	/* For now, we don't support device configure when data
+	 * path driver is attached
+	 */
+	if (dev->backend_priv) {
+		EMU_IAVF_LOG(ERR, "Configure failed because of "
+			"data path attached\n");
+		return -EPERM;
+	}
+
+	iavf->max_be_lanqp = conf->qp_num;
+	return 0;
+}
 
 static int iavf_emu_dev_close(struct rte_emudev *dev)
 {
@@ -26,6 +91,159 @@ static int iavf_emu_dev_close(struct rte_emudev *dev)
 	return 0;
 }
 
+static int iavf_emu_get_dev_info(struct rte_emudev *dev,
+	rte_emudev_obj_t info)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+	struct rte_iavf_emu_config *conf = (struct rte_iavf_emu_config *)info;
+
+	if (!info)
+		return -EINVAL;
+
+	conf->qp_num = iavf->max_be_lanqp;
+	return 0;
+}
+
+static int iavf_emu_get_mem_table(struct rte_emudev *dev,
+	rte_emudev_mem_table_t *tb)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	*tb = iavf->mem;
+
+	return 0;
+}
+
+static int iavf_emu_get_queue_info(struct rte_emudev *dev, uint32_t queue,
+		struct rte_emudev_q_info *info)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if (queue < RTE_IAVF_EMU_ADMINQ_NUM) {
+		struct iavf_emu_adminQ *adq = &iavf->adq[queue];
+		uint64_t base, size;
+
+		if (adq->ring_addr_lo == NULL ||
+		    adq->ring_addr_hi == NULL ||
+		    adq->ring_sz == NULL)
+			return -1;
+		base = RTE_IAVF_EMU_32_TO_64(*adq->ring_addr_hi,
+			*adq->ring_addr_lo);
+		size = *adq->ring_sz;
+		info->base = base;
+		info->size = size;
+		info->doorbell_id = queue;
+		/* RX AdminQ should have IRQ vector 0 */
+		info->irq_vector = queue - 1;
+	} else {
+		info->base = 0;
+		info->size = 0;
+		info->doorbell_id = queue;
+		info->irq_vector = -1;
+	}
+
+	return 0;
+}
+
+static int iavf_emu_get_irq_info(struct rte_emudev *dev, uint32_t vector,
+		struct rte_emudev_irq_info *info)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+	struct iavf_emu_intr *intr = iavf->intr;
+	struct iavf_emu_intr_info *intr_info = &intr->info[vector];
+
+	if (vector >= intr->intr_num)
+		return -EINVAL;
+
+	info->eventfd = intr_info->fd;
+	info->enable = intr_info->enable;
+	info->vector = vector;
+
+	return 0;
+}
+
+static int iavf_emu_get_db_info(struct rte_emudev *dev, uint32_t doorbell,
+		struct rte_emudev_db_info *info)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if (doorbell < RTE_IAVF_EMU_ADMINQ_NUM) {
+		struct iavf_emu_adminQ *adq = &iavf->adq[doorbell];
+
+		info->data.mem.base = (uint64_t)adq->doorbell;
+		info->data.mem.size = adq->db_size;
+	} else {
+		struct iavf_emu_lanQ *lanq =
+			&iavf->lanq[doorbell - RTE_IAVF_EMU_ADMINQ_NUM];
+
+		info->data.mem.base = (uint64_t)lanq->doorbell;
+		info->data.mem.size = lanq->db_size;
+	}
+
+	info->flag |= RTE_EMUDEV_DB_MEM;
+	info->id = doorbell;
+
+	return 0;
+}
+
+static int iavf_emu_subs_ev(struct rte_emudev *dev,
+	rte_emudev_event_chnl_t ev_chnl)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	iavf->ops = (struct rte_iavf_emu_notify_ops *)ev_chnl;
+
+	return 0;
+}
+
+static int iavf_emu_unsubs_ev(struct rte_emudev *dev,
+		rte_emudev_event_chnl_t ev_chnl)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if ((struct rte_iavf_emu_notify_ops *)ev_chnl == iavf->ops) {
+		iavf->ops = NULL;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int iavf_emu_get_attr(struct rte_emudev *dev, const char *attr_name,
+		rte_emudev_attr_t attr)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+	struct rte_vfio_user_reg_info *info;
+	int ret = 0;
+
+	info = &iavf->vfio->reg->reg_info[0];
+
+	if (!strcmp(attr_name, RTE_IAVF_EMU_ATTR_ASQ_HEAD))
+		*(uint64_t *)attr = (uint64_t)(uintptr_t)info->base
+			+ IAVF_VF_ATQH1;
+	else if (!strcmp(attr_name, RTE_IAVF_EMU_ATTR_ARQ_HEAD))
+		*(uint64_t *)attr = (uint64_t)(uintptr_t)info->base
+			+ IAVF_VF_ARQH1;
+	else if (!strcmp(attr_name, RTE_IAVF_EMU_ATTR_RESET))
+		*(uint64_t *)attr = (uint64_t)(uintptr_t)info->base
+			+ IAVF_VFGEN_RSTAT;
+	else
+		ret = -EINVAL;
+
+	return ret;
+}
+
 struct rte_emudev_ops emu_iavf_ops = {
+	.dev_start = iavf_emu_dev_start,
+	.dev_stop = iavf_emu_dev_stop,
+	.dev_configure = iavf_emu_dev_configure,
 	.dev_close = iavf_emu_dev_close,
+	.dev_info_get = iavf_emu_get_dev_info,
+	.get_mem_table = iavf_emu_get_mem_table,
+	.get_queue_info = iavf_emu_get_queue_info,
+	.get_irq_info = iavf_emu_get_irq_info,
+	.get_db_info = iavf_emu_get_db_info,
+	.subscribe_event = iavf_emu_subs_ev,
+	.unsubscribe_event = iavf_emu_unsubs_ev,
+	.get_attr = iavf_emu_get_attr,
 };
diff --git a/drivers/emu/iavf/rte_iavf_emu.h b/drivers/emu/iavf/rte_iavf_emu.h
index 6de0989f0b..2abcec97d4 100644
--- a/drivers/emu/iavf/rte_iavf_emu.h
+++ b/drivers/emu/iavf/rte_iavf_emu.h
@@ -6,13 +6,24 @@
 #define _IAVF_EMU_H
 
 #include <stdint.h>
+#include <stddef.h>
+#include <limits.h>
+#include <net/if.h>
+#include <sys/queue.h>
 
+#include <rte_vfio_user.h>
 #include <rte_emudev.h>
 
 #define RTE_IAVF_EMUDEV_TYPE "iavf"
+#define RTE_IAVF_EMU_ATTR_ASQ_HEAD "ASQ_H"
+#define RTE_IAVF_EMU_ATTR_ARQ_HEAD "ARQ_H"
+#define RTE_IAVF_EMU_ATTR_RESET "RESET"
+#define RTE_IAVF_EMU_RESET_IN_PROGRESS 0x00
+#define RTE_IAVF_EMU_RESET_COMPLETED 0x01
 #define RTE_IAVF_EMU_MAX_MEM_REGIONS 256
 #define RTE_IAVF_EMU_MAX_QP_NUM 256
 #define RTE_IAVF_EMU_MAX_INTR 32
+#define RTE_IAVF_EMU_32_TO_64(hi, lo) ((((uint64_t)(hi)) << 32) + (lo))
 
 enum {
 	RTE_IAVF_EMU_ADMINQ_TXQ = 0,
@@ -26,6 +37,11 @@ enum {
 	RTE_IAVF_EMU_MAPPABLE_REG_NUM = 2,
 };
 
+struct rte_iavf_emu_config {
+	/* Maximum queue pair number that data path driver can use */
+	uint32_t qp_num;
+};
+
 struct rte_iavf_emu_mem_reg {
 	uint64_t guest_phys_addr;
 	uint64_t host_user_addr;
@@ -40,6 +56,49 @@ struct rte_iavf_emu_mem {
 	struct rte_iavf_emu_mem_reg regions[RTE_IAVF_EMU_MAX_MEM_REGIONS];
 };
 
+/**
+ * Helper function for data path driver to translate address
+ * of one region
+ *
+ * @param mem
+ *   A pointer to DMA memory table
+ * @param g_addr
+ *   Guest I/O virtual base address of the region
+ * @param[in/out] len
+ *   The length of region
+ * @return
+ *   - >0: Success, process virtual address returned
+ *   - 0: Failure on translation
+ */
+__rte_experimental
+__rte_always_inline uint64_t
+rte_iavf_emu_get_dma_vaddr(struct rte_iavf_emu_mem *mem,
+	uint64_t g_addr, uint64_t *len)
+{
+	struct rte_iavf_emu_mem_reg *reg;
+	uint32_t i;
+
+	for (i = 0; i < mem->region_num; i++) {
+		reg = &mem->regions[i];
+
+		if (g_addr >= reg->guest_phys_addr &&
+			g_addr < reg->guest_phys_addr + reg->size) {
+
+			if (unlikely(*len > reg->guest_phys_addr +
+				 reg->size - g_addr))
+				*len = reg->guest_phys_addr +
+					reg->size - g_addr;
+
+			return g_addr - reg->guest_phys_addr +
+				reg->host_user_addr;
+		}
+	}
+
+	*len = 0;
+
+	return 0;
+}
+
 struct rte_iavf_emu_notify_ops {
 	/* Device is ready */
 	int (*device_ready)(struct rte_emudev *dev);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH 7/8] test/emudev: introduce functional test
  2020-12-18  7:47 [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver Chenbo Xia
                   ` (5 preceding siblings ...)
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 6/8] emu/iavf: add emudev operations to fit in emudev framework Chenbo Xia
@ 2020-12-18  7:47 ` Chenbo Xia
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 8/8] doc: update release notes for iavf emudev driver Chenbo Xia
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-18  7:47 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This patch introduces functional test for emudev. The
implementation of iavf emudev selftest is also added.

Signed-off-by: Miao Li <miao.li@intel.com>
Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
---
 app/test/meson.build                 |   5 +-
 app/test/test_emudev.c               |  29 +++++
 drivers/emu/iavf/iavf_emu.c          |   1 +
 drivers/emu/iavf/iavf_emu_internal.h |   1 +
 drivers/emu/iavf/iavf_emu_test.c     | 174 +++++++++++++++++++++++++++
 drivers/emu/iavf/meson.build         |   2 +-
 6 files changed, 210 insertions(+), 2 deletions(-)
 create mode 100644 app/test/test_emudev.c
 create mode 100644 drivers/emu/iavf/iavf_emu_test.c

diff --git a/app/test/meson.build b/app/test/meson.build
index f5b15ac44c..b8b79bbc8b 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -139,6 +139,7 @@ test_sources = files('commands.c',
 	'test_trace_register.c',
 	'test_trace_perf.c',
 	'test_vfio_user.c',
+	'test_emudev.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
@@ -176,7 +177,8 @@ test_deps = ['acl',
 	'stack',
 	'vfio_user',
 	'telemetry',
-	'timer'
+	'timer',
+	'emudev'
 ]
 
 # Each test is marked with flag true/false
@@ -327,6 +329,7 @@ driver_test_names = [
         'eventdev_selftest_octeontx',
         'eventdev_selftest_sw',
         'rawdev_autotest',
+        'emudev_autotest',
 ]
 
 dump_test_names = [
diff --git a/app/test/test_emudev.c b/app/test/test_emudev.c
new file mode 100644
index 0000000000..0dfce162ed
--- /dev/null
+++ b/app/test/test_emudev.c
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <rte_emudev.h>
+#include <rte_bus_vdev.h>
+
+#include "test.h"
+
+static int
+test_emudev_selftest_impl(const char *pmd, const char *opts)
+{
+	int ret = 0;
+
+	if (rte_emudev_get_dev_id(pmd) == -ENODEV)
+		ret = rte_vdev_init(pmd, opts);
+	if (ret)
+		return TEST_SKIPPED;
+
+	return rte_emudev_selftest(rte_emudev_get_dev_id(pmd));
+}
+
+static int
+test_emudev_selftest(void)
+{
+	return test_emudev_selftest_impl("emu_iavf", "sock=/tmp/sock1");
+}
+
+REGISTER_TEST_COMMAND(emudev_autotest, test_emudev_selftest);
diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index 9ad371ca98..88bf2bdf94 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -246,4 +246,5 @@ struct rte_emudev_ops emu_iavf_ops = {
 	.subscribe_event = iavf_emu_subs_ev,
 	.unsubscribe_event = iavf_emu_unsubs_ev,
 	.get_attr = iavf_emu_get_attr,
+	.dev_selftest = iavf_emu_selftest,
 };
diff --git a/drivers/emu/iavf/iavf_emu_internal.h b/drivers/emu/iavf/iavf_emu_internal.h
index 10197c00ba..1ac7f96566 100644
--- a/drivers/emu/iavf/iavf_emu_internal.h
+++ b/drivers/emu/iavf/iavf_emu_internal.h
@@ -65,4 +65,5 @@ struct iavf_emudev {
 };
 
 void iavf_emu_uninit_device(struct iavf_emudev *dev);
+int iavf_emu_selftest(uint16_t dev_id);
 #endif
diff --git a/drivers/emu/iavf/iavf_emu_test.c b/drivers/emu/iavf/iavf_emu_test.c
new file mode 100644
index 0000000000..ad19134724
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emu_test.c
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <inttypes.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_memcpy.h>
+#include <rte_dev.h>
+#include <rte_emudev.h>
+#include <rte_bus_vdev.h>
+#include <rte_test.h>
+
+#include "iavf_emu_internal.h"
+
+#define TEST_DEV_NAME "emu_iavf"
+#define TEST_SUCCESS 0
+#define TEST_FAILED -1
+
+#define EMUDEV_TEST_RUN(setup, teardown, test) \
+	emudev_test_run(setup, teardown, test, #test)
+static uint16_t test_dev_id;
+static int total;
+static int passed;
+static int failed;
+static int unsupported;
+
+static int
+testsuite_setup(void)
+{
+	uint8_t count;
+	count = rte_emudev_count();
+	if (!count) {
+		EMU_IAVF_LOG(INFO, "No existing emu dev; "
+				  "Creating emu_iavf\n");
+		return rte_vdev_init(TEST_DEV_NAME, NULL);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static void
+testsuite_teardown(void)
+{
+	rte_vdev_uninit(TEST_DEV_NAME);
+}
+
+static void emudev_test_run(int (*setup)(void),
+			     void (*teardown)(void),
+			     int (*test)(void),
+			     const char *name)
+{
+	int ret = 0;
+
+	if (setup) {
+		ret = setup();
+		if (ret < 0) {
+			EMU_IAVF_LOG(INFO, "Error setting up test %s\n", name);
+			unsupported++;
+		}
+	}
+
+	if (test) {
+		ret = test();
+		if (ret < 0) {
+			failed++;
+			EMU_IAVF_LOG(INFO, "%s Failed\n", name);
+		} else {
+			passed++;
+			EMU_IAVF_LOG(INFO, "%s Passed\n", name);
+		}
+	}
+
+	if (teardown)
+		teardown();
+
+	total++;
+}
+
+static int
+test_emu_dev_count(void)
+{
+	uint8_t count;
+	count = rte_emudev_count();
+	RTE_TEST_ASSERT(count > 0, "Invalid emudev count %" PRIu8, count);
+	return TEST_SUCCESS;
+}
+
+static int
+test_emu_dev_get_dev_id(void)
+{
+	int ret;
+	ret = rte_emudev_get_dev_id("Invalid_emu_dev_device\n");
+	RTE_TEST_ASSERT_FAIL(ret, "Expected <0 for invalid dev name, ret=%d",
+			     ret);
+	return TEST_SUCCESS;
+}
+
+static int
+test_emu_dev_configure(void)
+{
+	int ret;
+	struct rte_emudev_info dev_conf;
+	struct rte_iavf_emu_config iavf_conf_set = {.qp_num = 1};
+	struct rte_iavf_emu_config iavf_conf_get = {0};
+
+	rte_emudev_stop(test_dev_id);
+
+	/* Check invalid configuration */
+	ret = rte_emudev_configure(test_dev_id, NULL);
+	RTE_TEST_ASSERT(ret == -EINVAL,
+			"Null configure; Expected -EINVAL, got %d", ret);
+
+	/* Valid configuration test */
+	dev_conf.dev_priv = (rte_emudev_obj_t)&iavf_conf_get;
+	ret = rte_emudev_get_dev_info(test_dev_id, &dev_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret,
+				"Failed to obtain emudev configuration (%d)",
+				ret);
+	dev_conf.dev_priv = (rte_emudev_obj_t)&iavf_conf_set;
+	ret = rte_emudev_configure(test_dev_id, &dev_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure emudev (%d)", ret);
+
+	memset(&iavf_conf_get, 0, sizeof(iavf_conf_get));
+	dev_conf.dev_priv = (rte_emudev_obj_t)&iavf_conf_get;
+	ret = rte_emudev_get_dev_info(test_dev_id, &dev_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret,
+				"Failed to obtain emudev configuration (%d)",
+				ret);
+
+	RTE_TEST_ASSERT_EQUAL(iavf_conf_set.qp_num,
+			      iavf_conf_get.qp_num,
+			      "Configuration test failed; num_queues (%d)(%d)",
+			      iavf_conf_set.qp_num,
+			      iavf_conf_get.qp_num);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_emu_dev_start_stop(void)
+{
+	int ret;
+	ret = rte_emudev_start(test_dev_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to start emudev (%d)", ret);
+
+	rte_emudev_stop(test_dev_id);
+
+	return TEST_SUCCESS;
+}
+
+int iavf_emu_selftest(uint16_t dev_id)
+{
+	test_dev_id = dev_id;
+	testsuite_setup();
+
+	EMUDEV_TEST_RUN(NULL, NULL, test_emu_dev_count);
+	EMUDEV_TEST_RUN(NULL, NULL, test_emu_dev_get_dev_id);
+	EMUDEV_TEST_RUN(NULL, NULL, test_emu_dev_configure);
+	EMUDEV_TEST_RUN(NULL, NULL, test_emu_dev_start_stop);
+
+	testsuite_teardown();
+
+	EMU_IAVF_LOG(INFO, "Total tests   : %d\n", total);
+	EMU_IAVF_LOG(INFO, "Passed        : %d\n", passed);
+	EMU_IAVF_LOG(INFO, "Failed        : %d\n", failed);
+	EMU_IAVF_LOG(INFO, "Not supported : %d\n", unsupported);
+
+	if (failed)
+		return -1;
+
+	return 0;
+}
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
index 3cab2226b7..613783e407 100644
--- a/drivers/emu/iavf/meson.build
+++ b/drivers/emu/iavf/meson.build
@@ -10,7 +10,7 @@ endif
 ext_deps += librt
 
 sources = files('iavf_emu.c', 'iavf_vfio_user.c',
-	'iavf_emudev.c')
+	'iavf_emu_test.c', 'iavf_emudev.c')
 
 deps += ['bus_vdev', 'emudev', 'vfio_user', 'common_iavf']
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH 8/8] doc: update release notes for iavf emudev driver
  2020-12-18  7:47 [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver Chenbo Xia
                   ` (6 preceding siblings ...)
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 7/8] test/emudev: introduce functional test Chenbo Xia
@ 2020-12-18  7:47 ` Chenbo Xia
  2020-12-18  9:53 ` [dpdk-dev] [PATCH 0/8] Introduce emudev library and " David Marchand
  2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-18  7:47 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

Update release notes for emulated iavf driver.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
---
 doc/guides/rel_notes/release_21_02.rst | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/doc/guides/rel_notes/release_21_02.rst b/doc/guides/rel_notes/release_21_02.rst
index 3d26b6b580..b310b67b7d 100644
--- a/doc/guides/rel_notes/release_21_02.rst
+++ b/doc/guides/rel_notes/release_21_02.rst
@@ -67,7 +67,7 @@ New Features
 
   See :doc:`../prog_guide/vfio_user_lib` for more information.
 
-* **Added emudev Library.**
+* **Added emudev Library and first emudev driver.**
 
   Added an experimental library ``librte_emudev`` to provide device abstraction
   for an emulated device.
@@ -77,6 +77,10 @@ New Features
   crypto and etc.). It can be attached to another data path driver (e.g, ethdev
   driver) to leverage the high performance of DPDK data path driver.
 
+  The first emudev driver is also introduced, which emulates software iavf
+  devices. This driver emulates all iavf device behavior except data path
+  handling.
+
   See :doc:`../prog_guide/emudev` for more information.
 
 Removed Items
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver
  2020-12-18  7:47 [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver Chenbo Xia
                   ` (7 preceding siblings ...)
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 8/8] doc: update release notes for iavf emudev driver Chenbo Xia
@ 2020-12-18  9:53 ` David Marchand
  2020-12-19  6:11   ` Xia, Chenbo
  2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
  9 siblings, 1 reply; 44+ messages in thread
From: David Marchand @ 2020-12-18  9:53 UTC (permalink / raw)
  To: Chenbo Xia
  Cc: dev, Thomas Monjalon, Stephen Hemminger, Liang, Cunming,
	xiuchun.lu, miao.li, Jingjing Wu

On Fri, Dec 18, 2020 at 9:02 AM Chenbo Xia <chenbo.xia@intel.com> wrote:
>
> This series introduces a new device abstraction called emudev for emulated
> devices. A new library (librte_emudev) is implemented. The first emudev
> driver is also introduced, which emulates Intel Adaptive Virtual Function
> (iavf) as a software network device.
>
> This series has a dependency on librte_vfio_user patch series:
> http://patchwork.dpdk.org/cover/85389/
>
> Background & Motivation
> -----------------------
> The disaggregated/multi-process QEMU is using VFIO-over-socket/vfio-user
> as the main transport mechanism to disaggregate IO services from QEMU.
> Therefore, librte_vfio_user is introduced in DPDK to accommodate
> emulated devices for high performance I/O. Although vfio-user library
> provides possibility of emulating devices in DPDK, DPDK does not have
> a device abstraction for emulated devices. A good device abstraction will
> be useful for applications or high performance data path driver. With
> this consideration, emudev library is designed and implemented. It also
> make it possbile to keep modular design on emulated devices by implementing
> data path related logic in a standalone driver (e.g., an ethdev driver)
> and keeps the unrelated logic in the emudev driver.

Since you mention performance, how does it compare to vhost-user/virtio?


-- 
David Marchand


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver
  2020-12-18  9:53 ` [dpdk-dev] [PATCH 0/8] Introduce emudev library and " David Marchand
@ 2020-12-19  6:11   ` Xia, Chenbo
  2020-12-21  9:52     ` Maxime Coquelin
  0 siblings, 1 reply; 44+ messages in thread
From: Xia, Chenbo @ 2020-12-19  6:11 UTC (permalink / raw)
  To: David Marchand
  Cc: dev, Thomas Monjalon, Stephen Hemminger, Liang, Cunming, Lu,
	Xiuchun, Li, Miao, Wu, Jingjing

Hi David,

> -----Original Message-----
> From: David Marchand <david.marchand@redhat.com>
> Sent: Friday, December 18, 2020 5:54 PM
> To: Xia, Chenbo <chenbo.xia@intel.com>
> Cc: dev <dev@dpdk.org>; Thomas Monjalon <thomas@monjalon.net>; Stephen
> Hemminger <stephen@networkplumber.org>; Liang, Cunming
> <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li, Miao
> <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>
> Subject: Re: [PATCH 0/8] Introduce emudev library and iavf emudev driver
> 
> On Fri, Dec 18, 2020 at 9:02 AM Chenbo Xia <chenbo.xia@intel.com> wrote:
> >
> > This series introduces a new device abstraction called emudev for
> emulated
> > devices. A new library (librte_emudev) is implemented. The first emudev
> > driver is also introduced, which emulates Intel Adaptive Virtual
> Function
> > (iavf) as a software network device.
> >
> > This series has a dependency on librte_vfio_user patch series:
> > http://patchwork.dpdk.org/cover/85389/
> >
> > Background & Motivation
> > -----------------------
> > The disaggregated/multi-process QEMU is using VFIO-over-socket/vfio-user
> > as the main transport mechanism to disaggregate IO services from QEMU.
> > Therefore, librte_vfio_user is introduced in DPDK to accommodate
> > emulated devices for high performance I/O. Although vfio-user library
> > provides possibility of emulating devices in DPDK, DPDK does not have
> > a device abstraction for emulated devices. A good device abstraction
> will
> > be useful for applications or high performance data path driver. With
> > this consideration, emudev library is designed and implemented. It also
> > make it possbile to keep modular design on emulated devices by
> implementing
> > data path related logic in a standalone driver (e.g., an ethdev driver)
> > and keeps the unrelated logic in the emudev driver.
> 
> Since you mention performance, how does it compare to vhost-user/virtio?

I think it depends on the device specification (i.e., how complex its data path
handling is). A first try on iavf spec shows better performance than virtio
in our simple tests.

Thanks!
Chenbo

> 
> 
> --
> David Marchand


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v2 0/8] Introduce emudev library and iavf emudev driver
  2020-12-18  7:47 [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver Chenbo Xia
                   ` (8 preceding siblings ...)
  2020-12-18  9:53 ` [dpdk-dev] [PATCH 0/8] Introduce emudev library and " David Marchand
@ 2020-12-19  6:27 ` Chenbo Xia
  2020-12-19  6:27   ` [dpdk-dev] [PATCH v2 1/8] lib: introduce emudev library Chenbo Xia
                     ` (9 more replies)
  9 siblings, 10 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-19  6:27 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This series introduces a new device abstraction called emudev for emulated
devices. A new library (librte_emudev) is implemented. The first emudev
driver is also introduced, which emulates Intel Adaptive Virtual Function
(iavf) as a software network device.

This series has a dependency on librte_vfio_user patch series:
http://patchwork.dpdk.org/cover/85389/

Background & Motivation 
-----------------------
The disaggregated/multi-process QEMU is using VFIO-over-socket/vfio-user
as the main transport mechanism to disaggregate IO services from QEMU.
Therefore, librte_vfio_user is introduced in DPDK to accommodate
emulated devices for high performance I/O. Although vfio-user library
provides possibility of emulating devices in DPDK, DPDK does not have
a device abstraction for emulated devices. A good device abstraction will
be useful for applications or high performance data path driver. With
this consideration, emudev library is designed and implemented. It also
make it possbile to keep modular design on emulated devices by implementing
data path related logic in a standalone driver (e.g., an ethdev driver)
and keeps the unrelated logic in the emudev driver.

Design overview
---------------

                    +---------------------------------------+
                    |   +---------------+    +-----------+  |
                    |   |  iavf_emudev  |<-->| data path |  |
                    |   |    driver     |    |   driver  |  |
                    |   +---------------+    +-----------+  |
                    |           |                           |
                    | --------------------------- VDEV BUS  |
                    |           |                           |
                    |   +---------------+                   |
+--------------+    |   | vdev:         |                   |
| +----------+ |    |   | /path/to/vfio |                   |
| | Generic  | |    |   +---------------+                   |
| | vfio-dev | |    |           |                           |
| +----------+ |    |           |                           |
| +----------+ |    |      +----------+                     |
| | vfio-user| |    |      | vfio-user|                     |
| | client   | |<---|----->| server   |                     |
| +----------+ |    |      +----------+                     |
| QEMU/DPDK    |    | DPDK                                  |
+--------------+    +---------------------------------------+

- Generic vfio-dev/vfio-user client/vfio-user server
  Above concepts are all introduced in librte_vfio_user patch series:
  http://patchwork.dpdk.org/cover/85389/

- vdev:/path/to/vfio.
  It binds to vdev bus driver. The vdev device is defined by DPDK applications
  through command line as '--vdev=emu_iavf, path=/path/to/socket' in iavf_emudev
  case. Parameters in command line include device name (emu_iavf) which is used
  to identify corresponding driver (in this case, iavf_emudev driver),
  path=/path/to/socket which is used to open the transport interface to vfio-user
  client in QEMU/DPDK.

- data path driver.
  The data path handling is splited to another standalone driver for modular
  design.

Why not rawdev for emulated device
----------------------------------
Instead of introducing new class emudev, emulated device could be presented as rawdev.
However, existing rawdev APIs cannot meet the requirements of emulated device. There are
three API categories for emudev. They are emudev device lifecycle management, backend
facing APIs, and emudev device provider facing APIs respectively. Existing rawdev APIs
could only cover lifecycle management APIs and some of backend facing APIs. Other APIs,
even if added to rawdev API are not required by other rawdev applications.

----------------------------------
v2:
 - fix driver meson build file


Chenbo Xia (8):
  lib: introduce emudev library
  doc: add emudev library guide
  emu: introduce emulated iavf driver
  emu/iavf: add vfio-user device register and unregister
  emu/iavf: add resource management and internal logic of iavf
  emu/iavf: add emudev operations to fit in emudev framework
  test/emudev: introduce functional test
  doc: update release notes for iavf emudev driver

 MAINTAINERS                            |   12 +
 app/test/meson.build                   |    5 +-
 app/test/test_emudev.c                 |   29 +
 doc/guides/prog_guide/emudev.rst       |  122 +++
 doc/guides/prog_guide/index.rst        |    1 +
 doc/guides/rel_notes/release_21_02.rst |   16 +
 drivers/emu/iavf/iavf_emu.c            |  250 ++++++
 drivers/emu/iavf/iavf_emu_internal.h   |   69 ++
 drivers/emu/iavf/iavf_emu_test.c       |  174 ++++
 drivers/emu/iavf/iavf_emudev.c         |  237 ++++++
 drivers/emu/iavf/iavf_vfio_user.c      | 1053 ++++++++++++++++++++++++
 drivers/emu/iavf/iavf_vfio_user.h      |   57 ++
 drivers/emu/iavf/meson.build           |   17 +
 drivers/emu/iavf/rte_iavf_emu.h        |  119 +++
 drivers/emu/iavf/version.map           |    3 +
 drivers/emu/meson.build                |    6 +
 drivers/meson.build                    |    1 +
 lib/librte_emudev/meson.build          |    5 +
 lib/librte_emudev/rte_emudev.c         |  486 +++++++++++
 lib/librte_emudev/rte_emudev.h         |  410 +++++++++
 lib/librte_emudev/rte_emudev_vdev.h    |   53 ++
 lib/librte_emudev/version.map          |   27 +
 lib/meson.build                        |    2 +-
 23 files changed, 3152 insertions(+), 2 deletions(-)
 create mode 100644 app/test/test_emudev.c
 create mode 100644 doc/guides/prog_guide/emudev.rst
 create mode 100644 drivers/emu/iavf/iavf_emu.c
 create mode 100644 drivers/emu/iavf/iavf_emu_internal.h
 create mode 100644 drivers/emu/iavf/iavf_emu_test.c
 create mode 100644 drivers/emu/iavf/iavf_emudev.c
 create mode 100644 drivers/emu/iavf/iavf_vfio_user.c
 create mode 100644 drivers/emu/iavf/iavf_vfio_user.h
 create mode 100644 drivers/emu/iavf/meson.build
 create mode 100644 drivers/emu/iavf/rte_iavf_emu.h
 create mode 100644 drivers/emu/iavf/version.map
 create mode 100644 drivers/emu/meson.build
 create mode 100644 lib/librte_emudev/meson.build
 create mode 100644 lib/librte_emudev/rte_emudev.c
 create mode 100644 lib/librte_emudev/rte_emudev.h
 create mode 100644 lib/librte_emudev/rte_emudev_vdev.h
 create mode 100644 lib/librte_emudev/version.map

-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v2 1/8] lib: introduce emudev library
  2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
@ 2020-12-19  6:27   ` Chenbo Xia
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 2/8] doc: add emudev library guide Chenbo Xia
                     ` (8 subsequent siblings)
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-19  6:27 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This patch introduces the emudev library. Emudev library is used
to abstract an emulated device, whose type could be general
(e.g., network, crypto and etc.). Several device-level APIs are
implemented to use or manipulate the device. It can be attached
to another data path driver (e.g., ethdev driver) to plug in its
high performance data path.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
Signed-off-by: Miao Li <miao.li@intel.com>
---
 MAINTAINERS                         |   5 +
 lib/librte_emudev/meson.build       |   5 +
 lib/librte_emudev/rte_emudev.c      | 486 ++++++++++++++++++++++++++++
 lib/librte_emudev/rte_emudev.h      | 410 +++++++++++++++++++++++
 lib/librte_emudev/rte_emudev_vdev.h |  53 +++
 lib/librte_emudev/version.map       |  27 ++
 lib/meson.build                     |   2 +-
 7 files changed, 987 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_emudev/meson.build
 create mode 100644 lib/librte_emudev/rte_emudev.c
 create mode 100644 lib/librte_emudev/rte_emudev.h
 create mode 100644 lib/librte_emudev/rte_emudev_vdev.h
 create mode 100644 lib/librte_emudev/version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index 5fb4880758..1b395e181d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1545,6 +1545,11 @@ M: Chenbo Xia <chenbo.xia@intel.com>
 M: Xiuchun Lu <xiuchun.lu@intel.com>
 F: lib/librte_vfio_user/
 
+Emudev - EXPERIMENTAL
+M: Chenbo Xia <chenbo.xia@intel.com>
+M: Xiuchun Lu <xiuchun.lu@intel.com>
+F: lib/librte_emudev/
+
 Test Applications
 -----------------
 
diff --git a/lib/librte_emudev/meson.build b/lib/librte_emudev/meson.build
new file mode 100644
index 0000000000..4e16cecbaf
--- /dev/null
+++ b/lib/librte_emudev/meson.build
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation
+
+sources = files('rte_emudev.c')
+headers = files('rte_emudev.h', 'rte_emudev_vdev.h')
diff --git a/lib/librte_emudev/rte_emudev.c b/lib/librte_emudev/rte_emudev.c
new file mode 100644
index 0000000000..2bbf3970d8
--- /dev/null
+++ b/lib/librte_emudev/rte_emudev.c
@@ -0,0 +1,486 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <string.h>
+
+#include <rte_malloc.h>
+#include <rte_memcpy.h>
+
+#include "rte_emudev.h"
+
+#define RTE_MAX_EMU_DEV 1024
+struct rte_emudev rte_emu_devices[RTE_MAX_EMU_DEV];
+
+static struct rte_emudev_global emu_dev_globals = {
+	.nb_devs = 0
+};
+
+static inline uint16_t rte_emu_alloc_dev_id(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < RTE_MAX_EMU_DEV; i++) {
+		if (rte_emu_devices[i].name[0] == '\0')
+			return i;
+	}
+	return RTE_MAX_EMU_DEV;
+}
+
+uint8_t
+rte_emudev_count(void)
+{
+	return emu_dev_globals.nb_devs;
+}
+
+int
+rte_emudev_get_dev_id(const char *name)
+{
+	uint16_t i;
+
+	if (!name) {
+		RTE_EMUDEV_LOG(ERR, "Failed to get device ID: "
+			"NULL device name\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < emu_dev_globals.nb_devs; i++)
+		if (!strncmp(rte_emu_devices[i].name, name,
+			RTE_EMU_NAME_MAX_LEN))
+			return i;
+
+	return -ENODEV;
+}
+
+struct rte_emudev *
+rte_emudev_allocate(const char *name)
+{
+	uint16_t dev_id;
+	struct rte_emudev *emu_dev = NULL;
+	size_t name_len;
+
+	if (!name) {
+		RTE_EMUDEV_LOG(ERR, "Failed to allocate emudev: "
+			"NULL device name\n");
+		return NULL;
+	}
+
+	name_len = strnlen(name, RTE_EMU_NAME_MAX_LEN);
+	if (!name_len) {
+		RTE_EMUDEV_LOG(ERR, "Emulated device name has zero length\n");
+		return NULL;
+	}
+
+	if (name_len >= RTE_EMU_NAME_MAX_LEN) {
+		RTE_EMUDEV_LOG(ERR, "Emulated device name too long\n");
+		return NULL;
+	}
+
+	if (rte_emudev_allocated(name) != NULL) {
+		RTE_EMUDEV_LOG(ERR,
+			"Emulated device with name %s already exists\n",
+			name);
+		return NULL;
+	}
+
+	dev_id = rte_emu_alloc_dev_id();
+	if (dev_id == RTE_MAX_EMU_DEV) {
+		RTE_EMUDEV_LOG(ERR, "Reached max number of Emulated device\n");
+		return NULL;
+	}
+
+	emu_dev = &rte_emu_devices[dev_id];
+	strncpy(emu_dev->name, name, sizeof(emu_dev->name));
+	emu_dev->dev_id = dev_id;
+	emu_dev_globals.nb_devs++;
+
+	return emu_dev;
+}
+
+int
+rte_emudev_release(struct rte_emudev *dev)
+{
+	if (!dev)
+		return -EINVAL;
+
+	if (dev->priv_data) {
+		rte_free(dev->priv_data);
+		dev->priv_data = NULL;
+	}
+
+	memset(dev, 0, sizeof(*dev));
+	emu_dev_globals.nb_devs--;
+	return 0;
+}
+
+struct rte_emudev *
+rte_emudev_allocated(const char *name)
+{
+	unsigned int i;
+
+	if (!name) {
+		RTE_EMUDEV_LOG(ERR, "Failed to find emudev: "
+			"NULL device name\n");
+		return NULL;
+	}
+
+	for (i = 0; i < RTE_MAX_EMU_DEV; i++) {
+		if (rte_emu_devices[i].dev_ops != NULL &&
+		    strcmp(rte_emu_devices[i].device->name, name) == 0)
+			return &rte_emu_devices[i];
+	}
+	return NULL;
+}
+
+int rte_emudev_is_valid_id(uint16_t dev_id)
+{
+	if (dev_id >= RTE_MAX_EMU_DEV ||
+		rte_emu_devices[dev_id].name[0] == '\0')
+		return 0;
+	else
+		return 1;
+}
+
+int
+rte_emudev_selftest(uint16_t dev_id)
+{
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	struct rte_emudev *dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_selftest, -ENOTSUP);
+
+	return (*dev->dev_ops->dev_selftest)(dev_id);
+}
+
+
+int rte_emudev_start(uint16_t dev_id)
+{
+	struct rte_emudev *dev;
+	int ret;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (dev->started) {
+		RTE_EMUDEV_LOG(ERR, "Device %u already started\n", dev_id);
+		return 0;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_start, -ENOTSUP);
+
+	ret = (*dev->dev_ops->dev_start)(dev);
+	if (ret)
+		return ret;
+
+	dev->started = 1;
+	return 0;
+}
+
+void rte_emudev_stop(uint16_t dev_id)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID(dev_id);
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (!dev->started) {
+		RTE_EMUDEV_LOG(ERR, "Device %u already stopped\n", dev_id);
+		return;
+	}
+
+	RTE_FUNC_PTR_OR_RET(*dev->dev_ops->dev_stop);
+
+	(*dev->dev_ops->dev_stop)(dev);
+
+	dev->started = 0;
+}
+
+int rte_emudev_configure(uint16_t dev_id, struct rte_emudev_info *dev_conf)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (!dev_conf)
+		return -EINVAL;
+
+	if (dev->started) {
+		RTE_EMUDEV_LOG(ERR, "Device %u must be stopped "
+			"before configure\n", dev_id);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_configure, -ENOTSUP);
+
+	if (strcmp(dev_conf->dev_type, dev->dev_info.dev_type)) {
+		RTE_EMUDEV_LOG(ERR, "Wrong device type to configure"
+			" for device %u\n", dev_id);
+		return -EINVAL;
+	}
+
+	return (*dev->dev_ops->dev_configure)(dev, dev_conf);
+}
+
+int rte_emudev_close(uint16_t dev_id)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (dev->started) {
+		RTE_EMUDEV_LOG(ERR, "Device %u must be stopped "
+			"before close\n", dev_id);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_close, -ENOTSUP);
+
+	(*dev->dev_ops->dev_close)(dev);
+
+	rte_emudev_release(dev);
+	return 0;
+}
+
+int rte_emudev_subscribe_event(uint16_t dev_id,
+		const rte_emudev_event_chnl_t ev_chnl)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!ev_chnl) {
+		RTE_EMUDEV_LOG(ERR, "Failed to subscribe because of NULL"
+			" event channel for device %u\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->subscribe_event, -ENOTSUP);
+
+	return (*dev->dev_ops->subscribe_event)(dev, ev_chnl);
+}
+
+int rte_emudev_unsubscribe_event(uint16_t dev_id,
+		const rte_emudev_event_chnl_t ev_chnl)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!ev_chnl) {
+		RTE_EMUDEV_LOG(ERR, "Failed to unsubscribe because of NULL"
+			" event channel for device %u\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->unsubscribe_event, -ENOTSUP);
+
+	return (*dev->dev_ops->unsubscribe_event)(dev, ev_chnl);
+}
+
+int rte_emudev_get_dev_info(uint16_t dev_id, struct rte_emudev_info *info)
+{
+	struct rte_emudev *dev;
+	struct rte_emudev_info *dev_info;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!info) {
+		RTE_EMUDEV_LOG(ERR, "NULL device info for device %u\n",
+			dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+	dev_info = &dev->dev_info;
+
+	strcpy(info->dev_type, dev_info->dev_type);
+	info->max_qp_num = dev_info->max_qp_num;
+	info->region_num = dev_info->region_num;
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_info_get, -ENOTSUP);
+
+	return (*dev->dev_ops->dev_info_get)(dev, info->dev_priv);
+}
+
+int rte_emudev_get_mem_table(uint16_t dev_id, rte_emudev_mem_table_t *tb)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!tb) {
+		RTE_EMUDEV_LOG(ERR, "NULL memory table for device %u\n",
+			dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_mem_table, -ENOTSUP);
+
+	return (*dev->dev_ops->get_mem_table)(dev, tb);
+}
+
+int rte_emudev_get_queue_info(uint16_t dev_id, uint32_t queue,
+	struct rte_emudev_q_info *info)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!info) {
+		RTE_EMUDEV_LOG(ERR, "NULL queue info for queue %d"
+			" of device %u\n", queue, dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (queue >= dev->dev_info.max_qp_num * 2) {
+		RTE_EMUDEV_LOG(ERR, "Queue index of device %u exceeds max\n",
+			dev_id);
+		return -EINVAL;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_queue_info, -ENOTSUP);
+
+	memset(info, 0, sizeof(*info));
+
+	return (*dev->dev_ops->get_queue_info)(dev, queue, info);
+}
+
+int rte_emudev_get_irq_info(uint16_t dev_id, uint32_t vector,
+	struct rte_emudev_irq_info *info)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!info) {
+		RTE_EMUDEV_LOG(ERR, "NULL irq info for vector %u"
+			" of device %u\n", vector, dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_irq_info, -ENOTSUP);
+
+	memset(info, 0, sizeof(*info));
+
+	return (*dev->dev_ops->get_irq_info)(dev, vector, info);
+}
+
+int rte_emudev_get_db_info(uint16_t dev_id, uint32_t doorbell,
+	struct rte_emudev_db_info *info)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!info) {
+		RTE_EMUDEV_LOG(ERR, "NULL doorbell info of device %u"
+			" for id %u\n", dev_id, doorbell);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_db_info, -ENOTSUP);
+
+	memset(info, 0, sizeof(*info));
+
+	return (*dev->dev_ops->get_db_info)(dev, doorbell, info);
+}
+
+int rte_emudev_set_attr(uint16_t dev_id, const char *attr_name,
+	rte_emudev_attr_t attr)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!attr_name) {
+		RTE_EMUDEV_LOG(ERR, "NULL attribute name of device %u "
+			"for set\n", dev_id);
+		return -EINVAL;
+	}
+
+	if (!attr) {
+		RTE_EMUDEV_LOG(ERR, "NULL attribute of device %u "
+			"for set_attr\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->set_attr, -ENOTSUP);
+
+	return (*dev->dev_ops->set_attr)(dev, attr_name, attr);
+}
+
+int rte_emudev_get_attr(uint16_t dev_id, const char *attr_name,
+	rte_emudev_attr_t attr)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!attr_name) {
+		RTE_EMUDEV_LOG(ERR, "NULL attribute name of device %u "
+			"for get\n", dev_id);
+		return -EINVAL;
+	}
+
+	if (!attr) {
+		RTE_EMUDEV_LOG(ERR, "NULL attribute of device %u "
+			"for get_attr\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_attr, -ENOTSUP);
+
+	return (*dev->dev_ops->get_attr)(dev, attr_name, attr);
+}
+
+int rte_emudev_region_map(uint16_t dev_id, uint32_t index,
+	uint64_t *region_size, uint64_t *base_addr)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!region_size || !base_addr)
+		return -EINVAL;
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (index >= dev->dev_info.region_num) {
+		RTE_EMUDEV_LOG(ERR, "Wrong region index for device %u\n",
+			dev_id);
+		return -EINVAL;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->region_map, -ENOTSUP);
+
+	memset(region_size, 0, sizeof(*region_size));
+	memset(base_addr, 0, sizeof(*base_addr));
+
+	return (*dev->dev_ops->region_map)(dev, index, region_size,
+		base_addr);
+}
+
+RTE_LOG_REGISTER(rte_emudev_logtype, lib.emudev, INFO);
diff --git a/lib/librte_emudev/rte_emudev.h b/lib/librte_emudev/rte_emudev.h
new file mode 100644
index 0000000000..df31c631b7
--- /dev/null
+++ b/lib/librte_emudev/rte_emudev.h
@@ -0,0 +1,410 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _RTE_EMUDEV_H_
+#define _RTE_EMUDEV_H_
+
+#include <rte_config.h>
+#include <rte_dev.h>
+#include <rte_compat.h>
+
+#define RTE_EMU_NAME_MAX_LEN RTE_DEV_NAME_MAX_LEN
+
+extern int rte_emudev_logtype;
+
+#define RTE_EMUDEV_LOG(level, ...) \
+	rte_log(RTE_LOG_ ## level, rte_emudev_logtype, "" __VA_ARGS__)
+
+/* Macros to check for valid dev id */
+#define RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id) do { \
+	if (!rte_emudev_is_valid_id(dev_id)) { \
+		RTE_EMUDEV_LOG(ERR, "Invalid dev_id=%u\n", dev_id); \
+		return -ENODEV; \
+	} \
+} while (0)
+
+#define RTE_EMU_CHECK_VALID_DEVID(dev_id) do { \
+	if (!rte_emudev_is_valid_id(dev_id)) { \
+		RTE_EMUDEV_LOG(ERR, "Invalid dev_id=%u\n", dev_id); \
+		return; \
+	} \
+} while (0)
+
+typedef void *rte_emudev_obj_t;
+typedef void *rte_emudev_attr_t;
+typedef void *rte_emudev_mem_table_t;
+typedef void *rte_emudev_event_chnl_t;
+
+struct rte_emudev;
+
+/** 
+ * Global structure used for maintaining state
+ * of allocated emu devices
+ */
+struct rte_emudev_global {
+	uint8_t nb_devs;	/**< Number of devices found */
+};
+
+struct rte_emudev_info {
+	char dev_type[RTE_EMU_NAME_MAX_LEN];
+	uint32_t region_num;
+	uint32_t max_qp_num;
+	rte_emudev_obj_t dev_priv;
+};
+
+struct rte_emudev_q_info {
+	uint64_t base;
+	uint64_t size;
+	int doorbell_id;
+	int irq_vector;
+};
+
+struct rte_emudev_irq_info {
+	uint32_t vector;
+	bool enable;
+	int eventfd;
+};
+
+struct rte_emudev_db_info {
+	uint32_t id;
+	uint32_t flag;
+#define RTE_EMUDEV_DB_FD	(0x1 << 0)
+#define RTE_EMUDEV_DB_MEM	(0x1 << 1)
+	union {
+		int eventfd;
+		struct {
+			uint64_t base;
+			uint64_t size;
+		} mem;
+	} data;
+};
+
+struct rte_emudev_ops {
+	int (*dev_start)(struct rte_emudev *dev);
+	void (*dev_stop)(struct rte_emudev *dev);
+	int (*dev_configure)(struct rte_emudev *dev,
+		struct rte_emudev_info *conf);
+	int (*dev_close)(struct rte_emudev *dev);
+	int (*dev_info_get)(struct rte_emudev *dev, rte_emudev_obj_t info);
+	int (*subscribe_event)(struct rte_emudev *dev,
+		rte_emudev_event_chnl_t ev_chnl);
+	int (*unsubscribe_event)(struct rte_emudev *dev,
+		rte_emudev_event_chnl_t ev_chnl);
+	int (*get_mem_table)(struct rte_emudev *dev,
+		rte_emudev_mem_table_t *tb);
+	int (*get_queue_info)(struct rte_emudev *dev, uint32_t queue,
+		struct rte_emudev_q_info *info);
+	int (*get_irq_info)(struct rte_emudev *dev, uint32_t vector,
+		struct rte_emudev_irq_info *info);
+	int (*get_db_info)(struct rte_emudev *dev, uint32_t doorbell,
+		struct rte_emudev_db_info *info);
+	int (*get_attr)(struct rte_emudev *dev, const char *attr_name,
+		rte_emudev_attr_t attr);
+	int (*set_attr)(struct rte_emudev *dev, const char *attr_name,
+		rte_emudev_attr_t attr);
+	int (*region_map)(struct rte_emudev *dev, uint32_t index,
+		uint64_t *region_size, uint64_t *base_addr);
+	int (*dev_selftest)(uint16_t dev_id);
+};
+
+struct rte_emudev {
+	char name[RTE_EMU_NAME_MAX_LEN];
+	uint16_t dev_id;
+	int numa_node;
+	int started;
+	struct rte_device *device;
+	struct rte_emudev_info dev_info;
+	const struct rte_emudev_ops *dev_ops;
+	void *priv_data;
+	void *backend_priv;
+} __rte_cache_aligned;
+
+/**
+ * Note that 'rte_emudev_allocate', 'rte_emudev_release' and
+ * 'rte_emudev_allocated' should be called by emulated device
+ * provider.
+ */
+
+/**
+ * Allocate a new emudev for an emulation device and returns the pointer
+ * to the emudev.
+ *
+ * @param name
+ *   Name of the emudev
+ * @return
+ *   Pointer to rte_emudev on success, NULL on failure
+ */
+__rte_experimental
+struct rte_emudev *rte_emudev_allocate(const char *name);
+
+/**
+ * Release the emudev.
+ *
+ * @param dev
+ *   The emulated device
+ * @return
+ *   - 0: Success, device release
+ *   - <0: Failure on release
+ */
+__rte_experimental
+int rte_emudev_release(struct rte_emudev *dev);
+
+/**
+ * Find an emudev using name.
+ *
+ * @param name
+ *   Name of the emudev
+ * @return
+ *   Pointer to rte_emudev on success, NULL on failure
+ */
+__rte_experimental
+struct rte_emudev *rte_emudev_allocated(const char *name);
+
+/**
+ * Start an emulation device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @return
+ *   - 0: Success, device start
+ *   - <0: Failure on start
+ */
+__rte_experimental
+int rte_emudev_start(uint16_t dev_id);
+
+/**
+ * Stop an emulation device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ */
+__rte_experimental
+void rte_emudev_stop(uint16_t dev_id);
+
+/**
+ * Configure an emulation device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param dev_conf
+ *   Device configure info
+ * @return
+ *   - 0: Success, device configured
+ *   - <0: Failure on configure
+ */
+__rte_experimental
+int rte_emudev_configure(uint16_t dev_id, struct rte_emudev_info *dev_conf);
+
+/**
+ * Close an emulation device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @return
+ *   - 0: Success, device close
+ *   - <0: Failure on close
+ */
+__rte_experimental
+int rte_emudev_close(uint16_t dev_id);
+
+/* Note that below APIs should only be called by back-end (data path) driver */
+
+/**
+ * Back-end driver subscribes events of the emulated device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param ev_chnl
+ *   Event channel that events should be passed to
+ * @return
+ *   - 0: Success, event subscribed
+ *   - <0: Failure on subscribe
+ */
+__rte_experimental
+int rte_emudev_subscribe_event(uint16_t dev_id,
+		const rte_emudev_event_chnl_t ev_chnl);
+
+/**
+ * Back-end driver unsubscribes events of the emulated device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param set
+ *   Event channel that events should be passed to
+ * @return
+ *   - 0: Success, event unsubscribed
+ *   - <0: Failure on unsubscribe
+ */
+__rte_experimental
+int rte_emudev_unsubscribe_event(uint16_t dev_id,
+		const rte_emudev_event_chnl_t ev_chnl);
+
+/**
+ * Get the total number of emulated devices that have been
+ * successfully initialised.
+ *
+ * @return
+ *   The total number of usable emudev.
+ */
+__rte_experimental
+uint8_t rte_emudev_count(void);
+
+/**
+ * Get the device identifier for the named emulated device.
+ *
+ * @param name
+ *   Emulated device name to select the device identifier.
+ *
+ * @return
+ *   - 0: Success, emulated device identifier returned
+ *   - <0: Failure on unsubscribe
+ */
+__rte_experimental
+int rte_emudev_get_dev_id(const char *name);
+
+/**
+ * Back-end driver gets the device info of the emulated device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @return
+ *   - 0: Success, emulated device info updated
+ *   - <0: Failure on get device information
+ */
+__rte_experimental
+int rte_emudev_get_dev_info(uint16_t dev_id, struct rte_emudev_info *info);
+
+/**
+ * Get the memory table content and operations of the emulated device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @return
+ *   - 0: Success, memory table of emulated device updated
+ *   - <0: Failure on get memory table
+ */
+__rte_experimental
+int rte_emudev_get_mem_table(uint16_t dev_id, rte_emudev_mem_table_t *tb);
+
+/**
+ * Get queue info of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param queue
+ *   Queue ID of emudev
+ * @return
+ *   - 0: Success, queue information of emulated device updated
+ *   - <0: Failure on get queue information
+ */
+__rte_experimental
+int rte_emudev_get_queue_info(uint16_t dev_id, uint32_t queue,
+	struct rte_emudev_q_info *info);
+
+/**
+ * Get irq info of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param vector
+ *   Interrupt vector
+ * @return
+ *   - 0: Success, irq information of emulated device updated
+ *   - <0: Failure on get irq information
+ */
+__rte_experimental
+int rte_emudev_get_irq_info(uint16_t dev_id, uint32_t vector,
+	struct rte_emudev_irq_info *info);
+
+/**
+ * Get doorbell info of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param doorbell
+ *   Doorbell ID
+ * @return
+ *   - 0: Success, doorbell information of emulated device updated
+ *   - <0: Failure on get doorbell information
+ */
+__rte_experimental
+int rte_emudev_get_db_info(uint16_t dev_id, uint32_t doorbell,
+	struct rte_emudev_db_info *info);
+
+/**
+ * Set attribute of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param attr_name
+ *   Opaque object representing an attribute in implementation.
+ * @param attr
+ *   Pointer to attribute
+ * @return
+ *   - 0: Success, attribute set
+ *   - <0: Failure on attribute set
+ */
+__rte_experimental
+int rte_emudev_set_attr(uint16_t dev_id, const char *attr_name,
+	rte_emudev_attr_t attr);
+
+/**
+ * Get attribute of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param attr_name
+ *   Opaque object representing an attribute in implementation.
+ * @return
+ *   - 0: Success, attribute of emulated device updated
+ *   - <0: Failure on attribute get
+ */
+__rte_experimental
+int rte_emudev_get_attr(uint16_t dev_id, const char *attr_name,
+	rte_emudev_attr_t attr);
+
+/**
+ * Back-end driver maps a region to the emulated device.
+ * Region name identifies the meaning of the region and the emulated
+ * device and the back-end driver should have the same definition of
+ * region name and its meaning.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param index
+ *   Region index
+ * @param attr
+ *   Pointer to attribute
+ * @return
+ *   - 0: Success, region mapped
+ *   - <0: Failure on region map
+ */
+__rte_experimental
+int rte_emudev_region_map(uint16_t dev_id, uint32_t index,
+	uint64_t *region_size, uint64_t *base_addr);
+
+/**
+ * Trigger the emudev self test.
+ *
+ * @param dev_id
+ *   The identifier of the device
+ * @return
+ *   - 0: Selftest successful
+ *   - <0: Failure on selftest
+ */
+__rte_experimental
+int rte_emudev_selftest(uint16_t dev_id);
+
+/**
+ * Check if an emudev device ID is valid.
+ *
+ * @param dev_id
+ *   The identifier of the device
+ * @return
+ *   0 on failure, 1 on success
+ */
+__rte_experimental
+int rte_emudev_is_valid_id(uint16_t dev_id);
+
+#endif /* _RTE_EMUDEV_H_ */
diff --git a/lib/librte_emudev/rte_emudev_vdev.h b/lib/librte_emudev/rte_emudev_vdev.h
new file mode 100644
index 0000000000..85f534b4bd
--- /dev/null
+++ b/lib/librte_emudev/rte_emudev_vdev.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _RTE_EMUDEV_VDEV_H_
+#define _RTE_EMUDEV_VDEV_H_
+
+#include <rte_bus_vdev.h>
+#include <rte_malloc.h>
+
+#include "rte_emudev.h"
+
+/**
+ * @internal
+ * Allocates a new emudev instance for an emulated device and
+ * returns the pointer to that instance for the driver to use.
+ *
+ * @param dev
+ *	Pointer to virtual device
+ *
+ * @param private_data_size
+ *	Size of private data structure
+ *
+ * @return
+ *	A pointer to a rte_emudev or NULL if allocation failed.
+ */
+static inline struct rte_emudev *
+rte_emu_vdev_allocate(struct rte_vdev_device *dev, size_t private_data_size)
+{
+	struct rte_emudev *emu_dev;
+	const char *name = rte_vdev_device_name(dev);
+
+	emu_dev = rte_emudev_allocate(name);
+	if (!emu_dev)
+		return NULL;
+
+	if (private_data_size) {
+		emu_dev->priv_data = rte_zmalloc_socket(name,
+			private_data_size, RTE_CACHE_LINE_SIZE,
+			dev->device.numa_node);
+		if (!emu_dev->priv_data) {
+			rte_emudev_release(emu_dev);
+			return NULL;
+		}
+	}
+
+	emu_dev->device = &dev->device;
+	emu_dev->numa_node = dev->device.numa_node;
+
+	return emu_dev;
+}
+
+#endif /* _RTE_EMUDEV_VDEV_H_ */
diff --git a/lib/librte_emudev/version.map b/lib/librte_emudev/version.map
new file mode 100644
index 0000000000..f800b4c21c
--- /dev/null
+++ b/lib/librte_emudev/version.map
@@ -0,0 +1,27 @@
+EXPERIMENTAL {
+	global:
+
+	rte_emudev_allocate;
+	rte_emudev_release;
+	rte_emudev_allocated;
+	rte_emudev_start;
+	rte_emudev_stop;
+	rte_emudev_configure;
+	rte_emudev_close;
+	rte_emudev_subscribe_event;
+	rte_emudev_unsubscribe_event;
+	rte_emudev_count;
+	rte_emudev_get_dev_id;
+	rte_emudev_get_dev_info;
+	rte_emudev_get_mem_table;
+	rte_emudev_get_queue_info;
+	rte_emudev_get_irq_info;
+	rte_emudev_get_db_info;
+	rte_emudev_set_attr;
+	rte_emudev_get_attr;
+	rte_emudev_region_map;
+	rte_emudev_selftest;
+	rte_emudev_is_valid_id;
+
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index b7fbfcc95b..6dd07fb73e 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -28,7 +28,7 @@ libraries = [
 	'rib', 'reorder', 'sched', 'security', 'stack', 'vhost',
 	# ipsec lib depends on net, crypto and security
 	'ipsec',
-	'vfio_user',
+	'vfio_user', 'emudev',
 	#fib lib depends on rib
 	'fib',
 	# add pkt framework libs which use other libs from above
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v2 2/8] doc: add emudev library guide
  2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
  2020-12-19  6:27   ` [dpdk-dev] [PATCH v2 1/8] lib: introduce emudev library Chenbo Xia
@ 2020-12-19  6:28   ` Chenbo Xia
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 3/8] emu: introduce emulated iavf driver Chenbo Xia
                     ` (7 subsequent siblings)
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-19  6:28 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

Add emudev library guide and update release notes.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
---
 doc/guides/prog_guide/emudev.rst       | 122 +++++++++++++++++++++++++
 doc/guides/prog_guide/index.rst        |   1 +
 doc/guides/rel_notes/release_21_02.rst |  12 +++
 3 files changed, 135 insertions(+)
 create mode 100644 doc/guides/prog_guide/emudev.rst

diff --git a/doc/guides/prog_guide/emudev.rst b/doc/guides/prog_guide/emudev.rst
new file mode 100644
index 0000000000..91ad520de7
--- /dev/null
+++ b/doc/guides/prog_guide/emudev.rst
@@ -0,0 +1,122 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2020 Intel Corporation.
+
+Emulated Device Library
+=================
+
+Introduction
+------------
+
+The DPDK Emudev library is an abstraction for emulated device. This library
+provides a generic set of APIs for device provider, data path provider and
+applications to use.
+
+A device provider could be implemented as a driver on vdev bus. It should
+expose itself as an emudev for applications to use. It is responsible for the
+device resource management and the device's internal logic. All specifics of a
+device, except data path handling, should be implemented in the device
+provider. The device provider uses emudev APIs mainly for create/destroy an
+emudev instance. The device provider should also use a tranport to communicate
+with device consumer (e.g., virtual machine monitor or container). A potential
+choice could be vfio-user library, which implements the vfio-user protocol for
+emulating devices outside of a virtual machine monitor.
+
+A data path provider could be implemented as any type of driver on vdev bus.
+If the device you want to emulate is a network device, you could implement
+it as an ethdev driver. It is responsible for all data path handling. The data
+path provider uses emudev APIs mainly for getting device-related information
+from the device provider.
+
+Applications uses emudev APIs for device lifecycle management and configuration.
+
+Design
+------------
+
+Some key objects are designed in emudev.
+
+  ``Regions`` are the device layout exposed to the data path provider.
+
+  ``Queues`` are the data path queues that the data path provider needs. Queue
+  information includes queue base address, queue size, queue-related doorbell
+  and interrupt information.
+
+  ``Memory Table`` is the DMA mapping table. The data path provider could use
+  it to perform DMA read/write on device consumer's memory.
+
+Information of above key objects could be acquired through emudev APIs. The
+following will introduce the emudev APIs which are used by data path provider
+and applications. The APIs for device provider to use are allocate/release APIs
+and will not be listed because it's similar to other device abstraction.
+
+There are five categories of APIs:
+
+1. Lifecycle management
+
+* ``rte_emu_dev_start(dev_id)``
+* ``rte_emu_dev_stop(dev_id)``
+* ``rte_emu_dev_configure(dev_id)``
+* ``rte_emu_dev_close(dev_id)``
+
+  Above APIs are respectively for device start/stop/configure/close and mainly
+  for applications to use.
+
+  ``dev_id`` is the emudev device ID.
+
+2. Notification
+
+* ``rte_emu_subscribe_event(dev_id, ev_chnl)``
+* ``rte_emu_unsubscribe_event(dev_id, ev_chnl)``
+
+  Above APIs are for data path provider and applications to register events.
+  The mechanism of event notification could be different in different device
+  providers. A possbile implementation could be event callbacks.
+
+  ``ev_chnl`` is the event channel pointer. The definition varies between
+  different devices.
+
+3. Region-related
+
+* ``rte_emu_region_map(dev_id, index, region_size, base_addr)``
+* ``rte_emu_get_attr(dev_id, attr_name, attr)``
+* ``rte_emu_set_attr(dev_id, attr_name, attr)``
+
+  Above APIs are for data path provider and applications to read/write regions.
+  ``rte_emu_region_map`` is for directly mapping the region and use the mapped
+  address to read/write it. ``rte_emu_get_attr`` and ``rte_emu_set_attr`` are
+  respectively for getting/setting certain attributes in all regions.
+
+  Applications will set attributes or write regions for device configuration.
+
+  In ``rte_emu_region_map``:
+  - ``index`` is the region index.
+  - ``region_size`` is for saving the size of mapped region.
+  - ``base_addr`` is for saving the address of mapped region.
+
+  In ``rte_emu_get_attr`` and ``rte_emu_set_attr``:
+  - ``attr_name`` is the name of attribute. Note that attribute names are aligned
+  between device provider and data path provider for the same device.
+  - ``attr`` is the attribute value.
+
+4. Queue-related
+
+* ``rte_emu_get_queue_info(dev_id, queue, info)``
+* ``rte_emu_get_irq_info(dev_id, irq, info)``
+* ``rte_emu_get_db_info(dev_id, doorbell, info)``
+
+  Above APIs are for data path provider to get queue/interrupt/doorbell information.
+
+  - ``queue``, ``irq`` and ``doorbell`` are respectively the queue/interrupt/doorbell
+  index.
+  - ``info`` is for saving the queue/interrupt/doorbell info.
+
+5. Direct Memory Access
+
+* ``rte_emu_get_mem_table(dev_id, tb)``
+
+  Above APIs are for data path provider to get the information of DMA memory table.
+  The memory table implementation varies between different devices and memory table
+  operations should better be helper functions exposed by device provider. Because
+  address translation make a difference in data path performance, the memory table
+  implementation should have high efficiency.
+
+  ``tb`` is for saving the DMA memory table.
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index f9847b1058..0ed15a0995 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -71,3 +71,4 @@ Programmer's Guide
     profile_app
     glossary
     vfio_user_lib
+    emudev
diff --git a/doc/guides/rel_notes/release_21_02.rst b/doc/guides/rel_notes/release_21_02.rst
index 6fbb6e8c39..3d26b6b580 100644
--- a/doc/guides/rel_notes/release_21_02.rst
+++ b/doc/guides/rel_notes/release_21_02.rst
@@ -67,6 +67,18 @@ New Features
 
   See :doc:`../prog_guide/vfio_user_lib` for more information.
 
+* **Added emudev Library.**
+
+  Added an experimental library ``librte_emudev`` to provide device abstraction
+  for an emulated device.
+
+  The library abstracts an emulated device and provides several categories of
+  device-level APIs. The specific device type could be general (e.g, network,
+  crypto and etc.). It can be attached to another data path driver (e.g, ethdev
+  driver) to leverage the high performance of DPDK data path driver.
+
+  See :doc:`../prog_guide/emudev` for more information.
+
 Removed Items
 -------------
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v2 3/8] emu: introduce emulated iavf driver
  2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
  2020-12-19  6:27   ` [dpdk-dev] [PATCH v2 1/8] lib: introduce emudev library Chenbo Xia
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 2/8] doc: add emudev library guide Chenbo Xia
@ 2020-12-19  6:28   ` Chenbo Xia
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 4/8] emu/iavf: add vfio-user device register and unregister Chenbo Xia
                     ` (6 subsequent siblings)
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-19  6:28 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This patch introduces emulated iavf driver. It is a vdev driver
emulating all iavf device behavior except data path handling.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
---
 MAINTAINERS                          |   7 +
 drivers/emu/iavf/iavf_emu.c          |  29 ++++
 drivers/emu/iavf/iavf_emu_internal.h |  49 +++++++
 drivers/emu/iavf/iavf_emudev.c       | 207 +++++++++++++++++++++++++++
 drivers/emu/iavf/meson.build         |   8 ++
 drivers/emu/iavf/rte_iavf_emu.h      |  43 ++++++
 drivers/emu/iavf/version.map         |   3 +
 drivers/emu/meson.build              |   6 +
 drivers/meson.build                  |   1 +
 9 files changed, 353 insertions(+)
 create mode 100644 drivers/emu/iavf/iavf_emu.c
 create mode 100644 drivers/emu/iavf/iavf_emu_internal.h
 create mode 100644 drivers/emu/iavf/iavf_emudev.c
 create mode 100644 drivers/emu/iavf/meson.build
 create mode 100644 drivers/emu/iavf/rte_iavf_emu.h
 create mode 100644 drivers/emu/iavf/version.map
 create mode 100644 drivers/emu/meson.build

diff --git a/MAINTAINERS b/MAINTAINERS
index 1b395e181d..bca206ba8f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1275,6 +1275,13 @@ F: doc/guides/rawdevs/ntb.rst
 F: examples/ntb/
 F: doc/guides/sample_app_ug/ntb.rst
 
+Emudev Drivers
+--------------
+
+Intel iavf
+M: Chenbo Xia <chenbo.xia@intel.com>
+M: Xiuchun Lu <xiuchun.lu@intel.com>
+F: drivers/emulation/iavf/
 
 Packet processing
 -----------------
diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
new file mode 100644
index 0000000000..68d2c440e3
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include "iavf_emu_internal.h"
+
+static int iavf_emu_dev_close(struct rte_emudev *dev)
+{
+	struct iavf_emudev *iavf;
+
+	/* For now, we don't support device close when data
+	 * path driver is attached
+	 */
+	if (dev->backend_priv) {
+		EMU_IAVF_LOG(ERR, "Close failed because of "
+			"data path attached\n");
+		return -EPERM;
+	}
+
+	iavf = (struct iavf_emudev *)dev->priv_data;
+	iavf_emu_uninit_device(iavf);
+	dev->priv_data = NULL;
+
+	return 0;
+}
+
+struct rte_emudev_ops emu_iavf_ops = {
+	.dev_close = iavf_emu_dev_close,
+};
diff --git a/drivers/emu/iavf/iavf_emu_internal.h b/drivers/emu/iavf/iavf_emu_internal.h
new file mode 100644
index 0000000000..a726bfe577
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emu_internal.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IAVF_EMU_ITNL_H
+#define _IAVF_EMU_ITNL_H
+
+#include <stdint.h>
+
+#include <rte_log.h>
+
+#include "rte_iavf_emu.h"
+
+extern struct rte_emudev_ops emu_iavf_ops;
+
+extern int emu_iavf_logtype;
+#define EMU_IAVF_LOG(level, ...) \
+	rte_log(RTE_LOG_ ## level, emu_iavf_logtype, "EMU_IAVF: " __VA_ARGS__)
+
+struct iavf_emu_intr_info {
+	int enable;
+	int fd;
+};
+
+struct iavf_emu_intr {
+	uint32_t intr_num;
+	struct iavf_emu_intr_info info[RTE_IAVF_EMU_MAX_INTR];
+};
+
+struct iavf_emu_lanQ {
+	uint16_t db_size;
+	void *doorbell;
+};
+
+struct iavf_emudev {
+	struct rte_emudev *edev;
+	/* Maximum LANQ queue pair that this emulated iavf has */
+	uint16_t max_lanqp;
+	/* Maximum LANQ queue pair number that back-end driver can use */
+	uint16_t max_be_lanqp;
+	unsigned int numa_node;
+	char *sock_addr;
+	struct rte_iavf_emu_mem *mem;
+	struct iavf_emu_intr *intr;
+	struct iavf_emu_lanQ *lanq;
+};
+
+void iavf_emu_uninit_device(struct iavf_emudev *dev);
+#endif
diff --git a/drivers/emu/iavf/iavf_emudev.c b/drivers/emu/iavf/iavf_emudev.c
new file mode 100644
index 0000000000..a4cd2deb06
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emudev.c
@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <rte_kvargs.h>
+#include <rte_emudev.h>
+#include <rte_emudev_vdev.h>
+
+#include "iavf_emu_internal.h"
+
+#define EMU_IAVF_SOCK_ARG "sock"
+#define EMU_IAVF_QUEUES_ARG "queues"
+
+static const char * const emu_iavf_valid_arg[] = {
+	EMU_IAVF_SOCK_ARG,
+	EMU_IAVF_QUEUES_ARG,
+	NULL
+};
+
+static inline int
+save_sockaddr(const char *key __rte_unused, const char *value,
+	void *extra_args)
+{
+	const char **sock_addr = extra_args;
+
+	if (value == NULL)
+		return -1;
+
+	*sock_addr = value;
+
+	return 0;
+}
+
+static inline int
+save_int(const char *key __rte_unused, const char *value, void *extra_args)
+{
+	uint16_t *n = extra_args;
+
+	if (value == NULL || extra_args == NULL)
+		return -EINVAL;
+
+	*n = (uint16_t)strtoul(value, NULL, 0);
+	if (*n == USHRT_MAX && errno == ERANGE)
+		return -1;
+
+	return 0;
+}
+
+static int iavf_emu_init_device(struct iavf_emudev *dev,
+	char *sock_addr, uint16_t queues, unsigned int numa_node)
+{
+	dev->sock_addr = rte_malloc_socket("sock_addr",
+		strlen(sock_addr) + 1, 0, numa_node);
+	if (!dev->sock_addr) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc sock addr\n");
+		goto exit;
+	}
+	strcpy(dev->sock_addr, sock_addr);
+
+	dev->mem = rte_zmalloc_socket("iavf_emu_mem",
+			sizeof(struct rte_iavf_emu_mem),
+			0, numa_node);
+	if (!dev->mem) {
+		EMU_IAVF_LOG(ERR, "Unable to alloc iavf_emu_mem.\n");
+		goto err_mem;
+	}
+
+	dev->intr = rte_zmalloc_socket("iavf_emu_intr",
+			sizeof(struct iavf_emu_intr),
+			0, numa_node);
+	if (!dev->intr) {
+		EMU_IAVF_LOG(ERR, "Unable to alloc iavf_emu_intr.\n");
+		goto err_intr;
+	}
+
+	dev->lanq = rte_zmalloc_socket("iavf_emu_lanQ",
+			sizeof(struct iavf_emu_lanQ) * queues * 2,
+			0, numa_node);
+	if (!dev->lanq) {
+		EMU_IAVF_LOG(ERR, "Unable to alloc iavf_emu_lanQ.\n");
+		goto err_lanq;
+	}
+
+	dev->numa_node = numa_node;
+
+	return 0;
+
+err_lanq:
+	rte_free(dev->lanq);
+err_intr:
+	rte_free(dev->intr);
+err_mem:
+	rte_free(dev->sock_addr);
+exit:
+	return -1;
+}
+
+void iavf_emu_uninit_device(struct iavf_emudev *dev)
+{
+	rte_free(dev->sock_addr);
+	rte_free(dev->mem);
+	rte_free(dev->intr);
+	rte_free(dev->lanq);
+}
+
+static int
+rte_emu_iavf_probe(struct rte_vdev_device *dev)
+{
+	struct rte_kvargs *kvlist = NULL;
+	struct rte_emudev *edev;
+	struct iavf_emudev *iavf;
+	char *sock_addr;
+	uint16_t queues;
+	int ret = 0;
+
+	kvlist = rte_kvargs_parse(rte_vdev_device_args(dev),
+		emu_iavf_valid_arg);
+	if (kvlist == NULL)
+		return -1;
+
+	if (rte_kvargs_count(kvlist, EMU_IAVF_SOCK_ARG) == 1) {
+		ret = rte_kvargs_process(kvlist, EMU_IAVF_SOCK_ARG,
+					 &save_sockaddr, &sock_addr);
+		if (ret < 0)
+			goto err;
+	} else {
+		ret = -1;
+		goto err;
+	}
+
+	if (rte_kvargs_count(kvlist, EMU_IAVF_QUEUES_ARG) == 1) {
+		ret = rte_kvargs_process(kvlist, EMU_IAVF_QUEUES_ARG,
+					 &save_int, &queues);
+		if (ret < 0 || queues > RTE_MAX_QUEUES_PER_PORT ||
+			queues > RTE_IAVF_EMU_MAX_QP_NUM)
+			goto err;
+
+	} else
+		queues = 1;
+
+	if (dev->device.numa_node == SOCKET_ID_ANY)
+		dev->device.numa_node = rte_socket_id();
+
+	edev = rte_emu_vdev_allocate(dev, sizeof(*iavf));
+	if (!edev) {
+		EMU_IAVF_LOG(ERR, "Failed to allocate emu_vdev\n");
+		ret = -1;
+		goto err;
+	}
+	edev->dev_ops = &emu_iavf_ops;
+	edev->dev_info.region_num = RTE_IAVF_EMU_MAPPABLE_REG_NUM;
+	edev->dev_info.max_qp_num = queues + RTE_IAVF_EMU_ADMINQ_NUM / 2;
+
+	strcpy(edev->dev_info.dev_type, RTE_IAVF_EMUDEV_TYPE);
+
+	iavf = (struct iavf_emudev *)edev->priv_data;
+	ret = iavf_emu_init_device(iavf, sock_addr, queues,
+		dev->device.numa_node);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to init new iavf device\n");
+		ret = -1;
+		goto err_ndev;
+	}
+
+	iavf->edev = edev;
+	/* If not configured, we assume back-end driver
+	 * can use all queues of emulated iavf
+	 */
+	iavf->max_be_lanqp = queues;
+	iavf->max_lanqp = queues;
+	edev->priv_data = (void *)iavf;
+
+	edev->started = 1;
+	rte_kvargs_free(kvlist);
+	return 0;
+
+err_ndev:
+	rte_emudev_release(edev);
+err:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
+static int
+rte_emu_iavf_remove(struct rte_vdev_device *dev)
+{
+	struct rte_emudev *emu_dev;
+
+	/* Find the emudev entry */
+	emu_dev = rte_emudev_allocated(rte_vdev_device_name(dev));
+	if (!emu_dev)
+		return 0;
+
+	return rte_emudev_close(emu_dev->dev_id);
+}
+
+static struct rte_vdev_driver emu_iavf_drv = {
+	.probe = rte_emu_iavf_probe,
+	.remove = rte_emu_iavf_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(emu_iavf, emu_iavf_drv);
+RTE_PMD_REGISTER_PARAM_STRING(emu_iavf,
+	"sock=<path> "
+	"queues=<int> ");
+
+RTE_LOG_REGISTER(emu_iavf_logtype, emu.iavf, INFO);
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
new file mode 100644
index 0000000000..58c2a90383
--- /dev/null
+++ b/drivers/emu/iavf/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation
+
+sources = files('iavf_emu.c', 'iavf_emudev.c')
+
+deps += ['bus_vdev', 'emudev']
+
+headers = files('rte_iavf_emu.h')
diff --git a/drivers/emu/iavf/rte_iavf_emu.h b/drivers/emu/iavf/rte_iavf_emu.h
new file mode 100644
index 0000000000..623c3c5d99
--- /dev/null
+++ b/drivers/emu/iavf/rte_iavf_emu.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IAVF_EMU_H
+#define _IAVF_EMU_H
+
+#include <stdint.h>
+
+#include <rte_emudev.h>
+
+#define RTE_IAVF_EMUDEV_TYPE "iavf"
+#define RTE_IAVF_EMU_MAX_MEM_REGIONS 256
+#define RTE_IAVF_EMU_MAX_QP_NUM 256
+#define RTE_IAVF_EMU_MAX_INTR 32
+
+enum {
+	RTE_IAVF_EMU_ADMINQ_TXQ = 0,
+	RTE_IAVF_EMU_ADMINQ_RXQ = 1,
+	RTE_IAVF_EMU_ADMINQ_NUM = 2,
+};
+
+enum {
+	RTE_IAVF_EMU_MAPPABLE_REG_BAR0 = 0,
+	RTE_IAVF_EMU_MAPPABLE_REG_BAR3 = 1,
+	RTE_IAVF_EMU_MAPPABLE_REG_NUM = 2,
+};
+
+struct rte_iavf_emu_mem_reg {
+	uint64_t guest_phys_addr;
+	uint64_t host_user_addr;
+	uint64_t size;
+	void	 *mmap_addr;
+	uint64_t mmap_size;
+	int fd;
+};
+
+struct rte_iavf_emu_mem {
+	uint32_t region_num;
+	struct rte_iavf_emu_mem_reg regions[RTE_IAVF_EMU_MAX_MEM_REGIONS];
+};
+
+#endif
diff --git a/drivers/emu/iavf/version.map b/drivers/emu/iavf/version.map
new file mode 100644
index 0000000000..4a76d1d52d
--- /dev/null
+++ b/drivers/emu/iavf/version.map
@@ -0,0 +1,3 @@
+DPDK_21 {
+	local: *;
+};
diff --git a/drivers/emu/meson.build b/drivers/emu/meson.build
new file mode 100644
index 0000000000..acc8c395ef
--- /dev/null
+++ b/drivers/emu/meson.build
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation
+
+drivers = ['iavf']
+std_deps = ['emudev']
+config_flag_fmt = 'RTE_LIBRTE_PMD_@0@_EMUDEV'
diff --git a/drivers/meson.build b/drivers/meson.build
index f9febc579e..64c34d2f9f 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -8,6 +8,7 @@ subdirs = [
 	'common/mlx5', # depends on bus.
 	'common/qat', # depends on bus.
 	'mempool', # depends on common and bus.
+	'emu', # depends on common and bus.
 	'net',     # depends on common, bus, mempool
 	'raw',     # depends on common, bus and net.
 	'crypto',  # depends on common, bus and mempool (net in future).
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v2 4/8] emu/iavf: add vfio-user device register and unregister
  2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
                     ` (2 preceding siblings ...)
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 3/8] emu: introduce emulated iavf driver Chenbo Xia
@ 2020-12-19  6:28   ` Chenbo Xia
  2021-01-04  6:45     ` Wu, Jingjing
  2021-01-05 13:41     ` Wu, Jingjing
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 5/8] emu/iavf: add resource management and internal logic of iavf Chenbo Xia
                     ` (5 subsequent siblings)
  9 siblings, 2 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-19  6:28 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This patch adds vfio-user APIs call in driver probe and remove.
rte_vfio_user_register() and rte_vfio_user_unregister() are called
to create/destroy a vfio-user device. Notify callbacks that
libvfio_user defines are also implemented.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Miao Li <miao.li@intel.com>
---
 drivers/emu/iavf/iavf_emu.c          |   3 +-
 drivers/emu/iavf/iavf_emu_internal.h |  19 ++
 drivers/emu/iavf/iavf_emudev.c       |  12 +-
 drivers/emu/iavf/iavf_vfio_user.c    | 384 +++++++++++++++++++++++++++
 drivers/emu/iavf/iavf_vfio_user.h    |  16 ++
 drivers/emu/iavf/meson.build         |   5 +-
 drivers/emu/iavf/rte_iavf_emu.h      |  17 ++
 7 files changed, 452 insertions(+), 4 deletions(-)
 create mode 100644 drivers/emu/iavf/iavf_vfio_user.c
 create mode 100644 drivers/emu/iavf/iavf_vfio_user.h

diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index 68d2c440e3..dfd9796920 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -2,7 +2,7 @@
  * Copyright(c) 2020 Intel Corporation
  */
 
-#include "iavf_emu_internal.h"
+#include "iavf_vfio_user.h"
 
 static int iavf_emu_dev_close(struct rte_emudev *dev)
 {
@@ -18,6 +18,7 @@ static int iavf_emu_dev_close(struct rte_emudev *dev)
 	}
 
 	iavf = (struct iavf_emudev *)dev->priv_data;
+	iavf_emu_unregister_vfio_user(iavf);
 	iavf_emu_uninit_device(iavf);
 	dev->priv_data = NULL;
 
diff --git a/drivers/emu/iavf/iavf_emu_internal.h b/drivers/emu/iavf/iavf_emu_internal.h
index a726bfe577..10197c00ba 100644
--- a/drivers/emu/iavf/iavf_emu_internal.h
+++ b/drivers/emu/iavf/iavf_emu_internal.h
@@ -17,6 +17,13 @@ extern int emu_iavf_logtype;
 #define EMU_IAVF_LOG(level, ...) \
 	rte_log(RTE_LOG_ ## level, emu_iavf_logtype, "EMU_IAVF: " __VA_ARGS__)
 
+struct iavf_emu_vfio_user {
+	int dev_id;
+	struct vfio_device_info *dev_info;
+	struct rte_vfio_user_regions *reg;
+	struct rte_vfio_user_irq_info *irq;
+};
+
 struct iavf_emu_intr_info {
 	int enable;
 	int fd;
@@ -27,6 +34,14 @@ struct iavf_emu_intr {
 	struct iavf_emu_intr_info info[RTE_IAVF_EMU_MAX_INTR];
 };
 
+struct iavf_emu_adminQ {
+	uint32_t *ring_addr_lo;
+	uint32_t *ring_addr_hi;
+	uint32_t *ring_sz;
+	uint16_t db_size;
+	void *doorbell;
+};
+
 struct iavf_emu_lanQ {
 	uint16_t db_size;
 	void *doorbell;
@@ -34,14 +49,18 @@ struct iavf_emu_lanQ {
 
 struct iavf_emudev {
 	struct rte_emudev *edev;
+	struct iavf_emu_vfio_user *vfio;
 	/* Maximum LANQ queue pair that this emulated iavf has */
 	uint16_t max_lanqp;
 	/* Maximum LANQ queue pair number that back-end driver can use */
 	uint16_t max_be_lanqp;
 	unsigned int numa_node;
+	int ready;
 	char *sock_addr;
+	struct rte_iavf_emu_notify_ops *ops;
 	struct rte_iavf_emu_mem *mem;
 	struct iavf_emu_intr *intr;
+	struct iavf_emu_adminQ adq[RTE_IAVF_EMU_ADMINQ_NUM];
 	struct iavf_emu_lanQ *lanq;
 };
 
diff --git a/drivers/emu/iavf/iavf_emudev.c b/drivers/emu/iavf/iavf_emudev.c
index a4cd2deb06..fbbe3d95a7 100644
--- a/drivers/emu/iavf/iavf_emudev.c
+++ b/drivers/emu/iavf/iavf_emudev.c
@@ -6,7 +6,7 @@
 #include <rte_emudev.h>
 #include <rte_emudev_vdev.h>
 
-#include "iavf_emu_internal.h"
+#include "iavf_vfio_user.h"
 
 #define EMU_IAVF_SOCK_ARG "sock"
 #define EMU_IAVF_QUEUES_ARG "queues"
@@ -170,10 +170,20 @@ rte_emu_iavf_probe(struct rte_vdev_device *dev)
 	iavf->max_lanqp = queues;
 	edev->priv_data = (void *)iavf;
 
+	ret = iavf_emu_register_vfio_user(iavf);
+	if (ret) {
+		EMU_IAVF_LOG(ERR,
+			"Emulated iavf failed to register vfio user.\n");
+		ret = -1;
+		goto err_reg;
+	}
+
 	edev->started = 1;
 	rte_kvargs_free(kvlist);
 	return 0;
 
+err_reg:
+	iavf_emu_uninit_device(iavf);
 err_ndev:
 	rte_emudev_release(edev);
 err:
diff --git a/drivers/emu/iavf/iavf_vfio_user.c b/drivers/emu/iavf/iavf_vfio_user.c
new file mode 100644
index 0000000000..aae47de9f3
--- /dev/null
+++ b/drivers/emu/iavf/iavf_vfio_user.c
@@ -0,0 +1,384 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <pthread.h>
+
+#include <rte_malloc.h>
+
+#include "iavf_vfio_user.h"
+#include <iavf_type.h>
+
+struct iavf_emu_sock_list {
+	TAILQ_ENTRY(iavf_emu_sock_list) next;
+	struct rte_emudev *emu_dev;
+};
+
+TAILQ_HEAD(iavf_emu_sock_list_head, iavf_emu_sock_list);
+
+static struct iavf_emu_sock_list_head sock_list =
+	TAILQ_HEAD_INITIALIZER(sock_list);
+
+static pthread_mutex_t sock_list_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static int iavf_emu_setup_irq(struct iavf_emudev *dev)
+{
+	struct iavf_emu_intr *intr;
+	struct rte_vfio_user_irq_info *irq;
+	int *fds = NULL;
+	uint32_t i, count;
+
+	irq = dev->vfio->irq;
+	if (!irq)
+		return -1;
+
+	count = irq->irq_info[VFIO_PCI_MSIX_IRQ_INDEX].count;
+	if (count) {
+		fds = rte_zmalloc("irq_fds", sizeof(int) * count, 0);
+		if (!fds) {
+			EMU_IAVF_LOG(ERR,
+				"Failed to alloc irq fds.\n");
+			return -1;
+		}
+	}
+
+	if (rte_vfio_user_get_irq(dev->vfio->dev_id,
+			VFIO_PCI_MSIX_IRQ_INDEX, count, fds)) {
+		EMU_IAVF_LOG(ERR, "Failed to get irqfds from vfio-user.\n");
+		return -1;
+	}
+
+	intr = dev->intr;
+	intr->intr_num = irq->irq_info[VFIO_PCI_MSIX_IRQ_INDEX].count;
+
+	for (i = 0; i < count; i++) {
+		intr->info[i].fd = fds[i];
+		intr->info[i].enable = 0;
+	}
+
+	rte_free(fds);
+
+	return 0;
+}
+
+static inline void iavf_emu_reset_irq(struct iavf_emudev *dev)
+{
+	struct iavf_emu_intr *intr = dev->intr;
+	uint32_t i;
+
+	for (i = 0; i < intr->intr_num; i++) {
+		intr->info[i].enable = 0;
+		intr->info[i].fd = -1;
+	}
+}
+
+static inline void iavf_emu_reset_regions(struct iavf_emudev *dev)
+{
+	struct rte_vfio_user_regions *reg = dev->vfio->reg;
+	struct rte_vfio_user_reg_info *vinfo;
+	uint32_t i;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		vinfo = &reg->reg_info[i];
+		if (vinfo->info->size && vinfo->base)
+			memset(vinfo->base, 0, vinfo->info->size);
+	}
+}
+
+static int iavf_emu_setup_mem_table(struct iavf_emudev *dev)
+{
+	const struct rte_vfio_user_mem *vfio_mem;
+	const struct rte_vfio_user_mtb_entry *entry;
+	struct rte_iavf_emu_mem *mem;
+	uint32_t i;
+
+	vfio_mem = rte_vfio_user_get_mem_table(dev->vfio->dev_id);
+	if (!vfio_mem) {
+		EMU_IAVF_LOG(ERR, "Unable to get vfio mem table.\n");
+		return -1;
+	}
+
+	mem = dev->mem;
+
+	mem->region_num = vfio_mem->entry_num;
+	if (mem->region_num > RTE_IAVF_EMU_MAX_MEM_REGIONS) {
+		EMU_IAVF_LOG(ERR, "Failed to set up mem table,"
+			"exceed max region num.\n");
+		return -1;
+	}
+
+	for (i = 0; i < vfio_mem->entry_num; i++) {
+		entry = &vfio_mem->entry[i];
+
+		mem->regions[i].guest_phys_addr = entry->gpa;
+		mem->regions[i].host_user_addr = entry->host_user_addr;
+		mem->regions[i].mmap_addr = entry->mmap_addr;
+		mem->regions[i].mmap_size = entry->mmap_size;
+		mem->regions[i].size = entry->size;
+		mem->regions[i].fd = entry->fd;
+	}
+
+	return 0;
+}
+
+static inline void iavf_emu_reset_mem_table(struct iavf_emudev *dev)
+{
+	struct rte_iavf_emu_mem *mem = dev->mem;
+	uint32_t i;
+
+	for (i = 0; i < mem->region_num; i++) {
+		mem->regions[i].guest_phys_addr = 0;
+		mem->regions[i].host_user_addr = 0;
+		mem->regions[i].mmap_addr = 0;
+		mem->regions[i].mmap_size = 0;
+		mem->regions[i].size = 0;
+		mem->regions[i].fd = -1;
+	}
+}
+
+static int iavf_emu_setup_queues(struct iavf_emudev *dev)
+{
+	struct iavf_emu_adminQ *asq, *arq;
+	struct rte_vfio_user_reg_info *info;
+	uint16_t i;
+
+	info = &dev->vfio->reg->reg_info[0];
+	asq = &dev->adq[RTE_IAVF_EMU_ADMINQ_TXQ];
+	arq = &dev->adq[RTE_IAVF_EMU_ADMINQ_RXQ];
+
+	asq->doorbell = (uint8_t *)info->base + IAVF_VF_ATQT1;
+	asq->db_size = 4;
+	asq->ring_addr_lo = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ATQBAL1);
+	asq->ring_addr_hi = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ATQBAH1);
+	asq->ring_sz = (uint32_t *)((uint8_t *)info->base + IAVF_VF_ATQLEN1);
+
+	arq->doorbell = (uint8_t *)info->base + IAVF_VF_ARQT1;
+	arq->db_size = 4;
+	arq->ring_addr_lo = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ARQBAL1);
+	arq->ring_addr_hi = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ARQBAH1);
+	arq->ring_sz = (uint32_t *)((uint8_t *)info->base + IAVF_VF_ARQLEN1);
+
+	for (i = 0; i < dev->max_lanqp; i++) {
+		dev->lanq[i * 2].doorbell = (uint8_t *)info->base +
+				IAVF_QTX_TAIL1(i);
+		dev->lanq[i * 2].db_size = 4;
+		dev->lanq[i * 2 + 1].doorbell = (uint8_t *)info->base +
+				IAVF_QRX_TAIL1(i);
+		dev->lanq[i * 2 + 1].db_size = 4;
+	}
+
+	return 0;
+}
+
+static inline void iavf_emu_reset_queues(struct iavf_emudev *dev)
+{
+	memset(&dev->adq, 0, RTE_IAVF_EMU_ADMINQ_NUM *
+		sizeof(struct iavf_emu_adminQ));
+
+	memset(dev->lanq, 0, dev->max_lanqp * 2 *
+		sizeof(struct iavf_emu_lanQ));
+}
+
+static void iavf_emu_reset_all_resources(struct iavf_emudev *dev)
+{
+	iavf_emu_reset_mem_table(dev);
+	iavf_emu_reset_irq(dev);
+	iavf_emu_reset_queues(dev);
+	iavf_emu_reset_regions(dev);
+}
+
+static inline struct iavf_emu_sock_list *
+iavf_emu_find_sock_list(char *sock_addr)
+{
+	struct iavf_emu_sock_list *list;
+	struct iavf_emudev *dev;
+	int list_exist;
+
+	if (!sock_addr)
+		return NULL;
+
+	pthread_mutex_lock(&sock_list_lock);
+
+	TAILQ_FOREACH(list, &sock_list, next) {
+		dev = (struct iavf_emudev *)list->emu_dev->priv_data;
+
+		if (!strcmp(dev->sock_addr, sock_addr)) {
+			list_exist = 1;
+			break;
+		}
+		break;
+	}
+
+	pthread_mutex_unlock(&sock_list_lock);
+
+	if (!list_exist)
+		return NULL;
+
+	return list;
+}
+
+static struct iavf_emudev *find_iavf_with_dev_id(int vfio_dev_id)
+{
+	struct iavf_emu_sock_list *list;
+	char sock_addr[PATH_MAX];
+	int ret;
+
+	ret = rte_vfio_get_sock_addr(vfio_dev_id, sock_addr,
+		sizeof(sock_addr));
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Can not find vfio device %d "
+			"sock_addr.\n", vfio_dev_id);
+		return NULL;
+	}
+
+	list = iavf_emu_find_sock_list(sock_addr);
+	if (!list) {
+		EMU_IAVF_LOG(ERR, "Can not find sock list.\n");
+		return NULL;
+	}
+
+	return (struct iavf_emudev *)list->emu_dev->priv_data;
+}
+
+static int iavf_emu_new_device(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+	int ret;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	dev->vfio->dev_id = vfio_dev_id;
+
+	ret = iavf_emu_setup_mem_table(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up memtable for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = iavf_emu_setup_irq(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up irq for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = iavf_emu_setup_queues(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up queues for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = dev->ops->device_ready(dev->edev);
+	if (ret)
+		return ret;
+
+	dev->ready = 1;
+	return 0;
+}
+
+static void iavf_emu_destroy_device(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return;
+
+	iavf_emu_reset_all_resources(dev);
+
+	dev->ops->device_destroy(dev->edev);
+}
+
+static int iavf_emu_update_status(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+	int ret;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	ret = iavf_emu_setup_mem_table(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up memtable for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = iavf_emu_setup_irq(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up irq for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	dev->ops->update_status(dev->edev);
+
+	return 0;
+}
+
+static int iavf_emu_lock_datapath(int vfio_dev_id, int lock)
+{
+	struct iavf_emudev *dev;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	return dev->ops->lock_dp(dev->edev, lock);
+}
+
+static int iavf_emu_reset_device(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	iavf_emu_reset_all_resources(dev);
+
+	return dev->ops->reset_device(dev->edev);
+}
+
+struct rte_vfio_user_notify_ops vfio_ops = {
+	.new_device = iavf_emu_new_device,
+	.destroy_device = iavf_emu_destroy_device,
+	.update_status = iavf_emu_update_status,
+	.lock_dp = iavf_emu_lock_datapath,
+	.reset_device = iavf_emu_reset_device,
+};
+
+int iavf_emu_register_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+
+	ret = rte_vfio_user_register(dev->sock_addr, &vfio_ops);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Register vfio_user failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+int iavf_emu_unregister_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+
+	ret = rte_vfio_user_unregister(dev->sock_addr);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Unregister vfio_user failed\n");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/drivers/emu/iavf/iavf_vfio_user.h b/drivers/emu/iavf/iavf_vfio_user.h
new file mode 100644
index 0000000000..aa2f3edc87
--- /dev/null
+++ b/drivers/emu/iavf/iavf_vfio_user.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IAVF_VFIO_USER_H
+#define _IAVF_VFIO_USER_H
+
+#include <rte_vfio_user.h>
+
+#include "iavf_emu_internal.h"
+
+int iavf_emu_register_vfio_user(struct iavf_emudev *dev);
+
+int iavf_emu_unregister_vfio_user(struct iavf_emudev *dev);
+
+#endif
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
index 58c2a90383..4f651258c2 100644
--- a/drivers/emu/iavf/meson.build
+++ b/drivers/emu/iavf/meson.build
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2020 Intel Corporation
 
-sources = files('iavf_emu.c', 'iavf_emudev.c')
+sources = files('iavf_emu.c', 'iavf_vfio_user.c',
+	'iavf_emudev.c')
 
-deps += ['bus_vdev', 'emudev']
+deps += ['bus_vdev', 'emudev', 'vfio_user', 'common_iavf']
 
 headers = files('rte_iavf_emu.h')
diff --git a/drivers/emu/iavf/rte_iavf_emu.h b/drivers/emu/iavf/rte_iavf_emu.h
index 623c3c5d99..6de0989f0b 100644
--- a/drivers/emu/iavf/rte_iavf_emu.h
+++ b/drivers/emu/iavf/rte_iavf_emu.h
@@ -40,4 +40,21 @@ struct rte_iavf_emu_mem {
 	struct rte_iavf_emu_mem_reg regions[RTE_IAVF_EMU_MAX_MEM_REGIONS];
 };
 
+struct rte_iavf_emu_notify_ops {
+	/* Device is ready */
+	int (*device_ready)(struct rte_emudev *dev);
+	/* Device is destroyed */
+	void (*device_destroy)(struct rte_emudev *dev);
+	/* Update device status */
+	int (*update_status)(struct rte_emudev *dev);
+	/* Start device */
+	int (*device_start)(struct rte_emudev *dev);
+	/* Stop device */
+	int (*device_stop)(struct rte_emudev *dev);
+	/* Lock or unlock data path */
+	int (*lock_dp)(struct rte_emudev *dev, int lock);
+	/* Reset device */
+	int (*reset_device)(struct rte_emudev *dev);
+};
+
 #endif
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v2 5/8] emu/iavf: add resource management and internal logic of iavf
  2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
                     ` (3 preceding siblings ...)
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 4/8] emu/iavf: add vfio-user device register and unregister Chenbo Xia
@ 2020-12-19  6:28   ` Chenbo Xia
  2020-12-29  6:05     ` Wu, Jingjing
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 6/8] emu/iavf: add emudev operations to fit in emudev framework Chenbo Xia
                     ` (4 subsequent siblings)
  9 siblings, 1 reply; 44+ messages in thread
From: Chenbo Xia @ 2020-12-19  6:28 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This patch adds the allocation and release of device resources.
Device resources include PCI BARs' memory and interrupt related
resources. Device internal logic is also added.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
---
 drivers/emu/iavf/iavf_emu.c       |   1 +
 drivers/emu/iavf/iavf_emudev.c    |  20 +
 drivers/emu/iavf/iavf_vfio_user.c | 669 ++++++++++++++++++++++++++++++
 drivers/emu/iavf/iavf_vfio_user.h |  41 ++
 drivers/emu/iavf/meson.build      |   8 +
 5 files changed, 739 insertions(+)

diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index dfd9796920..c1a702d744 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -18,6 +18,7 @@ static int iavf_emu_dev_close(struct rte_emudev *dev)
 	}
 
 	iavf = (struct iavf_emudev *)dev->priv_data;
+	iavf_emu_uninit_vfio_user(iavf);
 	iavf_emu_unregister_vfio_user(iavf);
 	iavf_emu_uninit_device(iavf);
 	dev->priv_data = NULL;
diff --git a/drivers/emu/iavf/iavf_emudev.c b/drivers/emu/iavf/iavf_emudev.c
index fbbe3d95a7..70cf558eef 100644
--- a/drivers/emu/iavf/iavf_emudev.c
+++ b/drivers/emu/iavf/iavf_emudev.c
@@ -178,10 +178,30 @@ rte_emu_iavf_probe(struct rte_vdev_device *dev)
 		goto err_reg;
 	}
 
+	ret = iavf_emu_init_vfio_user(iavf);
+	if (ret) {
+		EMU_IAVF_LOG(ERR,
+			"Emulated iavf failed to init vfio user.\n");
+		ret = -1;
+		goto err_init;
+	}
+
+	ret = iavf_emu_start_vfio_user(iavf);
+	if (ret) {
+		EMU_IAVF_LOG(ERR,
+			"Emulated iavf failed to start vfio user.\n");
+		ret = -1;
+		goto err_start;
+	}
+
 	edev->started = 1;
 	rte_kvargs_free(kvlist);
 	return 0;
 
+err_start:
+	iavf_emu_uninit_vfio_user(iavf);
+err_init:
+	iavf_emu_unregister_vfio_user(iavf);
 err_reg:
 	iavf_emu_uninit_device(iavf);
 err_ndev:
diff --git a/drivers/emu/iavf/iavf_vfio_user.c b/drivers/emu/iavf/iavf_vfio_user.c
index aae47de9f3..a4208de618 100644
--- a/drivers/emu/iavf/iavf_vfio_user.c
+++ b/drivers/emu/iavf/iavf_vfio_user.c
@@ -2,13 +2,36 @@
  * Copyright(c) 2020 Intel Corporation
  */
 
+#include <linux/pci.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
 #include <pthread.h>
+#include <sys/types.h>
 
 #include <rte_malloc.h>
+#include <rte_emudev.h>
+#include <rte_memcpy.h>
 
 #include "iavf_vfio_user.h"
 #include <iavf_type.h>
 
+#define STORE_LE16(addr, val)   (*(__u16 *)addr = val)
+#define STORE_LE32(addr, val)   (*(__u32 *)addr = val)
+
+#define IAVF_EMU_BAR0_SIZE 0x10000
+#define IAVF_EMU_BAR3_SIZE 0x1000
+#define IAVF_EMU_BAR_SIZE_MASK 0xffffffff
+#define IAVF_EMU_BAR_MASK(sz) (~(sz) + 1)
+#define IAVF_EMU_MSIX_TABLE_SIZE 0x5
+
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_SUBDEVICE_ID 0x1100
+#define PCI_CLASS_ETHERNET 0x0200
+
 struct iavf_emu_sock_list {
 	TAILQ_ENTRY(iavf_emu_sock_list) next;
 	struct rte_emudev *emu_dev;
@@ -174,6 +197,14 @@ static int iavf_emu_setup_queues(struct iavf_emudev *dev)
 	return 0;
 }
 
+static inline void iavf_emu_cleanup_queues(struct iavf_emudev *dev)
+{
+	memset(&dev->adq, 0, RTE_IAVF_EMU_ADMINQ_NUM *
+		sizeof(struct iavf_emu_adminQ));
+
+	rte_free(dev->lanq);
+}
+
 static inline void iavf_emu_reset_queues(struct iavf_emudev *dev)
 {
 	memset(&dev->adq, 0, RTE_IAVF_EMU_ADMINQ_NUM *
@@ -191,6 +222,576 @@ static void iavf_emu_reset_all_resources(struct iavf_emudev *dev)
 	iavf_emu_reset_regions(dev);
 }
 
+static int iavf_emu_init_dev(struct iavf_emudev *dev)
+{
+	struct iavf_emu_vfio_user *vfio;
+	struct vfio_device_info *dev_info;
+	struct rte_vfio_user_regions *reg;
+	struct rte_vfio_user_irq_info *irq;
+	struct vfio_region_info_cap_sparse_mmap *sparse;
+	int ret;
+	uint32_t i, j;
+
+	vfio = rte_zmalloc_socket("vfio", sizeof(*vfio), 0, dev->numa_node);
+	if (!vfio) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc iavf_emu_vfio_user\n");
+		ret = -1;
+		goto exit;
+	}
+
+	dev_info = rte_zmalloc_socket("vfio_dev_info",
+		sizeof(*dev_info), 0, dev->numa_node);
+	if (!dev_info) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc vfio dev_info\n");
+		ret = -1;
+		goto err_info;
+	}
+	dev_info->argsz = sizeof(*dev_info);
+	dev_info->flags = VFIO_DEVICE_FLAGS_PCI | VFIO_DEVICE_FLAGS_RESET;
+	dev_info->num_regions = VFIO_PCI_NUM_REGIONS;
+	dev_info->num_irqs = VFIO_PCI_NUM_IRQS;
+
+	reg = rte_zmalloc_socket("vfio_user_regions",
+		sizeof(*reg) + dev_info->num_regions *
+		sizeof(struct rte_vfio_user_reg_info), 0, dev->numa_node);
+	if (!reg) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc vfio_user_regions\n");
+		ret = -1;
+		goto err_reg;
+	}
+	reg->reg_num = dev_info->num_regions;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		struct rte_vfio_user_reg_info *vinfo = &reg->reg_info[i];
+		size_t sz = sizeof(struct vfio_region_info);
+
+		/* BAR0 has two sparse mmap area */
+		if (i == VFIO_PCI_BAR0_REGION_INDEX)
+			sz += sizeof(*sparse) + 2 * sizeof(*sparse->areas);
+
+		vinfo->info = rte_zmalloc_socket("vfio_region_info",
+			sz, 0, dev->numa_node);
+		if (!vinfo->info) {
+			EMU_IAVF_LOG(ERR, "Failed to alloc region info "
+				"for region %d\n", i);
+			ret = -1;
+			goto err_reg_alloc;
+		}
+
+		vinfo->info->index = i;
+
+		switch (i) {
+		case VFIO_PCI_CONFIG_REGION_INDEX:
+			vinfo->info->argsz = sz;
+			vinfo->info->offset = 0;
+			vinfo->info->size = IAVF_EMU_CFG_SPACE_SIZE;
+			vinfo->info->flags = VFIO_REGION_INFO_FLAG_READ |
+				       VFIO_REGION_INFO_FLAG_WRITE;
+			break;
+		case VFIO_PCI_BAR0_REGION_INDEX:
+			vinfo->info->argsz = sz;
+			vinfo->info->offset = 0;
+			vinfo->info->size = IAVF_EMU_BAR0_SIZE;
+			vinfo->info->flags  = VFIO_REGION_INFO_FLAG_READ |
+				       VFIO_REGION_INFO_FLAG_WRITE |
+				       VFIO_REGION_INFO_FLAG_MMAP |
+				       VFIO_REGION_INFO_FLAG_CAPS;
+			vinfo->info->cap_offset =
+						sizeof(struct vfio_region_info);
+
+			sparse = (struct vfio_region_info_cap_sparse_mmap *)
+						((uint8_t *)vinfo->info +
+						vinfo->info->cap_offset);
+			sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
+			sparse->header.version = 1;
+			sparse->nr_areas = 2;
+			sparse->areas[0].offset = 0;
+			sparse->areas[0].size = 0x3000;
+			sparse->areas[1].offset = 0x6000;
+			sparse->areas[1].size = IAVF_EMU_BAR0_SIZE - 0x6000;
+
+			break;
+		case VFIO_PCI_BAR3_REGION_INDEX:
+			vinfo->info->argsz = sz;
+			vinfo->info->offset = 0;
+			vinfo->info->size = IAVF_EMU_BAR3_SIZE;
+			vinfo->info->flags  = VFIO_REGION_INFO_FLAG_READ |
+				       VFIO_REGION_INFO_FLAG_WRITE |
+				       VFIO_REGION_INFO_FLAG_MMAP;
+			break;
+		default:
+			vinfo->info->argsz = sz;
+			vinfo->info->offset = 0;
+			vinfo->info->size = 0;
+			vinfo->info->flags = 0;
+		}
+	}
+
+	irq = rte_zmalloc_socket("vfio_user_irq_info", sizeof(*irq) +
+		VFIO_PCI_NUM_IRQS * sizeof(struct vfio_irq_info),
+		0, dev->numa_node);
+	if (!irq) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc vfio_user_irqs\n");
+		ret = -1;
+		goto err_irq;
+	}
+	irq->irq_num = VFIO_PCI_NUM_IRQS;
+
+	for (i = 0; i < VFIO_PCI_NUM_IRQS; i++) {
+		irq->irq_info[i].index = i;
+		irq->irq_info[i].flags =
+			VFIO_IRQ_INFO_EVENTFD | VFIO_IRQ_INFO_NORESIZE;
+		if (i == VFIO_PCI_MSIX_IRQ_INDEX)
+			irq->irq_info[i].count =
+				IAVF_EMU_MSIX_TABLE_SIZE + 1;
+		else if (i == VFIO_PCI_ERR_IRQ_INDEX)
+			irq->irq_info[i].count = 1;
+		else
+			irq->irq_info[i].count = 0;
+	}
+
+	vfio->dev_info = dev_info;
+	vfio->reg = reg;
+	vfio->irq = irq;
+	dev->vfio = vfio;
+
+	return 0;
+
+err_irq:
+err_reg_alloc:
+	for (j = 0; j < i; j++)
+		rte_free(reg->reg_info[j].info);
+	rte_free(reg);
+err_reg:
+	rte_free(dev_info);
+err_info:
+	rte_free(vfio);
+exit:
+	return ret;
+}
+
+static int iavf_emu_uninit_dev(struct iavf_emudev *dev)
+{
+	struct iavf_emu_vfio_user *vfio;
+	struct rte_vfio_user_regions *reg;
+	uint32_t i;
+
+	if (!dev->vfio)
+		return -1;
+
+	vfio = dev->vfio;
+	rte_free(vfio->dev_info);
+
+	reg = vfio->reg;
+	for (i = 0; i < reg->reg_num; i++)
+		rte_free(reg->reg_info[i].info);
+	rte_free(reg);
+
+	rte_free(vfio->irq);
+	rte_free(vfio);
+
+	return 0;
+}
+
+static int handle_pci_cmd_write(struct iavf_emu_pci_hdr *hdr,
+	char *buf, size_t count)
+{
+	/* Below are all R/W bits in command register */
+	uint16_t rw_bitmask = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+		PCI_COMMAND_MASTER | PCI_COMMAND_PARITY |
+		PCI_COMMAND_SERR | PCI_COMMAND_INTX_DISABLE;
+	uint16_t val;
+
+	if (count != 2) {
+		EMU_IAVF_LOG(ERR, "Wrong write count (%lu) for PCI_COMMAND\n",
+			count);
+		return -1;
+	}
+
+	val = *(uint16_t *)buf;
+	/* Only write the R/W bits */
+	hdr->cmd = (rw_bitmask & val) | (~rw_bitmask & hdr->cmd);
+
+	return 2;
+}
+
+static int handle_pci_status_write(struct iavf_emu_pci_hdr *hdr,
+	char *buf, size_t count)
+{
+	/* Below are all write-1-to-clear bits in status register */
+	uint16_t rw1c_bitmask = PCI_STATUS_PARITY |
+		PCI_STATUS_SIG_TARGET_ABORT |
+		PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT |
+		PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY;
+	uint16_t val;
+
+	if (count != 2) {
+		EMU_IAVF_LOG(ERR, "Wrong write count (%lu) for PCI_STATUS\n",
+			count);
+		return -1;
+	}
+
+	val = *(uint16_t *)buf;
+	/* Clear the write-1-to-clear bits*/
+	hdr->status = ~(rw1c_bitmask & val) & hdr->status;
+
+	return 2;
+}
+
+static int handle_pci_bar_write(struct iavf_emu_pci_hdr *hdr,
+	char *buf, size_t count, loff_t pos)
+{
+	uint32_t val, size;
+	uint8_t idx;
+
+	if (count != 4) {
+		EMU_IAVF_LOG(ERR, "Wrong write count (%lu) for "
+			"Base Address Register\n",
+			count);
+		return -1;
+	}
+
+	val = *(uint32_t *)buf;
+
+	if (pos == PCI_BASE_ADDRESS_0)
+		size = IAVF_EMU_BAR0_SIZE;
+	else if (pos == PCI_BASE_ADDRESS_3)
+		size = IAVF_EMU_BAR3_SIZE;
+	else
+		size = 0;
+
+	if (val == IAVF_EMU_BAR_SIZE_MASK)
+		val &= IAVF_EMU_BAR_MASK(size);
+
+	idx = (pos - PCI_BASE_ADDRESS_0) / 0x4;
+	hdr->bar[idx] |= val & ~PCI_BASE_ADDRESS_MEM_MASK;
+
+	return 4;
+}
+
+static int handle_cfg_write(struct iavf_emu_pci_hdr *hdr,
+	char *buf, size_t count, loff_t pos)
+{
+	int ret = count;
+
+	switch (pos) {
+	case PCI_COMMAND:
+		ret = handle_pci_cmd_write(hdr, buf, count);
+		break;
+	case PCI_STATUS:
+		ret = handle_pci_status_write(hdr, buf, count);
+		break;
+	case PCI_INTERRUPT_LINE:
+		if (count != 1) {
+			EMU_IAVF_LOG(ERR, "Wrong write count (%lu)"
+				"for PCI_INTERRUPT_LINE\n",
+				count);
+			return -1;
+		}
+		hdr->intrl = *(uint8_t *)buf;
+		ret = 1;
+		break;
+	case PCI_BASE_ADDRESS_0:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_1:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_2:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_3:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_4:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_5:
+		ret = handle_pci_bar_write(hdr, buf, count, pos);
+		break;
+	default:
+		EMU_IAVF_LOG(INFO, "Write request for cfg (pos: %ld) ignored\n",
+			pos);
+		break;
+	}
+
+	return ret;
+}
+
+static ssize_t iavf_emu_cfg_rw(struct rte_vfio_user_reg_info *reg, char *buf,
+	size_t count, loff_t pos, bool iswrite)
+{
+	struct iavf_emu_cfg_space *cfg;
+	char *reg_pos;
+	int ret = 0;
+
+	if (!reg->base) {
+		EMU_IAVF_LOG(ERR, "Config space not exist\n");
+		return -EFAULT;
+	}
+
+	if (pos + count > reg->info->size) {
+		EMU_IAVF_LOG(ERR, "Access exceeds config space size\n");
+		return -EINVAL;
+	}
+
+	cfg = (struct iavf_emu_cfg_space *)reg->base;
+	reg_pos = (char *)reg->base + pos;
+
+	if (!iswrite) {
+		rte_memcpy(buf, reg_pos, count);
+		ret = count;
+	} else {
+		ret = handle_cfg_write(&cfg->hdr, buf, count, pos);
+		if (ret < 0) {
+			EMU_IAVF_LOG(ERR, "Failed to write cfg space\n");
+			return -EINVAL;
+		}
+	}
+
+	return ret;
+}
+
+static int iavf_emu_init_cfg_space(struct rte_vfio_user_reg_info *vinfo,
+	unsigned int numa_node)
+{
+	char *v_cfg;
+
+	vinfo->base = rte_zmalloc_socket("cfg space",
+		IAVF_EMU_CFG_SPACE_SIZE,
+		0, numa_node);
+	if (!vinfo->base) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc cfg space\n");
+		return -1;
+	}
+	vinfo->rw = iavf_emu_cfg_rw;
+	vinfo->fd = -1;
+	vinfo->priv = NULL;
+
+	v_cfg = (char *)vinfo->base;
+
+	STORE_LE16((uint16_t *)&v_cfg[PCI_VENDOR_ID],
+		PCI_VENDOR_ID_INTEL);
+	STORE_LE16((uint16_t *)&v_cfg[PCI_DEVICE_ID],
+		IAVF_DEV_ID_ADAPTIVE_VF);
+	STORE_LE16((uint16_t *)&v_cfg[PCI_SUBSYSTEM_VENDOR_ID],
+		PCI_VENDOR_ID_INTEL);
+	STORE_LE16((uint16_t *)&v_cfg[PCI_SUBSYSTEM_ID],
+		   PCI_SUBDEVICE_ID);
+
+	STORE_LE16((uint16_t *)&v_cfg[PCI_COMMAND],
+		   PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+	STORE_LE16((uint16_t *)&v_cfg[PCI_CLASS_DEVICE],
+		   PCI_CLASS_ETHERNET);
+	v_cfg[PCI_CLASS_REVISION] = 0x01;
+
+	STORE_LE16((uint16_t *)&v_cfg[PCI_STATUS],
+			   PCI_STATUS_CAP_LIST);
+
+	STORE_LE32((uint32_t *)&v_cfg[PCI_BASE_ADDRESS_0],
+		   PCI_BASE_ADDRESS_SPACE_MEMORY |
+		   PCI_BASE_ADDRESS_MEM_TYPE_32	 |
+		   PCI_BASE_ADDRESS_MEM_PREFETCH);
+
+	STORE_LE32((uint32_t *)&v_cfg[PCI_BASE_ADDRESS_3],
+		   PCI_BASE_ADDRESS_SPACE_MEMORY |
+		   PCI_BASE_ADDRESS_MEM_TYPE_32);
+
+	STORE_LE16((uint16_t *)&v_cfg[PCI_CAPABILITY_LIST],
+			   0x70);
+	STORE_LE16((uint16_t *)&v_cfg[0x70],
+			   PCI_CAP_ID_MSIX);
+
+	STORE_LE16((uint16_t *)&v_cfg[0x70 + PCI_MSIX_FLAGS],
+			(IAVF_EMU_MSIX_TABLE_SIZE & PCI_MSIX_FLAGS_QSIZE) |
+			PCI_MSIX_FLAGS_ENABLE);
+
+	STORE_LE32((uint32_t *)&v_cfg[0x70 + PCI_MSIX_TABLE],
+			   (0x3 & PCI_MSIX_TABLE_BIR));
+
+	STORE_LE32((uint32_t *)&v_cfg[0x70 + PCI_MSIX_PBA],
+			(0x3 & PCI_MSIX_PBA_BIR) |
+			(0x100 & PCI_MSIX_PBA_OFFSET));
+
+	return 0;
+}
+
+static inline void iavf_emu_uninit_cfg_space(
+	struct rte_vfio_user_reg_info *vinfo)
+{
+	rte_free(vinfo->base);
+	vinfo->rw = NULL;
+	vinfo->info->size = 0;
+	vinfo->fd = -1;
+}
+static ssize_t iavf_emu_bar0_rw(struct rte_vfio_user_reg_info *reg, char *buf,
+	size_t count, loff_t pos, bool iswrite)
+{
+	struct iavf_emudev *dev = (struct iavf_emudev *)reg->priv;
+	char *reg_pos;
+
+	if (!reg->base) {
+		EMU_IAVF_LOG(ERR, "BAR 0 does not exist\n");
+		return -EFAULT;
+	}
+
+	if (pos + count > reg->info->size) {
+		EMU_IAVF_LOG(ERR, "Access exceeds BAR 0 size\n");
+		return -EINVAL;
+	}
+
+	reg_pos = (char *)reg->base + pos;
+
+	if (!iswrite) {
+		rte_memcpy(buf, reg_pos, count);
+	} else {
+		int tmp;
+		uint32_t val;
+		int idx = -1;
+
+		if (count != 4)
+			return -EINVAL;
+
+		val = *(uint32_t *)buf;
+		/* Only handle interrupt enable/disable for now */
+		if (pos == IAVF_VFINT_DYN_CTL01) {
+			tmp = val & IAVF_VFINT_DYN_CTL01_INTENA_MASK;
+			idx = 0;
+		} else if ((pos >= IAVF_VFINT_DYN_CTLN1(0)) && pos <=
+			IAVF_VFINT_DYN_CTLN1(RTE_IAVF_EMU_MAX_INTR - 1)) {
+			tmp = val & IAVF_VFINT_DYN_CTLN1_INTENA_MASK;
+			idx = pos - IAVF_VFINT_DYN_CTLN1(0);
+			if (idx % 4)
+				return -EINVAL;
+			idx = idx / 4;
+		}
+
+		if (idx != -1 &&
+			tmp != dev->intr->info[idx].enable && dev->ready) {
+			dev->ops->update_status(dev->edev);
+			dev->intr->info[idx].enable = tmp;
+		}
+
+		rte_memcpy(reg_pos, buf, count);
+	}
+
+	return count;
+}
+
+static int iavf_emu_alloc_reg(struct iavf_emudev *dev)
+{
+	struct rte_vfio_user_regions *reg = dev->vfio->reg;
+	struct rte_vfio_user_reg_info *vinfo;
+	char shm_str[64];
+	uint32_t i;
+	int ret;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		vinfo = &reg->reg_info[i];
+
+		switch (i) {
+		case VFIO_PCI_CONFIG_REGION_INDEX:
+			ret = iavf_emu_init_cfg_space(vinfo, dev->numa_node);
+			if (ret)
+				return ret;
+			break;
+		case VFIO_PCI_BAR0_REGION_INDEX:
+		case VFIO_PCI_BAR3_REGION_INDEX:
+			sprintf(shm_str, "AVF%d_BAR%d",
+				dev->edev->dev_id, i);
+			vinfo->fd = shm_open(shm_str,
+				O_RDWR|O_CREAT, 0700);
+			if (vinfo->fd == -1) {
+				EMU_IAVF_LOG(ERR,
+					"Failed to open shm for BAR %d\n", i);
+				goto exit;
+			}
+
+			if (ftruncate(vinfo->fd, vinfo->info->size) == -1) {
+				EMU_IAVF_LOG(ERR,
+					"Failed to ftruncate BAR %d\n", i);
+				ret = -1;
+				goto exit;
+			}
+
+			vinfo->base = mmap(NULL, vinfo->info->size,
+					PROT_READ | PROT_WRITE,
+					MAP_SHARED, vinfo->fd, 0);
+			memset(vinfo->base, 0, vinfo->info->size);
+			if (vinfo->base == MAP_FAILED) {
+				EMU_IAVF_LOG(ERR,
+					"Failed to mmap BAR %d\n", i);
+				ret = -1;
+				goto exit;
+			}
+			vinfo->priv = (void *)dev;
+			if (i == VFIO_PCI_BAR0_REGION_INDEX)
+				vinfo->rw = iavf_emu_bar0_rw;
+			else
+				vinfo->rw = NULL;
+			break;
+		default:
+			vinfo->base = NULL;
+			vinfo->rw = NULL;
+			vinfo->fd = -1;
+			vinfo->priv = NULL;
+			break;
+		}
+	}
+
+	return 0;
+
+exit:
+	for (;; i--) {
+		vinfo = &reg->reg_info[i];
+
+		if (i == VFIO_PCI_CONFIG_REGION_INDEX)
+			iavf_emu_uninit_cfg_space(vinfo);
+
+		if (!vinfo->info->size) {
+			if (!vinfo->base)
+				munmap(vinfo->base, vinfo->info->size);
+			if (vinfo->fd > 0) {
+				close(vinfo->fd);
+				sprintf(shm_str, "AVF%d_BAR%d",
+					dev->edev->dev_id, i);
+				shm_unlink(shm_str);
+				vinfo->fd = -1;
+			}
+		}
+
+		if (i == 0)
+			break;
+	}
+	return ret;
+}
+
+static void iavf_emu_free_reg(struct iavf_emudev *dev)
+{
+	struct rte_vfio_user_regions *reg = dev->vfio->reg;
+	struct rte_vfio_user_reg_info *vinfo;
+	char shm_str[64];
+	uint32_t i;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		vinfo = &reg->reg_info[i];
+
+		switch (i) {
+		case VFIO_PCI_CONFIG_REGION_INDEX:
+			iavf_emu_uninit_cfg_space(vinfo);
+			break;
+		case VFIO_PCI_BAR0_REGION_INDEX:
+			/* FALLTHROUGH */
+		case VFIO_PCI_BAR3_REGION_INDEX:
+			munmap(vinfo->base, vinfo->info->size);
+			close(vinfo->fd);
+			vinfo->fd = -1;
+			sprintf(shm_str, "AVF%d_BAR%d",
+				dev->edev->dev_id, i);
+			shm_unlink(shm_str);
+			vinfo->info->size = 0;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
 static inline struct iavf_emu_sock_list *
 iavf_emu_find_sock_list(char *sock_addr)
 {
@@ -382,3 +983,71 @@ int iavf_emu_unregister_vfio_user(struct iavf_emudev *dev)
 
 	return 0;
 }
+
+int iavf_emu_init_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+	struct iavf_emu_sock_list *list;
+
+	if (iavf_emu_init_dev(dev)) {
+		EMU_IAVF_LOG(ERR, "Emulated iavf dev init failed.\n");
+		ret = -1;
+		goto exit;
+	}
+
+	if (iavf_emu_alloc_reg(dev)) {
+		EMU_IAVF_LOG(ERR, "Emulated iavf alloc region failed.\n");
+		ret = -1;
+		goto err_alloc_reg;
+	}
+
+	ret = rte_vfio_user_set_dev_info(dev->sock_addr, dev->vfio->dev_info);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set vfio dev info\n");
+		goto err_set;
+	}
+
+	ret = rte_vfio_user_set_reg_info(dev->sock_addr, dev->vfio->reg);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set vfio region info\n");
+		goto err_set;
+	}
+
+	ret = rte_vfio_user_set_irq_info(dev->sock_addr, dev->vfio->irq);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set vfio irq info\n");
+		goto err_set;
+	}
+
+	list = rte_zmalloc_socket("list", sizeof(*list), 0, dev->numa_node);
+	list->emu_dev = dev->edev;
+	pthread_mutex_lock(&sock_list_lock);
+	TAILQ_INSERT_TAIL(&sock_list, list, next);
+	pthread_mutex_unlock(&sock_list_lock);
+
+	return 0;
+
+err_set:
+	iavf_emu_free_reg(dev);
+err_alloc_reg:
+	iavf_emu_uninit_dev(dev);
+exit:
+	return ret;
+}
+
+void iavf_emu_uninit_vfio_user(struct iavf_emudev *dev)
+{
+	iavf_emu_free_reg(dev);
+	iavf_emu_uninit_dev(dev);
+}
+
+int iavf_emu_start_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+
+	ret = rte_vfio_user_start(dev->sock_addr);
+	if (ret)
+		EMU_IAVF_LOG(ERR, "Start vfio user failed.\n");
+
+	return ret;
+}
diff --git a/drivers/emu/iavf/iavf_vfio_user.h b/drivers/emu/iavf/iavf_vfio_user.h
index aa2f3edc87..2ccb04eb48 100644
--- a/drivers/emu/iavf/iavf_vfio_user.h
+++ b/drivers/emu/iavf/iavf_vfio_user.h
@@ -5,12 +5,53 @@
 #ifndef _IAVF_VFIO_USER_H
 #define _IAVF_VFIO_USER_H
 
+#include <linux/pci_regs.h>
+
 #include <rte_vfio_user.h>
 
 #include "iavf_emu_internal.h"
 
+#define IAVF_EMU_CFG_SPACE_SIZE 0x100
+
+struct iavf_emu_pci_hdr {
+	uint16_t vid;		/* Vendor ID */
+	uint16_t did;		/* Device ID */
+	uint16_t cmd;		/* Command */
+	uint16_t status;	/* Status */
+	uint8_t rid;		/* Revision ID */
+	uint8_t cc_pi;		/* Program I/F in Class Code*/
+	uint8_t cc_sub;		/* Sub-Class Code */
+	uint8_t cc_base;	/* Base Class Code */
+	uint8_t cl_size;	/* Cache Line Size*/
+	uint8_t lt_timer;	/* Latency Timer */
+	uint8_t hdr_type;	/* Header Type */
+	uint8_t bist;		/* BIST */
+	uint32_t bar[6];	/* Base Address Registers */
+	uint32_t ccp;		/* Cardbus CIC Pointer */
+	uint16_t sub_vid;	/* Subsystem Vendor ID */
+	uint16_t sub_sid;	/* Subsystem ID */
+	uint32_t rom;		/* Expansion ROM Base Address */
+	uint8_t cap;		/* Capabilities Pointer */
+	uint8_t rsvd[7];	/* Reserved */
+	uint8_t intrl;		/* Interrupt Line */
+	uint8_t intrp;		/* Interrupt Pin */
+	uint8_t min_gnt;	/* Min_Gnt Register */
+	uint8_t max_lat;	/* Max_Lat Register */
+} __attribute((packed));
+
+struct iavf_emu_cfg_space {
+	struct iavf_emu_pci_hdr hdr;
+	uint8_t cfg_non_std[IAVF_EMU_CFG_SPACE_SIZE - PCI_STD_HEADER_SIZEOF];
+} __attribute((packed));
+
 int iavf_emu_register_vfio_user(struct iavf_emudev *dev);
 
 int iavf_emu_unregister_vfio_user(struct iavf_emudev *dev);
 
+int iavf_emu_init_vfio_user(struct iavf_emudev *dev);
+
+void iavf_emu_uninit_vfio_user(struct iavf_emudev *dev);
+
+int iavf_emu_start_vfio_user(struct iavf_emudev *dev);
+
 #endif
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
index 4f651258c2..3cab2226b7 100644
--- a/drivers/emu/iavf/meson.build
+++ b/drivers/emu/iavf/meson.build
@@ -1,6 +1,14 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2020 Intel Corporation
 
+librt = cc.find_library('rt', required: false)
+if not librt.found()
+	build = false
+	subdir_done()
+endif
+
+ext_deps += librt
+
 sources = files('iavf_emu.c', 'iavf_vfio_user.c',
 	'iavf_emudev.c')
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v2 6/8] emu/iavf: add emudev operations to fit in emudev framework
  2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
                     ` (4 preceding siblings ...)
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 5/8] emu/iavf: add resource management and internal logic of iavf Chenbo Xia
@ 2020-12-19  6:28   ` Chenbo Xia
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 7/8] test/emudev: introduce functional test Chenbo Xia
                     ` (3 subsequent siblings)
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-19  6:28 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This patch implements emudev opertions to make emulated iavf
fit into rte_emudev framework. Lifecycle related and device
resource related operations are both implemented.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
---
 drivers/emu/iavf/iavf_emu.c     | 218 ++++++++++++++++++++++++++++++++
 drivers/emu/iavf/rte_iavf_emu.h |  59 +++++++++
 2 files changed, 277 insertions(+)

diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index c1a702d744..9ad371ca98 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -2,7 +2,72 @@
  * Copyright(c) 2020 Intel Corporation
  */
 
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rte_malloc.h>
+#include <rte_emudev.h>
+
 #include "iavf_vfio_user.h"
+#include <iavf_type.h>
+
+static int iavf_emu_dev_start(struct rte_emudev *dev)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if (iavf->ops != NULL && iavf->ops->device_start != NULL)
+		iavf->ops->device_start(dev);
+
+	return 0;
+}
+
+static void iavf_emu_dev_stop(struct rte_emudev *dev)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if (iavf->ops != NULL && iavf->ops->device_stop != NULL)
+		iavf->ops->device_stop(dev);
+}
+
+static int iavf_emu_dev_configure(struct rte_emudev *dev,
+		struct rte_emudev_info *dev_conf)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+	struct rte_iavf_emu_config *conf =
+		(struct rte_iavf_emu_config *)dev_conf->dev_priv;
+
+	if (!dev_conf->dev_priv)
+		return -EINVAL;
+
+	/* Currently emulated iavf does not support max_qp_num
+	 * and region num configuration
+	 */
+	if (dev->dev_info.max_qp_num != dev_conf->max_qp_num ||
+		dev->dev_info.region_num != dev_conf->region_num) {
+		EMU_IAVF_LOG(ERR,
+			"Configure max_qp_num/region num not supported\n");
+		return -ENOTSUP;
+	}
+
+	if (conf->qp_num >  RTE_MAX_QUEUES_PER_PORT ||
+		conf->qp_num > RTE_IAVF_EMU_MAX_QP_NUM) {
+		EMU_IAVF_LOG(ERR, "Queue pair num exceeds max\n");
+		return -EINVAL;
+	}
+
+	/* For now, we don't support device configure when data
+	 * path driver is attached
+	 */
+	if (dev->backend_priv) {
+		EMU_IAVF_LOG(ERR, "Configure failed because of "
+			"data path attached\n");
+		return -EPERM;
+	}
+
+	iavf->max_be_lanqp = conf->qp_num;
+	return 0;
+}
 
 static int iavf_emu_dev_close(struct rte_emudev *dev)
 {
@@ -26,6 +91,159 @@ static int iavf_emu_dev_close(struct rte_emudev *dev)
 	return 0;
 }
 
+static int iavf_emu_get_dev_info(struct rte_emudev *dev,
+	rte_emudev_obj_t info)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+	struct rte_iavf_emu_config *conf = (struct rte_iavf_emu_config *)info;
+
+	if (!info)
+		return -EINVAL;
+
+	conf->qp_num = iavf->max_be_lanqp;
+	return 0;
+}
+
+static int iavf_emu_get_mem_table(struct rte_emudev *dev,
+	rte_emudev_mem_table_t *tb)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	*tb = iavf->mem;
+
+	return 0;
+}
+
+static int iavf_emu_get_queue_info(struct rte_emudev *dev, uint32_t queue,
+		struct rte_emudev_q_info *info)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if (queue < RTE_IAVF_EMU_ADMINQ_NUM) {
+		struct iavf_emu_adminQ *adq = &iavf->adq[queue];
+		uint64_t base, size;
+
+		if (adq->ring_addr_lo == NULL ||
+		    adq->ring_addr_hi == NULL ||
+		    adq->ring_sz == NULL)
+			return -1;
+		base = RTE_IAVF_EMU_32_TO_64(*adq->ring_addr_hi,
+			*adq->ring_addr_lo);
+		size = *adq->ring_sz;
+		info->base = base;
+		info->size = size;
+		info->doorbell_id = queue;
+		/* RX AdminQ should have IRQ vector 0 */
+		info->irq_vector = queue - 1;
+	} else {
+		info->base = 0;
+		info->size = 0;
+		info->doorbell_id = queue;
+		info->irq_vector = -1;
+	}
+
+	return 0;
+}
+
+static int iavf_emu_get_irq_info(struct rte_emudev *dev, uint32_t vector,
+		struct rte_emudev_irq_info *info)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+	struct iavf_emu_intr *intr = iavf->intr;
+	struct iavf_emu_intr_info *intr_info = &intr->info[vector];
+
+	if (vector >= intr->intr_num)
+		return -EINVAL;
+
+	info->eventfd = intr_info->fd;
+	info->enable = intr_info->enable;
+	info->vector = vector;
+
+	return 0;
+}
+
+static int iavf_emu_get_db_info(struct rte_emudev *dev, uint32_t doorbell,
+		struct rte_emudev_db_info *info)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if (doorbell < RTE_IAVF_EMU_ADMINQ_NUM) {
+		struct iavf_emu_adminQ *adq = &iavf->adq[doorbell];
+
+		info->data.mem.base = (uint64_t)adq->doorbell;
+		info->data.mem.size = adq->db_size;
+	} else {
+		struct iavf_emu_lanQ *lanq =
+			&iavf->lanq[doorbell - RTE_IAVF_EMU_ADMINQ_NUM];
+
+		info->data.mem.base = (uint64_t)lanq->doorbell;
+		info->data.mem.size = lanq->db_size;
+	}
+
+	info->flag |= RTE_EMUDEV_DB_MEM;
+	info->id = doorbell;
+
+	return 0;
+}
+
+static int iavf_emu_subs_ev(struct rte_emudev *dev,
+	rte_emudev_event_chnl_t ev_chnl)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	iavf->ops = (struct rte_iavf_emu_notify_ops *)ev_chnl;
+
+	return 0;
+}
+
+static int iavf_emu_unsubs_ev(struct rte_emudev *dev,
+		rte_emudev_event_chnl_t ev_chnl)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if ((struct rte_iavf_emu_notify_ops *)ev_chnl == iavf->ops) {
+		iavf->ops = NULL;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int iavf_emu_get_attr(struct rte_emudev *dev, const char *attr_name,
+		rte_emudev_attr_t attr)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+	struct rte_vfio_user_reg_info *info;
+	int ret = 0;
+
+	info = &iavf->vfio->reg->reg_info[0];
+
+	if (!strcmp(attr_name, RTE_IAVF_EMU_ATTR_ASQ_HEAD))
+		*(uint64_t *)attr = (uint64_t)(uintptr_t)info->base
+			+ IAVF_VF_ATQH1;
+	else if (!strcmp(attr_name, RTE_IAVF_EMU_ATTR_ARQ_HEAD))
+		*(uint64_t *)attr = (uint64_t)(uintptr_t)info->base
+			+ IAVF_VF_ARQH1;
+	else if (!strcmp(attr_name, RTE_IAVF_EMU_ATTR_RESET))
+		*(uint64_t *)attr = (uint64_t)(uintptr_t)info->base
+			+ IAVF_VFGEN_RSTAT;
+	else
+		ret = -EINVAL;
+
+	return ret;
+}
+
 struct rte_emudev_ops emu_iavf_ops = {
+	.dev_start = iavf_emu_dev_start,
+	.dev_stop = iavf_emu_dev_stop,
+	.dev_configure = iavf_emu_dev_configure,
 	.dev_close = iavf_emu_dev_close,
+	.dev_info_get = iavf_emu_get_dev_info,
+	.get_mem_table = iavf_emu_get_mem_table,
+	.get_queue_info = iavf_emu_get_queue_info,
+	.get_irq_info = iavf_emu_get_irq_info,
+	.get_db_info = iavf_emu_get_db_info,
+	.subscribe_event = iavf_emu_subs_ev,
+	.unsubscribe_event = iavf_emu_unsubs_ev,
+	.get_attr = iavf_emu_get_attr,
 };
diff --git a/drivers/emu/iavf/rte_iavf_emu.h b/drivers/emu/iavf/rte_iavf_emu.h
index 6de0989f0b..2abcec97d4 100644
--- a/drivers/emu/iavf/rte_iavf_emu.h
+++ b/drivers/emu/iavf/rte_iavf_emu.h
@@ -6,13 +6,24 @@
 #define _IAVF_EMU_H
 
 #include <stdint.h>
+#include <stddef.h>
+#include <limits.h>
+#include <net/if.h>
+#include <sys/queue.h>
 
+#include <rte_vfio_user.h>
 #include <rte_emudev.h>
 
 #define RTE_IAVF_EMUDEV_TYPE "iavf"
+#define RTE_IAVF_EMU_ATTR_ASQ_HEAD "ASQ_H"
+#define RTE_IAVF_EMU_ATTR_ARQ_HEAD "ARQ_H"
+#define RTE_IAVF_EMU_ATTR_RESET "RESET"
+#define RTE_IAVF_EMU_RESET_IN_PROGRESS 0x00
+#define RTE_IAVF_EMU_RESET_COMPLETED 0x01
 #define RTE_IAVF_EMU_MAX_MEM_REGIONS 256
 #define RTE_IAVF_EMU_MAX_QP_NUM 256
 #define RTE_IAVF_EMU_MAX_INTR 32
+#define RTE_IAVF_EMU_32_TO_64(hi, lo) ((((uint64_t)(hi)) << 32) + (lo))
 
 enum {
 	RTE_IAVF_EMU_ADMINQ_TXQ = 0,
@@ -26,6 +37,11 @@ enum {
 	RTE_IAVF_EMU_MAPPABLE_REG_NUM = 2,
 };
 
+struct rte_iavf_emu_config {
+	/* Maximum queue pair number that data path driver can use */
+	uint32_t qp_num;
+};
+
 struct rte_iavf_emu_mem_reg {
 	uint64_t guest_phys_addr;
 	uint64_t host_user_addr;
@@ -40,6 +56,49 @@ struct rte_iavf_emu_mem {
 	struct rte_iavf_emu_mem_reg regions[RTE_IAVF_EMU_MAX_MEM_REGIONS];
 };
 
+/**
+ * Helper function for data path driver to translate address
+ * of one region
+ *
+ * @param mem
+ *   A pointer to DMA memory table
+ * @param g_addr
+ *   Guest I/O virtual base address of the region
+ * @param[in/out] len
+ *   The length of region
+ * @return
+ *   - >0: Success, process virtual address returned
+ *   - 0: Failure on translation
+ */
+__rte_experimental
+__rte_always_inline uint64_t
+rte_iavf_emu_get_dma_vaddr(struct rte_iavf_emu_mem *mem,
+	uint64_t g_addr, uint64_t *len)
+{
+	struct rte_iavf_emu_mem_reg *reg;
+	uint32_t i;
+
+	for (i = 0; i < mem->region_num; i++) {
+		reg = &mem->regions[i];
+
+		if (g_addr >= reg->guest_phys_addr &&
+			g_addr < reg->guest_phys_addr + reg->size) {
+
+			if (unlikely(*len > reg->guest_phys_addr +
+				 reg->size - g_addr))
+				*len = reg->guest_phys_addr +
+					reg->size - g_addr;
+
+			return g_addr - reg->guest_phys_addr +
+				reg->host_user_addr;
+		}
+	}
+
+	*len = 0;
+
+	return 0;
+}
+
 struct rte_iavf_emu_notify_ops {
 	/* Device is ready */
 	int (*device_ready)(struct rte_emudev *dev);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v2 7/8] test/emudev: introduce functional test
  2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
                     ` (5 preceding siblings ...)
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 6/8] emu/iavf: add emudev operations to fit in emudev framework Chenbo Xia
@ 2020-12-19  6:28   ` Chenbo Xia
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 8/8] doc: update release notes for iavf emudev driver Chenbo Xia
                     ` (2 subsequent siblings)
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-19  6:28 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

This patch introduces functional test for emudev. The
implementation of iavf emudev selftest is also added.

Signed-off-by: Miao Li <miao.li@intel.com>
Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
---
 app/test/meson.build                 |   5 +-
 app/test/test_emudev.c               |  29 +++++
 drivers/emu/iavf/iavf_emu.c          |   1 +
 drivers/emu/iavf/iavf_emu_internal.h |   1 +
 drivers/emu/iavf/iavf_emu_test.c     | 174 +++++++++++++++++++++++++++
 drivers/emu/iavf/meson.build         |   2 +-
 6 files changed, 210 insertions(+), 2 deletions(-)
 create mode 100644 app/test/test_emudev.c
 create mode 100644 drivers/emu/iavf/iavf_emu_test.c

diff --git a/app/test/meson.build b/app/test/meson.build
index f5b15ac44c..b8b79bbc8b 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -139,6 +139,7 @@ test_sources = files('commands.c',
 	'test_trace_register.c',
 	'test_trace_perf.c',
 	'test_vfio_user.c',
+	'test_emudev.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
@@ -176,7 +177,8 @@ test_deps = ['acl',
 	'stack',
 	'vfio_user',
 	'telemetry',
-	'timer'
+	'timer',
+	'emudev'
 ]
 
 # Each test is marked with flag true/false
@@ -327,6 +329,7 @@ driver_test_names = [
         'eventdev_selftest_octeontx',
         'eventdev_selftest_sw',
         'rawdev_autotest',
+        'emudev_autotest',
 ]
 
 dump_test_names = [
diff --git a/app/test/test_emudev.c b/app/test/test_emudev.c
new file mode 100644
index 0000000000..0dfce162ed
--- /dev/null
+++ b/app/test/test_emudev.c
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <rte_emudev.h>
+#include <rte_bus_vdev.h>
+
+#include "test.h"
+
+static int
+test_emudev_selftest_impl(const char *pmd, const char *opts)
+{
+	int ret = 0;
+
+	if (rte_emudev_get_dev_id(pmd) == -ENODEV)
+		ret = rte_vdev_init(pmd, opts);
+	if (ret)
+		return TEST_SKIPPED;
+
+	return rte_emudev_selftest(rte_emudev_get_dev_id(pmd));
+}
+
+static int
+test_emudev_selftest(void)
+{
+	return test_emudev_selftest_impl("emu_iavf", "sock=/tmp/sock1");
+}
+
+REGISTER_TEST_COMMAND(emudev_autotest, test_emudev_selftest);
diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index 9ad371ca98..88bf2bdf94 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -246,4 +246,5 @@ struct rte_emudev_ops emu_iavf_ops = {
 	.subscribe_event = iavf_emu_subs_ev,
 	.unsubscribe_event = iavf_emu_unsubs_ev,
 	.get_attr = iavf_emu_get_attr,
+	.dev_selftest = iavf_emu_selftest,
 };
diff --git a/drivers/emu/iavf/iavf_emu_internal.h b/drivers/emu/iavf/iavf_emu_internal.h
index 10197c00ba..1ac7f96566 100644
--- a/drivers/emu/iavf/iavf_emu_internal.h
+++ b/drivers/emu/iavf/iavf_emu_internal.h
@@ -65,4 +65,5 @@ struct iavf_emudev {
 };
 
 void iavf_emu_uninit_device(struct iavf_emudev *dev);
+int iavf_emu_selftest(uint16_t dev_id);
 #endif
diff --git a/drivers/emu/iavf/iavf_emu_test.c b/drivers/emu/iavf/iavf_emu_test.c
new file mode 100644
index 0000000000..ad19134724
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emu_test.c
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <inttypes.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_memcpy.h>
+#include <rte_dev.h>
+#include <rte_emudev.h>
+#include <rte_bus_vdev.h>
+#include <rte_test.h>
+
+#include "iavf_emu_internal.h"
+
+#define TEST_DEV_NAME "emu_iavf"
+#define TEST_SUCCESS 0
+#define TEST_FAILED -1
+
+#define EMUDEV_TEST_RUN(setup, teardown, test) \
+	emudev_test_run(setup, teardown, test, #test)
+static uint16_t test_dev_id;
+static int total;
+static int passed;
+static int failed;
+static int unsupported;
+
+static int
+testsuite_setup(void)
+{
+	uint8_t count;
+	count = rte_emudev_count();
+	if (!count) {
+		EMU_IAVF_LOG(INFO, "No existing emu dev; "
+				  "Creating emu_iavf\n");
+		return rte_vdev_init(TEST_DEV_NAME, NULL);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static void
+testsuite_teardown(void)
+{
+	rte_vdev_uninit(TEST_DEV_NAME);
+}
+
+static void emudev_test_run(int (*setup)(void),
+			     void (*teardown)(void),
+			     int (*test)(void),
+			     const char *name)
+{
+	int ret = 0;
+
+	if (setup) {
+		ret = setup();
+		if (ret < 0) {
+			EMU_IAVF_LOG(INFO, "Error setting up test %s\n", name);
+			unsupported++;
+		}
+	}
+
+	if (test) {
+		ret = test();
+		if (ret < 0) {
+			failed++;
+			EMU_IAVF_LOG(INFO, "%s Failed\n", name);
+		} else {
+			passed++;
+			EMU_IAVF_LOG(INFO, "%s Passed\n", name);
+		}
+	}
+
+	if (teardown)
+		teardown();
+
+	total++;
+}
+
+static int
+test_emu_dev_count(void)
+{
+	uint8_t count;
+	count = rte_emudev_count();
+	RTE_TEST_ASSERT(count > 0, "Invalid emudev count %" PRIu8, count);
+	return TEST_SUCCESS;
+}
+
+static int
+test_emu_dev_get_dev_id(void)
+{
+	int ret;
+	ret = rte_emudev_get_dev_id("Invalid_emu_dev_device\n");
+	RTE_TEST_ASSERT_FAIL(ret, "Expected <0 for invalid dev name, ret=%d",
+			     ret);
+	return TEST_SUCCESS;
+}
+
+static int
+test_emu_dev_configure(void)
+{
+	int ret;
+	struct rte_emudev_info dev_conf;
+	struct rte_iavf_emu_config iavf_conf_set = {.qp_num = 1};
+	struct rte_iavf_emu_config iavf_conf_get = {0};
+
+	rte_emudev_stop(test_dev_id);
+
+	/* Check invalid configuration */
+	ret = rte_emudev_configure(test_dev_id, NULL);
+	RTE_TEST_ASSERT(ret == -EINVAL,
+			"Null configure; Expected -EINVAL, got %d", ret);
+
+	/* Valid configuration test */
+	dev_conf.dev_priv = (rte_emudev_obj_t)&iavf_conf_get;
+	ret = rte_emudev_get_dev_info(test_dev_id, &dev_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret,
+				"Failed to obtain emudev configuration (%d)",
+				ret);
+	dev_conf.dev_priv = (rte_emudev_obj_t)&iavf_conf_set;
+	ret = rte_emudev_configure(test_dev_id, &dev_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure emudev (%d)", ret);
+
+	memset(&iavf_conf_get, 0, sizeof(iavf_conf_get));
+	dev_conf.dev_priv = (rte_emudev_obj_t)&iavf_conf_get;
+	ret = rte_emudev_get_dev_info(test_dev_id, &dev_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret,
+				"Failed to obtain emudev configuration (%d)",
+				ret);
+
+	RTE_TEST_ASSERT_EQUAL(iavf_conf_set.qp_num,
+			      iavf_conf_get.qp_num,
+			      "Configuration test failed; num_queues (%d)(%d)",
+			      iavf_conf_set.qp_num,
+			      iavf_conf_get.qp_num);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_emu_dev_start_stop(void)
+{
+	int ret;
+	ret = rte_emudev_start(test_dev_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to start emudev (%d)", ret);
+
+	rte_emudev_stop(test_dev_id);
+
+	return TEST_SUCCESS;
+}
+
+int iavf_emu_selftest(uint16_t dev_id)
+{
+	test_dev_id = dev_id;
+	testsuite_setup();
+
+	EMUDEV_TEST_RUN(NULL, NULL, test_emu_dev_count);
+	EMUDEV_TEST_RUN(NULL, NULL, test_emu_dev_get_dev_id);
+	EMUDEV_TEST_RUN(NULL, NULL, test_emu_dev_configure);
+	EMUDEV_TEST_RUN(NULL, NULL, test_emu_dev_start_stop);
+
+	testsuite_teardown();
+
+	EMU_IAVF_LOG(INFO, "Total tests   : %d\n", total);
+	EMU_IAVF_LOG(INFO, "Passed        : %d\n", passed);
+	EMU_IAVF_LOG(INFO, "Failed        : %d\n", failed);
+	EMU_IAVF_LOG(INFO, "Not supported : %d\n", unsupported);
+
+	if (failed)
+		return -1;
+
+	return 0;
+}
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
index 3cab2226b7..613783e407 100644
--- a/drivers/emu/iavf/meson.build
+++ b/drivers/emu/iavf/meson.build
@@ -10,7 +10,7 @@ endif
 ext_deps += librt
 
 sources = files('iavf_emu.c', 'iavf_vfio_user.c',
-	'iavf_emudev.c')
+	'iavf_emu_test.c', 'iavf_emudev.c')
 
 deps += ['bus_vdev', 'emudev', 'vfio_user', 'common_iavf']
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v2 8/8] doc: update release notes for iavf emudev driver
  2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
                     ` (6 preceding siblings ...)
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 7/8] test/emudev: introduce functional test Chenbo Xia
@ 2020-12-19  6:28   ` Chenbo Xia
  2021-01-13 16:52   ` [dpdk-dev] [PATCH v2 0/8] Introduce emudev library and " Thomas Monjalon
  2021-01-14  6:25   ` [dpdk-dev] [PATCH v3 " Chenbo Xia
  9 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2020-12-19  6:28 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu

Update release notes for emulated iavf driver.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
---
 doc/guides/rel_notes/release_21_02.rst | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/doc/guides/rel_notes/release_21_02.rst b/doc/guides/rel_notes/release_21_02.rst
index 3d26b6b580..b310b67b7d 100644
--- a/doc/guides/rel_notes/release_21_02.rst
+++ b/doc/guides/rel_notes/release_21_02.rst
@@ -67,7 +67,7 @@ New Features
 
   See :doc:`../prog_guide/vfio_user_lib` for more information.
 
-* **Added emudev Library.**
+* **Added emudev Library and first emudev driver.**
 
   Added an experimental library ``librte_emudev`` to provide device abstraction
   for an emulated device.
@@ -77,6 +77,10 @@ New Features
   crypto and etc.). It can be attached to another data path driver (e.g, ethdev
   driver) to leverage the high performance of DPDK data path driver.
 
+  The first emudev driver is also introduced, which emulates software iavf
+  devices. This driver emulates all iavf device behavior except data path
+  handling.
+
   See :doc:`../prog_guide/emudev` for more information.
 
 Removed Items
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver
  2020-12-19  6:11   ` Xia, Chenbo
@ 2020-12-21  9:52     ` Maxime Coquelin
  2020-12-21 12:01       ` Maxime Coquelin
  0 siblings, 1 reply; 44+ messages in thread
From: Maxime Coquelin @ 2020-12-21  9:52 UTC (permalink / raw)
  To: Xia, Chenbo, David Marchand
  Cc: dev, Thomas Monjalon, Stephen Hemminger, Liang, Cunming, Lu,
	Xiuchun, Li, Miao, Wu, Jingjing

Hi Chenbo,

On 12/19/20 7:11 AM, Xia, Chenbo wrote:
> Hi David,
> 
>> -----Original Message-----
>> From: David Marchand <david.marchand@redhat.com>
>> Sent: Friday, December 18, 2020 5:54 PM
>> To: Xia, Chenbo <chenbo.xia@intel.com>
>> Cc: dev <dev@dpdk.org>; Thomas Monjalon <thomas@monjalon.net>; Stephen
>> Hemminger <stephen@networkplumber.org>; Liang, Cunming
>> <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li, Miao
>> <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>
>> Subject: Re: [PATCH 0/8] Introduce emudev library and iavf emudev driver
>>
>> On Fri, Dec 18, 2020 at 9:02 AM Chenbo Xia <chenbo.xia@intel.com> wrote:
>>>
>>> This series introduces a new device abstraction called emudev for
>> emulated
>>> devices. A new library (librte_emudev) is implemented. The first emudev
>>> driver is also introduced, which emulates Intel Adaptive Virtual
>> Function
>>> (iavf) as a software network device.
>>>
>>> This series has a dependency on librte_vfio_user patch series:
>>> http://patchwork.dpdk.org/cover/85389/
>>>
>>> Background & Motivation
>>> -----------------------
>>> The disaggregated/multi-process QEMU is using VFIO-over-socket/vfio-user
>>> as the main transport mechanism to disaggregate IO services from QEMU.
>>> Therefore, librte_vfio_user is introduced in DPDK to accommodate
>>> emulated devices for high performance I/O. Although vfio-user library
>>> provides possibility of emulating devices in DPDK, DPDK does not have
>>> a device abstraction for emulated devices. A good device abstraction
>> will
>>> be useful for applications or high performance data path driver. With
>>> this consideration, emudev library is designed and implemented. It also
>>> make it possbile to keep modular design on emulated devices by
>> implementing
>>> data path related logic in a standalone driver (e.g., an ethdev driver)
>>> and keeps the unrelated logic in the emudev driver.
>>
>> Since you mention performance, how does it compare to vhost-user/virtio?
> 
> I think it depends on the device specification (i.e., how complex its data path
> handling is). A first try on iavf spec shows better performance than virtio
> in our simple tests.

That's interesting! How big is the performance difference? And how do
we explain it?

If there are improvements that could be done in the Virtio
specification, it would be great to know and work on their
implementations. It worries me a bit that every one is coming with
his new device emulation every release, making things like live-
migration difficult to achieve in the future.

Regards,
Maxime

> Thanks!
> Chenbo
> 
>>
>>
>> --
>> David Marchand
> 


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver
  2020-12-21  9:52     ` Maxime Coquelin
@ 2020-12-21 12:01       ` Maxime Coquelin
  2020-12-22  3:09         ` Xia, Chenbo
  0 siblings, 1 reply; 44+ messages in thread
From: Maxime Coquelin @ 2020-12-21 12:01 UTC (permalink / raw)
  To: Xia, Chenbo, Thomas Monjalon, David Marchand
  Cc: dev, Stephen Hemminger, Liang, Cunming, Lu, Xiuchun, Li, Miao,
	Wu, Jingjing



On 12/21/20 10:52 AM, Maxime Coquelin wrote:
> Hi Chenbo,
> 
> On 12/19/20 7:11 AM, Xia, Chenbo wrote:
>> Hi David,
>>
>>> -----Original Message-----
>>> From: David Marchand <david.marchand@redhat.com>
>>> Sent: Friday, December 18, 2020 5:54 PM
>>> To: Xia, Chenbo <chenbo.xia@intel.com>
>>> Cc: dev <dev@dpdk.org>; Thomas Monjalon <thomas@monjalon.net>; Stephen
>>> Hemminger <stephen@networkplumber.org>; Liang, Cunming
>>> <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li, Miao
>>> <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>
>>> Subject: Re: [PATCH 0/8] Introduce emudev library and iavf emudev driver
>>>
>>> On Fri, Dec 18, 2020 at 9:02 AM Chenbo Xia <chenbo.xia@intel.com> wrote:
>>>>
>>>> This series introduces a new device abstraction called emudev for
>>> emulated
>>>> devices. A new library (librte_emudev) is implemented. The first emudev
>>>> driver is also introduced, which emulates Intel Adaptive Virtual
>>> Function
>>>> (iavf) as a software network device.
>>>>
>>>> This series has a dependency on librte_vfio_user patch series:
>>>> http://patchwork.dpdk.org/cover/85389/
>>>>
>>>> Background & Motivation
>>>> -----------------------
>>>> The disaggregated/multi-process QEMU is using VFIO-over-socket/vfio-user
>>>> as the main transport mechanism to disaggregate IO services from QEMU.
>>>> Therefore, librte_vfio_user is introduced in DPDK to accommodate
>>>> emulated devices for high performance I/O. Although vfio-user library
>>>> provides possibility of emulating devices in DPDK, DPDK does not have
>>>> a device abstraction for emulated devices. A good device abstraction
>>> will
>>>> be useful for applications or high performance data path driver. With
>>>> this consideration, emudev library is designed and implemented. It also
>>>> make it possbile to keep modular design on emulated devices by
>>> implementing
>>>> data path related logic in a standalone driver (e.g., an ethdev driver)
>>>> and keeps the unrelated logic in the emudev driver.
>>>
>>> Since you mention performance, how does it compare to vhost-user/virtio?
>>
>> I think it depends on the device specification (i.e., how complex its data path
>> handling is). A first try on iavf spec shows better performance than virtio
>> in our simple tests.
> 
> That's interesting! How big is the performance difference? And how do
> we explain it?
> 
> If there are improvements that could be done in the Virtio
> specification, it would be great to know and work on their
> implementations. It worries me a bit that every one is coming with
> his new device emulation every release, making things like live-
> migration difficult to achieve in the future.

I did a quick review of the IAVF emudev driver to understand what other
factors than ring layout could explain a performance gain.

My understanding is that part of the performance gain may come from
following things that are supported/implemented in Vhost-user backend
and not in IAVF driver:
1. Memory hotplug. It seems the datapath is not safe against memory
hotplug in the VM, which causes the memory tables to be updated
asynchronously from the datapath. In order to support it in Vhost-user
library, we had to introduce locks to ensure the datapath isn't
accessing the shared memory while it is being remapped.

2. Live-migration. This feature does not seem supported in the driver,
as I could not find dirty pages tracking mechanism. On Vhost-user side,
supporting implies adding more branch conditions in the hot path, even
when it is not used.

3. No IOMMU support. Same here, this is supported in Vhost-user library,
and adding its support in IAVF driver would introduce some more branches
in the hot path even when not used.

4. Out of bound memory accesses checks. While
rte_iavf_emu_get_dma_vaddr() provides a way to ensure the full requested
length is mapped, the data path does not use it. It does not even ensure
the translated address is non-NULL. It makes it trivial for a malicious
guest to make the hypervisor's vSwitch to crash by simply passing random
values as buffer's address and length. Fixing it is trivial, but it will
add several more checks and loops (if a buffer is spanned across two
pages) in the hot path.

Other than that, there is for sure a performance gain due to all the
features Virtio-net supports that we have to check and handle in the
hotpath, like indirect descriptors or mergeable buffers for example.

Best regards,
Maxime

> Regards,
> Maxime
> 
>> Thanks!
>> Chenbo
>>
>>>
>>>
>>> --
>>> David Marchand
>>


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver
  2020-12-21 12:01       ` Maxime Coquelin
@ 2020-12-22  3:09         ` Xia, Chenbo
  2020-12-22  8:48           ` Maxime Coquelin
  0 siblings, 1 reply; 44+ messages in thread
From: Xia, Chenbo @ 2020-12-22  3:09 UTC (permalink / raw)
  To: Maxime Coquelin, Thomas Monjalon, David Marchand
  Cc: dev, Stephen Hemminger, Liang, Cunming, Lu, Xiuchun, Li, Miao,
	Wu, Jingjing

Hi Maxime,

> -----Original Message-----
> From: Maxime Coquelin <maxime.coquelin@redhat.com>
> Sent: Monday, December 21, 2020 8:02 PM
> To: Xia, Chenbo <chenbo.xia@intel.com>; Thomas Monjalon <thomas@monjalon.net>;
> David Marchand <david.marchand@redhat.com>
> Cc: dev <dev@dpdk.org>; Stephen Hemminger <stephen@networkplumber.org>; Liang,
> Cunming <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li,
> Miao <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>
> Subject: Re: [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev
> driver
> 
> 
> 
> On 12/21/20 10:52 AM, Maxime Coquelin wrote:
> > Hi Chenbo,
> >
> > On 12/19/20 7:11 AM, Xia, Chenbo wrote:
> >> Hi David,
> >>
> >>> -----Original Message-----
> >>> From: David Marchand <david.marchand@redhat.com>
> >>> Sent: Friday, December 18, 2020 5:54 PM
> >>> To: Xia, Chenbo <chenbo.xia@intel.com>
> >>> Cc: dev <dev@dpdk.org>; Thomas Monjalon <thomas@monjalon.net>; Stephen
> >>> Hemminger <stephen@networkplumber.org>; Liang, Cunming
> >>> <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li, Miao
> >>> <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>
> >>> Subject: Re: [PATCH 0/8] Introduce emudev library and iavf emudev driver
> >>>
> >>> On Fri, Dec 18, 2020 at 9:02 AM Chenbo Xia <chenbo.xia@intel.com> wrote:
> >>>>
> >>>> This series introduces a new device abstraction called emudev for
> >>> emulated
> >>>> devices. A new library (librte_emudev) is implemented. The first emudev
> >>>> driver is also introduced, which emulates Intel Adaptive Virtual
> >>> Function
> >>>> (iavf) as a software network device.
> >>>>
> >>>> This series has a dependency on librte_vfio_user patch series:
> >>>> http://patchwork.dpdk.org/cover/85389/
> >>>>
> >>>> Background & Motivation
> >>>> -----------------------
> >>>> The disaggregated/multi-process QEMU is using VFIO-over-socket/vfio-user
> >>>> as the main transport mechanism to disaggregate IO services from QEMU.
> >>>> Therefore, librte_vfio_user is introduced in DPDK to accommodate
> >>>> emulated devices for high performance I/O. Although vfio-user library
> >>>> provides possibility of emulating devices in DPDK, DPDK does not have
> >>>> a device abstraction for emulated devices. A good device abstraction
> >>> will
> >>>> be useful for applications or high performance data path driver. With
> >>>> this consideration, emudev library is designed and implemented. It also
> >>>> make it possbile to keep modular design on emulated devices by
> >>> implementing
> >>>> data path related logic in a standalone driver (e.g., an ethdev driver)
> >>>> and keeps the unrelated logic in the emudev driver.
> >>>
> >>> Since you mention performance, how does it compare to vhost-user/virtio?
> >>
> >> I think it depends on the device specification (i.e., how complex its data
> path
> >> handling is). A first try on iavf spec shows better performance than virtio
> >> in our simple tests.
> >
> > That's interesting! How big is the performance difference? And how do
> > we explain it?
> >
> > If there are improvements that could be done in the Virtio
> > specification, it would be great to know and work on their
> > implementations. It worries me a bit that every one is coming with
> > his new device emulation every release, making things like live-
> > migration difficult to achieve in the future.
> 
> I did a quick review of the IAVF emudev driver to understand what other
> factors than ring layout could explain a performance gain.
> 
> My understanding is that part of the performance gain may come from
> following things that are supported/implemented in Vhost-user backend
> and not in IAVF driver:
> 1. Memory hotplug. It seems the datapath is not safe against memory
> hotplug in the VM, which causes the memory tables to be updated
> asynchronously from the datapath. In order to support it in Vhost-user
> library, we had to introduce locks to ensure the datapath isn't
> accessing the shared memory while it is being remapped.

I think now it uses the similar way that vhost-user does.

First, in the vfio-user patch series, we introduce a callback lock_dp to lock
the data path when messages like DMA MAP/UNMAP come. It will lock datapath
in our data path driver.

Note that the data path handling is in our data path driver:
http://patchwork.dpdk.org/cover/85500/

For modular design, iavf_emu driver emulates the device but the iavf back-end
driver does data path handling.

> 
> 2. Live-migration. This feature does not seem supported in the driver,
> as I could not find dirty pages tracking mechanism. On Vhost-user side,
> supporting implies adding more branch conditions in the hot path, even
> when it is not used.

Yes, we don't support this now in this version. And yes, when we support this
feature, it will introduce complexity in data path.

> 
> 3. No IOMMU support. Same here, this is supported in Vhost-user library,
> and adding its support in IAVF driver would introduce some more branches
> in the hot path even when not used.

Yes, vIOMMU is not fully supported in vfio-user spec for now and I also agree
when we have to support it, it will slow down the data path.

> 
> 4. Out of bound memory accesses checks. While
> rte_iavf_emu_get_dma_vaddr() provides a way to ensure the full requested
> length is mapped, the data path does not use it. It does not even ensure

In fact, it uses it 😊. I think you may miss our data path driver. Here's a 
use: http://patchwork.dpdk.org/patch/85504/

> the translated address is non-NULL. It makes it trivial for a malicious
> guest to make the hypervisor's vSwitch to crash by simply passing random
> values as buffer's address and length. Fixing it is trivial, but it will
> add several more checks and loops (if a buffer is spanned across two
> pages) in the hot path.

I don't quite understand this one. First, rte_iavf_emu_get_dma_vaddr() is the
only way to translate address. And I think this function will ensure the input
address is valid. Looking at the vhost side, vhost_iova_to_vva() does similar
things when vIOMMU is not used. Do I miss something? Just correct me if I am
wrong.

> 
> Other than that, there is for sure a performance gain due to all the
> features Virtio-net supports that we have to check and handle in the
> hotpath, like indirect descriptors or mergeable buffers for example.

I think iavf has similar features like indirect and mergeable to recv/xmit
large pkts. But I believe there will be some feature difference between
iavf and virtio/vhost.

I think you are correct that for this version, it's not fair to compare virtio/vhost
with iavf back-end because iavf back-end has not supported some features, and besides,
we have not optimized the data path of iavf back-end. We expect the performance of
iavf back-end in the same level of virtio 1.1 and hopefully better because of the ring
layout. But let's see when we can do complete performance analysis 😊.

Thanks!
Chenbo

> 
> Best regards,
> Maxime
> 
> > Regards,
> > Maxime
> >
> >> Thanks!
> >> Chenbo
> >>
> >>>
> >>>
> >>> --
> >>> David Marchand
> >>


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver
  2020-12-22  3:09         ` Xia, Chenbo
@ 2020-12-22  8:48           ` Maxime Coquelin
  2020-12-23  5:28             ` Xia, Chenbo
  0 siblings, 1 reply; 44+ messages in thread
From: Maxime Coquelin @ 2020-12-22  8:48 UTC (permalink / raw)
  To: Xia, Chenbo, Thomas Monjalon, David Marchand
  Cc: dev, Stephen Hemminger, Liang, Cunming, Lu, Xiuchun, Li, Miao,
	Wu, Jingjing

Hi Chenbo,

Thanks for the detailed reply.

On 12/22/20 4:09 AM, Xia, Chenbo wrote:
> Hi Maxime,
> 
>> -----Original Message-----
>> From: Maxime Coquelin <maxime.coquelin@redhat.com>
>> Sent: Monday, December 21, 2020 8:02 PM
>> To: Xia, Chenbo <chenbo.xia@intel.com>; Thomas Monjalon <thomas@monjalon.net>;
>> David Marchand <david.marchand@redhat.com>
>> Cc: dev <dev@dpdk.org>; Stephen Hemminger <stephen@networkplumber.org>; Liang,
>> Cunming <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li,
>> Miao <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>
>> Subject: Re: [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev
>> driver
>>
>>
>>
>> On 12/21/20 10:52 AM, Maxime Coquelin wrote:
>>> Hi Chenbo,
>>>
>>> On 12/19/20 7:11 AM, Xia, Chenbo wrote:
>>>> Hi David,
>>>>
>>>>> -----Original Message-----
>>>>> From: David Marchand <david.marchand@redhat.com>
>>>>> Sent: Friday, December 18, 2020 5:54 PM
>>>>> To: Xia, Chenbo <chenbo.xia@intel.com>
>>>>> Cc: dev <dev@dpdk.org>; Thomas Monjalon <thomas@monjalon.net>; Stephen
>>>>> Hemminger <stephen@networkplumber.org>; Liang, Cunming
>>>>> <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li, Miao
>>>>> <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>
>>>>> Subject: Re: [PATCH 0/8] Introduce emudev library and iavf emudev driver
>>>>>
>>>>> On Fri, Dec 18, 2020 at 9:02 AM Chenbo Xia <chenbo.xia@intel.com> wrote:
>>>>>>
>>>>>> This series introduces a new device abstraction called emudev for
>>>>> emulated
>>>>>> devices. A new library (librte_emudev) is implemented. The first emudev
>>>>>> driver is also introduced, which emulates Intel Adaptive Virtual
>>>>> Function
>>>>>> (iavf) as a software network device.
>>>>>>
>>>>>> This series has a dependency on librte_vfio_user patch series:
>>>>>> http://patchwork.dpdk.org/cover/85389/
>>>>>>
>>>>>> Background & Motivation
>>>>>> -----------------------
>>>>>> The disaggregated/multi-process QEMU is using VFIO-over-socket/vfio-user
>>>>>> as the main transport mechanism to disaggregate IO services from QEMU.
>>>>>> Therefore, librte_vfio_user is introduced in DPDK to accommodate
>>>>>> emulated devices for high performance I/O. Although vfio-user library
>>>>>> provides possibility of emulating devices in DPDK, DPDK does not have
>>>>>> a device abstraction for emulated devices. A good device abstraction
>>>>> will
>>>>>> be useful for applications or high performance data path driver. With
>>>>>> this consideration, emudev library is designed and implemented. It also
>>>>>> make it possbile to keep modular design on emulated devices by
>>>>> implementing
>>>>>> data path related logic in a standalone driver (e.g., an ethdev driver)
>>>>>> and keeps the unrelated logic in the emudev driver.
>>>>>
>>>>> Since you mention performance, how does it compare to vhost-user/virtio?
>>>>
>>>> I think it depends on the device specification (i.e., how complex its data
>> path
>>>> handling is). A first try on iavf spec shows better performance than virtio
>>>> in our simple tests.
>>>
>>> That's interesting! How big is the performance difference? And how do
>>> we explain it?
>>>
>>> If there are improvements that could be done in the Virtio
>>> specification, it would be great to know and work on their
>>> implementations. It worries me a bit that every one is coming with
>>> his new device emulation every release, making things like live-
>>> migration difficult to achieve in the future.
>>
>> I did a quick review of the IAVF emudev driver to understand what other
>> factors than ring layout could explain a performance gain.
>>
>> My understanding is that part of the performance gain may come from
>> following things that are supported/implemented in Vhost-user backend
>> and not in IAVF driver:
>> 1. Memory hotplug. It seems the datapath is not safe against memory
>> hotplug in the VM, which causes the memory tables to be updated
>> asynchronously from the datapath. In order to support it in Vhost-user
>> library, we had to introduce locks to ensure the datapath isn't
>> accessing the shared memory while it is being remapped.
> 
> I think now it uses the similar way that vhost-user does.
> 
> First, in the vfio-user patch series, we introduce a callback lock_dp to lock
> the data path when messages like DMA MAP/UNMAP come. It will lock datapath
> in our data path driver.
> 
> Note that the data path handling is in our data path driver:
> http://patchwork.dpdk.org/cover/85500/
> 
> For modular design, iavf_emu driver emulates the device but the iavf back-end
> driver does data path handling.

My analysis was actually based on the data path driver series.

My point was that iavfbe_recv_pkts() and iavfbe_xmit_pkts() are not safe
against asynchronous changes like memory table updates.

As far as I can see, the access_lock aren't taken by these functions, so
if for example a memory table update happen during the these functions
execution, it could lead to undefined behaviour. Only things checked
there is whether the queue is enabled when entering the function, but
this value can be changed right after having being checked.

For example, in Vhost-user lib, we protected rte_vhost_dequeue_burst()
and rte_vhost_enqueue_burst() with a spinlock. Note that this spinlock
is per-virtqueue in order to avoid contention between the different
queues.

>>
>> 2. Live-migration. This feature does not seem supported in the driver,
>> as I could not find dirty pages tracking mechanism. On Vhost-user side,
>> supporting implies adding more branch conditions in the hot path, even
>> when it is not used.
> 
> Yes, we don't support this now in this version. And yes, when we support this
> feature, it will introduce complexity in data path.
> 
>>
>> 3. No IOMMU support. Same here, this is supported in Vhost-user library,
>> and adding its support in IAVF driver would introduce some more branches
>> in the hot path even when not used.
> 
> Yes, vIOMMU is not fully supported in vfio-user spec for now and I also agree
> when we have to support it, it will slow down the data path.
> 
>>
>> 4. Out of bound memory accesses checks. While
>> rte_iavf_emu_get_dma_vaddr() provides a way to ensure the full requested
>> length is mapped, the data path does not use it. It does not even ensure
> 
> In fact, it uses it 😊. I think you may miss our data path driver. Here's a 
> use: http://patchwork.dpdk.org/patch/85504/

Sorry, I was not clear. What I meant is that
rte_iavf_emu_get_dma_vaddr() is indeed used, but its outputs aren't
checked properly.

>> the translated address is non-NULL. It makes it trivial for a malicious
>> guest to make the hypervisor's vSwitch to crash by simply passing random
>> values as buffer's address and length. Fixing it is trivial, but it will
>> add several more checks and loops (if a buffer is spanned across two
>> pages) in the hot path.
> 
> I don't quite understand this one. First, rte_iavf_emu_get_dma_vaddr() is the
> only way to translate address. And I think this function will ensure the input
> address is valid. Looking at the vhost side, vhost_iova_to_vva() does similar
> things when vIOMMU is not used. Do I miss something? Just correct me if I am
> wrong.

I think rte_iavf_emu_get_dma_vaddr() is good, the issue is the way it is
used in iavfbe_recv_pkts() and iavfbe_xmit_pkts().

First the returned address (desc_addr) is not checked. So if the start
address of the buffer is out of guest memory ranges, the host app will
crash with a segmentation fault. It can happen in case of bug in the
guest driver, or with a maliciously crafted descriptor.

Then, rte_iavf_emu_get_dma_vaddr() len parameter is a pointer. The
caller has to pass the length of the buffer it wants to map. Once
rte_iavf_emu_get_dma_vaddr() is updated with the contiguous length that
is mapped.

The caller needs to check whether all requested length is mapped, and
loop until it is all mapped:
http://code.dpdk.org/dpdk/latest/source/lib/librte_vhost/virtio_net.c#L484

This is required because a buffer contiguous in IOVA (guest physical
address space in this case) is not necessarily contiguous in Host
virtual address space. Also, this is required to be safe against
untrusted guests, as a malicious guest could pass ~0ULL in the
descriptor buffer len of the Tx path to crash the host application or
overwrite its memory.

Currently, in the driver, descriptor buf len is passed to the DMA map
function, but its value is not checked afterward, so it only ensure
first byte is mapped. For Tx, 1 bytes length is requested at DMA map, so
also only first byte of the buffer is ensured to be mapped (if desc_addr
is not NULL, which is not checked).

>>
>> Other than that, there is for sure a performance gain due to all the
>> features Virtio-net supports that we have to check and handle in the
>> hotpath, like indirect descriptors or mergeable buffers for example.
> 
> I think iavf has similar features like indirect and mergeable to recv/xmit
> large pkts. But I believe there will be some feature difference between
> iavf and virtio/vhost.

Sure, but I meant it might not be necessary to implement all those
features (if they are optional in the spec) in your driver, just that in
the case of Vhost we have a legacy to support.

> I think you are correct that for this version, it's not fair to compare virtio/vhost
> with iavf back-end because iavf back-end has not supported some features, and besides,
> we have not optimized the data path of iavf back-end. We expect the performance of
> iavf back-end in the same level of virtio 1.1 and hopefully better because of the ring
> layout. But let's see when we can do complete performance analysis 😊.

Thanks!
Maxime

> Thanks!
> Chenbo
> 
>>
>> Best regards,
>> Maxime
>>
>>> Regards,
>>> Maxime
>>>
>>>> Thanks!
>>>> Chenbo
>>>>
>>>>>
>>>>>
>>>>> --
>>>>> David Marchand
>>>>
> 


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver
  2020-12-22  8:48           ` Maxime Coquelin
@ 2020-12-23  5:28             ` Xia, Chenbo
  0 siblings, 0 replies; 44+ messages in thread
From: Xia, Chenbo @ 2020-12-23  5:28 UTC (permalink / raw)
  To: Maxime Coquelin, Thomas Monjalon, David Marchand, Wu, Jingjing
  Cc: dev, Stephen Hemminger, Liang, Cunming, Lu, Xiuchun, Li, Miao

Hi Maxime,

> -----Original Message-----
> From: Maxime Coquelin <maxime.coquelin@redhat.com>
> Sent: Tuesday, December 22, 2020 4:49 PM
> To: Xia, Chenbo <chenbo.xia@intel.com>; Thomas Monjalon <thomas@monjalon.net>;
> David Marchand <david.marchand@redhat.com>
> Cc: dev <dev@dpdk.org>; Stephen Hemminger <stephen@networkplumber.org>; Liang,
> Cunming <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li,
> Miao <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>
> Subject: Re: [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev
> driver
> 
> Hi Chenbo,
> 
> Thanks for the detailed reply.
> 
> On 12/22/20 4:09 AM, Xia, Chenbo wrote:
> > Hi Maxime,
> >
> >> -----Original Message-----
> >> From: Maxime Coquelin <maxime.coquelin@redhat.com>
> >> Sent: Monday, December 21, 2020 8:02 PM
> >> To: Xia, Chenbo <chenbo.xia@intel.com>; Thomas Monjalon
> <thomas@monjalon.net>;
> >> David Marchand <david.marchand@redhat.com>
> >> Cc: dev <dev@dpdk.org>; Stephen Hemminger <stephen@networkplumber.org>;
> Liang,
> >> Cunming <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li,
> >> Miao <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>
> >> Subject: Re: [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf
> emudev
> >> driver
> >>
> >>
> >>
> >> On 12/21/20 10:52 AM, Maxime Coquelin wrote:
> >>> Hi Chenbo,
> >>>
> >>> On 12/19/20 7:11 AM, Xia, Chenbo wrote:
> >>>> Hi David,
> >>>>
> >>>>> -----Original Message-----
> >>>>> From: David Marchand <david.marchand@redhat.com>
> >>>>> Sent: Friday, December 18, 2020 5:54 PM
> >>>>> To: Xia, Chenbo <chenbo.xia@intel.com>
> >>>>> Cc: dev <dev@dpdk.org>; Thomas Monjalon <thomas@monjalon.net>; Stephen
> >>>>> Hemminger <stephen@networkplumber.org>; Liang, Cunming
> >>>>> <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li, Miao
> >>>>> <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>
> >>>>> Subject: Re: [PATCH 0/8] Introduce emudev library and iavf emudev driver
> >>>>>
> >>>>> On Fri, Dec 18, 2020 at 9:02 AM Chenbo Xia <chenbo.xia@intel.com> wrote:
> >>>>>>
> >>>>>> This series introduces a new device abstraction called emudev for
> >>>>> emulated
> >>>>>> devices. A new library (librte_emudev) is implemented. The first emudev
> >>>>>> driver is also introduced, which emulates Intel Adaptive Virtual
> >>>>> Function
> >>>>>> (iavf) as a software network device.
> >>>>>>
> >>>>>> This series has a dependency on librte_vfio_user patch series:
> >>>>>> http://patchwork.dpdk.org/cover/85389/
> >>>>>>
> >>>>>> Background & Motivation
> >>>>>> -----------------------
> >>>>>> The disaggregated/multi-process QEMU is using VFIO-over-socket/vfio-
> user
> >>>>>> as the main transport mechanism to disaggregate IO services from QEMU.
> >>>>>> Therefore, librte_vfio_user is introduced in DPDK to accommodate
> >>>>>> emulated devices for high performance I/O. Although vfio-user library
> >>>>>> provides possibility of emulating devices in DPDK, DPDK does not have
> >>>>>> a device abstraction for emulated devices. A good device abstraction
> >>>>> will
> >>>>>> be useful for applications or high performance data path driver. With
> >>>>>> this consideration, emudev library is designed and implemented. It also
> >>>>>> make it possbile to keep modular design on emulated devices by
> >>>>> implementing
> >>>>>> data path related logic in a standalone driver (e.g., an ethdev driver)
> >>>>>> and keeps the unrelated logic in the emudev driver.
> >>>>>
> >>>>> Since you mention performance, how does it compare to vhost-user/virtio?
> >>>>
> >>>> I think it depends on the device specification (i.e., how complex its
> data
> >> path
> >>>> handling is). A first try on iavf spec shows better performance than
> virtio
> >>>> in our simple tests.
> >>>
> >>> That's interesting! How big is the performance difference? And how do
> >>> we explain it?
> >>>
> >>> If there are improvements that could be done in the Virtio
> >>> specification, it would be great to know and work on their
> >>> implementations. It worries me a bit that every one is coming with
> >>> his new device emulation every release, making things like live-
> >>> migration difficult to achieve in the future.
> >>
> >> I did a quick review of the IAVF emudev driver to understand what other
> >> factors than ring layout could explain a performance gain.
> >>
> >> My understanding is that part of the performance gain may come from
> >> following things that are supported/implemented in Vhost-user backend
> >> and not in IAVF driver:
> >> 1. Memory hotplug. It seems the datapath is not safe against memory
> >> hotplug in the VM, which causes the memory tables to be updated
> >> asynchronously from the datapath. In order to support it in Vhost-user
> >> library, we had to introduce locks to ensure the datapath isn't
> >> accessing the shared memory while it is being remapped.
> >
> > I think now it uses the similar way that vhost-user does.
> >
> > First, in the vfio-user patch series, we introduce a callback lock_dp to
> lock
> > the data path when messages like DMA MAP/UNMAP come. It will lock datapath
> > in our data path driver.
> >
> > Note that the data path handling is in our data path driver:
> > http://patchwork.dpdk.org/cover/85500/
> >
> > For modular design, iavf_emu driver emulates the device but the iavf back-
> end
> > driver does data path handling.
> 
> My analysis was actually based on the data path driver series.
> 
> My point was that iavfbe_recv_pkts() and iavfbe_xmit_pkts() are not safe
> against asynchronous changes like memory table updates.
> 
> As far as I can see, the access_lock aren't taken by these functions, so
> if for example a memory table update happen during the these functions
> execution, it could lead to undefined behaviour. Only things checked
> there is whether the queue is enabled when entering the function, but
> this value can be changed right after having being checked.
> 
> For example, in Vhost-user lib, we protected rte_vhost_dequeue_burst()
> and rte_vhost_enqueue_burst() with a spinlock. Note that this spinlock
> is per-virtqueue in order to avoid contention between the different
> queues.
> 

Oops, I didn't realize the data path driver missed that. And yes, the data path
driver should do that like you said.

> >>
> >> 2. Live-migration. This feature does not seem supported in the driver,
> >> as I could not find dirty pages tracking mechanism. On Vhost-user side,
> >> supporting implies adding more branch conditions in the hot path, even
> >> when it is not used.
> >
> > Yes, we don't support this now in this version. And yes, when we support
> this
> > feature, it will introduce complexity in data path.
> >
> >>
> >> 3. No IOMMU support. Same here, this is supported in Vhost-user library,
> >> and adding its support in IAVF driver would introduce some more branches
> >> in the hot path even when not used.
> >
> > Yes, vIOMMU is not fully supported in vfio-user spec for now and I also
> agree
> > when we have to support it, it will slow down the data path.
> >
> >>
> >> 4. Out of bound memory accesses checks. While
> >> rte_iavf_emu_get_dma_vaddr() provides a way to ensure the full requested
> >> length is mapped, the data path does not use it. It does not even ensure
> >
> > In fact, it uses it 😊. I think you may miss our data path driver. Here's a
> > use: http://patchwork.dpdk.org/patch/85504/
> 
> Sorry, I was not clear. What I meant is that
> rte_iavf_emu_get_dma_vaddr() is indeed used, but its outputs aren't
> checked properly.
> 
> >> the translated address is non-NULL. It makes it trivial for a malicious
> >> guest to make the hypervisor's vSwitch to crash by simply passing random
> >> values as buffer's address and length. Fixing it is trivial, but it will
> >> add several more checks and loops (if a buffer is spanned across two
> >> pages) in the hot path.
> >
> > I don't quite understand this one. First, rte_iavf_emu_get_dma_vaddr() is
> the
> > only way to translate address. And I think this function will ensure the
> input
> > address is valid. Looking at the vhost side, vhost_iova_to_vva() does
> similar
> > things when vIOMMU is not used. Do I miss something? Just correct me if I am
> > wrong.
> 
> I think rte_iavf_emu_get_dma_vaddr() is good, the issue is the way it is
> used in iavfbe_recv_pkts() and iavfbe_xmit_pkts().
> 
> First the returned address (desc_addr) is not checked. So if the start
> address of the buffer is out of guest memory ranges, the host app will
> crash with a segmentation fault. It can happen in case of bug in the
> guest driver, or with a maliciously crafted descriptor.
> 
> Then, rte_iavf_emu_get_dma_vaddr() len parameter is a pointer. The
> caller has to pass the length of the buffer it wants to map. Once
> rte_iavf_emu_get_dma_vaddr() is updated with the contiguous length that
> is mapped.
> 
> The caller needs to check whether all requested length is mapped, and
> loop until it is all mapped:
> http://code.dpdk.org/dpdk/latest/source/lib/librte_vhost/virtio_net.c#L484
> 
> This is required because a buffer contiguous in IOVA (guest physical
> address space in this case) is not necessarily contiguous in Host
> virtual address space. Also, this is required to be safe against
> untrusted guests, as a malicious guest could pass ~0ULL in the
> descriptor buffer len of the Tx path to crash the host application or
> overwrite its memory.
> 
> Currently, in the driver, descriptor buf len is passed to the DMA map
> function, but its value is not checked afterward, so it only ensure
> first byte is mapped. For Tx, 1 bytes length is requested at DMA map, so
> also only first byte of the buffer is ensured to be mapped (if desc_addr
> is not NULL, which is not checked).
>

Correct! I also missed that rte_iavf_emu_get_dma_vaddr() is not properly used
in data path driver. For the lock issue and this, @Wu, Jingjing could we fix
in V2?

Thanks for the good catches!
Chenbo
 
> >>
> >> Other than that, there is for sure a performance gain due to all the
> >> features Virtio-net supports that we have to check and handle in the
> >> hotpath, like indirect descriptors or mergeable buffers for example.
> >
> > I think iavf has similar features like indirect and mergeable to recv/xmit
> > large pkts. But I believe there will be some feature difference between
> > iavf and virtio/vhost.
> 
> Sure, but I meant it might not be necessary to implement all those
> features (if they are optional in the spec) in your driver, just that in
> the case of Vhost we have a legacy to support.
> 
> > I think you are correct that for this version, it's not fair to compare
> virtio/vhost
> > with iavf back-end because iavf back-end has not supported some features,
> and besides,
> > we have not optimized the data path of iavf back-end. We expect the
> performance of
> > iavf back-end in the same level of virtio 1.1 and hopefully better because
> of the ring
> > layout. But let's see when we can do complete performance analysis 😊.
> 
> Thanks!
> Maxime
> 
> > Thanks!
> > Chenbo
> >
> >>
> >> Best regards,
> >> Maxime
> >>
> >>> Regards,
> >>> Maxime
> >>>
> >>>> Thanks!
> >>>> Chenbo
> >>>>
> >>>>>
> >>>>>
> >>>>> --
> >>>>> David Marchand
> >>>>
> >


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH v2 5/8] emu/iavf: add resource management and internal logic of iavf
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 5/8] emu/iavf: add resource management and internal logic of iavf Chenbo Xia
@ 2020-12-29  6:05     ` Wu, Jingjing
  2020-12-30  1:59       ` Xia, Chenbo
  0 siblings, 1 reply; 44+ messages in thread
From: Wu, Jingjing @ 2020-12-29  6:05 UTC (permalink / raw)
  To: Xia, Chenbo, dev, thomas, david.marchand
  Cc: stephen, Liang, Cunming, Lu, Xiuchun, Li, Miao

> +static ssize_t iavf_emu_bar0_rw(struct rte_vfio_user_reg_info *reg, char
> *buf,
> +	size_t count, loff_t pos, bool iswrite) {
> +	struct iavf_emudev *dev = (struct iavf_emudev *)reg->priv;
> +	char *reg_pos;
> +
> +	if (!reg->base) {
> +		EMU_IAVF_LOG(ERR, "BAR 0 does not exist\n");
> +		return -EFAULT;
> +	}
> +
> +	if (pos + count > reg->info->size) {
> +		EMU_IAVF_LOG(ERR, "Access exceeds BAR 0 size\n");
> +		return -EINVAL;
> +	}
> +
> +	reg_pos = (char *)reg->base + pos;
> +
> +	if (!iswrite) {
> +		rte_memcpy(buf, reg_pos, count);
> +	} else {
> +		int tmp;
> +		uint32_t val;
> +		int idx = -1;
> +
> +		if (count != 4)
> +			return -EINVAL;
> +
> +		val = *(uint32_t *)buf;
> +		/* Only handle interrupt enable/disable for now */
> +		if (pos == IAVF_VFINT_DYN_CTL01) {
> +			tmp = val & IAVF_VFINT_DYN_CTL01_INTENA_MASK;
> +			idx = 0;
> +		} else if ((pos >= IAVF_VFINT_DYN_CTLN1(0)) && pos <=
> +
> 	IAVF_VFINT_DYN_CTLN1(RTE_IAVF_EMU_MAX_INTR - 1)) {
> +			tmp = val & IAVF_VFINT_DYN_CTLN1_INTENA_MASK;
> +			idx = pos - IAVF_VFINT_DYN_CTLN1(0);
> +			if (idx % 4)
> +				return -EINVAL;
> +			idx = idx / 4;
Should be idx = idx / 4 + 1; ?

> +		}
> +
> +		if (idx != -1 &&
> +			tmp != dev->intr->info[idx].enable && dev->ready) {
> +			dev->ops->update_status(dev->edev);
> +			dev->intr->info[idx].enable = tmp;
dev->intr->info[idx].enable need to be set before update_status callback is called.

> +		}
> +
> +		rte_memcpy(reg_pos, buf, count);
> +	}
> +
> +	return count;
> +}

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH v2 5/8] emu/iavf: add resource management and internal logic of iavf
  2020-12-29  6:05     ` Wu, Jingjing
@ 2020-12-30  1:59       ` Xia, Chenbo
  0 siblings, 0 replies; 44+ messages in thread
From: Xia, Chenbo @ 2020-12-30  1:59 UTC (permalink / raw)
  To: Wu, Jingjing, dev, thomas, david.marchand
  Cc: stephen, Liang, Cunming, Lu, Xiuchun, Li, Miao

Hi Jingjing,

> -----Original Message-----
> From: Wu, Jingjing <jingjing.wu@intel.com>
> Sent: Tuesday, December 29, 2020 2:05 PM
> To: Xia, Chenbo <chenbo.xia@intel.com>; dev@dpdk.org; thomas@monjalon.net;
> david.marchand@redhat.com
> Cc: stephen@networkplumber.org; Liang, Cunming <cunming.liang@intel.com>; Lu,
> Xiuchun <xiuchun.lu@intel.com>; Li, Miao <miao.li@intel.com>
> Subject: RE: [PATCH v2 5/8] emu/iavf: add resource management and internal
> logic of iavf
> 
> > +static ssize_t iavf_emu_bar0_rw(struct rte_vfio_user_reg_info *reg, char
> > *buf,
> > +	size_t count, loff_t pos, bool iswrite) {
> > +	struct iavf_emudev *dev = (struct iavf_emudev *)reg->priv;
> > +	char *reg_pos;
> > +
> > +	if (!reg->base) {
> > +		EMU_IAVF_LOG(ERR, "BAR 0 does not exist\n");
> > +		return -EFAULT;
> > +	}
> > +
> > +	if (pos + count > reg->info->size) {
> > +		EMU_IAVF_LOG(ERR, "Access exceeds BAR 0 size\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	reg_pos = (char *)reg->base + pos;
> > +
> > +	if (!iswrite) {
> > +		rte_memcpy(buf, reg_pos, count);
> > +	} else {
> > +		int tmp;
> > +		uint32_t val;
> > +		int idx = -1;
> > +
> > +		if (count != 4)
> > +			return -EINVAL;
> > +
> > +		val = *(uint32_t *)buf;
> > +		/* Only handle interrupt enable/disable for now */
> > +		if (pos == IAVF_VFINT_DYN_CTL01) {
> > +			tmp = val & IAVF_VFINT_DYN_CTL01_INTENA_MASK;
> > +			idx = 0;
> > +		} else if ((pos >= IAVF_VFINT_DYN_CTLN1(0)) && pos <=
> > +
> > 	IAVF_VFINT_DYN_CTLN1(RTE_IAVF_EMU_MAX_INTR - 1)) {
> > +			tmp = val & IAVF_VFINT_DYN_CTLN1_INTENA_MASK;
> > +			idx = pos - IAVF_VFINT_DYN_CTLN1(0);
> > +			if (idx % 4)
> > +				return -EINVAL;
> > +			idx = idx / 4;
> Should be idx = idx / 4 + 1; ?

Yes! Will fix in v3.

> 
> > +		}
> > +
> > +		if (idx != -1 &&
> > +			tmp != dev->intr->info[idx].enable && dev->ready) {
> > +			dev->ops->update_status(dev->edev);
> > +			dev->intr->info[idx].enable = tmp;
> dev->intr->info[idx].enable need to be set before update_status callback is
> called.

Yes. Will fix this too.

Thanks!
Chenbo

> 
> > +		}
> > +
> > +		rte_memcpy(reg_pos, buf, count);
> > +	}
> > +
> > +	return count;
> > +}

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH v2 4/8] emu/iavf: add vfio-user device register and unregister
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 4/8] emu/iavf: add vfio-user device register and unregister Chenbo Xia
@ 2021-01-04  6:45     ` Wu, Jingjing
  2021-01-05  1:26       ` Xia, Chenbo
  2021-01-05 13:41     ` Wu, Jingjing
  1 sibling, 1 reply; 44+ messages in thread
From: Wu, Jingjing @ 2021-01-04  6:45 UTC (permalink / raw)
  To: Xia, Chenbo, dev, thomas, david.marchand
  Cc: stephen, Liang, Cunming, Lu, Xiuchun, Li, Miao

> +static inline struct iavf_emu_sock_list * iavf_emu_find_sock_list(char
> +*sock_addr) {
> +	struct iavf_emu_sock_list *list;
> +	struct iavf_emudev *dev;
> +	int list_exist;

Initialize list_exist to 0?
> +
> +	if (!sock_addr)
> +		return NULL;
> +
> +	pthread_mutex_lock(&sock_list_lock);
> +
> +	TAILQ_FOREACH(list, &sock_list, next) {
> +		dev = (struct iavf_emudev *)list->emu_dev->priv_data;
> +
> +		if (!strcmp(dev->sock_addr, sock_addr)) {
> +			list_exist = 1;
> +			break;
> +		}
> +		break;
This "break" need to be removed.

> +	}
> +
> +	pthread_mutex_unlock(&sock_list_lock);
> +
> +	if (!list_exist)
> +		return NULL;
> +
> +	return list;
> +}
> +

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH v2 4/8] emu/iavf: add vfio-user device register and unregister
  2021-01-04  6:45     ` Wu, Jingjing
@ 2021-01-05  1:26       ` Xia, Chenbo
  0 siblings, 0 replies; 44+ messages in thread
From: Xia, Chenbo @ 2021-01-05  1:26 UTC (permalink / raw)
  To: Wu, Jingjing, dev, thomas, david.marchand
  Cc: stephen, Liang, Cunming, Lu, Xiuchun, Li, Miao

Hi Jingjing,

> -----Original Message-----
> From: Wu, Jingjing <jingjing.wu@intel.com>
> Sent: Monday, January 4, 2021 2:45 PM
> To: Xia, Chenbo <chenbo.xia@intel.com>; dev@dpdk.org; thomas@monjalon.net;
> david.marchand@redhat.com
> Cc: stephen@networkplumber.org; Liang, Cunming <cunming.liang@intel.com>; Lu,
> Xiuchun <xiuchun.lu@intel.com>; Li, Miao <miao.li@intel.com>
> Subject: RE: [PATCH v2 4/8] emu/iavf: add vfio-user device register and
> unregister
> 
> > +static inline struct iavf_emu_sock_list * iavf_emu_find_sock_list(char
> > +*sock_addr) {
> > +	struct iavf_emu_sock_list *list;
> > +	struct iavf_emudev *dev;
> > +	int list_exist;
> 
> Initialize list_exist to 0?

Yes, will fix in next version.

> > +
> > +	if (!sock_addr)
> > +		return NULL;
> > +
> > +	pthread_mutex_lock(&sock_list_lock);
> > +
> > +	TAILQ_FOREACH(list, &sock_list, next) {
> > +		dev = (struct iavf_emudev *)list->emu_dev->priv_data;
> > +
> > +		if (!strcmp(dev->sock_addr, sock_addr)) {
> > +			list_exist = 1;
> > +			break;
> > +		}
> > +		break;
> This "break" need to be removed.

Yes, will fix in next version.

Thanks,
Chenbo

> 
> > +	}
> > +
> > +	pthread_mutex_unlock(&sock_list_lock);
> > +
> > +	if (!list_exist)
> > +		return NULL;
> > +
> > +	return list;
> > +}
> > +

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH v2 4/8] emu/iavf: add vfio-user device register and unregister
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 4/8] emu/iavf: add vfio-user device register and unregister Chenbo Xia
  2021-01-04  6:45     ` Wu, Jingjing
@ 2021-01-05 13:41     ` Wu, Jingjing
  2021-01-06  7:41       ` Xia, Chenbo
  1 sibling, 1 reply; 44+ messages in thread
From: Wu, Jingjing @ 2021-01-05 13:41 UTC (permalink / raw)
  To: Xia, Chenbo, dev, thomas, david.marchand
  Cc: stephen, Liang, Cunming, Lu, Xiuchun, Li, Miao

> +static int iavf_emu_update_status(int vfio_dev_id) {
> +	struct iavf_emudev *dev;
> +	int ret;
> +
> +	dev = find_iavf_with_dev_id(vfio_dev_id);
> +	if (!dev)
> +		return -1;
> +
> +	ret = iavf_emu_setup_mem_table(dev);
> +	if (ret) {
> +		EMU_IAVF_LOG(ERR, "Failed to set up memtable for "
> +			"device %d", dev->vfio->dev_id);
> +		return ret;
> +	}
> +
> +	ret = iavf_emu_setup_irq(dev);
In update callback, irq fds will be reinitialized here. Think about if the update happening during mailbox communication, the eventfd of mailbox will be cleared without notify.

> +	if (ret) {
> +		EMU_IAVF_LOG(ERR, "Failed to set up irq for "
> +			"device %d", dev->vfio->dev_id);
> +		return ret;
> +	}
> +
> +	dev->ops->update_status(dev->edev);
> +
> +	return 0;
> +}

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH v2 4/8] emu/iavf: add vfio-user device register and unregister
  2021-01-05 13:41     ` Wu, Jingjing
@ 2021-01-06  7:41       ` Xia, Chenbo
  0 siblings, 0 replies; 44+ messages in thread
From: Xia, Chenbo @ 2021-01-06  7:41 UTC (permalink / raw)
  To: Wu, Jingjing, dev, thomas, david.marchand
  Cc: stephen, Liang, Cunming, Lu, Xiuchun, Li, Miao

Hi Jingjing,

> -----Original Message-----
> From: Wu, Jingjing <jingjing.wu@intel.com>
> Sent: Tuesday, January 5, 2021 9:42 PM
> To: Xia, Chenbo <chenbo.xia@intel.com>; dev@dpdk.org; thomas@monjalon.net;
> david.marchand@redhat.com
> Cc: stephen@networkplumber.org; Liang, Cunming <cunming.liang@intel.com>; Lu,
> Xiuchun <xiuchun.lu@intel.com>; Li, Miao <miao.li@intel.com>
> Subject: RE: [PATCH v2 4/8] emu/iavf: add vfio-user device register and
> unregister
> 
> > +static int iavf_emu_update_status(int vfio_dev_id) {
> > +	struct iavf_emudev *dev;
> > +	int ret;
> > +
> > +	dev = find_iavf_with_dev_id(vfio_dev_id);
> > +	if (!dev)
> > +		return -1;
> > +
> > +	ret = iavf_emu_setup_mem_table(dev);
> > +	if (ret) {
> > +		EMU_IAVF_LOG(ERR, "Failed to set up memtable for "
> > +			"device %d", dev->vfio->dev_id);
> > +		return ret;
> > +	}
> > +
> > +	ret = iavf_emu_setup_irq(dev);
> In update callback, irq fds will be reinitialized here. Think about if the
> update happening during mailbox communication, the eventfd of mailbox will be
> cleared without notify.

Correct! Will fix this in next version.

Thanks!
Chenbo

> 
> > +	if (ret) {
> > +		EMU_IAVF_LOG(ERR, "Failed to set up irq for "
> > +			"device %d", dev->vfio->dev_id);
> > +		return ret;
> > +	}
> > +
> > +	dev->ops->update_status(dev->edev);
> > +
> > +	return 0;
> > +}

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH 4/8] emu/iavf: add vfio-user device register and unregister
  2020-12-18  7:47 ` [dpdk-dev] [PATCH 4/8] emu/iavf: add vfio-user device register and unregister Chenbo Xia
@ 2021-01-07  7:18   ` Xing, Beilei
  2021-01-07  8:41     ` Xia, Chenbo
  0 siblings, 1 reply; 44+ messages in thread
From: Xing, Beilei @ 2021-01-07  7:18 UTC (permalink / raw)
  To: Xia, Chenbo, dev, thomas, david.marchand
  Cc: stephen, Liang, Cunming, Lu, Xiuchun, Li, Miao, Wu, Jingjing



> -----Original Message-----
> From: dev <dev-bounces@dpdk.org> On Behalf Of Chenbo Xia
> Sent: Friday, December 18, 2020 3:48 PM
> To: dev@dpdk.org; thomas@monjalon.net; david.marchand@redhat.com
> Cc: stephen@networkplumber.org; Liang, Cunming
> <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li, Miao
> <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>
> Subject: [dpdk-dev] [PATCH 4/8] emu/iavf: add vfio-user device register and
> unregister
> 
> This patch adds vfio-user APIs call in driver probe and remove.
> rte_vfio_user_register() and rte_vfio_user_unregister() are called to
> create/destroy a vfio-user device. Notify callbacks that libvfio_user defines are
> also implemented.
> 
> Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
> Signed-off-by: Miao Li <miao.li@intel.com>
> ---


> +static struct iavf_emudev *find_iavf_with_dev_id(int vfio_dev_id) {

It's better to change the function name to follow other function names' style.
iavf_emu_xxx 

> +	struct iavf_emu_sock_list *list;
> +	char sock_addr[PATH_MAX];
> +	int ret;
> +
> +	ret = rte_vfio_get_sock_addr(vfio_dev_id, sock_addr,
> +		sizeof(sock_addr));
> +	if (ret) {
> +		EMU_IAVF_LOG(ERR, "Can not find vfio device %d "
> +			"sock_addr.\n", vfio_dev_id);
> +		return NULL;
> +	}
> +
> +	list = iavf_emu_find_sock_list(sock_addr);
> +	if (!list) {
> +		EMU_IAVF_LOG(ERR, "Can not find sock list.\n");
> +		return NULL;
> +	}
> +
> +	return (struct iavf_emudev *)list->emu_dev->priv_data; }

It's better to check if list->emu_dev is NULL first.

> +
> +static int iavf_emu_new_device(int vfio_dev_id) {
> +	struct iavf_emudev *dev;
> +	int ret;
> +
> +	dev = find_iavf_with_dev_id(vfio_dev_id);
> +	if (!dev)
> +		return -1;
> +
> +	dev->vfio->dev_id = vfio_dev_id;
> +
> +	ret = iavf_emu_setup_mem_table(dev);
> +	if (ret) {
> +		EMU_IAVF_LOG(ERR, "Failed to set up memtable for "
> +			"device %d", dev->vfio->dev_id);
> +		return ret;
> +	}
> +
> +	ret = iavf_emu_setup_irq(dev);
> +	if (ret) {
> +		EMU_IAVF_LOG(ERR, "Failed to set up irq for "
> +			"device %d", dev->vfio->dev_id);
> +		return ret;
> +	}
> +
> +	ret = iavf_emu_setup_queues(dev);
> +	if (ret) {
> +		EMU_IAVF_LOG(ERR, "Failed to set up queues for "
> +			"device %d", dev->vfio->dev_id);
> +		return ret;
> +	}
> +
> +	ret = dev->ops->device_ready(dev->edev);

Same as above, and please also check other functions, such as device_destroy...

> +	if (ret)
> +		return ret;
> +
> +	dev->ready = 1;
> +	return 0;
> +}
> +
> +static void iavf_emu_destroy_device(int vfio_dev_id) {
> +	struct iavf_emudev *dev;
> +
> +	dev = find_iavf_with_dev_id(vfio_dev_id);
> +	if (!dev)
> +		return;
> +
> +	iavf_emu_reset_all_resources(dev);

Should we add 'dev->ready = 0' here?

> +
> +	dev->ops->device_destroy(dev->edev);
> +}
> +



> +static int iavf_emu_lock_datapath(int vfio_dev_id, int lock) {
> +	struct iavf_emudev *dev;
> +
> +	dev = find_iavf_with_dev_id(vfio_dev_id);
> +	if (!dev)
> +		return -1;
> +
> +	return dev->ops->lock_dp(dev->edev, lock); }
> +
> +static int iavf_emu_reset_device(int vfio_dev_id) {
> +	struct iavf_emudev *dev;
> +
> +	dev = find_iavf_with_dev_id(vfio_dev_id);
> +	if (!dev)
> +		return -1;
> +
> +	iavf_emu_reset_all_resources(dev);

Should we add 'dev->ready = 0' here?

> +
> +	return dev->ops->reset_device(dev->edev);
> +}
> +


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH 4/8] emu/iavf: add vfio-user device register and unregister
  2021-01-07  7:18   ` Xing, Beilei
@ 2021-01-07  8:41     ` Xia, Chenbo
  0 siblings, 0 replies; 44+ messages in thread
From: Xia, Chenbo @ 2021-01-07  8:41 UTC (permalink / raw)
  To: Xing, Beilei, dev, thomas, david.marchand
  Cc: stephen, Liang, Cunming, Lu, Xiuchun, Li, Miao, Wu, Jingjing

Hi Beilei,

> -----Original Message-----
> From: Xing, Beilei <beilei.xing@intel.com>
> Sent: Thursday, January 7, 2021 3:19 PM
> To: Xia, Chenbo <chenbo.xia@intel.com>; dev@dpdk.org; thomas@monjalon.net;
> david.marchand@redhat.com
> Cc: stephen@networkplumber.org; Liang, Cunming <cunming.liang@intel.com>; Lu,
> Xiuchun <xiuchun.lu@intel.com>; Li, Miao <miao.li@intel.com>; Wu, Jingjing
> <jingjing.wu@intel.com>
> Subject: RE: [dpdk-dev] [PATCH 4/8] emu/iavf: add vfio-user device register
> and unregister
> 
> 
> 
> > -----Original Message-----
> > From: dev <dev-bounces@dpdk.org> On Behalf Of Chenbo Xia
> > Sent: Friday, December 18, 2020 3:48 PM
> > To: dev@dpdk.org; thomas@monjalon.net; david.marchand@redhat.com
> > Cc: stephen@networkplumber.org; Liang, Cunming
> > <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li, Miao
> > <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>
> > Subject: [dpdk-dev] [PATCH 4/8] emu/iavf: add vfio-user device register and
> > unregister
> >
> > This patch adds vfio-user APIs call in driver probe and remove.
> > rte_vfio_user_register() and rte_vfio_user_unregister() are called to
> > create/destroy a vfio-user device. Notify callbacks that libvfio_user
> defines are
> > also implemented.
> >
> > Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
> > Signed-off-by: Miao Li <miao.li@intel.com>
> > ---
> 
> 
> > +static struct iavf_emudev *find_iavf_with_dev_id(int vfio_dev_id) {
> 
> It's better to change the function name to follow other function names' style.
> iavf_emu_xxx

OK, thanks!

> 
> > +	struct iavf_emu_sock_list *list;
> > +	char sock_addr[PATH_MAX];
> > +	int ret;
> > +
> > +	ret = rte_vfio_get_sock_addr(vfio_dev_id, sock_addr,
> > +		sizeof(sock_addr));
> > +	if (ret) {
> > +		EMU_IAVF_LOG(ERR, "Can not find vfio device %d "
> > +			"sock_addr.\n", vfio_dev_id);
> > +		return NULL;
> > +	}
> > +
> > +	list = iavf_emu_find_sock_list(sock_addr);
> > +	if (!list) {
> > +		EMU_IAVF_LOG(ERR, "Can not find sock list.\n");
> > +		return NULL;
> > +	}
> > +
> > +	return (struct iavf_emudev *)list->emu_dev->priv_data; }
> 
> It's better to check if list->emu_dev is NULL first.

'list->emu_dev->priv_data' is already used in iavf_emu_find_sock_list.
And based on the way we add to the list, emu_dev will not be NULL.
But thanks to your comment, I noticed I should just return struct iavf_emudev
in iavf_emu_find_sock_list. And the linked list may just store
'struct iavf_emudev' to make it easier.

> 
> > +
> > +static int iavf_emu_new_device(int vfio_dev_id) {
> > +	struct iavf_emudev *dev;
> > +	int ret;
> > +
> > +	dev = find_iavf_with_dev_id(vfio_dev_id);
> > +	if (!dev)
> > +		return -1;
> > +
> > +	dev->vfio->dev_id = vfio_dev_id;
> > +
> > +	ret = iavf_emu_setup_mem_table(dev);
> > +	if (ret) {
> > +		EMU_IAVF_LOG(ERR, "Failed to set up memtable for "
> > +			"device %d", dev->vfio->dev_id);
> > +		return ret;
> > +	}
> > +
> > +	ret = iavf_emu_setup_irq(dev);
> > +	if (ret) {
> > +		EMU_IAVF_LOG(ERR, "Failed to set up irq for "
> > +			"device %d", dev->vfio->dev_id);
> > +		return ret;
> > +	}
> > +
> > +	ret = iavf_emu_setup_queues(dev);
> > +	if (ret) {
> > +		EMU_IAVF_LOG(ERR, "Failed to set up queues for "
> > +			"device %d", dev->vfio->dev_id);
> > +		return ret;
> > +	}
> > +
> > +	ret = dev->ops->device_ready(dev->edev);
> 
> Same as above, and please also check other functions, such as device_destroy...

Yes! Will add check then.

> 
> > +	if (ret)
> > +		return ret;
> > +
> > +	dev->ready = 1;
> > +	return 0;
> > +}
> > +
> > +static void iavf_emu_destroy_device(int vfio_dev_id) {
> > +	struct iavf_emudev *dev;
> > +
> > +	dev = find_iavf_with_dev_id(vfio_dev_id);
> > +	if (!dev)
> > +		return;
> > +
> > +	iavf_emu_reset_all_resources(dev);
> 
> Should we add 'dev->ready = 0' here?

Yes. Will fix it.

> 
> > +
> > +	dev->ops->device_destroy(dev->edev);
> > +}
> > +
> 
> 
> 
> > +static int iavf_emu_lock_datapath(int vfio_dev_id, int lock) {
> > +	struct iavf_emudev *dev;
> > +
> > +	dev = find_iavf_with_dev_id(vfio_dev_id);
> > +	if (!dev)
> > +		return -1;
> > +
> > +	return dev->ops->lock_dp(dev->edev, lock); }
> > +
> > +static int iavf_emu_reset_device(int vfio_dev_id) {
> > +	struct iavf_emudev *dev;
> > +
> > +	dev = find_iavf_with_dev_id(vfio_dev_id);
> > +	if (!dev)
> > +		return -1;
> > +
> > +	iavf_emu_reset_all_resources(dev);
> 
> Should we add 'dev->ready = 0' here?

Yes. Will fix it.

Thanks,
Chenbo

> 
> > +
> > +	return dev->ops->reset_device(dev->edev);
> > +}
> > +


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH v2 0/8] Introduce emudev library and iavf emudev driver
  2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
                     ` (7 preceding siblings ...)
  2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 8/8] doc: update release notes for iavf emudev driver Chenbo Xia
@ 2021-01-13 16:52   ` Thomas Monjalon
  2021-01-14  1:35     ` Xia, Chenbo
  2021-01-14  6:25   ` [dpdk-dev] [PATCH v3 " Chenbo Xia
  9 siblings, 1 reply; 44+ messages in thread
From: Thomas Monjalon @ 2021-01-13 16:52 UTC (permalink / raw)
  To: Chenbo Xia
  Cc: dev, david.marchand, stephen, cunming.liang, xiuchun.lu, miao.li,
	jingjing.wu, techboard

19/12/2020 07:27, Chenbo Xia:
> This series introduces a new device abstraction called emudev for emulated
> devices. A new library (librte_emudev) is implemented. The first emudev
> driver is also introduced, which emulates Intel Adaptive Virtual Function
> (iavf) as a software network device.
> 
> This series has a dependency on librte_vfio_user patch series:
> http://patchwork.dpdk.org/cover/85389/
> 
> Background & Motivation 
> -----------------------
> The disaggregated/multi-process QEMU is using VFIO-over-socket/vfio-user
> as the main transport mechanism to disaggregate IO services from QEMU.
> Therefore, librte_vfio_user is introduced in DPDK to accommodate
> emulated devices for high performance I/O. Although vfio-user library
> provides possibility of emulating devices in DPDK, DPDK does not have
> a device abstraction for emulated devices. A good device abstraction will
> be useful for applications or high performance data path driver. With
> this consideration, emudev library is designed and implemented. It also
> make it possbile to keep modular design on emulated devices by implementing
> data path related logic in a standalone driver (e.g., an ethdev driver)
> and keeps the unrelated logic in the emudev driver.


As voted today in the Technical Board meeting,
there will be a draft repository to host this initiative.
A proposed name for the git tree is:
	dpdk-draft-emudev
Feel free to propose a better name.

This series cannot be merged in the main repository
until the Qemu specification is made official.
When Qemu part will be complete, we can assume more reviews
of the DPDK related code.
In the meantime, the draft repository will help getting interest,
testing, reviews and opinions.




^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [dpdk-dev] [PATCH v2 0/8] Introduce emudev library and iavf emudev driver
  2021-01-13 16:52   ` [dpdk-dev] [PATCH v2 0/8] Introduce emudev library and " Thomas Monjalon
@ 2021-01-14  1:35     ` Xia, Chenbo
  0 siblings, 0 replies; 44+ messages in thread
From: Xia, Chenbo @ 2021-01-14  1:35 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: dev, david.marchand, stephen, Liang, Cunming, Lu, Xiuchun, Li,
	Miao, Wu, Jingjing, techboard

Hi Thomas,

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Thursday, January 14, 2021 12:52 AM
> To: Xia, Chenbo <chenbo.xia@intel.com>
> Cc: dev@dpdk.org; david.marchand@redhat.com; stephen@networkplumber.org; Liang,
> Cunming <cunming.liang@intel.com>; Lu, Xiuchun <xiuchun.lu@intel.com>; Li,
> Miao <miao.li@intel.com>; Wu, Jingjing <jingjing.wu@intel.com>;
> techboard@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v2 0/8] Introduce emudev library and iavf
> emudev driver
> 
> 19/12/2020 07:27, Chenbo Xia:
> > This series introduces a new device abstraction called emudev for emulated
> > devices. A new library (librte_emudev) is implemented. The first emudev
> > driver is also introduced, which emulates Intel Adaptive Virtual Function
> > (iavf) as a software network device.
> >
> > This series has a dependency on librte_vfio_user patch series:
> > http://patchwork.dpdk.org/cover/85389/
> >
> > Background & Motivation
> > -----------------------
> > The disaggregated/multi-process QEMU is using VFIO-over-socket/vfio-user
> > as the main transport mechanism to disaggregate IO services from QEMU.
> > Therefore, librte_vfio_user is introduced in DPDK to accommodate
> > emulated devices for high performance I/O. Although vfio-user library
> > provides possibility of emulating devices in DPDK, DPDK does not have
> > a device abstraction for emulated devices. A good device abstraction will
> > be useful for applications or high performance data path driver. With
> > this consideration, emudev library is designed and implemented. It also
> > make it possbile to keep modular design on emulated devices by implementing
> > data path related logic in a standalone driver (e.g., an ethdev driver)
> > and keeps the unrelated logic in the emudev driver.
> 
> 
> As voted today in the Technical Board meeting,
> there will be a draft repository to host this initiative.
> A proposed name for the git tree is:
> 	dpdk-draft-emudev
> Feel free to propose a better name.

This works fine.

> 
> This series cannot be merged in the main repository
> until the Qemu specification is made official.
> When Qemu part will be complete, we can assume more reviews
> of the DPDK related code.

OK.

> In the meantime, the draft repository will help getting interest,
> testing, reviews and opinions.

Thanks for doing this 😊

Cheers,
Chenbo

> 
> 


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v3 0/8] Introduce emudev library and iavf emudev driver
  2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
                     ` (8 preceding siblings ...)
  2021-01-13 16:52   ` [dpdk-dev] [PATCH v2 0/8] Introduce emudev library and " Thomas Monjalon
@ 2021-01-14  6:25   ` Chenbo Xia
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 1/8] lib: introduce emudev library Chenbo Xia
                       ` (7 more replies)
  9 siblings, 8 replies; 44+ messages in thread
From: Chenbo Xia @ 2021-01-14  6:25 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu, beilei.xing

This series introduces a new device abstraction called emudev for emulated
devices. A new library (librte_emudev) is implemented. The first emudev
driver is also introduced, which emulates Intel Adaptive Virtual Function
(iavf) as a software network device.

This series has a dependency on librte_vfio_user patch series:
http://patchwork.dpdk.org/cover/86498/

Background & Motivation 
-----------------------
The disaggregated/multi-process QEMU is using VFIO-over-socket/vfio-user
as the main transport mechanism to disaggregate IO services from QEMU.
Therefore, librte_vfio_user is introduced in DPDK to accommodate
emulated devices for high performance I/O. Although vfio-user library
provides possibility of emulating devices in DPDK, DPDK does not have
a device abstraction for emulated devices. A good device abstraction will
be useful for applications or high performance data path driver. With
this consideration, emudev library is designed and implemented. It also
make it possbile to keep modular design on emulated devices by implementing
data path related logic in a standalone driver (e.g., an ethdev driver)
and keeps the unrelated logic in the emudev driver.

Design overview
---------------

                    +---------------------------------------+
                    |   +---------------+    +-----------+  |
                    |   |  iavf_emudev  |<-->| data path |  |
                    |   |    driver     |    |   driver  |  |
                    |   +---------------+    +-----------+  |
                    |           |                           |
                    | --------------------------- VDEV BUS  |
                    |           |                           |
                    |   +---------------+                   |
+--------------+    |   | vdev:         |                   |
| +----------+ |    |   | /path/to/vfio |                   |
| | Generic  | |    |   +---------------+                   |
| | vfio-dev | |    |           |                           |
| +----------+ |    |           |                           |
| +----------+ |    |      +----------+                     |
| | vfio-user| |    |      | vfio-user|                     |
| | client   | |<---|----->| server   |                     |
| +----------+ |    |      +----------+                     |
| QEMU/DPDK    |    | DPDK                                  |
+--------------+    +---------------------------------------+

- Generic vfio-dev/vfio-user client/vfio-user server
  Above concepts are all introduced in librte_vfio_user patch series:
  http://patchwork.dpdk.org/cover/86498/

- vdev:/path/to/vfio.
  It binds to vdev bus driver. The vdev device is defined by DPDK applications
  through command line as '--vdev=emu_iavf, path=/path/to/socket' in iavf_emudev
  case. Parameters in command line include device name (emu_iavf) which is used
  to identify corresponding driver (in this case, iavf_emudev driver),
  path=/path/to/socket which is used to open the transport interface to vfio-user
  client in QEMU/DPDK.

- data path driver.
  The data path handling is splited to another standalone driver for modular
  design.

Why not rawdev for emulated device
----------------------------------
Instead of introducing new class emudev, emulated device could be presented as rawdev.
However, existing rawdev APIs cannot meet the requirements of emulated device. There are
three API categories for emudev. They are emudev device lifecycle management, backend
facing APIs, and emudev device provider facing APIs respectively. Existing rawdev APIs
could only cover lifecycle management APIs and some of backend facing APIs. Other APIs,
even if added to rawdev API are not required by other rawdev applications.

----------------------------------
v3:
 - fix interrupt issue in iavf emudev driver (Jingjing) 

v2:
 - fix driver meson build file

Chenbo Xia (8):
  lib: introduce emudev library
  doc: add emudev library guide
  emu: introduce emulated iavf driver
  emu/iavf: add vfio-user device register and unregister
  emu/iavf: add resource management and internal logic of iavf
  emu/iavf: add emudev operations to fit in emudev framework
  test/emudev: introduce functional test
  doc: update release notes for iavf emudev driver

 MAINTAINERS                            |   12 +
 app/test/meson.build                   |    5 +-
 app/test/test_emudev.c                 |   29 +
 doc/guides/prog_guide/emudev.rst       |  122 +++
 doc/guides/prog_guide/index.rst        |    1 +
 doc/guides/rel_notes/release_21_02.rst |   16 +
 drivers/emu/iavf/iavf_emu.c            |  262 ++++++
 drivers/emu/iavf/iavf_emu_internal.h   |   69 ++
 drivers/emu/iavf/iavf_emu_test.c       |  174 ++++
 drivers/emu/iavf/iavf_emudev.c         |  239 ++++++
 drivers/emu/iavf/iavf_vfio_user.c      | 1076 ++++++++++++++++++++++++
 drivers/emu/iavf/iavf_vfio_user.h      |   57 ++
 drivers/emu/iavf/meson.build           |   17 +
 drivers/emu/iavf/rte_iavf_emu.h        |  119 +++
 drivers/emu/iavf/version.map           |    3 +
 drivers/emu/meson.build                |    6 +
 drivers/meson.build                    |    1 +
 lib/librte_emudev/meson.build          |    5 +
 lib/librte_emudev/rte_emudev.c         |  502 +++++++++++
 lib/librte_emudev/rte_emudev.h         |  431 ++++++++++
 lib/librte_emudev/rte_emudev_vdev.h    |   53 ++
 lib/librte_emudev/version.map          |   27 +
 lib/meson.build                        |    2 +-
 23 files changed, 3226 insertions(+), 2 deletions(-)
 create mode 100644 app/test/test_emudev.c
 create mode 100644 doc/guides/prog_guide/emudev.rst
 create mode 100644 drivers/emu/iavf/iavf_emu.c
 create mode 100644 drivers/emu/iavf/iavf_emu_internal.h
 create mode 100644 drivers/emu/iavf/iavf_emu_test.c
 create mode 100644 drivers/emu/iavf/iavf_emudev.c
 create mode 100644 drivers/emu/iavf/iavf_vfio_user.c
 create mode 100644 drivers/emu/iavf/iavf_vfio_user.h
 create mode 100644 drivers/emu/iavf/meson.build
 create mode 100644 drivers/emu/iavf/rte_iavf_emu.h
 create mode 100644 drivers/emu/iavf/version.map
 create mode 100644 drivers/emu/meson.build
 create mode 100644 lib/librte_emudev/meson.build
 create mode 100644 lib/librte_emudev/rte_emudev.c
 create mode 100644 lib/librte_emudev/rte_emudev.h
 create mode 100644 lib/librte_emudev/rte_emudev_vdev.h
 create mode 100644 lib/librte_emudev/version.map

-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v3 1/8] lib: introduce emudev library
  2021-01-14  6:25   ` [dpdk-dev] [PATCH v3 " Chenbo Xia
@ 2021-01-14  6:25     ` Chenbo Xia
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 2/8] doc: add emudev library guide Chenbo Xia
                       ` (6 subsequent siblings)
  7 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2021-01-14  6:25 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu, beilei.xing

This patch introduces the emudev library. Emudev library is used
to abstract an emulated device, whose type could be general
(e.g., network, crypto and etc.). Several device-level APIs are
implemented to use or manipulate the device. It can be attached
to another data path driver (e.g., ethdev driver) to plug in its
high performance data path.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
Signed-off-by: Miao Li <miao.li@intel.com>
---
 MAINTAINERS                         |   5 +
 lib/librte_emudev/meson.build       |   5 +
 lib/librte_emudev/rte_emudev.c      | 502 ++++++++++++++++++++++++++++
 lib/librte_emudev/rte_emudev.h      | 431 ++++++++++++++++++++++++
 lib/librte_emudev/rte_emudev_vdev.h |  53 +++
 lib/librte_emudev/version.map       |  27 ++
 lib/meson.build                     |   2 +-
 7 files changed, 1024 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_emudev/meson.build
 create mode 100644 lib/librte_emudev/rte_emudev.c
 create mode 100644 lib/librte_emudev/rte_emudev.h
 create mode 100644 lib/librte_emudev/rte_emudev_vdev.h
 create mode 100644 lib/librte_emudev/version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index 91b8b2ccc1..f5f2c4fe15 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1546,6 +1546,11 @@ M: Chenbo Xia <chenbo.xia@intel.com>
 M: Xiuchun Lu <xiuchun.lu@intel.com>
 F: lib/librte_vfio_user/
 
+Emudev - EXPERIMENTAL
+M: Chenbo Xia <chenbo.xia@intel.com>
+M: Xiuchun Lu <xiuchun.lu@intel.com>
+F: lib/librte_emudev/
+
 Test Applications
 -----------------
 
diff --git a/lib/librte_emudev/meson.build b/lib/librte_emudev/meson.build
new file mode 100644
index 0000000000..4e16cecbaf
--- /dev/null
+++ b/lib/librte_emudev/meson.build
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation
+
+sources = files('rte_emudev.c')
+headers = files('rte_emudev.h', 'rte_emudev_vdev.h')
diff --git a/lib/librte_emudev/rte_emudev.c b/lib/librte_emudev/rte_emudev.c
new file mode 100644
index 0000000000..9a0e42bead
--- /dev/null
+++ b/lib/librte_emudev/rte_emudev.c
@@ -0,0 +1,502 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <string.h>
+
+#include <rte_malloc.h>
+#include <rte_memcpy.h>
+
+#include "rte_emudev.h"
+
+#define RTE_MAX_EMU_DEV 1024
+struct rte_emudev rte_emu_devices[RTE_MAX_EMU_DEV];
+
+static struct rte_emudev_global emu_dev_globals = {
+	.nb_devs = 0
+};
+
+static inline uint16_t
+rte_emu_alloc_dev_id(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < RTE_MAX_EMU_DEV; i++) {
+		if (rte_emu_devices[i].name[0] == '\0')
+			return i;
+	}
+	return RTE_MAX_EMU_DEV;
+}
+
+uint8_t
+rte_emudev_count(void)
+{
+	return emu_dev_globals.nb_devs;
+}
+
+int
+rte_emudev_get_dev_id(const char *name)
+{
+	uint16_t i;
+
+	if (!name) {
+		RTE_EMUDEV_LOG(ERR, "Failed to get device ID: "
+			"NULL device name\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < emu_dev_globals.nb_devs; i++)
+		if (!strncmp(rte_emu_devices[i].name, name,
+			RTE_EMU_NAME_MAX_LEN))
+			return i;
+
+	return -ENODEV;
+}
+
+struct rte_emudev *
+rte_emudev_allocate(const char *name)
+{
+	uint16_t dev_id;
+	struct rte_emudev *emu_dev = NULL;
+	size_t name_len;
+
+	if (!name) {
+		RTE_EMUDEV_LOG(ERR, "Failed to allocate emudev: "
+			"NULL device name\n");
+		return NULL;
+	}
+
+	name_len = strnlen(name, RTE_EMU_NAME_MAX_LEN);
+	if (!name_len) {
+		RTE_EMUDEV_LOG(ERR, "Emulated device name has zero length\n");
+		return NULL;
+	}
+
+	if (name_len >= RTE_EMU_NAME_MAX_LEN) {
+		RTE_EMUDEV_LOG(ERR, "Emulated device name too long\n");
+		return NULL;
+	}
+
+	if (rte_emudev_allocated(name) != NULL) {
+		RTE_EMUDEV_LOG(ERR,
+			"Emulated device with name %s already exists\n",
+			name);
+		return NULL;
+	}
+
+	dev_id = rte_emu_alloc_dev_id();
+	if (dev_id == RTE_MAX_EMU_DEV) {
+		RTE_EMUDEV_LOG(ERR, "Reached max number of Emulated device\n");
+		return NULL;
+	}
+
+	emu_dev = &rte_emu_devices[dev_id];
+	strncpy(emu_dev->name, name, sizeof(emu_dev->name));
+	emu_dev->dev_id = dev_id;
+	emu_dev_globals.nb_devs++;
+
+	return emu_dev;
+}
+
+int
+rte_emudev_release(struct rte_emudev *dev)
+{
+	if (!dev)
+		return -EINVAL;
+
+	if (dev->priv_data) {
+		rte_free(dev->priv_data);
+		dev->priv_data = NULL;
+	}
+
+	memset(dev, 0, sizeof(*dev));
+	emu_dev_globals.nb_devs--;
+	return 0;
+}
+
+struct rte_emudev *
+rte_emudev_allocated(const char *name)
+{
+	unsigned int i;
+
+	if (!name) {
+		RTE_EMUDEV_LOG(ERR, "Failed to find emudev: "
+			"NULL device name\n");
+		return NULL;
+	}
+
+	for (i = 0; i < RTE_MAX_EMU_DEV; i++) {
+		if (rte_emu_devices[i].dev_ops != NULL &&
+		    strcmp(rte_emu_devices[i].device->name, name) == 0)
+			return &rte_emu_devices[i];
+	}
+	return NULL;
+}
+
+int
+rte_emudev_is_valid_id(uint16_t dev_id)
+{
+	if (dev_id >= RTE_MAX_EMU_DEV ||
+		rte_emu_devices[dev_id].name[0] == '\0')
+		return 0;
+	else
+		return 1;
+}
+
+int
+rte_emudev_selftest(uint16_t dev_id)
+{
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	struct rte_emudev *dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_selftest, -ENOTSUP);
+
+	return (*dev->dev_ops->dev_selftest)(dev_id);
+}
+
+
+int
+rte_emudev_start(uint16_t dev_id)
+{
+	struct rte_emudev *dev;
+	int ret;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (dev->started) {
+		RTE_EMUDEV_LOG(ERR, "Device %u already started\n", dev_id);
+		return 0;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_start, -ENOTSUP);
+
+	ret = (*dev->dev_ops->dev_start)(dev);
+	if (ret)
+		return ret;
+
+	dev->started = 1;
+	return 0;
+}
+
+void
+rte_emudev_stop(uint16_t dev_id)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID(dev_id);
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (!dev->started) {
+		RTE_EMUDEV_LOG(ERR, "Device %u already stopped\n", dev_id);
+		return;
+	}
+
+	RTE_FUNC_PTR_OR_RET(*dev->dev_ops->dev_stop);
+
+	(*dev->dev_ops->dev_stop)(dev);
+
+	dev->started = 0;
+}
+
+int
+rte_emudev_configure(uint16_t dev_id, struct rte_emudev_info *dev_conf)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (!dev_conf)
+		return -EINVAL;
+
+	if (dev->started) {
+		RTE_EMUDEV_LOG(ERR, "Device %u must be stopped "
+			"before configure\n", dev_id);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_configure, -ENOTSUP);
+
+	if (strcmp(dev_conf->dev_type, dev->dev_info.dev_type)) {
+		RTE_EMUDEV_LOG(ERR, "Wrong device type to configure"
+			" for device %u\n", dev_id);
+		return -EINVAL;
+	}
+
+	return (*dev->dev_ops->dev_configure)(dev, dev_conf);
+}
+
+int
+rte_emudev_close(uint16_t dev_id)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (dev->started) {
+		RTE_EMUDEV_LOG(ERR, "Device %u must be stopped "
+			"before close\n", dev_id);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_close, -ENOTSUP);
+
+	(*dev->dev_ops->dev_close)(dev);
+
+	rte_emudev_release(dev);
+	return 0;
+}
+
+int
+rte_emudev_subscribe_event(uint16_t dev_id,
+		const rte_emudev_event_chnl_t ev_chnl)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!ev_chnl) {
+		RTE_EMUDEV_LOG(ERR, "Failed to subscribe because of NULL"
+			" event channel for device %u\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->subscribe_event, -ENOTSUP);
+
+	return (*dev->dev_ops->subscribe_event)(dev, ev_chnl);
+}
+
+int
+rte_emudev_unsubscribe_event(uint16_t dev_id,
+		const rte_emudev_event_chnl_t ev_chnl)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!ev_chnl) {
+		RTE_EMUDEV_LOG(ERR, "Failed to unsubscribe because of NULL"
+			" event channel for device %u\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->unsubscribe_event, -ENOTSUP);
+
+	return (*dev->dev_ops->unsubscribe_event)(dev, ev_chnl);
+}
+
+int
+rte_emudev_get_dev_info(uint16_t dev_id, struct rte_emudev_info *info)
+{
+	struct rte_emudev *dev;
+	struct rte_emudev_info *dev_info;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!info) {
+		RTE_EMUDEV_LOG(ERR, "NULL device info for device %u\n",
+			dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+	dev_info = &dev->dev_info;
+
+	strcpy(info->dev_type, dev_info->dev_type);
+	info->max_qp_num = dev_info->max_qp_num;
+	info->region_num = dev_info->region_num;
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_info_get, -ENOTSUP);
+
+	return (*dev->dev_ops->dev_info_get)(dev, info->dev_priv);
+}
+
+int
+rte_emudev_get_mem_table(uint16_t dev_id, rte_emudev_mem_table_t *tb)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!tb) {
+		RTE_EMUDEV_LOG(ERR, "NULL memory table for device %u\n",
+			dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_mem_table, -ENOTSUP);
+
+	return (*dev->dev_ops->get_mem_table)(dev, tb);
+}
+
+int
+rte_emudev_get_queue_info(uint16_t dev_id, uint32_t queue,
+	struct rte_emudev_q_info *info)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!info) {
+		RTE_EMUDEV_LOG(ERR, "NULL queue info for queue %d"
+			" of device %u\n", queue, dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (queue >= dev->dev_info.max_qp_num * 2) {
+		RTE_EMUDEV_LOG(ERR, "Queue index of device %u exceeds max\n",
+			dev_id);
+		return -EINVAL;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_queue_info, -ENOTSUP);
+
+	memset(info, 0, sizeof(*info));
+
+	return (*dev->dev_ops->get_queue_info)(dev, queue, info);
+}
+
+int
+rte_emudev_get_irq_info(uint16_t dev_id, uint32_t vector,
+	struct rte_emudev_irq_info *info)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!info) {
+		RTE_EMUDEV_LOG(ERR, "NULL irq info for vector %u"
+			" of device %u\n", vector, dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_irq_info, -ENOTSUP);
+
+	memset(info, 0, sizeof(*info));
+
+	return (*dev->dev_ops->get_irq_info)(dev, vector, info);
+}
+
+int
+rte_emudev_get_db_info(uint16_t dev_id, uint32_t doorbell,
+	struct rte_emudev_db_info *info)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!info) {
+		RTE_EMUDEV_LOG(ERR, "NULL doorbell info of device %u"
+			" for id %u\n", dev_id, doorbell);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_db_info, -ENOTSUP);
+
+	memset(info, 0, sizeof(*info));
+
+	return (*dev->dev_ops->get_db_info)(dev, doorbell, info);
+}
+
+int
+rte_emudev_set_attr(uint16_t dev_id, const char *attr_name,
+	rte_emudev_attr_t attr)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!attr_name) {
+		RTE_EMUDEV_LOG(ERR, "NULL attribute name of device %u "
+			"for set\n", dev_id);
+		return -EINVAL;
+	}
+
+	if (!attr) {
+		RTE_EMUDEV_LOG(ERR, "NULL attribute of device %u "
+			"for set_attr\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->set_attr, -ENOTSUP);
+
+	return (*dev->dev_ops->set_attr)(dev, attr_name, attr);
+}
+
+int
+rte_emudev_get_attr(uint16_t dev_id, const char *attr_name,
+	rte_emudev_attr_t attr)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!attr_name) {
+		RTE_EMUDEV_LOG(ERR, "NULL attribute name of device %u "
+			"for get\n", dev_id);
+		return -EINVAL;
+	}
+
+	if (!attr) {
+		RTE_EMUDEV_LOG(ERR, "NULL attribute of device %u "
+			"for get_attr\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev = &rte_emu_devices[dev_id];
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->get_attr, -ENOTSUP);
+
+	return (*dev->dev_ops->get_attr)(dev, attr_name, attr);
+}
+
+int
+rte_emudev_region_map(uint16_t dev_id, uint32_t index,
+	uint64_t *region_size, uint64_t *base_addr)
+{
+	struct rte_emudev *dev;
+
+	RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id);
+
+	if (!region_size || !base_addr)
+		return -EINVAL;
+
+	dev = &rte_emu_devices[dev_id];
+
+	if (index >= dev->dev_info.region_num) {
+		RTE_EMUDEV_LOG(ERR, "Wrong region index for device %u\n",
+			dev_id);
+		return -EINVAL;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->region_map, -ENOTSUP);
+
+	memset(region_size, 0, sizeof(*region_size));
+	memset(base_addr, 0, sizeof(*base_addr));
+
+	return (*dev->dev_ops->region_map)(dev, index, region_size,
+		base_addr);
+}
+
+RTE_LOG_REGISTER(rte_emudev_logtype, lib.emudev, INFO);
diff --git a/lib/librte_emudev/rte_emudev.h b/lib/librte_emudev/rte_emudev.h
new file mode 100644
index 0000000000..d7328d4d12
--- /dev/null
+++ b/lib/librte_emudev/rte_emudev.h
@@ -0,0 +1,431 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _RTE_EMUDEV_H_
+#define _RTE_EMUDEV_H_
+
+#include <rte_config.h>
+#include <rte_dev.h>
+#include <rte_compat.h>
+
+#define RTE_EMU_NAME_MAX_LEN RTE_DEV_NAME_MAX_LEN
+
+extern int rte_emudev_logtype;
+
+#define RTE_EMUDEV_LOG(level, ...) \
+	rte_log(RTE_LOG_ ## level, rte_emudev_logtype, "" __VA_ARGS__)
+
+/* Macros to check for valid dev id */
+#define RTE_EMU_CHECK_VALID_DEVID_ERR(dev_id) do { \
+	if (!rte_emudev_is_valid_id(dev_id)) { \
+		RTE_EMUDEV_LOG(ERR, "Invalid dev_id=%u\n", dev_id); \
+		return -ENODEV; \
+	} \
+} while (0)
+
+#define RTE_EMU_CHECK_VALID_DEVID(dev_id) do { \
+	if (!rte_emudev_is_valid_id(dev_id)) { \
+		RTE_EMUDEV_LOG(ERR, "Invalid dev_id=%u\n", dev_id); \
+		return; \
+	} \
+} while (0)
+
+typedef void *rte_emudev_obj_t;
+typedef void *rte_emudev_attr_t;
+typedef void *rte_emudev_mem_table_t;
+typedef void *rte_emudev_event_chnl_t;
+
+struct rte_emudev;
+
+/** 
+ * Global structure used for maintaining state
+ * of allocated emu devices
+ */
+struct rte_emudev_global {
+	uint8_t nb_devs;	/**< Number of devices found */
+};
+
+struct rte_emudev_info {
+	char dev_type[RTE_EMU_NAME_MAX_LEN];
+	uint32_t region_num;
+	uint32_t max_qp_num;
+	rte_emudev_obj_t dev_priv;
+};
+
+struct rte_emudev_q_info {
+	uint64_t base;
+	uint64_t size;
+	int doorbell_id;
+	int irq_vector;
+};
+
+struct rte_emudev_irq_info {
+	uint32_t vector;
+	bool enable;
+	int eventfd;
+};
+
+struct rte_emudev_db_info {
+	uint32_t id;
+	uint32_t flag;
+#define RTE_EMUDEV_DB_FD	(0x1 << 0)
+#define RTE_EMUDEV_DB_MEM	(0x1 << 1)
+	union {
+		int eventfd;
+		struct {
+			uint64_t base;
+			uint64_t size;
+		} mem;
+	} data;
+};
+
+struct rte_emudev_ops {
+	int (*dev_start)(struct rte_emudev *dev);
+	void (*dev_stop)(struct rte_emudev *dev);
+	int (*dev_configure)(struct rte_emudev *dev,
+		struct rte_emudev_info *conf);
+	int (*dev_close)(struct rte_emudev *dev);
+	int (*dev_info_get)(struct rte_emudev *dev, rte_emudev_obj_t info);
+	int (*subscribe_event)(struct rte_emudev *dev,
+		rte_emudev_event_chnl_t ev_chnl);
+	int (*unsubscribe_event)(struct rte_emudev *dev,
+		rte_emudev_event_chnl_t ev_chnl);
+	int (*get_mem_table)(struct rte_emudev *dev,
+		rte_emudev_mem_table_t *tb);
+	int (*get_queue_info)(struct rte_emudev *dev, uint32_t queue,
+		struct rte_emudev_q_info *info);
+	int (*get_irq_info)(struct rte_emudev *dev, uint32_t vector,
+		struct rte_emudev_irq_info *info);
+	int (*get_db_info)(struct rte_emudev *dev, uint32_t doorbell,
+		struct rte_emudev_db_info *info);
+	int (*get_attr)(struct rte_emudev *dev, const char *attr_name,
+		rte_emudev_attr_t attr);
+	int (*set_attr)(struct rte_emudev *dev, const char *attr_name,
+		rte_emudev_attr_t attr);
+	int (*region_map)(struct rte_emudev *dev, uint32_t index,
+		uint64_t *region_size, uint64_t *base_addr);
+	int (*dev_selftest)(uint16_t dev_id);
+};
+
+struct rte_emudev {
+	char name[RTE_EMU_NAME_MAX_LEN];
+	uint16_t dev_id;
+	int numa_node;
+	int started;
+	struct rte_device *device;
+	struct rte_emudev_info dev_info;
+	const struct rte_emudev_ops *dev_ops;
+	void *priv_data;
+	void *backend_priv;
+} __rte_cache_aligned;
+
+/**
+ * Note that 'rte_emudev_allocate', 'rte_emudev_release' and
+ * 'rte_emudev_allocated' should be called by emulated device
+ * provider.
+ */
+
+/**
+ * Allocate a new emudev for an emulation device and returns the pointer
+ * to the emudev.
+ *
+ * @param name
+ *   Name of the emudev
+ * @return
+ *   Pointer to rte_emudev on success, NULL on failure
+ */
+__rte_experimental
+struct rte_emudev *
+rte_emudev_allocate(const char *name);
+
+/**
+ * Release the emudev.
+ *
+ * @param dev
+ *   The emulated device
+ * @return
+ *   - 0: Success, device release
+ *   - <0: Failure on release
+ */
+__rte_experimental
+int
+rte_emudev_release(struct rte_emudev *dev);
+
+/**
+ * Find an emudev using name.
+ *
+ * @param name
+ *   Name of the emudev
+ * @return
+ *   Pointer to rte_emudev on success, NULL on failure
+ */
+__rte_experimental
+struct rte_emudev *
+rte_emudev_allocated(const char *name);
+
+/**
+ * Start an emulation device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @return
+ *   - 0: Success, device start
+ *   - <0: Failure on start
+ */
+__rte_experimental
+int
+rte_emudev_start(uint16_t dev_id);
+
+/**
+ * Stop an emulation device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ */
+__rte_experimental
+void
+rte_emudev_stop(uint16_t dev_id);
+
+/**
+ * Configure an emulation device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param dev_conf
+ *   Device configure info
+ * @return
+ *   - 0: Success, device configured
+ *   - <0: Failure on configure
+ */
+__rte_experimental
+int
+rte_emudev_configure(uint16_t dev_id, struct rte_emudev_info *dev_conf);
+
+/**
+ * Close an emulation device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @return
+ *   - 0: Success, device close
+ *   - <0: Failure on close
+ */
+__rte_experimental
+int
+rte_emudev_close(uint16_t dev_id);
+
+/* Note that below APIs should only be called by back-end (data path) driver */
+
+/**
+ * Back-end driver subscribes events of the emulated device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param ev_chnl
+ *   Event channel that events should be passed to
+ * @return
+ *   - 0: Success, event subscribed
+ *   - <0: Failure on subscribe
+ */
+__rte_experimental
+int
+rte_emudev_subscribe_event(uint16_t dev_id,
+		const rte_emudev_event_chnl_t ev_chnl);
+
+/**
+ * Back-end driver unsubscribes events of the emulated device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param set
+ *   Event channel that events should be passed to
+ * @return
+ *   - 0: Success, event unsubscribed
+ *   - <0: Failure on unsubscribe
+ */
+__rte_experimental
+int
+rte_emudev_unsubscribe_event(uint16_t dev_id,
+		const rte_emudev_event_chnl_t ev_chnl);
+
+/**
+ * Get the total number of emulated devices that have been
+ * successfully initialised.
+ *
+ * @return
+ *   The total number of usable emudev.
+ */
+__rte_experimental
+uint8_t
+rte_emudev_count(void);
+
+/**
+ * Get the device identifier for the named emulated device.
+ *
+ * @param name
+ *   Emulated device name to select the device identifier.
+ *
+ * @return
+ *   - 0: Success, emulated device identifier returned
+ *   - <0: Failure on unsubscribe
+ */
+__rte_experimental
+int
+rte_emudev_get_dev_id(const char *name);
+
+/**
+ * Back-end driver gets the device info of the emulated device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @return
+ *   - 0: Success, emulated device info updated
+ *   - <0: Failure on get device information
+ */
+__rte_experimental
+int
+rte_emudev_get_dev_info(uint16_t dev_id, struct rte_emudev_info *info);
+
+/**
+ * Get the memory table content and operations of the emulated device.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @return
+ *   - 0: Success, memory table of emulated device updated
+ *   - <0: Failure on get memory table
+ */
+__rte_experimental
+int
+rte_emudev_get_mem_table(uint16_t dev_id, rte_emudev_mem_table_t *tb);
+
+/**
+ * Get queue info of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param queue
+ *   Queue ID of emudev
+ * @return
+ *   - 0: Success, queue information of emulated device updated
+ *   - <0: Failure on get queue information
+ */
+__rte_experimental
+int
+rte_emudev_get_queue_info(uint16_t dev_id, uint32_t queue,
+	struct rte_emudev_q_info *info);
+
+/**
+ * Get irq info of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param vector
+ *   Interrupt vector
+ * @return
+ *   - 0: Success, irq information of emulated device updated
+ *   - <0: Failure on get irq information
+ */
+__rte_experimental
+int
+rte_emudev_get_irq_info(uint16_t dev_id, uint32_t vector,
+	struct rte_emudev_irq_info *info);
+
+/**
+ * Get doorbell info of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param doorbell
+ *   Doorbell ID
+ * @return
+ *   - 0: Success, doorbell information of emulated device updated
+ *   - <0: Failure on get doorbell information
+ */
+__rte_experimental
+int
+rte_emudev_get_db_info(uint16_t dev_id, uint32_t doorbell,
+	struct rte_emudev_db_info *info);
+
+/**
+ * Set attribute of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param attr_name
+ *   Opaque object representing an attribute in implementation.
+ * @param attr
+ *   Pointer to attribute
+ * @return
+ *   - 0: Success, attribute set
+ *   - <0: Failure on attribute set
+ */
+__rte_experimental
+int
+rte_emudev_set_attr(uint16_t dev_id, const char *attr_name,
+	rte_emudev_attr_t attr);
+
+/**
+ * Get attribute of the emudev.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param attr_name
+ *   Opaque object representing an attribute in implementation.
+ * @return
+ *   - 0: Success, attribute of emulated device updated
+ *   - <0: Failure on attribute get
+ */
+__rte_experimental
+int
+rte_emudev_get_attr(uint16_t dev_id, const char *attr_name,
+	rte_emudev_attr_t attr);
+
+/**
+ * Back-end driver maps a region to the emulated device.
+ * Region name identifies the meaning of the region and the emulated
+ * device and the back-end driver should have the same definition of
+ * region name and its meaning.
+ *
+ * @param dev_id
+ *   Device ID of emudev
+ * @param index
+ *   Region index
+ * @param attr
+ *   Pointer to attribute
+ * @return
+ *   - 0: Success, region mapped
+ *   - <0: Failure on region map
+ */
+__rte_experimental
+int
+rte_emudev_region_map(uint16_t dev_id, uint32_t index,
+	uint64_t *region_size, uint64_t *base_addr);
+
+/**
+ * Trigger the emudev self test.
+ *
+ * @param dev_id
+ *   The identifier of the device
+ * @return
+ *   - 0: Selftest successful
+ *   - <0: Failure on selftest
+ */
+__rte_experimental
+int
+rte_emudev_selftest(uint16_t dev_id);
+
+/**
+ * Check if an emudev device ID is valid.
+ *
+ * @param dev_id
+ *   The identifier of the device
+ * @return
+ *   0 on failure, 1 on success
+ */
+__rte_experimental
+int
+rte_emudev_is_valid_id(uint16_t dev_id);
+
+#endif /* _RTE_EMUDEV_H_ */
diff --git a/lib/librte_emudev/rte_emudev_vdev.h b/lib/librte_emudev/rte_emudev_vdev.h
new file mode 100644
index 0000000000..85f534b4bd
--- /dev/null
+++ b/lib/librte_emudev/rte_emudev_vdev.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _RTE_EMUDEV_VDEV_H_
+#define _RTE_EMUDEV_VDEV_H_
+
+#include <rte_bus_vdev.h>
+#include <rte_malloc.h>
+
+#include "rte_emudev.h"
+
+/**
+ * @internal
+ * Allocates a new emudev instance for an emulated device and
+ * returns the pointer to that instance for the driver to use.
+ *
+ * @param dev
+ *	Pointer to virtual device
+ *
+ * @param private_data_size
+ *	Size of private data structure
+ *
+ * @return
+ *	A pointer to a rte_emudev or NULL if allocation failed.
+ */
+static inline struct rte_emudev *
+rte_emu_vdev_allocate(struct rte_vdev_device *dev, size_t private_data_size)
+{
+	struct rte_emudev *emu_dev;
+	const char *name = rte_vdev_device_name(dev);
+
+	emu_dev = rte_emudev_allocate(name);
+	if (!emu_dev)
+		return NULL;
+
+	if (private_data_size) {
+		emu_dev->priv_data = rte_zmalloc_socket(name,
+			private_data_size, RTE_CACHE_LINE_SIZE,
+			dev->device.numa_node);
+		if (!emu_dev->priv_data) {
+			rte_emudev_release(emu_dev);
+			return NULL;
+		}
+	}
+
+	emu_dev->device = &dev->device;
+	emu_dev->numa_node = dev->device.numa_node;
+
+	return emu_dev;
+}
+
+#endif /* _RTE_EMUDEV_VDEV_H_ */
diff --git a/lib/librte_emudev/version.map b/lib/librte_emudev/version.map
new file mode 100644
index 0000000000..f800b4c21c
--- /dev/null
+++ b/lib/librte_emudev/version.map
@@ -0,0 +1,27 @@
+EXPERIMENTAL {
+	global:
+
+	rte_emudev_allocate;
+	rte_emudev_release;
+	rte_emudev_allocated;
+	rte_emudev_start;
+	rte_emudev_stop;
+	rte_emudev_configure;
+	rte_emudev_close;
+	rte_emudev_subscribe_event;
+	rte_emudev_unsubscribe_event;
+	rte_emudev_count;
+	rte_emudev_get_dev_id;
+	rte_emudev_get_dev_info;
+	rte_emudev_get_mem_table;
+	rte_emudev_get_queue_info;
+	rte_emudev_get_irq_info;
+	rte_emudev_get_db_info;
+	rte_emudev_set_attr;
+	rte_emudev_get_attr;
+	rte_emudev_region_map;
+	rte_emudev_selftest;
+	rte_emudev_is_valid_id;
+
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index b7fbfcc95b..6dd07fb73e 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -28,7 +28,7 @@ libraries = [
 	'rib', 'reorder', 'sched', 'security', 'stack', 'vhost',
 	# ipsec lib depends on net, crypto and security
 	'ipsec',
-	'vfio_user',
+	'vfio_user', 'emudev',
 	#fib lib depends on rib
 	'fib',
 	# add pkt framework libs which use other libs from above
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v3 2/8] doc: add emudev library guide
  2021-01-14  6:25   ` [dpdk-dev] [PATCH v3 " Chenbo Xia
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 1/8] lib: introduce emudev library Chenbo Xia
@ 2021-01-14  6:25     ` Chenbo Xia
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 3/8] emu: introduce emulated iavf driver Chenbo Xia
                       ` (5 subsequent siblings)
  7 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2021-01-14  6:25 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu, beilei.xing

Add emudev library guide and update release notes.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
---
 doc/guides/prog_guide/emudev.rst       | 122 +++++++++++++++++++++++++
 doc/guides/prog_guide/index.rst        |   1 +
 doc/guides/rel_notes/release_21_02.rst |  12 +++
 3 files changed, 135 insertions(+)
 create mode 100644 doc/guides/prog_guide/emudev.rst

diff --git a/doc/guides/prog_guide/emudev.rst b/doc/guides/prog_guide/emudev.rst
new file mode 100644
index 0000000000..e40213bb5e
--- /dev/null
+++ b/doc/guides/prog_guide/emudev.rst
@@ -0,0 +1,122 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2020 Intel Corporation.
+
+Emulated Device Library
+=================
+
+Introduction
+------------
+
+The DPDK Emudev library is an abstraction for emulated device. This library
+provides a generic set of APIs for device provider, data path provider and
+applications to use.
+
+A device provider could be implemented as a driver on vdev bus. It should
+expose itself as an emudev for applications to use. It is responsible for the
+device resource management and the device's internal logic. All specifics of a
+device, except data path handling, should be implemented in the device
+provider. The device provider uses emudev APIs mainly for create/destroy an
+emudev instance. The device provider should also use a transport to communicate
+with device consumer (e.g., virtual machine monitor or container). A potential
+choice could be vfio-user library, which implements the vfio-user protocol for
+emulating devices outside of a virtual machine monitor.
+
+A data path provider could be implemented as any type of driver on vdev bus.
+If the device you want to emulate is a network device, you could implement
+it as an ethdev driver. It is responsible for all data path handling. The data
+path provider uses emudev APIs mainly for getting device-related information
+from the device provider.
+
+Applications uses emudev APIs for device lifecycle management and configuration.
+
+Design
+------------
+
+Some key objects are designed in emudev.
+
+  ``Regions`` are the device layout exposed to the data path provider.
+
+  ``Queues`` are the data path queues that the data path provider needs. Queue
+  information includes queue base address, queue size, queue-related doorbell
+  and interrupt information.
+
+  ``Memory Table`` is the DMA mapping table. The data path provider could use
+  it to perform DMA read/write on device consumer's memory.
+
+Information of above key objects could be acquired through emudev APIs. The
+following will introduce the emudev APIs which are used by data path provider
+and applications. The APIs for device provider to use are allocate/release APIs
+and will not be listed because it's similar to other device abstraction.
+
+There are five categories of APIs:
+
+1. Lifecycle management
+
+* ``rte_emu_dev_start(dev_id)``
+* ``rte_emu_dev_stop(dev_id)``
+* ``rte_emu_dev_configure(dev_id)``
+* ``rte_emu_dev_close(dev_id)``
+
+  Above APIs are respectively for device start/stop/configure/close and mainly
+  for applications to use.
+
+  ``dev_id`` is the emudev device ID.
+
+2. Notification
+
+* ``rte_emu_subscribe_event(dev_id, ev_chnl)``
+* ``rte_emu_unsubscribe_event(dev_id, ev_chnl)``
+
+  Above APIs are for data path provider and applications to register events.
+  The mechanism of event notification could be different in different device
+  providers. A possbile implementation could be event callbacks.
+
+  ``ev_chnl`` is the event channel pointer. The definition varies between
+  different devices.
+
+3. Region-related
+
+* ``rte_emu_region_map(dev_id, index, region_size, base_addr)``
+* ``rte_emu_get_attr(dev_id, attr_name, attr)``
+* ``rte_emu_set_attr(dev_id, attr_name, attr)``
+
+  Above APIs are for data path provider and applications to read/write regions.
+  ``rte_emu_region_map`` is for directly mapping the region and use the mapped
+  address to read/write it. ``rte_emu_get_attr`` and ``rte_emu_set_attr`` are
+  respectively for getting/setting certain attributes in all regions.
+
+  Applications will set attributes or write regions for device configuration.
+
+  In ``rte_emu_region_map``:
+  - ``index`` is the region index.
+  - ``region_size`` is for saving the size of mapped region.
+  - ``base_addr`` is for saving the address of mapped region.
+
+  In ``rte_emu_get_attr`` and ``rte_emu_set_attr``:
+  - ``attr_name`` is the name of attribute. Note that attribute names are aligned
+  between device provider and data path provider for the same device.
+  - ``attr`` is the attribute value.
+
+4. Queue-related
+
+* ``rte_emu_get_queue_info(dev_id, queue, info)``
+* ``rte_emu_get_irq_info(dev_id, irq, info)``
+* ``rte_emu_get_db_info(dev_id, doorbell, info)``
+
+  Above APIs are for data path provider to get queue/interrupt/doorbell information.
+
+  - ``queue``, ``irq`` and ``doorbell`` are respectively the queue/interrupt/doorbell
+  index.
+  - ``info`` is for saving the queue/interrupt/doorbell info.
+
+5. Direct Memory Access
+
+* ``rte_emu_get_mem_table(dev_id, tb)``
+
+  Above APIs are for data path provider to get the information of DMA memory table.
+  The memory table implementation varies between different devices and memory table
+  operations should better be helper functions exposed by device provider. Because
+  address translation make a difference in data path performance, the memory table
+  implementation should have high efficiency.
+
+  ``tb`` is for saving the DMA memory table.
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index f9847b1058..0ed15a0995 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -71,3 +71,4 @@ Programmer's Guide
     profile_app
     glossary
     vfio_user_lib
+    emudev
diff --git a/doc/guides/rel_notes/release_21_02.rst b/doc/guides/rel_notes/release_21_02.rst
index eabbbfebef..9686930de3 100644
--- a/doc/guides/rel_notes/release_21_02.rst
+++ b/doc/guides/rel_notes/release_21_02.rst
@@ -67,6 +67,18 @@ New Features
 
   See :doc:`../prog_guide/vfio_user_lib` for more information.
 
+* **Added emudev Library.**
+
+  Added an experimental library ``librte_emudev`` to provide device abstraction
+  for an emulated device.
+
+  The library abstracts an emulated device and provides several categories of
+  device-level APIs. The specific device type could be general (e.g, network,
+  crypto and etc.). It can be attached to another data path driver (e.g, ethdev
+  driver) to leverage the high performance of DPDK data path driver.
+
+  See :doc:`../prog_guide/emudev` for more information.
+
 Removed Items
 -------------
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v3 3/8] emu: introduce emulated iavf driver
  2021-01-14  6:25   ` [dpdk-dev] [PATCH v3 " Chenbo Xia
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 1/8] lib: introduce emudev library Chenbo Xia
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 2/8] doc: add emudev library guide Chenbo Xia
@ 2021-01-14  6:25     ` Chenbo Xia
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 4/8] emu/iavf: add vfio-user device register and unregister Chenbo Xia
                       ` (4 subsequent siblings)
  7 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2021-01-14  6:25 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu, beilei.xing

This patch introduces emulated iavf driver. It is a vdev driver
emulating all iavf device behavior except data path handling.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
---
 MAINTAINERS                          |   7 +
 drivers/emu/iavf/iavf_emu.c          |  30 ++++
 drivers/emu/iavf/iavf_emu_internal.h |  49 +++++++
 drivers/emu/iavf/iavf_emudev.c       | 209 +++++++++++++++++++++++++++
 drivers/emu/iavf/meson.build         |   8 +
 drivers/emu/iavf/rte_iavf_emu.h      |  43 ++++++
 drivers/emu/iavf/version.map         |   3 +
 drivers/emu/meson.build              |   6 +
 drivers/meson.build                  |   1 +
 9 files changed, 356 insertions(+)
 create mode 100644 drivers/emu/iavf/iavf_emu.c
 create mode 100644 drivers/emu/iavf/iavf_emu_internal.h
 create mode 100644 drivers/emu/iavf/iavf_emudev.c
 create mode 100644 drivers/emu/iavf/meson.build
 create mode 100644 drivers/emu/iavf/rte_iavf_emu.h
 create mode 100644 drivers/emu/iavf/version.map
 create mode 100644 drivers/emu/meson.build

diff --git a/MAINTAINERS b/MAINTAINERS
index f5f2c4fe15..3ef8bd1999 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1276,6 +1276,13 @@ F: doc/guides/rawdevs/ntb.rst
 F: examples/ntb/
 F: doc/guides/sample_app_ug/ntb.rst
 
+Emudev Drivers
+--------------
+
+Intel iavf
+M: Chenbo Xia <chenbo.xia@intel.com>
+M: Xiuchun Lu <xiuchun.lu@intel.com>
+F: drivers/emulation/iavf/
 
 Packet processing
 -----------------
diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
new file mode 100644
index 0000000000..98abfcdca2
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include "iavf_emu_internal.h"
+
+static int
+iavf_emu_dev_close(struct rte_emudev *dev)
+{
+	struct iavf_emudev *iavf;
+
+	/* For now, we don't support device close when data
+	 * path driver is attached
+	 */
+	if (dev->backend_priv) {
+		EMU_IAVF_LOG(ERR, "Close failed because of "
+			"data path attached\n");
+		return -EPERM;
+	}
+
+	iavf = (struct iavf_emudev *)dev->priv_data;
+	iavf_emu_uninit_device(iavf);
+	dev->priv_data = NULL;
+
+	return 0;
+}
+
+struct rte_emudev_ops emu_iavf_ops = {
+	.dev_close = iavf_emu_dev_close,
+};
diff --git a/drivers/emu/iavf/iavf_emu_internal.h b/drivers/emu/iavf/iavf_emu_internal.h
new file mode 100644
index 0000000000..a726bfe577
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emu_internal.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IAVF_EMU_ITNL_H
+#define _IAVF_EMU_ITNL_H
+
+#include <stdint.h>
+
+#include <rte_log.h>
+
+#include "rte_iavf_emu.h"
+
+extern struct rte_emudev_ops emu_iavf_ops;
+
+extern int emu_iavf_logtype;
+#define EMU_IAVF_LOG(level, ...) \
+	rte_log(RTE_LOG_ ## level, emu_iavf_logtype, "EMU_IAVF: " __VA_ARGS__)
+
+struct iavf_emu_intr_info {
+	int enable;
+	int fd;
+};
+
+struct iavf_emu_intr {
+	uint32_t intr_num;
+	struct iavf_emu_intr_info info[RTE_IAVF_EMU_MAX_INTR];
+};
+
+struct iavf_emu_lanQ {
+	uint16_t db_size;
+	void *doorbell;
+};
+
+struct iavf_emudev {
+	struct rte_emudev *edev;
+	/* Maximum LANQ queue pair that this emulated iavf has */
+	uint16_t max_lanqp;
+	/* Maximum LANQ queue pair number that back-end driver can use */
+	uint16_t max_be_lanqp;
+	unsigned int numa_node;
+	char *sock_addr;
+	struct rte_iavf_emu_mem *mem;
+	struct iavf_emu_intr *intr;
+	struct iavf_emu_lanQ *lanq;
+};
+
+void iavf_emu_uninit_device(struct iavf_emudev *dev);
+#endif
diff --git a/drivers/emu/iavf/iavf_emudev.c b/drivers/emu/iavf/iavf_emudev.c
new file mode 100644
index 0000000000..35c3557188
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emudev.c
@@ -0,0 +1,209 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <rte_kvargs.h>
+#include <rte_emudev.h>
+#include <rte_emudev_vdev.h>
+
+#include "iavf_emu_internal.h"
+
+#define EMU_IAVF_SOCK_ARG "sock"
+#define EMU_IAVF_QUEUES_ARG "queues"
+
+static const char * const emu_iavf_valid_arg[] = {
+	EMU_IAVF_SOCK_ARG,
+	EMU_IAVF_QUEUES_ARG,
+	NULL
+};
+
+static inline int
+save_sockaddr(const char *key __rte_unused, const char *value,
+	void *extra_args)
+{
+	const char **sock_addr = extra_args;
+
+	if (value == NULL)
+		return -1;
+
+	*sock_addr = value;
+
+	return 0;
+}
+
+static inline int
+save_int(const char *key __rte_unused, const char *value, void *extra_args)
+{
+	uint16_t *n = extra_args;
+
+	if (value == NULL || extra_args == NULL)
+		return -EINVAL;
+
+	*n = (uint16_t)strtoul(value, NULL, 0);
+	if (*n == USHRT_MAX && errno == ERANGE)
+		return -1;
+
+	return 0;
+}
+
+static int
+iavf_emu_init_device(struct iavf_emudev *dev,
+	char *sock_addr, uint16_t queues, unsigned int numa_node)
+{
+	dev->sock_addr = rte_malloc_socket("sock_addr",
+		strlen(sock_addr) + 1, 0, numa_node);
+	if (!dev->sock_addr) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc sock addr\n");
+		goto exit;
+	}
+	strcpy(dev->sock_addr, sock_addr);
+
+	dev->mem = rte_zmalloc_socket("iavf_emu_mem",
+			sizeof(struct rte_iavf_emu_mem),
+			0, numa_node);
+	if (!dev->mem) {
+		EMU_IAVF_LOG(ERR, "Unable to alloc iavf_emu_mem.\n");
+		goto err_mem;
+	}
+
+	dev->intr = rte_zmalloc_socket("iavf_emu_intr",
+			sizeof(struct iavf_emu_intr),
+			0, numa_node);
+	if (!dev->intr) {
+		EMU_IAVF_LOG(ERR, "Unable to alloc iavf_emu_intr.\n");
+		goto err_intr;
+	}
+
+	dev->lanq = rte_zmalloc_socket("iavf_emu_lanQ",
+			sizeof(struct iavf_emu_lanQ) * queues * 2,
+			0, numa_node);
+	if (!dev->lanq) {
+		EMU_IAVF_LOG(ERR, "Unable to alloc iavf_emu_lanQ.\n");
+		goto err_lanq;
+	}
+
+	dev->numa_node = numa_node;
+
+	return 0;
+
+err_lanq:
+	rte_free(dev->lanq);
+err_intr:
+	rte_free(dev->intr);
+err_mem:
+	rte_free(dev->sock_addr);
+exit:
+	return -1;
+}
+
+void
+iavf_emu_uninit_device(struct iavf_emudev *dev)
+{
+	rte_free(dev->sock_addr);
+	rte_free(dev->mem);
+	rte_free(dev->intr);
+	rte_free(dev->lanq);
+}
+
+static int
+rte_emu_iavf_probe(struct rte_vdev_device *dev)
+{
+	struct rte_kvargs *kvlist = NULL;
+	struct rte_emudev *edev;
+	struct iavf_emudev *iavf;
+	char *sock_addr;
+	uint16_t queues;
+	int ret = 0;
+
+	kvlist = rte_kvargs_parse(rte_vdev_device_args(dev),
+		emu_iavf_valid_arg);
+	if (kvlist == NULL)
+		return -1;
+
+	if (rte_kvargs_count(kvlist, EMU_IAVF_SOCK_ARG) == 1) {
+		ret = rte_kvargs_process(kvlist, EMU_IAVF_SOCK_ARG,
+					 &save_sockaddr, &sock_addr);
+		if (ret < 0)
+			goto err;
+	} else {
+		ret = -1;
+		goto err;
+	}
+
+	if (rte_kvargs_count(kvlist, EMU_IAVF_QUEUES_ARG) == 1) {
+		ret = rte_kvargs_process(kvlist, EMU_IAVF_QUEUES_ARG,
+					 &save_int, &queues);
+		if (ret < 0 || queues > RTE_MAX_QUEUES_PER_PORT ||
+			queues > RTE_IAVF_EMU_MAX_QP_NUM)
+			goto err;
+
+	} else
+		queues = 1;
+
+	if (dev->device.numa_node == SOCKET_ID_ANY)
+		dev->device.numa_node = rte_socket_id();
+
+	edev = rte_emu_vdev_allocate(dev, sizeof(*iavf));
+	if (!edev) {
+		EMU_IAVF_LOG(ERR, "Failed to allocate emu_vdev\n");
+		ret = -1;
+		goto err;
+	}
+	edev->dev_ops = &emu_iavf_ops;
+	edev->dev_info.region_num = RTE_IAVF_EMU_MAPPABLE_REG_NUM;
+	edev->dev_info.max_qp_num = queues + RTE_IAVF_EMU_ADMINQ_NUM / 2;
+
+	strcpy(edev->dev_info.dev_type, RTE_IAVF_EMUDEV_TYPE);
+
+	iavf = (struct iavf_emudev *)edev->priv_data;
+	ret = iavf_emu_init_device(iavf, sock_addr, queues,
+		dev->device.numa_node);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to init new iavf device\n");
+		ret = -1;
+		goto err_ndev;
+	}
+
+	iavf->edev = edev;
+	/* If not configured, we assume back-end driver
+	 * can use all queues of emulated iavf
+	 */
+	iavf->max_be_lanqp = queues;
+	iavf->max_lanqp = queues;
+	edev->priv_data = (void *)iavf;
+
+	edev->started = 1;
+	rte_kvargs_free(kvlist);
+	return 0;
+
+err_ndev:
+	rte_emudev_release(edev);
+err:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
+static int
+rte_emu_iavf_remove(struct rte_vdev_device *dev)
+{
+	struct rte_emudev *emu_dev;
+
+	/* Find the emudev entry */
+	emu_dev = rte_emudev_allocated(rte_vdev_device_name(dev));
+	if (!emu_dev)
+		return 0;
+
+	return rte_emudev_close(emu_dev->dev_id);
+}
+
+static struct rte_vdev_driver emu_iavf_drv = {
+	.probe = rte_emu_iavf_probe,
+	.remove = rte_emu_iavf_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(emu_iavf, emu_iavf_drv);
+RTE_PMD_REGISTER_PARAM_STRING(emu_iavf,
+	"sock=<path> "
+	"queues=<int> ");
+
+RTE_LOG_REGISTER(emu_iavf_logtype, emu.iavf, INFO);
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
new file mode 100644
index 0000000000..58c2a90383
--- /dev/null
+++ b/drivers/emu/iavf/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation
+
+sources = files('iavf_emu.c', 'iavf_emudev.c')
+
+deps += ['bus_vdev', 'emudev']
+
+headers = files('rte_iavf_emu.h')
diff --git a/drivers/emu/iavf/rte_iavf_emu.h b/drivers/emu/iavf/rte_iavf_emu.h
new file mode 100644
index 0000000000..623c3c5d99
--- /dev/null
+++ b/drivers/emu/iavf/rte_iavf_emu.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IAVF_EMU_H
+#define _IAVF_EMU_H
+
+#include <stdint.h>
+
+#include <rte_emudev.h>
+
+#define RTE_IAVF_EMUDEV_TYPE "iavf"
+#define RTE_IAVF_EMU_MAX_MEM_REGIONS 256
+#define RTE_IAVF_EMU_MAX_QP_NUM 256
+#define RTE_IAVF_EMU_MAX_INTR 32
+
+enum {
+	RTE_IAVF_EMU_ADMINQ_TXQ = 0,
+	RTE_IAVF_EMU_ADMINQ_RXQ = 1,
+	RTE_IAVF_EMU_ADMINQ_NUM = 2,
+};
+
+enum {
+	RTE_IAVF_EMU_MAPPABLE_REG_BAR0 = 0,
+	RTE_IAVF_EMU_MAPPABLE_REG_BAR3 = 1,
+	RTE_IAVF_EMU_MAPPABLE_REG_NUM = 2,
+};
+
+struct rte_iavf_emu_mem_reg {
+	uint64_t guest_phys_addr;
+	uint64_t host_user_addr;
+	uint64_t size;
+	void	 *mmap_addr;
+	uint64_t mmap_size;
+	int fd;
+};
+
+struct rte_iavf_emu_mem {
+	uint32_t region_num;
+	struct rte_iavf_emu_mem_reg regions[RTE_IAVF_EMU_MAX_MEM_REGIONS];
+};
+
+#endif
diff --git a/drivers/emu/iavf/version.map b/drivers/emu/iavf/version.map
new file mode 100644
index 0000000000..4a76d1d52d
--- /dev/null
+++ b/drivers/emu/iavf/version.map
@@ -0,0 +1,3 @@
+DPDK_21 {
+	local: *;
+};
diff --git a/drivers/emu/meson.build b/drivers/emu/meson.build
new file mode 100644
index 0000000000..acc8c395ef
--- /dev/null
+++ b/drivers/emu/meson.build
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation
+
+drivers = ['iavf']
+std_deps = ['emudev']
+config_flag_fmt = 'RTE_LIBRTE_PMD_@0@_EMUDEV'
diff --git a/drivers/meson.build b/drivers/meson.build
index f9febc579e..64c34d2f9f 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -8,6 +8,7 @@ subdirs = [
 	'common/mlx5', # depends on bus.
 	'common/qat', # depends on bus.
 	'mempool', # depends on common and bus.
+	'emu', # depends on common and bus.
 	'net',     # depends on common, bus, mempool
 	'raw',     # depends on common, bus and net.
 	'crypto',  # depends on common, bus and mempool (net in future).
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v3 4/8] emu/iavf: add vfio-user device register and unregister
  2021-01-14  6:25   ` [dpdk-dev] [PATCH v3 " Chenbo Xia
                       ` (2 preceding siblings ...)
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 3/8] emu: introduce emulated iavf driver Chenbo Xia
@ 2021-01-14  6:25     ` Chenbo Xia
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 5/8] emu/iavf: add resource management and internal logic of iavf Chenbo Xia
                       ` (3 subsequent siblings)
  7 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2021-01-14  6:25 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu, beilei.xing

This patch adds vfio-user APIs call in driver probe and remove.
rte_vfio_user_register() and rte_vfio_user_unregister() are called
to create/destroy a vfio-user device. Notify callbacks that
libvfio_user defines are also implemented.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Miao Li <miao.li@intel.com>
---
 drivers/emu/iavf/iavf_emu.c          |   3 +-
 drivers/emu/iavf/iavf_emu_internal.h |  19 ++
 drivers/emu/iavf/iavf_emudev.c       |  12 +-
 drivers/emu/iavf/iavf_vfio_user.c    | 399 +++++++++++++++++++++++++++
 drivers/emu/iavf/iavf_vfio_user.h    |  16 ++
 drivers/emu/iavf/meson.build         |   5 +-
 drivers/emu/iavf/rte_iavf_emu.h      |  17 ++
 7 files changed, 467 insertions(+), 4 deletions(-)
 create mode 100644 drivers/emu/iavf/iavf_vfio_user.c
 create mode 100644 drivers/emu/iavf/iavf_vfio_user.h

diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index 98abfcdca2..2f1513137c 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -2,7 +2,7 @@
  * Copyright(c) 2020 Intel Corporation
  */
 
-#include "iavf_emu_internal.h"
+#include "iavf_vfio_user.h"
 
 static int
 iavf_emu_dev_close(struct rte_emudev *dev)
@@ -19,6 +19,7 @@ iavf_emu_dev_close(struct rte_emudev *dev)
 	}
 
 	iavf = (struct iavf_emudev *)dev->priv_data;
+	iavf_emu_unregister_vfio_user(iavf);
 	iavf_emu_uninit_device(iavf);
 	dev->priv_data = NULL;
 
diff --git a/drivers/emu/iavf/iavf_emu_internal.h b/drivers/emu/iavf/iavf_emu_internal.h
index a726bfe577..10197c00ba 100644
--- a/drivers/emu/iavf/iavf_emu_internal.h
+++ b/drivers/emu/iavf/iavf_emu_internal.h
@@ -17,6 +17,13 @@ extern int emu_iavf_logtype;
 #define EMU_IAVF_LOG(level, ...) \
 	rte_log(RTE_LOG_ ## level, emu_iavf_logtype, "EMU_IAVF: " __VA_ARGS__)
 
+struct iavf_emu_vfio_user {
+	int dev_id;
+	struct vfio_device_info *dev_info;
+	struct rte_vfio_user_regions *reg;
+	struct rte_vfio_user_irq_info *irq;
+};
+
 struct iavf_emu_intr_info {
 	int enable;
 	int fd;
@@ -27,6 +34,14 @@ struct iavf_emu_intr {
 	struct iavf_emu_intr_info info[RTE_IAVF_EMU_MAX_INTR];
 };
 
+struct iavf_emu_adminQ {
+	uint32_t *ring_addr_lo;
+	uint32_t *ring_addr_hi;
+	uint32_t *ring_sz;
+	uint16_t db_size;
+	void *doorbell;
+};
+
 struct iavf_emu_lanQ {
 	uint16_t db_size;
 	void *doorbell;
@@ -34,14 +49,18 @@ struct iavf_emu_lanQ {
 
 struct iavf_emudev {
 	struct rte_emudev *edev;
+	struct iavf_emu_vfio_user *vfio;
 	/* Maximum LANQ queue pair that this emulated iavf has */
 	uint16_t max_lanqp;
 	/* Maximum LANQ queue pair number that back-end driver can use */
 	uint16_t max_be_lanqp;
 	unsigned int numa_node;
+	int ready;
 	char *sock_addr;
+	struct rte_iavf_emu_notify_ops *ops;
 	struct rte_iavf_emu_mem *mem;
 	struct iavf_emu_intr *intr;
+	struct iavf_emu_adminQ adq[RTE_IAVF_EMU_ADMINQ_NUM];
 	struct iavf_emu_lanQ *lanq;
 };
 
diff --git a/drivers/emu/iavf/iavf_emudev.c b/drivers/emu/iavf/iavf_emudev.c
index 35c3557188..6ba1cc2a89 100644
--- a/drivers/emu/iavf/iavf_emudev.c
+++ b/drivers/emu/iavf/iavf_emudev.c
@@ -6,7 +6,7 @@
 #include <rte_emudev.h>
 #include <rte_emudev_vdev.h>
 
-#include "iavf_emu_internal.h"
+#include "iavf_vfio_user.h"
 
 #define EMU_IAVF_SOCK_ARG "sock"
 #define EMU_IAVF_QUEUES_ARG "queues"
@@ -172,10 +172,20 @@ rte_emu_iavf_probe(struct rte_vdev_device *dev)
 	iavf->max_lanqp = queues;
 	edev->priv_data = (void *)iavf;
 
+	ret = iavf_emu_register_vfio_user(iavf);
+	if (ret) {
+		EMU_IAVF_LOG(ERR,
+			"Emulated iavf failed to register vfio user.\n");
+		ret = -1;
+		goto err_reg;
+	}
+
 	edev->started = 1;
 	rte_kvargs_free(kvlist);
 	return 0;
 
+err_reg:
+	iavf_emu_uninit_device(iavf);
 err_ndev:
 	rte_emudev_release(edev);
 err:
diff --git a/drivers/emu/iavf/iavf_vfio_user.c b/drivers/emu/iavf/iavf_vfio_user.c
new file mode 100644
index 0000000000..0cc4c433c2
--- /dev/null
+++ b/drivers/emu/iavf/iavf_vfio_user.c
@@ -0,0 +1,399 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <pthread.h>
+
+#include <rte_malloc.h>
+
+#include "iavf_vfio_user.h"
+#include <iavf_type.h>
+
+struct iavf_emu_sock_list {
+	TAILQ_ENTRY(iavf_emu_sock_list) next;
+	struct rte_emudev *emu_dev;
+};
+
+TAILQ_HEAD(iavf_emu_sock_list_head, iavf_emu_sock_list);
+
+static struct iavf_emu_sock_list_head sock_list =
+	TAILQ_HEAD_INITIALIZER(sock_list);
+
+static pthread_mutex_t sock_list_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static int
+iavf_emu_setup_irq(struct iavf_emudev *dev)
+{
+	struct iavf_emu_intr *intr;
+	struct rte_vfio_user_irq_info *irq;
+	int *fds = NULL;
+	uint32_t i, count;
+
+	irq = dev->vfio->irq;
+	if (!irq)
+		return -1;
+
+	count = irq->irq_info[VFIO_PCI_MSIX_IRQ_INDEX].count;
+	if (count) {
+		fds = rte_zmalloc("irq_fds", sizeof(int) * count, 0);
+		if (!fds) {
+			EMU_IAVF_LOG(ERR,
+				"Failed to alloc irq fds.\n");
+			return -1;
+		}
+	}
+
+	if (rte_vfio_user_get_irq(dev->vfio->dev_id,
+			VFIO_PCI_MSIX_IRQ_INDEX, count, fds)) {
+		EMU_IAVF_LOG(ERR, "Failed to get irqfds from vfio-user.\n");
+		return -1;
+	}
+
+	intr = dev->intr;
+	intr->intr_num = irq->irq_info[VFIO_PCI_MSIX_IRQ_INDEX].count;
+
+	for (i = 0; i < count; i++) {
+		intr->info[i].fd = fds[i];
+		intr->info[i].enable = 0;
+	}
+
+	rte_free(fds);
+
+	return 0;
+}
+
+static inline void
+iavf_emu_reset_irq(struct iavf_emudev *dev)
+{
+	struct iavf_emu_intr *intr = dev->intr;
+	uint32_t i;
+
+	for (i = 0; i < intr->intr_num; i++) {
+		intr->info[i].enable = 0;
+		intr->info[i].fd = -1;
+	}
+}
+
+static inline void
+iavf_emu_reset_regions(struct iavf_emudev *dev)
+{
+	struct rte_vfio_user_regions *reg = dev->vfio->reg;
+	struct rte_vfio_user_reg_info *vinfo;
+	uint32_t i;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		vinfo = &reg->reg_info[i];
+		if (vinfo->info->size && vinfo->base)
+			memset(vinfo->base, 0, vinfo->info->size);
+	}
+}
+
+static int
+iavf_emu_setup_mem_table(struct iavf_emudev *dev)
+{
+	const struct rte_vfio_user_mem *vfio_mem;
+	const struct rte_vfio_user_mtb_entry *entry;
+	struct rte_iavf_emu_mem *mem;
+	uint32_t i;
+
+	vfio_mem = rte_vfio_user_get_mem_table(dev->vfio->dev_id);
+	if (!vfio_mem) {
+		EMU_IAVF_LOG(ERR, "Unable to get vfio mem table.\n");
+		return -1;
+	}
+
+	mem = dev->mem;
+
+	mem->region_num = vfio_mem->entry_num;
+	if (mem->region_num > RTE_IAVF_EMU_MAX_MEM_REGIONS) {
+		EMU_IAVF_LOG(ERR, "Failed to set up mem table,"
+			"exceed max region num.\n");
+		return -1;
+	}
+
+	for (i = 0; i < vfio_mem->entry_num; i++) {
+		entry = &vfio_mem->entry[i];
+
+		mem->regions[i].guest_phys_addr = entry->gpa;
+		mem->regions[i].host_user_addr = entry->host_user_addr;
+		mem->regions[i].mmap_addr = entry->mmap_addr;
+		mem->regions[i].mmap_size = entry->mmap_size;
+		mem->regions[i].size = entry->size;
+		mem->regions[i].fd = entry->fd;
+	}
+
+	return 0;
+}
+
+static inline void
+iavf_emu_reset_mem_table(struct iavf_emudev *dev)
+{
+	struct rte_iavf_emu_mem *mem = dev->mem;
+	uint32_t i;
+
+	for (i = 0; i < mem->region_num; i++) {
+		mem->regions[i].guest_phys_addr = 0;
+		mem->regions[i].host_user_addr = 0;
+		mem->regions[i].mmap_addr = 0;
+		mem->regions[i].mmap_size = 0;
+		mem->regions[i].size = 0;
+		mem->regions[i].fd = -1;
+	}
+}
+
+static int
+iavf_emu_setup_queues(struct iavf_emudev *dev)
+{
+	struct iavf_emu_adminQ *asq, *arq;
+	struct rte_vfio_user_reg_info *info;
+	uint16_t i;
+
+	info = &dev->vfio->reg->reg_info[0];
+	asq = &dev->adq[RTE_IAVF_EMU_ADMINQ_TXQ];
+	arq = &dev->adq[RTE_IAVF_EMU_ADMINQ_RXQ];
+
+	asq->doorbell = (uint8_t *)info->base + IAVF_VF_ATQT1;
+	asq->db_size = 4;
+	asq->ring_addr_lo = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ATQBAL1);
+	asq->ring_addr_hi = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ATQBAH1);
+	asq->ring_sz = (uint32_t *)((uint8_t *)info->base + IAVF_VF_ATQLEN1);
+
+	arq->doorbell = (uint8_t *)info->base + IAVF_VF_ARQT1;
+	arq->db_size = 4;
+	arq->ring_addr_lo = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ARQBAL1);
+	arq->ring_addr_hi = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ARQBAH1);
+	arq->ring_sz = (uint32_t *)((uint8_t *)info->base + IAVF_VF_ARQLEN1);
+
+	for (i = 0; i < dev->max_lanqp; i++) {
+		dev->lanq[i * 2].doorbell = (uint8_t *)info->base +
+				IAVF_QTX_TAIL1(i);
+		dev->lanq[i * 2].db_size = 4;
+		dev->lanq[i * 2 + 1].doorbell = (uint8_t *)info->base +
+				IAVF_QRX_TAIL1(i);
+		dev->lanq[i * 2 + 1].db_size = 4;
+	}
+
+	return 0;
+}
+
+static inline void
+iavf_emu_reset_queues(struct iavf_emudev *dev)
+{
+	memset(&dev->adq, 0, RTE_IAVF_EMU_ADMINQ_NUM *
+		sizeof(struct iavf_emu_adminQ));
+
+	memset(dev->lanq, 0, dev->max_lanqp * 2 *
+		sizeof(struct iavf_emu_lanQ));
+}
+
+static void
+iavf_emu_reset_all_resources(struct iavf_emudev *dev)
+{
+	iavf_emu_reset_mem_table(dev);
+	iavf_emu_reset_irq(dev);
+	iavf_emu_reset_queues(dev);
+	iavf_emu_reset_regions(dev);
+}
+
+static inline struct iavf_emu_sock_list *
+iavf_emu_find_sock_list(char *sock_addr)
+{
+	struct iavf_emu_sock_list *list;
+	struct iavf_emudev *dev;
+	int list_exist = 0;
+
+	if (!sock_addr)
+		return NULL;
+
+	pthread_mutex_lock(&sock_list_lock);
+
+	TAILQ_FOREACH(list, &sock_list, next) {
+		dev = (struct iavf_emudev *)list->emu_dev->priv_data;
+
+		if (!strcmp(dev->sock_addr, sock_addr)) {
+			list_exist = 1;
+			break;
+		}
+	}
+
+	pthread_mutex_unlock(&sock_list_lock);
+
+	if (!list_exist)
+		return NULL;
+
+	return list;
+}
+
+static struct iavf_emudev *
+find_iavf_with_dev_id(int vfio_dev_id)
+{
+	struct iavf_emu_sock_list *list;
+	char sock_addr[PATH_MAX];
+	int ret;
+
+	ret = rte_vfio_get_sock_addr(vfio_dev_id, sock_addr,
+		sizeof(sock_addr));
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Can not find vfio device %d "
+			"sock_addr.\n", vfio_dev_id);
+		return NULL;
+	}
+
+	list = iavf_emu_find_sock_list(sock_addr);
+	if (!list) {
+		EMU_IAVF_LOG(ERR, "Can not find sock list.\n");
+		return NULL;
+	}
+
+	return (struct iavf_emudev *)list->emu_dev->priv_data;
+}
+
+static int
+iavf_emu_new_device(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+	int ret;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	dev->vfio->dev_id = vfio_dev_id;
+
+	ret = iavf_emu_setup_mem_table(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up memtable for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = iavf_emu_setup_irq(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up irq for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = iavf_emu_setup_queues(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up queues for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = dev->ops->device_ready(dev->edev);
+	if (ret)
+		return ret;
+
+	dev->ready = 1;
+	return 0;
+}
+
+static void
+iavf_emu_destroy_device(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return;
+
+	iavf_emu_reset_all_resources(dev);
+
+	dev->ops->device_destroy(dev->edev);
+}
+
+static int
+iavf_emu_update_status(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+	int ret;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	ret = iavf_emu_setup_mem_table(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up memtable for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = iavf_emu_setup_irq(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up irq for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	dev->ops->update_status(dev->edev);
+
+	return 0;
+}
+
+static int
+iavf_emu_lock_datapath(int vfio_dev_id, int lock)
+{
+	struct iavf_emudev *dev;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	return dev->ops->lock_dp(dev->edev, lock);
+}
+
+static int
+iavf_emu_reset_device(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	iavf_emu_reset_all_resources(dev);
+
+	return dev->ops->reset_device(dev->edev);
+}
+
+struct rte_vfio_user_notify_ops vfio_ops = {
+	.new_device = iavf_emu_new_device,
+	.destroy_device = iavf_emu_destroy_device,
+	.update_status = iavf_emu_update_status,
+	.lock_dp = iavf_emu_lock_datapath,
+	.reset_device = iavf_emu_reset_device,
+};
+
+int
+iavf_emu_register_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+
+	ret = rte_vfio_user_register(dev->sock_addr, &vfio_ops);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Register vfio_user failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+iavf_emu_unregister_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+
+	ret = rte_vfio_user_unregister(dev->sock_addr);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Unregister vfio_user failed\n");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/drivers/emu/iavf/iavf_vfio_user.h b/drivers/emu/iavf/iavf_vfio_user.h
new file mode 100644
index 0000000000..aa2f3edc87
--- /dev/null
+++ b/drivers/emu/iavf/iavf_vfio_user.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IAVF_VFIO_USER_H
+#define _IAVF_VFIO_USER_H
+
+#include <rte_vfio_user.h>
+
+#include "iavf_emu_internal.h"
+
+int iavf_emu_register_vfio_user(struct iavf_emudev *dev);
+
+int iavf_emu_unregister_vfio_user(struct iavf_emudev *dev);
+
+#endif
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
index 58c2a90383..4f651258c2 100644
--- a/drivers/emu/iavf/meson.build
+++ b/drivers/emu/iavf/meson.build
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2020 Intel Corporation
 
-sources = files('iavf_emu.c', 'iavf_emudev.c')
+sources = files('iavf_emu.c', 'iavf_vfio_user.c',
+	'iavf_emudev.c')
 
-deps += ['bus_vdev', 'emudev']
+deps += ['bus_vdev', 'emudev', 'vfio_user', 'common_iavf']
 
 headers = files('rte_iavf_emu.h')
diff --git a/drivers/emu/iavf/rte_iavf_emu.h b/drivers/emu/iavf/rte_iavf_emu.h
index 623c3c5d99..6de0989f0b 100644
--- a/drivers/emu/iavf/rte_iavf_emu.h
+++ b/drivers/emu/iavf/rte_iavf_emu.h
@@ -40,4 +40,21 @@ struct rte_iavf_emu_mem {
 	struct rte_iavf_emu_mem_reg regions[RTE_IAVF_EMU_MAX_MEM_REGIONS];
 };
 
+struct rte_iavf_emu_notify_ops {
+	/* Device is ready */
+	int (*device_ready)(struct rte_emudev *dev);
+	/* Device is destroyed */
+	void (*device_destroy)(struct rte_emudev *dev);
+	/* Update device status */
+	int (*update_status)(struct rte_emudev *dev);
+	/* Start device */
+	int (*device_start)(struct rte_emudev *dev);
+	/* Stop device */
+	int (*device_stop)(struct rte_emudev *dev);
+	/* Lock or unlock data path */
+	int (*lock_dp)(struct rte_emudev *dev, int lock);
+	/* Reset device */
+	int (*reset_device)(struct rte_emudev *dev);
+};
+
 #endif
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v3 5/8] emu/iavf: add resource management and internal logic of iavf
  2021-01-14  6:25   ` [dpdk-dev] [PATCH v3 " Chenbo Xia
                       ` (3 preceding siblings ...)
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 4/8] emu/iavf: add vfio-user device register and unregister Chenbo Xia
@ 2021-01-14  6:25     ` Chenbo Xia
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 6/8] emu/iavf: add emudev operations to fit in emudev framework Chenbo Xia
                       ` (2 subsequent siblings)
  7 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2021-01-14  6:25 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu, beilei.xing

This patch adds the allocation and release of device resources.
Device resources include PCI BARs' memory and interrupt related
resources. Device internal logic is also added.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
---
 drivers/emu/iavf/iavf_emu.c       |   1 +
 drivers/emu/iavf/iavf_emudev.c    |  20 +
 drivers/emu/iavf/iavf_vfio_user.c | 683 +++++++++++++++++++++++++++++-
 drivers/emu/iavf/iavf_vfio_user.h |  41 ++
 drivers/emu/iavf/meson.build      |   8 +
 5 files changed, 750 insertions(+), 3 deletions(-)

diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index 2f1513137c..7506849e42 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -19,6 +19,7 @@ iavf_emu_dev_close(struct rte_emudev *dev)
 	}
 
 	iavf = (struct iavf_emudev *)dev->priv_data;
+	iavf_emu_uninit_vfio_user(iavf);
 	iavf_emu_unregister_vfio_user(iavf);
 	iavf_emu_uninit_device(iavf);
 	dev->priv_data = NULL;
diff --git a/drivers/emu/iavf/iavf_emudev.c b/drivers/emu/iavf/iavf_emudev.c
index 6ba1cc2a89..74f4829e7f 100644
--- a/drivers/emu/iavf/iavf_emudev.c
+++ b/drivers/emu/iavf/iavf_emudev.c
@@ -180,10 +180,30 @@ rte_emu_iavf_probe(struct rte_vdev_device *dev)
 		goto err_reg;
 	}
 
+	ret = iavf_emu_init_vfio_user(iavf);
+	if (ret) {
+		EMU_IAVF_LOG(ERR,
+			"Emulated iavf failed to init vfio user.\n");
+		ret = -1;
+		goto err_init;
+	}
+
+	ret = iavf_emu_start_vfio_user(iavf);
+	if (ret) {
+		EMU_IAVF_LOG(ERR,
+			"Emulated iavf failed to start vfio user.\n");
+		ret = -1;
+		goto err_start;
+	}
+
 	edev->started = 1;
 	rte_kvargs_free(kvlist);
 	return 0;
 
+err_start:
+	iavf_emu_uninit_vfio_user(iavf);
+err_init:
+	iavf_emu_unregister_vfio_user(iavf);
 err_reg:
 	iavf_emu_uninit_device(iavf);
 err_ndev:
diff --git a/drivers/emu/iavf/iavf_vfio_user.c b/drivers/emu/iavf/iavf_vfio_user.c
index 0cc4c433c2..5c690978bd 100644
--- a/drivers/emu/iavf/iavf_vfio_user.c
+++ b/drivers/emu/iavf/iavf_vfio_user.c
@@ -2,13 +2,36 @@
  * Copyright(c) 2020 Intel Corporation
  */
 
+#include <linux/pci.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
 #include <pthread.h>
+#include <sys/types.h>
 
 #include <rte_malloc.h>
+#include <rte_emudev.h>
+#include <rte_memcpy.h>
 
 #include "iavf_vfio_user.h"
 #include <iavf_type.h>
 
+#define STORE_LE16(addr, val)   (*(__u16 *)addr = val)
+#define STORE_LE32(addr, val)   (*(__u32 *)addr = val)
+
+#define IAVF_EMU_BAR0_SIZE 0x10000
+#define IAVF_EMU_BAR3_SIZE 0x1000
+#define IAVF_EMU_BAR_SIZE_MASK 0xffffffff
+#define IAVF_EMU_BAR_MASK(sz) (~(sz) + 1)
+#define IAVF_EMU_MSIX_TABLE_SIZE 0x20
+
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_SUBDEVICE_ID 0x1100
+#define PCI_CLASS_ETHERNET 0x0200
+
 struct iavf_emu_sock_list {
 	TAILQ_ENTRY(iavf_emu_sock_list) next;
 	struct rte_emudev *emu_dev;
@@ -52,10 +75,8 @@ iavf_emu_setup_irq(struct iavf_emudev *dev)
 	intr = dev->intr;
 	intr->intr_num = irq->irq_info[VFIO_PCI_MSIX_IRQ_INDEX].count;
 
-	for (i = 0; i < count; i++) {
+	for (i = 0; i < count; i++)
 		intr->info[i].fd = fds[i];
-		intr->info[i].enable = 0;
-	}
 
 	rte_free(fds);
 
@@ -199,6 +220,591 @@ iavf_emu_reset_all_resources(struct iavf_emudev *dev)
 	iavf_emu_reset_regions(dev);
 }
 
+static int
+iavf_emu_init_dev(struct iavf_emudev *dev)
+{
+	struct iavf_emu_vfio_user *vfio;
+	struct vfio_device_info *dev_info;
+	struct rte_vfio_user_regions *reg;
+	struct rte_vfio_user_irq_info *irq;
+	struct vfio_region_info_cap_sparse_mmap *sparse;
+	int ret;
+	uint32_t i, j;
+
+	vfio = rte_zmalloc_socket("vfio", sizeof(*vfio), 0, dev->numa_node);
+	if (!vfio) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc iavf_emu_vfio_user\n");
+		ret = -1;
+		goto exit;
+	}
+
+	dev_info = rte_zmalloc_socket("vfio_dev_info",
+		sizeof(*dev_info), 0, dev->numa_node);
+	if (!dev_info) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc vfio dev_info\n");
+		ret = -1;
+		goto err_info;
+	}
+	dev_info->argsz = sizeof(*dev_info);
+	dev_info->flags = VFIO_DEVICE_FLAGS_PCI | VFIO_DEVICE_FLAGS_RESET;
+	dev_info->num_regions = VFIO_PCI_NUM_REGIONS;
+	dev_info->num_irqs = VFIO_PCI_NUM_IRQS;
+
+	reg = rte_zmalloc_socket("vfio_user_regions",
+		sizeof(*reg) + dev_info->num_regions *
+		sizeof(struct rte_vfio_user_reg_info), 0, dev->numa_node);
+	if (!reg) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc vfio_user_regions\n");
+		ret = -1;
+		goto err_reg;
+	}
+	reg->reg_num = dev_info->num_regions;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		struct rte_vfio_user_reg_info *vinfo = &reg->reg_info[i];
+		size_t sz = sizeof(struct vfio_region_info);
+
+		/* BAR0 has two sparse mmap area */
+		if (i == VFIO_PCI_BAR0_REGION_INDEX)
+			sz += sizeof(*sparse) + 2 * sizeof(*sparse->areas);
+
+		vinfo->info = rte_zmalloc_socket("vfio_region_info",
+			sz, 0, dev->numa_node);
+		if (!vinfo->info) {
+			EMU_IAVF_LOG(ERR, "Failed to alloc region info "
+				"for region %d\n", i);
+			ret = -1;
+			goto err_reg_alloc;
+		}
+
+		vinfo->info->index = i;
+
+		switch (i) {
+		case VFIO_PCI_CONFIG_REGION_INDEX:
+			vinfo->info->argsz = sz;
+			vinfo->info->offset = 0;
+			vinfo->info->size = IAVF_EMU_CFG_SPACE_SIZE;
+			vinfo->info->flags = VFIO_REGION_INFO_FLAG_READ |
+				       VFIO_REGION_INFO_FLAG_WRITE;
+			break;
+		case VFIO_PCI_BAR0_REGION_INDEX:
+			vinfo->info->argsz = sz;
+			vinfo->info->offset = 0;
+			vinfo->info->size = IAVF_EMU_BAR0_SIZE;
+			vinfo->info->flags  = VFIO_REGION_INFO_FLAG_READ |
+				       VFIO_REGION_INFO_FLAG_WRITE |
+				       VFIO_REGION_INFO_FLAG_MMAP |
+				       VFIO_REGION_INFO_FLAG_CAPS;
+			vinfo->info->cap_offset =
+						sizeof(struct vfio_region_info);
+
+			sparse = (struct vfio_region_info_cap_sparse_mmap *)
+						((uint8_t *)vinfo->info +
+						vinfo->info->cap_offset);
+			sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
+			sparse->header.version = 1;
+			sparse->nr_areas = 2;
+			sparse->areas[0].offset = 0;
+			sparse->areas[0].size = 0x3000;
+			sparse->areas[1].offset = 0x6000;
+			sparse->areas[1].size = IAVF_EMU_BAR0_SIZE - 0x6000;
+
+			break;
+		case VFIO_PCI_BAR3_REGION_INDEX:
+			vinfo->info->argsz = sz;
+			vinfo->info->offset = 0;
+			vinfo->info->size = IAVF_EMU_BAR3_SIZE;
+			vinfo->info->flags  = VFIO_REGION_INFO_FLAG_READ |
+				       VFIO_REGION_INFO_FLAG_WRITE |
+				       VFIO_REGION_INFO_FLAG_MMAP;
+			break;
+		default:
+			vinfo->info->argsz = sz;
+			vinfo->info->offset = 0;
+			vinfo->info->size = 0;
+			vinfo->info->flags = 0;
+		}
+	}
+
+	irq = rte_zmalloc_socket("vfio_user_irq_info", sizeof(*irq) +
+		VFIO_PCI_NUM_IRQS * sizeof(struct vfio_irq_info),
+		0, dev->numa_node);
+	if (!irq) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc vfio_user_irqs\n");
+		ret = -1;
+		goto err_irq;
+	}
+	irq->irq_num = VFIO_PCI_NUM_IRQS;
+
+	for (i = 0; i < VFIO_PCI_NUM_IRQS; i++) {
+		irq->irq_info[i].index = i;
+		irq->irq_info[i].flags =
+			VFIO_IRQ_INFO_EVENTFD | VFIO_IRQ_INFO_NORESIZE;
+		if (i == VFIO_PCI_MSIX_IRQ_INDEX)
+			irq->irq_info[i].count =
+				IAVF_EMU_MSIX_TABLE_SIZE + 1;
+		else if (i == VFIO_PCI_ERR_IRQ_INDEX)
+			irq->irq_info[i].count = 1;
+		else
+			irq->irq_info[i].count = 0;
+	}
+
+	vfio->dev_info = dev_info;
+	vfio->reg = reg;
+	vfio->irq = irq;
+	dev->vfio = vfio;
+
+	return 0;
+
+err_irq:
+err_reg_alloc:
+	for (j = 0; j < i; j++)
+		rte_free(reg->reg_info[j].info);
+	rte_free(reg);
+err_reg:
+	rte_free(dev_info);
+err_info:
+	rte_free(vfio);
+exit:
+	return ret;
+}
+
+static int
+iavf_emu_uninit_dev(struct iavf_emudev *dev)
+{
+	struct iavf_emu_vfio_user *vfio;
+	struct rte_vfio_user_regions *reg;
+	uint32_t i;
+
+	if (!dev->vfio)
+		return -1;
+
+	vfio = dev->vfio;
+	rte_free(vfio->dev_info);
+
+	reg = vfio->reg;
+	for (i = 0; i < reg->reg_num; i++)
+		rte_free(reg->reg_info[i].info);
+	rte_free(reg);
+
+	rte_free(vfio->irq);
+	rte_free(vfio);
+
+	return 0;
+}
+
+static int
+handle_pci_cmd_write(struct iavf_emu_pci_hdr *hdr,
+	char *buf, size_t count)
+{
+	/* Below are all R/W bits in command register */
+	uint16_t rw_bitmask = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+		PCI_COMMAND_MASTER | PCI_COMMAND_PARITY |
+		PCI_COMMAND_SERR | PCI_COMMAND_INTX_DISABLE;
+	uint16_t val;
+
+	if (count != 2) {
+		EMU_IAVF_LOG(ERR, "Wrong write count (%lu) for PCI_COMMAND\n",
+			count);
+		return -1;
+	}
+
+	val = *(uint16_t *)buf;
+	/* Only write the R/W bits */
+	hdr->cmd = (rw_bitmask & val) | (~rw_bitmask & hdr->cmd);
+
+	return 2;
+}
+
+static int
+handle_pci_status_write(struct iavf_emu_pci_hdr *hdr,
+	char *buf, size_t count)
+{
+	/* Below are all write-1-to-clear bits in status register */
+	uint16_t rw1c_bitmask = PCI_STATUS_PARITY |
+		PCI_STATUS_SIG_TARGET_ABORT |
+		PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT |
+		PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY;
+	uint16_t val;
+
+	if (count != 2) {
+		EMU_IAVF_LOG(ERR, "Wrong write count (%lu) for PCI_STATUS\n",
+			count);
+		return -1;
+	}
+
+	val = *(uint16_t *)buf;
+	/* Clear the write-1-to-clear bits*/
+	hdr->status = ~(rw1c_bitmask & val) & hdr->status;
+
+	return 2;
+}
+
+static int
+handle_pci_bar_write(struct iavf_emu_pci_hdr *hdr,
+	char *buf, size_t count, loff_t pos)
+{
+	uint32_t val, size;
+	uint8_t idx;
+
+	if (count != 4) {
+		EMU_IAVF_LOG(ERR, "Wrong write count (%lu) for "
+			"Base Address Register\n",
+			count);
+		return -1;
+	}
+
+	val = *(uint32_t *)buf;
+
+	if (pos == PCI_BASE_ADDRESS_0)
+		size = IAVF_EMU_BAR0_SIZE;
+	else if (pos == PCI_BASE_ADDRESS_3)
+		size = IAVF_EMU_BAR3_SIZE;
+	else
+		size = 0;
+
+	if (val == IAVF_EMU_BAR_SIZE_MASK)
+		val &= IAVF_EMU_BAR_MASK(size);
+
+	idx = (pos - PCI_BASE_ADDRESS_0) / 0x4;
+	hdr->bar[idx] |= val & ~PCI_BASE_ADDRESS_MEM_MASK;
+
+	return 4;
+}
+
+static int
+handle_cfg_write(struct iavf_emu_pci_hdr *hdr,
+	char *buf, size_t count, loff_t pos)
+{
+	int ret = count;
+
+	switch (pos) {
+	case PCI_COMMAND:
+		ret = handle_pci_cmd_write(hdr, buf, count);
+		break;
+	case PCI_STATUS:
+		ret = handle_pci_status_write(hdr, buf, count);
+		break;
+	case PCI_INTERRUPT_LINE:
+		if (count != 1) {
+			EMU_IAVF_LOG(ERR, "Wrong write count (%lu)"
+				"for PCI_INTERRUPT_LINE\n",
+				count);
+			return -1;
+		}
+		hdr->intrl = *(uint8_t *)buf;
+		ret = 1;
+		break;
+	case PCI_BASE_ADDRESS_0:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_1:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_2:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_3:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_4:
+		/* FALLTHROUGH */
+	case PCI_BASE_ADDRESS_5:
+		ret = handle_pci_bar_write(hdr, buf, count, pos);
+		break;
+	default:
+		EMU_IAVF_LOG(INFO, "Write request for cfg (pos: %ld) ignored\n",
+			pos);
+		break;
+	}
+
+	return ret;
+}
+
+static ssize_t
+iavf_emu_cfg_rw(struct rte_vfio_user_reg_info *reg, char *buf,
+	size_t count, loff_t pos, bool iswrite)
+{
+	struct iavf_emu_cfg_space *cfg;
+	char *reg_pos;
+	int ret = 0;
+
+	if (!reg->base) {
+		EMU_IAVF_LOG(ERR, "Config space not exist\n");
+		return -EFAULT;
+	}
+
+	if (pos + count > reg->info->size) {
+		EMU_IAVF_LOG(ERR, "Access exceeds config space size\n");
+		return -EINVAL;
+	}
+
+	cfg = (struct iavf_emu_cfg_space *)reg->base;
+	reg_pos = (char *)reg->base + pos;
+
+	if (!iswrite) {
+		rte_memcpy(buf, reg_pos, count);
+		ret = count;
+	} else {
+		ret = handle_cfg_write(&cfg->hdr, buf, count, pos);
+		if (ret < 0) {
+			EMU_IAVF_LOG(ERR, "Failed to write cfg space\n");
+			return -EINVAL;
+		}
+	}
+
+	return ret;
+}
+
+static int
+iavf_emu_init_cfg_space(struct rte_vfio_user_reg_info *vinfo,
+	unsigned int numa_node)
+{
+	char *v_cfg;
+
+	vinfo->base = rte_zmalloc_socket("cfg space",
+		IAVF_EMU_CFG_SPACE_SIZE,
+		0, numa_node);
+	if (!vinfo->base) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc cfg space\n");
+		return -1;
+	}
+	vinfo->rw = iavf_emu_cfg_rw;
+	vinfo->fd = -1;
+	vinfo->priv = NULL;
+
+	v_cfg = (char *)vinfo->base;
+
+	STORE_LE16((uint16_t *)&v_cfg[PCI_VENDOR_ID],
+		PCI_VENDOR_ID_INTEL);
+	STORE_LE16((uint16_t *)&v_cfg[PCI_DEVICE_ID],
+		IAVF_DEV_ID_ADAPTIVE_VF);
+	STORE_LE16((uint16_t *)&v_cfg[PCI_SUBSYSTEM_VENDOR_ID],
+		PCI_VENDOR_ID_INTEL);
+	STORE_LE16((uint16_t *)&v_cfg[PCI_SUBSYSTEM_ID],
+		   PCI_SUBDEVICE_ID);
+
+	STORE_LE16((uint16_t *)&v_cfg[PCI_COMMAND],
+		   PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+	STORE_LE16((uint16_t *)&v_cfg[PCI_CLASS_DEVICE],
+		   PCI_CLASS_ETHERNET);
+	v_cfg[PCI_CLASS_REVISION] = 0x01;
+
+	STORE_LE16((uint16_t *)&v_cfg[PCI_STATUS],
+			   PCI_STATUS_CAP_LIST);
+
+	STORE_LE32((uint32_t *)&v_cfg[PCI_BASE_ADDRESS_0],
+		   PCI_BASE_ADDRESS_SPACE_MEMORY |
+		   PCI_BASE_ADDRESS_MEM_TYPE_32	 |
+		   PCI_BASE_ADDRESS_MEM_PREFETCH);
+
+	STORE_LE32((uint32_t *)&v_cfg[PCI_BASE_ADDRESS_3],
+		   PCI_BASE_ADDRESS_SPACE_MEMORY |
+		   PCI_BASE_ADDRESS_MEM_TYPE_32);
+
+	STORE_LE16((uint16_t *)&v_cfg[PCI_CAPABILITY_LIST],
+			   0x70);
+	STORE_LE16((uint16_t *)&v_cfg[0x70],
+			   PCI_CAP_ID_MSIX);
+
+	STORE_LE16((uint16_t *)&v_cfg[0x70 + PCI_MSIX_FLAGS],
+			(IAVF_EMU_MSIX_TABLE_SIZE & PCI_MSIX_FLAGS_QSIZE) |
+			PCI_MSIX_FLAGS_ENABLE);
+
+	STORE_LE32((uint32_t *)&v_cfg[0x70 + PCI_MSIX_TABLE],
+			   (0x3 & PCI_MSIX_TABLE_BIR));
+
+	STORE_LE32((uint32_t *)&v_cfg[0x70 + PCI_MSIX_PBA],
+			(0x3 & PCI_MSIX_PBA_BIR) |
+			(0x100 & PCI_MSIX_PBA_OFFSET));
+
+	return 0;
+}
+
+static inline void
+iavf_emu_uninit_cfg_space(struct rte_vfio_user_reg_info *vinfo)
+{
+	rte_free(vinfo->base);
+	vinfo->rw = NULL;
+	vinfo->info->size = 0;
+	vinfo->fd = -1;
+}
+
+static ssize_t
+iavf_emu_bar0_rw(struct rte_vfio_user_reg_info *reg, char *buf,
+	size_t count, loff_t pos, bool iswrite)
+{
+	struct iavf_emudev *dev = (struct iavf_emudev *)reg->priv;
+	char *reg_pos;
+
+	if (!reg->base) {
+		EMU_IAVF_LOG(ERR, "BAR 0 does not exist\n");
+		return -EFAULT;
+	}
+
+	if (pos + count > reg->info->size) {
+		EMU_IAVF_LOG(ERR, "Access exceeds BAR 0 size\n");
+		return -EINVAL;
+	}
+
+	reg_pos = (char *)reg->base + pos;
+
+	if (!iswrite) {
+		rte_memcpy(buf, reg_pos, count);
+	} else {
+		int tmp;
+		uint32_t val;
+		int idx = -1;
+
+		if (count != 4)
+			return -EINVAL;
+
+		val = *(uint32_t *)buf;
+		/* Only handle interrupt enable/disable for now */
+		if (pos == IAVF_VFINT_DYN_CTL01) {
+			tmp = val & IAVF_VFINT_DYN_CTL01_INTENA_MASK;
+			idx = 0;
+		} else if ((pos >= IAVF_VFINT_DYN_CTLN1(0)) && pos <=
+			IAVF_VFINT_DYN_CTLN1(RTE_IAVF_EMU_MAX_INTR - 1)) {
+			tmp = val & IAVF_VFINT_DYN_CTLN1_INTENA_MASK;
+			idx = pos - IAVF_VFINT_DYN_CTLN1(0);
+			if (idx % 4)
+				return -EINVAL;
+			idx = idx / 4 + 1;
+		}
+
+		if (idx != -1 &&
+			tmp != dev->intr->info[idx].enable && dev->ready) {
+			dev->intr->info[idx].enable = tmp;
+			rte_wmb();
+			dev->ops->lock_dp(dev->edev, 1);
+			dev->ops->update_status(dev->edev);
+			dev->ops->lock_dp(dev->edev, 0);
+		}
+
+		rte_memcpy(reg_pos, buf, count);
+	}
+
+	return count;
+}
+
+static int
+iavf_emu_alloc_reg(struct iavf_emudev *dev)
+{
+	struct rte_vfio_user_regions *reg = dev->vfio->reg;
+	struct rte_vfio_user_reg_info *vinfo;
+	char shm_str[64];
+	uint32_t i;
+	int ret;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		vinfo = &reg->reg_info[i];
+
+		switch (i) {
+		case VFIO_PCI_CONFIG_REGION_INDEX:
+			ret = iavf_emu_init_cfg_space(vinfo, dev->numa_node);
+			if (ret)
+				return ret;
+			break;
+		case VFIO_PCI_BAR0_REGION_INDEX:
+		case VFIO_PCI_BAR3_REGION_INDEX:
+			sprintf(shm_str, "AVF%d_BAR%d",
+				dev->edev->dev_id, i);
+			vinfo->fd = shm_open(shm_str,
+				O_RDWR|O_CREAT, 0700);
+			if (vinfo->fd == -1) {
+				EMU_IAVF_LOG(ERR,
+					"Failed to open shm for BAR %d\n", i);
+				goto exit;
+			}
+
+			if (ftruncate(vinfo->fd, vinfo->info->size) == -1) {
+				EMU_IAVF_LOG(ERR,
+					"Failed to ftruncate BAR %d\n", i);
+				ret = -1;
+				goto exit;
+			}
+
+			vinfo->base = mmap(NULL, vinfo->info->size,
+					PROT_READ | PROT_WRITE,
+					MAP_SHARED, vinfo->fd, 0);
+			memset(vinfo->base, 0, vinfo->info->size);
+			if (vinfo->base == MAP_FAILED) {
+				EMU_IAVF_LOG(ERR,
+					"Failed to mmap BAR %d\n", i);
+				ret = -1;
+				goto exit;
+			}
+			vinfo->priv = (void *)dev;
+			if (i == VFIO_PCI_BAR0_REGION_INDEX)
+				vinfo->rw = iavf_emu_bar0_rw;
+			else
+				vinfo->rw = NULL;
+			break;
+		default:
+			vinfo->base = NULL;
+			vinfo->rw = NULL;
+			vinfo->fd = -1;
+			vinfo->priv = NULL;
+			break;
+		}
+	}
+
+	return 0;
+
+exit:
+	for (;; i--) {
+		vinfo = &reg->reg_info[i];
+
+		if (i == VFIO_PCI_CONFIG_REGION_INDEX)
+			iavf_emu_uninit_cfg_space(vinfo);
+
+		if (!vinfo->info->size) {
+			if (!vinfo->base)
+				munmap(vinfo->base, vinfo->info->size);
+			if (vinfo->fd > 0) {
+				close(vinfo->fd);
+				sprintf(shm_str, "AVF%d_BAR%d",
+					dev->edev->dev_id, i);
+				shm_unlink(shm_str);
+				vinfo->fd = -1;
+			}
+		}
+
+		if (i == 0)
+			break;
+	}
+	return ret;
+}
+
+static void
+iavf_emu_free_reg(struct iavf_emudev *dev)
+{
+	struct rte_vfio_user_regions *reg = dev->vfio->reg;
+	struct rte_vfio_user_reg_info *vinfo;
+	char shm_str[64];
+	uint32_t i;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		vinfo = &reg->reg_info[i];
+
+		switch (i) {
+		case VFIO_PCI_CONFIG_REGION_INDEX:
+			iavf_emu_uninit_cfg_space(vinfo);
+			break;
+		case VFIO_PCI_BAR0_REGION_INDEX:
+			/* FALLTHROUGH */
+		case VFIO_PCI_BAR3_REGION_INDEX:
+			munmap(vinfo->base, vinfo->info->size);
+			close(vinfo->fd);
+			vinfo->fd = -1;
+			sprintf(shm_str, "AVF%d_BAR%d",
+				dev->edev->dev_id, i);
+			shm_unlink(shm_str);
+			vinfo->info->size = 0;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
 static inline struct iavf_emu_sock_list *
 iavf_emu_find_sock_list(char *sock_addr)
 {
@@ -397,3 +1003,74 @@ iavf_emu_unregister_vfio_user(struct iavf_emudev *dev)
 
 	return 0;
 }
+
+int
+iavf_emu_init_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+	struct iavf_emu_sock_list *list;
+
+	if (iavf_emu_init_dev(dev)) {
+		EMU_IAVF_LOG(ERR, "Emulated iavf dev init failed.\n");
+		ret = -1;
+		goto exit;
+	}
+
+	if (iavf_emu_alloc_reg(dev)) {
+		EMU_IAVF_LOG(ERR, "Emulated iavf alloc region failed.\n");
+		ret = -1;
+		goto err_alloc_reg;
+	}
+
+	ret = rte_vfio_user_set_dev_info(dev->sock_addr, dev->vfio->dev_info);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set vfio dev info\n");
+		goto err_set;
+	}
+
+	ret = rte_vfio_user_set_reg_info(dev->sock_addr, dev->vfio->reg);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set vfio region info\n");
+		goto err_set;
+	}
+
+	ret = rte_vfio_user_set_irq_info(dev->sock_addr, dev->vfio->irq);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set vfio irq info\n");
+		goto err_set;
+	}
+
+	list = rte_zmalloc_socket("list", sizeof(*list), 0, dev->numa_node);
+	list->emu_dev = dev->edev;
+	pthread_mutex_lock(&sock_list_lock);
+	TAILQ_INSERT_TAIL(&sock_list, list, next);
+	pthread_mutex_unlock(&sock_list_lock);
+
+	return 0;
+
+err_set:
+	iavf_emu_free_reg(dev);
+err_alloc_reg:
+	iavf_emu_uninit_dev(dev);
+exit:
+	return ret;
+}
+
+void
+iavf_emu_uninit_vfio_user(struct iavf_emudev *dev)
+{
+	iavf_emu_free_reg(dev);
+	iavf_emu_uninit_dev(dev);
+}
+
+int
+iavf_emu_start_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+
+	ret = rte_vfio_user_start(dev->sock_addr);
+	if (ret)
+		EMU_IAVF_LOG(ERR, "Start vfio user failed.\n");
+
+	return ret;
+}
diff --git a/drivers/emu/iavf/iavf_vfio_user.h b/drivers/emu/iavf/iavf_vfio_user.h
index aa2f3edc87..2ccb04eb48 100644
--- a/drivers/emu/iavf/iavf_vfio_user.h
+++ b/drivers/emu/iavf/iavf_vfio_user.h
@@ -5,12 +5,53 @@
 #ifndef _IAVF_VFIO_USER_H
 #define _IAVF_VFIO_USER_H
 
+#include <linux/pci_regs.h>
+
 #include <rte_vfio_user.h>
 
 #include "iavf_emu_internal.h"
 
+#define IAVF_EMU_CFG_SPACE_SIZE 0x100
+
+struct iavf_emu_pci_hdr {
+	uint16_t vid;		/* Vendor ID */
+	uint16_t did;		/* Device ID */
+	uint16_t cmd;		/* Command */
+	uint16_t status;	/* Status */
+	uint8_t rid;		/* Revision ID */
+	uint8_t cc_pi;		/* Program I/F in Class Code*/
+	uint8_t cc_sub;		/* Sub-Class Code */
+	uint8_t cc_base;	/* Base Class Code */
+	uint8_t cl_size;	/* Cache Line Size*/
+	uint8_t lt_timer;	/* Latency Timer */
+	uint8_t hdr_type;	/* Header Type */
+	uint8_t bist;		/* BIST */
+	uint32_t bar[6];	/* Base Address Registers */
+	uint32_t ccp;		/* Cardbus CIC Pointer */
+	uint16_t sub_vid;	/* Subsystem Vendor ID */
+	uint16_t sub_sid;	/* Subsystem ID */
+	uint32_t rom;		/* Expansion ROM Base Address */
+	uint8_t cap;		/* Capabilities Pointer */
+	uint8_t rsvd[7];	/* Reserved */
+	uint8_t intrl;		/* Interrupt Line */
+	uint8_t intrp;		/* Interrupt Pin */
+	uint8_t min_gnt;	/* Min_Gnt Register */
+	uint8_t max_lat;	/* Max_Lat Register */
+} __attribute((packed));
+
+struct iavf_emu_cfg_space {
+	struct iavf_emu_pci_hdr hdr;
+	uint8_t cfg_non_std[IAVF_EMU_CFG_SPACE_SIZE - PCI_STD_HEADER_SIZEOF];
+} __attribute((packed));
+
 int iavf_emu_register_vfio_user(struct iavf_emudev *dev);
 
 int iavf_emu_unregister_vfio_user(struct iavf_emudev *dev);
 
+int iavf_emu_init_vfio_user(struct iavf_emudev *dev);
+
+void iavf_emu_uninit_vfio_user(struct iavf_emudev *dev);
+
+int iavf_emu_start_vfio_user(struct iavf_emudev *dev);
+
 #endif
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
index 4f651258c2..3cab2226b7 100644
--- a/drivers/emu/iavf/meson.build
+++ b/drivers/emu/iavf/meson.build
@@ -1,6 +1,14 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2020 Intel Corporation
 
+librt = cc.find_library('rt', required: false)
+if not librt.found()
+	build = false
+	subdir_done()
+endif
+
+ext_deps += librt
+
 sources = files('iavf_emu.c', 'iavf_vfio_user.c',
 	'iavf_emudev.c')
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v3 6/8] emu/iavf: add emudev operations to fit in emudev framework
  2021-01-14  6:25   ` [dpdk-dev] [PATCH v3 " Chenbo Xia
                       ` (4 preceding siblings ...)
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 5/8] emu/iavf: add resource management and internal logic of iavf Chenbo Xia
@ 2021-01-14  6:25     ` Chenbo Xia
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 7/8] test/emudev: introduce functional test Chenbo Xia
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 8/8] doc: update release notes for iavf emudev driver Chenbo Xia
  7 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2021-01-14  6:25 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu, beilei.xing

This patch implements emudev operations to make emulated iavf
fit into rte_emudev framework. Lifecycle related and device
resource related operations are both implemented.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
---
 drivers/emu/iavf/iavf_emu.c     | 229 ++++++++++++++++++++++++++++++++
 drivers/emu/iavf/rte_iavf_emu.h |  59 ++++++++
 2 files changed, 288 insertions(+)

diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index 7506849e42..cc56ec1c4b 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -2,7 +2,75 @@
  * Copyright(c) 2020 Intel Corporation
  */
 
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rte_malloc.h>
+#include <rte_emudev.h>
+
 #include "iavf_vfio_user.h"
+#include <iavf_type.h>
+
+static int
+iavf_emu_dev_start(struct rte_emudev *dev)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if (iavf->ops != NULL && iavf->ops->device_start != NULL)
+		iavf->ops->device_start(dev);
+
+	return 0;
+}
+
+static void
+iavf_emu_dev_stop(struct rte_emudev *dev)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if (iavf->ops != NULL && iavf->ops->device_stop != NULL)
+		iavf->ops->device_stop(dev);
+}
+
+static int
+iavf_emu_dev_configure(struct rte_emudev *dev,
+		struct rte_emudev_info *dev_conf)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+	struct rte_iavf_emu_config *conf =
+		(struct rte_iavf_emu_config *)dev_conf->dev_priv;
+
+	if (!dev_conf->dev_priv)
+		return -EINVAL;
+
+	/* Currently emulated iavf does not support max_qp_num
+	 * and region num configuration
+	 */
+	if (dev->dev_info.max_qp_num != dev_conf->max_qp_num ||
+		dev->dev_info.region_num != dev_conf->region_num) {
+		EMU_IAVF_LOG(ERR,
+			"Configure max_qp_num/region num not supported\n");
+		return -ENOTSUP;
+	}
+
+	if (conf->qp_num >  RTE_MAX_QUEUES_PER_PORT ||
+		conf->qp_num > RTE_IAVF_EMU_MAX_QP_NUM) {
+		EMU_IAVF_LOG(ERR, "Queue pair num exceeds max\n");
+		return -EINVAL;
+	}
+
+	/* For now, we don't support device configure when data
+	 * path driver is attached
+	 */
+	if (dev->backend_priv) {
+		EMU_IAVF_LOG(ERR, "Configure failed because of "
+			"data path attached\n");
+		return -EPERM;
+	}
+
+	iavf->max_be_lanqp = conf->qp_num;
+	return 0;
+}
 
 static int
 iavf_emu_dev_close(struct rte_emudev *dev)
@@ -27,6 +95,167 @@ iavf_emu_dev_close(struct rte_emudev *dev)
 	return 0;
 }
 
+static int
+iavf_emu_get_dev_info(struct rte_emudev *dev,
+	rte_emudev_obj_t info)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+	struct rte_iavf_emu_config *conf = (struct rte_iavf_emu_config *)info;
+
+	if (!info)
+		return -EINVAL;
+
+	conf->qp_num = iavf->max_be_lanqp;
+	return 0;
+}
+
+static int
+iavf_emu_get_mem_table(struct rte_emudev *dev,
+	rte_emudev_mem_table_t *tb)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	*tb = iavf->mem;
+
+	return 0;
+}
+
+static int
+iavf_emu_get_queue_info(struct rte_emudev *dev, uint32_t queue,
+		struct rte_emudev_q_info *info)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if (queue < RTE_IAVF_EMU_ADMINQ_NUM) {
+		struct iavf_emu_adminQ *adq = &iavf->adq[queue];
+		uint64_t base, size;
+
+		if (adq->ring_addr_lo == NULL ||
+		    adq->ring_addr_hi == NULL ||
+		    adq->ring_sz == NULL)
+			return -1;
+		base = RTE_IAVF_EMU_32_TO_64(*adq->ring_addr_hi,
+			*adq->ring_addr_lo);
+		size = *adq->ring_sz;
+		info->base = base;
+		info->size = size;
+		info->doorbell_id = queue;
+		/* RX AdminQ should have IRQ vector 0 */
+		info->irq_vector = queue - 1;
+	} else {
+		info->base = 0;
+		info->size = 0;
+		info->doorbell_id = queue;
+		info->irq_vector = -1;
+	}
+
+	return 0;
+}
+
+static int
+iavf_emu_get_irq_info(struct rte_emudev *dev, uint32_t vector,
+		struct rte_emudev_irq_info *info)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+	struct iavf_emu_intr *intr = iavf->intr;
+	struct iavf_emu_intr_info *intr_info = &intr->info[vector];
+
+	if (vector >= intr->intr_num)
+		return -EINVAL;
+
+	info->eventfd = intr_info->fd;
+	info->enable = intr_info->enable;
+	info->vector = vector;
+
+	return 0;
+}
+
+static int
+iavf_emu_get_db_info(struct rte_emudev *dev, uint32_t doorbell,
+		struct rte_emudev_db_info *info)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if (doorbell < RTE_IAVF_EMU_ADMINQ_NUM) {
+		struct iavf_emu_adminQ *adq = &iavf->adq[doorbell];
+
+		info->data.mem.base = (uint64_t)adq->doorbell;
+		info->data.mem.size = adq->db_size;
+	} else {
+		struct iavf_emu_lanQ *lanq =
+			&iavf->lanq[doorbell - RTE_IAVF_EMU_ADMINQ_NUM];
+
+		info->data.mem.base = (uint64_t)lanq->doorbell;
+		info->data.mem.size = lanq->db_size;
+	}
+
+	info->flag |= RTE_EMUDEV_DB_MEM;
+	info->id = doorbell;
+
+	return 0;
+}
+
+static int
+iavf_emu_subs_ev(struct rte_emudev *dev,
+	rte_emudev_event_chnl_t ev_chnl)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	iavf->ops = (struct rte_iavf_emu_notify_ops *)ev_chnl;
+
+	return 0;
+}
+
+static int
+iavf_emu_unsubs_ev(struct rte_emudev *dev,
+		rte_emudev_event_chnl_t ev_chnl)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+
+	if ((struct rte_iavf_emu_notify_ops *)ev_chnl == iavf->ops) {
+		iavf->ops = NULL;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+iavf_emu_get_attr(struct rte_emudev *dev, const char *attr_name,
+		rte_emudev_attr_t attr)
+{
+	struct iavf_emudev *iavf = (struct iavf_emudev *)dev->priv_data;
+	struct rte_vfio_user_reg_info *info;
+	int ret = 0;
+
+	info = &iavf->vfio->reg->reg_info[0];
+
+	if (!strcmp(attr_name, RTE_IAVF_EMU_ATTR_ASQ_HEAD))
+		*(uint64_t *)attr = (uint64_t)(uintptr_t)info->base
+			+ IAVF_VF_ATQH1;
+	else if (!strcmp(attr_name, RTE_IAVF_EMU_ATTR_ARQ_HEAD))
+		*(uint64_t *)attr = (uint64_t)(uintptr_t)info->base
+			+ IAVF_VF_ARQH1;
+	else if (!strcmp(attr_name, RTE_IAVF_EMU_ATTR_RESET))
+		*(uint64_t *)attr = (uint64_t)(uintptr_t)info->base
+			+ IAVF_VFGEN_RSTAT;
+	else
+		ret = -EINVAL;
+
+	return ret;
+}
+
 struct rte_emudev_ops emu_iavf_ops = {
+	.dev_start = iavf_emu_dev_start,
+	.dev_stop = iavf_emu_dev_stop,
+	.dev_configure = iavf_emu_dev_configure,
 	.dev_close = iavf_emu_dev_close,
+	.dev_info_get = iavf_emu_get_dev_info,
+	.get_mem_table = iavf_emu_get_mem_table,
+	.get_queue_info = iavf_emu_get_queue_info,
+	.get_irq_info = iavf_emu_get_irq_info,
+	.get_db_info = iavf_emu_get_db_info,
+	.subscribe_event = iavf_emu_subs_ev,
+	.unsubscribe_event = iavf_emu_unsubs_ev,
+	.get_attr = iavf_emu_get_attr,
 };
diff --git a/drivers/emu/iavf/rte_iavf_emu.h b/drivers/emu/iavf/rte_iavf_emu.h
index 6de0989f0b..2abcec97d4 100644
--- a/drivers/emu/iavf/rte_iavf_emu.h
+++ b/drivers/emu/iavf/rte_iavf_emu.h
@@ -6,13 +6,24 @@
 #define _IAVF_EMU_H
 
 #include <stdint.h>
+#include <stddef.h>
+#include <limits.h>
+#include <net/if.h>
+#include <sys/queue.h>
 
+#include <rte_vfio_user.h>
 #include <rte_emudev.h>
 
 #define RTE_IAVF_EMUDEV_TYPE "iavf"
+#define RTE_IAVF_EMU_ATTR_ASQ_HEAD "ASQ_H"
+#define RTE_IAVF_EMU_ATTR_ARQ_HEAD "ARQ_H"
+#define RTE_IAVF_EMU_ATTR_RESET "RESET"
+#define RTE_IAVF_EMU_RESET_IN_PROGRESS 0x00
+#define RTE_IAVF_EMU_RESET_COMPLETED 0x01
 #define RTE_IAVF_EMU_MAX_MEM_REGIONS 256
 #define RTE_IAVF_EMU_MAX_QP_NUM 256
 #define RTE_IAVF_EMU_MAX_INTR 32
+#define RTE_IAVF_EMU_32_TO_64(hi, lo) ((((uint64_t)(hi)) << 32) + (lo))
 
 enum {
 	RTE_IAVF_EMU_ADMINQ_TXQ = 0,
@@ -26,6 +37,11 @@ enum {
 	RTE_IAVF_EMU_MAPPABLE_REG_NUM = 2,
 };
 
+struct rte_iavf_emu_config {
+	/* Maximum queue pair number that data path driver can use */
+	uint32_t qp_num;
+};
+
 struct rte_iavf_emu_mem_reg {
 	uint64_t guest_phys_addr;
 	uint64_t host_user_addr;
@@ -40,6 +56,49 @@ struct rte_iavf_emu_mem {
 	struct rte_iavf_emu_mem_reg regions[RTE_IAVF_EMU_MAX_MEM_REGIONS];
 };
 
+/**
+ * Helper function for data path driver to translate address
+ * of one region
+ *
+ * @param mem
+ *   A pointer to DMA memory table
+ * @param g_addr
+ *   Guest I/O virtual base address of the region
+ * @param[in/out] len
+ *   The length of region
+ * @return
+ *   - >0: Success, process virtual address returned
+ *   - 0: Failure on translation
+ */
+__rte_experimental
+__rte_always_inline uint64_t
+rte_iavf_emu_get_dma_vaddr(struct rte_iavf_emu_mem *mem,
+	uint64_t g_addr, uint64_t *len)
+{
+	struct rte_iavf_emu_mem_reg *reg;
+	uint32_t i;
+
+	for (i = 0; i < mem->region_num; i++) {
+		reg = &mem->regions[i];
+
+		if (g_addr >= reg->guest_phys_addr &&
+			g_addr < reg->guest_phys_addr + reg->size) {
+
+			if (unlikely(*len > reg->guest_phys_addr +
+				 reg->size - g_addr))
+				*len = reg->guest_phys_addr +
+					reg->size - g_addr;
+
+			return g_addr - reg->guest_phys_addr +
+				reg->host_user_addr;
+		}
+	}
+
+	*len = 0;
+
+	return 0;
+}
+
 struct rte_iavf_emu_notify_ops {
 	/* Device is ready */
 	int (*device_ready)(struct rte_emudev *dev);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v3 7/8] test/emudev: introduce functional test
  2021-01-14  6:25   ` [dpdk-dev] [PATCH v3 " Chenbo Xia
                       ` (5 preceding siblings ...)
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 6/8] emu/iavf: add emudev operations to fit in emudev framework Chenbo Xia
@ 2021-01-14  6:25     ` Chenbo Xia
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 8/8] doc: update release notes for iavf emudev driver Chenbo Xia
  7 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2021-01-14  6:25 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu, beilei.xing

This patch introduces functional test for emudev. The
implementation of iavf emudev selftest is also added.

Signed-off-by: Miao Li <miao.li@intel.com>
Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
---
 app/test/meson.build                 |   5 +-
 app/test/test_emudev.c               |  29 +++++
 drivers/emu/iavf/iavf_emu.c          |   1 +
 drivers/emu/iavf/iavf_emu_internal.h |   1 +
 drivers/emu/iavf/iavf_emu_test.c     | 174 +++++++++++++++++++++++++++
 drivers/emu/iavf/meson.build         |   2 +-
 6 files changed, 210 insertions(+), 2 deletions(-)
 create mode 100644 app/test/test_emudev.c
 create mode 100644 drivers/emu/iavf/iavf_emu_test.c

diff --git a/app/test/meson.build b/app/test/meson.build
index f5b15ac44c..b8b79bbc8b 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -139,6 +139,7 @@ test_sources = files('commands.c',
 	'test_trace_register.c',
 	'test_trace_perf.c',
 	'test_vfio_user.c',
+	'test_emudev.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
@@ -176,7 +177,8 @@ test_deps = ['acl',
 	'stack',
 	'vfio_user',
 	'telemetry',
-	'timer'
+	'timer',
+	'emudev'
 ]
 
 # Each test is marked with flag true/false
@@ -327,6 +329,7 @@ driver_test_names = [
         'eventdev_selftest_octeontx',
         'eventdev_selftest_sw',
         'rawdev_autotest',
+        'emudev_autotest',
 ]
 
 dump_test_names = [
diff --git a/app/test/test_emudev.c b/app/test/test_emudev.c
new file mode 100644
index 0000000000..0dfce162ed
--- /dev/null
+++ b/app/test/test_emudev.c
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <rte_emudev.h>
+#include <rte_bus_vdev.h>
+
+#include "test.h"
+
+static int
+test_emudev_selftest_impl(const char *pmd, const char *opts)
+{
+	int ret = 0;
+
+	if (rte_emudev_get_dev_id(pmd) == -ENODEV)
+		ret = rte_vdev_init(pmd, opts);
+	if (ret)
+		return TEST_SKIPPED;
+
+	return rte_emudev_selftest(rte_emudev_get_dev_id(pmd));
+}
+
+static int
+test_emudev_selftest(void)
+{
+	return test_emudev_selftest_impl("emu_iavf", "sock=/tmp/sock1");
+}
+
+REGISTER_TEST_COMMAND(emudev_autotest, test_emudev_selftest);
diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index cc56ec1c4b..8439677b65 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -258,4 +258,5 @@ struct rte_emudev_ops emu_iavf_ops = {
 	.subscribe_event = iavf_emu_subs_ev,
 	.unsubscribe_event = iavf_emu_unsubs_ev,
 	.get_attr = iavf_emu_get_attr,
+	.dev_selftest = iavf_emu_selftest,
 };
diff --git a/drivers/emu/iavf/iavf_emu_internal.h b/drivers/emu/iavf/iavf_emu_internal.h
index 10197c00ba..1ac7f96566 100644
--- a/drivers/emu/iavf/iavf_emu_internal.h
+++ b/drivers/emu/iavf/iavf_emu_internal.h
@@ -65,4 +65,5 @@ struct iavf_emudev {
 };
 
 void iavf_emu_uninit_device(struct iavf_emudev *dev);
+int iavf_emu_selftest(uint16_t dev_id);
 #endif
diff --git a/drivers/emu/iavf/iavf_emu_test.c b/drivers/emu/iavf/iavf_emu_test.c
new file mode 100644
index 0000000000..22450c1970
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emu_test.c
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <inttypes.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_memcpy.h>
+#include <rte_dev.h>
+#include <rte_emudev.h>
+#include <rte_bus_vdev.h>
+#include <rte_test.h>
+
+#include "iavf_emu_internal.h"
+
+#define TEST_DEV_NAME "emu_iavf"
+#define TEST_SUCCESS 0
+#define TEST_FAILED -1
+
+#define EMUDEV_TEST_RUN(setup, teardown, test) \
+	emudev_test_run(setup, teardown, test, #test)
+static uint16_t test_dev_id;
+static int total;
+static int passed;
+static int failed;
+static int unsupported;
+
+static int
+testsuite_setup(void)
+{
+	uint8_t count;
+	count = rte_emudev_count();
+	if (!count) {
+		EMU_IAVF_LOG(INFO, "No existing emu dev; "
+				  "Creating emu_iavf\n");
+		return rte_vdev_init(TEST_DEV_NAME, NULL);
+	}
+
+	return TEST_SUCCESS;
+}
+
+static void
+testsuite_teardown(void)
+{
+	rte_vdev_uninit(TEST_DEV_NAME);
+}
+
+static void
+emudev_test_run(int (*setup)(void), void (*teardown)(void),
+		int (*test)(void), const char *name)
+{
+	int ret = 0;
+
+	if (setup) {
+		ret = setup();
+		if (ret < 0) {
+			EMU_IAVF_LOG(INFO, "Error setting up test %s\n", name);
+			unsupported++;
+		}
+	}
+
+	if (test) {
+		ret = test();
+		if (ret < 0) {
+			failed++;
+			EMU_IAVF_LOG(INFO, "%s Failed\n", name);
+		} else {
+			passed++;
+			EMU_IAVF_LOG(INFO, "%s Passed\n", name);
+		}
+	}
+
+	if (teardown)
+		teardown();
+
+	total++;
+}
+
+static int
+test_emu_dev_count(void)
+{
+	uint8_t count;
+	count = rte_emudev_count();
+	RTE_TEST_ASSERT(count > 0, "Invalid emudev count %" PRIu8, count);
+	return TEST_SUCCESS;
+}
+
+static int
+test_emu_dev_get_dev_id(void)
+{
+	int ret;
+	ret = rte_emudev_get_dev_id("Invalid_emu_dev_device\n");
+	RTE_TEST_ASSERT_FAIL(ret, "Expected <0 for invalid dev name, ret=%d",
+			     ret);
+	return TEST_SUCCESS;
+}
+
+static int
+test_emu_dev_configure(void)
+{
+	int ret;
+	struct rte_emudev_info dev_conf;
+	struct rte_iavf_emu_config iavf_conf_set = {.qp_num = 1};
+	struct rte_iavf_emu_config iavf_conf_get = {0};
+
+	rte_emudev_stop(test_dev_id);
+
+	/* Check invalid configuration */
+	ret = rte_emudev_configure(test_dev_id, NULL);
+	RTE_TEST_ASSERT(ret == -EINVAL,
+			"Null configure; Expected -EINVAL, got %d", ret);
+
+	/* Valid configuration test */
+	dev_conf.dev_priv = (rte_emudev_obj_t)&iavf_conf_get;
+	ret = rte_emudev_get_dev_info(test_dev_id, &dev_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret,
+				"Failed to obtain emudev configuration (%d)",
+				ret);
+	dev_conf.dev_priv = (rte_emudev_obj_t)&iavf_conf_set;
+	ret = rte_emudev_configure(test_dev_id, &dev_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure emudev (%d)", ret);
+
+	memset(&iavf_conf_get, 0, sizeof(iavf_conf_get));
+	dev_conf.dev_priv = (rte_emudev_obj_t)&iavf_conf_get;
+	ret = rte_emudev_get_dev_info(test_dev_id, &dev_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret,
+				"Failed to obtain emudev configuration (%d)",
+				ret);
+
+	RTE_TEST_ASSERT_EQUAL(iavf_conf_set.qp_num,
+			      iavf_conf_get.qp_num,
+			      "Configuration test failed; num_queues (%d)(%d)",
+			      iavf_conf_set.qp_num,
+			      iavf_conf_get.qp_num);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_emu_dev_start_stop(void)
+{
+	int ret;
+	ret = rte_emudev_start(test_dev_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to start emudev (%d)", ret);
+
+	rte_emudev_stop(test_dev_id);
+
+	return TEST_SUCCESS;
+}
+
+int
+iavf_emu_selftest(uint16_t dev_id)
+{
+	test_dev_id = dev_id;
+	testsuite_setup();
+
+	EMUDEV_TEST_RUN(NULL, NULL, test_emu_dev_count);
+	EMUDEV_TEST_RUN(NULL, NULL, test_emu_dev_get_dev_id);
+	EMUDEV_TEST_RUN(NULL, NULL, test_emu_dev_configure);
+	EMUDEV_TEST_RUN(NULL, NULL, test_emu_dev_start_stop);
+
+	testsuite_teardown();
+
+	EMU_IAVF_LOG(INFO, "Total tests   : %d\n", total);
+	EMU_IAVF_LOG(INFO, "Passed        : %d\n", passed);
+	EMU_IAVF_LOG(INFO, "Failed        : %d\n", failed);
+	EMU_IAVF_LOG(INFO, "Not supported : %d\n", unsupported);
+
+	if (failed)
+		return -1;
+
+	return 0;
+}
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
index 3cab2226b7..613783e407 100644
--- a/drivers/emu/iavf/meson.build
+++ b/drivers/emu/iavf/meson.build
@@ -10,7 +10,7 @@ endif
 ext_deps += librt
 
 sources = files('iavf_emu.c', 'iavf_vfio_user.c',
-	'iavf_emudev.c')
+	'iavf_emu_test.c', 'iavf_emudev.c')
 
 deps += ['bus_vdev', 'emudev', 'vfio_user', 'common_iavf']
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [dpdk-dev] [PATCH v3 8/8] doc: update release notes for iavf emudev driver
  2021-01-14  6:25   ` [dpdk-dev] [PATCH v3 " Chenbo Xia
                       ` (6 preceding siblings ...)
  2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 7/8] test/emudev: introduce functional test Chenbo Xia
@ 2021-01-14  6:25     ` Chenbo Xia
  7 siblings, 0 replies; 44+ messages in thread
From: Chenbo Xia @ 2021-01-14  6:25 UTC (permalink / raw)
  To: dev, thomas, david.marchand
  Cc: stephen, cunming.liang, xiuchun.lu, miao.li, jingjing.wu, beilei.xing

Update release notes for emulated iavf driver.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
---
 doc/guides/rel_notes/release_21_02.rst | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/doc/guides/rel_notes/release_21_02.rst b/doc/guides/rel_notes/release_21_02.rst
index 9686930de3..90030079a5 100644
--- a/doc/guides/rel_notes/release_21_02.rst
+++ b/doc/guides/rel_notes/release_21_02.rst
@@ -67,7 +67,7 @@ New Features
 
   See :doc:`../prog_guide/vfio_user_lib` for more information.
 
-* **Added emudev Library.**
+* **Added emudev Library and first emudev driver.**
 
   Added an experimental library ``librte_emudev`` to provide device abstraction
   for an emulated device.
@@ -77,6 +77,10 @@ New Features
   crypto and etc.). It can be attached to another data path driver (e.g, ethdev
   driver) to leverage the high performance of DPDK data path driver.
 
+  The first emudev driver is also introduced, which emulates software iavf
+  devices. This driver emulates all iavf device behavior except data path
+  handling.
+
   See :doc:`../prog_guide/emudev` for more information.
 
 Removed Items
-- 
2.17.1


^ permalink raw reply	[flat|nested] 44+ messages in thread

end of thread, other threads:[~2021-01-14  6:31 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-18  7:47 [dpdk-dev] [PATCH 0/8] Introduce emudev library and iavf emudev driver Chenbo Xia
2020-12-18  7:47 ` [dpdk-dev] [PATCH 1/8] lib: introduce emudev library Chenbo Xia
2020-12-18  7:47 ` [dpdk-dev] [PATCH 2/8] doc: add emudev library guide Chenbo Xia
2020-12-18  7:47 ` [dpdk-dev] [PATCH 3/8] emu: introduce emulated iavf driver Chenbo Xia
2020-12-18  7:47 ` [dpdk-dev] [PATCH 4/8] emu/iavf: add vfio-user device register and unregister Chenbo Xia
2021-01-07  7:18   ` Xing, Beilei
2021-01-07  8:41     ` Xia, Chenbo
2020-12-18  7:47 ` [dpdk-dev] [PATCH 5/8] emu/iavf: add resource management and internal logic of iavf Chenbo Xia
2020-12-18  7:47 ` [dpdk-dev] [PATCH 6/8] emu/iavf: add emudev operations to fit in emudev framework Chenbo Xia
2020-12-18  7:47 ` [dpdk-dev] [PATCH 7/8] test/emudev: introduce functional test Chenbo Xia
2020-12-18  7:47 ` [dpdk-dev] [PATCH 8/8] doc: update release notes for iavf emudev driver Chenbo Xia
2020-12-18  9:53 ` [dpdk-dev] [PATCH 0/8] Introduce emudev library and " David Marchand
2020-12-19  6:11   ` Xia, Chenbo
2020-12-21  9:52     ` Maxime Coquelin
2020-12-21 12:01       ` Maxime Coquelin
2020-12-22  3:09         ` Xia, Chenbo
2020-12-22  8:48           ` Maxime Coquelin
2020-12-23  5:28             ` Xia, Chenbo
2020-12-19  6:27 ` [dpdk-dev] [PATCH v2 " Chenbo Xia
2020-12-19  6:27   ` [dpdk-dev] [PATCH v2 1/8] lib: introduce emudev library Chenbo Xia
2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 2/8] doc: add emudev library guide Chenbo Xia
2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 3/8] emu: introduce emulated iavf driver Chenbo Xia
2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 4/8] emu/iavf: add vfio-user device register and unregister Chenbo Xia
2021-01-04  6:45     ` Wu, Jingjing
2021-01-05  1:26       ` Xia, Chenbo
2021-01-05 13:41     ` Wu, Jingjing
2021-01-06  7:41       ` Xia, Chenbo
2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 5/8] emu/iavf: add resource management and internal logic of iavf Chenbo Xia
2020-12-29  6:05     ` Wu, Jingjing
2020-12-30  1:59       ` Xia, Chenbo
2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 6/8] emu/iavf: add emudev operations to fit in emudev framework Chenbo Xia
2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 7/8] test/emudev: introduce functional test Chenbo Xia
2020-12-19  6:28   ` [dpdk-dev] [PATCH v2 8/8] doc: update release notes for iavf emudev driver Chenbo Xia
2021-01-13 16:52   ` [dpdk-dev] [PATCH v2 0/8] Introduce emudev library and " Thomas Monjalon
2021-01-14  1:35     ` Xia, Chenbo
2021-01-14  6:25   ` [dpdk-dev] [PATCH v3 " Chenbo Xia
2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 1/8] lib: introduce emudev library Chenbo Xia
2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 2/8] doc: add emudev library guide Chenbo Xia
2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 3/8] emu: introduce emulated iavf driver Chenbo Xia
2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 4/8] emu/iavf: add vfio-user device register and unregister Chenbo Xia
2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 5/8] emu/iavf: add resource management and internal logic of iavf Chenbo Xia
2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 6/8] emu/iavf: add emudev operations to fit in emudev framework Chenbo Xia
2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 7/8] test/emudev: introduce functional test Chenbo Xia
2021-01-14  6:25     ` [dpdk-dev] [PATCH v3 8/8] doc: update release notes for iavf emudev driver Chenbo Xia

DPDK patches and discussions

This inbox may be cloned and mirrored by anyone:

	git clone --mirror http://inbox.dpdk.org/dev/0 dev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 dev dev/ http://inbox.dpdk.org/dev \
		dev@dpdk.org
	public-inbox-index dev

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://inbox.dpdk.org/inbox.dpdk.dev


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git