From b5e751b51f14cffada6be647aa576b79f1755d7e Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 19 Feb 2016 09:23:03 +0100 Subject: [PATCH 1/8] spice: init dcl before registering qxl interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this spice might callback into qemu before ssd->dcl.con is initialized, resulting in a segfault due to NULL pointer dereference. Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau --- ui/spice-display.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/spice-display.c b/ui/spice-display.c index 8a5b3258bd..cdbc78d81a 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -775,14 +775,14 @@ static void qemu_spice_display_init_one(QemuConsole *con) qemu_spice_display_init_common(ssd); + ssd->dcl.ops = &display_listener_ops; + ssd->dcl.con = con; + ssd->qxl.base.sif = &dpy_interface.base; qemu_spice_add_display_interface(&ssd->qxl, con); assert(ssd->worker); - qemu_spice_create_host_memslot(ssd); - ssd->dcl.ops = &display_listener_ops; - ssd->dcl.con = con; register_displaychangelistener(&ssd->dcl); } From 014cb152b8219a2cd31ce6443f8708386ded751b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 3 Dec 2015 12:56:34 +0100 Subject: [PATCH 2/8] configure: add dma-buf support detection. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set CONFIG_OPENGL_DMABUF in case both mesa and libepoxy are new enough to have support for dma-buf import/export. Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau --- configure | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 0aa249b788..0c0472a7da 100755 --- a/configure +++ b/configure @@ -279,6 +279,7 @@ smartcard="" libusb="" usb_redir="" opengl="" +opengl_dmabuf="no" zlib="yes" lzo="" snappy="" @@ -3274,7 +3275,7 @@ libs_softmmu="$libs_softmmu $fdt_libs" # opengl probe (for sdl2, gtk, milkymist-tmu2) if test "$opengl" != "no" ; then - opengl_pkgs="epoxy" + opengl_pkgs="epoxy libdrm gbm" if $pkg_config $opengl_pkgs x11; then opengl_cflags="$($pkg_config --cflags $opengl_pkgs) $x11_cflags" opengl_libs="$($pkg_config --libs $opengl_pkgs) $x11_libs" @@ -3292,6 +3293,18 @@ if test "$opengl" != "no" ; then fi fi +if test "$opengl" = "yes"; then + cat > $TMPC << EOF +#include +#ifndef EGL_MESA_image_dma_buf_export +# error mesa/epoxy lacks support for dmabufs (mesa 10.6+) +#endif +int main(void) { return 0; } +EOF + if compile_prog "" "" ; then + opengl_dmabuf=yes + fi +fi ########################################## # archipelago probe @@ -4752,6 +4765,7 @@ echo "smartcard support $smartcard" echo "libusb $libusb" echo "usb net redir $usb_redir" echo "OpenGL support $opengl" +echo "OpenGL dmabufs $opengl_dmabuf" echo "libiscsi support $libiscsi" echo "libnfs support $libnfs" echo "build guest agent $guest_agent" @@ -5050,6 +5064,7 @@ if test "$gtk" = "yes" ; then echo "CONFIG_GTK=y" >> $config_host_mak echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak + echo "GTK_LIBS=$gtk_libs" >> $config_host_mak if test "$gtk_gl" = "yes" ; then echo "CONFIG_GTK_GL=y" >> $config_host_mak fi @@ -5158,6 +5173,9 @@ if test "$opengl" = "yes" ; then echo "CONFIG_OPENGL=y" >> $config_host_mak echo "OPENGL_CFLAGS=$opengl_cflags" >> $config_host_mak echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak + if test "$opengl_dmabuf" = "yes" ; then + echo "CONFIG_OPENGL_DMABUF=y" >> $config_host_mak + fi fi if test "$lzo" = "yes" ; then From 1e3165980c352f90d0f1295bfc4fbfa31193b13f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 12 Oct 2015 12:03:49 +0200 Subject: [PATCH 3/8] egl-helpers: add functions for render nodes and dma-buf passing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds helpers to open a drm render node and create a opengl context for it. Also add a helper to export a texture as dma-buf. Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau --- include/ui/egl-helpers.h | 13 ++++ ui/egl-helpers.c | 129 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h index 8c84398001..03fcf4bba2 100644 --- a/include/ui/egl-helpers.h +++ b/include/ui/egl-helpers.h @@ -3,10 +3,23 @@ #include #include +#include extern EGLDisplay *qemu_egl_display; extern EGLConfig qemu_egl_config; +#ifdef CONFIG_OPENGL_DMABUF + +extern int qemu_egl_rn_fd; +extern struct gbm_device *qemu_egl_rn_gbm_dev; +extern EGLContext qemu_egl_rn_ctx; + +int qemu_egl_rendernode_open(void); +int egl_rendernode_init(void); +int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc); + +#endif + EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win); int qemu_egl_init_dpy(EGLNativeDisplayType dpy, bool gles, bool debug); diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 4c83834615..54be44ccea 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -1,6 +1,8 @@ #include "qemu/osdep.h" #include +#include +#include "config-host.h" #include "ui/egl-helpers.h" EGLDisplay *qemu_egl_display; @@ -20,6 +22,133 @@ static int egl_debug; /* ---------------------------------------------------------------------- */ +#ifdef CONFIG_OPENGL_DMABUF + +int qemu_egl_rn_fd; +struct gbm_device *qemu_egl_rn_gbm_dev; +EGLContext qemu_egl_rn_ctx; + +int qemu_egl_rendernode_open(void) +{ + DIR *dir; + struct dirent *e; + int r, fd; + char *p; + + dir = opendir("/dev/dri"); + if (!dir) { + return -1; + } + + fd = -1; + while ((e = readdir(dir))) { + if (e->d_type != DT_CHR) { + continue; + } + + if (strncmp(e->d_name, "renderD", 7)) { + continue; + } + + r = asprintf(&p, "/dev/dri/%s", e->d_name); + if (r < 0) { + return -1; + } + + r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); + if (r < 0) { + free(p); + continue; + } + fd = r; + free(p); + break; + } + + closedir(dir); + if (fd < 0) { + return -1; + } + return fd; +} + +int egl_rendernode_init(void) +{ + qemu_egl_rn_fd = -1; + + qemu_egl_rn_fd = qemu_egl_rendernode_open(); + if (qemu_egl_rn_fd == -1) { + fprintf(stderr, "egl: no drm render node available\n"); + goto err; + } + + qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd); + if (!qemu_egl_rn_gbm_dev) { + fprintf(stderr, "egl: gbm_create_device failed\n"); + goto err; + } + + qemu_egl_init_dpy((EGLNativeDisplayType)qemu_egl_rn_gbm_dev, false, false); + + if (!epoxy_has_egl_extension(qemu_egl_display, + "EGL_KHR_surfaceless_context")) { + fprintf(stderr, "egl: EGL_KHR_surfaceless_context not supported\n"); + goto err; + } + if (!epoxy_has_egl_extension(qemu_egl_display, + "EGL_MESA_image_dma_buf_export")) { + fprintf(stderr, "egl: EGL_MESA_image_dma_buf_export not supported\n"); + goto err; + } + + qemu_egl_rn_ctx = qemu_egl_init_ctx(); + if (!qemu_egl_rn_ctx) { + fprintf(stderr, "egl: egl_init_ctx failed\n"); + goto err; + } + + return 0; + +err: + if (qemu_egl_rn_gbm_dev) { + gbm_device_destroy(qemu_egl_rn_gbm_dev); + } + if (qemu_egl_rn_fd != -1) { + close(qemu_egl_rn_fd); + } + + return -1; +} + +int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc) +{ + EGLImageKHR image; + EGLint num_planes, fd; + + image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(), + EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer)(unsigned long)tex_id, + NULL); + if (!image) { + return -1; + } + + eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc, + &num_planes, NULL); + if (num_planes != 1) { + eglDestroyImageKHR(qemu_egl_display, image); + return -1; + } + eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL); + eglDestroyImageKHR(qemu_egl_display, image); + + return fd; +} + +#endif /* CONFIG_OPENGL_DMABUF */ + +/* ---------------------------------------------------------------------- */ + EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win) { EGLSurface esurface; From 58c7b618f3889f53308142403117d5ae24bda9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 Dec 2015 17:08:10 +0100 Subject: [PATCH 4/8] spice: reset cursor on resize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spice server will clear the cursor on resize. QXL driver reset it after resize, however, virtio and other devices do not. Teach qemu to set it back. Signed-off-by: Marc-André Lureau Signed-off-by: Gerd Hoffmann --- ui/spice-display.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ui/spice-display.c b/ui/spice-display.c index cdbc78d81a..4e5c8a2d97 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -460,6 +460,13 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, memset(&ssd->dirty, 0, sizeof(ssd->dirty)); ssd->notify++; + + qemu_mutex_lock(&ssd->lock); + if (ssd->cursor) { + g_free(ssd->ptr_define); + ssd->ptr_define = qemu_spice_create_cursor_update(ssd, ssd->cursor, 0); + } + qemu_mutex_unlock(&ssd->lock); } static void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd) @@ -467,8 +474,6 @@ static void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd) if (ssd->cursor) { assert(ssd->dcl.con); dpy_cursor_define(ssd->dcl.con, ssd->cursor); - cursor_put(ssd->cursor); - ssd->cursor = NULL; } if (ssd->mouse_x != -1 && ssd->mouse_y != -1) { assert(ssd->dcl.con); @@ -750,6 +755,11 @@ static void display_mouse_define(DisplayChangeListener *dcl, SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); qemu_mutex_lock(&ssd->lock); + if (c) { + cursor_get(c); + } + cursor_put(ssd->cursor); + ssd->cursor = c; ssd->hot_x = c->hot_x; ssd->hot_y = c->hot_y; g_free(ssd->ptr_move); From 474114b7305cc1be7c2ee8ba5267be159a9d56e3 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 13 Oct 2015 15:39:34 +0200 Subject: [PATCH 5/8] spice: add opengl/virgl/dmabuf support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for dma-buf passing to spice. This makes virtio-gpu with 3d acceleration work with spice. Workflow: * virglrenderer renders the guest command stream into a texture. * qemu exports the texture as dma-buf and passes on that dma-buf to spice-server. * spice-server passes the dma-buf to spice-client, using unix socket file descriptor passing. * spice-client asks the window systems composer to render the dma-buf to the screen. Requires cutting edge spice (server) and spice-gtk (client) builds, from git master branch. Also requires libvirt managing your qemu instance, and using "virt-viewer --attach $guest". libvirt will connect spice-server and spice-client using unix sockets instead of tcp sockets then, which is required for file descriptor passing. Works for the local case (spice server and client on the same machine) only. Supporting remote too is planned (by feeding the dma-bufs into gpu-assisted video encoder), but not there yet. gl mode is turned off by default, use "-spice gl=on,$otherargs" to enable it. Signed-off-by: Marc-André Lureau Signed-off-by: Gerd Hoffmann --- include/ui/spice-display.h | 15 +++++ qemu-options.hx | 4 ++ ui/spice-core.c | 15 ++++- ui/spice-display.c | 110 ++++++++++++++++++++++++++++++++++++- 4 files changed, 140 insertions(+), 4 deletions(-) diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index b25328a6ba..48dc8c4990 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -24,6 +24,14 @@ #include "ui/console.h" #include "sysemu/sysemu.h" +#if defined(CONFIG_OPENGL_DMABUF) +# if SPICE_SERVER_VERSION >= 0x000d00 /* release 0.13.0 */ +# define HAVE_SPICE_GL 1 +# include "ui/egl-helpers.h" +# include "ui/egl-context.h" +# endif +#endif + #define NUM_MEMSLOTS 8 #define MEMSLOT_GENERATION_BITS 8 #define MEMSLOT_SLOT_BITS 8 @@ -50,6 +58,7 @@ enum { QXL_COOKIE_TYPE_IO, QXL_COOKIE_TYPE_RENDER_UPDATE_AREA, QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG, + QXL_COOKIE_TYPE_GL_DRAW_DONE, }; typedef struct QXLCookie { @@ -104,6 +113,12 @@ struct SimpleSpiceDisplay { QEMUCursor *cursor; int mouse_x, mouse_y; QEMUBH *cursor_bh; + +#ifdef HAVE_SPICE_GL + /* opengl rendering */ + QEMUBH *gl_unblock_bh; + int dmabuf_fd; +#endif }; struct SimpleSpiceUpdate { diff --git a/qemu-options.hx b/qemu-options.hx index 2f0465eeb1..f528405810 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1051,6 +1051,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice, " [,streaming-video=[off|all|filter]][,disable-copy-paste]\n" " [,disable-agent-file-xfer][,agent-mouse=[on|off]]\n" " [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n" + " [,gl=[on|off]]\n" " enable spice\n" " at least one of {port, tls-port} is mandatory\n", QEMU_ARCH_ALL) @@ -1142,6 +1143,9 @@ Enable/disable audio stream compression (using celt 0.5.1). Default is on. @item seamless-migration=[on|off] Enable/disable spice seamless migration. Default is off. +@item gl=[on|off] +Enable/disable OpenGL context. Default is off. + @end table ETEXI diff --git a/ui/spice-core.c b/ui/spice-core.c index 4dbd99ab19..5abec1721a 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -494,9 +494,14 @@ static QemuOptsList qemu_spice_opts = { },{ .name = "playback-compression", .type = QEMU_OPT_BOOL, - }, { + },{ .name = "seamless-migration", .type = QEMU_OPT_BOOL, +#ifdef HAVE_SPICE_GL + },{ + .name = "gl", + .type = QEMU_OPT_BOOL, +#endif }, { /* end of list */ } }, @@ -819,6 +824,14 @@ void qemu_spice_init(void) #if SPICE_SERVER_VERSION >= 0x000c02 qemu_spice_register_ports(); #endif + +#ifdef HAVE_SPICE_GL + if (qemu_opt_get_bool(opts, "gl", 0)) { + if (egl_rendernode_init() == 0) { + display_opengl = 1; + } + } +#endif } int qemu_spice_add_interface(SpiceBaseInstance *sin) diff --git a/ui/spice-display.c b/ui/spice-display.c index 4e5c8a2d97..904fb33b14 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -650,9 +650,23 @@ static void interface_update_area_complete(QXLInstance *sin, /* called from spice server thread context only */ static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) { - /* should never be called, used in qxl native mode only */ - fprintf(stderr, "%s: abort()\n", __func__); - abort(); + QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token; + + switch (cookie->type) { +#ifdef HAVE_SPICE_GL + case QXL_COOKIE_TYPE_GL_DRAW_DONE: + { + SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); + qemu_bh_schedule(ssd->gl_unblock_bh); + break; + } +#endif + default: + /* should never be called, used in qxl native mode only */ + fprintf(stderr, "%s: abort()\n", __func__); + abort(); + } + g_free(cookie); } static void interface_set_client_capabilities(QXLInstance *sin, @@ -779,6 +793,89 @@ static const DisplayChangeListenerOps display_listener_ops = { .dpy_cursor_define = display_mouse_define, }; +#ifdef HAVE_SPICE_GL + +static void qemu_spice_gl_block(SimpleSpiceDisplay *ssd, bool block) +{ + graphic_hw_gl_block(ssd->dcl.con, block); +} + +static void qemu_spice_gl_unblock_bh(void *opaque) +{ + SimpleSpiceDisplay *ssd = opaque; + + qemu_spice_gl_block(ssd, false); +} + +static QEMUGLContext qemu_spice_gl_create_context(DisplayChangeListener *dcl, + QEMUGLParams *params) +{ + eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + qemu_egl_rn_ctx); + return qemu_egl_create_context(dcl, params); +} + +static void qemu_spice_gl_scanout(DisplayChangeListener *dcl, + uint32_t tex_id, + bool y_0_top, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + EGLint stride = 0, fourcc = 0; + int fd = -1; + + if (tex_id) { + fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc); + if (fd < 0) { + fprintf(stderr, "%s: failed to get fd for texture\n", __func__); + return; + } + } + dprint(0, "%s: %dx%d (stride %d, fourcc 0x%x)\n", __func__, + w, h, stride, fourcc); + + assert(!tex_id || fd >= 0); + + /* note: spice server will close the fd */ + spice_qxl_gl_scanout(&ssd->qxl, fd, + surface_width(ssd->ds), + surface_height(ssd->ds), + stride, fourcc, y_0_top); +} + +static void qemu_spice_gl_update(DisplayChangeListener *dcl, + uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + uint64_t cookie; + + dprint(1, "%s\n", __func__); + qemu_spice_gl_block(ssd, true); + cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); + spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie); +} + +static const DisplayChangeListenerOps display_listener_gl_ops = { + .dpy_name = "spice-egl", + .dpy_gfx_update = display_update, + .dpy_gfx_switch = display_switch, + .dpy_gfx_check_format = qemu_pixman_check_format, + .dpy_refresh = display_refresh, + .dpy_mouse_set = display_mouse_set, + .dpy_cursor_define = display_mouse_define, + + .dpy_gl_ctx_create = qemu_spice_gl_create_context, + .dpy_gl_ctx_destroy = qemu_egl_destroy_context, + .dpy_gl_ctx_make_current = qemu_egl_make_context_current, + .dpy_gl_ctx_get_current = qemu_egl_get_current_context, + + .dpy_gl_scanout = qemu_spice_gl_scanout, + .dpy_gl_update = qemu_spice_gl_update, +}; + +#endif /* HAVE_SPICE_GL */ + static void qemu_spice_display_init_one(QemuConsole *con) { SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1); @@ -786,6 +883,13 @@ static void qemu_spice_display_init_one(QemuConsole *con) qemu_spice_display_init_common(ssd); ssd->dcl.ops = &display_listener_ops; +#ifdef HAVE_SPICE_GL + if (display_opengl) { + ssd->dcl.ops = &display_listener_gl_ops; + ssd->dmabuf_fd = -1; + ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd); + } +#endif ssd->dcl.con = con; ssd->qxl.base.sif = &dpy_interface.base; From 8e388e907b853ca42f5c8825042fa17b126d86de Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 19 Feb 2016 07:46:47 +0100 Subject: [PATCH 6/8] spice/gl: add unblock timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pure debug aid, print a warning in case unblocking doesn't happen within one second. Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau --- include/ui/spice-display.h | 1 + ui/spice-display.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index 48dc8c4990..69a222b543 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -117,6 +117,7 @@ struct SimpleSpiceDisplay { #ifdef HAVE_SPICE_GL /* opengl rendering */ QEMUBH *gl_unblock_bh; + QEMUTimer *gl_unblock_timer; int dmabuf_fd; #endif }; diff --git a/ui/spice-display.c b/ui/spice-display.c index 904fb33b14..d6e31e4037 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -797,6 +797,15 @@ static const DisplayChangeListenerOps display_listener_ops = { static void qemu_spice_gl_block(SimpleSpiceDisplay *ssd, bool block) { + uint64_t timeout; + + if (block) { + timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + timeout += 1000; /* one sec */ + timer_mod(ssd->gl_unblock_timer, timeout); + } else { + timer_del(ssd->gl_unblock_timer); + } graphic_hw_gl_block(ssd->dcl.con, block); } @@ -807,6 +816,11 @@ static void qemu_spice_gl_unblock_bh(void *opaque) qemu_spice_gl_block(ssd, false); } +static void qemu_spice_gl_block_timer(void *opaque) +{ + fprintf(stderr, "WARNING: spice: no gl-draw-done within one second\n"); +} + static QEMUGLContext qemu_spice_gl_create_context(DisplayChangeListener *dcl, QEMUGLParams *params) { @@ -888,6 +902,8 @@ static void qemu_spice_display_init_one(QemuConsole *con) ssd->dcl.ops = &display_listener_gl_ops; ssd->dmabuf_fd = -1; ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd); + ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME, + qemu_spice_gl_block_timer, ssd); } #endif ssd->dcl.con = con; From 22672a3798f1d705c2b1e14a246efd4a8e4bc661 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 16 Feb 2016 11:05:18 +0100 Subject: [PATCH 7/8] spice/gl: tweak debug messages. Adjust message levels, make messages more verbose. Signed-off-by: Gerd Hoffmann --- ui/spice-display.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ui/spice-display.c b/ui/spice-display.c index d6e31e4037..242ab5f468 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -568,7 +568,7 @@ static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext) static int interface_req_cmd_notification(QXLInstance *sin) { - dprint(1, "%s/%d:\n", __func__, sin->id); + dprint(2, "%s/%d:\n", __func__, sin->id); return 1; } @@ -621,7 +621,7 @@ static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext) static int interface_req_cursor_notification(QXLInstance *sin) { - dprint(1, "%s:\n", __FUNCTION__); + dprint(2, "%s:\n", __func__); return 1; } @@ -845,9 +845,11 @@ static void qemu_spice_gl_scanout(DisplayChangeListener *dcl, fprintf(stderr, "%s: failed to get fd for texture\n", __func__); return; } + dprint(1, "%s: %dx%d (stride %d, fourcc 0x%x)\n", __func__, + w, h, stride, fourcc); + } else { + dprint(1, "%s: no texture (no framebuffer)\n", __func__); } - dprint(0, "%s: %dx%d (stride %d, fourcc 0x%x)\n", __func__, - w, h, stride, fourcc); assert(!tex_id || fd >= 0); @@ -864,7 +866,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); uint64_t cookie; - dprint(1, "%s\n", __func__); + dprint(2, "%s: %dx%d+%d+%d\n", __func__, w, h, x, y); qemu_spice_gl_block(ssd, true); cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie); From b82fc321bf02aaceea0beb4dfabb4ca86a9a5ff6 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Mon, 22 Feb 2016 17:17:32 +0000 Subject: [PATCH 8/8] Postcopy+spice: Pass spice migration data earlier Spice hooks the migration status changes to figure out when to transmit information to the new spice server; but the migration status in postcopy doesn't quite fit - the destination starts running before the end of the source migration. It's not a case of hanging off the migration status change to postcopy-active either, since that happens before we stop the guest CPU. Fix it by sending a notify just after sending the device state, and adding a flag that can be tested by the notify receiver. Symptom: spice handover doesn't work with the error: red_worker.c:11540:display_channel_wait_for_migrate_data: timeout Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Amit Shah Message-id: 1456161452-25318-1-git-send-email-dgilbert@redhat.com Signed-off-by: Gerd Hoffmann --- include/migration/migration.h | 4 ++++ migration/migration.c | 14 ++++++++++++++ ui/spice-core.c | 3 ++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/migration/migration.h b/include/migration/migration.h index 74684ad929..97622e4fd2 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -159,6 +159,8 @@ struct MigrationState /* Flag set once the migration has been asked to enter postcopy */ bool start_postcopy; + /* Flag set after postcopy has sent the device state */ + bool postcopy_after_devices; /* Flag set once the migration thread is running (and needs joining) */ bool migration_thread_running; @@ -212,6 +214,8 @@ bool migration_has_finished(MigrationState *); bool migration_has_failed(MigrationState *); /* True if outgoing migration has entered postcopy phase */ bool migration_in_postcopy(MigrationState *); +/* ...and after the device transmission */ +bool migration_in_postcopy_after_devices(MigrationState *); MigrationState *migrate_get_current(void); void migrate_compress_threads_create(void); diff --git a/migration/migration.c b/migration/migration.c index a64cfcdb07..fc5e50b0be 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -905,6 +905,11 @@ bool migration_in_postcopy(MigrationState *s) return (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); } +bool migration_in_postcopy_after_devices(MigrationState *s) +{ + return migration_in_postcopy(s) && s->postcopy_after_devices; +} + MigrationState *migrate_init(const MigrationParams *params) { MigrationState *s = migrate_get_current(); @@ -930,6 +935,7 @@ MigrationState *migrate_init(const MigrationParams *params) s->setup_time = 0; s->dirty_sync_count = 0; s->start_postcopy = false; + s->postcopy_after_devices = false; s->migration_thread_running = false; s->last_req_rb = NULL; @@ -1489,6 +1495,14 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running) goto fail_closefb; } qemu_fclose(fb); + + /* Send a notify to give a chance for anything that needs to happen + * at the transition to postcopy and after the device state; in particular + * spice needs to trigger a transition now + */ + ms->postcopy_after_devices = true; + notifier_list_notify(&migration_state_notifiers, ms); + ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - time_at_stop; qemu_mutex_unlock_iothread(); diff --git a/ui/spice-core.c b/ui/spice-core.c index 5abec1721a..a68a665a9a 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -573,7 +573,8 @@ static void migration_state_notifier(Notifier *notifier, void *data) if (migration_in_setup(s)) { spice_server_migrate_start(spice_server); - } else if (migration_has_finished(s)) { + } else if (migration_has_finished(s) || + migration_in_postcopy_after_devices(s)) { spice_server_migrate_end(spice_server, true); spice_have_target_host = false; } else if (migration_has_failed(s)) {