diff --git a/drivers/net/virtio/qtest.h b/drivers/net/virtio/qtest.h
index 46b9ee6..421e62c 100644
--- a/drivers/net/virtio/qtest.h
+++ b/drivers/net/virtio/qtest.h
@@ -35,5 +35,60 @@
 #define _VIRTIO_QTEST_H_
 
 #define QTEST_DRV_NAME		        "eth_qtest_virtio"
+#define QTEST_DEVICE_NUM                3
+
+#include <linux/pci_regs.h>
+
+/* Device information */
+#define VIRTIO_NET_DEVICE_ID            0x1000
+#define VIRTIO_NET_VENDOR_ID            0x1af4
+#define VIRTIO_NET_IRQ_NUM              10
+#define IVSHMEM_DEVICE_ID               0x1110
+#define IVSHMEM_VENDOR_ID               0x1af4
+#define PIIX3_DEVICE_ID                 0x7000
+#define PIIX3_VENDOR_ID                 0x8086
+
+/* ------------------------------------------------------------
+ * IO port mapping of qtest guest
+ * ------------------------------------------------------------
+ * 0x0000 - 0xbfff : not used
+ * 0xc000 - 0xc03f : virtio-net(BAR0)
+ * 0xc040 - 0xffff : not used
+ *
+ * ------------------------------------------------------------
+ * Memory mapping of qtest quest
+ * ------------------------------------------------------------
+ * 0x00000000_00000000 - 0x00000000_3fffffff : not used
+ * 0x00000000_40000000 - 0x00000000_40000fff : virtio-net(BAR1)
+ * 0x00000000_40001000 - 0x00000000_40ffffff : not used
+ * 0x00000000_41000000 - 0x00000000_417fffff : virtio-net(BAR4)
+ * 0x00000000_41800000 - 0x00000000_41ffffff : not used
+ * 0x00000000_42000000 - 0x00000000_420000ff : ivshmem(BAR0)
+ * 0x00000000_42000100 - 0x00000000_42ffffff : not used
+ * 0x00000000_80000000 - 0xffffffff_ffffffff : ivshmem(BAR2)
+ *
+ * We can only specify start address of a region. The region size
+ * will be defined by the device implementation in QEMU.
+ * The size will be pow of 2 according to the PCI specification.
+ * Also, the region start address should be aligned by region size.
+ *
+ * BAR2 of ivshmem will be used to mmap DPDK application memory.
+ * So this address will be dynamically changed, but not to overlap
+ * others, it should be mmaped between above addresses. Such allocation
+ * is done by EAL. Check rte_eal_get_free_region() also.
+ */
+#define VIRTIO_NET_IO_START             0xc000
+#define VIRTIO_NET_MEMORY1_START	0x40000000
+#define VIRTIO_NET_MEMORY2_START	0x41000000
+#define IVSHMEM_MEMORY_START            0x42000000
+
+static inline struct rte_pci_id
+qtest_get_pci_id_of_virtio_net(void)
+{
+	struct rte_pci_id id =  {VIRTIO_NET_DEVICE_ID,
+		VIRTIO_NET_VENDOR_ID, PCI_ANY_ID, PCI_ANY_ID};
+
+	return id;
+}
 
 #endif /* _VIRTIO_QTEST_H_ */
diff --git a/drivers/net/virtio/virtio_ethdev.c b/drivers/net/virtio/virtio_ethdev.c
index 747596d..4e454db 100644
--- a/drivers/net/virtio/virtio_ethdev.c
+++ b/drivers/net/virtio/virtio_ethdev.c
@@ -60,6 +60,10 @@
 #include "virtqueue.h"
 #include "virtio_rxtx.h"
 
+#ifdef RTE_VIRTIO_VDEV_QTEST
+#include "qtest.h"
+#include "qtest_utils.h"
+#endif
 
 static int eth_virtio_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_virtio_dev_uninit(struct rte_eth_dev *eth_dev);
@@ -387,7 +391,7 @@ int virtio_dev_queue_setup(struct rte_eth_dev *dev,
 			return -ENOMEM;
 		}
 	}
-#ifdef RTE_VIRTIO_VDEV
+#if defined(RTE_VIRTIO_VDEV) || defined(RTE_VIRTIO_VDEV_QTEST)
 	else
 		vq->vq_ring_mem = (phys_addr_t)mz->addr; /* Use vaddr!!! */
 #endif
