All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 0/2] Introduce virtio-vhost-user device
@ 2022-05-19  9:43 Usama Arif
  2022-05-19  9:43 ` [RFC 1/2] vhost-user: share the vhost-user protocol related structures Usama Arif
  2022-05-19  9:43 ` [RFC 2/2] virtio-vhost-user: add virtio-vhost-user device Usama Arif
  0 siblings, 2 replies; 3+ messages in thread
From: Usama Arif @ 2022-05-19  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: mst, stefanha, ndragazis, fam.zheng, liangma, Usama Arif

The virtio-vhost-user (vvu) allows moving the vhost-user process inside
a VM. This is done by moving vhost device backend into a guest and
tunneling the vhost-user protocol over a new type of device called
virtio-vhost-user.

A usecase for this is live-updating the host kernel which has the DPDK
application running inside a VM with vvu device. The DPDK application
doesn't need to be restarted after the host kernel has been updated
as the DPDK VM is snapshotted before host kernel update, allowing DPDK
state to be saved.
There should potentially be further usecases, as it allows isolating the
vhost-user process inside a VM, with more components like SPDK.

vvu is based on the work initially done by Stefan Hajnoczi [1]
and continued by Nikos Dragazis [2], [3].

The virtio-spec changes for vvu are currently in review[4]. A HTML
version with the changes is available at [5].

A working prototype showcasing vvu can be reproduced using
instructions in [6].

Thanks and looking forward to your response!
Usama

[1] https://wiki.qemu.org/Features/VirtioVhostUser
[2] https://ndragazis.github.io/dpdk-vhost-vvu-demo.html
[3] https://lists.oasis-open.org/archives/virtio-dev/202005/msg00132.html
[4] https://lists.oasis-open.org/archives/virtio-dev/202204/msg00022.html
[5] https://uarif1.github.io/vvu/v2/virtio-v1.1-cs01
[6] https://uarif1.github.io/vvu/dpdk-vvu-instructions


Usama Arif (2):
  vhost-user: share the vhost-user protocol related structures
  virtio-vhost-user: add virtio-vhost-user device

 hw/virtio/Kconfig                           |    5 +
 hw/virtio/meson.build                       |    2 +
 hw/virtio/trace-events                      |   26 +
 hw/virtio/vhost-user.c                      |  160 ---
 hw/virtio/virtio-pci.c                      |   13 +-
 hw/virtio/virtio-vhost-user-pci.c           |  471 ++++++++
 hw/virtio/virtio-vhost-user.c               | 1066 +++++++++++++++++++
 hw/virtio/virtio.c                          |    7 +-
 include/hw/pci/pci.h                        |    1 +
 include/hw/virtio/vhost-user.h              |  163 +++
 include/hw/virtio/virtio-pci.h              |    7 +
 include/hw/virtio/virtio-vhost-user.h       |  126 +++
 include/hw/virtio/virtio.h                  |    2 +
 include/standard-headers/linux/virtio_ids.h |    1 +
 14 files changed, 1880 insertions(+), 170 deletions(-)
 create mode 100644 hw/virtio/virtio-vhost-user-pci.c
 create mode 100644 hw/virtio/virtio-vhost-user.c
 create mode 100644 include/hw/virtio/virtio-vhost-user.h

-- 
2.25.1



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

* [RFC 1/2] vhost-user: share the vhost-user protocol related structures
  2022-05-19  9:43 [RFC 0/2] Introduce virtio-vhost-user device Usama Arif
@ 2022-05-19  9:43 ` Usama Arif
  2022-05-19  9:43 ` [RFC 2/2] virtio-vhost-user: add virtio-vhost-user device Usama Arif
  1 sibling, 0 replies; 3+ messages in thread
From: Usama Arif @ 2022-05-19  9:43 UTC (permalink / raw)
  To: qemu-devel
  Cc: mst, stefanha, ndragazis, fam.zheng, liangma, Usama Arif, Wei Wang

Put the vhost-user protocol related data structures to vhost-user.h,
so that they can be used in other implementations (e.g. a backend
implementation).

Signed-off-by: Wei Wang <wei.w.wang@intel.com>
Signed-off-by: Usama Arif <usama.arif@bytedance.com>
---
 hw/virtio/vhost-user.c         | 160 --------------------------------
 include/hw/virtio/vhost-user.h | 163 +++++++++++++++++++++++++++++++++
 2 files changed, 163 insertions(+), 160 deletions(-)

diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index b040c1ad2b..cf06c3604c 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -21,7 +21,6 @@
 #include "qemu/error-report.h"
 #include "qemu/main-loop.h"
 #include "qemu/sockets.h"
-#include "sysemu/cryptodev.h"
 #include "migration/migration.h"
 #include "migration/postcopy-ram.h"
 #include "trace.h"
@@ -37,10 +36,6 @@
 #include <linux/userfaultfd.h>
 #endif
 
-#define VHOST_MEMORY_BASELINE_NREGIONS    8
-#define VHOST_USER_F_PROTOCOL_FEATURES 30
-#define VHOST_USER_SLAVE_MAX_FDS     8
-
 /*
  * Set maximum number of RAM slots supported to
  * the maximum number supported by the target
@@ -59,11 +54,6 @@
 #define VHOST_USER_MAX_RAM_SLOTS 512
 #endif
 
-/*
- * Maximum size of virtio device config space
- */
-#define VHOST_USER_MAX_CONFIG_SIZE 256
-
 enum VhostUserProtocolFeature {
     VHOST_USER_PROTOCOL_F_MQ = 0,
     VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
@@ -84,157 +74,7 @@ enum VhostUserProtocolFeature {
     VHOST_USER_PROTOCOL_F_MAX
 };
 
-#define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1)
-
-typedef enum VhostUserRequest {
-    VHOST_USER_NONE = 0,
-    VHOST_USER_GET_FEATURES = 1,
-    VHOST_USER_SET_FEATURES = 2,
-    VHOST_USER_SET_OWNER = 3,
-    VHOST_USER_RESET_OWNER = 4,
-    VHOST_USER_SET_MEM_TABLE = 5,
-    VHOST_USER_SET_LOG_BASE = 6,
-    VHOST_USER_SET_LOG_FD = 7,
-    VHOST_USER_SET_VRING_NUM = 8,
-    VHOST_USER_SET_VRING_ADDR = 9,
-    VHOST_USER_SET_VRING_BASE = 10,
-    VHOST_USER_GET_VRING_BASE = 11,
-    VHOST_USER_SET_VRING_KICK = 12,
-    VHOST_USER_SET_VRING_CALL = 13,
-    VHOST_USER_SET_VRING_ERR = 14,
-    VHOST_USER_GET_PROTOCOL_FEATURES = 15,
-    VHOST_USER_SET_PROTOCOL_FEATURES = 16,
-    VHOST_USER_GET_QUEUE_NUM = 17,
-    VHOST_USER_SET_VRING_ENABLE = 18,
-    VHOST_USER_SEND_RARP = 19,
-    VHOST_USER_NET_SET_MTU = 20,
-    VHOST_USER_SET_SLAVE_REQ_FD = 21,
-    VHOST_USER_IOTLB_MSG = 22,
-    VHOST_USER_SET_VRING_ENDIAN = 23,
-    VHOST_USER_GET_CONFIG = 24,
-    VHOST_USER_SET_CONFIG = 25,
-    VHOST_USER_CREATE_CRYPTO_SESSION = 26,
-    VHOST_USER_CLOSE_CRYPTO_SESSION = 27,
-    VHOST_USER_POSTCOPY_ADVISE  = 28,
-    VHOST_USER_POSTCOPY_LISTEN  = 29,
-    VHOST_USER_POSTCOPY_END     = 30,
-    VHOST_USER_GET_INFLIGHT_FD = 31,
-    VHOST_USER_SET_INFLIGHT_FD = 32,
-    VHOST_USER_GPU_SET_SOCKET = 33,
-    VHOST_USER_RESET_DEVICE = 34,
-    /* Message number 35 reserved for VHOST_USER_VRING_KICK. */
-    VHOST_USER_GET_MAX_MEM_SLOTS = 36,
-    VHOST_USER_ADD_MEM_REG = 37,
-    VHOST_USER_REM_MEM_REG = 38,
-    VHOST_USER_MAX
-} VhostUserRequest;
-
-typedef enum VhostUserSlaveRequest {
-    VHOST_USER_SLAVE_NONE = 0,
-    VHOST_USER_SLAVE_IOTLB_MSG = 1,
-    VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
-    VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3,
-    VHOST_USER_SLAVE_MAX
-}  VhostUserSlaveRequest;
-
-typedef struct VhostUserMemoryRegion {
-    uint64_t guest_phys_addr;
-    uint64_t memory_size;
-    uint64_t userspace_addr;
-    uint64_t mmap_offset;
-} VhostUserMemoryRegion;
-
-typedef struct VhostUserMemory {
-    uint32_t nregions;
-    uint32_t padding;
-    VhostUserMemoryRegion regions[VHOST_MEMORY_BASELINE_NREGIONS];
-} VhostUserMemory;
-
-typedef struct VhostUserMemRegMsg {
-    uint64_t padding;
-    VhostUserMemoryRegion region;
-} VhostUserMemRegMsg;
-
-typedef struct VhostUserLog {
-    uint64_t mmap_size;
-    uint64_t mmap_offset;
-} VhostUserLog;
-
-typedef struct VhostUserConfig {
-    uint32_t offset;
-    uint32_t size;
-    uint32_t flags;
-    uint8_t region[VHOST_USER_MAX_CONFIG_SIZE];
-} VhostUserConfig;
-
-#define VHOST_CRYPTO_SYM_HMAC_MAX_KEY_LEN    512
-#define VHOST_CRYPTO_SYM_CIPHER_MAX_KEY_LEN  64
-
-typedef struct VhostUserCryptoSession {
-    /* session id for success, -1 on errors */
-    int64_t session_id;
-    CryptoDevBackendSymSessionInfo session_setup_data;
-    uint8_t key[VHOST_CRYPTO_SYM_CIPHER_MAX_KEY_LEN];
-    uint8_t auth_key[VHOST_CRYPTO_SYM_HMAC_MAX_KEY_LEN];
-} VhostUserCryptoSession;
-
-static VhostUserConfig c __attribute__ ((unused));
-#define VHOST_USER_CONFIG_HDR_SIZE (sizeof(c.offset) \
-                                   + sizeof(c.size) \
-                                   + sizeof(c.flags))
-
-typedef struct VhostUserVringArea {
-    uint64_t u64;
-    uint64_t size;
-    uint64_t offset;
-} VhostUserVringArea;
-
-typedef struct VhostUserInflight {
-    uint64_t mmap_size;
-    uint64_t mmap_offset;
-    uint16_t num_queues;
-    uint16_t queue_size;
-} VhostUserInflight;
-
-typedef struct {
-    VhostUserRequest request;
-
-#define VHOST_USER_VERSION_MASK     (0x3)
-#define VHOST_USER_REPLY_MASK       (0x1<<2)
-#define VHOST_USER_NEED_REPLY_MASK  (0x1 << 3)
-    uint32_t flags;
-    uint32_t size; /* the following payload size */
-} QEMU_PACKED VhostUserHeader;
-
-typedef union {
-#define VHOST_USER_VRING_IDX_MASK   (0xff)
-#define VHOST_USER_VRING_NOFD_MASK  (0x1<<8)
-        uint64_t u64;
-        struct vhost_vring_state state;
-        struct vhost_vring_addr addr;
-        VhostUserMemory memory;
-        VhostUserMemRegMsg mem_reg;
-        VhostUserLog log;
-        struct vhost_iotlb_msg iotlb;
-        VhostUserConfig config;
-        VhostUserCryptoSession session;
-        VhostUserVringArea area;
-        VhostUserInflight inflight;
-} VhostUserPayload;
-
-typedef struct VhostUserMsg {
-    VhostUserHeader hdr;
-    VhostUserPayload payload;
-} QEMU_PACKED VhostUserMsg;
-
 static VhostUserMsg m __attribute__ ((unused));
