*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/virtio/vhost.h"
#include "hw/virtio/vhost-backend.h"
#include "hw/virtio/virtio-net.h"
#include "sysemu/kvm.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"
-#include "exec/ram_addr.h"
#include "migration/migration.h"
-#include <fcntl.h>
-#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
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_MAX
};
VHOST_USER_GET_FEATURES = 1,
VHOST_USER_SET_FEATURES = 2,
VHOST_USER_SET_OWNER = 3,
- VHOST_USER_RESET_DEVICE = 4,
+ 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_GET_QUEUE_NUM = 17,
VHOST_USER_SET_VRING_ENABLE = 18,
VHOST_USER_SEND_RARP = 19,
+ VHOST_USER_NET_SET_MTU = 20,
VHOST_USER_MAX
} VhostUserRequest;
VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
} VhostUserMemory;
+typedef struct VhostUserLog {
+ uint64_t mmap_size;
+ uint64_t mmap_offset;
+} VhostUserLog;
+
typedef struct VhostUserMsg {
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 */
union {
struct vhost_vring_state state;
struct vhost_vring_addr addr;
VhostUserMemory memory;
- };
+ VhostUserLog log;
+ } payload;
} QEMU_PACKED VhostUserMsg;
static VhostUserMsg m __attribute__ ((unused));
static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
{
- CharDriverState *chr = dev->opaque;
+ CharBackend *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) {
- error_report("Failed to read msg header. Read %d instead of %d.", r,
- size);
+ error_report("Failed to read msg header. Read %d instead of %d."
+ " Original request %d.", r, size, msg->request);
goto fail;
}
return -1;
}
+static int process_message_reply(struct vhost_dev *dev,
+ VhostUserMsg msg)
+{
+ VhostUserMsg msg_reply;
+
+ if ((msg.flags & VHOST_USER_NEED_REPLY_MASK) == 0) {
+ return 0;
+ }
+
+ if (vhost_user_read(dev, &msg_reply) < 0) {
+ return -1;
+ }
+
+ if (msg_reply.request != msg.request) {
+ error_report("Received unexpected msg type."
+ "Expected %d received %d",
+ msg.request, msg_reply.request);
+ return -1;
+ }
+
+ return msg_reply.payload.u64 ? -1 : 0;
+}
+
static bool vhost_user_one_time_request(VhostUserRequest request)
{
switch (request) {
case VHOST_USER_SET_OWNER:
- case VHOST_USER_RESET_DEVICE:
+ case VHOST_USER_RESET_OWNER:
case VHOST_USER_SET_MEM_TABLE:
case VHOST_USER_GET_QUEUE_NUM:
+ case VHOST_USER_NET_SET_MTU:
return true;
default:
return false;
static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg,
int *fds, int fd_num)
{
- CharDriverState *chr = dev->opaque;
- int size = VHOST_USER_HDR_SIZE + msg->size;
+ CharBackend *chr = dev->opaque;
+ int ret, size = VHOST_USER_HDR_SIZE + msg->size;
/*
* For non-vring specific requests, like VHOST_USER_SET_MEM_TABLE,
* request, we just ignore it.
*/
if (vhost_user_one_time_request(msg->request) && dev->vq_index != 0) {
+ msg->flags &= ~VHOST_USER_NEED_REPLY_MASK;
return 0;
}
- if (fd_num) {
- qemu_chr_fe_set_msgfds(chr, fds, fd_num);
+ if (qemu_chr_fe_set_msgfds(chr, fds, fd_num) < 0) {
+ error_report("Failed to set msg fds.");
+ return -1;
+ }
+
+ 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 qemu_chr_fe_write_all(chr, (const uint8_t *) msg, size) == size ?
- 0 : -1;
+ return 0;
}
static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base,
VhostUserMsg msg = {
.request = VHOST_USER_SET_LOG_BASE,
.flags = VHOST_USER_VERSION,
- .u64 = base,
- .size = sizeof(m.u64),
+ .payload.log.mmap_size = log->size * sizeof(*(log->log)),
+ .payload.log.mmap_offset = 0,
+ .size = sizeof(msg.payload.log),
};
if (shmfd && log->fd != -1) {
fds[fd_num++] = log->fd;
}
- vhost_user_write(dev, &msg, fds, fd_num);
+ if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
+ return -1;
+ }
if (shmfd) {
msg.size = 0;
if (vhost_user_read(dev, &msg) < 0) {
- return 0;
+ return -1;
}
if (msg.request != VHOST_USER_SET_LOG_BASE) {
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 ram_addr;
+ ram_addr_t offset;
+ MemoryRegion *mr;
assert((uintptr_t)reg->userspace_addr == reg->userspace_addr);
- qemu_ram_addr_from_host((void *)(uintptr_t)reg->userspace_addr,
- &ram_addr);
- fd = qemu_get_ram_fd(ram_addr);
+ mr = memory_region_from_host((void *)(uintptr_t)reg->userspace_addr,
+ &offset);
+ fd = memory_region_get_fd(mr);
if (fd > 0) {
- msg.memory.regions[fd_num].userspace_addr = reg->userspace_addr;
- msg.memory.regions[fd_num].memory_size = reg->memory_size;
- msg.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr;
- msg.memory.regions[fd_num].mmap_offset = reg->userspace_addr -
- (uintptr_t) qemu_get_ram_block_host_ptr(ram_addr);
+ 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.memory.nregions = fd_num;
+ msg.payload.memory.nregions = fd_num;
if (!fd_num) {
error_report("Failed initializing vhost-user memory map, "
return -1;
}
- msg.size = sizeof(m.memory.nregions);
- msg.size += sizeof(m.memory.padding);
+ msg.size = sizeof(msg.payload.memory.nregions);
+ msg.size += sizeof(msg.payload.memory.padding);
msg.size += fd_num * sizeof(VhostUserMemoryRegion);
- vhost_user_write(dev, &msg, fds, fd_num);
+ if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
+ return -1;
+ }
+
+ if (reply_supported) {
+ return process_message_reply(dev, msg);
+ }
return 0;
}
VhostUserMsg msg = {
.request = VHOST_USER_SET_VRING_ADDR,
.flags = VHOST_USER_VERSION,
- .addr = *addr,
- .size = sizeof(*addr),
+ .payload.addr = *addr,
+ .size = sizeof(msg.payload.addr),
};
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
return 0;
}
VhostUserMsg msg = {
.request = request,
.flags = VHOST_USER_VERSION,
- .state = *ring,
- .size = sizeof(*ring),
+ .payload.state = *ring,
+ .size = sizeof(msg.payload.state),
};
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
return 0;
}
static int vhost_user_set_vring_enable(struct vhost_dev *dev, int enable)
{
- struct vhost_vring_state state = {
- .index = dev->vq_index,
- .num = enable,
- };
+ int i;
- if (!(dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_MQ))) {
+ if (!virtio_has_feature(dev->features, VHOST_USER_F_PROTOCOL_FEATURES)) {
return -1;
}
- return vhost_set_vring(dev, VHOST_USER_SET_VRING_ENABLE, &state);
-}
+ 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);
+ }
+
+ return 0;
+}
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,
- .state = *ring,
- .size = sizeof(*ring),
+ .payload.state = *ring,
+ .size = sizeof(msg.payload.state),
};
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
if (vhost_user_read(dev, &msg) < 0) {
- return 0;
+ return -1;
}
if (msg.request != VHOST_USER_GET_VRING_BASE) {
return -1;
}
- if (msg.size != sizeof(m.state)) {
+ if (msg.size != sizeof(msg.payload.state)) {
error_report("Received bad msg size.");
return -1;
}
- *ring = msg.state;
+ *ring = msg.payload.state;
return 0;
}
VhostUserMsg msg = {
.request = request,
.flags = VHOST_USER_VERSION,
- .u64 = file->index & VHOST_USER_VRING_IDX_MASK,
- .size = sizeof(m.u64),
+ .payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK,
+ .size = sizeof(msg.payload.u64),
};
if (ioeventfd_enabled() && file->fd > 0) {
fds[fd_num++] = file->fd;
} else {
- msg.u64 |= VHOST_USER_VRING_NOFD_MASK;
+ msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK;
}
- vhost_user_write(dev, &msg, fds, fd_num);
+ if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
+ return -1;
+ }
return 0;
}
VhostUserMsg msg = {
.request = request,
.flags = VHOST_USER_VERSION,
- .u64 = u64,
- .size = sizeof(m.u64),
+ .payload.u64 = u64,
+ .size = sizeof(msg.payload.u64),
};
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
return 0;
}
return 0;
}
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
if (vhost_user_read(dev, &msg) < 0) {
- return 0;
+ return -1;
}
if (msg.request != request) {
return -1;
}
- if (msg.size != sizeof(m.u64)) {
+ if (msg.size != sizeof(msg.payload.u64)) {
error_report("Received bad msg size.");
return -1;
}
- *u64 = msg.u64;
+ *u64 = msg.payload.u64;
return 0;
}
.flags = VHOST_USER_VERSION,
};
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
return 0;
}
static int vhost_user_reset_device(struct vhost_dev *dev)
{
VhostUserMsg msg = {
- .request = VHOST_USER_RESET_DEVICE,
+ .request = VHOST_USER_RESET_OWNER,
.flags = VHOST_USER_VERSION,
};
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
return 0;
}
static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr)
{
VhostUserMsg msg = { 0 };
- int err;
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
VHOST_USER_PROTOCOL_F_RARP)) {
msg.request = VHOST_USER_SEND_RARP;
msg.flags = VHOST_USER_VERSION;
- memcpy((char *)&msg.u64, mac_addr, 6);
- msg.size = sizeof(m.u64);
+ memcpy((char *)&msg.payload.u64, mac_addr, 6);
+ msg.size = sizeof(msg.payload.u64);
- err = vhost_user_write(dev, &msg, NULL, 0);
- return err;
+ return vhost_user_write(dev, &msg, NULL, 0);
}
return -1;
}
+static bool vhost_user_can_merge(struct vhost_dev *dev,
+ uint64_t start1, uint64_t size1,
+ uint64_t start2, uint64_t size2)
+{
+ ram_addr_t offset;
+ int mfd, rfd;
+ MemoryRegion *mr;
+
+ mr = memory_region_from_host((void *)(uintptr_t)start1, &offset);
+ mfd = memory_region_get_fd(mr);
+
+ mr = memory_region_from_host((void *)(uintptr_t)start2, &offset);
+ rfd = memory_region_get_fd(mr);
+
+ return mfd == rfd;
+}
+
+static int vhost_user_net_set_mtu(struct vhost_dev *dev, uint16_t mtu)
+{
+ VhostUserMsg msg;
+ bool reply_supported = virtio_has_feature(dev->protocol_features,
+ VHOST_USER_PROTOCOL_F_REPLY_ACK);
+
+ if (!(dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_NET_MTU))) {
+ return 0;
+ }
+
+ msg.request = VHOST_USER_NET_SET_MTU;
+ msg.payload.u64 = mtu;
+ msg.size = sizeof(msg.payload.u64);
+ msg.flags = VHOST_USER_VERSION;
+ if (reply_supported) {
+ msg.flags |= VHOST_USER_NEED_REPLY_MASK;
+ }
+
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
+
+ /* If reply_ack supported, slave has to ack specified MTU is valid */
+ if (reply_supported) {
+ return process_message_reply(dev, msg);
+ }
+
+ return 0;
+}
+
const VhostOps user_ops = {
.backend_type = VHOST_BACKEND_TYPE_USER,
.vhost_backend_init = vhost_user_init,
.vhost_set_vring_enable = vhost_user_set_vring_enable,
.vhost_requires_shm_log = vhost_user_requires_shm_log,
.vhost_migration_done = vhost_user_migration_done,
+ .vhost_backend_can_merge = vhost_user_can_merge,
+ .vhost_net_set_mtu = vhost_user_net_set_mtu,
};