2014-05-27 20:06:29 +08:00
|
|
|
/*
|
|
|
|
* vhost-user.c
|
|
|
|
*
|
|
|
|
* Copyright (c) 2013 Virtual Open Systems Sarl.
|
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
|
|
* See the COPYING file in the top-level directory.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "clients.h"
|
|
|
|
#include "net/vhost_net.h"
|
|
|
|
#include "net/vhost-user.h"
|
|
|
|
#include "sysemu/char.h"
|
2014-06-10 18:02:16 +08:00
|
|
|
#include "qemu/config-file.h"
|
2014-05-27 20:06:29 +08:00
|
|
|
#include "qemu/error-report.h"
|
|
|
|
|
|
|
|
typedef struct VhostUserState {
|
|
|
|
NetClientState nc;
|
|
|
|
CharDriverState *chr;
|
|
|
|
VHostNetState *vhost_net;
|
|
|
|
} VhostUserState;
|
|
|
|
|
2014-06-10 18:02:16 +08:00
|
|
|
typedef struct VhostUserChardevProps {
|
|
|
|
bool is_socket;
|
|
|
|
bool is_unix;
|
|
|
|
bool is_server;
|
|
|
|
} VhostUserChardevProps;
|
|
|
|
|
2014-05-27 20:06:29 +08:00
|
|
|
VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
|
|
|
|
{
|
|
|
|
VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
|
2014-06-10 18:02:16 +08:00
|
|
|
assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
|
2014-05-27 20:06:29 +08:00
|
|
|
return s->vhost_net;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_running(VhostUserState *s)
|
|
|
|
{
|
|
|
|
return (s->vhost_net) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_user_start(VhostUserState *s)
|
|
|
|
{
|
|
|
|
VhostNetOptions options;
|
|
|
|
|
|
|
|
if (vhost_user_running(s)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
options.backend_type = VHOST_BACKEND_TYPE_USER;
|
|
|
|
options.net_backend = &s->nc;
|
|
|
|
options.opaque = s->chr;
|
|
|
|
|
|
|
|
s->vhost_net = vhost_net_init(&options);
|
|
|
|
|
|
|
|
return vhost_user_running(s) ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vhost_user_stop(VhostUserState *s)
|
|
|
|
{
|
|
|
|
if (vhost_user_running(s)) {
|
|
|
|
vhost_net_cleanup(s->vhost_net);
|
|
|
|
}
|
|
|
|
|
|
|
|
s->vhost_net = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vhost_user_cleanup(NetClientState *nc)
|
|
|
|
{
|
|
|
|
VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
|
|
|
|
|
|
|
|
vhost_user_stop(s);
|
|
|
|
qemu_purge_queued_packets(nc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool vhost_user_has_vnet_hdr(NetClientState *nc)
|
|
|
|
{
|
|
|
|
assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool vhost_user_has_ufo(NetClientState *nc)
|
|
|
|
{
|
|
|
|
assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static NetClientInfo net_vhost_user_info = {
|
2014-06-10 18:02:16 +08:00
|
|
|
.type = NET_CLIENT_OPTIONS_KIND_VHOST_USER,
|
2014-05-27 20:06:29 +08:00
|
|
|
.size = sizeof(VhostUserState),
|
|
|
|
.cleanup = vhost_user_cleanup,
|
|
|
|
.has_vnet_hdr = vhost_user_has_vnet_hdr,
|
|
|
|
.has_ufo = vhost_user_has_ufo,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void net_vhost_link_down(VhostUserState *s, bool link_down)
|
|
|
|
{
|
|
|
|
s->nc.link_down = link_down;
|
|
|
|
|
|
|
|
if (s->nc.peer) {
|
|
|
|
s->nc.peer->link_down = link_down;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->nc.info->link_status_changed) {
|
|
|
|
s->nc.info->link_status_changed(&s->nc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->nc.peer && s->nc.peer->info->link_status_changed) {
|
|
|
|
s->nc.peer->info->link_status_changed(s->nc.peer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void net_vhost_user_event(void *opaque, int event)
|
|
|
|
{
|
|
|
|
VhostUserState *s = opaque;
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case CHR_EVENT_OPENED:
|
|
|
|
vhost_user_start(s);
|
|
|
|
net_vhost_link_down(s, false);
|
2015-05-28 09:23:06 +08:00
|
|
|
error_report("chardev \"%s\" went up", s->nc.info_str);
|
2014-05-27 20:06:29 +08:00
|
|
|
break;
|
|
|
|
case CHR_EVENT_CLOSED:
|
|
|
|
net_vhost_link_down(s, true);
|
|
|
|
vhost_user_stop(s);
|
2015-05-28 09:23:06 +08:00
|
|
|
error_report("chardev \"%s\" went down", s->nc.info_str);
|
2014-05-27 20:06:29 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int net_vhost_user_init(NetClientState *peer, const char *device,
|
2015-05-28 09:23:06 +08:00
|
|
|
const char *name, CharDriverState *chr,
|
|
|
|
uint32_t queues)
|
2014-05-27 20:06:29 +08:00
|
|
|
{
|
|
|
|
NetClientState *nc;
|
|
|
|
VhostUserState *s;
|
2015-05-28 09:23:06 +08:00
|
|
|
int i;
|
2014-05-27 20:06:29 +08:00
|
|
|
|
2015-05-28 09:23:06 +08:00
|
|
|
for (i = 0; i < queues; i++) {
|
|
|
|
nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
|
2014-05-27 20:06:29 +08:00
|
|
|
|
2015-05-28 09:23:06 +08:00
|
|
|
snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s",
|
|
|
|
i, chr->label);
|
2014-05-27 20:06:29 +08:00
|
|
|
|
2015-05-28 09:23:06 +08:00
|
|
|
s = DO_UPCAST(VhostUserState, nc, nc);
|
2014-05-27 20:06:29 +08:00
|
|
|
|
2015-05-28 09:23:06 +08:00
|
|
|
/* We don't provide a receive callback */
|
|
|
|
s->nc.receive_disabled = 1;
|
|
|
|
s->chr = chr;
|
|
|
|
s->nc.queue_index = i;
|
2014-05-27 20:06:29 +08:00
|
|
|
|
2015-05-28 09:23:06 +08:00
|
|
|
qemu_chr_add_handlers(s->chr, NULL, NULL, net_vhost_user_event, s);
|
|
|
|
}
|
2014-05-27 20:06:29 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-03-12 15:40:25 +08:00
|
|
|
static int net_vhost_chardev_opts(void *opaque,
|
|
|
|
const char *name, const char *value,
|
|
|
|
Error **errp)
|
2014-06-10 18:02:16 +08:00
|
|
|
{
|
|
|
|
VhostUserChardevProps *props = opaque;
|
|
|
|
|
|
|
|
if (strcmp(name, "backend") == 0 && strcmp(value, "socket") == 0) {
|
|
|
|
props->is_socket = true;
|
|
|
|
} else if (strcmp(name, "path") == 0) {
|
|
|
|
props->is_unix = true;
|
|
|
|
} else if (strcmp(name, "server") == 0) {
|
|
|
|
props->is_server = true;
|
|
|
|
} else {
|
vhost-user: Improve -netdev/netdev_add/-net/... error reporting
When -netdev vhost-user fails, it first reports a specific error, then
one or more generic ones, like this:
$ qemu-system-x86_64 -netdev vhost-user,id=foo,chardev=xxx
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: chardev "xxx" not found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: No suitable chardev found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: Device 'vhost-user' could not be initialized
With the command line, the messages go to stderr. In HMP, they go to
the monitor. In QMP, the last one becomes the error reply, and the
others go to stderr.
Convert net_init_vhost_user() and its helpers to Error. This
suppresses the unwanted unspecific error messages, and makes the
specific error the QMP error reply.
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Jason Wang <jasowang@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
2015-03-13 21:17:16 +08:00
|
|
|
error_setg(errp,
|
|
|
|
"vhost-user does not support a chardev with option %s=%s",
|
|
|
|
name, value);
|
2014-06-10 18:02:16 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
vhost-user: Improve -netdev/netdev_add/-net/... error reporting
When -netdev vhost-user fails, it first reports a specific error, then
one or more generic ones, like this:
$ qemu-system-x86_64 -netdev vhost-user,id=foo,chardev=xxx
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: chardev "xxx" not found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: No suitable chardev found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: Device 'vhost-user' could not be initialized
With the command line, the messages go to stderr. In HMP, they go to
the monitor. In QMP, the last one becomes the error reply, and the
others go to stderr.
Convert net_init_vhost_user() and its helpers to Error. This
suppresses the unwanted unspecific error messages, and makes the
specific error the QMP error reply.
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Jason Wang <jasowang@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
2015-03-13 21:17:16 +08:00
|
|
|
static CharDriverState *net_vhost_parse_chardev(
|
|
|
|
const NetdevVhostUserOptions *opts, Error **errp)
|
2014-06-10 18:02:16 +08:00
|
|
|
{
|
|
|
|
CharDriverState *chr = qemu_chr_find(opts->chardev);
|
|
|
|
VhostUserChardevProps props;
|
|
|
|
|
|
|
|
if (chr == NULL) {
|
vhost-user: Improve -netdev/netdev_add/-net/... error reporting
When -netdev vhost-user fails, it first reports a specific error, then
one or more generic ones, like this:
$ qemu-system-x86_64 -netdev vhost-user,id=foo,chardev=xxx
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: chardev "xxx" not found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: No suitable chardev found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: Device 'vhost-user' could not be initialized
With the command line, the messages go to stderr. In HMP, they go to
the monitor. In QMP, the last one becomes the error reply, and the
others go to stderr.
Convert net_init_vhost_user() and its helpers to Error. This
suppresses the unwanted unspecific error messages, and makes the
specific error the QMP error reply.
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Jason Wang <jasowang@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
2015-03-13 21:17:16 +08:00
|
|
|
error_setg(errp, "chardev \"%s\" not found", opts->chardev);
|
2014-06-10 18:02:16 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* inspect chardev opts */
|
|
|
|
memset(&props, 0, sizeof(props));
|
vhost-user: Improve -netdev/netdev_add/-net/... error reporting
When -netdev vhost-user fails, it first reports a specific error, then
one or more generic ones, like this:
$ qemu-system-x86_64 -netdev vhost-user,id=foo,chardev=xxx
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: chardev "xxx" not found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: No suitable chardev found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: Device 'vhost-user' could not be initialized
With the command line, the messages go to stderr. In HMP, they go to
the monitor. In QMP, the last one becomes the error reply, and the
others go to stderr.
Convert net_init_vhost_user() and its helpers to Error. This
suppresses the unwanted unspecific error messages, and makes the
specific error the QMP error reply.
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Jason Wang <jasowang@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
2015-03-13 21:17:16 +08:00
|
|
|
if (qemu_opt_foreach(chr->opts, net_vhost_chardev_opts, &props, errp)) {
|
2014-06-10 18:02:16 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!props.is_socket || !props.is_unix) {
|
vhost-user: Improve -netdev/netdev_add/-net/... error reporting
When -netdev vhost-user fails, it first reports a specific error, then
one or more generic ones, like this:
$ qemu-system-x86_64 -netdev vhost-user,id=foo,chardev=xxx
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: chardev "xxx" not found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: No suitable chardev found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: Device 'vhost-user' could not be initialized
With the command line, the messages go to stderr. In HMP, they go to
the monitor. In QMP, the last one becomes the error reply, and the
others go to stderr.
Convert net_init_vhost_user() and its helpers to Error. This
suppresses the unwanted unspecific error messages, and makes the
specific error the QMP error reply.
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Jason Wang <jasowang@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
2015-03-13 21:17:16 +08:00
|
|
|
error_setg(errp, "chardev \"%s\" is not a unix socket",
|
|
|
|
opts->chardev);
|
2014-06-10 18:02:16 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_chr_fe_claim_no_fail(chr);
|
|
|
|
|
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
|
2015-03-13 20:35:14 +08:00
|
|
|
static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
|
2014-06-10 18:02:16 +08:00
|
|
|
{
|
|
|
|
const char *name = opaque;
|
|
|
|
const char *driver, *netdev;
|
|
|
|
const char virtio_name[] = "virtio-net-";
|
|
|
|
|
|
|
|
driver = qemu_opt_get(opts, "driver");
|
|
|
|
netdev = qemu_opt_get(opts, "netdev");
|
|
|
|
|
|
|
|
if (!driver || !netdev) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(netdev, name) == 0 &&
|
|
|
|
strncmp(driver, virtio_name, strlen(virtio_name)) != 0) {
|
vhost-user: Improve -netdev/netdev_add/-net/... error reporting
When -netdev vhost-user fails, it first reports a specific error, then
one or more generic ones, like this:
$ qemu-system-x86_64 -netdev vhost-user,id=foo,chardev=xxx
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: chardev "xxx" not found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: No suitable chardev found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: Device 'vhost-user' could not be initialized
With the command line, the messages go to stderr. In HMP, they go to
the monitor. In QMP, the last one becomes the error reply, and the
others go to stderr.
Convert net_init_vhost_user() and its helpers to Error. This
suppresses the unwanted unspecific error messages, and makes the
specific error the QMP error reply.
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Jason Wang <jasowang@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
2015-03-13 21:17:16 +08:00
|
|
|
error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
|
2014-06-10 18:02:16 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-27 20:06:29 +08:00
|
|
|
int net_init_vhost_user(const NetClientOptions *opts, const char *name,
|
2015-05-15 19:58:50 +08:00
|
|
|
NetClientState *peer, Error **errp)
|
2014-05-27 20:06:29 +08:00
|
|
|
{
|
2015-05-28 09:23:06 +08:00
|
|
|
uint32_t queues;
|
2014-06-10 18:02:16 +08:00
|
|
|
const NetdevVhostUserOptions *vhost_user_opts;
|
|
|
|
CharDriverState *chr;
|
|
|
|
|
|
|
|
assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
|
|
|
|
vhost_user_opts = opts->vhost_user;
|
|
|
|
|
vhost-user: Improve -netdev/netdev_add/-net/... error reporting
When -netdev vhost-user fails, it first reports a specific error, then
one or more generic ones, like this:
$ qemu-system-x86_64 -netdev vhost-user,id=foo,chardev=xxx
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: chardev "xxx" not found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: No suitable chardev found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: Device 'vhost-user' could not be initialized
With the command line, the messages go to stderr. In HMP, they go to
the monitor. In QMP, the last one becomes the error reply, and the
others go to stderr.
Convert net_init_vhost_user() and its helpers to Error. This
suppresses the unwanted unspecific error messages, and makes the
specific error the QMP error reply.
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Jason Wang <jasowang@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
2015-03-13 21:17:16 +08:00
|
|
|
chr = net_vhost_parse_chardev(vhost_user_opts, errp);
|
2014-06-10 18:02:16 +08:00
|
|
|
if (!chr) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* verify net frontend */
|
|
|
|
if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net,
|
vhost-user: Improve -netdev/netdev_add/-net/... error reporting
When -netdev vhost-user fails, it first reports a specific error, then
one or more generic ones, like this:
$ qemu-system-x86_64 -netdev vhost-user,id=foo,chardev=xxx
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: chardev "xxx" not found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: No suitable chardev found
qemu-system-x86_64: -netdev vhost-user,id=foo,chardev=xxx: Device 'vhost-user' could not be initialized
With the command line, the messages go to stderr. In HMP, they go to
the monitor. In QMP, the last one becomes the error reply, and the
others go to stderr.
Convert net_init_vhost_user() and its helpers to Error. This
suppresses the unwanted unspecific error messages, and makes the
specific error the QMP error reply.
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Jason Wang <jasowang@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
2015-03-13 21:17:16 +08:00
|
|
|
(char *)name, errp)) {
|
2014-06-10 18:02:16 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-05-28 09:23:06 +08:00
|
|
|
/* number of queues for multiqueue */
|
|
|
|
if (vhost_user_opts->has_queues) {
|
|
|
|
queues = vhost_user_opts->queues;
|
|
|
|
} else {
|
|
|
|
queues = 1;
|
|
|
|
}
|
2014-06-10 18:02:16 +08:00
|
|
|
|
2015-05-28 09:23:06 +08:00
|
|
|
return net_vhost_user_init(peer, "vhost_user", name, chr, queues);
|
2014-05-27 20:06:29 +08:00
|
|
|
}
|