[v3,1/8] lib: introduce emudev library

Message ID 20210114062512.45462-2-chenbo.xia@intel.com (mailing list archive)
State Changes Requested, archived
Delegated to: Thomas Monjalon
Headers
Series Introduce emudev library and iavf emudev driver |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Chenbo Xia Jan. 14, 2021, 6:25 a.m. UTC
  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
  

Patch

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