]> Git Repo - qemu.git/blob - net/vhost-user.c
vhost-user: disconnect on start failure
[qemu.git] / net / vhost-user.c
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 "sysemu/char.h"
16 #include "qemu/config-file.h"
17 #include "qemu/error-report.h"
18 #include "qmp-commands.h"
19 #include "trace.h"
20
21 typedef struct VhostUserState {
22     NetClientState nc;
23     CharDriverState *chr;
24     VHostNetState *vhost_net;
25     int watch;
26 } VhostUserState;
27
28 typedef struct VhostUserChardevProps {
29     bool is_socket;
30     bool is_unix;
31 } VhostUserChardevProps;
32
33 VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
34 {
35     VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
36     assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
37     return s->vhost_net;
38 }
39
40 static int vhost_user_running(VhostUserState *s)
41 {
42     return (s->vhost_net) ? 1 : 0;
43 }
44
45 static void vhost_user_stop(int queues, NetClientState *ncs[])
46 {
47     VhostUserState *s;
48     int i;
49
50     for (i = 0; i < queues; i++) {
51         assert (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
52
53         s = DO_UPCAST(VhostUserState, nc, ncs[i]);
54         if (!vhost_user_running(s)) {
55             continue;
56         }
57
58         if (s->vhost_net) {
59             vhost_net_cleanup(s->vhost_net);
60             s->vhost_net = NULL;
61         }
62     }
63 }
64
65 static int vhost_user_start(int queues, NetClientState *ncs[])
66 {
67     VhostNetOptions options;
68     VhostUserState *s;
69     int max_queues;
70     int i;
71
72     options.backend_type = VHOST_BACKEND_TYPE_USER;
73
74     for (i = 0; i < queues; i++) {
75         assert (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
76
77         s = DO_UPCAST(VhostUserState, nc, ncs[i]);
78         if (vhost_user_running(s)) {
79             continue;
80         }
81
82         options.net_backend = ncs[i];
83         options.opaque      = s->chr;
84         s->vhost_net = vhost_net_init(&options);
85         if (!s->vhost_net) {
86             error_report("failed to init vhost_net for queue %d", i);
87             goto err;
88         }
89
90         if (i == 0) {
91             max_queues = vhost_net_get_max_queues(s->vhost_net);
92             if (queues > max_queues) {
93                 error_report("you are asking more queues than supported: %d",
94                              max_queues);
95                 goto err;
96             }
97         }
98     }
99
100     return 0;
101
102 err:
103     vhost_user_stop(i + 1, ncs);
104     return -1;
105 }
106
107 static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf,
108                                   size_t size)
109 {
110     /* In case of RARP (message size is 60) notify backup to send a fake RARP.
111        This fake RARP will be sent by backend only for guest
112        without GUEST_ANNOUNCE capability.
113      */
114     if (size == 60) {
115         VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
116         int r;
117         static int display_rarp_failure = 1;
118         char mac_addr[6];
119
120         /* extract guest mac address from the RARP message */
121         memcpy(mac_addr, &buf[6], 6);
122
123         r = vhost_net_notify_migration_done(s->vhost_net, mac_addr);
124
125         if ((r != 0) && (display_rarp_failure)) {
126             fprintf(stderr,
127                     "Vhost user backend fails to broadcast fake RARP\n");
128             fflush(stderr);
129             display_rarp_failure = 0;
130         }
131     }
132
133     return size;
134 }
135
136 static void vhost_user_cleanup(NetClientState *nc)
137 {
138     VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
139
140     if (s->vhost_net) {
141         vhost_net_cleanup(s->vhost_net);
142         s->vhost_net = NULL;
143     }
144
145     qemu_purge_queued_packets(nc);
146 }
147
148 static bool vhost_user_has_vnet_hdr(NetClientState *nc)
149 {
150     assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
151
152     return true;
153 }
154
155 static bool vhost_user_has_ufo(NetClientState *nc)
156 {
157     assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
158
159     return true;
160 }
161
162 static NetClientInfo net_vhost_user_info = {
163         .type = NET_CLIENT_OPTIONS_KIND_VHOST_USER,
164         .size = sizeof(VhostUserState),
165         .receive = vhost_user_receive,
166         .cleanup = vhost_user_cleanup,
167         .has_vnet_hdr = vhost_user_has_vnet_hdr,
168         .has_ufo = vhost_user_has_ufo,
169 };
170
171 static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond,
172                                            void *opaque)
173 {
174     VhostUserState *s = opaque;
175     uint8_t buf[1];
176
177     /* We don't actually want to read anything, but CHR_EVENT_CLOSED will be
178      * raised as a side-effect of the read.
179      */
180     qemu_chr_fe_read_all(s->chr, buf, sizeof(buf));
181
182     return FALSE;
183 }
184
185 static void net_vhost_user_event(void *opaque, int event)
186 {
187     const char *name = opaque;
188     NetClientState *ncs[MAX_QUEUE_NUM];
189     VhostUserState *s;
190     Error *err = NULL;
191     int queues;
192
193     queues = qemu_find_net_clients_except(name, ncs,
194                                           NET_CLIENT_OPTIONS_KIND_NIC,
195                                           MAX_QUEUE_NUM);
196     assert(queues < MAX_QUEUE_NUM);
197
198     s = DO_UPCAST(VhostUserState, nc, ncs[0]);
199     trace_vhost_user_event(s->chr->label, event);
200     switch (event) {
201     case CHR_EVENT_OPENED:
202         s->watch = qemu_chr_fe_add_watch(s->chr, G_IO_HUP,
203                                          net_vhost_user_watch, s);
204         if (vhost_user_start(queues, ncs) < 0) {
205             qemu_chr_disconnect(s->chr);
206             return;
207         }
208         qmp_set_link(name, true, &err);
209         break;
210     case CHR_EVENT_CLOSED:
211         qmp_set_link(name, false, &err);
212         vhost_user_stop(queues, ncs);
213         g_source_remove(s->watch);
214         s->watch = 0;
215         break;
216     }
217
218     if (err) {
219         error_report_err(err);
220     }
221 }
222
223 static int net_vhost_user_init(NetClientState *peer, const char *device,
224                                const char *name, CharDriverState *chr,
225                                int queues)
226 {
227     NetClientState *nc;
228     VhostUserState *s;
229     int i;
230
231     assert(name);
232     assert(queues > 0);
233
234     for (i = 0; i < queues; i++) {
235         nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
236
237         snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s",
238                  i, chr->label);
239
240         nc->queue_index = i;
241
242         s = DO_UPCAST(VhostUserState, nc, nc);
243         s->chr = chr;
244     }
245
246     qemu_chr_add_handlers(chr, NULL, NULL, net_vhost_user_event, nc[0].name);
247
248     return 0;
249 }
250
251 static int net_vhost_chardev_opts(void *opaque,
252                                   const char *name, const char *value,
253                                   Error **errp)
254 {
255     VhostUserChardevProps *props = opaque;
256
257     if (strcmp(name, "backend") == 0 && strcmp(value, "socket") == 0) {
258         props->is_socket = true;
259     } else if (strcmp(name, "path") == 0) {
260         props->is_unix = true;
261     } else if (strcmp(name, "server") == 0) {
262     } else {
263         error_setg(errp,
264                    "vhost-user does not support a chardev with option %s=%s",
265                    name, value);
266         return -1;
267     }
268     return 0;
269 }
270
271 static CharDriverState *net_vhost_parse_chardev(
272     const NetdevVhostUserOptions *opts, Error **errp)
273 {
274     CharDriverState *chr = qemu_chr_find(opts->chardev);
275     VhostUserChardevProps props;
276
277     if (chr == NULL) {
278         error_setg(errp, "chardev \"%s\" not found", opts->chardev);
279         return NULL;
280     }
281
282     /* inspect chardev opts */
283     memset(&props, 0, sizeof(props));
284     if (qemu_opt_foreach(chr->opts, net_vhost_chardev_opts, &props, errp)) {
285         return NULL;
286     }
287
288     if (!props.is_socket || !props.is_unix) {
289         error_setg(errp, "chardev \"%s\" is not a unix socket",
290                    opts->chardev);
291         return NULL;
292     }
293
294     qemu_chr_fe_claim_no_fail(chr);
295
296     return chr;
297 }
298
299 static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
300 {
301     const char *name = opaque;
302     const char *driver, *netdev;
303     const char virtio_name[] = "virtio-net-";
304
305     driver = qemu_opt_get(opts, "driver");
306     netdev = qemu_opt_get(opts, "netdev");
307
308     if (!driver || !netdev) {
309         return 0;
310     }
311
312     if (strcmp(netdev, name) == 0 &&
313         strncmp(driver, virtio_name, strlen(virtio_name)) != 0) {
314         error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
315         return -1;
316     }
317
318     return 0;
319 }
320
321 int net_init_vhost_user(const NetClientOptions *opts, const char *name,
322                         NetClientState *peer, Error **errp)
323 {
324     int queues;
325     const NetdevVhostUserOptions *vhost_user_opts;
326     CharDriverState *chr;
327
328     assert(opts->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
329     vhost_user_opts = opts->u.vhost_user.data;
330
331     chr = net_vhost_parse_chardev(vhost_user_opts, errp);
332     if (!chr) {
333         return -1;
334     }
335
336     /* verify net frontend */
337     if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net,
338                           (char *)name, errp)) {
339         return -1;
340     }
341
342     queues = vhost_user_opts->has_queues ? vhost_user_opts->queues : 1;
343     if (queues < 1 || queues > MAX_QUEUE_NUM) {
344         error_setg(errp,
345                    "vhost-user number of queues must be in range [1, %d]",
346                    MAX_QUEUE_NUM);
347         return -1;
348     }
349
350     return net_vhost_user_init(peer, "vhost_user", name, chr, queues);
351 }
This page took 0.042713 seconds and 4 git commands to generate.