-#define VHOST_USER_HDR_SIZE (sizeof(VhostUserHeader))
-
-#define VHOST_USER_PAYLOAD_SIZE (sizeof(VhostUserPayload))
-
-/* The version of the protocol we support */
-#define VHOST_USER_VERSION    (0x1)
-
 struct vhost_user {
     struct vhost_dev *dev;
     /* Shared between vhost devs of the same virtio device */
diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h
index c6e693cd3f..892020a45d 100644
--- a/include/hw/virtio/vhost-user.h
+++ b/include/hw/virtio/vhost-user.h
@@ -10,6 +10,169 @@
 
 #include "chardev/char-fe.h"
 #include "hw/virtio/virtio.h"
+#include <linux/vhost.h>
+#include "sysemu/cryptodev.h"
+
+#define VHOST_MEMORY_BASELINE_NREGIONS    8
+#define VHOST_USER_F_PROTOCOL_FEATURES 30
+#define VHOST_USER_SLAVE_MAX_FDS     8
+
+/*
+ * Maximum size of virtio device config space
+ */
+#define VHOST_USER_MAX_CONFIG_SIZE 256
+
+#define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1)
+
+#define VHOST_MEMORY_MAX_NREGIONS    8
+
+typedef enum VhostUserRequest {
+    VHOST_USER_NONE = 0,
+    VHOST_USER_GET_FEATURES = 1,
+    VHOST_USER_SET_FEATURES = 2,
+    VHOST_USER_SET_OWNER = 3,
+    VHOST_USER_RESET_OWNER = 4,
+    VHOST_USER_SET_MEM_TABLE = 5,
+    VHOST_USER_SET_LOG_BASE = 6,
+    VHOST_USER_SET_LOG_FD = 7,
+    VHOST_USER_SET_VRING_NUM = 8,
+    VHOST_USER_SET_VRING_ADDR = 9,
+    VHOST_USER_SET_VRING_BASE = 10,
+    VHOST_USER_GET_VRING_BASE = 11,
+    VHOST_USER_SET_VRING_KICK = 12,
+    VHOST_USER_SET_VRING_CALL = 13,
+    VHOST_USER_SET_VRING_ERR = 14,
+    VHOST_USER_GET_PROTOCOL_FEATURES = 15,
+    VHOST_USER_SET_PROTOCOL_FEATURES = 16,
+    VHOST_USER_GET_QUEUE_NUM = 17,
+    VHOST_USER_SET_VRING_ENABLE = 18,
+    VHOST_USER_SEND_RARP = 19,
+    VHOST_USER_NET_SET_MTU = 20,
+    VHOST_USER_SET_SLAVE_REQ_FD = 21,
+    VHOST_USER_IOTLB_MSG = 22,
+    VHOST_USER_SET_VRING_ENDIAN = 23,
+    VHOST_USER_GET_CONFIG = 24,
+    VHOST_USER_SET_CONFIG = 25,
+    VHOST_USER_CREATE_CRYPTO_SESSION = 26,
+    VHOST_USER_CLOSE_CRYPTO_SESSION = 27,
+    VHOST_USER_POSTCOPY_ADVISE  = 28,
+    VHOST_USER_POSTCOPY_LISTEN  = 29,
+    VHOST_USER_POSTCOPY_END     = 30,
+    VHOST_USER_GET_INFLIGHT_FD = 31,
+    VHOST_USER_SET_INFLIGHT_FD = 32,
+    VHOST_USER_GPU_SET_SOCKET = 33,
+    VHOST_USER_RESET_DEVICE = 34,
+    /* Message number 35 reserved for VHOST_USER_VRING_KICK. */
+    VHOST_USER_GET_MAX_MEM_SLOTS = 36,
+    VHOST_USER_ADD_MEM_REG = 37,
+    VHOST_USER_REM_MEM_REG = 38,
+    VHOST_USER_MAX
+} VhostUserRequest;
+
+typedef enum VhostUserSlaveRequest {
+    VHOST_USER_SLAVE_NONE = 0,
+    VHOST_USER_SLAVE_IOTLB_MSG = 1,
+    VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
+    VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3,
+    VHOST_USER_SLAVE_MAX
+}  VhostUserSlaveRequest;
+
+typedef struct VhostUserMemoryRegion {
+    uint64_t guest_phys_addr;
+    uint64_t memory_size;
+    uint64_t userspace_addr;
+    uint64_t mmap_offset;
+} VhostUserMemoryRegion;
+
+typedef struct VhostUserMemory {
+    uint32_t nregions;
+    uint32_t padding;
+    VhostUserMemoryRegion regions[VHOST_MEMORY_BASELINE_NREGIONS];
+} VhostUserMemory;
+
+typedef struct VhostUserMemRegMsg {
+    uint64_t padding;
+    VhostUserMemoryRegion region;
+} VhostUserMemRegMsg;
+
+typedef struct VhostUserLog {
+    uint64_t mmap_size;
+    uint64_t mmap_offset;
+} VhostUserLog;
+
+typedef struct VhostUserConfig {
+    uint32_t offset;
+    uint32_t size;
+    uint32_t flags;
+    uint8_t region[VHOST_USER_MAX_CONFIG_SIZE];
+} VhostUserConfig;
+
+#define VHOST_CRYPTO_SYM_HMAC_MAX_KEY_LEN    512
+#define VHOST_CRYPTO_SYM_CIPHER_MAX_KEY_LEN  64
+
+typedef struct VhostUserCryptoSession {
+    /* session id for success, -1 on errors */
+    int64_t session_id;
+    CryptoDevBackendSymSessionInfo session_setup_data;
+    uint8_t key[VHOST_CRYPTO_SYM_CIPHER_MAX_KEY_LEN];
+    uint8_t auth_key[VHOST_CRYPTO_SYM_HMAC_MAX_KEY_LEN];
+} VhostUserCryptoSession;
+
+static VhostUserConfig c __attribute__ ((unused));
+#define VHOST_USER_CONFIG_HDR_SIZE (sizeof(c.offset) \
+                                   + sizeof(c.size) \
+                                   + sizeof(c.flags))
+
+typedef struct VhostUserVringArea {
+    uint64_t u64;
+    uint64_t size;
+    uint64_t offset;
+} VhostUserVringArea;
+
+typedef struct VhostUserInflight {
+    uint64_t mmap_size;
+    uint64_t mmap_offset;
+    uint16_t num_queues;
+    uint16_t queue_size;
+} VhostUserInflight;
+
+typedef struct {
+    VhostUserRequest request;
+
+#define VHOST_USER_VERSION_MASK     (0x3)
+#define VHOST_USER_REPLY_MASK       (0x1<<2)
+#define VHOST_USER_NEED_REPLY_MASK  (0x1 << 3)
+    uint32_t flags;
+    uint32_t size; /* the following payload size */
+} QEMU_PACKED VhostUserHeader;
+
+typedef union {
+#define VHOST_USER_VRING_IDX_MASK   (0xff)
+#define VHOST_USER_VRING_NOFD_MASK  (0x1<<8)
+        uint64_t u64;
+        struct vhost_vring_state state;
+        struct vhost_vring_addr addr;
+        VhostUserMemory memory;
+        VhostUserMemRegMsg mem_reg;
+        VhostUserLog log;
+        struct vhost_iotlb_msg iotlb;
+        VhostUserConfig config;
+        VhostUserCryptoSession session;
+        VhostUserVringArea area;
+        VhostUserInflight inflight;
+} VhostUserPayload;
+
+typedef struct VhostUserMsg {
+    VhostUserHeader hdr;
+    VhostUserPayload payload;
+} QEMU_PACKED VhostUserMsg;
+
+#define VHOST_USER_HDR_SIZE (sizeof(VhostUserHeader))
+
+#define VHOST_USER_PAYLOAD_SIZE (sizeof(VhostUserPayload))
+
+/* The version of the protocol we support */
+#define VHOST_USER_VERSION    (0x1)
 
 /**
  * VhostUserHostNotifier - notifier information for one queue
-- 
2.25.1



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

* [RFC 2/2] virtio-vhost-user: add virtio-vhost-user device
  2022-05-19  9:43 [RFC 0/2] Introduce virtio-vhost-user device Usama Arif
  2022-05-19  9:43 ` [RFC 1/2] vhost-user: share the vhost-user protocol related structures Usama Arif
@ 2022-05-19  9:43 ` Usama Arif
  1 sibling, 0 replies; 3+ messages in thread
From: Usama Arif @ 2022-05-19  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: mst, stefanha, ndragazis, fam.zheng, liangma, Usama Arif

The virtio-vhost-user device lets a guest act as a vhost device backend.
It works by tunneling vhost-user protocol messages into a guest.  The
new device syntax is as follows:

  -chardev socket,id=chardev0,path=vhost-user.sock,server=on,wait=off \
  -device virtio-vhost-user-pci,chardev=chardev0

The VIRTIO device specification is here:
https://uarif1.github.io/vvu/v2/virtio-v1.1-cs01

For more information about virtio-vhost-user, see
https://wiki.qemu.org/Features/VirtioVhostUser.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Nikos Dragazis <ndragazis@arrikto.com>
Signed-off-by: Usama Arif <usama.arif@bytedance.com>
---
 hw/virtio/Kconfig                           |    5 +
 hw/virtio/meson.build                       |    2 +
 hw/virtio/trace-events                      |   26 +
 hw/virtio/virtio-pci.c                      |   13 +-
 hw/virtio/virtio-vhost-user-pci.c           |  471 ++++++++
 hw/virtio/virtio-vhost-user.c               | 1066 +++++++++++++++++++
 hw/virtio/virtio.c                          |    7 +-
 include/hw/pci/pci.h                        |    1 +
 include/hw/virtio/virtio-pci.h              |    7 +
 include/hw/virtio/virtio-vhost-user.h       |  126 +++
 include/hw/virtio/virtio.h                  |    2 +
 include/standard-headers/linux/virtio_ids.h |    1 +
 12 files changed, 1717 insertions(+), 10 deletions(-)
 create mode 100644 hw/virtio/virtio-vhost-user-pci.c
 create mode 100644 hw/virtio/virtio-vhost-user.c
 create mode 100644 include/hw/virtio/virtio-vhost-user.h

diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
index e9ecae1f50..813bfc4600 100644
--- a/hw/virtio/Kconfig
+++ b/hw/virtio/Kconfig
@@ -80,3 +80,8 @@ config VHOST_USER_FS
     bool
     default y
     depends on VIRTIO && VHOST_USER
+
+config VIRTIO_VHOST_USER
+    bool
+    default y
+    depends on VIRTIO && VHOST_USER
\ No newline at end of file
diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
index 7e8877fd64..11ed09d5ff 100644
--- a/hw/virtio/meson.build
+++ b/hw/virtio/meson.build
@@ -29,6 +29,7 @@ virtio_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: files('virtio-iommu.c'))
 virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c'))
 virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c'))
 virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c'))
+virtio_ss.add(when: 'CONFIG_VIRTIO_VHOST_USER', if_true: files('virtio-vhost-user.c'))
 
 virtio_pci_ss = ss.source_set()
 virtio_pci_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-pci.c'))
@@ -54,6 +55,7 @@ virtio_pci_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-serial-pc
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_PMEM', if_true: files('virtio-pmem-pci.c'))
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: files('virtio-iommu-pci.c'))
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem-pci.c'))
+virtio_pci_ss.add(when: 'CONFIG_VIRTIO_VHOST_USER', if_true: files('virtio-vhost-user-pci.c'))
 
 virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', if_true: virtio_pci_ss)
 
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index ab8e095b73..9dccf723ad 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -85,6 +85,32 @@ virtio_balloon_get_config(uint32_t num_pages, uint32_t actual) "num_pages: %d ac
 virtio_balloon_set_config(uint32_t actual, uint32_t oldactual) "actual: %d oldactual: %d"
 virtio_balloon_to_target(uint64_t target, uint32_t num_pages) "balloon target: 0x%"PRIx64" num_pages: %d"
 
+# hw/virtio/virtio-vhost-user.c
+virtio_vhost_user_m2s_bad_version(void *s, unsigned int version) "s %p version %u"
+virtio_vhost_user_m2s_unexpected_reply(void *s) "s %p"
+virtio_vhost_user_m2s_bad_payload_size(void *s, unsigned int size) "s %p size %u"
+virtio_vhost_user_m2s_bad_request(void *s, unsigned request) "s %p request %u"
+virtio_vhost_user_m2s_request(void *s, unsigned int request) "s %p request %u"
+virtio_vhost_user_m2s_unknown_request(void *s, unsigned int request) "s %p request %u"
+virtio_vhost_user_s2m_bad_version(void *s, unsigned int version) "s %p version %u"
+virtio_vhost_user_s2m_expected_reply(void *s) "s %p"
+virtio_vhost_user_s2m_bad_payload_size(void *s, unsigned int size) "s %p size %u"
+virtio_vhost_user_s2m_bad_request(void *s, unsigned request) "s %p request %u"
+virtio_vhost_user_s2m_request(void *s, unsigned int request) "s %p request %u"
+virtio_vhost_user_s2m_unknown_request(void *s, unsigned int request) "s %p request %u"
+virtio_vhost_user_rxq_empty(void *s) "s %p"
+virtio_vhost_user_tx_done(void *s) "s %p"
+virtio_vhost_user_chr_event(void *s, int event) "s %p event %d"
+virtio_vhost_user_chr_change(void *s) "s %p"
+virtio_vhost_user_conn_state_transition(void *s, int old_state, int event, int new_state) "s %p old_state %d event %d new_state %d"
+virtio_vhost_user_set_config(void *s, unsigned int old_status, unsigned int new_status) "s %p old_status %u new_status %u"
+virtio_vhost_user_doorbell_write(void *s, unsigned int vq_idx, ssize_t nwritten) "s %p vq_idx %u nwritten %zd"
+virtio_vhost_user_notification_read(void *s, uint64_t addr, uint64_t return_val) "s %p addr 0x%"PRIx64" return_val 0x%"PRIx64
+virtio_vhost_user_notification_write(void *s, uint64_t addr, uint64_t val) "s %p addr 0x%"PRIx64" val 0x%"PRIx64
+virtio_vhost_user_guest_notifier_read(int kickfd, uint16_t vector) "kickfd %d vector 0x%"PRIx16
+virtio_vhost_user_memory_region(void *s, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, uint64_t mmap_offset, void *mmap_addr) "s %p guest_phys_addr 0x%"PRIx64" memory_size 0x%"PRIx64" userspace_addr 0x%"PRIx64" mmap_offset 0x%"PRIx64" mmap_addr %p"
+
+
 # virtio-mmio.c
 virtio_mmio_read(uint64_t offset) "virtio_mmio_read offset 0x%" PRIx64
 virtio_mmio_write_offset(uint64_t offset, uint64_t value) "virtio_mmio_write offset 0x%" PRIx64 " value 0x%" PRIx64
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 0566ad7d00..9ad5c56388 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -18,7 +18,6 @@
 #include "qemu/osdep.h"
 
 #include "exec/memop.h"
-#include "standard-headers/linux/virtio_pci.h"
 #include "hw/boards.h"
 #include "hw/virtio/virtio.h"
 #include "migration/qemu-file-types.h"
@@ -222,7 +221,7 @@ static bool virtio_pci_ioeventfd_enabled(DeviceState *d)
 
 #define QEMU_VIRTIO_PCI_QUEUE_MEM_MULT 0x1000
 
-static inline int virtio_pci_queue_mem_mult(struct VirtIOPCIProxy *proxy)
+inline int virtio_pci_queue_mem_mult(struct VirtIOPCIProxy *proxy)
 {
     return (proxy->flags & VIRTIO_PCI_FLAG_PAGE_PER_VQ) ?
         QEMU_VIRTIO_PCI_QUEUE_MEM_MULT : 4;
@@ -1558,11 +1557,11 @@ static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy,
                           proxy->notify_pio.size);
 }
 
-static void virtio_pci_modern_region_map(VirtIOPCIProxy *proxy,
-                                         VirtIOPCIRegion *region,
-                                         struct virtio_pci_cap *cap,
-                                         MemoryRegion *mr,
-                                         uint8_t bar)
+void virtio_pci_modern_region_map(VirtIOPCIProxy *proxy,
+                                  VirtIOPCIRegion *region,
+                                  struct virtio_pci_cap *cap,
+                                  MemoryRegion *mr,
+                                  uint8_t bar)
 {
     memory_region_add_subregion(mr, region->offset, &region->mr);
 
diff --git a/hw/virtio/virtio-vhost-user-pci.c b/hw/virtio/virtio-vhost-user-pci.c
new file mode 100644
index 0000000000..b4e0ba735b
--- /dev/null
+++ b/hw/virtio/virtio-vhost-user-pci.c
@@ -0,0 +1,471 @@
+/*
+ * Virtio Vhost-user Device
+ *
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Authors:
+ *  Stefan Hajnoczi   <stefanha@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/virtio-pci.h"
+#include "hw/virtio/virtio-vhost-user.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/pci/msix.h"
+
+typedef struct VirtIOVhostUserPCI VirtIOVhostUserPCI;
+
+/*
+ * virtio-vhost-user-pci: This extends VirtioPCIProxy.
+ */
+
+#define TYPE_VIRTIO_VHOST_USER_PCI "virtio-vhost-user-pci-base"
+#define VIRTIO_VHOST_USER_PCI(obj) OBJECT_CHECK(VirtIOVhostUserPCI, \
+    (obj), TYPE_VIRTIO_VHOST_USER_PCI)
+#define VIRTIO_VHOST_USER_PCI_GET_CLASS(obj) \
+        OBJECT_GET_CLASS(VirtioVhostUserPCIClass, obj, \
+        TYPE_VIRTIO_VHOST_USER_PCI)
+#define VIRTIO_VHOST_USER_PCI_CLASS(klass) \
+        OBJECT_CLASS_CHECK(VirtioVhostUserPCIClass, klass, \
+        TYPE_VIRTIO_VHOST_USER_PCI)
+
+struct VirtIOVhostUserPCI {
+    VirtIOPCIProxy parent_obj;
+    VirtIOVhostUser vdev;
+
+    MemoryRegion additional_resources_bar;
+
+    VirtIOPCIRegion doorbells;
+    VirtIOPCIRegion notifications;
+    VirtIOPCIRegion shared_memory;
+};
+
+typedef struct VirtioVhostUserPCIClass {
+    VirtioPCIClass parent_class;
+
+    void (*set_vhost_mem_regions)(VirtIOVhostUserPCI *vvup);
+    void (*delete_vhost_mem_region)(VirtIOVhostUserPCI *vvup, MemoryRegion *mr);
+    void (*cleanup_bar)(VirtIOVhostUserPCI *vvup);
+    void (*register_doorbell)(VirtIOVhostUserPCI *vvup, EventNotifier *e,
+                              uint8_t vq_idx);
+    void (*unregister_doorbell)(VirtIOVhostUserPCI *vvup, EventNotifier *e,
+                                uint8_t vq_idx);
+} VirtioVhostUserPCIClass;
+
+static Property virtio_vhost_user_pci_properties[] = {
+    DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+                       DEV_NVECTORS_UNSPECIFIED),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+/*
+ * Handler for the frontend kickfd notifications. Inject an INTx or MSI-X
+ * interrupt to the guest in response to the frontend notification. Use the
+ * appropriate vector in the latter case.
+ */
+void virtio_vhost_user_guest_notifier_read(EventNotifier *n)
+{
+    struct kickfd *kickfd = container_of(n, struct kickfd, guest_notifier);
+    VirtIODevice *vdev = kickfd->vdev;
+    VirtIOVhostUser *vvu = container_of(vdev, struct VirtIOVhostUser,
+        parent_obj);
+    VirtIOVhostUserPCI *vvup = container_of(vvu, struct VirtIOVhostUserPCI,
+        vdev);
+    VirtIOPCIProxy *proxy = &vvup->parent_obj;
+    PCIDevice *pci_dev = &proxy->pci_dev;
+
+    if (event_notifier_test_and_clear(n)) {
+        /*
+         * The ISR status register is used only for INTx interrupts. Thus, we
+         * use it only in this case.
+         */
+        if (!msix_enabled(pci_dev)) {
+            virtio_set_isr(vdev, 0x2);
+        }
+        /*
+         * Send an interrupt, either with INTx or MSI-X mechanism. msix_notify()
+         * already handles the case where the MSI-X vector is NO_VECTOR by not
+         * issuing interrupts. Thus, we don't have to check this case here.
+         */
+        virtio_notify_vector(vdev, kickfd->msi_vector);
+
+        trace_virtio_vhost_user_guest_notifier_read(kickfd->guest_notifier.rfd,
+            kickfd->msi_vector);
+    }
+}
+
+static uint64_t virtio_vhost_user_doorbells_read(void *opaque, hwaddr addr,
+                                                 unsigned size)
+{
+    return 0;
+}
+
+static void virtio_vhost_user_doorbells_write(void *opaque, hwaddr addr,
+                                              uint64_t val, unsigned size)
+{
+    VirtIOVhostUserPCI *vvup = opaque;
+    VirtIOPCIProxy *proxy = &vvup->parent_obj;
+    VirtIOVhostUser *s = &vvup->vdev;
+    unsigned idx = addr / virtio_pci_queue_mem_mult(proxy);
+
+    if (idx < VIRTIO_QUEUE_MAX) {
+        /* We shouldn't reach at this point since we are using ioeventfds. */
+        if (event_notifier_get_fd(&s->callfds[idx]) >= 0) {
+            ssize_t nwritten;
+
+            nwritten = event_notifier_set(&s->callfds[idx]);
+            trace_virtio_vhost_user_doorbell_write(s, idx, nwritten);
+
+        }
+    } else if (idx == VIRTIO_QUEUE_MAX) {
+        /* TODO log doorbell */
+   }
+}
+
+static void vvu_register_doorbell(VirtIOVhostUserPCI *vvup, EventNotifier *e,
+                                  uint8_t vq_idx)
+{
+    VirtIOPCIProxy *proxy = &vvup->parent_obj;
+    hwaddr addr = vq_idx * virtio_pci_queue_mem_mult(proxy);
+
+    /* Register the callfd EventNotifier as ioeventfd */
+    memory_region_add_eventfd(&vvup->doorbells.mr, addr, 2, false, vq_idx, e);
+}
+
+void virtio_vhost_user_register_doorbell(VirtIOVhostUser *s, EventNotifier *e,
+                                         uint8_t vq_idx)
+{
+    VirtIOVhostUserPCI *vvup = container_of(s, struct VirtIOVhostUserPCI, vdev);
+    VirtioVhostUserPCIClass *vvup_class = VIRTIO_VHOST_USER_PCI_GET_CLASS(vvup);
+
+    vvup_class->register_doorbell(vvup, e, vq_idx);
+}
+
+static void vvu_unregister_doorbell(VirtIOVhostUserPCI *vvup, EventNotifier *e,
+                                    uint8_t vq_idx)
+{
+    VirtIOPCIProxy *proxy = &vvup->parent_obj;
+    hwaddr addr = vq_idx * virtio_pci_queue_mem_mult(proxy);
+
+    /* Unregister the callfd EventNotifier */
+    memory_region_del_eventfd(&vvup->doorbells.mr, addr, 2, false, vq_idx, e);
+}
+
+void virtio_vhost_user_unregister_doorbell(VirtIOVhostUser *s, EventNotifier *e,
+                                           uint8_t vq_idx)
+{
+    VirtIOVhostUserPCI *vvup = container_of(s, struct VirtIOVhostUserPCI, vdev);
+    VirtioVhostUserPCIClass *vvup_class = VIRTIO_VHOST_USER_PCI_GET_CLASS(vvup);
+
+    vvup_class->unregister_doorbell(vvup, e, vq_idx);
+}
+
+static uint64_t virtio_vhost_user_notification_read(void *opaque, hwaddr addr,
+                                               unsigned size)
+{
+    VirtIOVhostUserPCI *vvup = opaque;
+    VirtIOVhostUser *s = &vvup->vdev;
+    uint64_t val = 0;
+
+    switch (addr) {
+    case NOTIFICATION_SELECT:
+           val = s->nselect;
+           break;
+    case NOTIFICATION_MSIX_VECTOR:
+            if (s->nselect < ARRAY_SIZE(s->kickfds)) {
+                val = s->kickfds[s->nselect].msi_vector;
+            }
+           break;
+    default:
+           break;
+    }
+
+    trace_virtio_vhost_user_notification_read(s, addr, val);
+
+    return val;
+}
+
+/* Set the MSI vectors for the frontend virtqueue notifications. */
+static void virtio_vhost_user_notification_write(void *opaque, hwaddr addr,
+                                               uint64_t val, unsigned size)
+{
+   /*
+    * MMIO regions are byte-addressable. The value of the `addr` argument is
+    * relative to the starting address of the MMIO region. For example,
+    * `addr = 6` means that the 6th byte of this MMIO region has been written.
+    */
+    VirtIOVhostUserPCI *vvup = opaque;
+    VirtIOPCIProxy *proxy = &vvup->parent_obj;
+    VirtIOVhostUser *s = &vvup->vdev;
+
+    switch (addr) {
+    case NOTIFICATION_SELECT:
+       if (val < VIRTIO_QUEUE_MAX) {
+            s->nselect = val;
+       }
+       break;
+    case NOTIFICATION_MSIX_VECTOR:
+        msix_vector_unuse(&proxy->pci_dev, s->kickfds[s->nselect].msi_vector);
+        if (msix_vector_use(&proxy->pci_dev, val) < 0) {
+            val = VIRTIO_NO_VECTOR;
+        }
+        s->kickfds[s->nselect].msi_vector = val;
+       break;
+    default:
+        break;
+    }
+
+    trace_virtio_vhost_user_notification_write(s, addr, val);
+}
+
+/*
+ * Add the shared memory region as a subregion of the
+ * additional_resources_bar.
+ */
+static void vvu_set_vhost_mem_regions(VirtIOVhostUserPCI *vvup)
+{
+    VirtIOVhostUser *s = &vvup->vdev;
+    VhostUserMemory m = s->read_msg.payload.memory, *memory = &m;
+    hwaddr subregion_offset;
+    uint32_t i;
+
+    /* Start after the notification structure */
+    subregion_offset = vvup->shared_memory.offset;
+
+    for (i = 0; i < memory->nregions; i++) {
+        VirtIOVhostUserMemTableRegion *region = &s->mem_table[i];
+
+        memory_region_init_ram_device_ptr(&region->mr, OBJECT(vvup),
+                "virtio-vhost-user-mem-table-region",
+                region->total_size, region->mmap_addr);
+        memory_region_add_subregion(&vvup->additional_resources_bar,
+                                    subregion_offset, &region->mr);
+
+        subregion_offset += region->total_size;
+    }
+}
+
+void virtio_vhost_user_set_vhost_mem_regions(VirtIOVhostUser *s)
+{
+    VirtIOVhostUserPCI *vvup = container_of(s, struct VirtIOVhostUserPCI, vdev);
+    VirtioVhostUserPCIClass *vvup_class = VIRTIO_VHOST_USER_PCI_GET_CLASS(vvup);
+
+    vvup_class->set_vhost_mem_regions(vvup);
+}
+
+static void vvu_delete_vhost_mem_region(VirtIOVhostUserPCI *vvup,
+    MemoryRegion *mr)
+{
+    memory_region_del_subregion(&vvup->additional_resources_bar, mr);
+    object_unparent(OBJECT(mr));
+}
+
+
+void virtio_vhost_user_delete_vhost_mem_region(VirtIOVhostUser *s,
+    MemoryRegion *mr)
+{
+    VirtIOVhostUserPCI *vvup = container_of(s, struct VirtIOVhostUserPCI, vdev);
+    VirtioVhostUserPCIClass *vvup_class = VIRTIO_VHOST_USER_PCI_GET_CLASS(vvup);
+
+    vvup_class->delete_vhost_mem_region(vvup, mr);
+}
+
+static void virtio_vhost_user_init_bar(VirtIOVhostUserPCI *vvup)
+{
+    /* virtio-pci doesn't use BAR 2 & 3, so we use it */
+    const int bar_index = 2;
+
+    /*
+     * TODO If the BAR is too large the guest won't have address space to map
+     * it!
+     */
+    const uint64_t bar_size = 1ULL << 36;
+
+    memory_region_init(&vvup->additional_resources_bar, OBJECT(vvup),
+                       "virtio-vhost-user", bar_size);
+    pci_register_bar(&vvup->parent_obj.pci_dev, bar_index,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY |
+                     PCI_BASE_ADDRESS_MEM_PREFETCH |
+                     PCI_BASE_ADDRESS_MEM_TYPE_64,
+                     &vvup->additional_resources_bar);
+
+    /*
+     * Initialize the VirtIOPCIRegions for the virtio configuration structures
+     * corresponding to the additional device resource capabilities.
+     * Place the additional device resources in the additional_resources_bar.
+     */
+    VirtIOPCIProxy *proxy = VIRTIO_PCI(vvup);
+
+    vvup->doorbells.offset = 0x0;
+    /* VIRTIO_QUEUE_MAX + 1 for logfd */
+    vvup->doorbells.size = virtio_pci_queue_mem_mult(proxy)
+        * (VIRTIO_QUEUE_MAX + 1);
+    /* TODO Not sure if it is necessary for the size to be aligned */
+    vvup->doorbells.size = QEMU_ALIGN_UP(vvup->doorbells.size, 4096);
+    vvup->doorbells.type = VIRTIO_PCI_CAP_DOORBELL_CFG;
+
+    vvup->notifications.offset = vvup->doorbells.offset + vvup->doorbells.size;
+    vvup->notifications.size = 0x1000;
+    vvup->notifications.type = VIRTIO_PCI_CAP_NOTIFICATION_CFG;
+
+    /* cap.offset and cap.length must be 4096-byte (0x1000) aligned. */
+    vvup->shared_memory.offset = vvup->notifications.offset
+        + vvup->notifications.size;
+    vvup->shared_memory.offset = QEMU_ALIGN_UP(vvup->shared_memory.offset,
+        4096);
+    /* TODO Reconsider the shared memory cap.length later */
+    /*
+     * The size of the shared memory region in the additional resources BAR
+     * doesn't fit into the length field (uint32_t) of the virtio capability
+     * structure. However, we don't need to pass this information to the guest
+     * driver via the shared memory capability because the guest can figure out
+     * the length of the vhost memory regions from the SET_MEM_TABLE vhost-user
+     * messages. Therefore, the size of the shared memory region that we are
+     * declaring here has no meaning and the guest driver shouldn't rely on
+     * this.
+     */
+    vvup->shared_memory.size = 0x1000;
+    vvup->shared_memory.type = VIRTIO_PCI_CAP_SHARED_MEMORY_CFG;
+
+    /*
+     * Initialize the MMIO MemoryRegions for the additional device
+     *resources.
+     */
+
+    const struct MemoryRegionOps doorbell_ops = {
+        .read = virtio_vhost_user_doorbells_read,
+        .write = virtio_vhost_user_doorbells_write,
+        .impl = {
+            .min_access_size = 1,
+            .max_access_size = 4,
+        },
+        .endianness = DEVICE_LITTLE_ENDIAN,
+    };
+
+    const struct MemoryRegionOps notification_ops = {
+        .read = virtio_vhost_user_notification_read,
+        .write = virtio_vhost_user_notification_write,
+        .impl = {
+            .min_access_size = 1,
+            .max_access_size = 4,
+        },
+        .endianness = DEVICE_LITTLE_ENDIAN,
+    };
+
+    memory_region_init_io(&vvup->doorbells.mr, OBJECT(vvup),
+                   &doorbell_ops, vvup, "virtio-vhost-user-doorbell-cfg",
+                   vvup->doorbells.size);
+
+    memory_region_init_io(&vvup->notifications.mr, OBJECT(vvup),
+                    &notification_ops, vvup,
+                    "virtio-vhost-user-notification-cfg",
+                    vvup->notifications.size);
+
+    /*
+     * Register the virtio PCI configuration structures
+     * for the additional device resources. This involves
+     * registering the corresponding MemoryRegions as
+     * subregions of the additional_resources_bar and creating
+     * virtio capabilities.
+     */
+    struct virtio_pci_cap cap = {
+        .cap_len = sizeof cap,
+    };
+    struct virtio_pci_doorbell_cap doorbell = {
+        .cap.cap_len = sizeof doorbell,
+        .doorbell_off_multiplier =
+            cpu_to_le32(virtio_pci_queue_mem_mult(proxy)),
+    };
+    virtio_pci_modern_region_map(proxy, &vvup->doorbells, &doorbell.cap,
+                                 &vvup->additional_resources_bar, bar_index);
+    virtio_pci_modern_region_map(proxy, &vvup->notifications, &cap,
+                                 &vvup->additional_resources_bar, bar_index);
+    virtio_pci_modern_region_map(proxy, &vvup->shared_memory, &cap,
+                                 &vvup->additional_resources_bar, bar_index);
+}
+
+static void vvu_cleanup_bar(VirtIOVhostUserPCI *vvup)
+{
+    memory_region_del_subregion(&vvup->additional_resources_bar,
+                                &vvup->doorbells.mr);
+    memory_region_del_subregion(&vvup->additional_resources_bar,
+                                &vvup->notifications.mr);
+}
+
+void virtio_vhost_user_cleanup_additional_resources(VirtIOVhostUser *s)
+{
+    VirtIOVhostUserPCI *vvup = container_of(s, struct VirtIOVhostUserPCI, vdev);
+    VirtioVhostUserPCIClass *vvup_class = VIRTIO_VHOST_USER_PCI_GET_CLASS(vvup);
+
+    vvup_class->cleanup_bar(vvup);
+}
+
+static void virtio_vhost_user_pci_realize(VirtIOPCIProxy *vpci_dev,
+                                          Error **errp)
+{
+    VirtIOVhostUserPCI *vvup = VIRTIO_VHOST_USER_PCI(vpci_dev);
+    DeviceState *vdev = DEVICE(&vvup->vdev);
+
+    if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+        vpci_dev->nvectors = VIRTIO_QUEUE_MAX + 3;
+    }
+
+    virtio_vhost_user_init_bar(vvup);
+
+    qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus), &error_abort);
+    object_property_set_bool(OBJECT(vdev), "realized", true, errp);
+}
+
+static void virtio_vhost_user_pci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+    PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+    VirtioVhostUserPCIClass *vvup_class = VIRTIO_VHOST_USER_PCI_CLASS(klass);
+
+    device_class_set_props(dc, virtio_vhost_user_pci_properties);
+    k->realize = virtio_vhost_user_pci_realize;
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+
+    pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_VHOST_USER;
+    pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+    pcidev_k->class_id = PCI_CLASS_OTHERS;
+
+    vvup_class->set_vhost_mem_regions = vvu_set_vhost_mem_regions;
+    vvup_class->delete_vhost_mem_region = vvu_delete_vhost_mem_region;
+    vvup_class->cleanup_bar = vvu_cleanup_bar;
+    vvup_class->register_doorbell = vvu_register_doorbell;
+    vvup_class->unregister_doorbell = vvu_unregister_doorbell;
+}
+
+static void virtio_vhost_user_pci_initfn(Object *obj)
+{
+    VirtIOVhostUserPCI *dev = VIRTIO_VHOST_USER_PCI(obj);
+
+     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+                                TYPE_VIRTIO_VHOST_USER);
+}
+
+static const VirtioPCIDeviceTypeInfo virtio_vhost_user_pci_info = {
+    .base_name     = TYPE_VIRTIO_VHOST_USER_PCI,
+    .generic_name  = "virtio-vhost-user-pci",
+    .instance_size = sizeof(VirtIOVhostUserPCI),
+    .instance_init = virtio_vhost_user_pci_initfn,
+    .class_size    = sizeof(VirtioVhostUserPCIClass),
+    .class_init    = virtio_vhost_user_pci_class_init,
+};
+
+static void virtio_vhost_user_pci_register_types(void)
+{
+    virtio_pci_types_register(&virtio_vhost_user_pci_info);
+}
+
+type_init(virtio_vhost_user_pci_register_types);
diff --git a/hw/virtio/virtio-vhost-user.c b/hw/virtio/virtio-vhost-user.c
new file mode 100644
index 0000000000..6e4c6ec11c
--- /dev/null
+++ b/hw/virtio/virtio-vhost-user.c
@@ -0,0 +1,1066 @@
+/*
+ * Virtio Vhost-user Device
+ *
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Authors:
+ *  Stefan Hajnoczi   <stefanha@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "standard-headers/linux/virtio_ids.h"
+#include "qapi/error.h"
+#include "qemu/iov.h"
+#include "qemu/sockets.h"
+#include "hw/virtio/virtio-access.h"
+#include "hw/virtio/virtio-vhost-user.h"
+#include "trace.h"
+#include "qemu/uuid.h"
+
+enum VhostUserProtocolFeature {
+    VHOST_USER_PROTOCOL_F_MQ = 0,
+    VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
+    VHOST_USER_PROTOCOL_F_RARP = 2,
+    VHOST_USER_PROTOCOL_F_REPLY_ACK = 3,
+    VHOST_USER_PROTOCOL_F_NET_MTU = 4,
+    VHOST_USER_PROTOCOL_F_BACKEND_REQ = 5,
+    VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6,
+    VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7,
+    VHOST_USER_PROTOCOL_F_PAGEFAULT = 8,
+    VHOST_USER_PROTOCOL_F_CONFIG = 9,
+    VHOST_USER_PROTOCOL_F_BACKEND_SEND_FD = 10,
+    VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11,
+    VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD = 12,
+    VHOST_USER_PROTOCOL_F_RESET_DEVICE = 13,
+    /* Feature 14 reserved for VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS. */
+    VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS = 15,
+    VHOST_USER_PROTOCOL_F_MAX
+};
+
+/* vmstate migration version number */
+#define VIRTIO_VHOST_USER_VM_VERSION    0
+
+/*
+ * Descriptor ring size.  Only one vhost-user protocol message is processed at
+ * a time but later messages can be queued.
+ */
+#define VIRTIO_VHOST_USER_VIRTQUEUE_SIZE 128
+
+/* Protocol features that have been implemented */
+#define SUPPORTED_VHOST_USER_FEATURES \
+    (VHOST_USER_PROTOCOL_F_MQ | VHOST_USER_PROTOCOL_F_REPLY_ACK)
+
+/*
+ * Connection state machine
+ *
+ * The vhost-user frontend might not always be connected and the driver might
+ * not always be ready either.  The device interface has a way to manage
+ * connection establishment:
+ *
+ * The driver indicates readiness with the VIRTIO_VHOST_USER_STATUS_BACKEND_UP
+ * status bit.  The device then begins establishing a connection with the
+ * vhost-user frontend. The VIRTIO_VHOST_USER_STATUS_FRONTEND_UP status bit is
+ * set when connected.
+ *
+ * The driver may decide it wants to disconnect at any time.  Vhost-user
+ * protocol violations and other errors might cause the device to give up on
+ * the connection too.
+ *
+ * This state machine captures all transitions in one place.  This way the
+ * connection management code isn't sprinkled around many locations.
+ */
+typedef enum {
+    CONN_STATE_UNDEFINED,
+    CONN_STATE_INITIAL,     /* !BACKEND_UP + !CHR_OPENED */
+    CONN_STATE_BACKEND_UP,    /* BACKEND_UP + !CHR_OPENED */
+    CONN_STATE_CHR_OPENED,  /* !BACKEND_UP + CHR_OPENED */
+    CONN_STATE_CONNECTED,   /* BACKEND_UP + CHR_OPENED */
+    CONN_STATE_MAX
+} ConnectionState;
+
+typedef enum {
+    /* Driver sets VIRTIO_VHOST_USER_STATUS_BACKEND_UP */
+    CONN_EVENT_BACKEND_UP,
+
+    /* Driver clears VIRTIO_VHOST_USER_STATUS_BACKEND_UP */
+    CONN_EVENT_BACKEND_DOWN,
+
+    /* Socket connected and also each time we update chardev handlers */
+    CONN_EVENT_CHR_OPENED,
+
+    /* Socket disconnected */
+    CONN_EVENT_CHR_CLOSED,
+
+    /* Socket chardev was replaced */
+    CONN_EVENT_CHR_CHANGE,
+
+    /* Socket I/O error */
+    CONN_EVENT_SOCKET_ERROR,
+
+    /* Virtio device reset */
+    CONN_EVENT_DEVICE_RESET,
+
+    /* Vhost-user protocol violation by frontend */
+    CONN_EVENT_FRONTEND_EINVAL,
+
+    /* Vhost-user protocol violation by backend */
+    CONN_EVENT_BACKEND_EINVAL,
+
+    CONN_EVENT_MAX
+} ConnectionEvent;
+
+static void conn_state_transition(VirtIOVhostUser *s, ConnectionEvent evt);
+
+static void virtio_vhost_user_reset_async_state(VirtIOVhostUser *s)
+{
+    s->read_bytes_needed = 0;
+    s->read_ptr = NULL;
+    s->read_done = NULL;
+    s->read_waiting_on_rxq = false;
+    s->read_msg_size = 0;
+
+    s->write_bytes_avail = 0;
+    s->write_ptr = NULL;
+    s->write_done = NULL;
+    if (s->write_watch_tag) {
+        g_source_remove(s->write_watch_tag);
+    }
+    s->write_watch_tag = 0;
+}
+
+static void virtio_vhost_user_chr_event(void *opaque, QEMUChrEvent event)
+{
+    VirtIOVhostUser *s = opaque;
+
+    trace_virtio_vhost_user_chr_event(s, event);
+
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        conn_state_transition(s, CONN_EVENT_CHR_OPENED);
+        break;
+    case CHR_EVENT_CLOSED:
+        conn_state_transition(s, CONN_EVENT_CHR_CLOSED);
+        break;
+    case CHR_EVENT_BREAK:
+    case CHR_EVENT_MUX_IN:
+    case CHR_EVENT_MUX_OUT:
+        /* Ignore */
+        break;
+    }
+}
+
+static int virtio_vhost_user_chr_change(void *opaque)
+{
+    VirtIOVhostUser *s = opaque;
+
+    trace_virtio_vhost_user_chr_change(s);
+
+    if (s->config.status & VIRTIO_VHOST_USER_STATUS_FRONTEND_UP) {
+        conn_state_transition(s, CONN_EVENT_CHR_CHANGE);
+    }
+    return 0;
+}
+
+static int virtio_vhost_user_chr_can_read(void *opaque)
+{
+    VirtIOVhostUser *s = opaque;
+
+    return s->read_bytes_needed;
+}
+
+static void virtio_vhost_user_chr_read(void *opaque,
+                                       const uint8_t *buf, int size)
+{
+    VirtIOVhostUser *s = opaque;
+
+    assert(size <= s->read_bytes_needed);
+
+    memcpy(s->read_ptr, buf, size);
+    s->read_ptr += size;
+    s->read_bytes_needed -= size;
+
+    if (s->read_bytes_needed == 0) {
+        qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
+                virtio_vhost_user_chr_event,
+                virtio_vhost_user_chr_change,
+                s, NULL, false);
+
+        s->read_done(s);
+    }
+}
+
+/* Start reading from vhost-user socket */
+static void virtio_vhost_user_aio_read(VirtIOVhostUser *s,
+                                       void *buf, size_t len,
+                                       void (*done)(VirtIOVhostUser *s))
+{
+    assert(s->read_bytes_needed == 0);
+
+    s->read_ptr = buf;
+    s->read_bytes_needed = len;
+    s->read_done = done;
+
+    qemu_chr_fe_set_handlers(&s->chr,
+            virtio_vhost_user_chr_can_read,
+            virtio_vhost_user_chr_read,
+            virtio_vhost_user_chr_event,
+            virtio_vhost_user_chr_change,
+            s, NULL, false);
+}
+
+/* Called once with chan=NULL, cond=0 to begin and then called by event loop */
+static gboolean virtio_vhost_user_chr_write(void *do_not_use, GIOCondition cond,
+                                            void *opaque)
+{
+    VirtIOVhostUser *s = opaque;
+    int nwritten;
+    guint tag = s->write_watch_tag;
+
+    nwritten = qemu_chr_fe_write(&s->chr, s->write_ptr, s->write_bytes_avail);
+    if (nwritten < 0) {
+        if (errno == EAGAIN) {
+            nwritten = 0;
+        } else {
+            conn_state_transition(s, CONN_EVENT_SOCKET_ERROR);
+            return G_SOURCE_REMOVE;
+        }
+    }
+
+    s->write_bytes_avail -= nwritten;
+    if (s->write_bytes_avail == 0) {
+        s->write_done(s);
+        return G_SOURCE_REMOVE;
+    }
+
+    if (tag == 0) {
+        tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
+                                    virtio_vhost_user_chr_write, s);
+        if (!tag) {
+            conn_state_transition(s, CONN_EVENT_SOCKET_ERROR);
+            return G_SOURCE_REMOVE;
+        }
+
+        s->write_watch_tag = tag;
+    }
+
+    return G_SOURCE_CONTINUE;
+}
+
+/* Start writing to vhost-user socket */
+static void virtio_vhost_user_aio_write(VirtIOVhostUser *s,
+                                        void *buf, size_t len,
+                                        void (*done)(VirtIOVhostUser *s))
+{
+    assert(s->write_bytes_avail == 0);
+
+    s->write_ptr = buf;
+    s->write_bytes_avail = len;
+    s->write_done = done;
+
+    virtio_vhost_user_chr_write(NULL, 0, s);
+}
+
+static void virtio_vhost_user_cleanup_kickfds(VirtIOVhostUser *s)
+{
+    size_t i;
+
+    for (i = 0; i < ARRAY_SIZE(s->kickfds); i++) {
+        if (event_notifier_get_fd(&s->kickfds[i].guest_notifier) >= 0) {
+            /* Remove the kickfd from the main event loop */
+            event_notifier_set_handler(&s->kickfds[i].guest_notifier, NULL);
+            close(s->kickfds[i].guest_notifier.rfd);
+            event_notifier_init_fd(&s->kickfds[i].guest_notifier, -1);
+            s->kickfds[i].msi_vector = VIRTIO_NO_VECTOR;
+        }
+    }
+}
+
+static void virtio_vhost_user_cleanup_callfds(VirtIOVhostUser *s)
+{
+    size_t i;
+
+    for (i = 0; i < ARRAY_SIZE(s->callfds); i++) {
+        if (event_notifier_get_fd(&s->callfds[i]) >= 0) {
+            virtio_vhost_user_unregister_doorbell(s, &s->callfds[i], i);
+            close(s->callfds[i].rfd);
+            event_notifier_init_fd(&s->callfds[i], -1);
+        }
+    }
+}
+
+static void virtio_vhost_user_cleanup_mem_table(VirtIOVhostUser *s)
+{
+    int i;
+
+    for (i = 0; i < VHOST_MEMORY_MAX_NREGIONS; i++) {
+        VirtIOVhostUserMemTableRegion *region = &s->mem_table[i];
+
+        if (!region->mmap_addr) {
+            continue;
+        }
+
+        munmap(region->mmap_addr, region->total_size);
+        region->mmap_addr = NULL;
+
+        virtio_vhost_user_delete_vhost_mem_region(s, &region->mr);
+    }
+}
+
+static void conn_action_set_backend_up(VirtIOVhostUser *s)
+{
+    /* Guest-initiated, no need for virtio_notify_config() */
+    s->config.status = VIRTIO_VHOST_USER_STATUS_BACKEND_UP;
+}
+
+static void conn_action_set_backend_down(VirtIOVhostUser *s)
+{
+    /* Guest-initiated, no need for virtio_notify_config() */
+    s->config.status = 0;
+}
+
+static void virtio_vhost_user_hdr_done(VirtIOVhostUser *s);
+
+static void conn_action_connect(VirtIOVhostUser *s)
+{
+    s->config.status = VIRTIO_VHOST_USER_STATUS_BACKEND_UP |
+                       VIRTIO_VHOST_USER_STATUS_FRONTEND_UP;
+    virtio_notify_config(VIRTIO_DEVICE(s));
+
+    /* Begin servicing vhost-user messages */
+    virtio_vhost_user_aio_read(s, &s->read_msg, VHOST_USER_HDR_SIZE,
+                               virtio_vhost_user_hdr_done);
+}
+
+static void conn_action_disconnect_no_notify(VirtIOVhostUser *s)
+{
+    qemu_chr_fe_set_handlers(&s->chr,
+            NULL,
+            NULL,
+            virtio_vhost_user_chr_event,
+            virtio_vhost_user_chr_change,
+            s, NULL, false);
+    qemu_chr_fe_set_open(&s->chr, 0);
+
+    virtio_vhost_user_reset_async_state(s);
+
+    /* TODO drain txq? */
+
+    /*
+     * It is only safe to clean up resources where future accesses have no
+     * guest-visible effects.  Vcpus may still access resources if they haven't
+     * noticed the disconnect event yet.  Callfds are safe since writes to
+     * invalid indices are ignored.  Memory table regions cannot be unmapped
+     * since vring polling may still be running.
+     */
+    virtio_vhost_user_cleanup_kickfds(s);
+    virtio_vhost_user_cleanup_callfds(s);
+
+    s->config.status = 0;
+}
+
+static void conn_action_disconnect(VirtIOVhostUser *s)
+{
+    conn_action_disconnect_no_notify(s);
+    virtio_notify_config(VIRTIO_DEVICE(s));
+}
+
+static const struct {
+    void (*action)(VirtIOVhostUser *s);
+    ConnectionState new_state;
+} conn_state_machine[CONN_STATE_MAX][CONN_EVENT_MAX] = {
+    [CONN_STATE_INITIAL] = {
+        [CONN_EVENT_BACKEND_UP] = {conn_action_set_backend_up,
+                                 CONN_STATE_BACKEND_UP},
+        [CONN_EVENT_CHR_OPENED] = {NULL, CONN_STATE_CHR_OPENED},
+        [CONN_EVENT_CHR_CLOSED] = {NULL, CONN_STATE_INITIAL},
+        [CONN_EVENT_CHR_CHANGE] = {NULL, CONN_STATE_INITIAL},
+        [CONN_EVENT_DEVICE_RESET] = {NULL, CONN_STATE_INITIAL},
+    },
+    [CONN_STATE_BACKEND_UP] = {
+        [CONN_EVENT_BACKEND_DOWN] = {conn_action_set_backend_down,
+                                   CONN_STATE_INITIAL},
+        [CONN_EVENT_CHR_OPENED] = {conn_action_connect, CONN_STATE_CONNECTED},
+        [CONN_EVENT_CHR_CLOSED] = {NULL, CONN_STATE_BACKEND_UP},
+        [CONN_EVENT_CHR_CHANGE] = {NULL, CONN_STATE_BACKEND_UP},
+        [CONN_EVENT_DEVICE_RESET] = {conn_action_set_backend_down,
+                                     CONN_STATE_INITIAL},
+    },
+    [CONN_STATE_CHR_OPENED] = {
+        [CONN_EVENT_BACKEND_UP] = {conn_action_connect, CONN_STATE_CONNECTED},
+        [CONN_EVENT_CHR_OPENED] = {NULL, CONN_STATE_CHR_OPENED},
+        [CONN_EVENT_CHR_CLOSED] = {NULL, CONN_STATE_INITIAL},
+        [CONN_EVENT_CHR_CHANGE] = {NULL, CONN_STATE_CHR_OPENED},
+        [CONN_EVENT_DEVICE_RESET] = {NULL, CONN_STATE_INITIAL},
+    },
+    [CONN_STATE_CONNECTED] = {
+        [CONN_EVENT_BACKEND_DOWN] = {conn_action_disconnect_no_notify,
+                                   CONN_STATE_INITIAL},
+        [CONN_EVENT_CHR_OPENED] = {NULL, CONN_STATE_CONNECTED},
+        [CONN_EVENT_CHR_CLOSED] = {conn_action_disconnect,
+                                   CONN_STATE_INITIAL},
+        [CONN_EVENT_CHR_CHANGE] = {conn_action_disconnect, CONN_STATE_INITIAL},
+        [CONN_EVENT_SOCKET_ERROR] = {conn_action_disconnect,
+                                     CONN_STATE_INITIAL},
+        [CONN_EVENT_DEVICE_RESET] = {conn_action_disconnect_no_notify,
+                                     CONN_STATE_INITIAL},
+        [CONN_EVENT_FRONTEND_EINVAL] = {conn_action_disconnect,
+                                      CONN_STATE_INITIAL},
+        [CONN_EVENT_BACKEND_EINVAL] = {conn_action_disconnect,
+                                     CONN_STATE_INITIAL},
+    },
+};
+
+static void conn_state_transition(VirtIOVhostUser *s, ConnectionEvent evt)
+{
+    ConnectionState old_state = s->conn_state;
+    ConnectionState new_state = conn_state_machine[old_state][evt].new_state;
+
+    trace_virtio_vhost_user_conn_state_transition(s, old_state, evt,
+                                                  new_state);
+    assert(new_state != CONN_STATE_UNDEFINED);
+
+    s->conn_state = new_state;
+
+    if (conn_state_machine[old_state][evt].action) {
+        conn_state_machine[old_state][evt].action(s);
+    }
+}
+
+/*
+ * Frontend-to-backend message processing
+ *
+ * Messages are read from the vhost-user socket into s->read_msg.  They are
+ * then parsed and may be modified.  Finally they are put onto the rxq for the
+ * driver to read.
+ *
+ * Functions with "m2s" in their name handle the frontend-to-backend code path.
+ */
+
+/* Put s->read_msg onto the rxq */
+static void virtio_vhost_user_deliver_m2s(VirtIOVhostUser *s)
+{
+    VirtQueueElement *elem;
+    size_t copied;
+
+    elem = virtqueue_pop(s->rxq, sizeof(*elem));
+    if (!elem) {
+        /* Leave message in s->read_msg and wait for rxq */
+        trace_virtio_vhost_user_rxq_empty(s);
+        s->read_waiting_on_rxq = true;
+        return;
+    }
+
+    s->read_waiting_on_rxq = false;
+
+    copied = iov_from_buf(elem->in_sg, elem->in_num, 0, &s->read_msg,
+                          s->read_msg_size);
+    if (copied != s->read_msg_size) {
+        g_free(elem);
+        virtio_error(VIRTIO_DEVICE(s),
+                     "rxq buffer too small, got %zu, needed %zu",
+                     copied, s->read_msg_size);
+        return;
+    }
+
+    virtqueue_push(s->rxq, elem, copied);
+    g_free(elem);
+
+    virtio_notify(VIRTIO_DEVICE(s), s->rxq);
+
+    /* Next message, please */
+    virtio_vhost_user_aio_read(s, &s->read_msg, VHOST_USER_HDR_SIZE,
+                               virtio_vhost_user_hdr_done);
+}
+
+static void m2s_get_vring_base(VirtIOVhostUser *s)
+{
+    unsigned int vq_idx;
+
+    vq_idx = s->read_msg.payload.state.index;
+
+    if (event_notifier_get_fd(&s->kickfds[vq_idx].guest_notifier) >= 0) {
+        /* Remove the kickfd from the main event loop */
+        event_notifier_set_handler(&s->kickfds[vq_idx].guest_notifier, NULL);
+        close(s->kickfds[vq_idx].guest_notifier.rfd);
+        event_notifier_init_fd(&s->kickfds[vq_idx].guest_notifier, -1);
+    }
+
+    if (event_notifier_get_fd(&s->callfds[vq_idx]) >= 0) {
+        virtio_vhost_user_unregister_doorbell(s, &s->callfds[vq_idx], vq_idx);
+        close(s->callfds[vq_idx].rfd);
+        event_notifier_init_fd(&s->callfds[vq_idx], -1);
+    }
+}
+
+static void m2s_set_vring_kick(VirtIOVhostUser *s)
+{
+    uint8_t vq_idx;
+    int fd;
+
+    vq_idx = s->read_msg.payload.u64 & VHOST_USER_VRING_IDX_MASK;
+
+    if (s->read_msg.payload.u64 & VHOST_USER_VRING_NOFD_MASK) {
+        fd = -1;
+    } else {
+        fd = qemu_chr_fe_get_msgfd(&s->chr);
+
+        /* Must not block when reach max eventfd counter value */
+        qemu_socket_set_nonblock(fd);
+    }
+
+    if (event_notifier_get_fd(&s->kickfds[vq_idx].guest_notifier) >= 0) {
+        /* Remove the kickfd from the main event loop */
+        event_notifier_set_handler(&s->kickfds[vq_idx].guest_notifier, NULL);
+        close(s->kickfds[vq_idx].guest_notifier.rfd);
+        event_notifier_init_fd(&s->kickfds[vq_idx].guest_notifier, -1);
+    }
+
+    /* Initialize the EventNotifier with the received kickfd */
+    event_notifier_init_fd(&s->kickfds[vq_idx].guest_notifier, fd);
+
+    /* Insert the kickfd in the main event loop */
+    if (fd != -1) {
+        event_notifier_set_handler(&s->kickfds[vq_idx].guest_notifier,
+            virtio_vhost_user_guest_notifier_read);
+    }
+}
+
+static void m2s_set_vring_call(VirtIOVhostUser *s)
+{
+    uint8_t vq_idx;
+    int fd;
+
+    vq_idx = s->read_msg.payload.u64 & VHOST_USER_VRING_IDX_MASK;
+
+    /* We should always have a large enough array */
+    QEMU_BUILD_BUG_ON(0xff >= ARRAY_SIZE(s->callfds));
+
+    if (s->read_msg.payload.u64 & VHOST_USER_VRING_NOFD_MASK) {
+        fd = -1;
+    } else {
+        fd = qemu_chr_fe_get_msgfd(&s->chr);
+
+        /* Must not block when reach max eventfd counter value */
+        qemu_socket_set_nonblock(fd);
+    }
+
+    if (event_notifier_get_fd(&s->callfds[vq_idx]) >= 0) {
+        virtio_vhost_user_unregister_doorbell(s, &s->callfds[vq_idx], vq_idx);
+        close(s->callfds[vq_idx].rfd);
+        event_notifier_init_fd(&s->callfds[vq_idx], -1);
+    }
+
+    /* Initialize the EventNotifier with the received callfd */
+    event_notifier_init_fd(&s->callfds[vq_idx], fd);
+
+    /* Register the EventNotifier as an ioeventfd. */
+    if (fd != -1) {
+        virtio_vhost_user_register_doorbell(s, &s->callfds[vq_idx], vq_idx);
+    }
+}
+
+static void m2s_set_mem_table(VirtIOVhostUser *s)
+{
+    VhostUserMemory m = s->read_msg.payload.memory, *memory = &m;
+    int fds[VHOST_MEMORY_MAX_NREGIONS];
+    int num_fds;
+    uint32_t i;
+
+    if (memory->nregions > VHOST_MEMORY_MAX_NREGIONS) {
+        conn_state_transition(s, CONN_EVENT_FRONTEND_EINVAL);
+        return;
+    }
+
+    num_fds = qemu_chr_fe_get_msgfds(&s->chr, fds, ARRAY_SIZE(fds));
+    if (num_fds != memory->nregions) {
+        conn_state_transition(s, CONN_EVENT_FRONTEND_EINVAL);
+        return;
+    }
+
+    virtio_vhost_user_cleanup_mem_table(s);
+
+    for (i = 0; i < memory->nregions; i++) {
+        VhostUserMemoryRegion *input = &memory->regions[i];
+        VirtIOVhostUserMemTableRegion *region = &s->mem_table[i];
+        void *mmap_addr;
+
+        region->total_size = input->mmap_offset + input->memory_size;
+        if (region->total_size < input->mmap_offset ||
+            region->total_size < input->memory_size) {
+            goto err;
+        }
+
+        mmap_addr = mmap(0, region->total_size, PROT_READ | PROT_WRITE,
+                         MAP_SHARED, fds[i], 0);
+        close(fds[i]);
+        fds[i] = -1;
+        if (mmap_addr == MAP_FAILED) {
+            goto err;
+        }
+        region->mmap_addr = mmap_addr;
+
+        trace_virtio_vhost_user_memory_region(s,
+                memory->regions[i].guest_phys_addr,
+                memory->regions[i].memory_size,
+                memory->regions[i].userspace_addr,
+                memory->regions[i].mmap_offset,
+                region->mmap_addr);
+    }
+
+    /*
+     * Export the mmapped vhost memory regions to the guest through PCI
+     * transport
+     */
+    virtio_vhost_user_set_vhost_mem_regions(s);
+
+    return;
+
+err:
+    for (i = 0; i < memory->nregions; i++) {
+        if (fds[i] >= 0) {
+            close(fds[i]);
+        }
+    }
+    conn_state_transition(s, CONN_EVENT_FRONTEND_EINVAL);
+}
+
+static void m2s_set_protocol_features(VirtIOVhostUser *s)
+{
+    /* Only allow features we support too */
+    s->read_msg.payload.u64 &= SUPPORTED_VHOST_USER_FEATURES;
+}
+
+/* Parse s->read_msg from frontend */
+static void virtio_vhost_user_parse_m2s(VirtIOVhostUser *s)
+{
+    uint32_t version = s->read_msg.hdr.flags & VHOST_USER_VERSION_MASK;
+
+    if (version != VHOST_USER_VERSION) {
+        trace_virtio_vhost_user_m2s_bad_version(s, version);
+        conn_state_transition(s, CONN_EVENT_FRONTEND_EINVAL);
+        return;
+    }
+
+    if (s->read_msg.hdr.flags & VHOST_USER_REPLY_MASK) {
+        trace_virtio_vhost_user_m2s_unexpected_reply(s);
+        conn_state_transition(s, CONN_EVENT_FRONTEND_EINVAL);
+        return;
+    }
+
+    if (s->read_msg.hdr.request >= VHOST_USER_MAX) {
+        trace_virtio_vhost_user_m2s_bad_request(s, s->read_msg.hdr.request);
+        conn_state_transition(s, CONN_EVENT_FRONTEND_EINVAL);
+        return;
+    }
+
+    trace_virtio_vhost_user_m2s_request(s, s->read_msg.hdr.request);
+
+    /* Most messages are passed through but a few need to be handled */
+    switch (s->read_msg.hdr.request) {
+    case VHOST_USER_GET_FEATURES:
+        break;
+    case VHOST_USER_SET_FEATURES:
+        break;
+    case VHOST_USER_SET_OWNER:
+        break;
+    case VHOST_USER_RESET_OWNER:
+        break;
+    case VHOST_USER_SET_MEM_TABLE:
+        m2s_set_mem_table(s);
+        break;
+    case VHOST_USER_SET_VRING_NUM:
+        break;
+    case VHOST_USER_SET_VRING_ADDR:
+        break;
+    case VHOST_USER_SET_VRING_BASE:
+        break;
+    case VHOST_USER_GET_VRING_BASE:
+        m2s_get_vring_base(s);
+        break;
+    case VHOST_USER_SET_VRING_KICK:
+        m2s_set_vring_kick(s);
+        break;
+    case VHOST_USER_SET_VRING_CALL:
+        m2s_set_vring_call(s);
+        break;
+    case VHOST_USER_GET_PROTOCOL_FEATURES:
+        break;
+    case VHOST_USER_SET_PROTOCOL_FEATURES:
+        m2s_set_protocol_features(s);
+        break;
+    case VHOST_USER_GET_QUEUE_NUM:
+        break;
+    case VHOST_USER_SET_VRING_ENABLE:
+        break;
+    default:
+        trace_virtio_vhost_user_m2s_unknown_request(s, s->read_msg.hdr.request);
+        conn_state_transition(s, CONN_EVENT_FRONTEND_EINVAL);
+        return;
+    }
+
+    /* Bail if a handler function reset the connection */
+    if (s->conn_state != CONN_STATE_CONNECTED) {
+        return;
+    }
+
+    /* Stash size before we endian-convert s->read_msg */
+    s->read_msg_size = VHOST_USER_HDR_SIZE + s->read_msg.hdr.size;
+
+    /* TODO convert read_msg to little-endian for cross-endian support */
+
+    virtio_vhost_user_deliver_m2s(s);
+}
+
+static void virtio_vhost_user_hdr_done(VirtIOVhostUser *s)
+{
+    if (s->read_msg.hdr.size > VHOST_USER_PAYLOAD_SIZE) {
+        trace_virtio_vhost_user_m2s_bad_payload_size(s, s->read_msg.hdr.size);
+        conn_state_transition(s, CONN_EVENT_FRONTEND_EINVAL);
+        return;
+    }
+
+    /* Clear out unused payload bytes */
+    memset(&s->read_msg.payload, 0, VHOST_USER_PAYLOAD_SIZE);
+
+    if (s->read_msg.hdr.size > 0) {
+        virtio_vhost_user_aio_read(s, &s->read_msg.payload,
+                                   s->read_msg.hdr.size,
+                                   virtio_vhost_user_parse_m2s);
+    } else {
+        virtio_vhost_user_parse_m2s(s);
+    }
+}
+
+static void virtio_vhost_user_rxq(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIOVhostUser *s = VIRTIO_VHOST_USER(vdev);
+
+    if (s->read_waiting_on_rxq) {
+        virtio_vhost_user_deliver_m2s(s);
+    }
+}
+
+/*
+ * Backend-to-frontend message processing
+ *
+ * Messages are read from the txq into s->write_msg.  They are then parsed and
+ * may be modified.  Finally they are written to the vhost-user socket.
+ *
+ * Functions with "s2m" in their name handle the backend-to-frontend code path.
+ */
+
+static void s2m_get_protocol_features(VirtIOVhostUser *s)
+{
+    /* Only allow features we support too */
+    s->write_msg.payload.u64 &= SUPPORTED_VHOST_USER_FEATURES;
+}
+
+static void virtio_vhost_user_tx_done(VirtIOVhostUser *s);
+
+/* Parse s->write_msg from backend */
+static void virtio_vhost_user_parse_s2m(VirtIOVhostUser *s)
+{
+    uint32_t version = s->write_msg.hdr.flags & VHOST_USER_VERSION_MASK;
+
+    if (version != VHOST_USER_VERSION) {
+        trace_virtio_vhost_user_s2m_bad_version(s, version);
+        conn_state_transition(s, CONN_EVENT_BACKEND_EINVAL);
+        return;
+    }
+
+    if (!(s->write_msg.hdr.flags & VHOST_USER_REPLY_MASK)) {
+        trace_virtio_vhost_user_s2m_expected_reply(s);
+        conn_state_transition(s, CONN_EVENT_BACKEND_EINVAL);
+        return;
+    }
+
+    if (s->write_msg.hdr.request >= VHOST_USER_MAX) {
+        trace_virtio_vhost_user_s2m_bad_request(s, s->write_msg.hdr.request);
+        conn_state_transition(s, CONN_EVENT_BACKEND_EINVAL);
+        return;
+    }
+
+    trace_virtio_vhost_user_s2m_request(s, s->write_msg.hdr.request);
+
+    /* Very few messages need to be touched */
+    switch (s->write_msg.hdr.request) {
+    case VHOST_USER_GET_FEATURES:
+        break;
+    case VHOST_USER_SET_FEATURES:
+        break;
+    case VHOST_USER_SET_OWNER:
+        break;
+    case VHOST_USER_RESET_OWNER:
+        break;
+    case VHOST_USER_SET_MEM_TABLE:
+        break;
+    case VHOST_USER_SET_VRING_NUM:
+        break;
+    case VHOST_USER_SET_VRING_ADDR:
+        break;
+    case VHOST_USER_SET_VRING_BASE:
+        break;
+    case VHOST_USER_GET_VRING_BASE:
+        break;
+    case VHOST_USER_SET_VRING_KICK:
+        break;
+    case VHOST_USER_SET_VRING_CALL:
+        break;
+    case VHOST_USER_GET_PROTOCOL_FEATURES:
+        s2m_get_protocol_features(s);
+        break;
+    case VHOST_USER_SET_PROTOCOL_FEATURES:
+        break;
+    case VHOST_USER_GET_QUEUE_NUM:
+        break;
+    case VHOST_USER_SET_VRING_ENABLE:
+        break;
+    default:
+        trace_virtio_vhost_user_s2m_unknown_request(s,
+                                                    s->write_msg.hdr.request);
+        conn_state_transition(s, CONN_EVENT_BACKEND_EINVAL);
+        return;
+    }
+
+    /* Bail if a handler function reset the connection */
+    if (s->conn_state != CONN_STATE_CONNECTED) {
+        return;
+    }
+
+    virtio_vhost_user_aio_write(s, &s->write_msg,
+                                VHOST_USER_HDR_SIZE + s->write_msg.hdr.size,
+                                virtio_vhost_user_tx_done);
+}
+
+static void virtio_vhost_user_txq(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIOVhostUser *s = VIRTIO_VHOST_USER(vdev);
+    VirtQueueElement *elem;
+    size_t msgsize;
+    size_t copied;
+
+    /* If the last message is still being transferred we'll come back later */
+    if (s->write_bytes_avail != 0) {
+        return;
+    }
+
+    elem = virtqueue_pop(s->txq, sizeof(*elem));
+    if (!elem) {
+        return; /* no elements left on virtqueue */
+    }
+
+    msgsize = iov_size(elem->out_sg, elem->out_num);
+    if (msgsize < VHOST_USER_HDR_SIZE || msgsize > sizeof(s->write_msg)) {
+        g_free(elem);
+        virtio_error(VIRTIO_DEVICE(s),
+                     "invalid txq buffer size, got %zu", msgsize);
+        return;
+    }
+
+    /* Clear out unused payload bytes */
+    memset(&s->write_msg.payload, 0, VHOST_USER_PAYLOAD_SIZE);
+
+    copied = iov_to_buf(elem->out_sg, elem->out_num, 0,
+                        &s->write_msg, msgsize);
+    if (copied != VHOST_USER_HDR_SIZE + s->write_msg.hdr.size ||
+        copied != msgsize) {
+        g_free(elem);
+        virtio_error(VIRTIO_DEVICE(s),
+                     "invalid txq buffer size, got %zu", msgsize);
+        return;
+    }
+
+    virtqueue_push(s->txq, elem, copied);
+    g_free(elem);
+
+    virtio_notify(VIRTIO_DEVICE(s), s->txq);
+
+    /* TODO convert from little-endian */
+
+    virtio_vhost_user_parse_s2m(s);
+}
+
+static void virtio_vhost_user_tx_done(VirtIOVhostUser *s)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(s);
+    VirtQueue *vq = s->txq;
+
+    trace_virtio_vhost_user_tx_done(s);
+
+    /* Try to process more messages from the driver */
+    virtio_vhost_user_txq(vdev, vq);
+}
+
+static uint64_t
+virtio_vhost_user_get_features(VirtIODevice *vdev,
+                               uint64_t requested_features,
+                               Error **errp)
+{
+    return requested_features;
+}
+
+static void virtio_vhost_user_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+    VirtIOVhostUserConfig *vvuconfig = (VirtIOVhostUserConfig *)config;
+    VirtIOVhostUser *s = VIRTIO_VHOST_USER(vdev);
+
+    virtio_stl_p(vdev, &vvuconfig->status, s->config.status);
+    virtio_stl_p(vdev, &vvuconfig->max_vhost_queues,
+                 s->config.max_vhost_queues);
+    memcpy(vvuconfig->uuid, s->config.uuid, sizeof(vvuconfig->uuid));
+}
+
+static void virtio_vhost_user_set_config(VirtIODevice *vdev,
+                                         const uint8_t *config)
+{
+    VirtIOVhostUserConfig *vvuconfig = (VirtIOVhostUserConfig *)config;
+    VirtIOVhostUser *s = VIRTIO_VHOST_USER(vdev);
+    uint32_t status;
+    bool old_backend_up;
+    bool new_backend_up;
+
+    status = virtio_ldl_p(vdev, &vvuconfig->status);
+    trace_virtio_vhost_user_set_config(s, s->config.status, status);
+    if (status & ~(VIRTIO_VHOST_USER_STATUS_BACKEND_UP |
+                   VIRTIO_VHOST_USER_STATUS_FRONTEND_UP)) {
+        virtio_error(vdev, "undefined virtio-vhost-user status bit set "
+                           "(0x%x)", status);
+        return;
+    }
+
+    old_backend_up = s->config.status & VIRTIO_VHOST_USER_STATUS_BACKEND_UP;
+    new_backend_up = status & VIRTIO_VHOST_USER_STATUS_BACKEND_UP;
+
+    if (!old_backend_up && new_backend_up) {
+        conn_state_transition(s, CONN_EVENT_BACKEND_UP);
+    } else if (old_backend_up && !new_backend_up) {
+        conn_state_transition(s, CONN_EVENT_BACKEND_DOWN);
+    }
+}
+
+static void virtio_vhost_user_reset(VirtIODevice *vdev)
+{
+    VirtIOVhostUser *s = VIRTIO_VHOST_USER(vdev);
+
+    conn_state_transition(s, CONN_EVENT_DEVICE_RESET);
+
+    virtio_vhost_user_reset_async_state(s);
+}
+
+static void virtio_vhost_user_device_realize(DeviceState *dev, Error **errp)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+    VirtIOVhostUser *s = VIRTIO_VHOST_USER(dev);
+    size_t i;
+
+    if (!qemu_chr_fe_backend_connected(&s->chr)) {
+        error_setg(errp, "Missing chardev");
+        return;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(s->kickfds); i++) {
+        s->kickfds[i].vdev = vdev;
+        event_notifier_init_fd(&s->kickfds[i].guest_notifier, -1);
+        s->kickfds[i].msi_vector = VIRTIO_NO_VECTOR;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(s->callfds); i++) {
+        event_notifier_init_fd(&s->callfds[i], -1);
+    }
+
+    virtio_init(vdev, VIRTIO_ID_VHOST_USER,
+                sizeof(VirtIOVhostUserConfig));
+
+    s->rxq = virtio_add_queue(vdev, VIRTIO_VHOST_USER_VIRTQUEUE_SIZE,
+                                 virtio_vhost_user_rxq);
+    s->txq = virtio_add_queue(vdev, VIRTIO_VHOST_USER_VIRTQUEUE_SIZE,
+
+                                 virtio_vhost_user_txq);
+    /* Each vhost-user queue uses doorbells and a notification resources */
+    s->config.max_vhost_queues = 1024;
+
+    /* Generate a uuid */
+    QemuUUID uuid;
+    qemu_uuid_generate(&uuid);
+    memcpy(s->config.uuid, uuid.data, sizeof(uuid.data));
+
+    virtio_vhost_user_reset_async_state(s);
+
+    s->conn_state = CONN_STATE_INITIAL;
+    qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, virtio_vhost_user_chr_event,
+            virtio_vhost_user_chr_change, s, NULL, false);
+}
+
+static void virtio_vhost_user_device_unrealize(DeviceState *dev)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+    VirtIOVhostUser *s = VIRTIO_VHOST_USER(vdev);
+
+    qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL,
+                             NULL, NULL, NULL, false);
+    virtio_cleanup(vdev);
+    virtio_vhost_user_cleanup_mem_table(s);
+    virtio_vhost_user_cleanup_kickfds(s);
+    virtio_vhost_user_cleanup_callfds(s);
+    virtio_vhost_user_cleanup_additional_resources(s);
+}
+
+static const VMStateDescription vmstate_virtio_vhost_user_device = {
+    .name = "virtio-vhost-user-device",
+    .version_id = VIRTIO_VHOST_USER_VM_VERSION,
+    .minimum_version_id = VIRTIO_VHOST_USER_VM_VERSION,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static const VMStateDescription vmstate_virtio_vhost_user = {
+    .name = "virtio-vhost-user",
+    .minimum_version_id = VIRTIO_VHOST_USER_VM_VERSION,
+    .version_id = VIRTIO_VHOST_USER_VM_VERSION,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT32(conn_state, VirtIOVhostUser),
+        VMSTATE_VIRTIO_DEVICE,
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static Property virtio_vhost_user_properties[] = {
+    DEFINE_PROP_CHR("chardev", VirtIOVhostUser, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_vhost_user_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+    device_class_set_props(dc, virtio_vhost_user_properties);
+    dc->vmsd = &vmstate_virtio_vhost_user;
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+    vdc->realize = virtio_vhost_user_device_realize;
+    vdc->unrealize = virtio_vhost_user_device_unrealize;
+    vdc->get_config = virtio_vhost_user_get_config;
+    vdc->set_config = virtio_vhost_user_set_config;
+    vdc->get_features = virtio_vhost_user_get_features;
+    vdc->reset = virtio_vhost_user_reset;
+    vdc->vmsd = &vmstate_virtio_vhost_user_device;
+}
+
+static const TypeInfo virtio_vhost_user_info = {
+    .name = TYPE_VIRTIO_VHOST_USER,
+    .parent = TYPE_VIRTIO_DEVICE,
+    .instance_size = sizeof(VirtIOVhostUser),
+    .class_init = virtio_vhost_user_class_init,
+};
+
+static void virtio_register_types(void)
+{
+    type_register_static(&virtio_vhost_user_info);
+}
+
+type_init(virtio_register_types)
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 5d607aeaa0..89870ddd0b 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -171,7 +171,8 @@ const char *virtio_device_names[] = {
     [VIRTIO_ID_PARAM_SERV] = "virtio-param-serv",
     [VIRTIO_ID_AUDIO_POLICY] = "virtio-audio-pol",
     [VIRTIO_ID_BT] = "virtio-bluetooth",
-    [VIRTIO_ID_GPIO] = "virtio-gpio"
+    [VIRTIO_ID_GPIO] = "virtio-gpio",
+    [VIRTIO_ID_VHOST_USER] = "virtio-vhost-user"
 };
 
 static const char *virtio_id_to_name(uint16_t device_id)
@@ -1937,7 +1938,7 @@ void qemu_put_virtqueue_element(VirtIODevice *vdev, QEMUFile *f,
 }
 
 /* virtio device */
-static void virtio_notify_vector(VirtIODevice *vdev, uint16_t vector)
+void virtio_notify_vector(VirtIODevice *vdev, uint16_t vector)
 {
     BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
@@ -2454,7 +2455,7 @@ void virtio_del_queue(VirtIODevice *vdev, int n)
     virtio_delete_queue(&vdev->vq[n]);
 }
 
-static void virtio_set_isr(VirtIODevice *vdev, int value)
+void virtio_set_isr(VirtIODevice *vdev, int value)
 {
     uint8_t old = qatomic_read(&vdev->isr);
 
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 44dacfa224..9cbeced174 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -86,6 +86,7 @@ extern bool pci_available;
 #define PCI_DEVICE_ID_VIRTIO_PMEM        0x1013
 #define PCI_DEVICE_ID_VIRTIO_IOMMU       0x1014
 #define PCI_DEVICE_ID_VIRTIO_MEM         0x1015
+#define PCI_DEVICE_ID_VIRTIO_VHOST_USER  0x1016
 
 #define PCI_VENDOR_ID_REDHAT             0x1b36
 #define PCI_DEVICE_ID_REDHAT_BRIDGE      0x0001
diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h
index 2446dcd9ae..66147a7446 100644
--- a/include/hw/virtio/virtio-pci.h
+++ b/include/hw/virtio/virtio-pci.h
@@ -18,6 +18,7 @@
 #include "hw/pci/msi.h"
 #include "hw/virtio/virtio-bus.h"
 #include "qom/object.h"
+#include "standard-headers/linux/virtio_pci.h"
 
 
 /* virtio-pci-bus */
@@ -252,4 +253,10 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t);
  */
 unsigned virtio_pci_optimal_num_queues(unsigned fixed_queues);
 
+int virtio_pci_queue_mem_mult(struct VirtIOPCIProxy *proxy);
+void virtio_pci_modern_region_map(VirtIOPCIProxy *proxy,
+                                  VirtIOPCIRegion *region,
+                                  struct virtio_pci_cap *cap,
+                                  MemoryRegion *mr,
+                                  uint8_t bar);
 #endif
diff --git a/include/hw/virtio/virtio-vhost-user.h b/include/hw/virtio/virtio-vhost-user.h
new file mode 100644
index 0000000000..fda64a1c71
--- /dev/null
+++ b/include/hw/virtio/virtio-vhost-user.h
@@ -0,0 +1,126 @@
+/*
+ * Virtio Vhost-user Device
+ *
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Authors:
+ *  Stefan Hajnoczi   <stefanha@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_VIRTIO_VHOST_USER_H
+#define QEMU_VIRTIO_VHOST_USER_H
+
+#include "hw/virtio/virtio.h"
+#include "standard-headers/linux/virtio_pci.h"
+#include "hw/virtio/vhost-user.h"
+#include "chardev/char-fe.h"
+
+#define TYPE_VIRTIO_VHOST_USER "virtio-vhost-user-device"
+#define VIRTIO_VHOST_USER(obj) \
+        OBJECT_CHECK(VirtIOVhostUser, (obj), TYPE_VIRTIO_VHOST_USER)
+
+/* Macros for the offsets in virtio notification structure */
+#define NOTIFICATION_SELECT            0
+#define NOTIFICATION_MSIX_VECTOR       2
+
+/* Macros for the additional resources configuration types */
+#define VIRTIO_PCI_CAP_DOORBELL_CFG 6
+#define VIRTIO_PCI_CAP_NOTIFICATION_CFG 7
+#define VIRTIO_PCI_CAP_SHARED_MEMORY_CFG 8
+
+/* The virtio configuration space fields */
+typedef struct {
+    uint32_t status;
+#define VIRTIO_VHOST_USER_STATUS_BACKEND_UP (1 << 0)
+#define VIRTIO_VHOST_USER_STATUS_FRONTEND_UP (1 << 1)
+    uint32_t max_vhost_queues;
+    uint8_t uuid[16];
+} QEMU_PACKED VirtIOVhostUserConfig;
+
+/* Keep track of the mmap for each memory table region */
+typedef struct {
+    MemoryRegion mr;
+    void *mmap_addr;
+    size_t total_size;
+} VirtIOVhostUserMemTableRegion;
+
+struct kickfd {
+    VirtIODevice *vdev;
+    EventNotifier guest_notifier;
+    uint16_t msi_vector;
+};
+
+/* Additional resources configuration structures */
+
+/* Doorbell structure layout */
+struct virtio_pci_doorbell_cap {
+    struct virtio_pci_cap cap;
+    uint32_t doorbell_off_multiplier;
+};
+
+/* Notification structure layout */
+struct virtio_pci_notification_cfg {
+    uint16_t notification_select;              /* read-write */
+    uint16_t notification_msix_vector;         /* read-write */
+};
+
+typedef struct VirtIOVhostUser VirtIOVhostUser;
+struct VirtIOVhostUser {
+    VirtIODevice parent_obj;
+
+    /* The vhost-user socket */
+    CharBackend chr;
+
+    /* notification select */
+    uint16_t nselect;
+    /* Eventfds from VHOST_USER_SET_VRING_KICK along with the MSI-X vectors. */
+    struct kickfd kickfds[VIRTIO_QUEUE_MAX];
+
+    /* Eventfds from VHOST_USER_SET_VRING_CALL */
+    EventNotifier callfds[VIRTIO_QUEUE_MAX];
+
+    /* Mapped memory regions from VHOST_USER_SET_MEM_TABLE */
+    VirtIOVhostUserMemTableRegion mem_table[VHOST_MEMORY_MAX_NREGIONS];
+
+    VirtIOVhostUserConfig config;
+
+    /* Connection establishment state */
+    int conn_state;
+
+    /* Device-to-driver message queue */
+    VirtQueue *rxq;
+
+    /* Driver-to-device message queue */
+    VirtQueue *txq;
+
+    /* Asynchronous read state */
+    int read_bytes_needed;
+    void *read_ptr;
+    void (*read_done)(VirtIOVhostUser *s);
+    VhostUserMsg read_msg;
+    bool read_waiting_on_rxq; /* need rx buffer? */
+    size_t read_msg_size;
+
+    /* Asynchronous write state */
+    int write_bytes_avail;
+    void *write_ptr;
+    void (*write_done)(VirtIOVhostUser *s);
+    VhostUserMsg write_msg;
+    guint write_watch_tag;
+};
+
+void virtio_vhost_user_set_vhost_mem_regions(VirtIOVhostUser *s);
+void virtio_vhost_user_delete_vhost_mem_region(VirtIOVhostUser *s,
+                                               MemoryRegion *mr);
+void virtio_vhost_user_cleanup_additional_resources(VirtIOVhostUser *s);
+void virtio_vhost_user_register_doorbell(VirtIOVhostUser *s, EventNotifier *e,
+                                         uint8_t vq_idx);
+void virtio_vhost_user_unregister_doorbell(VirtIOVhostUser *s, EventNotifier *e,
+                                           uint8_t vq_idx);
+void virtio_vhost_user_guest_notifier_read(EventNotifier *n);
+
+#endif /* QEMU_VIRTIO_VHOST_USER_H */
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index db1c0ddf6b..68476186c2 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -324,6 +324,8 @@ void virtio_queue_aio_attach_host_notifier_no_poll(VirtQueue *vq, AioContext *ct
 void virtio_queue_aio_detach_host_notifier(VirtQueue *vq, AioContext *ctx);
 VirtQueue *virtio_vector_first_queue(VirtIODevice *vdev, uint16_t vector);
 VirtQueue *virtio_vector_next_queue(VirtQueue *vq);
+void virtio_set_isr(VirtIODevice *vdev, int value);
+void virtio_notify_vector(VirtIODevice *vdev, uint16_t vector);
 
 static inline void virtio_add_feature(uint64_t *features, unsigned int fbit)
 {
diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h
index 80d76b75bc..6249506ddd 100644
--- a/include/standard-headers/linux/virtio_ids.h
+++ b/include/standard-headers/linux/virtio_ids.h
@@ -68,6 +68,7 @@
 #define VIRTIO_ID_AUDIO_POLICY		39 /* virtio audio policy */
 #define VIRTIO_ID_BT			40 /* virtio bluetooth */
 #define VIRTIO_ID_GPIO			41 /* virtio gpio */
+#define VIRTIO_ID_VHOST_USER		43 /* virtio vhost-user */
 
 /*
  * Virtio Transitional IDs
-- 
2.25.1



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

end of thread, other threads:[~2022-05-19  9:51 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-19  9:43 [RFC 0/2] Introduce virtio-vhost-user device Usama Arif
2022-05-19  9:43 ` [RFC 1/2] vhost-user: share the vhost-user protocol related structures Usama Arif
2022-05-19  9:43 ` [RFC 2/2] virtio-vhost-user: add virtio-vhost-user device Usama Arif

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.