[dpdk-dev,RFC,2/2] virtio: Extend virtio-net PMD to support container environment

Message ID 1447930650-26023-3-git-send-email-mukawa@igel.co.jp (mailing list archive)
State RFC, archived
Headers

Commit Message

Tetsuya Mukawa Nov. 19, 2015, 10:57 a.m. UTC
  The patch extends virtio-net PMD to work on host. Actually, we don't have
virtio-net devices on host. Thus, this PMD called "cvio PMD" is for
virtual device.

To prepare virtio-net device on host, the users need to invoke QEMU process
in special qtest mode. In this mode, no guest runs. Also, this mode is mainly
used for testing QEMU devices from outer process.
Here is example of command line.

 $ qemu-system-x86_64 -machine accel=qtest -display none \
         -qtest unix:/tmp/qtest.sock,server \
         -netdev type=tap,script=/etc/qemu-ifup,id=net0 \
         -device virtio-net-pci,netdev=net0 \
         -chardev socket,id=chr0,path=/tmp/ivshmem.sock,server \
         -device ivshmem,size=1G,chardev=chr0,vectors=1

After invoking QEMU, cvio PMD can connect to QEMU process using unix
domain sockets. Over these sockets, virtio-net device and ivshmem device
in QEMU are probed by cvio PMD. Here is example of command line.

 $ testpmd -c f -n 1 -m 1024 --shm \
         --vdev="eth_cvio0,qtest=/tmp/qtest.sock,ivshmem=/tmp/ivshmem.sock" \
         -- --disable-hw-vlan --txqflags=0xf00 -i

Please specify same unix domain sockets and memory size in both command
lines like above.

Also, "--shm" option is needed for cvio PMD like above. This option creates
one hugepage file on hugetlbfs. It means we need enough contiguous memory.
If there is no enough contiguous memory, initialization will be failed.

This contiguous memory is used for sharing memory between DPDK application
and ivshmem device in QEMU.

Signed-off-by: Tetsuya Mukawa <mukawa@igel.co.jp>
---
 config/common_linuxapp             |   5 +
 drivers/net/virtio/Makefile        |   4 +
 drivers/net/virtio/qtest.c         | 590 +++++++++++++++++++++++++++++++++++++
 drivers/net/virtio/virtio_ethdev.c | 214 ++++++++++++--
 drivers/net/virtio/virtio_ethdev.h |  16 +
 drivers/net/virtio/virtio_pci.h    |  25 ++
 6 files changed, 833 insertions(+), 21 deletions(-)
 create mode 100644 drivers/net/virtio/qtest.c
  

Patch

diff --git a/config/common_linuxapp b/config/common_linuxapp
index 7248262..11e8fd1 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -478,3 +478,8 @@  CONFIG_RTE_APP_TEST=y
 CONFIG_RTE_TEST_PMD=y
 CONFIG_RTE_TEST_PMD_RECORD_CORE_CYCLES=n
 CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n
+
+#
+# Enable virtio support for container
+#
+CONFIG_RTE_VIRTIO_VDEV=n
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 43835ba..5d6f69c 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -52,6 +52,10 @@  SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_rxtx.c
 SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_ethdev.c
 SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_rxtx_simple.c
 
+ifeq ($(CONFIG_RTE_VIRTIO_VDEV),y)
+	SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += qtest.c
+endif
+
 # this lib depends upon:
 DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_eal lib/librte_ether
 DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_mempool lib/librte_mbuf
