From: Marc-André Lureau <marcandre.lur...@redhat.com> Learn to give a socket to the slave to let him make requests to the master.
Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- docs/specs/vhost-user.txt | 23 +++++++++++++++ hw/virtio/vhost-user.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/docs/specs/vhost-user.txt b/docs/specs/vhost-user.txt index 1bc6adb..5e00bd3 100644 --- a/docs/specs/vhost-user.txt +++ b/docs/specs/vhost-user.txt @@ -136,10 +136,23 @@ As older slaves don't support negotiating protocol features, a feature bit was dedicated for this purpose: #define VHOST_USER_F_PROTOCOL_FEATURES 30 +Slave communication +------------------- + +Since the vhost-user protocol do not let slave make requests back to +the master, an optional communication channel is provided if the slave +declares VHOST_USER_PROTOCOL_F_SLAVE_REQ feature. + +The fd is provided via VHOST_USER_SET_SLAVE_FD ancillary data. + +A slave may then send VHOST_USER_SLAVE_* messages to the master by +using this fd. + Protocol features ----------------- #define VHOST_USER_PROTOCOL_F_LOG_SHMFD 0 +#define VHOST_USER_PROTOCOL_F_SLAVE_REQ 1 Message types ------------- @@ -308,6 +321,16 @@ Message types invalid FD flag. This flag is set when there is no file descriptor in the ancillary data. + * VHOST_USER_SET_SLAVE_FD + Id: 17 + Equivalent ioctl: N/A + Master payload: N/A + + Set the file descriptor for the salve to make VHOST_USER_SLAVE_* + request to the master. It is passed in the ancillary data. + This message is only sent if VHOST_USER_PROTOCOL_F_SLAVE_REQ + feature is available. + Migration --------- diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 2875b69..49f566c 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -27,8 +27,9 @@ #define VHOST_USER_F_PROTOCOL_FEATURES 30 -#define VHOST_USER_PROTOCOL_FEATURE_MASK 0x1ULL +#define VHOST_USER_PROTOCOL_FEATURE_MASK 0x3ULL #define VHOST_USER_PROTOCOL_F_LOG_SHMFD 0 +#define VHOST_USER_PROTOCOL_F_SLAVE_REQ 1 typedef enum VhostUserRequest { VHOST_USER_NONE = 0, @@ -48,9 +49,15 @@ typedef enum VhostUserRequest { VHOST_USER_SET_VRING_ERR = 14, VHOST_USER_GET_PROTOCOL_FEATURES = 15, VHOST_USER_SET_PROTOCOL_FEATURES = 16, + VHOST_USER_SET_SLAVE_FD = 17, VHOST_USER_MAX } VhostUserRequest; +typedef enum VhostUserSlaveRequest { + VHOST_USER_SLAVE_NONE = 0, + VHOST_USER_SLAVE_MAX +} VhostUserSlaveRequest; + typedef struct VhostUserMemoryRegion { uint64_t guest_phys_addr; uint64_t memory_size; @@ -93,6 +100,7 @@ static VhostUserMsg m __attribute__ ((unused)); struct vhost_user { CharDriverState *chr; + CharDriverState *slave_chr; }; static bool ioeventfd_enabled(void) @@ -277,6 +285,7 @@ static int vhost_user_call(struct vhost_dev *dev, break; + case VHOST_USER_SET_SLAVE_FD: case VHOST_USER_SET_LOG_FD: fds[fd_num++] = *((int *) arg); break; @@ -361,6 +370,43 @@ end: return ret; } +static int slave_can_receive(void *opaque) +{ + return VHOST_USER_HDR_SIZE; +} + +static void slave_receive(void *opaque, const uint8_t *buf, int size) +{ + struct vhost_dev *dev = opaque; + VhostUserMsg *msg = (VhostUserMsg *)buf; + + if (size != VHOST_USER_HDR_SIZE) { + error_report("Failed to read from slave."); + return; + } + + switch (msg->request) { + default: + error_report("Received unexpected msg type."); + } +} + +static void slave_event(void *opaque, int event) +{ + struct vhost_dev *dev = opaque; + struct vhost_user *u = dev->opaque; + CharDriverState *slave_chr = u->slave_chr; + + switch (event) { + case CHR_EVENT_CLOSED: + if (slave_chr) { + u->slave_chr = NULL; + qemu_chr_free(slave_chr); + } + break; + } +} + static int vhost_user_init(struct vhost_dev *dev, void *opaque) { VhostUserMsg msg = { 0 }; @@ -417,16 +463,39 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) } } + if (__virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_SLAVE_REQ)) { + int sv[2]; + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { + error_report("socketpair() failed"); + return -1; + } + + vhost_user_call(dev, VHOST_USER_SET_SLAVE_FD, &sv[1]); + + u->slave_chr = qemu_chr_open_eventfd(sv[0]); + qemu_chr_add_handlers(u->slave_chr, slave_can_receive, slave_receive, + slave_event, dev); + } + + return 0; } static int vhost_user_cleanup(struct vhost_dev *dev) { struct vhost_user *u; + CharDriverState *slave_chr; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); u = dev->opaque; + if (u->slave_chr) { + slave_chr = u->slave_chr; + u->slave_chr = NULL; + qemu_chr_free(slave_chr); + } g_free(u); dev->opaque = 0; -- 2.4.3