]>
git.proxmox.com Git - mirror_qemu.git/blob - util/vhost-user-server.c
2 * Sharing QEMU devices via vhost-user protocol
4 * Copyright (c) Coiby Xu <coiby.xu@gmail.com>.
5 * Copyright (c) 2020 Red Hat, Inc.
7 * This work is licensed under the terms of the GNU GPL, version 2 or
8 * later. See the COPYING file in the top-level directory.
10 #include "qemu/osdep.h"
11 #include "qemu/main-loop.h"
12 #include "vhost-user-server.h"
14 static void vmsg_close_fds(VhostUserMsg
*vmsg
)
17 for (i
= 0; i
< vmsg
->fd_num
; i
++) {
22 static void vmsg_unblock_fds(VhostUserMsg
*vmsg
)
25 for (i
= 0; i
< vmsg
->fd_num
; i
++) {
26 qemu_set_nonblock(vmsg
->fds
[i
]);
30 static void vu_accept(QIONetListener
*listener
, QIOChannelSocket
*sioc
,
33 static void close_client(VuServer
*server
)
36 * Before closing the client
38 * 1. Let vu_client_trip stop processing new vhost-user msg
40 * 2. remove kick_handler
42 * 3. wait for the kick handler to be finished
44 * 4. wait for the current vhost-user msg to be finished processing
47 QIOChannelSocket
*sioc
= server
->sioc
;
48 /* When this is set vu_client_trip will stop new processing vhost-user message */
51 VuFdWatch
*vu_fd_watch
, *next
;
52 QTAILQ_FOREACH_SAFE(vu_fd_watch
, &server
->vu_fd_watches
, next
, next
) {
53 aio_set_fd_handler(server
->ioc
->ctx
, vu_fd_watch
->fd
, true, NULL
,
57 while (!QTAILQ_EMPTY(&server
->vu_fd_watches
)) {
58 QTAILQ_FOREACH_SAFE(vu_fd_watch
, &server
->vu_fd_watches
, next
, next
) {
59 if (!vu_fd_watch
->processing
) {
60 QTAILQ_REMOVE(&server
->vu_fd_watches
, vu_fd_watch
, next
);
66 while (server
->processing_msg
) {
67 if (server
->ioc
->read_coroutine
) {
68 server
->ioc
->read_coroutine
= NULL
;
69 qio_channel_set_aio_fd_handler(server
->ioc
, server
->ioc
->ctx
, NULL
,
71 server
->processing_msg
= false;
75 vu_deinit(&server
->vu_dev
);
76 object_unref(OBJECT(sioc
));
77 object_unref(OBJECT(server
->ioc
));
80 static void panic_cb(VuDev
*vu_dev
, const char *buf
)
82 VuServer
*server
= container_of(vu_dev
, VuServer
, vu_dev
);
84 /* avoid while loop in close_client */
85 server
->processing_msg
= false;
88 error_report("vu_panic: %s", buf
);
95 if (server
->device_panic_notifier
) {
96 server
->device_panic_notifier(server
);
100 * Set the callback function for network listener so another
101 * vhost-user client can connect to this server
103 qio_net_listener_set_client_func(server
->listener
,
109 static bool coroutine_fn
110 vu_message_read(VuDev
*vu_dev
, int conn_fd
, VhostUserMsg
*vmsg
)
113 .iov_base
= (char *)vmsg
,
114 .iov_len
= VHOST_USER_HDR_SIZE
,
116 int rc
, read_bytes
= 0;
117 Error
*local_err
= NULL
;
119 * Store fds/nfds returned from qio_channel_readv_full into
120 * temporary variables.
122 * VhostUserMsg is a packed structure, gcc will complain about passing
123 * pointer to a packed structure member if we pass &VhostUserMsg.fd_num
124 * and &VhostUserMsg.fds directly when calling qio_channel_readv_full,
125 * thus two temporary variables nfds and fds are used here.
127 size_t nfds
= 0, nfds_t
= 0;
128 const size_t max_fds
= G_N_ELEMENTS(vmsg
->fds
);
130 VuServer
*server
= container_of(vu_dev
, VuServer
, vu_dev
);
131 QIOChannel
*ioc
= server
->ioc
;
134 error_report_err(local_err
);
138 assert(qemu_in_coroutine());
141 * qio_channel_readv_full may have short reads, keeping calling it
142 * until getting VHOST_USER_HDR_SIZE or 0 bytes in total
144 rc
= qio_channel_readv_full(ioc
, &iov
, 1, &fds_t
, &nfds_t
, &local_err
);
146 if (rc
== QIO_CHANNEL_ERR_BLOCK
) {
147 qio_channel_yield(ioc
, G_IO_IN
);
150 error_report_err(local_err
);
156 if (nfds
+ nfds_t
> max_fds
) {
157 error_report("A maximum of %zu fds are allowed, "
158 "however got %zu fds now",
159 max_fds
, nfds
+ nfds_t
);
162 memcpy(vmsg
->fds
+ nfds
, fds_t
,
163 nfds_t
*sizeof(vmsg
->fds
[0]));
167 if (read_bytes
== VHOST_USER_HDR_SIZE
|| rc
== 0) {
170 iov
.iov_base
= (char *)vmsg
+ read_bytes
;
171 iov
.iov_len
= VHOST_USER_HDR_SIZE
- read_bytes
;
175 /* qio_channel_readv_full will make socket fds blocking, unblock them */
176 vmsg_unblock_fds(vmsg
);
177 if (vmsg
->size
> sizeof(vmsg
->payload
)) {
178 error_report("Error: too big message request: %d, "
179 "size: vmsg->size: %u, "
180 "while sizeof(vmsg->payload) = %zu",
181 vmsg
->request
, vmsg
->size
, sizeof(vmsg
->payload
));
185 struct iovec iov_payload
= {
186 .iov_base
= (char *)&vmsg
->payload
,
187 .iov_len
= vmsg
->size
,
190 rc
= qio_channel_readv_all_eof(ioc
, &iov_payload
, 1, &local_err
);
192 error_report_err(local_err
);
200 vmsg_close_fds(vmsg
);
206 static void vu_client_start(VuServer
*server
);
207 static coroutine_fn
void vu_client_trip(void *opaque
)
209 VuServer
*server
= opaque
;
211 while (!server
->aio_context_changed
&& server
->sioc
) {
212 server
->processing_msg
= true;
213 vu_dispatch(&server
->vu_dev
);
214 server
->processing_msg
= false;
217 if (server
->aio_context_changed
&& server
->sioc
) {
218 server
->aio_context_changed
= false;
219 vu_client_start(server
);
223 static void vu_client_start(VuServer
*server
)
225 server
->co_trip
= qemu_coroutine_create(vu_client_trip
, server
);
226 aio_co_enter(server
->ctx
, server
->co_trip
);
230 * a wrapper for vu_kick_cb
232 * since aio_dispatch can only pass one user data pointer to the
233 * callback function, pack VuDev and pvt into a struct. Then unpack it
234 * and pass them to vu_kick_cb
236 static void kick_handler(void *opaque
)
238 VuFdWatch
*vu_fd_watch
= opaque
;
239 vu_fd_watch
->processing
= true;
240 vu_fd_watch
->cb(vu_fd_watch
->vu_dev
, 0, vu_fd_watch
->pvt
);
241 vu_fd_watch
->processing
= false;
245 static VuFdWatch
*find_vu_fd_watch(VuServer
*server
, int fd
)
248 VuFdWatch
*vu_fd_watch
, *next
;
249 QTAILQ_FOREACH_SAFE(vu_fd_watch
, &server
->vu_fd_watches
, next
, next
) {
250 if (vu_fd_watch
->fd
== fd
) {
258 set_watch(VuDev
*vu_dev
, int fd
, int vu_evt
,
259 vu_watch_cb cb
, void *pvt
)
262 VuServer
*server
= container_of(vu_dev
, VuServer
, vu_dev
);
267 VuFdWatch
*vu_fd_watch
= find_vu_fd_watch(server
, fd
);
270 VuFdWatch
*vu_fd_watch
= g_new0(VuFdWatch
, 1);
272 QTAILQ_INSERT_TAIL(&server
->vu_fd_watches
, vu_fd_watch
, next
);
274 vu_fd_watch
->fd
= fd
;
275 vu_fd_watch
->cb
= cb
;
276 qemu_set_nonblock(fd
);
277 aio_set_fd_handler(server
->ioc
->ctx
, fd
, true, kick_handler
,
278 NULL
, NULL
, vu_fd_watch
);
279 vu_fd_watch
->vu_dev
= vu_dev
;
280 vu_fd_watch
->pvt
= pvt
;
285 static void remove_watch(VuDev
*vu_dev
, int fd
)
291 server
= container_of(vu_dev
, VuServer
, vu_dev
);
293 VuFdWatch
*vu_fd_watch
= find_vu_fd_watch(server
, fd
);
298 aio_set_fd_handler(server
->ioc
->ctx
, fd
, true, NULL
, NULL
, NULL
, NULL
);
300 QTAILQ_REMOVE(&server
->vu_fd_watches
, vu_fd_watch
, next
);
305 static void vu_accept(QIONetListener
*listener
, QIOChannelSocket
*sioc
,
308 VuServer
*server
= opaque
;
311 warn_report("Only one vhost-user client is allowed to "
312 "connect the server one time");
316 if (!vu_init(&server
->vu_dev
, server
->max_queues
, sioc
->fd
, panic_cb
,
317 vu_message_read
, set_watch
, remove_watch
, server
->vu_iface
)) {
318 error_report("Failed to initialize libvhost-user");
323 * Unset the callback function for network listener to make another
324 * vhost-user client keeping waiting until this client disconnects
326 qio_net_listener_set_client_func(server
->listener
,
332 * Increase the object reference, so sioc will not freed by
333 * qio_net_listener_channel_func which will call object_unref(OBJECT(sioc))
335 object_ref(OBJECT(server
->sioc
));
336 qio_channel_set_name(QIO_CHANNEL(sioc
), "vhost-user client");
337 server
->ioc
= QIO_CHANNEL(sioc
);
338 object_ref(OBJECT(server
->ioc
));
339 qio_channel_attach_aio_context(server
->ioc
, server
->ctx
);
340 qio_channel_set_blocking(QIO_CHANNEL(server
->sioc
), false, NULL
);
341 vu_client_start(server
);
345 void vhost_user_server_stop(VuServer
*server
)
348 close_client(server
);
351 if (server
->listener
) {
352 qio_net_listener_disconnect(server
->listener
);
353 object_unref(OBJECT(server
->listener
));
358 void vhost_user_server_set_aio_context(VuServer
*server
, AioContext
*ctx
)
360 VuFdWatch
*vu_fd_watch
, *next
;
362 IOHandler
*io_read
= NULL
;
365 server
->ctx
= ctx
? ctx
: qemu_get_aio_context();
368 /* not yet serving any client*/
373 qio_channel_attach_aio_context(server
->ioc
, ctx
);
374 server
->aio_context_changed
= true;
375 io_read
= kick_handler
;
378 qio_channel_detach_aio_context(server
->ioc
);
379 /* server->ioc->ctx keeps the old AioConext */
380 ctx
= server
->ioc
->ctx
;
384 QTAILQ_FOREACH_SAFE(vu_fd_watch
, &server
->vu_fd_watches
, next
, next
) {
385 if (vu_fd_watch
->cb
) {
386 opaque
= attach
? vu_fd_watch
: NULL
;
387 aio_set_fd_handler(ctx
, vu_fd_watch
->fd
, true,
395 bool vhost_user_server_start(VuServer
*server
,
396 SocketAddress
*socket_addr
,
399 DevicePanicNotifierFn
*device_panic_notifier
,
400 const VuDevIface
*vu_iface
,
403 QIONetListener
*listener
= qio_net_listener_new();
404 if (qio_net_listener_open_sync(listener
, socket_addr
, 1,
406 object_unref(OBJECT(listener
));
410 /* zero out unspecified fields */
411 *server
= (VuServer
) {
412 .listener
= listener
,
413 .vu_iface
= vu_iface
,
414 .max_queues
= max_queues
,
416 .device_panic_notifier
= device_panic_notifier
,
419 qio_net_listener_set_name(server
->listener
, "vhost-user-backend-listener");
421 qio_net_listener_set_client_func(server
->listener
,
426 QTAILQ_INIT(&server
->vu_fd_watches
);