diff --git a/drivers/net/virtio/qtest.c b/drivers/net/virtio/qtest.c
new file mode 100644
index 0000000..005fa24
--- /dev/null
+++ b/drivers/net/virtio/qtest.c
@@ -0,0 +1,590 @@ 
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 IGEL Co., Ltd. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of IGEL Co., Ltd. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/queue.h>
+#include <signal.h>
+
+#include "virtio_pci.h"
+#include "virtio_logs.h"
+#include "virtio_ethdev.h"
+
+#define IVSHMEM_PROTOCOL_VERSION	0
+
+#define NB_BUS				256
+#define NB_DEVICE			32
+#define NB_PCI_BAR			6
+
+#define REG_ADDR_VENDOR_ID		0x0
+#define REG_ADDR_DEVICE_ID		0x2
+#define REG_ADDR_COMMAND		0x4
+#define REG_ADDR_STATUS			0x6
+#define REG_ADDR_REVISION_ID		0x8
+#define REG_ADDR_CLASS_CODE		0x9
+#define REG_ADDR_CACHE_LINE_S		0xc
+#define REG_ADDR_LAT_TIMER		0xd
+#define REG_ADDR_HEADER_TYPE		0xe
+#define REG_ADDR_BIST			0xf
+#define REG_ADDR_BAR0			0x10
+#define REG_ADDR_BAR1			0x14
+#define REG_ADDR_BAR2			0x18
+#define REG_ADDR_BAR3			0x1c
+#define REG_ADDR_BAR4			0x20
+#define REG_ADDR_BAR5			0x24
+
+#define REG_VAL_COMMAND_IO		0x1
+#define REG_VAL_COMMAND_MEMORY		0x2
+#define REG_VAL_COMMAND_MASTER		0x4
+
+#define REG_VAL_HEADER_TYPE_ENDPOINT	0x0
+
+#define REG_VAL_BAR_MEMORY		0x0
+#define REG_VAL_BAR_IO			0x1
+#define REG_VAL_BAR_LOCATE_32		0x0
+#define REG_VAL_BAR_LOCATE_UNDER_1MB	0x2
+#define REG_VAL_BAR_LOCATE_64		0x4
+
+#define VIRTIO_NET_IO_START		0xc000
+
+enum qtest_pci_bar_type {
+	QTEST_PCI_BAR_DISABLE = 0,
+	QTEST_PCI_BAR_IO,
+	QTEST_PCI_BAR_MEMORY_UNDER_1MB,
+	QTEST_PCI_BAR_MEMORY_32,
+	QTEST_PCI_BAR_MEMORY_64
+};
+
+struct qtest_pci_bar {
+	enum qtest_pci_bar_type type;
+	uint8_t addr;
+	uint64_t region_start;
+	uint64_t region_size;
+};
+
+TAILQ_HEAD(qtest_pci_device_list, qtest_pci_device);
+struct qtest_pci_device {
+	TAILQ_ENTRY(qtest_pci_device) next;
+	const char *name;
+	uint16_t device_id;
+	uint16_t vendor_id;
+	uint8_t bus_addr;
+	uint8_t device_addr;
+	struct qtest_pci_bar bar[NB_PCI_BAR];
+	int (*init)(int fd, struct qtest_pci_device *dev);
+};
+
+struct qtest_session {
+	int qtest_socket;
+	int ivshmem_socket;
+	int shm_fd;
+	struct qtest_pci_device_list head;
+};
+
+static uint32_t
+qtest_inl(int fd, uint16_t addr)
+{
+	char buf[1024];
+	int ret;
+
+	snprintf(buf, sizeof(buf), "inl 0x%x\n", addr);
+	ret = write(fd, buf, strlen(buf));
+	ret = read(fd, buf, sizeof(buf));
+	buf[ret] = '\0';
+	return strtoul(buf + strlen("OK "), NULL, 16);
+}
+
+static void
+qtest_outl(int fd, uint16_t addr, uint32_t val)
+{
+	char buf[1024];
+	int ret;
+
+	snprintf(buf, sizeof(buf), "outl 0x%x 0x%x\n", addr, val);
+	ret = write(fd, buf, strlen(buf));
+	if (ret < 0)
+		return;
+
+	ret = read(fd, buf, sizeof(buf));
+	if (ret < 0)
+		return;
+}
+
+static int
+qtest_pci_readb(int fd, uint8_t bus, uint8_t device,
+		uint8_t function, uint8_t offset)
+{
+	uint32_t tmp;
+
+	tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 |
+		(function & 0xf) << 8 | (offset & 0xfc);
+
+	qtest_outl(fd, 0xcf8, tmp);
+	tmp = qtest_inl(fd, 0xcfc);
+
+	return (tmp >> ((offset & 0x3) * 8)) & 0xff;
+}
+
+static uint32_t
+qtest_pci_readl(int fd, uint8_t bus, uint8_t device,
+		uint8_t function, uint8_t offset)
+{
+	uint32_t tmp;
+
+	tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 |
+		(function & 0xf) << 8 | (offset & 0xfc);
+
+	qtest_outl(fd, 0xcf8, tmp);
+	return qtest_inl(fd, 0xcfc);
+}
+
+static void
+qtest_pci_writel(int fd, uint8_t bus, uint8_t device,
+		uint8_t function, uint8_t offset, uint32_t value)
+{
+	uint32_t tmp;
+
+	tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 |
+		(function & 0xf) << 8 | (offset & 0xfc);
+
+	qtest_outl(fd, 0xcf8, tmp);
+	qtest_outl(fd, 0xcfc, value);
+	return;
+}
+
+static uint64_t
+qtest_pci_readq(int fd, uint8_t bus, uint8_t device,
+		uint8_t function, uint8_t offset)
+{
+	uint32_t tmp;
+	uint64_t val;
+
+	tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 |
+		(function & 0xf) << 8 | (offset & 0xfc);
+
+	qtest_outl(fd, 0xcf8, tmp);
+	val = (uint64_t)qtest_inl(fd, 0xcfc);
+
+	tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 |
+		(function & 0xf) << 8 | ((offset + 4)& 0xfc);
+
+	qtest_outl(fd, 0xcf8, tmp);
+	val |= (uint64_t)qtest_inl(fd, 0xcfc) << 32;
+
+	return val;
+}
+
+static void
+qtest_pci_writeq(int fd, uint8_t bus, uint8_t device,
+		uint8_t function, uint8_t offset, uint64_t value)
+{
+	uint32_t tmp;
+
+	tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 |
+		(function & 0xf) << 8 | (offset & 0xfc);
+
+	qtest_outl(fd, 0xcf8, tmp);
+	qtest_outl(fd, 0xcfc, (uint32_t)(value & 0xffffffff));
+
+	tmp = 0x80000000 | (bus & 0xff) << 16 | (device & 0x1f) << 11 |
+		(function & 0xf) << 8 | ((offset + 4) & 0xfc);
+
+	qtest_outl(fd, 0xcf8, tmp);
+	qtest_outl(fd, 0xcfc, (uint32_t)(value >> 32));
+	return;
+}
+
+static int
+qtest_init_pci_device(int fd, struct qtest_pci_device *dev)
+{
+	uint8_t i, bus, device;
+	uint32_t val;
+	uint64_t val64;
+
+	bus = dev->bus_addr;
+	device = dev->device_addr;
+
+	PMD_DRV_LOG(INFO,
+		"Find %s on virtual PCI bus: %04x:%02x:00.0\n",
+		dev->name, bus, device);
+
+	/* Check header type */
+	val = qtest_pci_readb(fd, bus, device, 0, REG_ADDR_HEADER_TYPE);
+	if (val != REG_VAL_HEADER_TYPE_ENDPOINT) {
+		PMD_DRV_LOG(ERR, "Unexpected header type %d\n", val);
+		return -1;
+	}
+
+	/* Check BAR type */
+	for (i = 0; i < NB_PCI_BAR; i++) {
+		val = qtest_pci_readl(fd, bus, device, 0, dev->bar[i].addr);
+
+		switch (dev->bar[i].type) {
+		case QTEST_PCI_BAR_IO:
+			if ((val & 0x1) != REG_VAL_BAR_IO)
+				goto error;
+			break;
+		case QTEST_PCI_BAR_MEMORY_UNDER_1MB:
+			if ((val & 0x1) != REG_VAL_BAR_MEMORY)
+				goto error;
+			if ((val & 0x6) != REG_VAL_BAR_LOCATE_UNDER_1MB)
+				goto error;
+			break;
+		case QTEST_PCI_BAR_MEMORY_32:
+			if ((val & 0x1) != REG_VAL_BAR_MEMORY)
+				goto error;
+			if ((val & 0x6) != REG_VAL_BAR_LOCATE_32)
+				goto error;
+			break;
+		case QTEST_PCI_BAR_MEMORY_64:
+			if ((val & 0x1) != REG_VAL_BAR_MEMORY)
+				goto error;
+			if ((val & 0x6) != REG_VAL_BAR_LOCATE_64)
+				goto error;
+			break;
+		case QTEST_PCI_BAR_DISABLE:
+			break;
+		}
+	}
+
+	/* Enable device */
+	val = qtest_pci_readl(fd, bus, device, 0, REG_ADDR_COMMAND);
+	val |= REG_VAL_COMMAND_IO | REG_VAL_COMMAND_MEMORY | REG_VAL_COMMAND_MASTER;
+	qtest_pci_writel(fd, bus, device, 0, REG_ADDR_COMMAND, val);
+
+	/* Calculate BAR size */
+	for (i = 0; i < NB_PCI_BAR; i++) {
+		switch (dev->bar[i].type) {
+		case QTEST_PCI_BAR_IO:
+		case QTEST_PCI_BAR_MEMORY_UNDER_1MB:
+		case QTEST_PCI_BAR_MEMORY_32:
+			qtest_pci_writel(fd, bus, device, 0,
+					dev->bar[i].addr, 0xffffffff);
+			val = qtest_pci_readl(fd, bus, device,
+					0, dev->bar[i].addr);
+			dev->bar[i].region_size = ~(val & 0xfffffff0) + 1;
+			break;
+		case QTEST_PCI_BAR_MEMORY_64:
+			qtest_pci_writeq(fd, bus, device, 0,
+					dev->bar[i].addr, 0xffffffffffffffff);
+			val64 = qtest_pci_readq(fd, bus, device,
+					0, dev->bar[i].addr);
+			dev->bar[i].region_size =
+					~(val64 & 0xfffffffffffffff0) + 1;
+			break;
+		case QTEST_PCI_BAR_DISABLE:
+			break;
+		}
+	}
+
+	/* Set BAR region */
+	for (i = 0; i < NB_PCI_BAR; i++) {
+		switch (dev->bar[i].type) {
+		case QTEST_PCI_BAR_IO:
+		case QTEST_PCI_BAR_MEMORY_UNDER_1MB:
+		case QTEST_PCI_BAR_MEMORY_32:
+			qtest_pci_writel(fd, bus, device, 0, dev->bar[i].addr,
+				dev->bar[i].region_start);
+			PMD_DRV_LOG(INFO, "Set BAR of %s device: 0x%lx - 0x%lx\n",
+				dev->name, dev->bar[i].region_start,
+				dev->bar[i].region_start + dev->bar[i].region_size);
+			break;
+		case QTEST_PCI_BAR_MEMORY_64:
+			qtest_pci_writeq(fd, bus, device, 0, dev->bar[i].addr,
+				dev->bar[i].region_start);
+			PMD_DRV_LOG(INFO, "Set BAR of %s device: 0x%lx - 0x%lx\n",
+				dev->name, dev->bar[i].region_start,
+				dev->bar[i].region_start + dev->bar[i].region_size);
+			break;
+		case QTEST_PCI_BAR_DISABLE:
+			break;
+		}
+
+	}
+
+	return 0;
+
+error:
+	PMD_DRV_LOG(ERR, "Unexpected BAR type\n");
+	return -1;
+}
+
+static int
+qtest_try_init_pci_device(struct qtest_session *s, uint16_t bus, uint8_t device)
+{
+	struct qtest_pci_device *dev;
+	uint32_t val;
+
+	val = qtest_pci_readl(s->qtest_socket, bus, device, 0, 0);
+	TAILQ_FOREACH(dev, &s->head, next) {
+		if (val == ((uint32_t)dev->device_id << 16 | dev->vendor_id)) {
+			dev->bus_addr = bus;
+			dev->device_addr = device;
+			return dev->init(s->qtest_socket, dev);
+		}
+	}
+
+	return 0;
+}
+
+static int
+qtest_init_pci_devices(struct qtest_session *s)
+{
+	uint16_t bus;
+	uint8_t device;
+	int ret;
+
+	bus = 0;
+	do {
+		device = 0;
+		do {
+			ret = qtest_try_init_pci_device(s, bus, device);
+			if (ret != 0)
+				return ret;
+		}
+		while (device++ != NB_DEVICE - 1);
+	} while (bus++ != NB_BUS - 1);
+
+	return 0;
+}
+
+static void
+qtest_close_session(struct qtest_session *s)
+{
+	close(s->qtest_socket);
+	close(s->ivshmem_socket);
+}
+
+static int
+qtest_register_target_devices(struct qtest_session *s)
+{
+	struct qtest_pci_device *virtio_net, *ivshmem;
+	void *addr;
+
+	virtio_net = malloc(sizeof(*virtio_net));
+	if (virtio_net == NULL)
+		return -1;
+
+	ivshmem = malloc(sizeof(*ivshmem));
+	if (ivshmem == NULL)
+		return -1;
+
+	memset(virtio_net, 0, sizeof(*virtio_net));
+	memset(ivshmem, 0, sizeof(*ivshmem));
+
+	TAILQ_INIT(&s->head);
+
+	virtio_net->name = "virtio-net";
+	virtio_net->device_id = 0x1000;
+	virtio_net->vendor_id = 0x1af4;
+	virtio_net->init = qtest_init_pci_device;
+	virtio_net->bar[0].addr = REG_ADDR_BAR0;
+	virtio_net->bar[0].type = QTEST_PCI_BAR_IO;
+	virtio_net->bar[0].region_start = VIRTIO_NET_IO_START;
+
+	TAILQ_INSERT_TAIL(&s->head, virtio_net, next);
+
+	ivshmem->name = "ivshmem";
+	ivshmem->device_id = 0x1110;
+	ivshmem->vendor_id = 0x1af4;
+	ivshmem->init = qtest_init_pci_device;
+	ivshmem->bar[0].addr = REG_ADDR_BAR0;
+	ivshmem->bar[0].type = QTEST_PCI_BAR_MEMORY_32;
+	ivshmem->bar[0].region_start = 0;
+	ivshmem->bar[1].addr = REG_ADDR_BAR2;
+	ivshmem->bar[1].type = QTEST_PCI_BAR_MEMORY_64;
+
+	rte_memseg_info_get(0, NULL, NULL, &addr);
+	ivshmem->bar[1].region_start = rte_mem_virt2phy(addr);
+
+	TAILQ_INSERT_TAIL(&s->head, ivshmem, next);
+
+	return 0;
+}
+
+static int
+qtest_send_message_to_ivshmem(int sock_fd, uint64_t client_id, int shm_fd)
+{
+	struct iovec iov;
+	struct msghdr msgh;
+	size_t fdsize = sizeof(int);
+	char control[CMSG_SPACE(fdsize)];
+	struct cmsghdr *cmsg;
+	int ret;
+
+	memset(&msgh, 0, sizeof(msgh));
+	iov.iov_base = &client_id;
+	iov.iov_len = sizeof(client_id);
+
+	msgh.msg_iov = &iov;
+	msgh.msg_iovlen = 1;
+
+	if (shm_fd >= 0) {
+		msgh.msg_control = &control;
+		msgh.msg_controllen = sizeof(control);
+		cmsg = CMSG_FIRSTHDR(&msgh);
+		cmsg->cmsg_len = CMSG_LEN(fdsize);
+		cmsg->cmsg_level = SOL_SOCKET;
+		cmsg->cmsg_type = SCM_RIGHTS;
+		memcpy(CMSG_DATA(cmsg), &shm_fd, fdsize);
+	}
+
+	do {
+		ret = sendmsg(sock_fd, &msgh, 0);
+	} while (ret < 0 && errno == EINTR);
+
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "sendmsg error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int
+qtest_setup_shared_memory(struct qtest_session *s)
+{
+	int ret;
+
+	/* send our protocol version first */
+	ret = qtest_send_message_to_ivshmem(s->ivshmem_socket,
+			IVSHMEM_PROTOCOL_VERSION, -1);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR,
+			"Failed to send protocol version to ivshmem\n");
+		return -1;
+	}
+
+	/* send cliend id */
+	ret = qtest_send_message_to_ivshmem(s->ivshmem_socket, 0, -1);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to send VMID to ivshmem\n");
+		return -1;
+	}
+
+	/* send shm_fd */
+	ret = qtest_send_message_to_ivshmem(s->ivshmem_socket, -1, s->shm_fd);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to file descriptor to ivshmem\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void
+qtest_remove_target_devices(struct qtest_session *s)
+{
+	struct qtest_pci_device *dev, *next;
+
+	for (dev = TAILQ_FIRST(&s->head); dev != NULL; dev = next) {
+		next = TAILQ_NEXT(dev, next);
+		TAILQ_REMOVE(&s->head, dev, next);
+		free(dev);
+	}
+}
+
+int
+qtest_vdev_init(struct rte_eth_dev_data *data,
+		int qtest_socket, int ivshmem_socket)
+{
+	struct virtio_hw *hw = data->dev_private;
+	struct qtest_session *s;
+	uint64_t size = 0;
+	int ret;
+	int shm_fd;
+
+	s = malloc(sizeof(*s));
+
+	ret = qtest_register_target_devices(s);
+	if (ret != 0) {
+		PMD_DRV_LOG(ERR, "Failed to initialize qtest session\n");
+		return -1;
+	}
+
+	rte_memseg_info_get(0, &shm_fd, &size, NULL);
+
+	s->qtest_socket = qtest_socket;
+	s->ivshmem_socket = ivshmem_socket;
+	s->shm_fd = shm_fd;
+
+	ret = qtest_setup_shared_memory(s);
+	if (ret != 0) {
+		PMD_DRV_LOG(ERR, "Failed to setup shared memory\n");
+		return -1;
+	}
+
+	ret = qtest_init_pci_devices(s);
+	if (ret != 0) {
+		PMD_DRV_LOG(ERR, "Failed to initialize devices\n");
+		return -1;
+	}
+
+	hw->qsession = (void *)s;
+
+	return 0;
+}
+
+void
+qtest_vdev_uninit(struct rte_eth_dev_data *data)
+{
+	struct virtio_hw *hw = data->dev_private;
+	struct qtest_session *s;
+
+	s = (struct qtest_session *)hw->qsession;
+	qtest_close_session(s);
+	qtest_remove_target_devices(s);
+}
+
+void
+virtio_ioport_write(struct virtio_hw *hw, uint64_t addr, uint64_t val)
+{
+	struct qtest_session *s = (struct qtest_session *)hw->qsession;
+
+	return qtest_outl(s->qtest_socket,
+			VIRTIO_NET_IO_START + (uint16_t)addr, val);
+}
+
+uint32_t
+virtio_ioport_read(struct virtio_hw *hw, uint64_t addr)
+{
+	struct qtest_session *s = (struct qtest_session *)hw->qsession;
+
+	return qtest_inl(s->qtest_socket,
+			VIRTIO_NET_IO_START + (uint16_t)addr);
+}
+
diff --git a/drivers/net/virtio/virtio_ethdev.c b/drivers/net/virtio/virtio_ethdev.c
index 74c00ee..ea42ef1 100644
--- a/drivers/net/virtio/virtio_ethdev.c
+++ b/drivers/net/virtio/virtio_ethdev.c
@@ -36,6 +36,11 @@ 
 #include <stdio.h>
 #include <errno.h>
 #include <unistd.h>
