mirror of https://github.com/xemu-project/xemu.git
vhost-user: support registering external host notifiers
This patch introduces VHOST_USER_PROTOCOL_F_HOST_NOTIFIER. With this feature negotiated, vhost-user backend can register memory region based host notifiers. And it will allow the guest driver in the VM to notify the hardware accelerator at the vhost-user backend directly. Signed-off-by: Tiwei Bie <tiwei.bie@intel.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
4d0cf552d3
commit
44866521bd
|
@ -132,6 +132,16 @@ Depending on the request type, payload can be:
|
||||||
Payload: Size bytes array holding the contents of the virtio
|
Payload: Size bytes array holding the contents of the virtio
|
||||||
device's configuration space
|
device's configuration space
|
||||||
|
|
||||||
|
* Vring area description
|
||||||
|
-----------------------
|
||||||
|
| u64 | size | offset |
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
u64: a 64-bit integer contains vring index and flags
|
||||||
|
Size: a 64-bit size of this area
|
||||||
|
Offset: a 64-bit offset of this area from the start of the
|
||||||
|
supplied file descriptor
|
||||||
|
|
||||||
In QEMU the vhost-user message is implemented with the following struct:
|
In QEMU the vhost-user message is implemented with the following struct:
|
||||||
|
|
||||||
typedef struct VhostUserMsg {
|
typedef struct VhostUserMsg {
|
||||||
|
@ -146,6 +156,7 @@ typedef struct VhostUserMsg {
|
||||||
VhostUserLog log;
|
VhostUserLog log;
|
||||||
struct vhost_iotlb_msg iotlb;
|
struct vhost_iotlb_msg iotlb;
|
||||||
VhostUserConfig config;
|
VhostUserConfig config;
|
||||||
|
VhostUserVringArea area;
|
||||||
};
|
};
|
||||||
} QEMU_PACKED VhostUserMsg;
|
} QEMU_PACKED VhostUserMsg;
|
||||||
|
|
||||||
|
@ -385,6 +396,7 @@ Protocol features
|
||||||
#define VHOST_USER_PROTOCOL_F_PAGEFAULT 8
|
#define VHOST_USER_PROTOCOL_F_PAGEFAULT 8
|
||||||
#define VHOST_USER_PROTOCOL_F_CONFIG 9
|
#define VHOST_USER_PROTOCOL_F_CONFIG 9
|
||||||
#define VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD 10
|
#define VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD 10
|
||||||
|
#define VHOST_USER_PROTOCOL_F_HOST_NOTIFIER 11
|
||||||
|
|
||||||
Master message types
|
Master message types
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -782,6 +794,27 @@ Slave message types
|
||||||
the VHOST_USER_NEED_REPLY flag, master must respond with zero when
|
the VHOST_USER_NEED_REPLY flag, master must respond with zero when
|
||||||
operation is successfully completed, or non-zero otherwise.
|
operation is successfully completed, or non-zero otherwise.
|
||||||
|
|
||||||
|
* VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG
|
||||||
|
|
||||||
|
Id: 3
|
||||||
|
Equivalent ioctl: N/A
|
||||||
|
Slave payload: vring area description
|
||||||
|
Master payload: N/A
|
||||||
|
|
||||||
|
Sets host notifier for a specified queue. The queue index is contained
|
||||||
|
in the u64 field of the vring area description. The host notifier is
|
||||||
|
described by the file descriptor (typically it's a VFIO device fd) which
|
||||||
|
is passed as ancillary data and the size (which is mmap size and should
|
||||||
|
be the same as host page size) and offset (which is mmap offset) carried
|
||||||
|
in the vring area description. QEMU can mmap the file descriptor based
|
||||||
|
on the size and offset to get a memory range. Registering a host notifier
|
||||||
|
means mapping this memory range to the VM as the specified queue's notify
|
||||||
|
MMIO region. Slave sends this request to tell QEMU to de-register the
|
||||||
|
existing notifier if any and register the new notifier if the request is
|
||||||
|
sent with a file descriptor.
|
||||||
|
This request should be sent only when VHOST_USER_PROTOCOL_F_HOST_NOTIFIER
|
||||||
|
protocol feature has been successfully negotiated.
|
||||||
|
|
||||||
VHOST_USER_PROTOCOL_F_REPLY_ACK:
|
VHOST_USER_PROTOCOL_F_REPLY_ACK:
|
||||||
-------------------------------
|
-------------------------------
|
||||||
The original vhost-user specification only demands replies for certain
|
The original vhost-user specification only demands replies for certain
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "hw/virtio/vhost.h"
|
#include "hw/virtio/vhost.h"
|
||||||
#include "hw/virtio/vhost-user.h"
|
#include "hw/virtio/vhost-user.h"
|
||||||
#include "hw/virtio/vhost-backend.h"
|
#include "hw/virtio/vhost-backend.h"
|
||||||
|
#include "hw/virtio/virtio.h"
|
||||||
#include "hw/virtio/virtio-net.h"
|
#include "hw/virtio/virtio-net.h"
|
||||||
#include "chardev/char-fe.h"
|
#include "chardev/char-fe.h"
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
|
@ -50,6 +51,7 @@ enum VhostUserProtocolFeature {
|
||||||
VHOST_USER_PROTOCOL_F_PAGEFAULT = 8,
|
VHOST_USER_PROTOCOL_F_PAGEFAULT = 8,
|
||||||
VHOST_USER_PROTOCOL_F_CONFIG = 9,
|
VHOST_USER_PROTOCOL_F_CONFIG = 9,
|
||||||
VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10,
|
VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10,
|
||||||
|
VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11,
|
||||||
VHOST_USER_PROTOCOL_F_MAX
|
VHOST_USER_PROTOCOL_F_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -94,6 +96,7 @@ typedef enum VhostUserSlaveRequest {
|
||||||
VHOST_USER_SLAVE_NONE = 0,
|
VHOST_USER_SLAVE_NONE = 0,
|
||||||
VHOST_USER_SLAVE_IOTLB_MSG = 1,
|
VHOST_USER_SLAVE_IOTLB_MSG = 1,
|
||||||
VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
|
VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
|
||||||
|
VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3,
|
||||||
VHOST_USER_SLAVE_MAX
|
VHOST_USER_SLAVE_MAX
|
||||||
} VhostUserSlaveRequest;
|
} VhostUserSlaveRequest;
|
||||||
|
|
||||||
|
@ -138,6 +141,12 @@ static VhostUserConfig c __attribute__ ((unused));
|
||||||
+ sizeof(c.size) \
|
+ sizeof(c.size) \
|
||||||
+ sizeof(c.flags))
|
+ sizeof(c.flags))
|
||||||
|
|
||||||
|
typedef struct VhostUserVringArea {
|
||||||
|
uint64_t u64;
|
||||||
|
uint64_t size;
|
||||||
|
uint64_t offset;
|
||||||
|
} VhostUserVringArea;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
VhostUserRequest request;
|
VhostUserRequest request;
|
||||||
|
|
||||||
|
@ -159,6 +168,7 @@ typedef union {
|
||||||
struct vhost_iotlb_msg iotlb;
|
struct vhost_iotlb_msg iotlb;
|
||||||
VhostUserConfig config;
|
VhostUserConfig config;
|
||||||
VhostUserCryptoSession session;
|
VhostUserCryptoSession session;
|
||||||
|
VhostUserVringArea area;
|
||||||
} VhostUserPayload;
|
} VhostUserPayload;
|
||||||
|
|
||||||
typedef struct VhostUserMsg {
|
typedef struct VhostUserMsg {
|
||||||
|
@ -640,9 +650,37 @@ static int vhost_user_set_vring_num(struct vhost_dev *dev,
|
||||||
return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring);
|
return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void vhost_user_host_notifier_restore(struct vhost_dev *dev,
|
||||||
|
int queue_idx)
|
||||||
|
{
|
||||||
|
struct vhost_user *u = dev->opaque;
|
||||||
|
VhostUserHostNotifier *n = &u->user->notifier[queue_idx];
|
||||||
|
VirtIODevice *vdev = dev->vdev;
|
||||||
|
|
||||||
|
if (n->addr && !n->set) {
|
||||||
|
virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true);
|
||||||
|
n->set = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vhost_user_host_notifier_remove(struct vhost_dev *dev,
|
||||||
|
int queue_idx)
|
||||||
|
{
|
||||||
|
struct vhost_user *u = dev->opaque;
|
||||||
|
VhostUserHostNotifier *n = &u->user->notifier[queue_idx];
|
||||||
|
VirtIODevice *vdev = dev->vdev;
|
||||||
|
|
||||||
|
if (n->addr && n->set) {
|
||||||
|
virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false);
|
||||||
|
n->set = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int vhost_user_set_vring_base(struct vhost_dev *dev,
|
static int vhost_user_set_vring_base(struct vhost_dev *dev,
|
||||||
struct vhost_vring_state *ring)
|
struct vhost_vring_state *ring)
|
||||||
{
|
{
|
||||||
|
vhost_user_host_notifier_restore(dev, ring->index);
|
||||||
|
|
||||||
return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring);
|
return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,6 +714,8 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev,
|
||||||
.hdr.size = sizeof(msg.payload.state),
|
.hdr.size = sizeof(msg.payload.state),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vhost_user_host_notifier_remove(dev, ring->index);
|
||||||
|
|
||||||
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -849,6 +889,66 @@ static int vhost_user_slave_handle_config_change(struct vhost_dev *dev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev,
|
||||||
|
VhostUserVringArea *area,
|
||||||
|
int fd)
|
||||||
|
{
|
||||||
|
int queue_idx = area->u64 & VHOST_USER_VRING_IDX_MASK;
|
||||||
|
size_t page_size = qemu_real_host_page_size;
|
||||||
|
struct vhost_user *u = dev->opaque;
|
||||||
|
VhostUserState *user = u->user;
|
||||||
|
VirtIODevice *vdev = dev->vdev;
|
||||||
|
VhostUserHostNotifier *n;
|
||||||
|
void *addr;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
if (!virtio_has_feature(dev->protocol_features,
|
||||||
|
VHOST_USER_PROTOCOL_F_HOST_NOTIFIER) ||
|
||||||
|
vdev == NULL || queue_idx >= virtio_get_num_queues(vdev)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = &user->notifier[queue_idx];
|
||||||
|
|
||||||
|
if (n->addr) {
|
||||||
|
virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false);
|
||||||
|
object_unparent(OBJECT(&n->mr));
|
||||||
|
munmap(n->addr, page_size);
|
||||||
|
n->addr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (area->u64 & VHOST_USER_VRING_NOFD_MASK) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sanity check. */
|
||||||
|
if (area->size != page_size) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||||
|
fd, area->offset);
|
||||||
|
if (addr == MAP_FAILED) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = g_strdup_printf("vhost-user/host-notifier@%p mmaps[%d]",
|
||||||
|
user, queue_idx);
|
||||||
|
memory_region_init_ram_device_ptr(&n->mr, OBJECT(vdev), name,
|
||||||
|
page_size, addr);
|
||||||
|
g_free(name);
|
||||||
|
|
||||||
|
if (virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true)) {
|
||||||
|
munmap(addr, page_size);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n->addr = addr;
|
||||||
|
n->set = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void slave_read(void *opaque)
|
static void slave_read(void *opaque)
|
||||||
{
|
{
|
||||||
struct vhost_dev *dev = opaque;
|
struct vhost_dev *dev = opaque;
|
||||||
|
@ -917,6 +1017,10 @@ static void slave_read(void *opaque)
|
||||||
case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG :
|
case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG :
|
||||||
ret = vhost_user_slave_handle_config_change(dev);
|
ret = vhost_user_slave_handle_config_change(dev);
|
||||||
break;
|
break;
|
||||||
|
case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG:
|
||||||
|
ret = vhost_user_slave_handle_vring_host_notifier(dev, &payload.area,
|
||||||
|
fd[0]);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
error_report("Received unexpected msg type.");
|
error_report("Received unexpected msg type.");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@ -1648,6 +1752,15 @@ VhostUserState *vhost_user_init(void)
|
||||||
|
|
||||||
void vhost_user_cleanup(VhostUserState *user)
|
void vhost_user_cleanup(VhostUserState *user)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
|
||||||
|
if (user->notifier[i].addr) {
|
||||||
|
object_unparent(OBJECT(&user->notifier[i].mr));
|
||||||
|
munmap(user->notifier[i].addr, qemu_real_host_page_size);
|
||||||
|
user->notifier[i].addr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const VhostOps user_ops = {
|
const VhostOps user_ops = {
|
||||||
|
|
|
@ -9,9 +9,17 @@
|
||||||
#define HW_VIRTIO_VHOST_USER_H
|
#define HW_VIRTIO_VHOST_USER_H
|
||||||
|
|
||||||
#include "chardev/char-fe.h"
|
#include "chardev/char-fe.h"
|
||||||
|
#include "hw/virtio/virtio.h"
|
||||||
|
|
||||||
|
typedef struct VhostUserHostNotifier {
|
||||||
|
MemoryRegion mr;
|
||||||
|
void *addr;
|
||||||
|
bool set;
|
||||||
|
} VhostUserHostNotifier;
|
||||||
|
|
||||||
typedef struct VhostUserState {
|
typedef struct VhostUserState {
|
||||||
CharBackend *chr;
|
CharBackend *chr;
|
||||||
|
VhostUserHostNotifier notifier[VIRTIO_QUEUE_MAX];
|
||||||
} VhostUserState;
|
} VhostUserState;
|
||||||
|
|
||||||
VhostUserState *vhost_user_init(void);
|
VhostUserState *vhost_user_init(void);
|
||||||
|
|
Loading…
Reference in New Issue