2014-05-27 20:06:02 +08:00
|
|
|
/*
|
|
|
|
* vhost-user
|
|
|
|
*
|
|
|
|
* Copyright (c) 2013 Virtual Open Systems Sarl.
|
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
|
|
* See the COPYING file in the top-level directory.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2016-01-27 02:17:07 +08:00
|
|
|
#include "qemu/osdep.h"
|
include/qemu/osdep.h: Don't include qapi/error.h
Commit 57cb38b included qapi/error.h into qemu/osdep.h to get the
Error typedef. Since then, we've moved to include qemu/osdep.h
everywhere. Its file comment explains: "To avoid getting into
possible circular include dependencies, this file should not include
any other QEMU headers, with the exceptions of config-host.h,
compiler.h, os-posix.h and os-win32.h, all of which are doing a
similar job to this file and are under similar constraints."
qapi/error.h doesn't do a similar job, and it doesn't adhere to
similar constraints: it includes qapi-types.h. That's in excess of
100KiB of crap most .c files don't actually need.
Add the typedef to qemu/typedefs.h, and include that instead of
qapi/error.h. Include qapi/error.h in .c files that need it and don't
get it now. Include qapi-types.h in qom/object.h for uint16List.
Update scripts/clean-includes accordingly. Update it further to match
reality: replace config.h by config-target.h, add sysemu/os-posix.h,
sysemu/os-win32.h. Update the list of includes in the qemu/osdep.h
comment quoted above similarly.
This reduces the number of objects depending on qapi/error.h from "all
of them" to less than a third. Unfortunately, the number depending on
qapi-types.h shrinks only a little. More work is needed for that one.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
[Fix compilation without the spice devel packages. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-03-14 16:01:28 +08:00
|
|
|
#include "qapi/error.h"
|
2014-05-27 20:06:02 +08:00
|
|
|
#include "hw/virtio/vhost.h"
|
|
|
|
#include "hw/virtio/vhost-backend.h"
|
2015-10-09 23:17:32 +08:00
|
|
|
#include "hw/virtio/virtio-net.h"
|
2014-05-27 20:06:02 +08:00
|
|
|
#include "sysemu/char.h"
|
|
|
|
#include "sysemu/kvm.h"
|
|
|
|
#include "qemu/error-report.h"
|
|
|
|
#include "qemu/sockets.h"
|
2015-10-09 23:17:27 +08:00
|
|
|
#include "migration/migration.h"
|
2014-05-27 20:06:02 +08:00
|
|
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <linux/vhost.h>
|
|
|
|
|
|
|
|
#define VHOST_MEMORY_MAX_NREGIONS 8
|
2015-09-23 12:19:56 +08:00
|
|
|
#define VHOST_USER_F_PROTOCOL_FEATURES 30
|
2015-09-23 12:19:58 +08:00
|
|
|
|
2015-10-09 23:17:33 +08:00
|
|
|
enum VhostUserProtocolFeature {
|
|
|
|
VHOST_USER_PROTOCOL_F_MQ = 0,
|
|
|
|
VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
|
|
|
|
VHOST_USER_PROTOCOL_F_RARP = 2,
|
2016-08-05 18:53:50 +08:00
|
|
|
VHOST_USER_PROTOCOL_F_REPLY_ACK = 3,
|
2015-10-09 23:17:33 +08:00
|
|
|
|
|
|
|
VHOST_USER_PROTOCOL_F_MAX
|
|
|
|
};
|
|
|
|
|
|
|
|
#define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1)
|
2014-05-27 20:06:02 +08:00
|
|
|
|
|
|
|
typedef enum VhostUserRequest {
|
|
|
|
VHOST_USER_NONE = 0,
|
|
|
|
VHOST_USER_GET_FEATURES = 1,
|
|
|
|
VHOST_USER_SET_FEATURES = 2,
|
|
|
|
VHOST_USER_SET_OWNER = 3,
|
2015-11-11 21:24:37 +08:00
|
|
|
VHOST_USER_RESET_OWNER = 4,
|
2014-05-27 20:06:02 +08:00
|
|
|
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,
|
2015-09-23 12:19:56 +08:00
|
|
|
VHOST_USER_GET_PROTOCOL_FEATURES = 15,
|
|
|
|
VHOST_USER_SET_PROTOCOL_FEATURES = 16,
|
2015-09-23 12:19:58 +08:00
|
|
|
VHOST_USER_GET_QUEUE_NUM = 17,
|
2015-09-23 12:20:01 +08:00
|
|
|
VHOST_USER_SET_VRING_ENABLE = 18,
|
2015-10-09 23:17:32 +08:00
|
|
|
VHOST_USER_SEND_RARP = 19,
|
2014-05-27 20:06:02 +08:00
|
|
|
VHOST_USER_MAX
|
|
|
|
} VhostUserRequest;
|
|
|
|
|
|
|
|
typedef struct VhostUserMemoryRegion {
|
|
|
|
uint64_t guest_phys_addr;
|
|
|
|
uint64_t memory_size;
|
|
|
|
uint64_t userspace_addr;
|
2014-06-27 05:01:32 +08:00
|
|
|
uint64_t mmap_offset;
|
2014-05-27 20:06:02 +08:00
|
|
|
} VhostUserMemoryRegion;
|
|
|
|
|
|
|
|
typedef struct VhostUserMemory {
|
|
|
|
uint32_t nregions;
|
|
|
|
uint32_t padding;
|
|
|
|
VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
|
|
|
|
} VhostUserMemory;
|
|
|
|
|
2015-11-11 22:26:02 +08:00
|
|
|
typedef struct VhostUserLog {
|
|
|
|
uint64_t mmap_size;
|
|
|
|
uint64_t mmap_offset;
|
|
|
|
} VhostUserLog;
|
|
|
|
|
2014-05-27 20:06:02 +08:00
|
|
|
typedef struct VhostUserMsg {
|
|
|
|
VhostUserRequest request;
|
|
|
|
|
|
|
|
#define VHOST_USER_VERSION_MASK (0x3)
|
|
|
|
#define VHOST_USER_REPLY_MASK (0x1<<2)
|
2016-08-05 18:53:50 +08:00
|
|
|
#define VHOST_USER_NEED_REPLY_MASK (0x1 << 3)
|
2014-05-27 20:06:02 +08:00
|
|
|
uint32_t flags;
|
|
|
|
uint32_t size; /* the following payload size */
|
|
|
|
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;
|
2015-11-11 22:26:02 +08:00
|
|
|
VhostUserLog log;
|
2015-10-23 03:28:37 +08:00
|
|
|
} payload;
|
2014-05-27 20:06:02 +08:00
|
|
|
} QEMU_PACKED VhostUserMsg;
|
|
|
|
|
|
|
|
static VhostUserMsg m __attribute__ ((unused));
|
|
|
|
#define VHOST_USER_HDR_SIZE (sizeof(m.request) \
|
|
|
|
+ sizeof(m.flags) \
|
|
|
|
+ sizeof(m.size))
|
|
|
|
|
|
|
|
#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE)
|
|
|
|
|
|
|
|
/* The version of the protocol we support */
|
|
|
|
#define VHOST_USER_VERSION (0x1)
|
|
|
|
|
|
|
|
static bool ioeventfd_enabled(void)
|
|
|
|
{
|
|
|
|
return kvm_enabled() && kvm_eventfds_enabled();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = dev->opaque;
|
|
|
|
uint8_t *p = (uint8_t *) msg;
|
|
|
|
int r, size = VHOST_USER_HDR_SIZE;
|
|
|
|
|
|
|
|
r = qemu_chr_fe_read_all(chr, p, size);
|
|
|
|
if (r != size) {
|
2015-11-16 19:55:53 +08:00
|
|
|
error_report("Failed to read msg header. Read %d instead of %d."
|
|
|
|
" Original request %d.", r, size, msg->request);
|
2014-05-27 20:06:02 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* validate received flags */
|
|
|
|
if (msg->flags != (VHOST_USER_REPLY_MASK | VHOST_USER_VERSION)) {
|
|
|
|
error_report("Failed to read msg header."
|
2015-04-28 17:11:04 +08:00
|
|
|
" Flags 0x%x instead of 0x%x.", msg->flags,
|
2014-05-27 20:06:02 +08:00
|
|
|
VHOST_USER_REPLY_MASK | VHOST_USER_VERSION);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* validate message size is sane */
|
|
|
|
if (msg->size > VHOST_USER_PAYLOAD_SIZE) {
|
|
|
|
error_report("Failed to read msg header."
|
2015-04-28 17:11:04 +08:00
|
|
|
" Size %d exceeds the maximum %zu.", msg->size,
|
2014-05-27 20:06:02 +08:00
|
|
|
VHOST_USER_PAYLOAD_SIZE);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->size) {
|
|
|
|
p += VHOST_USER_HDR_SIZE;
|
|
|
|
size = msg->size;
|
|
|
|
r = qemu_chr_fe_read_all(chr, p, size);
|
|
|
|
if (r != size) {
|
|
|
|
error_report("Failed to read msg payload."
|
2015-04-28 17:11:04 +08:00
|
|
|
" Read %d instead of %d.", r, msg->size);
|
2014-05-27 20:06:02 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-08-05 18:53:50 +08:00
|
|
|
static int process_message_reply(struct vhost_dev *dev,
|
|
|
|
VhostUserRequest request)
|
|
|
|
{
|
|
|
|
VhostUserMsg msg;
|
|
|
|
|
|
|
|
if (vhost_user_read(dev, &msg) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg.request != request) {
|
|
|
|
error_report("Received unexpected msg type."
|
|
|
|
"Expected %d received %d",
|
|
|
|
request, msg.request);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg.payload.u64 ? -1 : 0;
|
|
|
|
}
|
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
static bool vhost_user_one_time_request(VhostUserRequest request)
|
|
|
|
{
|
|
|
|
switch (request) {
|
|
|
|
case VHOST_USER_SET_OWNER:
|
2015-11-11 21:24:37 +08:00
|
|
|
case VHOST_USER_RESET_OWNER:
|
2015-10-09 23:17:28 +08:00
|
|
|
case VHOST_USER_SET_MEM_TABLE:
|
|
|
|
case VHOST_USER_GET_QUEUE_NUM:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* most non-init callers ignore the error */
|
2014-05-27 20:06:02 +08:00
|
|
|
static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg,
|
|
|
|
int *fds, int fd_num)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = dev->opaque;
|
2016-07-27 05:15:24 +08:00
|
|
|
int ret, size = VHOST_USER_HDR_SIZE + msg->size;
|
2014-05-27 20:06:02 +08:00
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
/*
|
|
|
|
* For non-vring specific requests, like VHOST_USER_SET_MEM_TABLE,
|
|
|
|
* we just need send it once in the first time. For later such
|
|
|
|
* request, we just ignore it.
|
|
|
|
*/
|
|
|
|
if (vhost_user_one_time_request(msg->request) && dev->vq_index != 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-27 05:15:10 +08:00
|
|
|
if (qemu_chr_fe_set_msgfds(chr, fds, fd_num) < 0) {
|
2016-07-27 05:15:24 +08:00
|
|
|
error_report("Failed to set msg fds.");
|
2016-07-27 05:15:10 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2014-05-27 20:06:02 +08:00
|
|
|
|
2016-07-27 05:15:24 +08:00
|
|
|
ret = qemu_chr_fe_write_all(chr, (const uint8_t *) msg, size);
|
|
|
|
if (ret != size) {
|
|
|
|
error_report("Failed to write msg."
|
|
|
|
" Wrote %d instead of %d.", ret, size);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2014-05-27 20:06:02 +08:00
|
|
|
}
|
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base,
|
|
|
|
struct vhost_log *log)
|
vhost-user: add multiple queue support
This patch is initially based a patch from Nikolay Nikolaev.
This patch adds vhost-user multiple queue support, by creating a nc
and vhost_net pair for each queue.
Qemu exits if find that the backend can't support the number of requested
queues (by providing queues=# option). The max number is queried by a
new message, VHOST_USER_GET_QUEUE_NUM, and is sent only when protocol
feature VHOST_USER_PROTOCOL_F_MQ is present first.
The max queue check is done at vhost-user initiation stage. We initiate
one queue first, which, in the meantime, also gets the max_queues the
backend supports.
In older version, it was reported that some messages are sent more times
than necessary. Here we came an agreement with Michael that we could
categorize vhost user messages to 2 types: non-vring specific messages,
which should be sent only once, and vring specific messages, which should
be sent per queue.
Here I introduced a helper function vhost_user_one_time_request(), which
lists following messages as non-vring specific messages:
VHOST_USER_SET_OWNER
VHOST_USER_RESET_DEVICE
VHOST_USER_SET_MEM_TABLE
VHOST_USER_GET_QUEUE_NUM
For above messages, we simply ignore them when they are not sent the first
time.
Signed-off-by: Nikolay Nikolaev <n.nikolaev@virtualopensystems.com>
Signed-off-by: Changchun Ouyang <changchun.ouyang@intel.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
Reviewed-by: Jason Wang <jasowang@redhat.com>
Tested-by: Marcel Apfelbaum <marcel@redhat.com>
2015-09-23 12:20:00 +08:00
|
|
|
{
|
2015-10-09 23:17:28 +08:00
|
|
|
int fds[VHOST_MEMORY_MAX_NREGIONS];
|
|
|
|
size_t fd_num = 0;
|
|
|
|
bool shmfd = virtio_has_feature(dev->protocol_features,
|
|
|
|
VHOST_USER_PROTOCOL_F_LOG_SHMFD);
|
|
|
|
VhostUserMsg msg = {
|
|
|
|
.request = VHOST_USER_SET_LOG_BASE,
|
|
|
|
.flags = VHOST_USER_VERSION,
|
2015-11-18 22:13:54 +08:00
|
|
|
.payload.log.mmap_size = log->size * sizeof(*(log->log)),
|
2015-11-11 22:26:02 +08:00
|
|
|
.payload.log.mmap_offset = 0,
|
|
|
|
.size = sizeof(msg.payload.log),
|
2015-10-09 23:17:28 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
if (shmfd && log->fd != -1) {
|
|
|
|
fds[fd_num++] = log->fd;
|
|
|
|
}
|
|
|
|
|
2016-07-27 05:15:11 +08:00
|
|
|
if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-10-09 23:17:28 +08:00
|
|
|
|
|
|
|
if (shmfd) {
|
|
|
|
msg.size = 0;
|
|
|
|
if (vhost_user_read(dev, &msg) < 0) {
|
2016-07-27 05:15:11 +08:00
|
|
|
return -1;
|
2015-10-09 23:17:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (msg.request != VHOST_USER_SET_LOG_BASE) {
|
|
|
|
error_report("Received unexpected msg type. "
|
|
|
|
"Expected %d received %d",
|
|
|
|
VHOST_USER_SET_LOG_BASE, msg.request);
|
|
|
|
return -1;
|
|
|
|
}
|
vhost-user: add multiple queue support
This patch is initially based a patch from Nikolay Nikolaev.
This patch adds vhost-user multiple queue support, by creating a nc
and vhost_net pair for each queue.
Qemu exits if find that the backend can't support the number of requested
queues (by providing queues=# option). The max number is queried by a
new message, VHOST_USER_GET_QUEUE_NUM, and is sent only when protocol
feature VHOST_USER_PROTOCOL_F_MQ is present first.
The max queue check is done at vhost-user initiation stage. We initiate
one queue first, which, in the meantime, also gets the max_queues the
backend supports.
In older version, it was reported that some messages are sent more times
than necessary. Here we came an agreement with Michael that we could
categorize vhost user messages to 2 types: non-vring specific messages,
which should be sent only once, and vring specific messages, which should
be sent per queue.
Here I introduced a helper function vhost_user_one_time_request(), which
lists following messages as non-vring specific messages:
VHOST_USER_SET_OWNER
VHOST_USER_RESET_DEVICE
VHOST_USER_SET_MEM_TABLE
VHOST_USER_GET_QUEUE_NUM
For above messages, we simply ignore them when they are not sent the first
time.
Signed-off-by: Nikolay Nikolaev <n.nikolaev@virtualopensystems.com>
Signed-off-by: Changchun Ouyang <changchun.ouyang@intel.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
Reviewed-by: Jason Wang <jasowang@redhat.com>
Tested-by: Marcel Apfelbaum <marcel@redhat.com>
2015-09-23 12:20:00 +08:00
|
|
|
}
|
2015-10-09 23:17:28 +08:00
|
|
|
|
|
|
|
return 0;
|
vhost-user: add multiple queue support
This patch is initially based a patch from Nikolay Nikolaev.
This patch adds vhost-user multiple queue support, by creating a nc
and vhost_net pair for each queue.
Qemu exits if find that the backend can't support the number of requested
queues (by providing queues=# option). The max number is queried by a
new message, VHOST_USER_GET_QUEUE_NUM, and is sent only when protocol
feature VHOST_USER_PROTOCOL_F_MQ is present first.
The max queue check is done at vhost-user initiation stage. We initiate
one queue first, which, in the meantime, also gets the max_queues the
backend supports.
In older version, it was reported that some messages are sent more times
than necessary. Here we came an agreement with Michael that we could
categorize vhost user messages to 2 types: non-vring specific messages,
which should be sent only once, and vring specific messages, which should
be sent per queue.
Here I introduced a helper function vhost_user_one_time_request(), which
lists following messages as non-vring specific messages:
VHOST_USER_SET_OWNER
VHOST_USER_RESET_DEVICE
VHOST_USER_SET_MEM_TABLE
VHOST_USER_GET_QUEUE_NUM
For above messages, we simply ignore them when they are not sent the first
time.
Signed-off-by: Nikolay Nikolaev <n.nikolaev@virtualopensystems.com>
Signed-off-by: Changchun Ouyang <changchun.ouyang@intel.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
Reviewed-by: Jason Wang <jasowang@redhat.com>
Tested-by: Marcel Apfelbaum <marcel@redhat.com>
2015-09-23 12:20:00 +08:00
|
|
|
}
|
|
|
|
|
2016-08-15 21:35:24 +08:00
|
|
|
static int vhost_user_set_mem_table(struct vhost_dev *dev,
|
|
|
|
struct vhost_memory *mem)
|
|
|
|
{
|
|
|
|
int fds[VHOST_MEMORY_MAX_NREGIONS];
|
|
|
|
int i, fd;
|
|
|
|
size_t fd_num = 0;
|
|
|
|
bool reply_supported = virtio_has_feature(dev->protocol_features,
|
|
|
|
VHOST_USER_PROTOCOL_F_REPLY_ACK);
|
|
|
|
|
|
|
|
VhostUserMsg msg = {
|
|
|
|
.request = VHOST_USER_SET_MEM_TABLE,
|
|
|
|
.flags = VHOST_USER_VERSION,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (reply_supported) {
|
|
|
|
msg.flags |= VHOST_USER_NEED_REPLY_MASK;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < dev->mem->nregions; ++i) {
|
|
|
|
struct vhost_memory_region *reg = dev->mem->regions + i;
|
|
|
|
ram_addr_t offset;
|
|
|
|
MemoryRegion *mr;
|
|
|
|
|
|
|
|
assert((uintptr_t)reg->userspace_addr == reg->userspace_addr);
|
|
|
|
mr = memory_region_from_host((void *)(uintptr_t)reg->userspace_addr,
|
|
|
|
&offset);
|
|
|
|
fd = memory_region_get_fd(mr);
|
|
|
|
if (fd > 0) {
|
|
|
|
msg.payload.memory.regions[fd_num].userspace_addr = reg->userspace_addr;
|
|
|
|
msg.payload.memory.regions[fd_num].memory_size = reg->memory_size;
|
|
|
|
msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr;
|
|
|
|
msg.payload.memory.regions[fd_num].mmap_offset = offset;
|
|
|
|
assert(fd_num < VHOST_MEMORY_MAX_NREGIONS);
|
|
|
|
fds[fd_num++] = fd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
msg.payload.memory.nregions = fd_num;
|
|
|
|
|
|
|
|
if (!fd_num) {
|
|
|
|
error_report("Failed initializing vhost-user memory map, "
|
|
|
|
"consider using -object memory-backend-file share=on");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg.size = sizeof(msg.payload.memory.nregions);
|
|
|
|
msg.size += sizeof(msg.payload.memory.padding);
|
|
|
|
msg.size += fd_num * sizeof(VhostUserMemoryRegion);
|
|
|
|
|
|
|
|
if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reply_supported) {
|
|
|
|
return process_message_reply(dev, msg.request);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
static int vhost_user_set_vring_addr(struct vhost_dev *dev,
|
|
|
|
struct vhost_vring_addr *addr)
|
|
|
|
{
|
|
|
|
VhostUserMsg msg = {
|
|
|
|
.request = VHOST_USER_SET_VRING_ADDR,
|
|
|
|
.flags = VHOST_USER_VERSION,
|
2015-10-23 03:28:37 +08:00
|
|
|
.payload.addr = *addr,
|
2015-10-23 03:33:39 +08:00
|
|
|
.size = sizeof(msg.payload.addr),
|
2015-10-09 23:17:28 +08:00
|
|
|
};
|
2014-05-27 20:06:02 +08:00
|
|
|
|
2016-07-27 05:15:11 +08:00
|
|
|
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2014-05-27 20:06:02 +08:00
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2014-05-27 20:06:02 +08:00
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
static int vhost_user_set_vring_endian(struct vhost_dev *dev,
|
|
|
|
struct vhost_vring_state *ring)
|
|
|
|
{
|
|
|
|
error_report("vhost-user trying to send unhandled ioctl");
|
|
|
|
return -1;
|
|
|
|
}
|
2014-05-27 20:06:02 +08:00
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
static int vhost_set_vring(struct vhost_dev *dev,
|
|
|
|
unsigned long int request,
|
|
|
|
struct vhost_vring_state *ring)
|
|
|
|
{
|
|
|
|
VhostUserMsg msg = {
|
|
|
|
.request = request,
|
|
|
|
.flags = VHOST_USER_VERSION,
|
2015-10-23 03:28:37 +08:00
|
|
|
.payload.state = *ring,
|
2015-10-23 03:33:39 +08:00
|
|
|
.size = sizeof(msg.payload.state),
|
2015-10-09 23:17:28 +08:00
|
|
|
};
|
|
|
|
|
2016-07-27 05:15:11 +08:00
|
|
|
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-10-09 23:17:28 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_set_vring_num(struct vhost_dev *dev,
|
|
|
|
struct vhost_vring_state *ring)
|
|
|
|
{
|
|
|
|
return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_set_vring_base(struct vhost_dev *dev,
|
|
|
|
struct vhost_vring_state *ring)
|
|
|
|
{
|
|
|
|
return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_set_vring_enable(struct vhost_dev *dev, int enable)
|
|
|
|
{
|
2015-11-17 00:40:18 +08:00
|
|
|
int i;
|
2015-10-09 23:17:28 +08:00
|
|
|
|
2015-11-13 15:24:09 +08:00
|
|
|
if (!virtio_has_feature(dev->features, VHOST_USER_F_PROTOCOL_FEATURES)) {
|
2014-05-27 20:06:02 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-11-17 00:40:18 +08:00
|
|
|
for (i = 0; i < dev->nvqs; ++i) {
|
|
|
|
struct vhost_vring_state state = {
|
|
|
|
.index = dev->vq_index + i,
|
|
|
|
.num = enable,
|
|
|
|
};
|
|
|
|
|
|
|
|
vhost_set_vring(dev, VHOST_USER_SET_VRING_ENABLE, &state);
|
|
|
|
}
|
2015-10-09 23:17:28 +08:00
|
|
|
|
2015-11-17 00:40:18 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2015-10-09 23:17:28 +08:00
|
|
|
|
|
|
|
static int vhost_user_get_vring_base(struct vhost_dev *dev,
|
|
|
|
struct vhost_vring_state *ring)
|
|
|
|
{
|
|
|
|
VhostUserMsg msg = {
|
|
|
|
.request = VHOST_USER_GET_VRING_BASE,
|
|
|
|
.flags = VHOST_USER_VERSION,
|
2015-10-23 03:28:37 +08:00
|
|
|
.payload.state = *ring,
|
2015-10-23 03:33:39 +08:00
|
|
|
.size = sizeof(msg.payload.state),
|
2015-10-09 23:17:28 +08:00
|
|
|
};
|
|
|
|
|
2016-07-27 05:15:11 +08:00
|
|
|
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-10-09 23:17:28 +08:00
|
|
|
|
|
|
|
if (vhost_user_read(dev, &msg) < 0) {
|
2016-07-27 05:15:11 +08:00
|
|
|
return -1;
|
2014-05-27 20:06:02 +08:00
|
|
|
}
|
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
if (msg.request != VHOST_USER_GET_VRING_BASE) {
|
|
|
|
error_report("Received unexpected msg type. Expected %d received %d",
|
|
|
|
VHOST_USER_GET_VRING_BASE, msg.request);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-05-27 20:06:02 +08:00
|
|
|
|
2015-10-23 03:31:28 +08:00
|
|
|
if (msg.size != sizeof(msg.payload.state)) {
|
2015-10-09 23:17:28 +08:00
|
|
|
error_report("Received bad msg size.");
|
|
|
|
return -1;
|
2014-05-27 20:06:02 +08:00
|
|
|
}
|
|
|
|
|
2015-10-23 03:28:37 +08:00
|
|
|
*ring = msg.payload.state;
|
2015-10-09 23:17:28 +08:00
|
|
|
|
2014-05-27 20:06:02 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
static int vhost_set_vring_file(struct vhost_dev *dev,
|
|
|
|
VhostUserRequest request,
|
|
|
|
struct vhost_vring_file *file)
|
2015-10-09 23:17:23 +08:00
|
|
|
{
|
2015-10-09 23:17:26 +08:00
|
|
|
int fds[VHOST_MEMORY_MAX_NREGIONS];
|
|
|
|
size_t fd_num = 0;
|
2015-10-09 23:17:23 +08:00
|
|
|
VhostUserMsg msg = {
|
2015-10-09 23:17:28 +08:00
|
|
|
.request = request,
|
2015-10-09 23:17:23 +08:00
|
|
|
.flags = VHOST_USER_VERSION,
|
2015-10-23 03:28:37 +08:00
|
|
|
.payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK,
|
2015-10-23 03:31:28 +08:00
|
|
|
.size = sizeof(msg.payload.u64),
|
2015-10-09 23:17:23 +08:00
|
|
|
};
|
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
if (ioeventfd_enabled() && file->fd > 0) {
|
|
|
|
fds[fd_num++] = file->fd;
|
|
|
|
} else {
|
2015-10-23 03:28:37 +08:00
|
|
|
msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK;
|
2015-10-09 23:17:26 +08:00
|
|
|
}
|
|
|
|
|
2016-07-27 05:15:11 +08:00
|
|
|
if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-10-09 23:17:26 +08:00
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2015-10-09 23:17:26 +08:00
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
static int vhost_user_set_vring_kick(struct vhost_dev *dev,
|
|
|
|
struct vhost_vring_file *file)
|
|
|
|
{
|
|
|
|
return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_KICK, file);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_set_vring_call(struct vhost_dev *dev,
|
|
|
|
struct vhost_vring_file *file)
|
|
|
|
{
|
|
|
|
return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_CALL, file);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64)
|
|
|
|
{
|
|
|
|
VhostUserMsg msg = {
|
|
|
|
.request = request,
|
|
|
|
.flags = VHOST_USER_VERSION,
|
2015-10-23 03:28:37 +08:00
|
|
|
.payload.u64 = u64,
|
2015-10-23 03:31:28 +08:00
|
|
|
.size = sizeof(msg.payload.u64),
|
2015-10-09 23:17:28 +08:00
|
|
|
};
|
|
|
|
|
2016-07-27 05:15:11 +08:00
|
|
|
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-10-09 23:17:28 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_set_features(struct vhost_dev *dev,
|
|
|
|
uint64_t features)
|
|
|
|
{
|
|
|
|
return vhost_user_set_u64(dev, VHOST_USER_SET_FEATURES, features);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_set_protocol_features(struct vhost_dev *dev,
|
|
|
|
uint64_t features)
|
|
|
|
{
|
|
|
|
return vhost_user_set_u64(dev, VHOST_USER_SET_PROTOCOL_FEATURES, features);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64)
|
|
|
|
{
|
|
|
|
VhostUserMsg msg = {
|
|
|
|
.request = request,
|
|
|
|
.flags = VHOST_USER_VERSION,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (vhost_user_one_time_request(request) && dev->vq_index != 0) {
|
|
|
|
return 0;
|
2015-10-09 23:17:26 +08:00
|
|
|
}
|
2015-10-09 23:17:23 +08:00
|
|
|
|
2016-07-27 05:15:11 +08:00
|
|
|
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-10-09 23:17:28 +08:00
|
|
|
|
|
|
|
if (vhost_user_read(dev, &msg) < 0) {
|
2016-07-27 05:15:11 +08:00
|
|
|
return -1;
|
2015-10-09 23:17:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (msg.request != request) {
|
|
|
|
error_report("Received unexpected msg type. Expected %d received %d",
|
|
|
|
request, msg.request);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-10-23 03:31:28 +08:00
|
|
|
if (msg.size != sizeof(msg.payload.u64)) {
|
2015-10-09 23:17:28 +08:00
|
|
|
error_report("Received bad msg size.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-10-23 03:28:37 +08:00
|
|
|
*u64 = msg.payload.u64;
|
2015-10-09 23:17:28 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_get_features(struct vhost_dev *dev, uint64_t *features)
|
|
|
|
{
|
|
|
|
return vhost_user_get_u64(dev, VHOST_USER_GET_FEATURES, features);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_set_owner(struct vhost_dev *dev)
|
|
|
|
{
|
|
|
|
VhostUserMsg msg = {
|
|
|
|
.request = VHOST_USER_SET_OWNER,
|
|
|
|
.flags = VHOST_USER_VERSION,
|
|
|
|
};
|
|
|
|
|
2016-07-27 05:15:11 +08:00
|
|
|
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-10-09 23:17:28 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_reset_device(struct vhost_dev *dev)
|
|
|
|
{
|
|
|
|
VhostUserMsg msg = {
|
2015-11-11 21:24:37 +08:00
|
|
|
.request = VHOST_USER_RESET_OWNER,
|
2015-10-09 23:17:28 +08:00
|
|
|
.flags = VHOST_USER_VERSION,
|
|
|
|
};
|
|
|
|
|
2016-07-27 05:15:11 +08:00
|
|
|
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-10-09 23:17:28 +08:00
|
|
|
|
2015-10-09 23:17:23 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-27 20:06:02 +08:00
|
|
|
static int vhost_user_init(struct vhost_dev *dev, void *opaque)
|
|
|
|
{
|
2015-10-09 23:17:28 +08:00
|
|
|
uint64_t features;
|
2015-09-23 12:19:56 +08:00
|
|
|
int err;
|
|
|
|
|
2014-05-27 20:06:02 +08:00
|
|
|
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
|
|
|
|
|
|
|
|
dev->opaque = opaque;
|
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
err = vhost_user_get_features(dev, &features);
|
2015-09-23 12:19:56 +08:00
|
|
|
if (err < 0) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virtio_has_feature(features, VHOST_USER_F_PROTOCOL_FEATURES)) {
|
|
|
|
dev->backend_features |= 1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
|
|
|
|
|
2015-10-09 23:17:28 +08:00
|
|
|
err = vhost_user_get_u64(dev, VHOST_USER_GET_PROTOCOL_FEATURES,
|
|
|
|
&features);
|
2015-09-23 12:19:56 +08:00
|
|
|
if (err < 0) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->protocol_features = features & VHOST_USER_PROTOCOL_FEATURE_MASK;
|
2015-10-09 23:17:28 +08:00
|
|
|
err = vhost_user_set_protocol_features(dev, dev->protocol_features);
|
2015-09-23 12:19:56 +08:00
|
|
|
if (err < 0) {
|
|
|
|
return err;
|
|
|
|
}
|
2015-09-23 12:19:58 +08:00
|
|
|
|
|
|
|
/* query the max queues we support if backend supports Multiple Queue */
|
|
|
|
if (dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_MQ)) {
|
2015-10-09 23:17:28 +08:00
|
|
|
err = vhost_user_get_u64(dev, VHOST_USER_GET_QUEUE_NUM,
|
|
|
|
&dev->max_queues);
|
2015-09-23 12:19:58 +08:00
|
|
|
if (err < 0) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
2015-09-23 12:19:56 +08:00
|
|
|
}
|
|
|
|
|
2015-10-09 23:17:27 +08:00
|
|
|
if (dev->migration_blocker == NULL &&
|
|
|
|
!virtio_has_feature(dev->protocol_features,
|
|
|
|
VHOST_USER_PROTOCOL_F_LOG_SHMFD)) {
|
|
|
|
error_setg(&dev->migration_blocker,
|
|
|
|
"Migration disabled: vhost-user backend lacks "
|
|
|
|
"VHOST_USER_PROTOCOL_F_LOG_SHMFD feature.");
|
|
|
|
}
|
|
|
|
|
2014-05-27 20:06:02 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_cleanup(struct vhost_dev *dev)
|
|
|
|
{
|
|
|
|
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
|
|
|
|
|
|
|
|
dev->opaque = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-23 12:19:59 +08:00
|
|
|
static int vhost_user_get_vq_index(struct vhost_dev *dev, int idx)
|
|
|
|
{
|
|
|
|
assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
2015-10-06 16:37:27 +08:00
|
|
|
static int vhost_user_memslots_limit(struct vhost_dev *dev)
|
|
|
|
{
|
|
|
|
return VHOST_MEMORY_MAX_NREGIONS;
|
|
|
|
}
|
|
|
|
|
2015-10-09 23:17:24 +08:00
|
|
|
static bool vhost_user_requires_shm_log(struct vhost_dev *dev)
|
|
|
|
{
|
|
|
|
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
|
|
|
|
|
|
|
|
return virtio_has_feature(dev->protocol_features,
|
|
|
|
VHOST_USER_PROTOCOL_F_LOG_SHMFD);
|
|
|
|
}
|
|
|
|
|
2015-10-09 23:17:32 +08:00
|
|
|
static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr)
|
|
|
|
{
|
|
|
|
VhostUserMsg msg = { 0 };
|
|
|
|
|
|
|
|
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
|
|
|
|
|
|
|
|
/* If guest supports GUEST_ANNOUNCE do nothing */
|
|
|
|
if (virtio_has_feature(dev->acked_features, VIRTIO_NET_F_GUEST_ANNOUNCE)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if backend supports VHOST_USER_PROTOCOL_F_RARP ask it to send the RARP */
|
|
|
|
if (virtio_has_feature(dev->protocol_features,
|
|
|
|
VHOST_USER_PROTOCOL_F_RARP)) {
|
|
|
|
msg.request = VHOST_USER_SEND_RARP;
|
|
|
|
msg.flags = VHOST_USER_VERSION;
|
2015-10-23 03:28:37 +08:00
|
|
|
memcpy((char *)&msg.payload.u64, mac_addr, 6);
|
2015-10-23 03:31:28 +08:00
|
|
|
msg.size = sizeof(msg.payload.u64);
|
2015-10-09 23:17:32 +08:00
|
|
|
|
2016-07-27 05:15:11 +08:00
|
|
|
return vhost_user_write(dev, &msg, NULL, 0);
|
2015-10-09 23:17:32 +08:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-02-21 23:01:47 +08:00
|
|
|
static bool vhost_user_can_merge(struct vhost_dev *dev,
|
|
|
|
uint64_t start1, uint64_t size1,
|
|
|
|
uint64_t start2, uint64_t size2)
|
|
|
|
{
|
2016-03-25 19:55:08 +08:00
|
|
|
ram_addr_t offset;
|
2016-02-21 23:01:47 +08:00
|
|
|
int mfd, rfd;
|
|
|
|
MemoryRegion *mr;
|
|
|
|
|
2016-03-25 19:55:08 +08:00
|
|
|
mr = memory_region_from_host((void *)(uintptr_t)start1, &offset);
|
2016-03-25 19:30:16 +08:00
|
|
|
mfd = memory_region_get_fd(mr);
|
2016-02-21 23:01:47 +08:00
|
|
|
|
2016-03-25 19:55:08 +08:00
|
|
|
mr = memory_region_from_host((void *)(uintptr_t)start2, &offset);
|
2016-03-25 19:30:16 +08:00
|
|
|
rfd = memory_region_get_fd(mr);
|
2016-02-21 23:01:47 +08:00
|
|
|
|
|
|
|
return mfd == rfd;
|
|
|
|
}
|
|
|
|
|
2014-05-27 20:06:02 +08:00
|
|
|
const VhostOps user_ops = {
|
|
|
|
.backend_type = VHOST_BACKEND_TYPE_USER,
|
|
|
|
.vhost_backend_init = vhost_user_init,
|
2015-09-23 12:19:59 +08:00
|
|
|
.vhost_backend_cleanup = vhost_user_cleanup,
|
2015-10-06 16:37:27 +08:00
|
|
|
.vhost_backend_memslots_limit = vhost_user_memslots_limit,
|
2015-10-09 23:17:28 +08:00
|
|
|
.vhost_set_log_base = vhost_user_set_log_base,
|
|
|
|
.vhost_set_mem_table = vhost_user_set_mem_table,
|
|
|
|
.vhost_set_vring_addr = vhost_user_set_vring_addr,
|
|
|
|
.vhost_set_vring_endian = vhost_user_set_vring_endian,
|
|
|
|
.vhost_set_vring_num = vhost_user_set_vring_num,
|
|
|
|
.vhost_set_vring_base = vhost_user_set_vring_base,
|
|
|
|
.vhost_get_vring_base = vhost_user_get_vring_base,
|
|
|
|
.vhost_set_vring_kick = vhost_user_set_vring_kick,
|
|
|
|
.vhost_set_vring_call = vhost_user_set_vring_call,
|
|
|
|
.vhost_set_features = vhost_user_set_features,
|
|
|
|
.vhost_get_features = vhost_user_get_features,
|
|
|
|
.vhost_set_owner = vhost_user_set_owner,
|
|
|
|
.vhost_reset_device = vhost_user_reset_device,
|
|
|
|
.vhost_get_vq_index = vhost_user_get_vq_index,
|
|
|
|
.vhost_set_vring_enable = vhost_user_set_vring_enable,
|
2015-10-09 23:17:24 +08:00
|
|
|
.vhost_requires_shm_log = vhost_user_requires_shm_log,
|
2015-10-09 23:17:32 +08:00
|
|
|
.vhost_migration_done = vhost_user_migration_done,
|
2016-02-21 23:01:47 +08:00
|
|
|
.vhost_backend_can_merge = vhost_user_can_merge,
|
2015-09-23 12:19:59 +08:00
|
|
|
};
|