From 5c10dbb7b577370e86ff459973b06d530c3777cf Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 30 Oct 2015 12:09:56 +0100 Subject: [PATCH 01/20] buffer: make the Buffer capacity increase in powers of two This makes sure the number of reallocs is in O(log N). Signed-off-by: Peter Lieven Reviewed-by: Daniel P. Berrange Signed-off-by: Gerd Hoffmann Message-id: 1446203414-4013-2-git-send-email-kraxel@redhat.com [ rebased to util/buffer.c ] Signed-off-by: Gerd Hoffmann --- util/buffer.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/util/buffer.c b/util/buffer.c index cedd055680..7ddd693fa8 100644 --- a/util/buffer.c +++ b/util/buffer.c @@ -20,10 +20,13 @@ #include "qemu/buffer.h" +#define BUFFER_MIN_INIT_SIZE 4096 + void buffer_reserve(Buffer *buffer, size_t len) { if ((buffer->capacity - buffer->offset) < len) { - buffer->capacity += (len + 1024); + buffer->capacity = pow2ceil(buffer->offset + len); + buffer->capacity = MAX(buffer->capacity, BUFFER_MIN_INIT_SIZE); buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); } } From 810082d15c244b8b29470d3bb1c6b11fc9a40c25 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:09:57 +0100 Subject: [PATCH 02/20] buffer: add buffer_init Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-3-git-send-email-kraxel@redhat.com --- include/qemu/buffer.h | 12 ++++++++++++ util/buffer.c | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/qemu/buffer.h b/include/qemu/buffer.h index b380cec6fa..0710e165f2 100644 --- a/include/qemu/buffer.h +++ b/include/qemu/buffer.h @@ -34,11 +34,23 @@ typedef struct Buffer Buffer; */ struct Buffer { + char *name; size_t capacity; size_t offset; uint8_t *buffer; }; +/** + * buffer_init: + * @buffer: the buffer object + * @name: buffer name + * + * Optionally attach a name to the buffer, to make it easier + * to identify in debug traces. + */ +void buffer_init(Buffer *buffer, const char *name, ...) + GCC_FMT_ATTR(2, 3); + /** * buffer_reserve: * @buffer: the buffer object diff --git a/util/buffer.c b/util/buffer.c index 7ddd693fa8..12bf2d788b 100644 --- a/util/buffer.c +++ b/util/buffer.c @@ -22,6 +22,15 @@ #define BUFFER_MIN_INIT_SIZE 4096 +void buffer_init(Buffer *buffer, const char *name, ...) +{ + va_list ap; + + va_start(ap, name); + buffer->name = g_strdup_vprintf(name, ap); + va_end(ap); +} + void buffer_reserve(Buffer *buffer, size_t len) { if ((buffer->capacity - buffer->offset) < len) { @@ -49,9 +58,11 @@ void buffer_reset(Buffer *buffer) void buffer_free(Buffer *buffer) { g_free(buffer->buffer); + g_free(buffer->name); buffer->offset = 0; buffer->capacity = 0; buffer->buffer = NULL; + buffer->name = NULL; } void buffer_append(Buffer *buffer, const void *data, size_t len) From 4d1eb5fdb141c9100eb82e1dc7d4547fb1decd8b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:09:58 +0100 Subject: [PATCH 03/20] buffer: add buffer_move_empty Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel Berrange Message-id: 1446203414-4013-4-git-send-email-kraxel@redhat.com --- include/qemu/buffer.h | 10 ++++++++++ util/buffer.c | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/qemu/buffer.h b/include/qemu/buffer.h index 0710e165f2..f53ee9e6af 100644 --- a/include/qemu/buffer.h +++ b/include/qemu/buffer.h @@ -127,4 +127,14 @@ uint8_t *buffer_end(Buffer *buffer); */ gboolean buffer_empty(Buffer *buffer); +/** + * buffer_move_empty: + * @to: destination buffer object + * @from: source buffer object + * + * Moves buffer, without copying data. 'to' buffer must be empty. + * 'from' buffer is empty and zero-sized on return. + */ +void buffer_move_empty(Buffer *to, Buffer *from); + #endif /* QEMU_BUFFER_H__ */ diff --git a/util/buffer.c b/util/buffer.c index 12bf2d788b..c7a39ec5da 100644 --- a/util/buffer.c +++ b/util/buffer.c @@ -77,3 +77,17 @@ void buffer_advance(Buffer *buffer, size_t len) (buffer->offset - len)); buffer->offset -= len; } + +void buffer_move_empty(Buffer *to, Buffer *from) +{ + assert(to->offset == 0); + + g_free(to->buffer); + to->offset = from->offset; + to->capacity = from->capacity; + to->buffer = from->buffer; + + from->offset = 0; + from->capacity = 0; + from->buffer = NULL; +} From 830a9583206a051c240b74c3f688a015dc5d2967 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:09:59 +0100 Subject: [PATCH 04/20] buffer: add buffer_move Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-5-git-send-email-kraxel@redhat.com --- include/qemu/buffer.h | 10 ++++++++++ util/buffer.c | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/include/qemu/buffer.h b/include/qemu/buffer.h index f53ee9e6af..1358df1aac 100644 --- a/include/qemu/buffer.h +++ b/include/qemu/buffer.h @@ -137,4 +137,14 @@ gboolean buffer_empty(Buffer *buffer); */ void buffer_move_empty(Buffer *to, Buffer *from); +/** + * buffer_move: + * @to: destination buffer object + * @from: source buffer object + * + * Moves buffer, copying data (unless 'to' buffer happens to be empty). + * 'from' buffer is empty and zero-sized on return. + */ +void buffer_move(Buffer *to, Buffer *from); + #endif /* QEMU_BUFFER_H__ */ diff --git a/util/buffer.c b/util/buffer.c index c7a39ec5da..e8f798e620 100644 --- a/util/buffer.c +++ b/util/buffer.c @@ -91,3 +91,19 @@ void buffer_move_empty(Buffer *to, Buffer *from) from->capacity = 0; from->buffer = NULL; } + +void buffer_move(Buffer *to, Buffer *from) +{ + if (to->offset == 0) { + buffer_move_empty(to, from); + return; + } + + buffer_reserve(to, from->offset); + buffer_append(to, from->buffer, from->offset); + + g_free(from->buffer); + from->offset = 0; + from->capacity = 0; + from->buffer = NULL; +} From 1ff36b5d4d00a4e3633eb960bf2be562f5e47dbf Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:10:00 +0100 Subject: [PATCH 05/20] buffer: add buffer_shrink Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-6-git-send-email-kraxel@redhat.com --- include/qemu/buffer.h | 10 ++++++++++ util/buffer.c | 20 +++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/include/qemu/buffer.h b/include/qemu/buffer.h index 1358df1aac..0a69b3a972 100644 --- a/include/qemu/buffer.h +++ b/include/qemu/buffer.h @@ -51,6 +51,16 @@ struct Buffer { void buffer_init(Buffer *buffer, const char *name, ...) GCC_FMT_ATTR(2, 3); +/** + * buffer_shrink: + * @buffer: the buffer object + * + * Try to shrink the buffer. Checks current buffer capacity and size + * and reduces capacity in case only a fraction of the buffer is + * actually used. + */ +void buffer_shrink(Buffer *buffer); + /** * buffer_reserve: * @buffer: the buffer object diff --git a/util/buffer.c b/util/buffer.c index e8f798e620..234e33d5c1 100644 --- a/util/buffer.c +++ b/util/buffer.c @@ -20,7 +20,8 @@ #include "qemu/buffer.h" -#define BUFFER_MIN_INIT_SIZE 4096 +#define BUFFER_MIN_INIT_SIZE 4096 +#define BUFFER_MIN_SHRINK_SIZE 65536 void buffer_init(Buffer *buffer, const char *name, ...) { @@ -31,6 +32,23 @@ void buffer_init(Buffer *buffer, const char *name, ...) va_end(ap); } +void buffer_shrink(Buffer *buffer) +{ + /* + * Only shrink in case the used size is *much* smaller than the + * capacity, to avoid bumping up & down the buffers all the time. + * realloc() isn't exactly cheap ... + */ + if (buffer->offset < (buffer->capacity >> 3) && + buffer->capacity > BUFFER_MIN_SHRINK_SIZE) { + return; + } + + buffer->capacity = pow2ceil(buffer->offset); + buffer->capacity = MAX(buffer->capacity, BUFFER_MIN_SHRINK_SIZE); + buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); +} + void buffer_reserve(Buffer *buffer, size_t len) { if ((buffer->capacity - buffer->offset) < len) { From d2b90718d25ed6dc8a2bb7f06504e6500dcc7bae Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:10:01 +0100 Subject: [PATCH 06/20] buffer: add tracing Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-7-git-send-email-kraxel@redhat.com --- trace-events | 6 ++++++ util/buffer.c | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/trace-events b/trace-events index 72136b9846..e67ad81c04 100644 --- a/trace-events +++ b/trace-events @@ -1387,6 +1387,12 @@ ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "ad prep_io_800_writeb(uint32_t addr, uint32_t val) "0x%08" PRIx32 " => 0x%02" PRIx32 prep_io_800_readb(uint32_t addr, uint32_t retval) "0x%08" PRIx32 " <= 0x%02" PRIx32 +# io/buffer.c +buffer_resize(const char *buf, size_t olen, size_t len) "%s: old %zd, new %zd" +buffer_move_empty(const char *buf, size_t len, const char *from) "%s: %zd bytes from %s" +buffer_move(const char *buf, size_t len, const char *from) "%s: %zd bytes from %s" +buffer_free(const char *buf, size_t len) "%s: capacity %zd" + # util/hbitmap.c hbitmap_iter_skip_words(const void *hb, void *hbi, uint64_t pos, unsigned long cur) "hb %p hbi %p pos %"PRId64" cur 0x%lx" hbitmap_reset(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 diff --git a/util/buffer.c b/util/buffer.c index 234e33d5c1..ae2907ebd4 100644 --- a/util/buffer.c +++ b/util/buffer.c @@ -19,6 +19,7 @@ */ #include "qemu/buffer.h" +#include "trace.h" #define BUFFER_MIN_INIT_SIZE 4096 #define BUFFER_MIN_SHRINK_SIZE 65536 @@ -34,6 +35,8 @@ void buffer_init(Buffer *buffer, const char *name, ...) void buffer_shrink(Buffer *buffer) { + size_t old; + /* * Only shrink in case the used size is *much* smaller than the * capacity, to avoid bumping up & down the buffers all the time. @@ -44,17 +47,25 @@ void buffer_shrink(Buffer *buffer) return; } + old = buffer->capacity; buffer->capacity = pow2ceil(buffer->offset); buffer->capacity = MAX(buffer->capacity, BUFFER_MIN_SHRINK_SIZE); buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); + trace_buffer_resize(buffer->name ?: "unnamed", + old, buffer->capacity); } void buffer_reserve(Buffer *buffer, size_t len) { + size_t old; + if ((buffer->capacity - buffer->offset) < len) { + old = buffer->capacity; buffer->capacity = pow2ceil(buffer->offset + len); buffer->capacity = MAX(buffer->capacity, BUFFER_MIN_INIT_SIZE); buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); + trace_buffer_resize(buffer->name ?: "unnamed", + old, buffer->capacity); } } @@ -75,6 +86,7 @@ void buffer_reset(Buffer *buffer) void buffer_free(Buffer *buffer) { + trace_buffer_free(buffer->name ?: "unnamed", buffer->capacity); g_free(buffer->buffer); g_free(buffer->name); buffer->offset = 0; @@ -98,6 +110,9 @@ void buffer_advance(Buffer *buffer, size_t len) void buffer_move_empty(Buffer *to, Buffer *from) { + trace_buffer_move_empty(to->name ?: "unnamed", + from->offset, + from->name ?: "unnamed"); assert(to->offset == 0); g_free(to->buffer); @@ -117,6 +132,9 @@ void buffer_move(Buffer *to, Buffer *from) return; } + trace_buffer_move(to->name ?: "unnamed", + from->offset, + from->name ?: "unnamed"); buffer_reserve(to, from->offset); buffer_append(to, from->buffer, from->offset); From 543b95801f98ab2cb7413c39779fd5b7f363ce3d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:10:02 +0100 Subject: [PATCH 07/20] vnc: attach names to buffers Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-8-git-send-email-kraxel@redhat.com --- ui/vnc-jobs.c | 3 +++ ui/vnc.c | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 22c9abce55..2e6c15faa2 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -218,6 +218,8 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) int n_rectangles; int saved_offset; + buffer_init(&vs.output, "vnc-worker-output"); + vnc_lock_queue(queue); while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) { qemu_cond_wait(&queue->cond, &queue->mutex); @@ -302,6 +304,7 @@ static VncJobQueue *vnc_queue_init(void) qemu_cond_init(&queue->cond); qemu_mutex_init(&queue->mutex); + buffer_init(&queue->buffer, "vnc-job-queue"); QTAILQ_INIT(&queue->jobs); return queue; } diff --git a/ui/vnc.c b/ui/vnc.c index a47f2b382c..cdafd09d90 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2988,6 +2988,26 @@ static void vnc_connect(VncDisplay *vd, int csock, vs->csock = csock; vs->vd = vd; + buffer_init(&vs->input, "vnc-input/%d", csock); + buffer_init(&vs->output, "vnc-output/%d", csock); + buffer_init(&vs->ws_input, "vnc-ws_input/%d", csock); + buffer_init(&vs->ws_output, "vnc-ws_output/%d", csock); + buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%d", csock); + + buffer_init(&vs->tight.tight, "vnc-tight/%d", csock); + buffer_init(&vs->tight.zlib, "vnc-tight-zlib/%d", csock); + buffer_init(&vs->tight.gradient, "vnc-tight-gradient/%d", csock); +#ifdef CONFIG_VNC_JPEG + buffer_init(&vs->tight.jpeg, "vnc-tight-jpeg/%d", csock); +#endif +#ifdef CONFIG_VNC_PNG + buffer_init(&vs->tight.png, "vnc-tight-png/%d", csock); +#endif + buffer_init(&vs->zlib.zlib, "vnc-zlib/%d", csock); + buffer_init(&vs->zrle.zrle, "vnc-zrle/%d", csock); + buffer_init(&vs->zrle.fb, "vnc-zrle-fb/%d", csock); + buffer_init(&vs->zrle.zlib, "vnc-zrle-zlib/%d", csock); + if (skipauth) { vs->auth = VNC_AUTH_NONE; vs->subauth = VNC_AUTH_INVALID; From 8305f917c1bc86b1ead0fa9197b5443a4bd611f3 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:10:03 +0100 Subject: [PATCH 08/20] vnc: kill jobs queue buffer Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-9-git-send-email-kraxel@redhat.com --- ui/vnc-jobs.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 2e6c15faa2..329d13ea3a 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -54,7 +54,6 @@ struct VncJobQueue { QemuCond cond; QemuMutex mutex; QemuThread thread; - Buffer buffer; bool exit; QTAILQ_HEAD(, VncJob) jobs; }; @@ -193,7 +192,6 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->zlib = orig->zlib; local->hextile = orig->hextile; local->zrle = orig->zrle; - local->output = queue->buffer; local->csock = -1; /* Don't do any network work on this thread */ buffer_reset(&local->output); @@ -206,8 +204,6 @@ static void vnc_async_encoding_end(VncState *orig, VncState *local) orig->hextile = local->hextile; orig->zrle = local->zrle; orig->lossy_rect = local->lossy_rect; - - queue->buffer = local->output; } static int vnc_worker_thread_loop(VncJobQueue *queue) @@ -304,7 +300,6 @@ static VncJobQueue *vnc_queue_init(void) qemu_cond_init(&queue->cond); qemu_mutex_init(&queue->mutex); - buffer_init(&queue->buffer, "vnc-job-queue"); QTAILQ_INIT(&queue->jobs); return queue; } @@ -313,7 +308,6 @@ static void vnc_queue_clear(VncJobQueue *q) { qemu_cond_destroy(&queue->cond); qemu_mutex_destroy(&queue->mutex); - buffer_free(&queue->buffer); g_free(q); queue = NULL; /* Unset global queue */ } From d90340115a3569caa72063775ff927f4dc353551 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:10:04 +0100 Subject: [PATCH 09/20] vnc-jobs: move buffer reset, use new buffer move Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-10-git-send-email-kraxel@redhat.com --- ui/vnc-jobs.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 329d13ea3a..fd9ed39f26 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -29,6 +29,7 @@ #include "vnc.h" #include "vnc-jobs.h" #include "qemu/sockets.h" +#include "qemu/main-loop.h" #include "block/aio.h" /* @@ -165,8 +166,11 @@ void vnc_jobs_consume_buffer(VncState *vs) vnc_lock_output(vs); if (vs->jobs_buffer.offset) { - vnc_write(vs, vs->jobs_buffer.buffer, vs->jobs_buffer.offset); - buffer_reset(&vs->jobs_buffer); + if (vs->csock != -1 && buffer_empty(&vs->output)) { + qemu_set_fd_handler(vs->csock, vnc_client_read, + vnc_client_write, vs); + } + buffer_move(&vs->output, &vs->jobs_buffer); } flush = vs->csock != -1 && vs->abort != true; vnc_unlock_output(vs); @@ -193,8 +197,6 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->hextile = orig->hextile; local->zrle = orig->zrle; local->csock = -1; /* Don't do any network work on this thread */ - - buffer_reset(&local->output); } static void vnc_async_encoding_end(VncState *orig, VncState *local) @@ -272,14 +274,13 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) vnc_lock_output(job->vs); if (job->vs->csock != -1) { - buffer_reserve(&job->vs->jobs_buffer, vs.output.offset); - buffer_append(&job->vs->jobs_buffer, vs.output.buffer, - vs.output.offset); + buffer_move(&job->vs->jobs_buffer, &vs.output); /* Copy persistent encoding data */ vnc_async_encoding_end(job->vs, &vs); qemu_bh_schedule(job->vs->bh); } else { + buffer_reset(&vs.output); /* Copy persistent encoding data */ vnc_async_encoding_end(job->vs, &vs); } From e081aae5ae01f5ff695ba9fee4e622053d8e4bfe Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:10:05 +0100 Subject: [PATCH 10/20] vnc: zap dead code Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-11-git-send-email-kraxel@redhat.com --- ui/vnc.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index cdafd09d90..49de39d0d6 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -732,10 +732,6 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, width, height, NULL, 0); /* guest surface */ -#if 0 /* FIXME */ - if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel) - console_color_init(ds); -#endif qemu_pixman_image_unref(vd->guest.fb); vd->guest.fb = pixman_image_ref(surface->image); vd->guest.format = surface->format; From d05959c2e111858bb83c40ae5d8b8c10964b7bb0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:10:06 +0100 Subject: [PATCH 11/20] vnc: add vnc_width+vnc_height helpers Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-12-git-send-email-kraxel@redhat.com --- ui/vnc.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index 49de39d0d6..9ffad16056 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -615,6 +615,17 @@ static void framebuffer_update_request(VncState *vs, int incremental, static void vnc_refresh(DisplayChangeListener *dcl); static int vnc_refresh_server_surface(VncDisplay *vd); +static int vnc_width(VncDisplay *vd) +{ + return MIN(VNC_MAX_WIDTH, ROUND_UP(surface_width(vd->ds), + VNC_DIRTY_PIXELS_PER_BIT)); +} + +static int vnc_height(VncDisplay *vd) +{ + return MIN(VNC_MAX_HEIGHT, surface_height(vd->ds)); +} + static void vnc_set_area_dirty(DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT), int width, int height, @@ -725,9 +736,8 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, /* server surface */ qemu_pixman_image_unref(vd->server); vd->ds = surface; - width = MIN(VNC_MAX_WIDTH, ROUND_UP(surface_width(vd->ds), - VNC_DIRTY_PIXELS_PER_BIT)); - height = MIN(VNC_MAX_HEIGHT, surface_height(vd->ds)); + width = vnc_width(vd); + height = vnc_height(vd); vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, width, height, NULL, 0); From 453f842bc4cab49f10c267cff9ad3cf657265d49 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:10:07 +0100 Subject: [PATCH 12/20] vnc: factor out vnc_update_server_surface Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-13-git-send-email-kraxel@redhat.com --- ui/vnc.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index 9ffad16056..58a66b5576 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -724,6 +724,17 @@ void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y) return ptr; } +static void vnc_update_server_surface(VncDisplay *vd) +{ + qemu_pixman_image_unref(vd->server); + vd->server = NULL; + + vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, + vnc_width(vd), + vnc_height(vd), + NULL, 0); +} + static void vnc_dpy_switch(DisplayChangeListener *dcl, DisplaySurface *surface) { @@ -732,19 +743,17 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, int width, height; vnc_abort_display_jobs(vd); + vd->ds = surface; /* server surface */ - qemu_pixman_image_unref(vd->server); - vd->ds = surface; - width = vnc_width(vd); - height = vnc_height(vd); - vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, - width, height, NULL, 0); + vnc_update_server_surface(vd); /* guest surface */ qemu_pixman_image_unref(vd->guest.fb); vd->guest.fb = pixman_image_ref(surface->image); vd->guest.format = surface->format; + width = vnc_width(vd); + height = vnc_height(vd); memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty)); vnc_set_area_dirty(vd->guest.dirty, width, height, 0, 0, width, height); From f7b3d68c95bc4f8915a3d084360aa07c7f4e4717 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:10:08 +0100 Subject: [PATCH 13/20] vnc: use vnc_{width,height} in vnc_set_area_dirty Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-14-git-send-email-kraxel@redhat.com --- ui/vnc.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index 58a66b5576..eb1fcb30a1 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -628,8 +628,12 @@ static int vnc_height(VncDisplay *vd) static void vnc_set_area_dirty(DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT), - int width, int height, - int x, int y, int w, int h) { + VncDisplay *vd, + int x, int y, int w, int h) +{ + int width = vnc_width(vd); + int height = vnc_height(vd); + /* this is needed this to ensure we updated all affected * blocks if x % VNC_DIRTY_PIXELS_PER_BIT != 0 */ w += (x % VNC_DIRTY_PIXELS_PER_BIT); @@ -651,10 +655,8 @@ static void vnc_dpy_update(DisplayChangeListener *dcl, { VncDisplay *vd = container_of(dcl, VncDisplay, dcl); struct VncSurface *s = &vd->guest; - int width = pixman_image_get_width(vd->server); - int height = pixman_image_get_height(vd->server); - vnc_set_area_dirty(s->dirty, width, height, x, y, w, h); + vnc_set_area_dirty(s->dirty, vd, x, y, w, h); } void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, @@ -755,7 +757,7 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, width = vnc_width(vd); height = vnc_height(vd); memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty)); - vnc_set_area_dirty(vd->guest.dirty, width, height, 0, 0, + vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0, width, height); QTAILQ_FOREACH(vs, &vd->clients, next) { @@ -765,7 +767,7 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, vnc_cursor_define(vs); } memset(vs->dirty, 0x00, sizeof(vs->dirty)); - vnc_set_area_dirty(vs->dirty, width, height, 0, 0, + vnc_set_area_dirty(vs->dirty, vd, 0, 0, width, height); } } @@ -2021,9 +2023,6 @@ static void ext_key_event(VncState *vs, int down, static void framebuffer_update_request(VncState *vs, int incremental, int x, int y, int w, int h) { - int width = pixman_image_get_width(vs->vd->server); - int height = pixman_image_get_height(vs->vd->server); - vs->need_update = 1; if (incremental) { @@ -2031,7 +2030,7 @@ static void framebuffer_update_request(VncState *vs, int incremental, } vs->force_update = 1; - vnc_set_area_dirty(vs->dirty, width, height, x, y, w, h); + vnc_set_area_dirty(vs->dirty, vs->vd, x, y, w, h); } static void send_ext_key_event_ack(VncState *vs) From c7628bff4138ce906a3620d12e0820c1cf6c140d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:10:09 +0100 Subject: [PATCH 14/20] vnc: only alloc server surface with clients connected Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-15-git-send-email-kraxel@redhat.com --- ui/vnc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ui/vnc.c b/ui/vnc.c index eb1fcb30a1..a8cce5a45b 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -731,6 +731,10 @@ static void vnc_update_server_surface(VncDisplay *vd) qemu_pixman_image_unref(vd->server); vd->server = NULL; + if (QTAILQ_EMPTY(&vd->clients)) { + return; + } + vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, vnc_width(vd), vnc_height(vd), @@ -1241,6 +1245,10 @@ void vnc_disconnect_finish(VncState *vs) if (vs->initialized) { QTAILQ_REMOVE(&vs->vd->clients, vs, next); qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier); + if (QTAILQ_EMPTY(&vs->vd->clients)) { + /* last client gone */ + vnc_update_server_surface(vs->vd); + } } if (vs->vd->lock_key_sync) @@ -3079,6 +3087,7 @@ void vnc_init_state(VncState *vs) { vs->initialized = true; VncDisplay *vd = vs->vd; + bool first_client = QTAILQ_EMPTY(&vd->clients); vs->last_x = -1; vs->last_y = -1; @@ -3092,6 +3101,9 @@ void vnc_init_state(VncState *vs) vs->bh = qemu_bh_new(vnc_jobs_bh, vs); QTAILQ_INSERT_TAIL(&vd->clients, vs, next); + if (first_client) { + vnc_update_server_surface(vd); + } graphic_hw_update(vd->dcl.con); From 2e0c90af0a33451498d333d72c06e5429c7cd168 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 30 Oct 2015 12:10:10 +0100 Subject: [PATCH 15/20] vnc: fix local state init Signed-off-by: Gerd Hoffmann Reviewed-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-16-git-send-email-kraxel@redhat.com --- ui/vnc-jobs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index fd9ed39f26..12389cc2e8 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -185,6 +185,9 @@ void vnc_jobs_consume_buffer(VncState *vs) */ static void vnc_async_encoding_start(VncState *orig, VncState *local) { + buffer_init(&local->output, "vnc-worker-output"); + local->csock = -1; /* Don't do any network work on this thread */ + local->vnc_encoding = orig->vnc_encoding; local->features = orig->features; local->vd = orig->vd; @@ -196,7 +199,6 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->zlib = orig->zlib; local->hextile = orig->hextile; local->zrle = orig->zrle; - local->csock = -1; /* Don't do any network work on this thread */ } static void vnc_async_encoding_end(VncState *orig, VncState *local) @@ -212,12 +214,10 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) { VncJob *job; VncRectEntry *entry, *tmp; - VncState vs; + VncState vs = {}; int n_rectangles; int saved_offset; - buffer_init(&vs.output, "vnc-worker-output"); - vnc_lock_queue(queue); while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) { qemu_cond_wait(&queue->cond, &queue->mutex); From c3d6899c5e67dfd7ff195eccc10541f3b7e141a7 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 30 Oct 2015 12:10:11 +0100 Subject: [PATCH 16/20] vnc: recycle empty vs->output buffer If the vs->output buffer is empty it will be dropped by the next qio_buffer_move_empty in vnc_jobs_consume_buffer anyway. So reuse the allocated buffer from this buffer in the worker thread where we otherwise would start with an empty (unallocated buffer). Signed-off-by: Peter Lieven Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-17-git-send-email-kraxel@redhat.com [ added a comment describing the non-obvious optimization ] Signed-off-by: Gerd Hoffmann --- ui/vnc-jobs.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 12389cc2e8..08f0163fbc 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -235,6 +235,14 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) vnc_unlock_output(job->vs); goto disconnected; } + if (buffer_empty(&job->vs->output)) { + /* + * Looks like a NOP as it obviously moves no data. But it + * moves the empty buffer, so we don't have to malloc a new + * one for vs.output + */ + buffer_move_empty(&vs.output, &job->vs->output); + } vnc_unlock_output(job->vs); /* Make a local copy of vs and switch output buffers */ From fd95243372f7657c2d24aed269e3be01bed1b87c Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 30 Oct 2015 12:10:12 +0100 Subject: [PATCH 17/20] buffer: factor out buffer_req_size Signed-off-by: Peter Lieven Signed-off-by: Gerd Hoffmann Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-18-git-send-email-kraxel@redhat.com --- util/buffer.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/util/buffer.c b/util/buffer.c index ae2907ebd4..31f1d9f286 100644 --- a/util/buffer.c +++ b/util/buffer.c @@ -24,6 +24,13 @@ #define BUFFER_MIN_INIT_SIZE 4096 #define BUFFER_MIN_SHRINK_SIZE 65536 +static size_t buffer_req_size(Buffer *buffer, size_t len) +{ + return MAX(BUFFER_MIN_INIT_SIZE, + pow2ceil(buffer->offset + len)); +} + + void buffer_init(Buffer *buffer, const char *name, ...) { va_list ap; @@ -61,8 +68,7 @@ void buffer_reserve(Buffer *buffer, size_t len) if ((buffer->capacity - buffer->offset) < len) { old = buffer->capacity; - buffer->capacity = pow2ceil(buffer->offset + len); - buffer->capacity = MAX(buffer->capacity, BUFFER_MIN_INIT_SIZE); + buffer->capacity = buffer_req_size(buffer, len); buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); trace_buffer_resize(buffer->name ?: "unnamed", old, buffer->capacity); From 4ec5ba151ff3f2ac8dc44dabd058eca5846654a6 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 30 Oct 2015 12:10:13 +0100 Subject: [PATCH 18/20] buffer: factor out buffer_adj_size Signed-off-by: Peter Lieven Signed-off-by: Gerd Hoffmann Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-19-git-send-email-kraxel@redhat.com --- util/buffer.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/util/buffer.c b/util/buffer.c index 31f1d9f286..fe5a44e708 100644 --- a/util/buffer.c +++ b/util/buffer.c @@ -30,6 +30,14 @@ static size_t buffer_req_size(Buffer *buffer, size_t len) pow2ceil(buffer->offset + len)); } +static void buffer_adj_size(Buffer *buffer, size_t len) +{ + size_t old = buffer->capacity; + buffer->capacity = buffer_req_size(buffer, len); + buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); + trace_buffer_resize(buffer->name ?: "unnamed", + old, buffer->capacity); +} void buffer_init(Buffer *buffer, const char *name, ...) { @@ -42,8 +50,6 @@ void buffer_init(Buffer *buffer, const char *name, ...) void buffer_shrink(Buffer *buffer) { - size_t old; - /* * Only shrink in case the used size is *much* smaller than the * capacity, to avoid bumping up & down the buffers all the time. @@ -54,24 +60,13 @@ void buffer_shrink(Buffer *buffer) return; } - old = buffer->capacity; - buffer->capacity = pow2ceil(buffer->offset); - buffer->capacity = MAX(buffer->capacity, BUFFER_MIN_SHRINK_SIZE); - buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); - trace_buffer_resize(buffer->name ?: "unnamed", - old, buffer->capacity); + buffer_adj_size(buffer, 0); } void buffer_reserve(Buffer *buffer, size_t len) { - size_t old; - if ((buffer->capacity - buffer->offset) < len) { - old = buffer->capacity; - buffer->capacity = buffer_req_size(buffer, len); - buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); - trace_buffer_resize(buffer->name ?: "unnamed", - old, buffer->capacity); + buffer_adj_size(buffer, len); } } From f14c3d85b003d8614144ae67a26157667c1e1245 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 30 Oct 2015 12:10:14 +0100 Subject: [PATCH 19/20] buffer: allow a buffer to shrink gracefully the idea behind this patch is to allow the buffer to shrink, but make this a seldom operation. The buffers average size is measured exponentionally smoothed with am alpha of 1/128. Signed-off-by: Peter Lieven Signed-off-by: Gerd Hoffmann Reviewed-by: Daniel P. Berrange Message-id: 1446203414-4013-20-git-send-email-kraxel@redhat.com --- include/qemu/buffer.h | 1 + util/buffer.c | 41 +++++++++++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/include/qemu/buffer.h b/include/qemu/buffer.h index 0a69b3a972..dead9b77e1 100644 --- a/include/qemu/buffer.h +++ b/include/qemu/buffer.h @@ -37,6 +37,7 @@ struct Buffer { char *name; size_t capacity; size_t offset; + uint64_t avg_size; uint8_t *buffer; }; diff --git a/util/buffer.c b/util/buffer.c index fe5a44e708..8b27c08aac 100644 --- a/util/buffer.c +++ b/util/buffer.c @@ -24,6 +24,11 @@ #define BUFFER_MIN_INIT_SIZE 4096 #define BUFFER_MIN_SHRINK_SIZE 65536 +/* define the factor alpha for the expentional smoothing + * that is used in the average size calculation. a shift + * of 7 results in an alpha of 1/2^7. */ +#define BUFFER_AVG_SIZE_SHIFT 7 + static size_t buffer_req_size(Buffer *buffer, size_t len) { return MAX(BUFFER_MIN_INIT_SIZE, @@ -37,6 +42,11 @@ static void buffer_adj_size(Buffer *buffer, size_t len) buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); trace_buffer_resize(buffer->name ?: "unnamed", old, buffer->capacity); + + /* make it even harder for the buffer to shrink, reset average size + * to currenty capacity if it is larger than the average. */ + buffer->avg_size = MAX(buffer->avg_size, + buffer->capacity << BUFFER_AVG_SIZE_SHIFT); } void buffer_init(Buffer *buffer, const char *name, ...) @@ -48,16 +58,29 @@ void buffer_init(Buffer *buffer, const char *name, ...) va_end(ap); } +static uint64_t buffer_get_avg_size(Buffer *buffer) +{ + return buffer->avg_size >> BUFFER_AVG_SIZE_SHIFT; +} + void buffer_shrink(Buffer *buffer) { - /* - * Only shrink in case the used size is *much* smaller than the - * capacity, to avoid bumping up & down the buffers all the time. - * realloc() isn't exactly cheap ... - */ - if (buffer->offset < (buffer->capacity >> 3) && - buffer->capacity > BUFFER_MIN_SHRINK_SIZE) { - return; + size_t new; + + /* Calculate the average size of the buffer as + * avg_size = avg_size * ( 1 - a ) + required_size * a + * where a is 1 / 2 ^ BUFFER_AVG_SIZE_SHIFT. */ + buffer->avg_size *= (1 << BUFFER_AVG_SIZE_SHIFT) - 1; + buffer->avg_size >>= BUFFER_AVG_SIZE_SHIFT; + buffer->avg_size += buffer_req_size(buffer, 0); + + /* And then only shrink if the average size of the buffer is much + * too big, to avoid bumping up & down the buffers all the time. + * realloc() isn't exactly cheap ... */ + new = buffer_req_size(buffer, buffer_get_avg_size(buffer)); + if (new < buffer->capacity >> 3 && + new >= BUFFER_MIN_SHRINK_SIZE) { + buffer_adj_size(buffer, buffer_get_avg_size(buffer)); } buffer_adj_size(buffer, 0); @@ -83,6 +106,7 @@ uint8_t *buffer_end(Buffer *buffer) void buffer_reset(Buffer *buffer) { buffer->offset = 0; + buffer_shrink(buffer); } void buffer_free(Buffer *buffer) @@ -107,6 +131,7 @@ void buffer_advance(Buffer *buffer, size_t len) memmove(buffer->buffer, buffer->buffer + len, (buffer->offset - len)); buffer->offset -= len; + buffer_shrink(buffer); } void buffer_move_empty(Buffer *to, Buffer *from) From 382e1737d3467b76e8ade34b96afaae91509002e Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 5 Nov 2015 10:12:18 +0100 Subject: [PATCH 20/20] vnc: fix mismerge Commit "4d77b1f vnc: fix bug: vnc server can't start when 'to' is specified" was rebased incorrectly, fix it. Reported-by: Markus Armbruster Signed-off-by: Gerd Hoffmann Reviewed-by: Markus Armbruster Reviewed-by: Yang Hongyang Message-id: 1446714738-22400-1-git-send-email-kraxel@redhat.com --- ui/vnc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index a8cce5a45b..369ad7b11d 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3616,8 +3616,6 @@ void vnc_display_open(const char *id, Error **errp) "%d", (int)baseport + 5900); if (to) { - saddr->u.inet->has_to = true; - saddr->u.inet->to = to; saddr->u.inet->has_to = true; saddr->u.inet->to = to + 5900; }