#include "clients.h"
#include "net/vhost_net.h"
#include "net/vhost-user.h"
-#include "sysemu/char.h"
+#include "hw/virtio/vhost-user.h"
+#include "chardev/char-fe.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-net.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
-#include "qmp-commands.h"
+#include "qemu/option.h"
#include "trace.h"
-typedef struct VhostUserState {
+typedef struct NetVhostUserState {
NetClientState nc;
CharBackend chr; /* only queue index 0 */
+ VhostUserState *vhost_user;
VHostNetState *vhost_net;
guint watch;
uint64_t acked_features;
bool started;
-} VhostUserState;
+} NetVhostUserState;
VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
{
- VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
+ NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
return s->vhost_net;
}
uint64_t vhost_user_get_acked_features(NetClientState *nc)
{
- VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
+ NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
return s->acked_features;
}
static void vhost_user_stop(int queues, NetClientState *ncs[])
{
- VhostUserState *s;
+ NetVhostUserState *s;
int i;
for (i = 0; i < queues; i++) {
assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
- s = DO_UPCAST(VhostUserState, nc, ncs[i]);
+ s = DO_UPCAST(NetVhostUserState, nc, ncs[i]);
if (s->vhost_net) {
/* save acked features */
}
}
-static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be)
+static int vhost_user_start(int queues, NetClientState *ncs[],
+ VhostUserState *be)
{
VhostNetOptions options;
struct vhost_net *net = NULL;
- VhostUserState *s;
+ NetVhostUserState *s;
int max_queues;
int i;
for (i = 0; i < queues; i++) {
assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
- s = DO_UPCAST(VhostUserState, nc, ncs[i]);
+ s = DO_UPCAST(NetVhostUserState, nc, ncs[i]);
options.net_backend = ncs[i];
options.opaque = be;
err:
if (net) {
vhost_net_cleanup(net);
+ g_free(net);
}
vhost_user_stop(i, ncs);
return -1;
without GUEST_ANNOUNCE capability.
*/
if (size == 60) {
- VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
+ NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
int r;
static int display_rarp_failure = 1;
char mac_addr[6];
return size;
}
-static void vhost_user_cleanup(NetClientState *nc)
+static void net_vhost_user_cleanup(NetClientState *nc)
{
- VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
+ NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
if (s->vhost_net) {
vhost_net_cleanup(s->vhost_net);
s->vhost_net = NULL;
}
if (nc->queue_index == 0) {
- qemu_chr_fe_deinit(&s->chr);
+ if (s->watch) {
+ g_source_remove(s->watch);
+ s->watch = 0;
+ }
+ qemu_chr_fe_deinit(&s->chr, true);
+ if (s->vhost_user) {
+ vhost_user_cleanup(s->vhost_user);
+ g_free(s->vhost_user);
+ s->vhost_user = NULL;
+ }
}
qemu_purge_queued_packets(nc);
}
+static int vhost_user_set_vnet_endianness(NetClientState *nc,
+ bool enable)
+{
+ /* Nothing to do. If the server supports
+ * VHOST_USER_PROTOCOL_F_CROSS_ENDIAN, it will get the
+ * vnet header endianness from there. If it doesn't, negotiation
+ * fails.
+ */
+ return 0;
+}
+
static bool vhost_user_has_vnet_hdr(NetClientState *nc)
{
assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
static NetClientInfo net_vhost_user_info = {
.type = NET_CLIENT_DRIVER_VHOST_USER,
- .size = sizeof(VhostUserState),
+ .size = sizeof(NetVhostUserState),
.receive = vhost_user_receive,
- .cleanup = vhost_user_cleanup,
+ .cleanup = net_vhost_user_cleanup,
.has_vnet_hdr = vhost_user_has_vnet_hdr,
.has_ufo = vhost_user_has_ufo,
+ .set_vnet_be = vhost_user_set_vnet_endianness,
+ .set_vnet_le = vhost_user_set_vnet_endianness,
};
static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond,
void *opaque)
{
- VhostUserState *s = opaque;
+ NetVhostUserState *s = opaque;
qemu_chr_fe_disconnect(&s->chr);
- return FALSE;
+ return TRUE;
+}
+
+static void net_vhost_user_event(void *opaque, int event);
+
+static void chr_closed_bh(void *opaque)
+{
+ const char *name = opaque;
+ NetClientState *ncs[MAX_QUEUE_NUM];
+ NetVhostUserState *s;
+ Error *err = NULL;
+ int queues;
+
+ queues = qemu_find_net_clients_except(name, ncs,
+ NET_CLIENT_DRIVER_NIC,
+ MAX_QUEUE_NUM);
+ assert(queues < MAX_QUEUE_NUM);
+
+ s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
+
+ qmp_set_link(name, false, &err);
+
+ qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event,
+ NULL, opaque, NULL, true);
+
+ if (err) {
+ error_report_err(err);
+ }
}
static void net_vhost_user_event(void *opaque, int event)
{
const char *name = opaque;
NetClientState *ncs[MAX_QUEUE_NUM];
- VhostUserState *s;
+ NetVhostUserState *s;
Chardev *chr;
Error *err = NULL;
int queues;
MAX_QUEUE_NUM);
assert(queues < MAX_QUEUE_NUM);
- s = DO_UPCAST(VhostUserState, nc, ncs[0]);
+ s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
chr = qemu_chr_fe_get_driver(&s->chr);
trace_vhost_user_event(chr->label, event);
switch (event) {
case CHR_EVENT_OPENED:
- s->watch = qemu_chr_fe_add_watch(&s->chr, G_IO_HUP,
- net_vhost_user_watch, s);
- if (vhost_user_start(queues, ncs, &s->chr) < 0) {
+ if (vhost_user_start(queues, ncs, s->vhost_user) < 0) {
qemu_chr_fe_disconnect(&s->chr);
return;
}
+ s->watch = qemu_chr_fe_add_watch(&s->chr, G_IO_HUP,
+ net_vhost_user_watch, s);
qmp_set_link(name, true, &err);
s->started = true;
break;
case CHR_EVENT_CLOSED:
- qmp_set_link(name, false, &err);
- vhost_user_stop(queues, ncs);
- g_source_remove(s->watch);
- s->watch = 0;
+ /* a close event may happen during a read/write, but vhost
+ * code assumes the vhost_dev remains setup, so delay the
+ * stop & clear to idle.
+ * FIXME: better handle failure in vhost code, remove bh
+ */
+ if (s->watch) {
+ AioContext *ctx = qemu_get_current_aio_context();
+
+ g_source_remove(s->watch);
+ s->watch = 0;
+ qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL,
+ NULL, NULL, false);
+
+ aio_bh_schedule_oneshot(ctx, chr_closed_bh, opaque);
+ }
break;
}
{
Error *err = NULL;
NetClientState *nc, *nc0 = NULL;
- VhostUserState *s;
+ NetVhostUserState *s = NULL;
+ VhostUserState *user;
int i;
assert(name);
assert(queues > 0);
+ user = g_new0(struct VhostUserState, 1);
for (i = 0; i < queues; i++) {
nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s",
nc->queue_index = i;
if (!nc0) {
nc0 = nc;
- s = DO_UPCAST(VhostUserState, nc, nc);
- if (!qemu_chr_fe_init(&s->chr, chr, &err)) {
+ s = DO_UPCAST(NetVhostUserState, nc, nc);
+ if (!qemu_chr_fe_init(&s->chr, chr, &err) ||
+ !vhost_user_init(user, &s->chr, &err)) {
error_report_err(err);
- return -1;
+ goto err;
}
}
-
+ s = DO_UPCAST(NetVhostUserState, nc, nc);
+ s->vhost_user = user;
}
- s = DO_UPCAST(VhostUserState, nc, nc0);
+ s = DO_UPCAST(NetVhostUserState, nc, nc0);
do {
if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) {
error_report_err(err);
- return -1;
+ goto err;
}
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
- net_vhost_user_event, nc0->name, NULL, true);
+ net_vhost_user_event, NULL, nc0->name, NULL,
+ true);
} while (!s->started);
assert(s->vhost_net);
return 0;
+
+err:
+ if (user) {
+ vhost_user_cleanup(user);
+ g_free(user);
+ if (s) {
+ s->vhost_user = NULL;
+ }
+ }
+ if (nc0) {
+ qemu_del_net_client(nc0);
+ }
+
+ return -1;
}
static Chardev *net_vhost_claim_chardev(