Support host notifier in libvhost-user and vhost-user-bridge
cover Support host notifier in libvhost-user and vhost-user-bridge
This patch set extends libvhost-user and vhost-user-bridge to support the host notifier [1] feature in vhost user.
A new API is added for libvhost-user to support setting host notifier for each queue:
/**
* vu_set_queue_host_notifier:
* @dev: a VuDev context
* @vq: a VuVirtq queue
* @fd: a file descriptor
* @size: host page size
* @offset: notifier offset in @fd file
*
* Set queue's host notifier. This function may be called several
* times for the same queue. If called with -1 @fd, the notifier
* is removed.
*/
bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd,
int size, int offset);
A new option (-H) is added for vhost-user-bridge to enable the host notifier. It's pretty simple to test:
# Launch vhost-user-bridge with host notifier enabled
$ make tests/vhost-user-bridge $ ./tests/vhost-user-bridge -H
# Launch QEMU on another terminal
$ ./x86_64-softmmu/qemu-system-x86_64 \
-smp 4 -m 1024M -cpu host -enable-kvm \
-hda "$DISK" \
-object memory-backend-file,id=mem,size=1024M,mem-path=/dev/shm,share=on \
-numa node,memdev=mem -mem-prealloc \
-chardev socket,id=char0,path=/tmp/vubr.sock \
-netdev type=vhost-user,id=net0,chardev=char0 \
-device virtio-net-pci,netdev=net0,page-per-vq=onMore details can be found from the patches.
Thanks!
[1] http://lists.nongnu.org/archive/html/qemu-devel/2018-04/msg01779.html
Tiwei Bie (2):
libvhost-user: support host notifier
vhost-user-bridge: support host notifier
contrib/libvhost-user/libvhost-user.c | 76 ++++++++++++++++++++++++--- contrib/libvhost-user/libvhost-user.h | 31 +++++++++++ tests/vhost-user-bridge.c | 98 +++++++++++++++++++++++++++++++++-- 3 files changed, 194 insertions(+), 11 deletions(-)
Signature · 1 line
2.11.0
PATCH 1/2 libvhost-user: support host notifier
This patch introduces the host notifier support in libvhost-user. A new API is added to support setting host notifier for each queue.
--- contrib/libvhost-user/libvhost-user.c | 76 +++++++++++++++++++++++++++++++---- contrib/libvhost-user/libvhost-user.h | 31 ++++++++++++++ 2 files changed, 99 insertions(+), 8 deletions(-)
contrib/libvhost-user/libvhost-user.c +68 -8
@@ -314,11 +314,6 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) msg.msg_controllen =0 ; } -/* Set the version in the flags when sending the reply */ - vmsg->flags &= ~VHOST_USER_VERSION_MASK ; - vmsg->flags |=VHOST_USER_VERSION ; - vmsg->flags |=VHOST_USER_REPLY_MASK ; -do { rc =sendmsg (conn_fd, &msg,0 ); }while (rc <0 && (errno ==EINTR || errno ==EAGAIN ));
@@ -339,6 +334,39 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)return true; } +static bool +vu_send_reply (VuDev *dev,int conn_fd,VhostUserMsg *vmsg) +{ +/* Set the version in the flags when sending the reply */ + vmsg->flags &= ~VHOST_USER_VERSION_MASK ; + vmsg->flags |=VHOST_USER_VERSION ; + vmsg->flags |=VHOST_USER_REPLY_MASK ; + +return vu_message_write (dev, conn_fd, vmsg); +} + +static bool +vu_process_message_reply (VuDev *dev,const VhostUserMsg *vmsg) +{ +VhostUserMsg msg_reply; + +if ((vmsg->flags &VHOST_USER_NEED_REPLY_MASK ) ==0 ) { +return true; + } + +if (!vu_message_read (dev, dev->slave_fd, &msg_reply)) { +return false; + } + +if (msg_reply.request != vmsg->request) { +DPRINT ("Received unexpected msg type. Expected %d received %d" , + vmsg->request, msg_reply.request); +return false; + } + +return msg_reply.payload.u64 ==0 ; +} +/* Kick the log_call_fd if required. */ static void vu_log_kick (VuDev *dev)
@@ -534,7 +562,7 @@ vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg)/* Send the message back to qemu with the addresses filled in */ vmsg->fd_num =0 ; -if (!vu_message_write (dev, dev->sock, vmsg)) { +if (!vu_send_reply (dev, dev->sock, vmsg)) {vu_panic (dev,"failed to respond to set-mem-table for postcopy" );return false; }
@@ -914,6 +942,37 @@ void vu_set_queue_handler(VuDev *dev, VuVirtq *vq, } } +bool vu_set_queue_host_notifier (VuDev *dev,VuVirtq *vq,int fd, +int size,int offset) +{ +int qidx = vq - dev->vq; +int fd_num =0 ; +VhostUserMsg vmsg = { + .request =VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG , + .flags =VHOST_USER_VERSION |VHOST_USER_NEED_REPLY_MASK , + .size =sizeof (vmsg.payload.area), + .payload.area = { + .u64 = qidx &VHOST_USER_VRING_IDX_MASK , + .size = size, + .offset = offset, + }, + }; + +if (fd ==-1 ) { + vmsg.payload.area.u64 |=VHOST_USER_VRING_NOFD_MASK ; + }else { + vmsg.fds[fd_num++] = fd; + } + + vmsg.fd_num = fd_num; + +if (!vu_message_write (dev, dev->slave_fd, &vmsg)) { +return false; + } + +return vu_process_message_reply (dev, &vmsg); +} +static bool vu_set_vring_call_exec (VuDev *dev,VhostUserMsg *vmsg) {
@@ -966,7 +1025,8 @@ static bool vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) {uint64_t features =1ULL <<VHOST_USER_PROTOCOL_F_LOG_SHMFD | -1ULL <<VHOST_USER_PROTOCOL_F_SLAVE_REQ ; +1ULL <<VHOST_USER_PROTOCOL_F_SLAVE_REQ | +1ULL <<VHOST_USER_PROTOCOL_F_HOST_NOTIFIER ;if (have_userfault ()) { features |=1ULL <<VHOST_USER_PROTOCOL_F_PAGEFAULT ;
@@ -1250,7 +1310,7 @@ vu_dispatch(VuDev *dev) goto end; } -if (!vu_message_write (dev, dev->sock, &vmsg)) { +if (!vu_send_reply (dev, dev->sock, &vmsg)) { goto end; }
contrib/libvhost-user/libvhost-user.h +31 -0
@@ -51,6 +51,7 @@ enum VhostUserProtocolFeature {VHOST_USER_PROTOCOL_F_CRYPTO_SESSION =7 ,VHOST_USER_PROTOCOL_F_PAGEFAULT =8 ,VHOST_USER_PROTOCOL_F_CONFIG =9 , +VHOST_USER_PROTOCOL_F_HOST_NOTIFIER =10 , VHOST_USER_PROTOCOL_F_MAX };
@@ -92,6 +93,14 @@ typedef enum VhostUserRequest {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;
@@ -122,6 +131,12 @@ static VhostUserConfig c __attribute__ ((unused)); + sizeof(c.size) \ + sizeof(c.flags)) +typedef struct VhostUserVringArea { +uint64_t u64; +uint64_t size; +uint64_t offset; +}VhostUserVringArea ; +#if defined(_WIN32)# define VU_PACKED __attribute__((gcc_struct, packed))#else
@@ -133,6 +148,7 @@ typedef struct VhostUserMsg {#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 */
@@ -145,6 +161,7 @@ typedef struct VhostUserMsg {VhostUserMemory memory;VhostUserLog log;VhostUserConfig config; +VhostUserVringArea area; } payload;int fds[VHOST_MEMORY_MAX_NREGIONS ];
@@ -368,6 +385,20 @@ VuVirtq *vu_get_queue(VuDev *dev, int qidx);void vu_set_queue_handler (VuDev *dev,VuVirtq *vq,vu_queue_handler_cb handler); +/** +* vu_set_queue_host_notifier: +* @dev: a VuDev context +* @vq: a VuVirtq queue +* @fd: a file descriptor +* @size: host page size +* @offset: notifier offset in @fd file +* +* Set queue's host notifier. This function may be called several +* times for the same queue. If called with -1 @fd, the notifier +* is removed. +*/ +bool vu_set_queue_host_notifier (VuDev *dev,VuVirtq *vq,int fd, +int size,int offset);/** * vu_queue_set_notification:
Signature · 1 line
2.11.0
PATCH 2/2 vhost-user-bridge: support host notifier
In reply to Tiwei BieThis patch introduces the host notifier support in vhost-user-bridge. A new option (-H) is added to use the host notifier. This is mainly used to test the host notifier implementation in vhost user.
--- tests/vhost-user-bridge.c | 98 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 3 deletions(-)
tests/vhost-user-bridge.c +95 -3
@@ -29,6 +29,7 @@#define _FILE_OFFSET_BITS 64 +#include "qemu/atomic.h" #include "qemu/osdep.h" #include "qemu/iov.h" #include "standard-headers/linux/virtio_net.h"
@@ -65,6 +66,11 @@ typedef struct VubrDev {int sock;int ready;int quit; +struct { +int fd; +void *addr; +pthread_t thread; + } notifier; } VubrDev; static void
@@ -445,14 +451,22 @@ static uint64_t vubr_get_features(VuDev *dev) {return 1ULL <<VIRTIO_NET_F_GUEST_ANNOUNCE | -1ULL <<VIRTIO_NET_F_MRG_RXBUF ; +1ULL <<VIRTIO_NET_F_MRG_RXBUF | +1ULL <<VIRTIO_F_VERSION_1 ; }static void vubr_queue_set_started (VuDev *dev,int qidx,bool started) { +VubrDev *vubr =container_of (dev, VubrDev, vudev);VuVirtq *vq =vu_get_queue (dev, qidx); +if (started && vubr->notifier.fd >=0 ) { +vu_set_queue_host_notifier (dev, vq, vubr->notifier.fd, +getpagesize (), + qidx *getpagesize ()); + } +if (qidx %2 ==1 ) {vu_set_queue_handler (dev, vq, started ? vubr_handle_tx :NULL ); }
@@ -522,6 +536,8 @@ vubr_new(const char *path, bool client)vubr_die ("socket" ); } + dev->notifier.fd =-1 ; + un.sun_family =AF_UNIX ;strcpy (un.sun_path, path); len =sizeof (un.sun_family) +strlen (path);
@@ -559,6 +575,73 @@ vubr_new(const char *path, bool client)return dev; } +static void *notifier_thread (void *arg) +{ +VuDev *dev = (VuDev *)arg; +VubrDev *vubr =container_of (dev, VubrDev, vudev); +int pagesize =getpagesize (); +int qidx; + +while (true) { +for (qidx =0 ; qidx <VHOST_MAX_NR_VIRTQUEUE ; qidx++) { +uint16_t *n = vubr->notifier.addr + pagesize * qidx; + +if (*n == qidx) { + *n =0xffff ; +/* We won't miss notifications if we reset +* the memory first. */ +smp_mb (); + +DPRINT ("Got a notification for queue%d via host notifier.\n" , + qidx); + +if (qidx %2 ==1 ) { +vubr_handle_tx (dev, qidx); + } + } +usleep (1000 ); + } + } + +return NULL ; +} + +static void +vubr_host_notifier_setup (VubrDev *dev) +{ +char template[] ="/tmp/vubr-XXXXXX" ; +pthread_t thread; +size_t length; +void *addr; +int fd; + + length =getpagesize () *VHOST_MAX_NR_VIRTQUEUE ; + + fd =mkstemp (template); +if (fd <0 ) { +vubr_die ("mkstemp()" ); + } + +if (posix_fallocate (fd,0 , length) !=0 ) { +vubr_die ("posix_fallocate()" ); + } + + addr =mmap (NULL , length,PROT_READ |PROT_WRITE ,MAP_SHARED , fd,0 ); +if (addr ==MAP_FAILED ) { +vubr_die ("mmap()" ); + } + +memset (addr,0xff , length); + +if (pthread_create (&thread,NULL , notifier_thread, &dev->vudev) !=0 ) { +vubr_die ("pthread_create()" ); + } + + dev->notifier.fd = fd; + dev->notifier.addr = addr; + dev->notifier.thread = thread; +} +static void vubr_set_host (struct sockaddr_in *saddr,const char *host) {
@@ -673,8 +756,9 @@ main(int argc, char *argv[])VubrDev *dev;int opt;bool client = false; +bool host_notifier = false; -while ((opt =getopt (argc, argv,"l:r:u:c" )) !=-1 ) { +while ((opt =getopt (argc, argv,"l:r:u:cH" )) !=-1 ) {switch (opt) {case 'l' :
@@ -693,6 +777,9 @@ main(int argc, char *argv[])case 'c' : client = true;break ; +case 'H' : + host_notifier = true; +break ;default : goto out; }
@@ -708,6 +795,10 @@ main(int argc, char *argv[])return 1 ; } +if (host_notifier) { +vubr_host_notifier_setup (dev); + } +vubr_backend_udp_setup (dev, lhost, lport, rhost, rport);vubr_run (dev);
@@ -717,7 +808,7 @@ main(int argc, char *argv[]) out:fprintf (stderr,"Usage: %s " , argv[0 ]); -fprintf (stderr,"[-c] [-u ud_socket_path] [-l lhost:lport] [-r rhost:rport]\n" ); +fprintf (stderr,"[-c] [-H] [-u ud_socket_path] [-l lhost:lport] [-r rhost:rport]\n" );fprintf (stderr,"\t-u path to unix doman socket. default: %s\n" ,DEFAULT_UD_SOCKET ); fprintf(stderr, "\t-l local host and port. default: %s:%s\n",
@@ -725,6 +816,7 @@ out:fprintf (stderr,"\t-r remote host and port. default: %s:%s\n" ,DEFAULT_RHOST ,DEFAULT_RPORT );fprintf (stderr,"\t-c client mode\n" ); +fprintf (stderr,"\t-H use host notifier\n" );return 1 ; }
Signature · 1 line
2.11.0