typedef struct VhostUserState {
NetClientState nc;
- CharDriverState *chr;
+ CharBackend chr; /* only queue index 0 */
VHostNetState *vhost_net;
guint watch;
uint64_t acked_features;
}
}
-static int vhost_user_start(int queues, NetClientState *ncs[])
+static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be)
{
VhostNetOptions options;
struct vhost_net *net = NULL;
s = DO_UPCAST(VhostUserState, nc, ncs[i]);
options.net_backend = ncs[i];
- options.opaque = s->chr;
+ options.opaque = be;
options.busyloop_timeout = 0;
net = vhost_net_init(&options);
if (!net) {
g_free(s->vhost_net);
s->vhost_net = NULL;
}
- if (s->chr) {
- qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
- qemu_chr_fe_release(s->chr);
- s->chr = NULL;
+ if (nc->queue_index == 0) {
+ Chardev *chr = qemu_chr_fe_get_driver(&s->chr);
+
+ qemu_chr_fe_deinit(&s->chr);
+ qemu_chr_delete(chr);
}
qemu_purge_queued_packets(nc);
{
VhostUserState *s = opaque;
- qemu_chr_disconnect(s->chr);
+ 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];
+ VhostUserState *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(VhostUserState, nc, ncs[0]);
+
+ qmp_set_link(name, false, &err);
+ vhost_user_stop(queues, ncs);
+
+ qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event,
+ 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;
+ Chardev *chr;
Error *err = NULL;
int queues;
assert(queues < MAX_QUEUE_NUM);
s = DO_UPCAST(VhostUserState, nc, ncs[0]);
- trace_vhost_user_event(s->chr->label, event);
+ 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) < 0) {
- qemu_chr_disconnect(s->chr);
+ if (vhost_user_start(queues, ncs, &s->chr) < 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, false);
+
+ aio_bh_schedule_oneshot(ctx, chr_closed_bh, opaque);
+ }
break;
}
}
static int net_vhost_user_init(NetClientState *peer, const char *device,
- const char *name, CharDriverState *chr,
+ const char *name, Chardev *chr,
int queues)
{
+ Error *err = NULL;
NetClientState *nc, *nc0 = NULL;
VhostUserState *s;
int i;
for (i = 0; i < queues; i++) {
nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
- if (!nc0) {
- nc0 = nc;
- }
-
snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s",
i, chr->label);
-
nc->queue_index = i;
+ if (!nc0) {
+ nc0 = nc;
+ s = DO_UPCAST(VhostUserState, nc, nc);
+ if (!qemu_chr_fe_init(&s->chr, chr, &err)) {
+ error_report_err(err);
+ return -1;
+ }
+ }
- s = DO_UPCAST(VhostUserState, nc, nc);
- s->chr = chr;
}
s = DO_UPCAST(VhostUserState, nc, nc0);
do {
- Error *err = NULL;
- if (qemu_chr_wait_connected(chr, &err) < 0) {
+ if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) {
error_report_err(err);
return -1;
}
- qemu_chr_add_handlers(chr, NULL, NULL,
- net_vhost_user_event, nc0->name);
+ qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
+ net_vhost_user_event, nc0->name, NULL, true);
} while (!s->started);
assert(s->vhost_net);
return 0;
}
-static CharDriverState *net_vhost_claim_chardev(
+static Chardev *net_vhost_claim_chardev(
const NetdevVhostUserOptions *opts, Error **errp)
{
- CharDriverState *chr = qemu_chr_find(opts->chardev);
+ Chardev *chr = qemu_chr_find(opts->chardev);
if (chr == NULL) {
error_setg(errp, "chardev \"%s\" not found", opts->chardev);
return NULL;
}
- qemu_chr_fe_claim_no_fail(chr);
-
return chr;
}
{
int queues;
const NetdevVhostUserOptions *vhost_user_opts;
- CharDriverState *chr;
+ Chardev *chr;
assert(netdev->type == NET_CLIENT_DRIVER_VHOST_USER);
vhost_user_opts = &netdev->u.vhost_user;