@@ -431,7 +435,7 @@ int virtio_dev_queue_setup(struct rte_eth_dev *dev,
 
 		if (virtio_dev_check(dev, RTE_ETH_DEV_PCI, NULL, 0))
 			vq->virtio_net_hdr_mem = mz->phys_addr;
-#ifdef RTE_VIRTIO_VDEV
+#if defined(RTE_VIRTIO_VDEV) || defined(RTE_VIRTIO_VDEV_QTEST)
 		else
 			vq->virtio_net_hdr_mem = (phys_addr_t)mz->addr;
 #endif
@@ -441,7 +445,7 @@ int virtio_dev_queue_setup(struct rte_eth_dev *dev,
 
 	if (virtio_dev_check(dev, RTE_ETH_DEV_PCI, NULL, 0))
 		vq->offset = offsetof(struct rte_mbuf, buf_physaddr);
-#ifdef RTE_VIRTIO_VDEV
+#if defined(RTE_VIRTIO_VDEV) || defined(RTE_VIRTIO_VDEV_QTEST)
 	else
 		vq->offset = offsetof(struct rte_mbuf, buf_addr);
 #endif
@@ -999,6 +1003,23 @@ virtio_interrupt_handler(__rte_unused struct rte_intr_handle *handle,
 	isr = vtpci_isr(hw);
 	PMD_DRV_LOG(INFO, "interrupt status = %#x", isr);
 
+#ifdef RTE_VIRTIO_VDEV_QTEST
+	if (virtio_dev_check(dev, RTE_ETH_DEV_VIRTUAL, QTEST_DRV_NAME, 0)) {
+		if (qtest_intr_enable(hw->qsession) < 0)
+			PMD_DRV_LOG(ERR, "interrupt enable failed");
+		/*
+		 * If last qtest message is interrupt, 'isr' will be 0
+		 * becasue socket has been closed already.
+		 * But still we want to notice this event to EAL.
+		 * So just ignore isr value.
+		 */
+		if (virtio_dev_link_update(dev, 0) == 0)
+			_rte_eth_dev_callback_process(dev,
+					RTE_ETH_EVENT_INTR_LSC);
+		return;
+	}
+#endif
+
 	if (virtio_dev_check(dev, RTE_ETH_DEV_PCI, NULL, 0))
 		if (rte_intr_enable(&dev->pci_dev->intr_handle) < 0)
 			PMD_DRV_LOG(ERR, "interrupt enable failed");
@@ -1058,6 +1079,13 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev)
 		if (vtpci_init(eth_dev, hw) < 0)
 			return -1;
 	}
+#ifdef RTE_VIRTIO_VDEV_QTEST
+	else if (virtio_dev_check(eth_dev,
+				RTE_ETH_DEV_VIRTUAL, QTEST_DRV_NAME, 0)) {
+		if (vtpci_init(eth_dev, hw) < 0)
+			return -1;
+	}
+#endif
 
 	/* Reset the device although not necessary at startup */
 	vtpci_reset(hw);
@@ -1077,6 +1105,13 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev)
 
 		rte_eth_copy_pci_info(eth_dev, pci_dev);
 	}
+#ifdef RTE_VIRTIO_VDEV_QTEST
+	else if (virtio_dev_check(eth_dev,
+				RTE_ETH_DEV_VIRTUAL, QTEST_DRV_NAME, 0)) {
+		if (!vtpci_with_feature(hw, VIRTIO_NET_F_STATUS))
+			pci_dev->driver->drv_flags &= ~RTE_PCI_DRV_INTR_LSC;
+	}
+#endif
 
 	rx_func_get(eth_dev);
 
@@ -1165,6 +1200,26 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev)
 						   virtio_interrupt_handler,
 						   eth_dev);
 	}
+#ifdef RTE_VIRTIO_VDEV_QTEST
+	else if (virtio_dev_check(eth_dev, RTE_ETH_DEV_VIRTUAL,
+				QTEST_DRV_NAME, 0)) {
+		struct rte_pci_id id;
+
+		id = qtest_get_pci_id_of_virtio_net();
+		RTE_SET_USED(id);
+
+		PMD_INIT_LOG(DEBUG, "port %d vendorID=0x%x deviceID=0x%x",
+				eth_dev->data->port_id,
+				id.vendor_id, id.device_id);
+
+		/* Setup interrupt callback  */
+		if (virtio_dev_check(eth_dev, RTE_ETH_DEV_VIRTUAL,
+					NULL, RTE_ETH_DEV_INTR_LSC))
+			qtest_intr_callback_register(hw->qsession,
+					virtio_interrupt_handler, eth_dev);
+	}
+#endif
+
 	virtio_dev_cq_start(eth_dev);
 
 	return 0;
