mips: Set the CP0.Config3.DSP and CP0.Config3.DSP2P bits
[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 "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"
17
18 typedef struct VhostUserState {
19 NetClientState nc;
20 CharDriverState *chr;
21 bool vhostforce;
22 VHostNetState *vhost_net;
23 } VhostUserState;
24
25 typedef struct VhostUserChardevProps {
26 bool is_socket;
27 bool is_unix;
28 bool is_server;
29 } VhostUserChardevProps;
30
31 VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
32 {
33 VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
34 assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
35 return s->vhost_net;
36 }
37
38 static int vhost_user_running(VhostUserState *s)
39 {
40 return (s->vhost_net) ? 1 : 0;
41 }
42
43 static int vhost_user_start(VhostUserState *s)
44 {
45 VhostNetOptions options;
46
47 if (vhost_user_running(s)) {
48 return 0;
49 }
50
51 options.backend_type = VHOST_BACKEND_TYPE_USER;
52 options.net_backend = &s->nc;
53 options.opaque = s->chr;
54 options.force = s->vhostforce;
55
56 s->vhost_net = vhost_net_init(&options);
57
58 return vhost_user_running(s) ? 0 : -1;
59 }
60
61 static void vhost_user_stop(VhostUserState *s)
62 {
63 if (vhost_user_running(s)) {
64 vhost_net_cleanup(s->vhost_net);
65 }
66
67 s->vhost_net = 0;
68 }
69
70 static void vhost_user_cleanup(NetClientState *nc)
71 {
72 VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
73
74 vhost_user_stop(s);
75 qemu_purge_queued_packets(nc);
76 }
77
78 static bool vhost_user_has_vnet_hdr(NetClientState *nc)
79 {
80 assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
81
82 return true;
83 }
84
85 static bool vhost_user_has_ufo(NetClientState *nc)
86 {
87 assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
88
89 return true;
90 }
91
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,
98 };
99
100 static void net_vhost_link_down(VhostUserState *s, bool link_down)
101 {
102 s->nc.link_down = link_down;
103
104 if (s->nc.peer) {
105 s->nc.peer->link_down = link_down;
106 }
107
108 if (s->nc.info->link_status_changed) {
109 s->nc.info->link_status_changed(&s->nc);
110 }
111
112 if (s->nc.peer && s->nc.peer->info->link_status_changed) {
113 s->nc.peer->info->link_status_changed(s->nc.peer);
114 }
115 }
116
117 static void net_vhost_user_event(void *opaque, int event)
118 {
119 VhostUserState *s = opaque;
120
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;
132 }
133 }
134
135 static int net_vhost_user_init(NetClientState *peer, const char *device,
136 const char *name, CharDriverState *chr,
137 bool vhostforce)
138 {
139 NetClientState *nc;
140 VhostUserState *s;
141
142 nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
143
144 snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user to %s",
145 chr->label);
146
147 s = DO_UPCAST(VhostUserState, nc, nc);
148
149 /* We don't provide a receive callback */
150 s->nc.receive_disabled = 1;
151 s->chr = chr;
152 s->vhostforce = vhostforce;
153
154 qemu_chr_add_handlers(s->chr, NULL, NULL, net_vhost_user_event, s);
155
156 return 0;
157 }
158
159 static int net_vhost_chardev_opts(const char *name, const char *value,
160 void *opaque)
161 {
162 VhostUserChardevProps *props = opaque;
163
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;
175 }
176 return 0;
177 }
178
179 static CharDriverState *net_vhost_parse_chardev(const NetdevVhostUserOptions *opts)
180 {
181 CharDriverState *chr = qemu_chr_find(opts->chardev);
182 VhostUserChardevProps props;
183
184 if (chr == NULL) {
185 error_report("chardev \"%s\" not found", opts->chardev);
186 return NULL;
187 }
188
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;
193 }
194
195 if (!props.is_socket || !props.is_unix) {
196 error_report("chardev \"%s\" is not a unix socket",
197 opts->chardev);
198 return NULL;
199 }
200
201 qemu_chr_fe_claim_no_fail(chr);
202
203 return chr;
204 }
205
206 static int net_vhost_check_net(QemuOpts *opts, void *opaque)
207 {
208 const char *name = opaque;
209 const char *driver, *netdev;
210 const char virtio_name[] = "virtio-net-";
211
212 driver = qemu_opt_get(opts, "driver");
213 netdev = qemu_opt_get(opts, "netdev");
214
215 if (!driver || !netdev) {
216 return 0;
217 }
218
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;
223 }
224
225 return 0;
226 }
227
228 int net_init_vhost_user(const NetClientOptions *opts, const char *name,
229 NetClientState *peer)
230 {
231 const NetdevVhostUserOptions *vhost_user_opts;
232 CharDriverState *chr;
233 bool vhostforce;
234
235 assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
236 vhost_user_opts = opts->vhost_user;
237
238 chr = net_vhost_parse_chardev(vhost_user_opts);
239 if (!chr) {
240 error_report("No suitable chardev found");
241 return -1;
242 }
243
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;
248 }
249
250 /* vhostforce for non-MSIX */
251 if (vhost_user_opts->has_vhostforce) {
252 vhostforce = vhost_user_opts->vhostforce;
253 } else {
254 vhostforce = false;
255 }
256
257 return net_vhost_user_init(peer, "vhost_user", name, chr, vhostforce);
258 }