@@ -19,6 +19,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "console.h"
#include "qxl.h"
static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect)
@@ -148,19 +149,69 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
}
/*
+ * struct used just for ppm save bh. We don't actually support multiple qxl
+ * screendump yet, but a) we will, and b) exporting qxl0 from qxl.c looks
+ * uglier imo.
+ */
+typedef struct QXLPPMSaveBHData {
+ PCIQXLDevice *qxl;
+ QXLCookie *cookie;
+} QXLPPMSaveBHData;
+
+static void qxl_cookie_render_free(PCIQXLDevice *qxl, QXLCookie *cookie)
+{
+ g_free(cookie->u.render.filename);
+ if (cookie->u.render.async && cookie->u.render.cb) {
+ (*cookie->u.render.cb)(cookie->u.render.cb_opaque);
+ }
+ g_free(cookie);
+ --qxl->render_update_cookie_num;
+}
+
+static void qxl_render_ppm_save_bh(void *opaque)
+{
+ QXLPPMSaveBHData *data = opaque;
+ PCIQXLDevice *qxl = data->qxl;
+ QXLCookie *cookie = data->cookie;
+ QEMUBH *bh = cookie->u.render.ppm_save_bh;
+
+ qemu_mutex_lock(&qxl->ssd.lock);
+ dprint(qxl, 1, "%s: %p (primary %p)\n", __func__,
+ qxl->ssd.ds->surface->data, qxl->guest_primary.data);
+ qxl_render_update_area_unlocked(qxl);
+ ppm_save(cookie->u.render.filename, qxl->ssd.ds->surface);
+ qxl_cookie_render_free(qxl, cookie);
+ qemu_mutex_unlock(&qxl->ssd.lock);
+ g_free(data);
+ qemu_bh_delete(bh);
+}
+
+/*
* use ssd.lock to protect render_update_cookie_num.
* qxl_render_update is called by io thread or vcpu thread, and the completion
* callbacks are called by spice_server thread, defering to bh called from the
* io thread.
*/
-void qxl_render_update(PCIQXLDevice *qxl)
+void qxl_render_update(PCIQXLDevice *qxl, const char *filename, bool async,
+ vga_hw_screen_dump_async_cb_ptr cb, void *cb_opaque)
{
QXLCookie *cookie;
+ QEMUBH *ppm_save_bh;
+ QXLPPMSaveBHData *ppm_save_bh_data;
+ assert(!async || cb);
qemu_mutex_lock(&qxl->ssd.lock);
if (!runstate_is_running() || !qxl->guest_primary.commands) {
qxl_render_update_area_unlocked(qxl);
+ if (filename) {
+ dprint(qxl, 1, "%s: screendump with no pending commands\n",
+ __func__);
+ ppm_save(filename, qxl->ssd.ds->surface);
+ }
+ if (async) {
+ (*cb)(cb_opaque);
+ }
qemu_mutex_unlock(&qxl->ssd.lock);
return;
}
@@ -171,8 +222,24 @@ void qxl_render_update(PCIQXLDevice *qxl)
cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA,
0);
qxl_set_rect_to_surface(qxl, &cookie->u.render.area);
+ if (filename && async) {
+ ppm_save_bh_data = g_malloc0(sizeof(*ppm_save_bh_data));
+ ppm_save_bh_data->qxl = qxl;
+ ppm_save_bh_data->cookie = cookie;
+ ppm_save_bh = qemu_bh_new(qxl_render_ppm_save_bh, ppm_save_bh_data);
+ cookie->u.render.filename = g_strdup(filename);
+ cookie->u.render.ppm_save_bh = ppm_save_bh;
+ cookie->u.render.async = async;
+ cookie->u.render.cb = cb;
+ cookie->u.render.cb_opaque = cb_opaque;
+ }
qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL,
0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie);
+ if (!async) {
+ dprint(qxl, 2, "warning: screendump saving possibly not up to date"
+ "surface\n");
+ ppm_save(cookie->u.render.filename, qxl->ssd.ds->surface);
+ }
}
void qxl_render_update_area_bh(void *opaque)
@@ -187,10 +254,14 @@ void qxl_render_update_area_bh(void *opaque)
void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie)
{
qemu_mutex_lock(&qxl->ssd.lock);
- qemu_bh_schedule(qxl->update_area_bh);
- qxl->render_update_cookie_num--;
+ if (cookie->u.render.filename) {
+ dprint(qxl, 1, "%s: scheduling ppm_save_bh\n", __func__);
+ qemu_bh_schedule(cookie->u.render.ppm_save_bh);
+ } else {
+ qemu_bh_schedule(qxl->update_area_bh);
+ qxl_cookie_render_free(qxl, cookie);
+ }
qemu_mutex_unlock(&qxl->ssd.lock);
- g_free(cookie);
}
static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor)
@@ -1477,7 +1477,7 @@ static void qxl_hw_update(void *opaque)
break;
case QXL_MODE_COMPAT:
case QXL_MODE_NATIVE:
- qxl_render_update(qxl);
+ qxl_render_update(qxl, NULL, false, NULL, NULL);
break;
default:
break;
@@ -1492,7 +1492,10 @@ static void qxl_hw_invalidate(void *opaque)
vga->invalidate(vga);
}
-static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
+static void qxl_hw_screen_dump_helper(void *opaque, const char *filename,
+ bool cswitch, bool async,
+ vga_hw_screen_dump_async_cb_ptr cb,
+ void *cb_opaque)
{
PCIQXLDevice *qxl = opaque;
VGACommonState *vga = &qxl->vga;
@@ -1500,17 +1503,38 @@ static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
switch (qxl->mode) {
case QXL_MODE_COMPAT:
case QXL_MODE_NATIVE:
- qxl_render_update(qxl);
- ppm_save(filename, qxl->ssd.ds->surface);
+ qxl_render_update(qxl, filename, async, cb, cb_opaque);
break;
case QXL_MODE_VGA:
- vga->screen_dump(vga, filename, cswitch);
+ if (async) {
+ if (vga->screen_dump_async) {
+ vga->screen_dump_async(vga, filename, cswitch, cb, cb_opaque);
+ } else {
+ vga->screen_dump(vga, filename, cswitch);
+ (*cb)(cb_opaque);
+ }
+ } else {
+ vga->screen_dump(vga, filename, cswitch);
+ }
break;
default:
break;
}
}
+static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
+{
+ qxl_hw_screen_dump_helper(opaque, filename, cswitch, false, NULL, NULL);
+}
+
+static void qxl_hw_screen_dump_async(void *opaque, const char *filename,
+ bool cswitch,
+ vga_hw_screen_dump_async_cb_ptr cb,
+ void *cb_opaque)
+{
+ qxl_hw_screen_dump_helper(opaque, filename, cswitch, true, cb, cb_opaque);
+}
+
static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
{
PCIQXLDevice *qxl = opaque;
@@ -1764,7 +1788,7 @@ static int qxl_init_primary(PCIDevice *dev)
vga->ds = graphic_console_init(qxl_hw_update, qxl_hw_invalidate,
qxl_hw_screen_dump, qxl_hw_text_update,
- NULL, qxl);
+ qxl_hw_screen_dump_async, qxl);
qemu_spice_display_init_common(&qxl->ssd, vga->ds);
qxl0 = qxl;
@@ -147,7 +147,8 @@ void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext);
/* qxl-render.c */
void qxl_render_resize(PCIQXLDevice *qxl);
-void qxl_render_update(PCIQXLDevice *qxl);
+void qxl_render_update(PCIQXLDevice *qxl, const char *filename, bool async,
+ vga_hw_screen_dump_async_cb_ptr cb, void *cb_opaque);
void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext);
void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie);
void qxl_render_update_area_bh(void *opaque);
@@ -62,6 +62,11 @@ typedef struct QXLCookie {
struct {
QXLRect area;
int redraw;
+ char *filename;
+ QEMUBH *ppm_save_bh;
+ bool async;
+ vga_hw_screen_dump_async_cb_ptr cb;
+ void *cb_opaque;
} render;
} u;
} QXLCookie;
Uses the newly introduced hw_screen_dump_async. Now that the deadlock with virt-manager is fixed we need to call ppm_save in a bh, with the new command we can notify virt-manager using the SCREEN_DUMP_COMPLETE event. Signed-off-by: Alon Levy <alevy@redhat.com> --- This needs to go on top of [PATCH v3 0/3] screendump async command http://patchwork.ozlabs.org/patch/144706/ http://patchwork.ozlabs.org/patch/144705/ http://patchwork.ozlabs.org/patch/144704/ (the patchwork ids are the reverse of the patch order, wierd). hw/qxl-render.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++-- hw/qxl.c | 36 +++++++++++++++++++---- hw/qxl.h | 3 +- ui/spice-display.h | 5 +++ 4 files changed, 112 insertions(+), 11 deletions(-)