@@ -1202,7 +1257,15 @@ eth_virtio_dev_uninit(struct rte_eth_dev *eth_dev)
 					     virtio_interrupt_handler,
 					     eth_dev);
 
-	rte_eal_pci_unmap_device(pci_dev);
+#ifdef RTE_VIRTIO_VDEV_QTEST
+	else if (virtio_dev_check(eth_dev, RTE_ETH_DEV_VIRTUAL,
+				QTEST_DRV_NAME, RTE_ETH_DEV_INTR_LSC))
+		qtest_intr_callback_unregister(hw->qsession,
+				virtio_interrupt_handler, eth_dev);
+#endif
+
+	if (virtio_dev_check(eth_dev, RTE_ETH_DEV_PCI, NULL, 0))
+		rte_eal_pci_unmap_device(pci_dev);
 
 	PMD_INIT_LOG(DEBUG, "dev_uninit completed");
 
@@ -1284,16 +1347,34 @@ virtio_dev_start(struct rte_eth_dev *dev)
 
 	/* check if lsc interrupt feature is enabled */
 	if (dev->data->dev_conf.intr_conf.lsc) {
-		if (!virtio_dev_check(dev, RTE_ETH_DEV_PCI,
-					NULL, RTE_PCI_DRV_INTR_LSC)) {
+		int pdev_has_lsc = 0, vdev_has_lsc = 0;
+
+		pdev_has_lsc = virtio_dev_check(dev, RTE_ETH_DEV_PCI,
+				NULL, RTE_PCI_DRV_INTR_LSC);
+#ifdef RTE_VIRTIO_VDEV_QTEST
+		vdev_has_lsc = virtio_dev_check(dev, RTE_ETH_DEV_VIRTUAL,
+				QTEST_DRV_NAME, RTE_ETH_DEV_INTR_LSC);
+#endif
+
+		if ((!pdev_has_lsc) && (!vdev_has_lsc)) {
 			PMD_DRV_LOG(ERR, "link status not supported by host");
 			return -ENOTSUP;
 		}
 
-		if (rte_intr_enable(&dev->pci_dev->intr_handle) < 0) {
-			PMD_DRV_LOG(ERR, "interrupt enable failed");
-			return -EIO;
+		if (pdev_has_lsc) {
+			if (rte_intr_enable(&dev->pci_dev->intr_handle) < 0) {
+				PMD_DRV_LOG(ERR, "interrupt enable failed");
+				return -EIO;
+			}
 		}
+#ifdef RTE_VIRTIO_VDEV_QTEST
+		else if (vdev_has_lsc) {
+			if (qtest_intr_enable(hw->qsession) < 0) {
+				PMD_DRV_LOG(ERR, "interrupt enable failed");
+				return -EIO;
+			}
+		}
+#endif
 	}
 
 	/* Initialize Link state */
@@ -1387,11 +1468,20 @@ static void
 virtio_dev_stop(struct rte_eth_dev *dev)
 {
 	struct rte_eth_link link;
+	struct virtio_hw *hw = dev->data->dev_private;
 
 	PMD_INIT_LOG(DEBUG, "stop");
+	RTE_SET_USED(hw);
 
-	if (dev->data->dev_conf.intr_conf.lsc)
-		rte_intr_disable(&dev->pci_dev->intr_handle);
+	if (dev->data->dev_conf.intr_conf.lsc) {
+		if (virtio_dev_check(dev, RTE_ETH_DEV_PCI, NULL, 0))
+			rte_intr_disable(&dev->pci_dev->intr_handle);
+#ifdef RTE_VIRTIO_VDEV_QTEST
+		else if (virtio_dev_check(dev, RTE_ETH_DEV_VIRTUAL,
+					QTEST_DRV_NAME, 0))
+			qtest_intr_disable(hw->qsession);
+#endif
+	}
 
 	memset(&link, 0, sizeof(link));
 	virtio_dev_atomic_write_link_status(dev, &link);
@@ -1628,3 +1718,348 @@ static struct rte_driver rte_cvio_driver = {
 PMD_REGISTER_DRIVER(rte_cvio_driver);
 
 #endif
+
+#ifdef RTE_VIRTIO_VDEV_QTEST
+
+#define ETH_VIRTIO_NET_ARG_QTEST_PATH           "qtest"
+#define ETH_VIRTIO_NET_ARG_IVSHMEM_PATH         "ivshmem"
+#define ETH_VIRTIO_NET_ARG_VIRTIO_NET_ADDR      "virtio-net-addr"
+#define ETH_VIRTIO_NET_ARG_IVSHMEM_ADDR         "ivshmem-addr"
+#define ETH_VIRTIO_NET_ARG_PIIX3_ADDR           "piix3-addr"
+
+static const char *valid_qtest_args[] = {
+	ETH_VIRTIO_NET_ARG_QTEST_PATH,
+	ETH_VIRTIO_NET_ARG_IVSHMEM_PATH,
+	ETH_VIRTIO_NET_ARG_VIRTIO_NET_ADDR,
+	ETH_VIRTIO_NET_ARG_IVSHMEM_ADDR,
+	ETH_VIRTIO_NET_ARG_PIIX3_ADDR,
+	NULL
+};
+
+static int
+get_socket_path_arg(const char *key __rte_unused,
+		const char *value, void *extra_args)
+{
+	char **p;
+
+	if ((value == NULL) || (extra_args == NULL))
+		return -EINVAL;
+
+	p = extra_args;
+	*p = strdup(value);
+
+	if (*p == NULL)
+		return -1;
+
+	return 0;
+}
+
+static int
+get_pci_addr_arg(const char *key __rte_unused,
+		const char *value, void *extra_args)
+{
+	struct rte_pci_addr *addr = extra_args;
+
+	if ((value == NULL) || (extra_args == NULL))
+		return -EINVAL;
+
+	if (eal_parse_pci_DomBDF(value, addr) != 0)
+		return -1;
+
+	if (addr->domain != 0)
+		return -1;
+
+	return 0;
+}
+
+static int
+virtio_net_eth_dev_free(struct rte_eth_dev *eth_dev)
+{
+	struct virtio_hw *hw;
+	int ret;
+
+	ret = rte_eth_dev_release_port(eth_dev);
+	if (ret < 0) {
+		PMD_INIT_LOG(ERR, "cannot release a port\n");
+		return -1;
+	}
+
+	hw = eth_dev->data->dev_private;
+	rte_free(hw);
+
+	return 0;
+}
+
+static struct rte_eth_dev *
+virtio_net_eth_dev_alloc(const char *name)
+{
+	struct rte_eth_dev *eth_dev;
+	struct rte_eth_dev_data *data;
+	struct virtio_hw *hw;
+	int ret;
+
+	eth_dev = rte_eth_dev_allocate(name, RTE_ETH_DEV_VIRTUAL);
+	if (eth_dev == NULL) {
+		PMD_INIT_LOG(ERR, "cannot alloc a port\n");
+		return NULL;
+	}
+
+	data = eth_dev->data;
+
+	hw = rte_zmalloc(NULL, sizeof(*hw), 0);
+	if (hw == NULL) {
+		PMD_INIT_LOG(ERR, "malloc virtio_hw failed\n");
+		ret = rte_eth_dev_release_port(eth_dev);
+		if (ret < 0)
+			rte_panic("cannot release a port");
+		return NULL;
+	}
+
+	data->dev_private = hw;
+	eth_dev->driver = &rte_virtio_pmd;
+	return eth_dev;
+}
+
+static int
+virtio_net_eth_pmd_parse_socket_path(struct rte_kvargs *kvlist,
+		const char *option, char **path)
+{
+	int ret;
+
+	if (rte_kvargs_count(kvlist, option) == 1) {
+		ret = rte_kvargs_process(kvlist, option,
+				&get_socket_path_arg, path);
+		if (ret != 0) {
+			PMD_INIT_LOG(ERR,
+					"Failed to connect to %s socket", option);
+			return -1;
+		}
+	} else {
+		PMD_INIT_LOG(ERR, "No argument specified for %s", option);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_net_eth_pmd_parse_pci_addr(struct rte_kvargs *kvlist,
+		const char *option, struct rte_pci_addr *addr,
+		struct rte_pci_addr *default_addr)
+{
+	int ret;
+
+	if (rte_kvargs_count(kvlist, option) == 1) {
+		ret = rte_kvargs_process(kvlist, option,
+				&get_pci_addr_arg, addr);
+		if (ret != 0) {
+			PMD_INIT_LOG(ERR,
+					"Specified invalid address in '%s'", option);
+			return -1;
+		}
+	} else
+		/* copy default pci address */
+		*addr = *default_addr;
+
+	return 0;
+}
+
+static int
+virtio_prepare_target_devices(struct qtest_pci_device *devices,
+			struct rte_kvargs *kvlist)
+{
+	struct qtest_pci_device *virtio_net, *ivshmem, *piix3;
+	struct rte_pci_addr default_addr;
+	const struct rte_memseg *ms;
+	int ret;
+
+	ms = rte_eal_get_physmem_layout();
+	/* if EAL memory size isn't pow of 2, ivshmem will refuse it */
+	if ((ms[0].len & (ms[0].len - 1)) != 0) {
+		PMD_DRV_LOG(ERR, "memory size must be power of 2\n");
+		return -1;
+	}
+
+	virtio_net = &devices[0];
+	ivshmem = &devices[1];
+	piix3 = &devices[2];
+
+	virtio_net->name = "virtio-net";
+	virtio_net->device_id = VIRTIO_NET_DEVICE_ID;
+	virtio_net->vendor_id = VIRTIO_NET_VENDOR_ID;
+	virtio_net->init = qtest_init_pci_device;
+	virtio_net->bar[0].addr = PCI_BASE_ADDRESS_0;
+	virtio_net->bar[0].type = QTEST_PCI_BAR_IO;
+	virtio_net->bar[0].region_start = VIRTIO_NET_IO_START;
+	virtio_net->bar[1].addr = PCI_BASE_ADDRESS_1;
+	virtio_net->bar[1].type = QTEST_PCI_BAR_MEMORY_32;
+	virtio_net->bar[1].region_start = VIRTIO_NET_MEMORY1_START;
+	virtio_net->bar[4].addr = PCI_BASE_ADDRESS_4;
+	virtio_net->bar[4].type = QTEST_PCI_BAR_MEMORY_64;
+	virtio_net->bar[4].region_start = VIRTIO_NET_MEMORY2_START;
+
+	ivshmem->name = "ivshmem";
+	ivshmem->device_id = IVSHMEM_DEVICE_ID;
+	ivshmem->vendor_id = IVSHMEM_VENDOR_ID;
+	ivshmem->init = qtest_init_pci_device;
+	ivshmem->bar[0].addr = PCI_BASE_ADDRESS_0;
+	ivshmem->bar[0].type = QTEST_PCI_BAR_MEMORY_32;
+	ivshmem->bar[0].region_start = IVSHMEM_MEMORY_START;
+	ivshmem->bar[2].addr = PCI_BASE_ADDRESS_2;
+	ivshmem->bar[2].type = QTEST_PCI_BAR_MEMORY_64;
+	/* In host mode, only one memory segment is vaild */
+	ivshmem->bar[2].region_start = (uint64_t)ms[0].addr;
+
+	/* piix3 is needed to route irqs from virtio-net to ioapic */
+	piix3->name = "piix3";
+	piix3->device_id = PIIX3_DEVICE_ID;
+	piix3->vendor_id = PIIX3_VENDOR_ID;
+	piix3->init = qtest_init_piix3_device;
+
+	/*
+	 * Set pci addresses specified by command line.
+	 * QTest utils will only check specified pci address.
+	 * If it's wrong, a target device won't be found.
+	 */
+	default_addr.domain = 0;
+	default_addr.bus = 0;
+	default_addr.function = 0;
+
+	default_addr.devid = 3;
+	ret = virtio_net_eth_pmd_parse_pci_addr(kvlist,
+			ETH_VIRTIO_NET_ARG_VIRTIO_NET_ADDR,
+			&virtio_net->specified_addr, &default_addr);
+	if (ret < 0)
+		return -1;
+
+	default_addr.devid = 4;
+	ret = virtio_net_eth_pmd_parse_pci_addr(kvlist,
+			ETH_VIRTIO_NET_ARG_IVSHMEM_ADDR,
+			&ivshmem->specified_addr, &default_addr);
+	if (ret < 0)
+		return -1;
+
+	default_addr.devid = 1;
+	ret = virtio_net_eth_pmd_parse_pci_addr(kvlist,
+			ETH_VIRTIO_NET_ARG_PIIX3_ADDR,
+			&piix3->specified_addr, &default_addr);
+	if (ret < 0)
+		return -1;
+
+	return 0;
+}
+/*
+ * Initialization when "CONFIG_RTE_VIRTIO_VDEV_QTEST" is enabled.
+ */
+static int
+rte_qtest_virtio_pmd_init(const char *name, const char *params)
+{
+	struct rte_kvargs *kvlist;
+	struct virtio_hw *hw = NULL;
+	struct rte_eth_dev *eth_dev = NULL;
+	char *qtest_path = NULL, *ivshmem_path = NULL;
+	struct qtest_pci_device devices[QTEST_DEVICE_NUM];
+	int ret;
+
+	if (params == NULL || params[0] == '\0')
+		return -EINVAL;
+
+	kvlist = rte_kvargs_parse(params, valid_qtest_args);
+	if (kvlist == NULL) {
+		PMD_INIT_LOG(ERR, "error when parsing param");
+		return -EFAULT;
+	}
+
+	ret = virtio_net_eth_pmd_parse_socket_path(kvlist,
+			ETH_VIRTIO_NET_ARG_IVSHMEM_PATH, &ivshmem_path);
+	if (ret < 0)
+		goto error;
+
+	ret = virtio_net_eth_pmd_parse_socket_path(kvlist,
+			ETH_VIRTIO_NET_ARG_QTEST_PATH, &qtest_path);
+	if (ret < 0)
+		goto error;
+
+	ret = virtio_prepare_target_devices(devices, kvlist);
+	if (ret < 0)
+		goto error;
+
+	eth_dev = virtio_net_eth_dev_alloc(name);
+	if (eth_dev == NULL)
+		goto error;
+
+	hw = eth_dev->data->dev_private;
+	hw->qsession = qtest_vdev_init(qtest_path, ivshmem_path,
+			VIRTIO_NET_IRQ_NUM, devices, QTEST_DEVICE_NUM);
+	if (hw->qsession == NULL)
+		goto error;
+
+	/* originally, this will be called in rte_eal_pci_probe() */
+	ret = eth_virtio_dev_init(eth_dev);
+	if (ret < 0)
+		goto error;
+
+	eth_dev->driver = NULL;
+	eth_dev->data->dev_flags |= RTE_ETH_DEV_DETACHABLE;
+	eth_dev->data->kdrv = RTE_KDRV_NONE;
+	eth_dev->data->drv_name = QTEST_DRV_NAME;
+
+	free(qtest_path);
+	free(ivshmem_path);
+	rte_kvargs_free(kvlist);
+	return 0;
+
+error:
+	if (hw != NULL && hw->qsession != NULL)
+		qtest_vdev_uninit(hw->qsession);
+	if (eth_dev)
+		virtio_net_eth_dev_free(eth_dev);
+	if (qtest_path)
+		free(qtest_path);
+	if (ivshmem_path)
+		free(ivshmem_path);
+	rte_kvargs_free(kvlist);
+	return -EFAULT;
+}
+
+/*
+ * Finalization when "CONFIG_RTE_VIRTIO_VDEV_QTEST" is enabled.
+ */
+static int
+rte_qtest_virtio_pmd_uninit(const char *name)
+{
+	struct rte_eth_dev *eth_dev;
+	struct virtio_hw *hw;
+	int ret;
+
+	if (name == NULL)
+		return -EINVAL;
+
+	/* find the ethdev entry */
+	eth_dev = rte_eth_dev_allocated(name);
+	if (eth_dev == NULL)
+		return -ENODEV;
+
+	ret = eth_virtio_dev_uninit(eth_dev);
+	if (ret != 0)
+		return -EFAULT;
+
+	hw = eth_dev->data->dev_private;
+	qtest_vdev_uninit(hw->qsession);
+
+	ret = virtio_net_eth_dev_free(eth_dev);
+	if (ret != 0)
+		return -EFAULT;
+
+	return 0;
+}
+
+static struct rte_driver rte_qtest_virtio_driver = {
+	.name   = QTEST_DRV_NAME,
+	.type   = PMD_VDEV,
+	.init   = rte_qtest_virtio_pmd_init,
+	.uninit = rte_qtest_virtio_pmd_uninit,
+};
+
+PMD_REGISTER_DRIVER(rte_qtest_virtio_driver);
+#endif /* RTE_VIRTIO_VDEV_QTEST */
