xref: /openbmc/qemu/net/vhost-user.c (revision effdacbf28d76ca0eec9086539649e547e510bbc)
1 /*
2  * vhost-user.c
3  *
4  * Copyright (c) 2013 Virtual Open Systems Sarl.
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7  * See the COPYING file in the top-level directory.
8  *
9  */
10 
11 #include "qemu/osdep.h"
12 #include "clients.h"
13 #include "net/vhost_net.h"
14 #include "net/vhost-user.h"
15 #include "hw/virtio/vhost.h"
16 #include "hw/virtio/vhost-user.h"
17 #include "standard-headers/linux/virtio_net.h"
18 #include "chardev/char-fe.h"
19 #include "qapi/error.h"
20 #include "qapi/qapi-commands-net.h"
21 #include "qapi/qapi-events-net.h"
22 #include "qemu/config-file.h"
23 #include "qemu/error-report.h"
24 #include "qemu/option.h"
25 #include "trace.h"
26 
27 static const int user_feature_bits[] = {
28     VIRTIO_F_NOTIFY_ON_EMPTY,
29     VIRTIO_F_NOTIFICATION_DATA,
30     VIRTIO_RING_F_INDIRECT_DESC,
31     VIRTIO_RING_F_EVENT_IDX,
32 
33     VIRTIO_F_ANY_LAYOUT,
34     VIRTIO_F_VERSION_1,
35     VIRTIO_NET_F_CSUM,
36     VIRTIO_NET_F_GUEST_CSUM,
37     VIRTIO_NET_F_GSO,
38     VIRTIO_NET_F_GUEST_TSO4,
39     VIRTIO_NET_F_GUEST_TSO6,
40     VIRTIO_NET_F_GUEST_ECN,
41     VIRTIO_NET_F_GUEST_UFO,
42     VIRTIO_NET_F_HOST_TSO4,
43     VIRTIO_NET_F_HOST_TSO6,
44     VIRTIO_NET_F_HOST_ECN,
45     VIRTIO_NET_F_HOST_UFO,
46     VIRTIO_NET_F_MRG_RXBUF,
47     VIRTIO_NET_F_MTU,
48     VIRTIO_F_IOMMU_PLATFORM,
49     VIRTIO_F_RING_PACKED,
50     VIRTIO_F_RING_RESET,
51     VIRTIO_F_IN_ORDER,
52     VIRTIO_NET_F_RSS,
53     VIRTIO_NET_F_RSC_EXT,
54     VIRTIO_NET_F_HASH_REPORT,
55     VIRTIO_NET_F_GUEST_USO4,
56     VIRTIO_NET_F_GUEST_USO6,
57     VIRTIO_NET_F_HOST_USO,
58 
59     /* This bit implies RARP isn't sent by QEMU out of band */
60     VIRTIO_NET_F_GUEST_ANNOUNCE,
61 
62     VIRTIO_NET_F_MQ,
63 
64     VHOST_INVALID_FEATURE_BIT
65 };
66 
67 typedef struct NetVhostUserState {
68     NetClientState nc;
69     CharBackend chr; /* only queue index 0 */
70     VhostUserState *vhost_user;
71     VHostNetState *vhost_net;
72     guint watch;
73     uint64_t acked_features;
74     bool started;
75 } NetVhostUserState;
76 
77 static struct vhost_net *vhost_user_get_vhost_net(NetClientState *nc)
78 {
79     NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
80     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
81     return s->vhost_net;
82 }
83 
84 uint64_t vhost_user_get_acked_features(NetClientState *nc)
85 {
86     NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
87     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
88     return s->acked_features;
89 }
90 
91 void vhost_user_save_acked_features(NetClientState *nc)
92 {
93     NetVhostUserState *s;
94 
95     s = DO_UPCAST(NetVhostUserState, nc, nc);
96     if (s->vhost_net) {
97         uint64_t features = vhost_net_get_acked_features(s->vhost_net);
98         if (features) {
99             s->acked_features = features;
100         }
101     }
102 }
103 
104 static void vhost_user_stop(int queues, NetClientState *ncs[])
105 {
106     int i;
107     NetVhostUserState *s;
108 
109     for (i = 0; i < queues; i++) {
110         assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
111 
112         s = DO_UPCAST(NetVhostUserState, nc, ncs[i]);
113 
114         if (s->vhost_net) {
115             vhost_user_save_acked_features(ncs[i]);
116             vhost_net_cleanup(s->vhost_net);
117         }
118     }
119 }
120 
121 static int vhost_user_start(int queues, NetClientState *ncs[],
122                             VhostUserState *be)
123 {
124     VhostNetOptions options;
125     struct vhost_net *net = NULL;
126     NetVhostUserState *s;
127     int max_queues;
128     int i;
129 
130     options.backend_type = VHOST_BACKEND_TYPE_USER;
131 
132     for (i = 0; i < queues; i++) {
133         assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
134 
135         s = DO_UPCAST(NetVhostUserState, nc, ncs[i]);
136 
137         options.net_backend = ncs[i];
138         options.opaque      = be;
139         options.busyloop_timeout = 0;
140         options.nvqs = 2;
141         options.feature_bits = user_feature_bits;
142         net = vhost_net_init(&options);
143         if (!net) {
144             error_report("failed to init vhost_net for queue %d", i);
145             goto err;
146         }
147 
148         if (i == 0) {
149             max_queues = vhost_net_get_max_queues(net);
150             if (queues > max_queues) {
151                 error_report("you are asking more queues than supported: %d",
152                              max_queues);
153                 goto err;
154             }
155         }
156 
157         if (s->vhost_net) {
158             vhost_net_cleanup(s->vhost_net);
159             g_free(s->vhost_net);
160         }
161         s->vhost_net = net;
162     }
163 
164     return 0;
165 
166 err:
167     if (net) {
168         vhost_net_cleanup(net);
169         g_free(net);
170     }
171     vhost_user_stop(i, ncs);
172     return -1;
173 }
174 
175 static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf,
176                                   size_t size)
177 {
178     /* In case of RARP (message size is 60) notify backup to send a fake RARP.
179        This fake RARP will be sent by backend only for guest
180        without GUEST_ANNOUNCE capability.
181      */
182     if (size == 60) {
183         NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
184         int r;
185         static int display_rarp_failure = 1;
186         char mac_addr[6];
187 
188         /* extract guest mac address from the RARP message */
189         memcpy(mac_addr, &buf[6], 6);
190 
191         r = vhost_net_notify_migration_done(s->vhost_net, mac_addr);
192 
193         if ((r != 0) && (display_rarp_failure)) {
194             fprintf(stderr,
195                     "Vhost user backend fails to broadcast fake RARP\n");
196             fflush(stderr);
197             display_rarp_failure = 0;
198         }
199     }
200 
201     return size;
202 }
203 
204 static void net_vhost_user_cleanup(NetClientState *nc)
205 {
206     NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
207 
208     if (s->vhost_net) {
209         vhost_net_cleanup(s->vhost_net);
210         g_free(s->vhost_net);
211         s->vhost_net = NULL;
212     }
213     if (nc->queue_index == 0) {
214         if (s->watch) {
215             g_source_remove(s->watch);
216             s->watch = 0;
217         }
218         qemu_chr_fe_deinit(&s->chr, true);
219         if (s->vhost_user) {
220             vhost_user_cleanup(s->vhost_user);
221             g_free(s->vhost_user);
222             s->vhost_user = NULL;
223         }
224     }
225 
226     qemu_purge_queued_packets(nc);
227 }
228 
229 static int vhost_user_set_vnet_endianness(NetClientState *nc,
230                                           bool enable)
231 {
232     /* Nothing to do.  If the server supports
233      * VHOST_USER_PROTOCOL_F_CROSS_ENDIAN, it will get the
234      * vnet header endianness from there.  If it doesn't, negotiation
235      * fails.
236      */
237     return 0;
238 }
239 
240 static bool vhost_user_has_vnet_hdr(NetClientState *nc)
241 {
242     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
243 
244     return true;
245 }
246 
247 static bool vhost_user_has_ufo(NetClientState *nc)
248 {
249     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
250 
251     return true;
252 }
253 
254 static bool vhost_user_check_peer_type(NetClientState *nc, ObjectClass *oc,
255                                        Error **errp)
256 {
257     const char *driver = object_class_get_name(oc);
258 
259     if (!g_str_has_prefix(driver, "virtio-net-")) {
260         error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
261         return false;
262     }
263 
264     return true;
265 }
266 
267 static NetClientInfo net_vhost_user_info = {
268         .type = NET_CLIENT_DRIVER_VHOST_USER,
269         .size = sizeof(NetVhostUserState),
270         .receive = vhost_user_receive,
271         .cleanup = net_vhost_user_cleanup,
272         .has_vnet_hdr = vhost_user_has_vnet_hdr,
273         .has_ufo = vhost_user_has_ufo,
274         .set_vnet_be = vhost_user_set_vnet_endianness,
275         .set_vnet_le = vhost_user_set_vnet_endianness,
276         .check_peer_type = vhost_user_check_peer_type,
277         .get_vhost_net = vhost_user_get_vhost_net,
278 };
279 
280 static gboolean net_vhost_user_watch(void *do_not_use, GIOCondition cond,
281                                      void *opaque)
282 {
283     NetVhostUserState *s = opaque;
284 
285     qemu_chr_fe_disconnect(&s->chr);
286 
287     return G_SOURCE_CONTINUE;
288 }
289 
290 static void net_vhost_user_event(void *opaque, QEMUChrEvent event);
291 
292 static void chr_closed_bh(void *opaque)
293 {
294     const char *name = opaque;
295     NetClientState *ncs[MAX_QUEUE_NUM];
296     NetVhostUserState *s;
297     Error *err = NULL;
298     int queues, i;
299 
300     queues = qemu_find_net_clients_except(name, ncs,
301                                           NET_CLIENT_DRIVER_NIC,
302                                           MAX_QUEUE_NUM);
303     assert(queues < MAX_QUEUE_NUM);
304 
305     s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
306 
307     for (i = queues -1; i >= 0; i--) {
308         vhost_user_save_acked_features(ncs[i]);
309     }
310 
311     net_client_set_link(ncs, queues, false);
312 
313     qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event,
314                              NULL, opaque, NULL, true);
315 
316     if (err) {
317         error_report_err(err);
318     }
319     qapi_event_send_netdev_vhost_user_disconnected(name);
320 }
321 
322 static void net_vhost_user_event(void *opaque, QEMUChrEvent event)
323 {
324     const char *name = opaque;
325     NetClientState *ncs[MAX_QUEUE_NUM];
326     NetVhostUserState *s;
327     Chardev *chr;
328     Error *err = NULL;
329     int queues;
330 
331     queues = qemu_find_net_clients_except(name, ncs,
332                                           NET_CLIENT_DRIVER_NIC,
333                                           MAX_QUEUE_NUM);
334     assert(queues < MAX_QUEUE_NUM);
335 
336     s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
337     chr = qemu_chr_fe_get_driver(&s->chr);
338     trace_vhost_user_event(chr->label, event);
339     switch (event) {
340     case CHR_EVENT_OPENED:
341         if (vhost_user_start(queues, ncs, s->vhost_user) < 0) {
342             qemu_chr_fe_disconnect(&s->chr);
343             return;
344         }
345         s->watch = qemu_chr_fe_add_watch(&s->chr, G_IO_HUP,
346                                          net_vhost_user_watch, s);
347         net_client_set_link(ncs, queues, true);
348         s->started = true;
349         qapi_event_send_netdev_vhost_user_connected(name, chr->label);
350         break;
351     case CHR_EVENT_CLOSED:
352         /* a close event may happen during a read/write, but vhost
353          * code assumes the vhost_dev remains setup, so delay the
354          * stop & clear to idle.
355          * FIXME: better handle failure in vhost code, remove bh
356          */
357         if (s->watch) {
358             AioContext *ctx = qemu_get_current_aio_context();
359 
360             g_source_remove(s->watch);
361             s->watch = 0;
362             qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL,
363                                      NULL, NULL, false);
364 
365             aio_bh_schedule_oneshot(ctx, chr_closed_bh, opaque);
366         }
367         break;
368     case CHR_EVENT_BREAK:
369     case CHR_EVENT_MUX_IN:
370     case CHR_EVENT_MUX_OUT:
371         /* Ignore */
372         break;
373     }
374 
375     if (err) {
376         error_report_err(err);
377     }
378 }
379 
380 static int net_vhost_user_init(NetClientState *peer, const char *device,
381                                const char *name, Chardev *chr,
382                                int queues)
383 {
384     Error *err = NULL;
385     NetClientState *nc, *nc0 = NULL;
386     NetVhostUserState *s = NULL;
387     VhostUserState *user;
388     int i;
389 
390     assert(name);
391     assert(queues > 0);
392 
393     user = g_new0(struct VhostUserState, 1);
394     for (i = 0; i < queues; i++) {
395         nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
396         qemu_set_info_str(nc, "vhost-user%d to %s", i, chr->label);
397         nc->queue_index = i;
398         if (!nc0) {
399             nc0 = nc;
400             s = DO_UPCAST(NetVhostUserState, nc, nc);
401             if (!qemu_chr_fe_init(&s->chr, chr, &err) ||
402                 !vhost_user_init(user, &s->chr, &err)) {
403                 error_report_err(err);
404                 goto err;
405             }
406         }
407         s = DO_UPCAST(NetVhostUserState, nc, nc);
408         s->vhost_user = user;
409     }
410 
411     s = DO_UPCAST(NetVhostUserState, nc, nc0);
412     do {
413         if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) {
414             error_report_err(err);
415             goto err;
416         }
417         qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
418                                  net_vhost_user_event, NULL, nc0->name, NULL,
419                                  true);
420     } while (!s->started);
421 
422     assert(s->vhost_net);
423 
424     return 0;
425 
426 err:
427     if (user) {
428         vhost_user_cleanup(user);
429         g_free(user);
430         if (s) {
431             s->vhost_user = NULL;
432         }
433     }
434     if (nc0) {
435         qemu_del_net_client(nc0);
436     }
437 
438     return -1;
439 }
440 
441 static Chardev *net_vhost_claim_chardev(
442     const NetdevVhostUserOptions *opts, Error **errp)
443 {
444     Chardev *chr = qemu_chr_find(opts->chardev);
445 
446     if (chr == NULL) {
447         error_setg(errp, "chardev \"%s\" not found", opts->chardev);
448         return NULL;
449     }
450 
451     if (!qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE)) {
452         error_setg(errp, "chardev \"%s\" is not reconnectable",
453                    opts->chardev);
454         return NULL;
455     }
456     if (!qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_FD_PASS)) {
457         error_setg(errp, "chardev \"%s\" does not support FD passing",
458                    opts->chardev);
459         return NULL;
460     }
461 
462     return chr;
463 }
464 
465 int net_init_vhost_user(const Netdev *netdev, const char *name,
466                         NetClientState *peer, Error **errp)
467 {
468     int queues;
469     const NetdevVhostUserOptions *vhost_user_opts;
470     Chardev *chr;
471 
472     assert(netdev->type == NET_CLIENT_DRIVER_VHOST_USER);
473     vhost_user_opts = &netdev->u.vhost_user;
474 
475     chr = net_vhost_claim_chardev(vhost_user_opts, errp);
476     if (!chr) {
477         return -1;
478     }
479 
480     queues = vhost_user_opts->has_queues ? vhost_user_opts->queues : 1;
481     if (queues < 1 || queues > MAX_QUEUE_NUM) {
482         error_setg(errp,
483                    "vhost-user number of queues must be in range [1, %d]",
484                    MAX_QUEUE_NUM);
485         return -1;
486     }
487 
488     return net_vhost_user_init(peer, "vhost_user", name, chr, queues);
489 }
490