console: make QMP/HMP screendump run in coroutine

Thanks to the monitors' coroutine support (merge commit b7092cda1b),
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
ppm_save() will write the screen image to disk in the coroutine context.

The IO is still blocking though, as the file is set blocking so far,
this could be addressed by some future change (with other caveats).

Related to:
https://bugzilla.redhat.com/show_bug.cgi?id=1230527

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Message-id: 20201027133602.3038018-4-marcandre.lureau@redhat.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Marc-André Lureau 2020-10-27 17:36:02 +04:00 committed by Gerd Hoffmann
parent d00ec2fe86
commit 0d9b90ce5c
4 changed files with 34 additions and 5 deletions

View File

@ -254,6 +254,7 @@ ERST
.help = "save screen from head 'head' of display device 'device' "
"into PPM image 'filename'",
.cmd = hmp_screendump,
.coroutine = true,
},
SRST

View File

@ -1762,7 +1762,8 @@ err_out:
goto out;
}
void hmp_screendump(Monitor *mon, const QDict *qdict)
void coroutine_fn
hmp_screendump(Monitor *mon, const QDict *qdict)
{
const char *filename = qdict_get_str(qdict, "filename");
const char *id = qdict_get_try_str(qdict, "device");

View File

@ -98,7 +98,8 @@
#
##
{ 'command': 'screendump',
'data': {'filename': 'str', '*device': 'str', '*head': 'int'} }
'data': {'filename': 'str', '*device': 'str', '*head': 'int'},
'coroutine': true }
##
# == Spice

View File

@ -168,6 +168,7 @@ struct QemuConsole {
QEMUFIFO out_fifo;
uint8_t out_fifo_buf[16];
QEMUTimer *kbd_timer;
CoQueue dump_queue;
QTAILQ_ENTRY(QemuConsole) next;
};
@ -263,6 +264,7 @@ static void gui_setup_refresh(DisplayState *ds)
void graphic_hw_update_done(QemuConsole *con)
{
qemu_co_queue_restart_all(&con->dump_queue);
}
void graphic_hw_update(QemuConsole *con)
@ -340,8 +342,15 @@ static bool ppm_save(int fd, pixman_image_t *image, Error **errp)
return true;
}
void qmp_screendump(const char *filename, bool has_device, const char *device,
bool has_head, int64_t head, Error **errp)
static void graphic_hw_update_bh(void *con)
{
graphic_hw_update(con);
}
/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */
void coroutine_fn
qmp_screendump(const char *filename, bool has_device, const char *device,
bool has_head, int64_t head, Error **errp)
{
g_autoptr(pixman_image_t) image = NULL;
QemuConsole *con;
@ -366,7 +375,18 @@ void qmp_screendump(const char *filename, bool has_device, const char *device,
}
}
graphic_hw_update(con);
if (qemu_co_queue_empty(&con->dump_queue)) {
/* Defer the update, it will restart the pending coroutines */
aio_bh_schedule_oneshot(qemu_get_aio_context(),
graphic_hw_update_bh, con);
}
qemu_co_queue_wait(&con->dump_queue, NULL);
/*
* All pending coroutines are woken up, while the BQL is held. No
* further graphic update are possible until it is released. Take
* an image ref before that.
*/
surface = qemu_console_surface(con);
if (!surface) {
error_setg(errp, "no surface");
@ -381,6 +401,11 @@ void qmp_screendump(const char *filename, bool has_device, const char *device,
return;
}
/*
* The image content could potentially be updated as the coroutine
* yields and releases the BQL. It could produce corrupted dump, but
* it should be otherwise safe.
*/
if (!ppm_save(fd, image, errp)) {
qemu_unlink(filename);
}
@ -1297,6 +1322,7 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
obj = object_new(TYPE_QEMU_CONSOLE);
s = QEMU_CONSOLE(obj);
qemu_co_queue_init(&s->dump_queue);
s->head = head;
object_property_add_link(obj, "device", TYPE_DEVICE,
(Object **)&s->device,