+#ifdef RTE_VIRTIO_VDEV
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
 #ifdef RTE_EXEC_ENV_LINUXAPP
 #include <dirent.h>
 #include <fcntl.h>
@@ -56,6 +61,9 @@ 
 #include <rte_memory.h>
 #include <rte_eal.h>
 #include <rte_dev.h>
+#ifdef RTE_VIRTIO_VDEV
+#include <rte_kvargs.h>
+#endif
 
 #include "virtio_ethdev.h"
 #include "virtio_pci.h"
@@ -491,8 +499,10 @@  virtio_dev_close(struct rte_eth_dev *dev)
 	PMD_INIT_LOG(DEBUG, "virtio_dev_close");
 
 	/* reset the NIC */
-	if (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)
+	if ((dev->dev_type == RTE_ETH_DEV_PCI) &&
+			(pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)) {
 		vtpci_irq_config(hw, VIRTIO_MSI_NO_VECTOR);
+	}
 	vtpci_reset(hw);
 	hw->started = 0;
 	virtio_dev_free_mbufs(dev);
@@ -1266,7 +1276,7 @@  eth_virtio_dev_init(struct rte_eth_dev *eth_dev)
 	struct virtio_hw *hw = eth_dev->data->dev_private;
 	struct virtio_net_config *config;
 	struct virtio_net_config local_config;
