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.
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 #include "qmp-commands.h"
20 typedef struct VhostUserState
{
23 VHostNetState
*vhost_net
;
26 typedef struct VhostUserChardevProps
{
30 } VhostUserChardevProps
;
32 VHostNetState
*vhost_user_get_vhost_net(NetClientState
*nc
)
34 VhostUserState
*s
= DO_UPCAST(VhostUserState
, nc
, nc
);
35 assert(nc
->info
->type
== NET_CLIENT_OPTIONS_KIND_VHOST_USER
);
39 static int vhost_user_running(VhostUserState
*s
)
41 return (s
->vhost_net
) ? 1 : 0;
44 static void vhost_user_stop(int queues
, NetClientState
*ncs
[])
49 for (i
= 0; i
< queues
; i
++) {
50 assert (ncs
[i
]->info
->type
== NET_CLIENT_OPTIONS_KIND_VHOST_USER
);
52 s
= DO_UPCAST(VhostUserState
, nc
, ncs
[i
]);
53 if (!vhost_user_running(s
)) {
58 vhost_net_cleanup(s
->vhost_net
);
64 static int vhost_user_start(int queues
, NetClientState
*ncs
[])
66 VhostNetOptions options
;
71 options
.backend_type
= VHOST_BACKEND_TYPE_USER
;
73 for (i
= 0; i
< queues
; i
++) {
74 assert (ncs
[i
]->info
->type
== NET_CLIENT_OPTIONS_KIND_VHOST_USER
);
76 s
= DO_UPCAST(VhostUserState
, nc
, ncs
[i
]);
77 if (vhost_user_running(s
)) {
81 options
.net_backend
= ncs
[i
];
82 options
.opaque
= s
->chr
;
83 s
->vhost_net
= vhost_net_init(&options
);
85 error_report("failed to init vhost_net for queue %d\n", i
);
90 max_queues
= vhost_net_get_max_queues(s
->vhost_net
);
91 if (queues
> max_queues
) {
92 error_report("you are asking more queues than "
93 "supported: %d\n", max_queues
);
102 vhost_user_stop(i
+ 1, ncs
);
106 static ssize_t
vhost_user_receive(NetClientState
*nc
, const uint8_t *buf
,
109 /* Discard the request that is received and managed by backend
115 static void vhost_user_cleanup(NetClientState
*nc
)
117 VhostUserState
*s
= DO_UPCAST(VhostUserState
, nc
, nc
);
120 vhost_net_cleanup(s
->vhost_net
);
124 qemu_purge_queued_packets(nc
);
127 static bool vhost_user_has_vnet_hdr(NetClientState
*nc
)
129 assert(nc
->info
->type
== NET_CLIENT_OPTIONS_KIND_VHOST_USER
);
134 static bool vhost_user_has_ufo(NetClientState
*nc
)
136 assert(nc
->info
->type
== NET_CLIENT_OPTIONS_KIND_VHOST_USER
);
141 static NetClientInfo net_vhost_user_info
= {
142 .type
= NET_CLIENT_OPTIONS_KIND_VHOST_USER
,
143 .size
= sizeof(VhostUserState
),
144 .receive
= vhost_user_receive
,
145 .cleanup
= vhost_user_cleanup
,
146 .has_vnet_hdr
= vhost_user_has_vnet_hdr
,
147 .has_ufo
= vhost_user_has_ufo
,
150 static void net_vhost_user_event(void *opaque
, int event
)
152 const char *name
= opaque
;
153 NetClientState
*ncs
[MAX_QUEUE_NUM
];
158 queues
= qemu_find_net_clients_except(name
, ncs
,
159 NET_CLIENT_OPTIONS_KIND_NIC
,
161 s
= DO_UPCAST(VhostUserState
, nc
, ncs
[0]);
162 trace_vhost_user_event(s
->chr
->label
, event
);
164 case CHR_EVENT_OPENED
:
165 if (vhost_user_start(queues
, ncs
) < 0) {
168 qmp_set_link(name
, true, &err
);
170 case CHR_EVENT_CLOSED
:
171 qmp_set_link(name
, true, &err
);
172 vhost_user_stop(queues
, ncs
);
177 error_report_err(err
);
181 static int net_vhost_user_init(NetClientState
*peer
, const char *device
,
182 const char *name
, CharDriverState
*chr
,
189 for (i
= 0; i
< queues
; i
++) {
190 nc
= qemu_new_net_client(&net_vhost_user_info
, peer
, device
, name
);
192 snprintf(nc
->info_str
, sizeof(nc
->info_str
), "vhost-user%d to %s",
197 s
= DO_UPCAST(VhostUserState
, nc
, nc
);
201 qemu_chr_add_handlers(chr
, NULL
, NULL
, net_vhost_user_event
, (void*)name
);
206 static int net_vhost_chardev_opts(void *opaque
,
207 const char *name
, const char *value
,
210 VhostUserChardevProps
*props
= opaque
;
212 if (strcmp(name
, "backend") == 0 && strcmp(value
, "socket") == 0) {
213 props
->is_socket
= true;
214 } else if (strcmp(name
, "path") == 0) {
215 props
->is_unix
= true;
216 } else if (strcmp(name
, "server") == 0) {
217 props
->is_server
= true;
220 "vhost-user does not support a chardev with option %s=%s",
227 static CharDriverState
*net_vhost_parse_chardev(
228 const NetdevVhostUserOptions
*opts
, Error
**errp
)
230 CharDriverState
*chr
= qemu_chr_find(opts
->chardev
);
231 VhostUserChardevProps props
;
234 error_setg(errp
, "chardev \"%s\" not found", opts
->chardev
);
238 /* inspect chardev opts */
239 memset(&props
, 0, sizeof(props
));
240 if (qemu_opt_foreach(chr
->opts
, net_vhost_chardev_opts
, &props
, errp
)) {
244 if (!props
.is_socket
|| !props
.is_unix
) {
245 error_setg(errp
, "chardev \"%s\" is not a unix socket",
250 qemu_chr_fe_claim_no_fail(chr
);
255 static int net_vhost_check_net(void *opaque
, QemuOpts
*opts
, Error
**errp
)
257 const char *name
= opaque
;
258 const char *driver
, *netdev
;
259 const char virtio_name
[] = "virtio-net-";
261 driver
= qemu_opt_get(opts
, "driver");
262 netdev
= qemu_opt_get(opts
, "netdev");
264 if (!driver
|| !netdev
) {
268 if (strcmp(netdev
, name
) == 0 &&
269 strncmp(driver
, virtio_name
, strlen(virtio_name
)) != 0) {
270 error_setg(errp
, "vhost-user requires frontend driver virtio-net-*");
277 int net_init_vhost_user(const NetClientOptions
*opts
, const char *name
,
278 NetClientState
*peer
, Error
**errp
)
281 const NetdevVhostUserOptions
*vhost_user_opts
;
282 CharDriverState
*chr
;
284 assert(opts
->kind
== NET_CLIENT_OPTIONS_KIND_VHOST_USER
);
285 vhost_user_opts
= opts
->vhost_user
;
287 chr
= net_vhost_parse_chardev(vhost_user_opts
, errp
);
292 /* verify net frontend */
293 if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net
,
294 (char *)name
, errp
)) {
298 queues
= vhost_user_opts
->has_queues
? vhost_user_opts
->queues
: 1;
300 return net_vhost_user_init(peer
, "vhost_user", name
, chr
, queues
);