diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 1e3e634b87..696474aea9 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -94,10 +94,10 @@ static void nbd_update_server_watch(NBDServerData *s) { if (s->listener) { if (!s->max_connections || s->connections < s->max_connections) { - qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, + qio_net_listener_set_client_aio_func(s->listener, nbd_accept, NULL, NULL); } else { - qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL); + qio_net_listener_set_client_aio_func(s->listener, NULL, NULL, NULL); } } } diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 62852e3caf..26d2f11202 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -1255,7 +1255,7 @@ static int qmp_chardev_open_socket_server(Chardev *chr, } qapi_free_SocketAddress(s->addr); - s->addr = socket_local_address(s->listener->sioc[0]->fd, errp); + s->addr = qio_net_listener_get_local_address(s->listener, 0, errp); skip_listen: update_disconnected_filename(s); diff --git a/include/io/channel-socket.h b/include/io/channel-socket.h index fcfd489c6c..a1ef3136ea 100644 --- a/include/io/channel-socket.h +++ b/include/io/channel-socket.h @@ -228,7 +228,7 @@ void qio_channel_socket_dgram_async(QIOChannelSocket *ioc, * release with a call qapi_free_SocketAddress() when no * longer required. * - * Returns: 0 on success, -1 on error + * Returns: the socket address struct, or NULL on error */ SocketAddress * qio_channel_socket_get_local_address(QIOChannelSocket *ioc, diff --git a/include/io/net-listener.h b/include/io/net-listener.h index ab9f291ed6..fdc5f1c81a 100644 --- a/include/io/net-listener.h +++ b/include/io/net-listener.h @@ -28,6 +28,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(QIONetListener, QIO_NET_LISTENER) +typedef struct QIONetListenerSource QIONetListenerSource; typedef void (*QIONetListenerClientFunc)(QIONetListener *listener, QIOChannelSocket *sioc, @@ -47,12 +48,15 @@ struct QIONetListener { Object parent; char *name; - QIOChannelSocket **sioc; - GSource **io_source; + QIONetListenerSource **source; size_t nsioc; + /* At most one of context or aio_context will be set */ + GMainContext *context; + AioContext *aio_context; bool connected; + QemuMutex lock; /* Protects remaining fields */ QIONetListenerClientFunc io_func; gpointer io_data; GDestroyNotify io_notify; @@ -148,6 +152,27 @@ void qio_net_listener_set_client_func(QIONetListener *listener, gpointer data, GDestroyNotify notify); +/** + * qio_net_listener_set_client_aio_func: + * @listener: the network listener object + * @func: the callback function + * @data: opaque data to pass to @func + * @context: AioContext that @func will be bound to; if NULL, this will + * will use qemu_get_aio_context(). + * + * Similar to qio_net_listener_set_client_func_full(), except that the polling + * will be done by an AioContext rather than a GMainContext. + * + * Because AioContext does not (yet) support a clean way to deregister + * a callback from one thread while another thread might be in that + * callback, this function is only safe to call from the thread + * currently associated with @context. + */ +void qio_net_listener_set_client_aio_func(QIONetListener *listener, + QIONetListenerClientFunc func, + void *data, + AioContext *context); + /** * qio_net_listener_wait_client: * @listener: the network listener object @@ -183,4 +208,46 @@ void qio_net_listener_disconnect(QIONetListener *listener); */ bool qio_net_listener_is_connected(QIONetListener *listener); + +/** + * qio_net_listener_nsioc: + * @listener: the network listener object + * + * Determine the number of listener channels currently owned by the + * given listener. + * + * Returns: number of channels, or 0 if not listening + */ +size_t qio_net_listener_nsioc(QIONetListener *listener); + + +/** + * qio_net_listener_sioc: + * @listener: the network listener object + * @n: index of the sioc to grab + * + * Accessor for the nth sioc owned by the listener. + * + * Returns: the requested listener, or NULL if not in bounds + */ +QIOChannelSocket *qio_net_listener_sioc(QIONetListener *listener, size_t n); + +/** + * qio_net_listener_get_local_address: + * @listener: the network listener object + * @n: index of the sioc to grab + * @errp: pointer to a NULL-initialized error object + * + * Get the string representation of the local socket + * address. A pointer to the allocated address information + * struct will be returned, which the caller is required to + * release with a call qapi_free_SocketAddress() when no + * longer required. + * + * Returns: the socket address struct, or NULL on error + */ +SocketAddress * +qio_net_listener_get_local_address(QIONetListener *listener, size_t n, + Error **errp); + #endif /* QIO_NET_LISTENER_H */ diff --git a/io/net-listener.c b/io/net-listener.c index 47405965a6..9410d72da9 100644 --- a/io/net-listener.c +++ b/io/net-listener.c @@ -23,10 +23,23 @@ #include "io/dns-resolver.h" #include "qapi/error.h" #include "qemu/module.h" +#include "qemu/lockable.h" +#include "qemu/main-loop.h" +#include "trace.h" + +struct QIONetListenerSource { + QIOChannelSocket *sioc; + GSource *io_source; + QIONetListener *listener; +}; QIONetListener *qio_net_listener_new(void) { - return QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER)); + QIONetListener *listener; + + listener = QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER)); + qemu_mutex_init(&listener->lock); + return listener; } void qio_net_listener_set_name(QIONetListener *listener, @@ -43,6 +56,10 @@ static gboolean qio_net_listener_channel_func(QIOChannel *ioc, { QIONetListener *listener = QIO_NET_LISTENER(opaque); QIOChannelSocket *sioc; + QIONetListenerClientFunc io_func; + gpointer io_data; + GMainContext *context; + AioContext *aio_context; sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), NULL); @@ -50,8 +67,16 @@ static gboolean qio_net_listener_channel_func(QIOChannel *ioc, return TRUE; } - if (listener->io_func) { - listener->io_func(listener, sioc, listener->io_data); + WITH_QEMU_LOCK_GUARD(&listener->lock) { + io_func = listener->io_func; + io_data = listener->io_data; + context = listener->context; + aio_context = listener->aio_context; + } + + trace_qio_net_listener_callback(listener, io_func, context, aio_context); + if (io_func) { + io_func(listener, sioc, io_data); } object_unref(OBJECT(sioc)); @@ -60,6 +85,17 @@ static gboolean qio_net_listener_channel_func(QIOChannel *ioc, } +static void qio_net_listener_aio_func(void *opaque) +{ + QIONetListenerSource *data = opaque; + + assert(data->io_source == NULL); + assert(data->listener->aio_context != NULL); + qio_net_listener_channel_func(QIO_CHANNEL(data->sioc), G_IO_IN, + data->listener); +} + + int qio_net_listener_open_sync(QIONetListener *listener, SocketAddress *addr, int num, @@ -104,6 +140,89 @@ int qio_net_listener_open_sync(QIONetListener *listener, } } +/* + * i == 0 to set watch on entire array, non-zero to only set watch on + * recent additions when earlier entries are already watched. + * + * listener->lock must be held by caller. + */ +static void +qio_net_listener_watch(QIONetListener *listener, size_t i, const char *caller) +{ + if (!listener->io_func) { + return; + } + + trace_qio_net_listener_watch(listener, listener->io_func, + listener->context, listener->aio_context, + caller); + for ( ; i < listener->nsioc; i++) { + if (!listener->aio_context) { + /* + * The user passed a GMainContext with the async callback; + * they plan on running the default or their own g_main_loop. + */ + object_ref(OBJECT(listener)); + listener->source[i]->io_source = qio_channel_add_watch_source( + QIO_CHANNEL(listener->source[i]->sioc), G_IO_IN, + qio_net_listener_channel_func, + listener, (GDestroyNotify)object_unref, listener->context); + } else { + /* + * The user passed an AioContext. At this point, + * AioContext lacks a clean way to call a notify function + * to release a final reference after any callback is + * complete. But we asserted earlier that the async + * callback is changed only from the thread associated + * with aio_context, which means no other thread is in the + * middle of running the callback when we are changing the + * refcount on listener here. Therefore, a single + * reference here is sufficient to ensure listener is not + * finalized during the callback. + */ + assert(listener->context == NULL); + if (i == 0) { + object_ref(OBJECT(listener)); + } + qio_channel_set_aio_fd_handler( + QIO_CHANNEL(listener->source[i]->sioc), + listener->aio_context, qio_net_listener_aio_func, + NULL, NULL, listener->source[i]); + } + } +} + +/* listener->lock must be held by caller. */ +static void +qio_net_listener_unwatch(QIONetListener *listener, const char *caller) +{ + size_t i; + + if (!listener->io_func) { + return; + } + + trace_qio_net_listener_unwatch(listener, listener->io_func, + listener->context, listener->aio_context, + caller); + for (i = 0; i < listener->nsioc; i++) { + if (!listener->aio_context) { + if (listener->source[i]->io_source) { + g_source_destroy(listener->source[i]->io_source); + g_source_unref(listener->source[i]->io_source); + listener->source[i]->io_source = NULL; + } + } else { + assert(listener->context == NULL); + qio_channel_set_aio_fd_handler( + QIO_CHANNEL(listener->source[i]->sioc), + listener->aio_context, NULL, NULL, NULL, NULL); + if (i == listener->nsioc - 1) { + object_unref(OBJECT(listener)); + } + } + } +} void qio_net_listener_add(QIONetListener *listener, QIOChannelSocket *sioc) @@ -112,61 +231,58 @@ void qio_net_listener_add(QIONetListener *listener, qio_channel_set_name(QIO_CHANNEL(sioc), listener->name); } - listener->sioc = g_renew(QIOChannelSocket *, listener->sioc, - listener->nsioc + 1); - listener->io_source = g_renew(typeof(listener->io_source[0]), - listener->io_source, - listener->nsioc + 1); - listener->sioc[listener->nsioc] = sioc; - listener->io_source[listener->nsioc] = NULL; + listener->source = g_renew(typeof(listener->source[0]), + listener->source, + listener->nsioc + 1); + listener->source[listener->nsioc] = g_new0(QIONetListenerSource, 1); + listener->source[listener->nsioc]->sioc = sioc; + listener->source[listener->nsioc]->listener = listener; object_ref(OBJECT(sioc)); listener->connected = true; - if (listener->io_func != NULL) { - object_ref(OBJECT(listener)); - listener->io_source[listener->nsioc] = qio_channel_add_watch_source( - QIO_CHANNEL(listener->sioc[listener->nsioc]), G_IO_IN, - qio_net_listener_channel_func, - listener, (GDestroyNotify)object_unref, NULL); - } - + QEMU_LOCK_GUARD(&listener->lock); listener->nsioc++; + qio_net_listener_watch(listener, listener->nsioc - 1, "add"); } +static void +qio_net_listener_set_client_func_internal(QIONetListener *listener, + QIONetListenerClientFunc func, + gpointer data, + GDestroyNotify notify, + GMainContext *context, + AioContext *aio_context) +{ + QEMU_LOCK_GUARD(&listener->lock); + if (listener->io_func == func && listener->io_data == data && + listener->io_notify == notify && listener->context == context && + listener->aio_context == aio_context) { + return; + } + + qio_net_listener_unwatch(listener, "set_client_func"); + if (listener->io_notify) { + listener->io_notify(listener->io_data); + } + listener->io_func = func; + listener->io_data = data; + listener->io_notify = notify; + listener->context = context; + listener->aio_context = aio_context; + + qio_net_listener_watch(listener, 0, "set_client_func"); +} + void qio_net_listener_set_client_func_full(QIONetListener *listener, QIONetListenerClientFunc func, gpointer data, GDestroyNotify notify, GMainContext *context) { - size_t i; - - if (listener->io_notify) { - listener->io_notify(listener->io_data); - } - listener->io_func = func; - listener->io_data = data; - listener->io_notify = notify; - - for (i = 0; i < listener->nsioc; i++) { - if (listener->io_source[i]) { - g_source_destroy(listener->io_source[i]); - g_source_unref(listener->io_source[i]); - listener->io_source[i] = NULL; - } - } - - if (listener->io_func != NULL) { - for (i = 0; i < listener->nsioc; i++) { - object_ref(OBJECT(listener)); - listener->io_source[i] = qio_channel_add_watch_source( - QIO_CHANNEL(listener->sioc[i]), G_IO_IN, - qio_net_listener_channel_func, - listener, (GDestroyNotify)object_unref, context); - } - } + qio_net_listener_set_client_func_internal(listener, func, data, + notify, context, NULL); } void qio_net_listener_set_client_func(QIONetListener *listener, @@ -174,8 +290,33 @@ void qio_net_listener_set_client_func(QIONetListener *listener, gpointer data, GDestroyNotify notify) { - qio_net_listener_set_client_func_full(listener, func, data, - notify, NULL); + qio_net_listener_set_client_func_internal(listener, func, data, + notify, NULL, NULL); +} + +void qio_net_listener_set_client_aio_func(QIONetListener *listener, + QIONetListenerClientFunc func, + void *data, + AioContext *context) +{ + if (!context) { + assert(qemu_in_main_thread()); + context = qemu_get_aio_context(); + } else { + /* + * TODO: The API was intentionally designed to allow a caller + * to pass an alternative AioContext for future expansion; + * however, actually implementating that is not possible + * without notify callbacks wired into AioContext similar to + * how they work in GSource. So for now, this code hard-codes + * the knowledge that the only client needing AioContext is + * the NBD server, which uses the global context and does not + * suffer from cross-thread safety issues. + */ + g_assert_not_reached(); + } + qio_net_listener_set_client_func_internal(listener, func, data, + NULL, NULL, context); } struct QIONetListenerClientWaitData { @@ -218,18 +359,14 @@ QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener) }; size_t i; - for (i = 0; i < listener->nsioc; i++) { - if (listener->io_source[i]) { - g_source_destroy(listener->io_source[i]); - g_source_unref(listener->io_source[i]); - listener->io_source[i] = NULL; - } + WITH_QEMU_LOCK_GUARD(&listener->lock) { + qio_net_listener_unwatch(listener, "wait_client"); } sources = g_new0(GSource *, listener->nsioc); for (i = 0; i < listener->nsioc; i++) { - sources[i] = qio_channel_create_watch(QIO_CHANNEL(listener->sioc[i]), - G_IO_IN); + sources[i] = qio_channel_create_watch( + QIO_CHANNEL(listener->source[i]->sioc), G_IO_IN); g_source_set_callback(sources[i], (GSourceFunc)qio_net_listener_wait_client_func, @@ -247,14 +384,8 @@ QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener) g_main_loop_unref(loop); g_main_context_unref(ctxt); - if (listener->io_func != NULL) { - for (i = 0; i < listener->nsioc; i++) { - object_ref(OBJECT(listener)); - listener->io_source[i] = qio_channel_add_watch_source( - QIO_CHANNEL(listener->sioc[i]), G_IO_IN, - qio_net_listener_channel_func, - listener, (GDestroyNotify)object_unref, NULL); - } + WITH_QEMU_LOCK_GUARD(&listener->lock) { + qio_net_listener_watch(listener, 0, "wait_client"); } return data.sioc; @@ -268,13 +399,10 @@ void qio_net_listener_disconnect(QIONetListener *listener) return; } + QEMU_LOCK_GUARD(&listener->lock); + qio_net_listener_unwatch(listener, "disconnect"); for (i = 0; i < listener->nsioc; i++) { - if (listener->io_source[i]) { - g_source_destroy(listener->io_source[i]); - g_source_unref(listener->io_source[i]); - listener->io_source[i] = NULL; - } - qio_channel_close(QIO_CHANNEL(listener->sioc[i]), NULL); + qio_channel_close(QIO_CHANNEL(listener->source[i]->sioc), NULL); } listener->connected = false; } @@ -285,22 +413,50 @@ bool qio_net_listener_is_connected(QIONetListener *listener) return listener->connected; } +size_t qio_net_listener_nsioc(QIONetListener *listener) +{ + return listener->nsioc; +} + +QIOChannelSocket *qio_net_listener_sioc(QIONetListener *listener, size_t n) +{ + if (n >= listener->nsioc) { + return NULL; + } + return listener->source[n]->sioc; +} + +SocketAddress * +qio_net_listener_get_local_address(QIONetListener *listener, size_t n, + Error **errp) +{ + QIOChannelSocket *sioc = qio_net_listener_sioc(listener, n); + + if (!sioc) { + error_setg(errp, "Listener index out of range"); + return NULL; + } + + return qio_channel_socket_get_local_address(sioc, errp); +} + static void qio_net_listener_finalize(Object *obj) { QIONetListener *listener = QIO_NET_LISTENER(obj); size_t i; + qio_net_listener_disconnect(listener); if (listener->io_notify) { listener->io_notify(listener->io_data); } - qio_net_listener_disconnect(listener); for (i = 0; i < listener->nsioc; i++) { - object_unref(OBJECT(listener->sioc[i])); + object_unref(OBJECT(listener->source[i]->sioc)); + g_free(listener->source[i]); } - g_free(listener->io_source); - g_free(listener->sioc); + g_free(listener->source); g_free(listener->name); + qemu_mutex_destroy(&listener->lock); } static const TypeInfo qio_net_listener_info = { diff --git a/io/trace-events b/io/trace-events index dc3a63ba1f..ec91453335 100644 --- a/io/trace-events +++ b/io/trace-events @@ -72,3 +72,8 @@ qio_channel_command_new_pid(void *ioc, int writefd, int readfd, int pid) "Comman qio_channel_command_new_spawn(void *ioc, const char *binary, int flags) "Command new spawn ioc=%p binary=%s flags=%d" qio_channel_command_abort(void *ioc, int pid) "Command abort ioc=%p pid=%d" qio_channel_command_wait(void *ioc, int pid, int ret, int status) "Command abort ioc=%p pid=%d ret=%d status=%d" + +# net-listener.c +qio_net_listener_watch(void *listener, void *func, void *gctx, void *actx, const char *extra) "Net listener=%p watch enabled func=%p ctx=%p/%p by %s" +qio_net_listener_unwatch(void *listener, void *func, void *gctx, void *actx, const char *extra) "Net listener=%p watch disabled func=%p ctx=%p/%p by %s" +qio_net_listener_callback(void *listener, void *func, void *gctx, void *actx) "Net listener=%p callback forwarding to func=%p ctx=%p/%p" diff --git a/migration/socket.c b/migration/socket.c index 5ec65b8c03..9e379bf56f 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -170,9 +170,9 @@ void socket_start_incoming_migration(SocketAddress *saddr, NULL, NULL, g_main_context_get_thread_default()); - for (i = 0; i < listener->nsioc; i++) { + for (i = 0; i < qio_net_listener_nsioc(listener); i++) { SocketAddress *address = - qio_channel_socket_get_local_address(listener->sioc[i], errp); + qio_net_listener_get_local_address(listener, i, errp); if (!address) { return; } diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 index 021169b4a1..10be2bd845 100755 --- a/tests/qemu-iotests/024 +++ b/tests/qemu-iotests/024 @@ -359,7 +359,7 @@ $QEMU_IO "$OVERLAY" -c "read -P 0x00 0 1M" | _filter_qemu_io $QEMU_IO "$OVERLAY" -c "read -P 0xff 1M 2M" | _filter_qemu_io $QEMU_IO "$OVERLAY" -c "read -P 0x00 3M 1M" | _filter_qemu_io -$QEMU_IMG map "$OVERLAY" | _filter_qemu_img_map +$QEMU_IO -c map "$OVERLAY" | _filter_qemu_io echo diff --git a/tests/qemu-iotests/024.out b/tests/qemu-iotests/024.out index 1b7522ba71..da8fedc08b 100644 --- a/tests/qemu-iotests/024.out +++ b/tests/qemu-iotests/024.out @@ -266,7 +266,6 @@ read 2097152/2097152 bytes at offset 1048576 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 1048576/1048576 bytes at offset 3145728 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Offset Length File -0 0x400000 TEST_DIR/subdir/t.IMGFMT +4 MiB (0x400000) bytes allocated at offset 0 bytes (0x0) *** done diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 index 41dcf3ff55..ceab990e64 100755 --- a/tests/qemu-iotests/207 +++ b/tests/qemu-iotests/207 @@ -119,7 +119,7 @@ with iotests.FilePath('t.img') as disk_path, \ iotests.img_info_log(remote_path) keys = subprocess.check_output( - 'ssh-keyscan 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' + + 'ssh-keyscan 127.0.0.1 2>/dev/null | grep -v "^#" | ' + 'cut -d" " -f3', shell=True).rstrip().decode('ascii').split('\n') diff --git a/tests/qemu-iotests/tests/nbd-in-qcow2-chain b/tests/qemu-iotests/tests/nbd-in-qcow2-chain new file mode 100755 index 0000000000..455ddfa86f --- /dev/null +++ b/tests/qemu-iotests/tests/nbd-in-qcow2-chain @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Test of opening both server and client NBD in a qcow2 backing chain +# +# Copyright (C) Red Hat, Inc. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +# creator +owner=eblake@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_qemu + _cleanup_test_img + rm -f "$SOCK_DIR/nbd" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter +. ./common.qemu +. ./common.nbd + +_supported_fmt qcow2 # Hardcoded to qcow2 command line and QMP below +_supported_proto file + +size=100M + +echo +echo "=== Preparing base image ===" + +TEST_IMG="$TEST_IMG.base" _make_test_img $size + +echo +echo "=== Starting QEMU and exposing base image ===" + +_launch_qemu -machine q35 +h1=$QEMU_HANDLE +_send_qemu_cmd $QEMU_HANDLE '{"execute": "qmp_capabilities"}' 'return' +_send_qemu_cmd $QEMU_HANDLE '{"execute": "blockdev-add", + "arguments": {"node-name":"base", "driver":"qcow2", + "file":{"driver":"file", "filename":"'"$TEST_IMG.base"'"}}}' 'return' +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", + "arguments": {"addr":{"type":"unix", + "data":{"path":"'"$SOCK_DIR/nbd"'"}}}}' 'return' +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments": {"device":"base","name":"base"}}' 'return' + +echo +echo "=== Adding explicit NBD client ===" + +_send_qemu_cmd $QEMU_HANDLE '{"execute": "blockdev-add", + "arguments": {"node-name":"client", "driver":"nbd", + "read-only":true, "export":"base", + "server":{"type":"unix", "path":"'"$SOCK_DIR/nbd"'"}}}' 'return' +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments": {"device":"client","name":"client"}}' 'return' + +echo +echo "=== Creating wrapper image ===" + +_make_test_img -F raw -b "nbd+unix:///base?socket=$SOCK_DIR/nbd" $size + +echo +echo "=== Adding wrapper image with implicit client from qcow2 file ===" + +_send_qemu_cmd $QEMU_HANDLE '{"execute": "blockdev-add", + "arguments": {"node-name":"wrap", "driver":"qcow2", + "file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' 'return' +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments": {"device":"wrap","name":"wrap"}}' 'return' + +echo +echo "=== Checking NBD server ===" + +$QEMU_NBD --list -k $SOCK_DIR/nbd + +echo +echo "=== Cleaning up ===" + +_send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' '' + +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/nbd-in-qcow2-chain.out b/tests/qemu-iotests/tests/nbd-in-qcow2-chain.out new file mode 100644 index 0000000000..b65cdaa4f2 --- /dev/null +++ b/tests/qemu-iotests/tests/nbd-in-qcow2-chain.out @@ -0,0 +1,75 @@ +QA output created by nbd-in-qcow2-chain + +=== Preparing base image === +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=104857600 + +=== Starting QEMU and exposing base image === +{"execute": "qmp_capabilities"} +{"return": {}} +{"execute": "blockdev-add", + "arguments": {"node-name":"base", "driver":"IMGFMT", + "file":{"driver":"file", "filename":"TEST_DIR/t.IMGFMT.base"}}} +{"return": {}} +{"execute":"nbd-server-start", + "arguments": {"addr":{"type":"unix", + "data":{"path":"SOCK_DIR/nbd"}}}} +{"return": {}} +{"execute":"nbd-server-add", + "arguments": {"device":"base","name":"base"}} +{"return": {}} + +=== Adding explicit NBD client === +{"execute": "blockdev-add", + "arguments": {"node-name":"client", "driver":"nbd", + "read-only":true, "export":"base", + "server":{"type":"unix", "path":"SOCK_DIR/nbd"}}} +{"return": {}} +{"execute":"nbd-server-add", + "arguments": {"device":"client","name":"client"}} +{"return": {}} + +=== Creating wrapper image === +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file=nbd+unix:///base?socket=SOCK_DIR/nbd backing_fmt=raw + +=== Adding wrapper image with implicit client from qcow2 file === +{"execute": "blockdev-add", + "arguments": {"node-name":"wrap", "driver":"IMGFMT", + "file":{"driver":"file", "filename":"TEST_DIR/t.IMGFMT"}}} +{"return": {}} +{"execute":"nbd-server-add", + "arguments": {"device":"wrap","name":"wrap"}} +{"return": {}} + +=== Checking NBD server === +exports available: 3 + export: 'base' + size: 104857600 + flags: 0x158f ( readonly flush fua df multi cache block-status-payload ) + min block: 1 + opt block: 4096 + max block: 33554432 + transaction size: 64-bit + available meta contexts: 1 + base:allocation + export: 'client' + size: 104857600 + flags: 0x158f ( readonly flush fua df multi cache block-status-payload ) + min block: 1 + opt block: 4096 + max block: 33554432 + transaction size: 64-bit + available meta contexts: 1 + base:allocation + export: 'wrap' + size: 104857600 + flags: 0x158f ( readonly flush fua df multi cache block-status-payload ) + min block: 1 + opt block: 4096 + max block: 33554432 + transaction size: 64-bit + available meta contexts: 1 + base:allocation + +=== Cleaning up === +{"execute":"quit"} +*** done diff --git a/tests/qemu-iotests/tests/vvfat.out b/tests/qemu-iotests/tests/vvfat.out old mode 100755 new mode 100644 diff --git a/ui/vnc.c b/ui/vnc.c index 50016ff7ab..0d499b208b 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -235,12 +235,12 @@ static VncServerInfo *vnc_server_info_get(VncDisplay *vd) VncServerInfo *info; Error *err = NULL; - if (!vd->listener || !vd->listener->nsioc) { + if (!vd->listener || !qio_net_listener_nsioc(vd->listener)) { return NULL; } info = g_malloc0(sizeof(*info)); - vnc_init_basic_info_from_server_addr(vd->listener->sioc[0], + vnc_init_basic_info_from_server_addr(qio_net_listener_sioc(vd->listener, 0), qapi_VncServerInfo_base(info), &err); info->auth = g_strdup(vnc_auth_name(vd)); if (err) { @@ -377,7 +377,7 @@ VncInfo *qmp_query_vnc(Error **errp) VncDisplay *vd = vnc_display_find(NULL); SocketAddress *addr = NULL; - if (vd == NULL || !vd->listener || !vd->listener->nsioc) { + if (vd == NULL || !vd->listener || !qio_net_listener_nsioc(vd->listener)) { info->enabled = false; } else { info->enabled = true; @@ -386,8 +386,7 @@ VncInfo *qmp_query_vnc(Error **errp) info->has_clients = true; info->clients = qmp_query_client_list(vd); - addr = qio_channel_socket_get_local_address(vd->listener->sioc[0], - errp); + addr = qio_net_listener_get_local_address(vd->listener, 0, errp); if (!addr) { goto out_error; } @@ -549,6 +548,8 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp) size_t i; QTAILQ_FOREACH(vd, &vnc_displays, next) { + size_t nsioc = 0; + info = g_new0(VncInfo2, 1); info->id = g_strdup(vd->id); info->clients = qmp_query_client_list(vd); @@ -559,14 +560,21 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp) "device", &error_abort)); info->display = g_strdup(dev->id); } - for (i = 0; vd->listener != NULL && i < vd->listener->nsioc; i++) { - info->server = qmp_query_server_entry( - vd->listener->sioc[i], false, vd->auth, vd->subauth, - info->server); + if (vd->listener != NULL) { + nsioc = qio_net_listener_nsioc(vd->listener); } - for (i = 0; vd->wslistener != NULL && i < vd->wslistener->nsioc; i++) { + for (i = 0; i < nsioc; i++) { info->server = qmp_query_server_entry( - vd->wslistener->sioc[i], true, vd->ws_auth, + qio_net_listener_sioc(vd->listener, i), false, vd->auth, + vd->subauth, info->server); + } + nsioc = 0; + if (vd->wslistener) { + nsioc = qio_net_listener_nsioc(vd->wslistener); + } + for (i = 0; i < nsioc; i++) { + info->server = qmp_query_server_entry( + qio_net_listener_sioc(vd->wslistener, i), true, vd->ws_auth, vd->ws_subauth, info->server); } @@ -3550,11 +3558,11 @@ static void vnc_display_print_local_addr(VncDisplay *vd) { SocketAddress *addr; - if (!vd->listener || !vd->listener->nsioc) { + if (!vd->listener || !qio_net_listener_nsioc(vd->listener)) { return; } - addr = qio_channel_socket_get_local_address(vd->listener->sioc[0], NULL); + addr = qio_net_listener_get_local_address(vd->listener, 0, NULL); if (!addr) { return; }