-	struct rte_pci_device *pci_dev;
+	struct rte_pci_device *pci_dev = eth_dev->pci_dev;
 
 	RTE_BUILD_BUG_ON(RTE_PKTMBUF_HEADROOM < sizeof(struct virtio_net_hdr));
 
@@ -1287,15 +1297,18 @@  eth_virtio_dev_init(struct rte_eth_dev *eth_dev)
 		return -ENOMEM;
 	}
 
-	pci_dev = eth_dev->pci_dev;
+	if (eth_dev->dev_type == RTE_ETH_DEV_PCI) {
+		rte_eth_copy_pci_info(eth_dev, pci_dev);
 
-	rte_eth_copy_pci_info(eth_dev, pci_dev);
+		if (virtio_resource_init(pci_dev) < 0)
+			return -1;
 
-	if (virtio_resource_init(pci_dev) < 0)
-		return -1;
-
-	hw->use_msix = virtio_has_msix(&pci_dev->addr);
-	hw->io_base = (uint32_t)(uintptr_t)pci_dev->mem_resource[0].addr;
+		hw->use_msix = virtio_has_msix(&pci_dev->addr);
+		hw->io_base = (uint32_t)(uintptr_t)pci_dev->mem_resource[0].addr;
+	} else {
+		hw->use_msix = 0;
+		hw->io_base = 0;
+	}
 
 	/* Reset the device although not necessary at startup */
 	vtpci_reset(hw);
