Thanks to the QMP coroutine support, the screendump handler can trigger a graphic_hw_update(), yield and let the main loop run until update is done. Then the handler is resumed, and the ppm_save() will write the screen image to disk in the coroutine context (thus non-blocking).
For now, HMP doesn't have coroutine support, so it remains potentially outdated or glitched. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1230527 Based-on: <20200109183545.27452-2-kw...@redhat.com> Cc: Kevin Wolf <kw...@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- qapi/ui.json | 3 ++- ui/console.c | 35 +++++++++++++++++++++++++++-------- ui/trace-events | 2 +- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/qapi/ui.json b/qapi/ui.json index e04525d8b4..d941202f34 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -96,7 +96,8 @@ # ## { 'command': 'screendump', - 'data': {'filename': 'str', '*device': 'str', '*head': 'int'} } + 'data': {'filename': 'str', '*device': 'str', '*head': 'int'}, + 'coroutine': true } ## # == Spice diff --git a/ui/console.c b/ui/console.c index ac79d679f5..db184b473f 100644 --- a/ui/console.c +++ b/ui/console.c @@ -167,6 +167,7 @@ struct QemuConsole { QEMUFIFO out_fifo; uint8_t out_fifo_buf[16]; QEMUTimer *kbd_timer; + Coroutine *screendump_co; QTAILQ_ENTRY(QemuConsole) next; }; @@ -194,7 +195,6 @@ static void dpy_refresh(DisplayState *s); static DisplayState *get_alloc_displaystate(void); static void text_console_update_cursor_timer(void); static void text_console_update_cursor(void *opaque); -static bool ppm_save(int fd, DisplaySurface *ds, Error **errp); static void gui_update(void *opaque) { @@ -263,6 +263,9 @@ static void gui_setup_refresh(DisplayState *ds) void graphic_hw_update_done(QemuConsole *con) { + if (con && con->screendump_co) { + aio_co_wake(con->screendump_co); + } } void graphic_hw_update(QemuConsole *con) @@ -310,16 +313,16 @@ void graphic_hw_invalidate(QemuConsole *con) } } -static bool ppm_save(int fd, DisplaySurface *ds, Error **errp) +static bool ppm_save(int fd, pixman_image_t *image, Error **errp) { - int width = pixman_image_get_width(ds->image); - int height = pixman_image_get_height(ds->image); + int width = pixman_image_get_width(image); + int height = pixman_image_get_height(image); g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd)); g_autofree char *header = NULL; g_autoptr(pixman_image_t) linebuf = NULL; int y; - trace_ppm_save(fd, ds); + trace_ppm_save(fd, image); header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255); if (qio_channel_write_all(QIO_CHANNEL(ioc), @@ -329,7 +332,7 @@ static bool ppm_save(int fd, DisplaySurface *ds, Error **errp) linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); for (y = 0; y < height; y++) { - qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y); + qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); if (qio_channel_write_all(QIO_CHANNEL(ioc), (char *)pixman_image_get_data(linebuf), pixman_image_get_stride(linebuf), errp) < 0) { @@ -340,11 +343,18 @@ static bool ppm_save(int fd, DisplaySurface *ds, Error **errp) return true; } +static void graphic_hw_update_bh(void *con) +{ + graphic_hw_update(con); +} + +/* may be called in coroutine context or not */ void qmp_screendump(const char *filename, bool has_device, const char *device, bool has_head, int64_t head, Error **errp) { QemuConsole *con; DisplaySurface *surface; + g_autoptr(pixman_image_t) image = NULL; int fd; if (has_device) { @@ -365,7 +375,15 @@ void qmp_screendump(const char *filename, bool has_device, const char *device, } } - graphic_hw_update(con); + if (qemu_in_coroutine()) { + assert(!con->screendump_co); + con->screendump_co = qemu_coroutine_self(); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + graphic_hw_update_bh, con); + qemu_coroutine_yield(); + con->screendump_co = NULL; + } + surface = qemu_console_surface(con); if (!surface) { error_setg(errp, "no surface"); @@ -379,7 +397,8 @@ void qmp_screendump(const char *filename, bool has_device, const char *device, return; } - if (!ppm_save(fd, surface, errp)) { + image = pixman_image_ref(surface->image); + if (!ppm_save(fd, image, errp)) { qemu_unlink(filename); } } diff --git a/ui/trace-events b/ui/trace-events index 0dcda393c1..e8726fc969 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -15,7 +15,7 @@ displaysurface_create_pixman(void *display_surface) "surface=%p" displaysurface_free(void *display_surface) "surface=%p" displaychangelistener_register(void *dcl, const char *name) "%p [ %s ]" displaychangelistener_unregister(void *dcl, const char *name) "%p [ %s ]" -ppm_save(int fd, void *display_surface) "fd=%d surface=%p" +ppm_save(int fd, void *image) "fd=%d image=%p" # gtk.c # gtk-gl-area.c -- 2.25.0.rc2.1.g09a9a1a997