From f27206ceedbe2efae37c8d143c5eb2db05251508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:10 +0400 Subject: [PATCH 01/20] hw/audio/hda: free timer on exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 280c1e1cd ("audio/hda: create millisecond timers that handle IO") Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-2-marcandre.lureau@redhat.com> --- hw/audio/hda-codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index b40eec9604..74c0292284 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -751,7 +751,7 @@ static void hda_audio_exit(HDACodecDevice *hda) continue; } if (a->use_timer) { - timer_del(st->buft); + timer_free(st->buft); } if (st->output) { AUD_close_out(&a->card, st->voice.out); From 6d6e23361fc732e4fe36a8bc5873b85f264ed53a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:11 +0400 Subject: [PATCH 02/20] hw/audio/hda: fix memory leak on audio setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When SET_STREAM_FORMAT is called, we should clear the existing setup. Factor out common function to close a stream. Direct leak of 144 byte(s) in 3 object(s) allocated from: #0 0x7f91d38f7350 in calloc (/lib64/libasan.so.8+0xf7350) (BuildId: a4ad7eb954b390cf00f07fa10952988a41d9fc7a) #1 0x7f91d2ab7871 in g_malloc0 (/lib64/libglib-2.0.so.0+0x64871) (BuildId: 36b60dbd02e796145a982d0151ce37202ec05649) #2 0x562fa2f447ee in timer_new_full /home/elmarco/src/qemu/include/qemu/timer.h:538 #3 0x562fa2f4486f in timer_new /home/elmarco/src/qemu/include/qemu/timer.h:559 #4 0x562fa2f448a9 in timer_new_ns /home/elmarco/src/qemu/include/qemu/timer.h:577 #5 0x562fa2f47955 in hda_audio_setup ../hw/audio/hda-codec.c:490 #6 0x562fa2f4897e in hda_audio_command ../hw/audio/hda-codec.c:605 Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-3-marcandre.lureau@redhat.com> --- hw/audio/hda-codec.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 74c0292284..bc661504cf 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -472,6 +472,24 @@ static void hda_audio_set_amp(HDAAudioStream *st) } } +static void hda_close_stream(HDAAudioState *a, HDAAudioStream *st) +{ + if (st->node == NULL) { + return; + } + if (a->use_timer) { + timer_free(st->buft); + st->buft = NULL; + } + if (st->output) { + AUD_close_out(&a->card, st->voice.out); + st->voice.out = NULL; + } else { + AUD_close_in(&a->card, st->voice.in); + st->voice.in = NULL; + } +} + static void hda_audio_setup(HDAAudioStream *st) { bool use_timer = st->state->use_timer; @@ -484,6 +502,7 @@ static void hda_audio_setup(HDAAudioStream *st) trace_hda_audio_format(st->node->name, st->as.nchannels, fmt2name[st->as.fmt], st->as.freq); + hda_close_stream(st->state, st); if (st->output) { if (use_timer) { cb = hda_audio_output_cb; @@ -741,23 +760,11 @@ static void hda_audio_init(HDACodecDevice *hda, static void hda_audio_exit(HDACodecDevice *hda) { HDAAudioState *a = HDA_AUDIO(hda); - HDAAudioStream *st; int i; dprint(a, 1, "%s\n", __func__); for (i = 0; i < ARRAY_SIZE(a->st); i++) { - st = a->st + i; - if (st->node == NULL) { - continue; - } - if (a->use_timer) { - timer_free(st->buft); - } - if (st->output) { - AUD_close_out(&a->card, st->voice.out); - } else { - AUD_close_in(&a->card, st->voice.in); - } + hda_close_stream(a, a->st + i); } AUD_remove_card(&a->card); } From 244d52ff736fefc3dd364ed091720aa896af306d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:12 +0400 Subject: [PATCH 03/20] ui/dbus: fix leak on message filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A filter function that wants to drop a message should return NULL, in which case it must also unref the message itself. Fixes: fa88b85de ("ui/dbus: filter out pending messages when scanout") Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-4-marcandre.lureau@redhat.com> --- ui/dbus-listener.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index a54123acea..434bd608f2 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -1001,6 +1001,7 @@ dbus_filter(GDBusConnection *connection, serial = g_dbus_message_get_serial(message); if (serial <= ddl->out_serial_to_discard) { trace_dbus_filter(serial, ddl->out_serial_to_discard); + g_object_unref(message); return NULL; } From 330ef31deb2e5461cff907488b710f5bd9cd2327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:13 +0400 Subject: [PATCH 04/20] ui/win32: fix potential use-after-free with dbus shared memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DisplaySurface may be free before the pixman image is freed, since the image is refcounted and used by different objects, including pending dbus messages. Furthermore, setting the destroy function in create_displaysurface_from() isn't appropriate, as it may not be used, and may be overriden as in ramfb. Set the destroy function when the shared handle is set, use the HANDLE directly for destroy data, using a single common helper qemu_pixman_win32_image_destroy(). Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-5-marcandre.lureau@redhat.com> --- hw/display/virtio-gpu.c | 14 ++------------ include/ui/qemu-pixman.h | 2 ++ ui/console.c | 24 ++---------------------- ui/qemu-pixman.c | 15 +++++++++++++++ 4 files changed, 21 insertions(+), 34 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 3281842bfe..017a0f170c 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -238,16 +238,6 @@ static uint32_t calc_image_hostmem(pixman_format_code_t pformat, return height * stride; } -#ifdef WIN32 -static void -win32_pixman_image_destroy(pixman_image_t *image, void *data) -{ - HANDLE handle = data; - - qemu_win32_map_free(pixman_image_get_data(image), handle, &error_warn); -} -#endif - static void virtio_gpu_resource_create_2d(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { @@ -308,7 +298,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, bits, c2d.height ? res->hostmem / c2d.height : 0); #ifdef WIN32 if (res->image) { - pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); + pixman_image_set_destroy_function(res->image, qemu_pixman_win32_image_destroy, res->handle); } #endif } @@ -1327,7 +1317,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, return -EINVAL; } #ifdef WIN32 - pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); + pixman_image_set_destroy_function(res->image, qemu_pixman_win32_image_destroy, res->handle); #endif res->addrs = g_new(uint64_t, res->iov_cnt); diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index ef13a8210c..e3dd72b9e3 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -97,6 +97,8 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, void qemu_pixman_image_unref(pixman_image_t *image); +void qemu_pixman_win32_image_destroy(pixman_image_t *image, void *data); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(pixman_image_t, qemu_pixman_image_unref) #endif /* QEMU_PIXMAN_H */ diff --git a/ui/console.c b/ui/console.c index 105a0e2c70..8f416ff0b9 100644 --- a/ui/console.c +++ b/ui/console.c @@ -461,24 +461,6 @@ void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, surface->handle = h; surface->handle_offset = offset; } - -static void -win32_pixman_image_destroy(pixman_image_t *image, void *data) -{ - DisplaySurface *surface = data; - - if (!surface->handle) { - return; - } - - assert(surface->handle_offset == 0); - - qemu_win32_map_free( - pixman_image_get_data(surface->image), - surface->handle, - &error_warn - ); -} #endif DisplaySurface *qemu_create_displaysurface(int width, int height) @@ -504,6 +486,8 @@ DisplaySurface *qemu_create_displaysurface(int width, int height) #ifdef WIN32 qemu_displaysurface_win32_set_handle(surface, handle, 0); + pixman_image_set_destroy_function(surface->image, + qemu_pixman_win32_image_destroy, handle); #endif return surface; } @@ -519,10 +503,6 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height, width, height, (void *)data, linesize); assert(surface->image != NULL); -#ifdef WIN32 - pixman_image_set_destroy_function(surface->image, - win32_pixman_image_destroy, surface); -#endif return surface; } diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index 6cada8b45e..3870e1a215 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -4,6 +4,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "ui/console.h" #include "standard-headers/drm/drm_fourcc.h" #include "trace.h" @@ -267,3 +268,17 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, pixman_image_unref(ibg); } #endif /* CONFIG_PIXMAN */ + +#ifdef WIN32 +void +qemu_pixman_win32_image_destroy(pixman_image_t *image, void *data) +{ + HANDLE handle = data; + + qemu_win32_map_free( + pixman_image_get_data(image), + handle, + &error_warn + ); +} +#endif From cf59889781297a5618f1735a5f31402caa806b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:14 +0400 Subject: [PATCH 05/20] ui/dbus: fix filtering all update messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Filtering pending messages when a new scanout is given shouldn't discard pending cursor changes, for example. Since filtering happens in a different thread, use atomic set/get. Fixes: fa88b85dea ("ui/dbus: filter out pending messages when scanout") Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-6-marcandre.lureau@redhat.com> --- ui/dbus-listener.c | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 434bd608f2..c69afc05a8 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "sysemu/sysemu.h" #include "dbus.h" +#include "glib.h" #ifdef G_OS_UNIX #include #endif @@ -85,7 +86,7 @@ struct _DBusDisplayListener { #endif guint dbus_filter; - guint32 out_serial_to_discard; + guint32 display_serial_to_discard; }; G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT) @@ -93,10 +94,12 @@ G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT) static void dbus_gfx_update(DisplayChangeListener *dcl, int x, int y, int w, int h); -static void ddl_discard_pending_messages(DBusDisplayListener *ddl) +static void ddl_discard_display_messages(DBusDisplayListener *ddl) { - ddl->out_serial_to_discard = g_dbus_connection_get_last_serial( + guint32 serial = g_dbus_connection_get_last_serial( g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy))); + + g_atomic_int_set(&ddl->display_serial_to_discard, serial); } #ifdef CONFIG_OPENGL @@ -290,7 +293,7 @@ static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, return; } - ddl_discard_pending_messages(ddl); + ddl_discard_display_messages(ddl); width = qemu_dmabuf_get_width(dmabuf); height = qemu_dmabuf_get_height(dmabuf); @@ -338,7 +341,7 @@ static bool dbus_scanout_map(DBusDisplayListener *ddl) return false; } - ddl_discard_pending_messages(ddl); + ddl_discard_display_messages(ddl); if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync( ddl->map_proxy, @@ -401,7 +404,7 @@ dbus_scanout_share_d3d_texture( return false; } - ddl_discard_pending_messages(ddl); + ddl_discard_display_messages(ddl); qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d( ddl->d3d11_proxy, @@ -659,7 +662,7 @@ static void ddl_scanout(DBusDisplayListener *ddl) surface_stride(ddl->ds) * surface_height(ddl->ds), TRUE, (GDestroyNotify)pixman_image_unref, pixman_image_ref(ddl->ds->image)); - ddl_discard_pending_messages(ddl); + ddl_discard_display_messages(ddl); qemu_dbus_display1_listener_call_scanout( ddl->proxy, surface_width(ddl->ds), surface_height(ddl->ds), @@ -992,17 +995,35 @@ dbus_filter(GDBusConnection *connection, gpointer user_data) { DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(user_data); - guint32 serial; + guint32 serial, discard_serial; if (incoming) { return message; } serial = g_dbus_message_get_serial(message); - if (serial <= ddl->out_serial_to_discard) { - trace_dbus_filter(serial, ddl->out_serial_to_discard); - g_object_unref(message); - return NULL; + + discard_serial = g_atomic_int_get(&ddl->display_serial_to_discard); + if (serial <= discard_serial) { + const char *member = g_dbus_message_get_member(message); + static const char *const display_messages[] = { + "Scanout", + "Update", +#ifdef CONFIG_GBM + "ScanoutDMABUF", + "UpdateDMABUF", +#endif + "ScanoutMap", + "UpdateMap", + "Disable", + NULL, + }; + + if (g_strv_contains(display_messages, member)) { + trace_dbus_filter(serial, discard_serial); + g_object_unref(message); + return NULL; + } } return message; From 6b9524dfa550e4ce66451e3bdfbe61f2a683fddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:15 +0400 Subject: [PATCH 06/20] ui/dbus: discard display messages on disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-7-marcandre.lureau@redhat.com> --- ui/dbus-listener.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index c69afc05a8..19cb74e92b 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -107,6 +107,8 @@ static void dbus_scanout_disable(DisplayChangeListener *dcl) { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); + ddl_discard_display_messages(ddl); + qemu_dbus_display1_listener_call_disable( ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } From dcf62fb6ce8f56709d74c9b79c15478b9f3ff266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:16 +0400 Subject: [PATCH 07/20] ui/dbus: discard pending CursorDefine on new one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to scanout updates, let's discard pending cursor changes. Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-8-marcandre.lureau@redhat.com> --- ui/dbus-listener.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 19cb74e92b..eca6890ce6 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -87,6 +87,7 @@ struct _DBusDisplayListener { guint dbus_filter; guint32 display_serial_to_discard; + guint32 cursor_serial_to_discard; }; G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT) @@ -102,6 +103,14 @@ static void ddl_discard_display_messages(DBusDisplayListener *ddl) g_atomic_int_set(&ddl->display_serial_to_discard, serial); } +static void ddl_discard_cursor_messages(DBusDisplayListener *ddl) +{ + guint32 serial = g_dbus_connection_get_last_serial( + g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy))); + + g_atomic_int_set(&ddl->cursor_serial_to_discard, serial); +} + #ifdef CONFIG_OPENGL static void dbus_scanout_disable(DisplayChangeListener *dcl) { @@ -502,6 +511,8 @@ static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, return; } + ddl_discard_cursor_messages(ddl); + egl_dmabuf_import_texture(dmabuf); texture = qemu_dmabuf_get_texture(dmabuf); if (!texture) { @@ -745,6 +756,8 @@ static void dbus_cursor_define(DisplayChangeListener *dcl, DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); GVariant *v_data = NULL; + ddl_discard_cursor_messages(ddl); + v_data = g_variant_new_from_data( G_VARIANT_TYPE("ay"), c->data, @@ -1028,6 +1041,21 @@ dbus_filter(GDBusConnection *connection, } } + discard_serial = g_atomic_int_get(&ddl->cursor_serial_to_discard); + if (serial <= discard_serial) { + const gchar *member = g_dbus_message_get_member(message); + static const char *const cursor_messages[] = { + "CursorDefine", + NULL + }; + + if (g_strv_contains(cursor_messages, member)) { + trace_dbus_filter(serial, discard_serial); + g_object_unref(message); + return NULL; + } + } + return message; } From c90204b65400d77a918844889ad6789858406203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:17 +0400 Subject: [PATCH 08/20] util/memfd: report potential errors on free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-9-marcandre.lureau@redhat.com> --- util/memfd.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/util/memfd.c b/util/memfd.c index 4a3c07e0be..8a2e906962 100644 --- a/util/memfd.c +++ b/util/memfd.c @@ -28,6 +28,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qemu/memfd.h" #include "qemu/host-utils.h" @@ -149,11 +150,15 @@ err: void qemu_memfd_free(void *ptr, size_t size, int fd) { if (ptr) { - munmap(ptr, size); + if (munmap(ptr, size) != 0) { + error_report("memfd munmap() failed: %s", strerror(errno)); + } } if (fd != -1) { - close(fd); + if (close(fd) != 0) { + error_report("memfd close() failed: %s", strerror(errno)); + } } } From 1bfb726112ea4fda07c988f08df32d1eebb9abec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:18 +0400 Subject: [PATCH 09/20] ui/pixman: generalize shared_image_destroy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Learn to free memfd-allocated shared memory. Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-10-marcandre.lureau@redhat.com> --- hw/display/virtio-gpu.c | 4 ++-- include/ui/qemu-pixman.h | 2 +- ui/console.c | 2 +- ui/qemu-pixman.c | 20 ++++++++++++-------- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 017a0f170c..77f6e76f23 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -298,7 +298,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, bits, c2d.height ? res->hostmem / c2d.height : 0); #ifdef WIN32 if (res->image) { - pixman_image_set_destroy_function(res->image, qemu_pixman_win32_image_destroy, res->handle); + pixman_image_set_destroy_function(res->image, qemu_pixman_shared_image_destroy, res->handle); } #endif } @@ -1317,7 +1317,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, return -EINVAL; } #ifdef WIN32 - pixman_image_set_destroy_function(res->image, qemu_pixman_win32_image_destroy, res->handle); + pixman_image_set_destroy_function(res->image, qemu_pixman_shared_image_destroy, res->handle); #endif res->addrs = g_new(uint64_t, res->iov_cnt); diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index e3dd72b9e3..a97f56d09a 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -97,7 +97,7 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, void qemu_pixman_image_unref(pixman_image_t *image); -void qemu_pixman_win32_image_destroy(pixman_image_t *image, void *data); +void qemu_pixman_shared_image_destroy(pixman_image_t *image, void *data); G_DEFINE_AUTOPTR_CLEANUP_FUNC(pixman_image_t, qemu_pixman_image_unref) diff --git a/ui/console.c b/ui/console.c index 8f416ff0b9..fdd76c2be4 100644 --- a/ui/console.c +++ b/ui/console.c @@ -487,7 +487,7 @@ DisplaySurface *qemu_create_displaysurface(int width, int height) #ifdef WIN32 qemu_displaysurface_win32_set_handle(surface, handle, 0); pixman_image_set_destroy_function(surface->image, - qemu_pixman_win32_image_destroy, handle); + qemu_pixman_shared_image_destroy, handle); #endif return surface; } diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index 3870e1a215..46a91e7f7a 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -6,6 +6,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "ui/console.h" +#include "qemu/memfd.h" #include "standard-headers/drm/drm_fourcc.h" #include "trace.h" @@ -269,16 +270,19 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, } #endif /* CONFIG_PIXMAN */ -#ifdef WIN32 void -qemu_pixman_win32_image_destroy(pixman_image_t *image, void *data) +qemu_pixman_shared_image_destroy(pixman_image_t *image, void *data) { + void *ptr = pixman_image_get_data(image); + +#ifdef WIN32 HANDLE handle = data; - qemu_win32_map_free( - pixman_image_get_data(image), - handle, - &error_warn - ); -} + qemu_win32_map_free(ptr, handle, &error_warn); +#else + int shmfd = GPOINTER_TO_INT(data); + size_t size = pixman_image_get_height(image) * pixman_image_get_stride(image); + + qemu_memfd_free(ptr, size, shmfd); #endif +} From 2448ff392c46aec2835f5eb6214e73438bbecda5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:19 +0400 Subject: [PATCH 10/20] ui/dbus: do not limit to one listener per connection / bus name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an arbitrary limitation that doesn't concern QEMU directly and may make some use cases unnecessarily more complicated. Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-11-marcandre.lureau@redhat.com> --- ui/dbus-console.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/ui/dbus-console.c b/ui/dbus-console.c index 578b67f62b..5eb1d40d16 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -41,7 +41,7 @@ struct _DBusDisplayConsole { DisplayChangeListener dcl; DBusDisplay *display; - GHashTable *listeners; + GPtrArray *listeners; QemuDBusDisplay1Console *iface; QemuDBusDisplay1Keyboard *iface_kbd; @@ -142,8 +142,7 @@ dbus_display_console_init(DBusDisplayConsole *object) { DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object); - ddc->listeners = g_hash_table_new_full(g_str_hash, g_str_equal, - NULL, g_object_unref); + ddc->listeners = g_ptr_array_new_with_free_func(g_object_unref); ddc->dcl.ops = &dbus_console_dcl_ops; } @@ -157,7 +156,7 @@ dbus_display_console_dispose(GObject *object) g_clear_object(&ddc->iface_mouse); g_clear_object(&ddc->iface_kbd); g_clear_object(&ddc->iface); - g_clear_pointer(&ddc->listeners, g_hash_table_unref); + g_clear_pointer(&ddc->listeners, g_ptr_array_unref); g_clear_pointer(&ddc->kbd, qkbd_state_free); G_OBJECT_CLASS(dbus_display_console_parent_class)->dispose(object); @@ -179,7 +178,7 @@ listener_vanished_cb(DBusDisplayListener *listener) trace_dbus_listener_vanished(name); - g_hash_table_remove(ddc->listeners, name); + g_ptr_array_remove_fast(ddc->listeners, listener); qkbd_state_lift_all_keys(ddc->kbd); } @@ -267,16 +266,6 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, DBusDisplayListener *listener; int fd; - if (sender && g_hash_table_contains(ddc->listeners, sender)) { - g_dbus_method_invocation_return_error( - invocation, - DBUS_DISPLAY_ERROR, - DBUS_DISPLAY_ERROR_INVALID, - "`%s` is already registered!", - sender); - return DBUS_METHOD_INVOCATION_HANDLED; - } - #ifdef G_OS_WIN32 if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) { return DBUS_METHOD_INVOCATION_HANDLED; @@ -331,9 +320,7 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } - g_hash_table_insert(ddc->listeners, - (gpointer)dbus_display_listener_get_bus_name(listener), - listener); + g_ptr_array_add(ddc->listeners, listener); g_object_connect(listener_conn, "swapped-signal::closed", listener_vanished_cb, listener, NULL); From 28a3ca04782c5b72b85e197ccfab287a66ce76cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:20 +0400 Subject: [PATCH 11/20] ui/dbus: add trace for can_share_map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-12-marcandre.lureau@redhat.com> --- ui/dbus-listener.c | 1 + ui/trace-events | 1 + 2 files changed, 2 insertions(+) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index eca6890ce6..7142afcddb 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -1089,6 +1089,7 @@ dbus_display_listener_new(const char *bus_name, ddl->console = console; dbus_display_listener_setup_shared_map(ddl); + trace_dbus_can_share_map(ddl->can_share_map); dbus_display_listener_setup_d3d11(ddl); con = qemu_console_lookup_by_index(dbus_display_console_get_index(console)); diff --git a/ui/trace-events b/ui/trace-events index fb253c1666..3da0d5e280 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -166,6 +166,7 @@ dbus_clipboard_unregister(const char *bus_name) "peer %s" dbus_scanout_texture(uint32_t tex_id, bool backing_y_0_top, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "tex_id:%u y0top:%d back:%ux%u %u+%u-%ux%u" dbus_gl_gfx_switch(void *p) "surf: %p" dbus_filter(unsigned int serial, unsigned int filter) "serial=%u (<= %u)" +dbus_can_share_map(bool share) "can_share_map: %d" # egl-helpers.c egl_init_d3d11_device(void *p) "d3d device: %p" From ec818df0005d1598d249f0cfea557226b6ad89a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:21 +0400 Subject: [PATCH 12/20] ui/surface: allocate shared memory on !win32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use qemu_memfd_alloc() to allocate the display surface memory, which will fallback on tmpfile/mmap() on systems without memfd, and allow to share the display with other processes. This is similar to how display memory is allocated on win32 since commit 09b4c198 ("console/win32: allocate shareable display surface"). Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-13-marcandre.lureau@redhat.com> --- include/ui/surface.h | 6 ++++++ ui/console.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/include/ui/surface.h b/include/ui/surface.h index 345b19169d..37d03be4af 100644 --- a/include/ui/surface.h +++ b/include/ui/surface.h @@ -26,6 +26,9 @@ typedef struct DisplaySurface { #ifdef WIN32 HANDLE handle; uint32_t handle_offset; +#else + int shmfd; + uint32_t shmfd_offset; #endif } DisplaySurface; @@ -40,6 +43,9 @@ DisplaySurface *qemu_create_placeholder_surface(int w, int h, #ifdef WIN32 void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, HANDLE h, uint32_t offset); +#else +void qemu_displaysurface_set_shmfd(DisplaySurface *surface, + int shmfd, uint32_t offset); #endif DisplaySurface *qemu_create_displaysurface(int width, int height); diff --git a/ui/console.c b/ui/console.c index fdd76c2be4..3a2aaba3c7 100644 --- a/ui/console.c +++ b/ui/console.c @@ -37,6 +37,7 @@ #include "trace.h" #include "exec/memory.h" #include "qom/object.h" +#include "qemu/memfd.h" #include "console-priv.h" @@ -461,6 +462,15 @@ void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, surface->handle = h; surface->handle_offset = offset; } +#else +void qemu_displaysurface_set_shmfd(DisplaySurface *surface, + int shmfd, uint32_t offset) +{ + assert(surface->shmfd == -1); + + surface->shmfd = shmfd; + surface->shmfd_offset = offset; +} #endif DisplaySurface *qemu_create_displaysurface(int width, int height) @@ -469,12 +479,16 @@ DisplaySurface *qemu_create_displaysurface(int width, int height) void *bits = NULL; #ifdef WIN32 HANDLE handle = NULL; +#else + int shmfd = -1; #endif trace_displaysurface_create(width, height); #ifdef WIN32 bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort); +#else + bits = qemu_memfd_alloc("displaysurface", width * height * 4, 0, &shmfd, &error_abort); #endif surface = qemu_create_displaysurface_from( @@ -486,9 +500,13 @@ DisplaySurface *qemu_create_displaysurface(int width, int height) #ifdef WIN32 qemu_displaysurface_win32_set_handle(surface, handle, 0); - pixman_image_set_destroy_function(surface->image, - qemu_pixman_shared_image_destroy, handle); + void *data = handle; +#else + qemu_displaysurface_set_shmfd(surface, shmfd, 0); + void *data = GINT_TO_POINTER(shmfd); #endif + pixman_image_set_destroy_function(surface->image, qemu_pixman_shared_image_destroy, data); + return surface; } @@ -499,6 +517,9 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height, DisplaySurface *surface = g_new0(DisplaySurface, 1); trace_displaysurface_create_from(surface, width, height, format); +#ifndef WIN32 + surface->shmfd = -1; +#endif surface->image = pixman_image_create_bits(format, width, height, (void *)data, linesize); @@ -512,6 +533,9 @@ DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image) DisplaySurface *surface = g_new0(DisplaySurface, 1); trace_displaysurface_create_pixman(surface); +#ifndef WIN32 + surface->shmfd = -1; +#endif surface->image = pixman_image_ref(image); return surface; From c118c8eb3e63c02421e8a02e82ffab6fa8369301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:22 +0400 Subject: [PATCH 13/20] meson: find_program('gdbus-codegen') directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gio.pc variable is a bit bogus in context of cross-compilation, since it contains an absolute path, relative to the sysroot directory. On Fedora, it ends up as: /usr/x86_64-w64-mingw32/sys-root/mingw/lib/pkgconfig/usr/bin/gdbus-codegen path which does not exist because it is not shipped by Fedora mingw packages. Instead, we can rely on meson find_program() behaviour to do a better job based on its search order and capabilities. Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-14-marcandre.lureau@redhat.com> --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index aecc381932..ed0d2086cb 100644 --- a/meson.build +++ b/meson.build @@ -1036,7 +1036,7 @@ if not get_option('gio').auto() or have_system gio = not_found endif if gio.found() - gdbus_codegen = find_program(gio.get_variable('gdbus_codegen'), + gdbus_codegen = find_program('gdbus-codegen', required: get_option('gio')) gio_unix = dependency('gio-unix-2.0', required: get_option('gio'), method: 'pkg-config') From 3a9d38d31ea7bf99c62c8d97433baa85b3bdd5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Oct 2024 16:50:23 +0400 Subject: [PATCH 14/20] ui/dbus: make Listener.Win32.Map win32-specific MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no types specific to Windows, so the code compiles on other platforms, but its useless on !Windows. Signed-off-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20241008125028.1177932-15-marcandre.lureau@redhat.com> --- ui/dbus-display1.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml index ce35d64eea..d5bb279698 100644 --- a/ui/dbus-display1.xml +++ b/ui/dbus-display1.xml @@ -476,6 +476,7 @@ org.qemu.Display1.Listener on ``/org/qemu/Display1/Listener`` for Windows specific shared memory scanouts. --> + + + + + + + + + + + + + + + + + + + + + + +