@@ -1308,8 +1321,10 @@  eth_virtio_dev_init(struct rte_eth_dev *eth_dev)
 	virtio_negotiate_features(hw);
 
 	/* If host does not support status then disable LSC */
-	if (!vtpci_with_feature(hw, VIRTIO_NET_F_STATUS))
+	if ((eth_dev->dev_type == RTE_ETH_DEV_PCI) &&
+			(!vtpci_with_feature(hw, VIRTIO_NET_F_STATUS))) {
 		pci_dev->driver->drv_flags &= ~RTE_PCI_DRV_INTR_LSC;
+	}
 
 	rx_func_get(eth_dev);
 
@@ -1385,14 +1400,16 @@  eth_virtio_dev_init(struct rte_eth_dev *eth_dev)
 
 	PMD_INIT_LOG(DEBUG, "hw->max_rx_queues=%d   hw->max_tx_queues=%d",
 			hw->max_rx_queues, hw->max_tx_queues);
-	PMD_INIT_LOG(DEBUG, "port %d vendorID=0x%x deviceID=0x%x",
-			eth_dev->data->port_id, pci_dev->id.vendor_id,
-			pci_dev->id.device_id);
+	if (eth_dev->dev_type == RTE_ETH_DEV_PCI) {
+		PMD_INIT_LOG(DEBUG, "port %d vendorID=0x%x deviceID=0x%x",
+				eth_dev->data->port_id, pci_dev->id.vendor_id,
+				pci_dev->id.device_id);
 
-	/* Setup interrupt callback  */
-	if (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)
-		rte_intr_callback_register(&pci_dev->intr_handle,
-				   virtio_interrupt_handler, eth_dev);
+		/* Setup interrupt callback  */
+		if (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)
+			rte_intr_callback_register(&pci_dev->intr_handle,
+					virtio_interrupt_handler, eth_dev);
+	}
 
 	virtio_dev_cq_start(eth_dev);
 
