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