]> git.proxmox.com Git - mirror_qemu.git/blame - net/vhost-user.c
libqos: Correct mask to align size to PAGE_SIZE in malloc-pc
[mirror_qemu.git] / net / vhost-user.c
CommitLineData
d314f586
NN
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"
03ce5744 15#include "qemu/config-file.h"
d314f586
NN
16#include "qemu/error-report.h"
17
18typedef struct VhostUserState {
19 NetClientState nc;
20 CharDriverState *chr;
21 bool vhostforce;
22 VHostNetState *vhost_net;
23} VhostUserState;
24
03ce5744
NN
25typedef struct VhostUserChardevProps {
26 bool is_socket;
27 bool is_unix;
28 bool is_server;
29} VhostUserChardevProps;
30
d314f586
NN
31VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
32{
33 VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
03ce5744 34 assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
d314f586
NN
35 return s->vhost_net;
36}
37
38static int vhost_user_running(VhostUserState *s)
39{
40 return (s->vhost_net) ? 1 : 0;
41}
42
43static 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
61static 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
70static 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
78static 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
85static 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
92static NetClientInfo net_vhost_user_info = {
03ce5744 93 .type = NET_CLIENT_OPTIONS_KIND_VHOST_USER,
d314f586
NN
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
100static 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
117static 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
135static 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
03ce5744
NN
159static 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
179static 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
206static 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
d314f586 228int net_init_vhost_user(const NetClientOptions *opts, const char *name,
03ce5744 229 NetClientState *peer)
d314f586 230{
03ce5744
NN
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);
d314f586 258}