@@ -1426,10 +1443,12 @@  eth_virtio_dev_uninit(struct rte_eth_dev *eth_dev)
 	eth_dev->data->mac_addrs = NULL;
 
 	/* reset interrupt callback  */
-	if (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)
+	if ((eth_dev->dev_type == RTE_ETH_DEV_PCI) &&
+			(pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)) {
 		rte_intr_callback_unregister(&pci_dev->intr_handle,
 						virtio_interrupt_handler,
 						eth_dev);
+	}
 
 	PMD_INIT_LOG(DEBUG, "dev_uninit completed");
 
@@ -1493,11 +1512,13 @@  virtio_dev_configure(struct rte_eth_dev *dev)
 		return -ENOTSUP;
 	}
 
-	if (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)
+	if ((dev->dev_type == RTE_ETH_DEV_PCI) &&
+			(pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)) {
 		if (vtpci_irq_config(hw, 0) == VIRTIO_MSI_NO_VECTOR) {
 			PMD_DRV_LOG(ERR, "failed to set config vector");
 			return -EBUSY;
 		}
+	}
 
 	return 0;
 }
@@ -1511,7 +1532,8 @@  virtio_dev_start(struct rte_eth_dev *dev)
 	struct rte_pci_device *pci_dev = dev->pci_dev;
 
 	/* check if lsc interrupt feature is enabled */
