pc: acpi-build: generate pvpanic device description dynamically
[qemu/qmp-unstable.git] / net / vhost-user.c
blob24e050c772974a4ee69b68244012321823cba433
1 /*
2 * vhost-user.c
4 * Copyright (c) 2013 Virtual Open Systems Sarl.
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.
9 */
11 #include "clients.h"
12 #include "net/vhost_net.h"
13 #include "net/vhost-user.h"
14 #include "sysemu/char.h"
15 #include "qemu/config-file.h"
16 #include "qemu/error-report.h"
18 typedef struct VhostUserState {
19 NetClientState nc;
20 CharDriverState *chr;
21 bool vhostforce;
22 VHostNetState *vhost_net;
23 } VhostUserState;
25 typedef struct VhostUserChardevProps {
26 bool is_socket;
27 bool is_unix;
28 bool is_server;
29 } VhostUserChardevProps;
31 VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
33 VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
34 assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
35 return s->vhost_net;
38 static int vhost_user_running(VhostUserState *s)
40 return (s->vhost_net) ? 1 : 0;
43 static int vhost_user_start(VhostUserState *s)
45 VhostNetOptions options;
47 if (vhost_user_running(s)) {
48 return 0;
51 options.backend_type = VHOST_BACKEND_TYPE_USER;
52 options.net_backend = &s->nc;
53 options.opaque = s->chr;
54 options.force = s->vhostforce;
56 s->vhost_net = vhost_net_init(&options);
58 return vhost_user_running(s) ? 0 : -1;
61 static void vhost_user_stop(VhostUserState *s)
63 if (vhost_user_running(s)) {
64 vhost_net_cleanup(s->vhost_net);
67 s->vhost_net = 0;
70 static void vhost_user_cleanup(NetClientState *nc)
72 VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
74 vhost_user_stop(s);
75 qemu_purge_queued_packets(nc);
78 static bool vhost_user_has_vnet_hdr(NetClientState *nc)
80 assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
82 return true;
85 static bool vhost_user_has_ufo(NetClientState *nc)
87 assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
89 return true;
92 static NetClientInfo net_vhost_user_info = {
93 .type = NET_CLIENT_OPTIONS_KIND_VHOST_USER,
94 .size = sizeof(VhostUserState),
95 .cleanup = vhost_user_cleanup,
96 .has_vnet_hdr = vhost_user_has_vnet_hdr,
97 .has_ufo = vhost_user_has_ufo,
100 static void net_vhost_link_down(VhostUserState *s, bool link_down)
102 s->nc.link_down = link_down;
104 if (s->nc.peer) {
105 s->nc.peer->link_down = link_down;
108 if (s->nc.info->link_status_changed) {
109 s->nc.info->link_status_changed(&s->nc);
112 if (s->nc.peer && s->nc.peer->info->link_status_changed) {
113 s->nc.peer->info->link_status_changed(s->nc.peer);
117 static void net_vhost_user_event(void *opaque, int event)
119 VhostUserState *s = opaque;
121 switch (event) {
122 case CHR_EVENT_OPENED:
123 vhost_user_start(s);
124 net_vhost_link_down(s, false);
125 error_report("chardev \"%s\" went up\n", s->chr->label);
126 break;
127 case CHR_EVENT_CLOSED:
128 net_vhost_link_down(s, true);
129 vhost_user_stop(s);
130 error_report("chardev \"%s\" went down\n", s->chr->label);
131 break;
135 static int net_vhost_user_init(NetClientState *peer, const char *device,
136 const char *name, CharDriverState *chr,
137 bool vhostforce)
139 NetClientState *nc;
140 VhostUserState *s;
142 nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
144 snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user to %s",
145 chr->label);
147 s = DO_UPCAST(VhostUserState, nc, nc);
149 /* We don't provide a receive callback */
150 s->nc.receive_disabled = 1;
151 s->chr = chr;
152 s->vhostforce = vhostforce;
154 qemu_chr_add_handlers(s->chr, NULL, NULL, net_vhost_user_event, s);
156 return 0;
159 static int net_vhost_chardev_opts(const char *name, const char *value,
160 void *opaque)
162 VhostUserChardevProps *props = opaque;
164 if (strcmp(name, "backend") == 0 && strcmp(value, "socket") == 0) {
165 props->is_socket = true;
166 } else if (strcmp(name, "path") == 0) {
167 props->is_unix = true;
168 } else if (strcmp(name, "server") == 0) {
169 props->is_server = true;
170 } else {
171 error_report("vhost-user does not support a chardev"
172 " with the following option:\n %s = %s",
173 name, value);
174 return -1;
176 return 0;
179 static CharDriverState *net_vhost_parse_chardev(const NetdevVhostUserOptions *opts)
181 CharDriverState *chr = qemu_chr_find(opts->chardev);
182 VhostUserChardevProps props;
184 if (chr == NULL) {
185 error_report("chardev \"%s\" not found", opts->chardev);
186 return NULL;
189 /* inspect chardev opts */
190 memset(&props, 0, sizeof(props));
191 if (qemu_opt_foreach(chr->opts, net_vhost_chardev_opts, &props, true) != 0) {
192 return NULL;
195 if (!props.is_socket || !props.is_unix) {
196 error_report("chardev \"%s\" is not a unix socket",
197 opts->chardev);
198 return NULL;
201 qemu_chr_fe_claim_no_fail(chr);
203 return chr;
206 static int net_vhost_check_net(QemuOpts *opts, void *opaque)
208 const char *name = opaque;
209 const char *driver, *netdev;
210 const char virtio_name[] = "virtio-net-";
212 driver = qemu_opt_get(opts, "driver");
213 netdev = qemu_opt_get(opts, "netdev");
215 if (!driver || !netdev) {
216 return 0;
219 if (strcmp(netdev, name) == 0 &&
220 strncmp(driver, virtio_name, strlen(virtio_name)) != 0) {
221 error_report("vhost-user requires frontend driver virtio-net-*");
222 return -1;
225 return 0;
228 int net_init_vhost_user(const NetClientOptions *opts, const char *name,
229 NetClientState *peer)
231 const NetdevVhostUserOptions *vhost_user_opts;
232 CharDriverState *chr;
233 bool vhostforce;
235 assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
236 vhost_user_opts = opts->vhost_user;
238 chr = net_vhost_parse_chardev(vhost_user_opts);
239 if (!chr) {
240 error_report("No suitable chardev found");
241 return -1;
244 /* verify net frontend */
245 if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net,
246 (char *)name, true) == -1) {
247 return -1;
250 /* vhostforce for non-MSIX */
251 if (vhost_user_opts->has_vhostforce) {
252 vhostforce = vhost_user_opts->vhostforce;
253 } else {
254 vhostforce = false;
257 return net_vhost_user_init(peer, "vhost_user", name, chr, vhostforce);