This patch adds 'backend_features' option for vhost-user backends. If this option is specified, QEMU assumes vhost-user backends support the features specified by user, and QEMU can start without vhost-user backend.
Here are examples. * QEMU is configured as vhost-user client. -chardev socket,id=chr0,path=/tmp/sock,reconnect=3 \ -netdev vhost-user,id=net0,chardev=chr0,vhostforce,backend_features=0x68000 \ -device virtio-net-pci,netdev=net0 \ * QEMU is configured as vhost-user server. -chardev socket,id=chr0,path=/tmp/sock,server,nowait \ -netdev vhost-user,id=net0,chardev=chr0,vhostforce,backend_features=0x68000 \ -device virtio-net-pci,netdev=net0 \ To know vhost-user backend features that the backend expects, please specify 0xffffffff as backend_features, then invoke QEMU and check error log like below. Lack of backend features. Expected 0xffffffff, but receives 0x68000 Above log indicates the backend features QEMU should be passed. Signed-off-by: Tetsuya Mukawa <muk...@igel.co.jp> --- hw/net/vhost_net.c | 9 ++++++++- hw/net/virtio-net.c | 24 ++++++++++++++++++++++++ hw/scsi/vhost-scsi.c | 2 +- hw/virtio/vhost-user.c | 9 +++++++++ hw/virtio/vhost.c | 8 ++++++-- include/hw/virtio/vhost.h | 5 ++++- include/hw/virtio/virtio-net.h | 2 ++ include/net/net.h | 6 ++++++ include/net/vhost_net.h | 2 ++ net/net.c | 18 ++++++++++++++++++ net/tap.c | 5 ++++- net/vhost-user.c | 42 ++++++++++++++++++++++++++++++++++++++++-- qapi-schema.json | 10 ++++++++-- qemu-options.hx | 3 ++- 14 files changed, 134 insertions(+), 11 deletions(-) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 47f8b89..f974d09 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -158,8 +158,15 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->dev.nvqs = 2; net->dev.vqs = net->vqs; + if (options->has_backend_features) { + net->dev.has_backend_features = options->has_backend_features; + net->dev.backend_features = options->backend_features; + } + r = vhost_dev_init(&net->dev, options->opaque, - options->backend_type, options->force); + options->backend_type, options->force, + options->has_backend_features, + options->backend_features); if (r < 0) { goto fail; } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 3af6faf..ebe5422 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -366,6 +366,28 @@ static int peer_has_ufo(VirtIONet *n) return n->has_ufo; } +static int peer_has_backend_features(VirtIONet *n) +{ + if (!peer_has_vnet_hdr(n)) + return 0; + + n->has_backend_features = + qemu_has_backend_features(qemu_get_queue(n->nic)->peer); + + return n->has_backend_features; +} + +static uint64_t peer_backend_features(VirtIONet *n) +{ + if (!peer_has_vnet_hdr(n)) + return 0; + + n->backend_features = + qemu_backend_features(qemu_get_queue(n->nic)->peer); + + return n->backend_features; +} + static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs) { int i; @@ -463,6 +485,8 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) } if (!get_vhost_net(nc->peer)) { + if (peer_has_backend_features(n)) + features = peer_backend_features(n); return features; } return vhost_net_get_features(get_vhost_net(nc->peer), features); diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 335f442..4998a95 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -246,7 +246,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) s->dev.backend_features = 0; ret = vhost_dev_init(&s->dev, (void *)(uintptr_t)vhostfd, - VHOST_BACKEND_TYPE_KERNEL, true); + VHOST_BACKEND_TYPE_KERNEL, true, false, 0); if (ret < 0) { error_setg(errp, "vhost-scsi: vhost initialization failed: %s", strerror(-ret)); diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 4d7e3ba..f0dcb97 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -307,6 +307,15 @@ static int vhost_user_call(struct vhost_dev *dev, unsigned long int request, error_report("Received bad msg size."); goto close; } + if (dev->has_backend_features) { + if (dev->backend_features != (dev->backend_features & msg.u64)) { + error_report("Lack of backend features. " + "Expected 0x%llx, but receives 0x%lx", + dev->backend_features, msg.u64); + goto close; + } + } + *((__u64 *) arg) = msg.u64; break; case VHOST_USER_GET_VRING_BASE: diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 54851b7..20cb116 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -811,7 +811,9 @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) } int vhost_dev_init(struct vhost_dev *hdev, void *opaque, - VhostBackendType backend_type, bool force) + VhostBackendType backend_type, bool force, + bool has_backend_features, + unsigned long long backend_features) { uint64_t features; int i, r; @@ -833,7 +835,9 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, r = hdev->vhost_ops->vhost_call(hdev, VHOST_GET_FEATURES, &features); if (r < 0) { - goto fail; + if (!has_backend_features) + goto fail; + features = backend_features; } for (i = 0; i < hdev->nvqs; ++i) { diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 8f04888..60306cb 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -41,6 +41,7 @@ struct vhost_dev { unsigned long long features; unsigned long long acked_features; unsigned long long backend_features; + bool has_backend_features; bool started; bool log_enabled; vhost_log_chunk_t *log; @@ -55,7 +56,9 @@ struct vhost_dev { }; int vhost_dev_init(struct vhost_dev *hdev, void *opaque, - VhostBackendType backend_type, bool force); + VhostBackendType backend_type, bool force, + bool has_backend_features, + unsigned long long backend_features); void vhost_dev_cleanup(struct vhost_dev *hdev); bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev); int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev); diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index e0dbb41..528da28 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -70,6 +70,8 @@ typedef struct VirtIONet { size_t guest_hdr_len; uint32_t host_features; uint8_t has_ufo; + uint8_t has_backend_features; + uint64_t backend_features; int mergeable_rx_bufs; uint8_t promisc; uint8_t allmulti; diff --git a/include/net/net.h b/include/net/net.h index e66ca03..e2eab3e 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -55,6 +55,8 @@ typedef bool (HasVnetHdrLen)(NetClientState *, int); typedef void (UsingVnetHdr)(NetClientState *, bool); typedef void (SetOffload)(NetClientState *, int, int, int, int, int); typedef void (SetVnetHdrLen)(NetClientState *, int); +typedef bool (HasBackendFeatures)(NetClientState *); +typedef unsigned long long (BackendFeatures)(NetClientState *); typedef struct NetClientInfo { NetClientOptionsKind type; @@ -73,6 +75,8 @@ typedef struct NetClientInfo { UsingVnetHdr *using_vnet_hdr; SetOffload *set_offload; SetVnetHdrLen *set_vnet_hdr_len; + HasBackendFeatures *has_backend_features; + BackendFeatures *backend_features; } NetClientInfo; struct NetClientState { @@ -136,6 +140,8 @@ bool qemu_has_ufo(NetClientState *nc); bool qemu_has_vnet_hdr(NetClientState *nc); bool qemu_has_vnet_hdr_len(NetClientState *nc, int len); void qemu_using_vnet_hdr(NetClientState *nc, bool enable); +bool qemu_has_backend_features(NetClientState *nc); +unsigned long long qemu_backend_features(NetClientState *nc); void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo); void qemu_set_vnet_hdr_len(NetClientState *nc, int len); diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index b1c18a3..b4ca8e8 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -12,6 +12,8 @@ typedef struct VhostNetOptions { NetClientState *net_backend; void *opaque; bool force; + bool has_backend_features; + unsigned long long backend_features; } VhostNetOptions; struct vhost_net *vhost_net_init(VhostNetOptions *options); diff --git a/net/net.c b/net/net.c index 7427f6a..85e41d0 100644 --- a/net/net.c +++ b/net/net.c @@ -459,6 +459,24 @@ void qemu_set_vnet_hdr_len(NetClientState *nc, int len) nc->info->set_vnet_hdr_len(nc, len); } +bool qemu_has_backend_features(NetClientState *nc) +{ + if (!nc || !nc->info->has_backend_features) { + return false; + } + + return nc->info->has_backend_features(nc); +} + +unsigned long long qemu_backend_features(NetClientState *nc) +{ + if (!nc || !nc->info->backend_features) { + return false; + } + + return nc->info->backend_features(nc); +} + int qemu_can_send_packet(NetClientState *sender) { int vm_running = runstate_is_running(); diff --git a/net/tap.c b/net/tap.c index 968df46..6d25170 100644 --- a/net/tap.c +++ b/net/tap.c @@ -636,12 +636,15 @@ static int net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } if (tap->has_vhost ? tap->vhost : - vhostfdname || (tap->has_vhostforce && tap->vhostforce)) { + vhostfdname || tap->has_backend_features || + (tap->has_vhostforce && tap->vhostforce)) { VhostNetOptions options; options.backend_type = VHOST_BACKEND_TYPE_KERNEL; options.net_backend = &s->nc; options.force = tap->has_vhostforce && tap->vhostforce; + options.has_backend_features = tap->has_backend_features; + options.backend_features = tap->backend_features; if (tap->has_vhostfd || tap->has_vhostfds) { vhostfd = monitor_fd_param(cur_mon, vhostfdname, &err); diff --git a/net/vhost-user.c b/net/vhost-user.c index d31fc41..2705393 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -19,6 +19,8 @@ typedef struct VhostUserState { NetClientState nc; CharDriverState *chr; VHostNetState *vhost_net; + bool has_backend_features; + unsigned long long backend_features; int watch; } VhostUserState; @@ -54,6 +56,7 @@ static int vhost_user_start(VhostUserState *s) options.net_backend = &s->nc; options.opaque = s->chr; options.force = true; + options.backend_features = s->backend_features; s->vhost_net = vhost_net_init(&options); @@ -91,12 +94,32 @@ static bool vhost_user_has_ufo(NetClientState *nc) return true; } +static bool vhost_user_has_backend_features(NetClientState *nc) +{ + assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER); + + VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + + return s->has_backend_features; +} + +static unsigned long long vhost_user_backend_features(NetClientState *nc) +{ + assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER); + + VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + + return s->backend_features; +} + static NetClientInfo net_vhost_user_info = { .type = NET_CLIENT_OPTIONS_KIND_VHOST_USER, .size = sizeof(VhostUserState), .cleanup = vhost_user_cleanup, .has_vnet_hdr = vhost_user_has_vnet_hdr, .has_ufo = vhost_user_has_ufo, + .has_backend_features = vhost_user_has_backend_features, + .backend_features = vhost_user_backend_features, }; static void net_vhost_link_down(VhostUserState *s, bool link_down) @@ -148,7 +171,9 @@ static void net_vhost_user_event(void *opaque, int event) } static int net_vhost_user_init(NetClientState *peer, const char *device, - const char *name, CharDriverState *chr) + const char *name, CharDriverState *chr, + bool has_backend_features, + unsigned long long backend_features) { NetClientState *nc; VhostUserState *s; @@ -163,6 +188,8 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, /* We don't provide a receive callback */ s->nc.receive_disabled = 1; s->chr = chr; + s->has_backend_features = has_backend_features; + s->backend_features = backend_features; qemu_chr_add_handlers(s->chr, NULL, NULL, net_vhost_user_event, s); @@ -247,6 +274,8 @@ int net_init_vhost_user(const NetClientOptions *opts, const char *name, { const NetdevVhostUserOptions *vhost_user_opts; CharDriverState *chr; + bool has_backend_features;; + unsigned long long backend_features; assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER); vhost_user_opts = opts->vhost_user; @@ -263,6 +292,15 @@ int net_init_vhost_user(const NetClientOptions *opts, const char *name, return -1; } + /* backend features */ + if (vhost_user_opts->has_backend_features) { + has_backend_features = true; + backend_features = vhost_user_opts->backend_features; + } else { + has_backend_features = false; + backend_features = 0; + } - return net_vhost_user_init(peer, "vhost_user", name, chr); + return net_vhost_user_init(peer, "vhost_user", name, chr, + has_backend_features, backend_features); } diff --git a/qapi-schema.json b/qapi-schema.json index f97ffa1..bab4a74 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2243,6 +2243,8 @@ # # @queues: #optional number of queues to be created for multiqueue capable tap # +# @backend_features: #optional feature flag to support vhost user backend +# # Since 1.2 ## { 'struct': 'NetdevTapOptions', @@ -2259,7 +2261,8 @@ '*vhostfd': 'str', '*vhostfds': 'str', '*vhostforce': 'bool', - '*queues': 'uint32'} } + '*queues': 'uint32', + '*backend_features':'uint64'} } ## # @NetdevSocketOptions @@ -2444,12 +2447,15 @@ # # @vhostforce: #optional vhost on for non-MSIX virtio guests (default: false). # +# @backend_features: #optional feature flag to support vhost user backend (default: 0). +# # Since 2.1 ## { 'struct': 'NetdevVhostUserOptions', 'data': { 'chardev': 'str', - '*vhostforce': 'bool' } } + '*vhostforce': 'bool', + '*backend_features': 'uint64' } } ## # @NetClientOptions diff --git a/qemu-options.hx b/qemu-options.hx index ec356f6..3ad3486 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1466,7 +1466,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, "-net tap[,vlan=n][,name=str],ifname=name\n" " connect the host TAP network interface to VLAN 'n'\n" #else - "-net tap[,vlan=n][,name=str][,fd=h][,fds=x:y:...:z][,ifname=name][,script=file][,downscript=dfile][,helper=helper][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostfds=x:y:...:z][,vhostforce=on|off][,queues=n]\n" + "-net tap[,vlan=n][,name=str][,fd=h][,fds=x:y:...:z][,ifname=name][,script=file][,downscript=dfile][,helper=helper][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostfds=x:y:...:z][,vhostforce=on|off][,queues=n][,backend_features=n]\n" " connect the host TAP network interface to VLAN 'n'\n" " use network scripts 'file' (default=" DEFAULT_NETWORK_SCRIPT ")\n" " to configure it and 'dfile' (default=" DEFAULT_NETWORK_DOWN_SCRIPT ")\n" @@ -1486,6 +1486,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, " use 'vhostfd=h' to connect to an already opened vhost net device\n" " use 'vhostfds=x:y:...:z to connect to multiple already opened vhost net devices\n" " use 'queues=n' to specify the number of queues to be created for multiqueue TAP\n" + " use 'backend_features=n' to specify the features that vhost backend supported\n" "-net bridge[,vlan=n][,name=str][,br=bridge][,helper=helper]\n" " connects a host TAP network interface to a host bridge device 'br'\n" " (default=" DEFAULT_BRIDGE_INTERFACE ") using the program 'helper'\n" -- 2.1.4