-	if (dev->data->dev_conf.intr_conf.lsc) {
+	if ((dev->dev_type == RTE_ETH_DEV_PCI) &&
+			(dev->data->dev_conf.intr_conf.lsc)) {
 		if (!(pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)) {
 			PMD_DRV_LOG(ERR, "link status not supported by host");
 			return -ENOTSUP;
@@ -1617,8 +1639,10 @@  virtio_dev_stop(struct rte_eth_dev *dev)
 
 	PMD_INIT_LOG(DEBUG, "stop");
 
-	if (dev->data->dev_conf.intr_conf.lsc)
+	if ((dev->dev_type == RTE_ETH_DEV_PCI) &&
+			(dev->data->dev_conf.intr_conf.lsc)) {
 		rte_intr_disable(&dev->pci_dev->intr_handle);
+	}
 
 	memset(&link, 0, sizeof(link));
 	virtio_dev_atomic_write_link_status(dev, &link);
@@ -1691,3 +1715,151 @@  static struct rte_driver rte_virtio_driver = {
 };
 
 PMD_REGISTER_DRIVER(rte_virtio_driver);
+
+#ifdef RTE_VIRTIO_VDEV
+
+#define ETH_CVIO_ARG_QTEST_PATH		"qtest"
+#define ETH_CVIO_ARG_IVSHMEM_PATH	"ivshmem"
+
+/*TODO: specify mac addr */
+static const char *valid_args[] = {
+	ETH_CVIO_ARG_QTEST_PATH,
+	ETH_CVIO_ARG_IVSHMEM_PATH,
+	NULL
+};
+
+static int
+get_string_arg(const char *key __rte_unused,
+		const char *value, void *extra_args)
+{
+	int ret, fd, loop = 3;
+	int *pfd = extra_args;
+	struct sockaddr_un sa = {0};
+
+	if ((value == NULL) || (extra_args == NULL))
+		return -EINVAL;
+
+	fd = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (fd < 0)
+		return -1;
+
+	sa.sun_family = AF_UNIX;
+	strncpy(sa.sun_path, value, sizeof(sa.sun_path));
+
+	/* We need to wait QEMU until socket will be prepared */
+	while (loop--) {
+		ret = connect(fd, (struct sockaddr*)&sa,
+				sizeof(struct sockaddr_un));
+		if (ret != 0)
+			sleep(1);
+		else
+			break;
+	}
+
+	if (ret != 0) {
+		close(fd);
+		return -1;
+	}
+
+	*pfd = fd;
+
+	return 0;
+}
+
+static struct rte_eth_dev *
+cvio_eth_dev_alloc(const char *name)
+{
+	struct rte_eth_dev *eth_dev;
+	struct rte_eth_dev_data *data;
+	struct virtio_hw *hw;
+
+	eth_dev = rte_eth_dev_allocate(name, RTE_ETH_DEV_VIRTUAL);
+	if (eth_dev == NULL)
+		rte_panic("cannot alloc rte_eth_dev\n");
+
+	data = eth_dev->data;
+
+	hw = rte_zmalloc(NULL, sizeof(*hw), 0);
+	if (!hw)
+		rte_panic("malloc virtio_hw failed\n");
+
+	data->dev_private = hw;
+	/* will be used in virtio_dev_info_get() */
+	eth_dev->driver = &rte_virtio_pmd;
+	/* TAILQ_INIT(&(eth_dev->link_intr_cbs)); */
+	return eth_dev;
+}
+
+/*
+ * Dev initialization routine.
+ * Invoked once for each virtio vdev at EAL init time,
+ * See rte_eal_dev_init().
+ * Returns 0 on success.
+ */
+static int
+rte_cvio_pmd_devinit(const char *name, const char *params)
+{
+	struct rte_kvargs *kvlist = NULL;
+	struct rte_eth_dev *eth_dev = NULL;
+	int ret, qtest_sock, ivshmem_sock;
+
+	if (params == NULL || params[0] == '\0') {
+		rte_panic("param is null\n");
+	}
+
+	kvlist = rte_kvargs_parse(params, valid_args);
+	if (!kvlist)
+		rte_panic("error when parsing param\n");
+
+	if (rte_kvargs_count(kvlist, ETH_CVIO_ARG_IVSHMEM_PATH) == 1) {
+		ret = rte_kvargs_process(kvlist, ETH_CVIO_ARG_IVSHMEM_PATH,
+				&get_string_arg, &ivshmem_sock);
+		if (ret != 0) {
+			PMD_INIT_LOG(ERR,
+				"Failed to connect to ivshmem socket");
+			return -1;
+		}
+	} else {
+		rte_panic("no arg: %s\n", ETH_CVIO_ARG_IVSHMEM_PATH);
+	}
+
+	if (rte_kvargs_count(kvlist, ETH_CVIO_ARG_QTEST_PATH) == 1) {
+		ret = rte_kvargs_process(kvlist, ETH_CVIO_ARG_QTEST_PATH,
+				&get_string_arg, &qtest_sock);
+		if (ret != 0) {
+			PMD_INIT_LOG(ERR,
+				"Failed to connect to qtest socket");
+			return -1;
+		}
+	} else {
+		rte_panic("no arg: %s\n", ETH_CVIO_ARG_QTEST_PATH);
+	}
+
+	eth_dev = cvio_eth_dev_alloc(name);
+
+	qtest_vdev_init(eth_dev->data, qtest_sock, ivshmem_sock);
+
+	/* originally, this will be called in rte_eal_pci_probe() */
+	eth_virtio_dev_init(eth_dev);
+
+	return 0;
+}
+
+static int
+rte_cvio_pmd_devuninit(const char *name)
+{
+	/* TODO: Support port hotlug */
+	rte_panic("%s", name);
+	return 0;
+}
+
+static struct rte_driver rte_cvio_driver = {
+	.name   = "eth_cvio",
+	.type   = PMD_VDEV,
+	.init   = rte_cvio_pmd_devinit,
+	.uninit = rte_cvio_pmd_devuninit,
+};
+
+PMD_REGISTER_DRIVER(rte_cvio_driver);
+
+#endif /* RTE_VIRTIO_VDEV */
diff --git a/drivers/net/virtio/virtio_ethdev.h b/drivers/net/virtio/virtio_ethdev.h
index ae2d47d..4f12c53 100644
--- a/drivers/net/virtio/virtio_ethdev.h
+++ b/drivers/net/virtio/virtio_ethdev.h
@@ -56,6 +56,16 @@ 
 #define VIRTIO_MAX_RX_PKTLEN  9728
 
 /* Features desired/implemented by this driver. */
+#ifdef RTE_VIRTIO_VDEV
+#define VIRTIO_PMD_GUEST_FEATURES		\
+	(1u << VIRTIO_NET_F_MAC		  |	\
+	 1u << VIRTIO_NET_F_MQ		  |	\
+	 1u << VIRTIO_NET_F_CTRL_MAC_ADDR |	\
+	 1u << VIRTIO_NET_F_CTRL_VQ	  |	\
+	 1u << VIRTIO_NET_F_CTRL_RX	  |	\
+	 1u << VIRTIO_NET_F_CTRL_VLAN	  |	\
+	 1u << VIRTIO_NET_F_MRG_RXBUF)
+#else /* RTE_VIRTIO_VDEV */
 #define VIRTIO_PMD_GUEST_FEATURES		\
 	(1u << VIRTIO_NET_F_MAC		  |	\
 	 1u << VIRTIO_NET_F_STATUS	  |	\
@@ -65,6 +75,7 @@ 
 	 1u << VIRTIO_NET_F_CTRL_RX	  |	\
 	 1u << VIRTIO_NET_F_CTRL_VLAN	  |	\
 	 1u << VIRTIO_NET_F_MRG_RXBUF)
+#endif /* RTE_VIRTIO_VDEV */
 
 /*
  * CQ function prototype
@@ -122,5 +133,10 @@  uint16_t virtio_xmit_pkts_simple(void *tx_queue, struct rte_mbuf **tx_pkts,
 #define VTNET_LRO_FEATURES (VIRTIO_NET_F_GUEST_TSO4 | \
 			    VIRTIO_NET_F_GUEST_TSO6 | VIRTIO_NET_F_GUEST_ECN)
 
+#ifdef RTE_VIRTIO_VDEV
+int qtest_vdev_init(struct rte_eth_dev_data *data,
+		int qtest_socket, int ivshmem_socket);
+void qtest_vdev_uninit(struct rte_eth_dev_data *data);
+#endif
 
 #endif /* _VIRTIO_ETHDEV_H_ */
diff --git a/drivers/net/virtio/virtio_pci.h b/drivers/net/virtio/virtio_pci.h
index 47f722a..fe884de 100644
--- a/drivers/net/virtio/virtio_pci.h
+++ b/drivers/net/virtio/virtio_pci.h
@@ -165,6 +165,9 @@  struct virtqueue;
 
 struct virtio_hw {
 	struct virtqueue *cvq;
+#ifdef RTE_VIRTIO_VDEV
+	void        *qsession;
+#endif
 	uint32_t    io_base;
 	uint32_t    guest_features;
 	uint32_t    max_tx_queues;
@@ -226,6 +229,26 @@  outl_p(unsigned int data, unsigned int port)
 }
 #endif
 
+#ifdef RTE_VIRTIO_VDEV
+
+uint32_t virtio_ioport_read(struct virtio_hw *, uint64_t);
+void virtio_ioport_write(struct virtio_hw *, uint64_t, uint64_t);
+
+#define VIRTIO_READ_REG_1(hw, reg) \
+	virtio_ioport_read(hw, reg)
+#define VIRTIO_WRITE_REG_1(hw, reg, value) \
+	virtio_ioport_write(hw, reg, value)
+#define VIRTIO_READ_REG_2(hw, reg) \
+	virtio_ioport_read(hw, reg)
+#define VIRTIO_WRITE_REG_2(hw, reg, value) \
+	virtio_ioport_write(hw, reg, value)
+#define VIRTIO_READ_REG_4(hw, reg) \
+	virtio_ioport_read(hw, reg)
+#define VIRTIO_WRITE_REG_4(hw, reg, value) \
+	virtio_ioport_write(hw, reg, value)
+
+#else /* RTE_VIRTIO_VDEV */
+
 #define VIRTIO_PCI_REG_ADDR(hw, reg) \
 	(unsigned short)((hw)->io_base + (reg))
 
@@ -244,6 +267,8 @@  outl_p(unsigned int data, unsigned int port)
 #define VIRTIO_WRITE_REG_4(hw, reg, value) \
 	outl_p((unsigned int)(value), (VIRTIO_PCI_REG_ADDR((hw), (reg))))
 
+#endif /* RTE_VIRTIO_VDEV */
+
 static inline int
 vtpci_with_feature(struct virtio_hw *hw, uint32_t bit)
 {