xref: /openbmc/qemu/net/vhost-user.c (revision a9c17e9a21af9c4bf9c08dedf0f0df4a6566cf52)
1d314f586SNikolay Nikolaev /*
2d314f586SNikolay Nikolaev  * vhost-user.c
3d314f586SNikolay Nikolaev  *
4d314f586SNikolay Nikolaev  * Copyright (c) 2013 Virtual Open Systems Sarl.
5d314f586SNikolay Nikolaev  *
6d314f586SNikolay Nikolaev  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7d314f586SNikolay Nikolaev  * See the COPYING file in the top-level directory.
8d314f586SNikolay Nikolaev  *
9d314f586SNikolay Nikolaev  */
10d314f586SNikolay Nikolaev 
112744d920SPeter Maydell #include "qemu/osdep.h"
12d314f586SNikolay Nikolaev #include "clients.h"
13d314f586SNikolay Nikolaev #include "net/vhost_net.h"
14d314f586SNikolay Nikolaev #include "net/vhost-user.h"
154d0cf552STiwei Bie #include "hw/virtio/vhost-user.h"
164d43a603SMarc-André Lureau #include "chardev/char-fe.h"
17e688df6bSMarkus Armbruster #include "qapi/error.h"
189af23989SMarkus Armbruster #include "qapi/qapi-commands-net.h"
1903ce5744SNikolay Nikolaev #include "qemu/config-file.h"
20d314f586SNikolay Nikolaev #include "qemu/error-report.h"
21922a01a0SMarkus Armbruster #include "qemu/option.h"
2269b32a6cSMarc-André Lureau #include "trace.h"
23d314f586SNikolay Nikolaev 
24703878e2STiwei Bie typedef struct NetVhostUserState {
25d314f586SNikolay Nikolaev     NetClientState nc;
265d300164SMarc-André Lureau     CharBackend chr; /* only queue index 0 */
274d0cf552STiwei Bie     VhostUserState *vhost_user;
28d314f586SNikolay Nikolaev     VHostNetState *vhost_net;
296f1de6b7SPaolo Bonzini     guint watch;
30a463215bSMarc-André Lureau     uint64_t acked_features;
31c89804d6SMarc-André Lureau     bool started;
32703878e2STiwei Bie } NetVhostUserState;
33d314f586SNikolay Nikolaev 
vhost_user_get_vhost_net(NetClientState * nc)34d314f586SNikolay Nikolaev VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
35d314f586SNikolay Nikolaev {
36703878e2STiwei Bie     NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
37f394b2e2SEric Blake     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
38d314f586SNikolay Nikolaev     return s->vhost_net;
39d314f586SNikolay Nikolaev }
40d314f586SNikolay Nikolaev 
vhost_user_get_acked_features(NetClientState * nc)41a463215bSMarc-André Lureau uint64_t vhost_user_get_acked_features(NetClientState *nc)
42a463215bSMarc-André Lureau {
43703878e2STiwei Bie     NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
44f394b2e2SEric Blake     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
45a463215bSMarc-André Lureau     return s->acked_features;
46a463215bSMarc-André Lureau }
47a463215bSMarc-André Lureau 
vhost_user_save_acked_features(NetClientState * nc)48937b7d96SHyman Huang(黄勇) void vhost_user_save_acked_features(NetClientState *nc)
49b931bfbfSChangchun Ouyang {
50703878e2STiwei Bie     NetVhostUserState *s;
51937b7d96SHyman Huang(黄勇) 
52937b7d96SHyman Huang(黄勇)     s = DO_UPCAST(NetVhostUserState, nc, nc);
53937b7d96SHyman Huang(黄勇)     if (s->vhost_net) {
54937b7d96SHyman Huang(黄勇)         uint64_t features = vhost_net_get_acked_features(s->vhost_net);
55937b7d96SHyman Huang(黄勇)         if (features) {
56937b7d96SHyman Huang(黄勇)             s->acked_features = features;
57937b7d96SHyman Huang(黄勇)         }
58937b7d96SHyman Huang(黄勇)     }
59937b7d96SHyman Huang(黄勇) }
60937b7d96SHyman Huang(黄勇) 
vhost_user_stop(int queues,NetClientState * ncs[])61937b7d96SHyman Huang(黄勇) static void vhost_user_stop(int queues, NetClientState *ncs[])
62937b7d96SHyman Huang(黄勇) {
63b931bfbfSChangchun Ouyang     int i;
64937b7d96SHyman Huang(黄勇)     NetVhostUserState *s;
65b931bfbfSChangchun Ouyang 
66b931bfbfSChangchun Ouyang     for (i = 0; i < queues; i++) {
67f394b2e2SEric Blake         assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
68b931bfbfSChangchun Ouyang 
69703878e2STiwei Bie         s = DO_UPCAST(NetVhostUserState, nc, ncs[i]);
70b931bfbfSChangchun Ouyang 
71b931bfbfSChangchun Ouyang         if (s->vhost_net) {
72937b7d96SHyman Huang(黄勇)             vhost_user_save_acked_features(ncs[i]);
73b931bfbfSChangchun Ouyang             vhost_net_cleanup(s->vhost_net);
74b931bfbfSChangchun Ouyang         }
75b931bfbfSChangchun Ouyang     }
76b931bfbfSChangchun Ouyang }
77b931bfbfSChangchun Ouyang 
vhost_user_start(int queues,NetClientState * ncs[],VhostUserState * be)784d0cf552STiwei Bie static int vhost_user_start(int queues, NetClientState *ncs[],
794d0cf552STiwei Bie                             VhostUserState *be)
80d314f586SNikolay Nikolaev {
81d314f586SNikolay Nikolaev     VhostNetOptions options;
82e6bcb1b6SMarc-André Lureau     struct vhost_net *net = NULL;
83703878e2STiwei Bie     NetVhostUserState *s;
84b931bfbfSChangchun Ouyang     int max_queues;
85b931bfbfSChangchun Ouyang     int i;
86d314f586SNikolay Nikolaev 
87d314f586SNikolay Nikolaev     options.backend_type = VHOST_BACKEND_TYPE_USER;
88d314f586SNikolay Nikolaev 
89b931bfbfSChangchun Ouyang     for (i = 0; i < queues; i++) {
90f394b2e2SEric Blake         assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
91d314f586SNikolay Nikolaev 
92703878e2STiwei Bie         s = DO_UPCAST(NetVhostUserState, nc, ncs[i]);
93d314f586SNikolay Nikolaev 
94b931bfbfSChangchun Ouyang         options.net_backend = ncs[i];
955d300164SMarc-André Lureau         options.opaque      = be;
9669e87b32SJason Wang         options.busyloop_timeout = 0;
976a756d14SJason Wang         options.nvqs = 2;
98e6bcb1b6SMarc-André Lureau         net = vhost_net_init(&options);
99e6bcb1b6SMarc-André Lureau         if (!net) {
1009af9e0feSMarkus Armbruster             error_report("failed to init vhost_net for queue %d", i);
101b931bfbfSChangchun Ouyang             goto err;
102b931bfbfSChangchun Ouyang         }
103b931bfbfSChangchun Ouyang 
104b931bfbfSChangchun Ouyang         if (i == 0) {
105e6bcb1b6SMarc-André Lureau             max_queues = vhost_net_get_max_queues(net);
106b931bfbfSChangchun Ouyang             if (queues > max_queues) {
1079af9e0feSMarkus Armbruster                 error_report("you are asking more queues than supported: %d",
1089af9e0feSMarkus Armbruster                              max_queues);
109b931bfbfSChangchun Ouyang                 goto err;
110b931bfbfSChangchun Ouyang             }
111b931bfbfSChangchun Ouyang         }
112e6bcb1b6SMarc-André Lureau 
113e6bcb1b6SMarc-André Lureau         if (s->vhost_net) {
114e6bcb1b6SMarc-André Lureau             vhost_net_cleanup(s->vhost_net);
115e6bcb1b6SMarc-André Lureau             g_free(s->vhost_net);
116e6bcb1b6SMarc-André Lureau         }
117e6bcb1b6SMarc-André Lureau         s->vhost_net = net;
118b931bfbfSChangchun Ouyang     }
119b931bfbfSChangchun Ouyang 
120b931bfbfSChangchun Ouyang     return 0;
121b931bfbfSChangchun Ouyang 
122b931bfbfSChangchun Ouyang err:
123e6bcb1b6SMarc-André Lureau     if (net) {
124e6bcb1b6SMarc-André Lureau         vhost_net_cleanup(net);
125a38a498dSlinzhecheng         g_free(net);
126e6bcb1b6SMarc-André Lureau     }
127e6bcb1b6SMarc-André Lureau     vhost_user_stop(i, ncs);
128b931bfbfSChangchun Ouyang     return -1;
129d314f586SNikolay Nikolaev }
130d314f586SNikolay Nikolaev 
vhost_user_receive(NetClientState * nc,const uint8_t * buf,size_t size)131f6f56291SThibaut Collet static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf,
132f6f56291SThibaut Collet                                   size_t size)
133f6f56291SThibaut Collet {
1343e866365SThibaut Collet     /* In case of RARP (message size is 60) notify backup to send a fake RARP.
1353e866365SThibaut Collet        This fake RARP will be sent by backend only for guest
1363e866365SThibaut Collet        without GUEST_ANNOUNCE capability.
137f6f56291SThibaut Collet      */
1383e866365SThibaut Collet     if (size == 60) {
139703878e2STiwei Bie         NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
1403e866365SThibaut Collet         int r;
1413e866365SThibaut Collet         static int display_rarp_failure = 1;
1423e866365SThibaut Collet         char mac_addr[6];
1433e866365SThibaut Collet 
1443e866365SThibaut Collet         /* extract guest mac address from the RARP message */
1453e866365SThibaut Collet         memcpy(mac_addr, &buf[6], 6);
1463e866365SThibaut Collet 
1473e866365SThibaut Collet         r = vhost_net_notify_migration_done(s->vhost_net, mac_addr);
1483e866365SThibaut Collet 
1493e866365SThibaut Collet         if ((r != 0) && (display_rarp_failure)) {
1503e866365SThibaut Collet             fprintf(stderr,
1513e866365SThibaut Collet                     "Vhost user backend fails to broadcast fake RARP\n");
1523e866365SThibaut Collet             fflush(stderr);
1533e866365SThibaut Collet             display_rarp_failure = 0;
1543e866365SThibaut Collet         }
1553e866365SThibaut Collet     }
1563e866365SThibaut Collet 
157f6f56291SThibaut Collet     return size;
158f6f56291SThibaut Collet }
159f6f56291SThibaut Collet 
net_vhost_user_cleanup(NetClientState * nc)1604d0cf552STiwei Bie static void net_vhost_user_cleanup(NetClientState *nc)
161d314f586SNikolay Nikolaev {
162703878e2STiwei Bie     NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
163d314f586SNikolay Nikolaev 
164b931bfbfSChangchun Ouyang     if (s->vhost_net) {
165b931bfbfSChangchun Ouyang         vhost_net_cleanup(s->vhost_net);
166e6bcb1b6SMarc-André Lureau         g_free(s->vhost_net);
167b931bfbfSChangchun Ouyang         s->vhost_net = NULL;
168b931bfbfSChangchun Ouyang     }
169c39860e6SMarc-André Lureau     if (nc->queue_index == 0) {
17041d4e5ecSYunjian Wang         if (s->watch) {
17141d4e5ecSYunjian Wang             g_source_remove(s->watch);
17241d4e5ecSYunjian Wang             s->watch = 0;
17341d4e5ecSYunjian Wang         }
1741ce2610cSMarc-André Lureau         qemu_chr_fe_deinit(&s->chr, true);
1754d0cf552STiwei Bie         if (s->vhost_user) {
1764d0cf552STiwei Bie             vhost_user_cleanup(s->vhost_user);
1774d0cf552STiwei Bie             g_free(s->vhost_user);
1784d0cf552STiwei Bie             s->vhost_user = NULL;
1794d0cf552STiwei Bie         }
18025f0d2aaSPaolo Bonzini     }
181b931bfbfSChangchun Ouyang 
182d314f586SNikolay Nikolaev     qemu_purge_queued_packets(nc);
183d314f586SNikolay Nikolaev }
184d314f586SNikolay Nikolaev 
vhost_user_set_vnet_endianness(NetClientState * nc,bool enable)185ba288898SPaolo Bonzini static int vhost_user_set_vnet_endianness(NetClientState *nc,
186ba288898SPaolo Bonzini                                           bool enable)
187ba288898SPaolo Bonzini {
188ba288898SPaolo Bonzini     /* Nothing to do.  If the server supports
189ba288898SPaolo Bonzini      * VHOST_USER_PROTOCOL_F_CROSS_ENDIAN, it will get the
190ba288898SPaolo Bonzini      * vnet header endianness from there.  If it doesn't, negotiation
191ba288898SPaolo Bonzini      * fails.
192ba288898SPaolo Bonzini      */
193ba288898SPaolo Bonzini     return 0;
194ba288898SPaolo Bonzini }
195ba288898SPaolo Bonzini 
vhost_user_has_vnet_hdr(NetClientState * nc)196d314f586SNikolay Nikolaev static bool vhost_user_has_vnet_hdr(NetClientState *nc)
197d314f586SNikolay Nikolaev {
198f394b2e2SEric Blake     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
199d314f586SNikolay Nikolaev 
200d314f586SNikolay Nikolaev     return true;
201d314f586SNikolay Nikolaev }
202d314f586SNikolay Nikolaev 
vhost_user_has_ufo(NetClientState * nc)203d314f586SNikolay Nikolaev static bool vhost_user_has_ufo(NetClientState *nc)
204d314f586SNikolay Nikolaev {
205f394b2e2SEric Blake     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
206d314f586SNikolay Nikolaev 
207d314f586SNikolay Nikolaev     return true;
208d314f586SNikolay Nikolaev }
209d314f586SNikolay Nikolaev 
vhost_user_check_peer_type(NetClientState * nc,ObjectClass * oc,Error ** errp)2105c485d51SKevin Wolf static bool vhost_user_check_peer_type(NetClientState *nc, ObjectClass *oc,
2115c485d51SKevin Wolf                                        Error **errp)
2125c485d51SKevin Wolf {
2135c485d51SKevin Wolf     const char *driver = object_class_get_name(oc);
2145c485d51SKevin Wolf 
2155c485d51SKevin Wolf     if (!g_str_has_prefix(driver, "virtio-net-")) {
2165c485d51SKevin Wolf         error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
2175c485d51SKevin Wolf         return false;
2185c485d51SKevin Wolf     }
2195c485d51SKevin Wolf 
2205c485d51SKevin Wolf     return true;
2215c485d51SKevin Wolf }
2225c485d51SKevin Wolf 
223d314f586SNikolay Nikolaev static NetClientInfo net_vhost_user_info = {
224f394b2e2SEric Blake         .type = NET_CLIENT_DRIVER_VHOST_USER,
225703878e2STiwei Bie         .size = sizeof(NetVhostUserState),
226f6f56291SThibaut Collet         .receive = vhost_user_receive,
2274d0cf552STiwei Bie         .cleanup = net_vhost_user_cleanup,
228d314f586SNikolay Nikolaev         .has_vnet_hdr = vhost_user_has_vnet_hdr,
229d314f586SNikolay Nikolaev         .has_ufo = vhost_user_has_ufo,
230ba288898SPaolo Bonzini         .set_vnet_be = vhost_user_set_vnet_endianness,
231ba288898SPaolo Bonzini         .set_vnet_le = vhost_user_set_vnet_endianness,
2325c485d51SKevin Wolf         .check_peer_type = vhost_user_check_peer_type,
233d314f586SNikolay Nikolaev };
234d314f586SNikolay Nikolaev 
net_vhost_user_watch(void * do_not_use,GIOCondition cond,void * opaque)235bf7b1eabSMarc-André Lureau static gboolean net_vhost_user_watch(void *do_not_use, GIOCondition cond,
236a6553598STetsuya Mukawa                                      void *opaque)
237a6553598STetsuya Mukawa {
238703878e2STiwei Bie     NetVhostUserState *s = opaque;
239a6553598STetsuya Mukawa 
2405345fdb4SMarc-André Lureau     qemu_chr_fe_disconnect(&s->chr);
241a6553598STetsuya Mukawa 
242*53c7c924SPhilippe Mathieu-Daudé     return G_SOURCE_CONTINUE;
243e7c83a88SMarc-André Lureau }
244e7c83a88SMarc-André Lureau 
245083b266fSPhilippe Mathieu-Daudé static void net_vhost_user_event(void *opaque, QEMUChrEvent event);
246e7c83a88SMarc-André Lureau 
chr_closed_bh(void * opaque)247e7c83a88SMarc-André Lureau static void chr_closed_bh(void *opaque)
248e7c83a88SMarc-André Lureau {
249e7c83a88SMarc-André Lureau     const char *name = opaque;
250e7c83a88SMarc-André Lureau     NetClientState *ncs[MAX_QUEUE_NUM];
251703878e2STiwei Bie     NetVhostUserState *s;
252e7c83a88SMarc-André Lureau     Error *err = NULL;
253f66337bdShaibinzhang(张海斌)     int queues, i;
254e7c83a88SMarc-André Lureau 
255e7c83a88SMarc-André Lureau     queues = qemu_find_net_clients_except(name, ncs,
256e7c83a88SMarc-André Lureau                                           NET_CLIENT_DRIVER_NIC,
257e7c83a88SMarc-André Lureau                                           MAX_QUEUE_NUM);
258e7c83a88SMarc-André Lureau     assert(queues < MAX_QUEUE_NUM);
259e7c83a88SMarc-André Lureau 
260703878e2STiwei Bie     s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
261e7c83a88SMarc-André Lureau 
262f66337bdShaibinzhang(张海斌)     for (i = queues -1; i >= 0; i--) {
263bebcac05SHyman Huang(黄勇)         vhost_user_save_acked_features(ncs[i]);
264f66337bdShaibinzhang(张海斌)     }
265c6beefd6SAdrian Moreno 
266e7c83a88SMarc-André Lureau     qmp_set_link(name, false, &err);
267e7c83a88SMarc-André Lureau 
268e7c83a88SMarc-André Lureau     qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event,
26981517ba3SAnton Nefedov                              NULL, opaque, NULL, true);
270e7c83a88SMarc-André Lureau 
271e7c83a88SMarc-André Lureau     if (err) {
272e7c83a88SMarc-André Lureau         error_report_err(err);
273e7c83a88SMarc-André Lureau     }
274a6553598STetsuya Mukawa }
275a6553598STetsuya Mukawa 
net_vhost_user_event(void * opaque,QEMUChrEvent event)276083b266fSPhilippe Mathieu-Daudé static void net_vhost_user_event(void *opaque, QEMUChrEvent event)
277d314f586SNikolay Nikolaev {
278b931bfbfSChangchun Ouyang     const char *name = opaque;
279b931bfbfSChangchun Ouyang     NetClientState *ncs[MAX_QUEUE_NUM];
280703878e2STiwei Bie     NetVhostUserState *s;
2810ec7b3e7SMarc-André Lureau     Chardev *chr;
282b931bfbfSChangchun Ouyang     Error *err = NULL;
283b931bfbfSChangchun Ouyang     int queues;
284d314f586SNikolay Nikolaev 
285b931bfbfSChangchun Ouyang     queues = qemu_find_net_clients_except(name, ncs,
286f394b2e2SEric Blake                                           NET_CLIENT_DRIVER_NIC,
287b931bfbfSChangchun Ouyang                                           MAX_QUEUE_NUM);
288c1bf3531SMarc-André Lureau     assert(queues < MAX_QUEUE_NUM);
289c1bf3531SMarc-André Lureau 
290703878e2STiwei Bie     s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
2915345fdb4SMarc-André Lureau     chr = qemu_chr_fe_get_driver(&s->chr);
2925345fdb4SMarc-André Lureau     trace_vhost_user_event(chr->label, event);
293d314f586SNikolay Nikolaev     switch (event) {
294d314f586SNikolay Nikolaev     case CHR_EVENT_OPENED:
2954d0cf552STiwei Bie         if (vhost_user_start(queues, ncs, s->vhost_user) < 0) {
2965345fdb4SMarc-André Lureau             qemu_chr_fe_disconnect(&s->chr);
2970d572afdSMarc-André Lureau             return;
298b931bfbfSChangchun Ouyang         }
299e7c83a88SMarc-André Lureau         s->watch = qemu_chr_fe_add_watch(&s->chr, G_IO_HUP,
300e7c83a88SMarc-André Lureau                                          net_vhost_user_watch, s);
301b931bfbfSChangchun Ouyang         qmp_set_link(name, true, &err);
302c89804d6SMarc-André Lureau         s->started = true;
303d314f586SNikolay Nikolaev         break;
304d314f586SNikolay Nikolaev     case CHR_EVENT_CLOSED:
305e7c83a88SMarc-André Lureau         /* a close event may happen during a read/write, but vhost
306e7c83a88SMarc-André Lureau          * code assumes the vhost_dev remains setup, so delay the
307e7c83a88SMarc-André Lureau          * stop & clear to idle.
308e7c83a88SMarc-André Lureau          * FIXME: better handle failure in vhost code, remove bh
309e7c83a88SMarc-André Lureau          */
310e7c83a88SMarc-André Lureau         if (s->watch) {
311e7c83a88SMarc-André Lureau             AioContext *ctx = qemu_get_current_aio_context();
312e7c83a88SMarc-André Lureau 
313a6553598STetsuya Mukawa             g_source_remove(s->watch);
314a6553598STetsuya Mukawa             s->watch = 0;
31581517ba3SAnton Nefedov             qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL,
316e7c83a88SMarc-André Lureau                                      NULL, NULL, false);
317e7c83a88SMarc-André Lureau 
318e7c83a88SMarc-André Lureau             aio_bh_schedule_oneshot(ctx, chr_closed_bh, opaque);
319e7c83a88SMarc-André Lureau         }
320d314f586SNikolay Nikolaev         break;
321d0ab6769SPhilippe Mathieu-Daudé     case CHR_EVENT_BREAK:
322d0ab6769SPhilippe Mathieu-Daudé     case CHR_EVENT_MUX_IN:
323d0ab6769SPhilippe Mathieu-Daudé     case CHR_EVENT_MUX_OUT:
324d0ab6769SPhilippe Mathieu-Daudé         /* Ignore */
325d0ab6769SPhilippe Mathieu-Daudé         break;
326d314f586SNikolay Nikolaev     }
327b931bfbfSChangchun Ouyang 
328b931bfbfSChangchun Ouyang     if (err) {
329b931bfbfSChangchun Ouyang         error_report_err(err);
330b931bfbfSChangchun Ouyang     }
331d314f586SNikolay Nikolaev }
332d314f586SNikolay Nikolaev 
net_vhost_user_init(NetClientState * peer,const char * device,const char * name,Chardev * chr,int queues)333d314f586SNikolay Nikolaev static int net_vhost_user_init(NetClientState *peer, const char *device,
334f9bb0c1fSJason Wang                                const char *name, Chardev *chr,
335f9bb0c1fSJason Wang                                int queues)
336d314f586SNikolay Nikolaev {
33732a6ebecSMarc-André Lureau     Error *err = NULL;
338c89804d6SMarc-André Lureau     NetClientState *nc, *nc0 = NULL;
3394d0cf552STiwei Bie     NetVhostUserState *s = NULL;
3400b99f224SMarc-André Lureau     VhostUserState *user;
341b931bfbfSChangchun Ouyang     int i;
342d314f586SNikolay Nikolaev 
343c1bf3531SMarc-André Lureau     assert(name);
344c1bf3531SMarc-André Lureau     assert(queues > 0);
345c1bf3531SMarc-André Lureau 
3460b99f224SMarc-André Lureau     user = g_new0(struct VhostUserState, 1);
347b931bfbfSChangchun Ouyang     for (i = 0; i < queues; i++) {
348d314f586SNikolay Nikolaev         nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
34953b85d95SLaurent Vivier         qemu_set_info_str(nc, "vhost-user%d to %s", i, chr->label);
350b931bfbfSChangchun Ouyang         nc->queue_index = i;
3515d300164SMarc-André Lureau         if (!nc0) {
3525d300164SMarc-André Lureau             nc0 = nc;
353703878e2STiwei Bie             s = DO_UPCAST(NetVhostUserState, nc, nc);
3540b99f224SMarc-André Lureau             if (!qemu_chr_fe_init(&s->chr, chr, &err) ||
3550b99f224SMarc-André Lureau                 !vhost_user_init(user, &s->chr, &err)) {
35632a6ebecSMarc-André Lureau                 error_report_err(err);
3574d0cf552STiwei Bie                 goto err;
35832a6ebecSMarc-André Lureau             }
359b931bfbfSChangchun Ouyang         }
3604d0cf552STiwei Bie         s = DO_UPCAST(NetVhostUserState, nc, nc);
3614d0cf552STiwei Bie         s->vhost_user = user;
3625d300164SMarc-André Lureau     }
3635d300164SMarc-André Lureau 
364703878e2STiwei Bie     s = DO_UPCAST(NetVhostUserState, nc, nc0);
365c89804d6SMarc-André Lureau     do {
3665345fdb4SMarc-André Lureau         if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) {
367c89804d6SMarc-André Lureau             error_report_err(err);
3684d0cf552STiwei Bie             goto err;
369c89804d6SMarc-André Lureau         }
3705345fdb4SMarc-André Lureau         qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
37181517ba3SAnton Nefedov                                  net_vhost_user_event, NULL, nc0->name, NULL,
37281517ba3SAnton Nefedov                                  true);
373c89804d6SMarc-André Lureau     } while (!s->started);
374d345ed2dSMichael S. Tsirkin 
3751a5b68ceSMarc-André Lureau     assert(s->vhost_net);
3761a5b68ceSMarc-André Lureau 
377d314f586SNikolay Nikolaev     return 0;
3784d0cf552STiwei Bie 
3794d0cf552STiwei Bie err:
3804d0cf552STiwei Bie     if (user) {
3814d0cf552STiwei Bie         vhost_user_cleanup(user);
3824d0cf552STiwei Bie         g_free(user);
3834d0cf552STiwei Bie         if (s) {
3844d0cf552STiwei Bie             s->vhost_user = NULL;
3854d0cf552STiwei Bie         }
3864d0cf552STiwei Bie     }
387c67daf4aSlinzhecheng     if (nc0) {
388c67daf4aSlinzhecheng         qemu_del_net_client(nc0);
389c67daf4aSlinzhecheng     }
3904d0cf552STiwei Bie 
3914d0cf552STiwei Bie     return -1;
392d314f586SNikolay Nikolaev }
393d314f586SNikolay Nikolaev 
net_vhost_claim_chardev(const NetdevVhostUserOptions * opts,Error ** errp)3940ec7b3e7SMarc-André Lureau static Chardev *net_vhost_claim_chardev(
39581904831SMarkus Armbruster     const NetdevVhostUserOptions *opts, Error **errp)
39603ce5744SNikolay Nikolaev {
3970ec7b3e7SMarc-André Lureau     Chardev *chr = qemu_chr_find(opts->chardev);
39803ce5744SNikolay Nikolaev 
39903ce5744SNikolay Nikolaev     if (chr == NULL) {
40081904831SMarkus Armbruster         error_setg(errp, "chardev \"%s\" not found", opts->chardev);
40103ce5744SNikolay Nikolaev         return NULL;
40203ce5744SNikolay Nikolaev     }
40303ce5744SNikolay Nikolaev 
4040a73336dSDaniel P. Berrange     if (!qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE)) {
4050a73336dSDaniel P. Berrange         error_setg(errp, "chardev \"%s\" is not reconnectable",
4060a73336dSDaniel P. Berrange                    opts->chardev);
40703ce5744SNikolay Nikolaev         return NULL;
40803ce5744SNikolay Nikolaev     }
4090a73336dSDaniel P. Berrange     if (!qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_FD_PASS)) {
4100a73336dSDaniel P. Berrange         error_setg(errp, "chardev \"%s\" does not support FD passing",
41103ce5744SNikolay Nikolaev                    opts->chardev);
41203ce5744SNikolay Nikolaev         return NULL;
41303ce5744SNikolay Nikolaev     }
41403ce5744SNikolay Nikolaev 
41503ce5744SNikolay Nikolaev     return chr;
41603ce5744SNikolay Nikolaev }
41703ce5744SNikolay Nikolaev 
net_init_vhost_user(const Netdev * netdev,const char * name,NetClientState * peer,Error ** errp)418cebea510SKővágó, Zoltán int net_init_vhost_user(const Netdev *netdev, const char *name,
419a30ecde6SMarkus Armbruster                         NetClientState *peer, Error **errp)
420d314f586SNikolay Nikolaev {
421b931bfbfSChangchun Ouyang     int queues;
42203ce5744SNikolay Nikolaev     const NetdevVhostUserOptions *vhost_user_opts;
4230ec7b3e7SMarc-André Lureau     Chardev *chr;
42403ce5744SNikolay Nikolaev 
425f394b2e2SEric Blake     assert(netdev->type == NET_CLIENT_DRIVER_VHOST_USER);
426f394b2e2SEric Blake     vhost_user_opts = &netdev->u.vhost_user;
42703ce5744SNikolay Nikolaev 
4280a73336dSDaniel P. Berrange     chr = net_vhost_claim_chardev(vhost_user_opts, errp);
42903ce5744SNikolay Nikolaev     if (!chr) {
43003ce5744SNikolay Nikolaev         return -1;
43103ce5744SNikolay Nikolaev     }
43203ce5744SNikolay Nikolaev 
433b931bfbfSChangchun Ouyang     queues = vhost_user_opts->has_queues ? vhost_user_opts->queues : 1;
434fff4e48eSIlya Maximets     if (queues < 1 || queues > MAX_QUEUE_NUM) {
4356f6f9512SVictor Kaplansky         error_setg(errp,
436fff4e48eSIlya Maximets                    "vhost-user number of queues must be in range [1, %d]",
437fff4e48eSIlya Maximets                    MAX_QUEUE_NUM);
4386f6f9512SVictor Kaplansky         return -1;
4396f6f9512SVictor Kaplansky     }
44003ce5744SNikolay Nikolaev 
441f9bb0c1fSJason Wang     return net_vhost_user_init(peer, "vhost_user", name, chr, queues);
442d314f586SNikolay Nikolaev }
443