[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] [HVM][VNC] Add a backoff feature to the vnc server, so that if it detects



# HG changeset patch
# User Steven Smith <ssmith@xxxxxxxxxxxxx>
# Node ID 449dcaff25513813da8bcda94ffc85f3f14c5857
# Parent  c742b2ae920c45c476a4c8b7fd28870df124071e
[HVM][VNC] Add a backoff feature to the vnc server, so that if it detects
that the display hasn't changed for a while it starts scanning more slowly.

Signed-off-by: Steven Smith <sos22@xxxxxxxxx>
---
 tools/ioemu/vl.c  |    6 +
 tools/ioemu/vl.h  |    1 
 tools/ioemu/vnc.c |  254 ++++++++++++++++++++++++++++++++----------------------
 3 files changed, 158 insertions(+), 103 deletions(-)

diff -r c742b2ae920c -r 449dcaff2551 tools/ioemu/vl.c
--- a/tools/ioemu/vl.c  Thu Sep 21 17:56:14 2006 +0100
+++ b/tools/ioemu/vl.c  Thu Sep 21 18:29:48 2006 +0100
@@ -723,6 +723,12 @@ void qemu_del_timer(QEMUTimer *ts)
         }
         pt = &t->next;
     }
+}
+
+void qemu_advance_timer(QEMUTimer *ts, int64_t expire_time)
+{
+    if (ts->expire_time > expire_time)
+       qemu_mod_timer(ts, expire_time);
 }
 
 /* modify the current timer so that it will be fired when current_time
diff -r c742b2ae920c -r 449dcaff2551 tools/ioemu/vl.h
--- a/tools/ioemu/vl.h  Thu Sep 21 17:56:14 2006 +0100
+++ b/tools/ioemu/vl.h  Thu Sep 21 18:29:48 2006 +0100
@@ -405,6 +405,7 @@ void qemu_free_timer(QEMUTimer *ts);
 void qemu_free_timer(QEMUTimer *ts);
 void qemu_del_timer(QEMUTimer *ts);
 void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time);
+void qemu_advance_timer(QEMUTimer *ts, int64_t expire_time);
 int qemu_timer_pending(QEMUTimer *ts);
 
 extern int64_t ticks_per_sec;
diff -r c742b2ae920c -r 449dcaff2551 tools/ioemu/vnc.c
--- a/tools/ioemu/vnc.c Thu Sep 21 17:56:14 2006 +0100
+++ b/tools/ioemu/vnc.c Thu Sep 21 18:29:48 2006 +0100
@@ -27,7 +27,19 @@
 #include "vl.h"
 #include "qemu_socket.h"
 
-#define VNC_REFRESH_INTERVAL (1000 / 30)
+/* The refresh interval starts at BASE.  If we scan the buffer and
+   find no change, we increase by INC, up to MAX.  If the mouse moves
+   or we get a keypress, the interval is set back to BASE.  If we find
+   an update, halve the interval.
+
+   All times in milliseconds. */
+#define VNC_REFRESH_INTERVAL_BASE 30
+#define VNC_REFRESH_INTERVAL_INC  50
+#define VNC_REFRESH_INTERVAL_MAX  2000
+
+/* Wait at most one second between updates, so that we can detect a
+   minimised vncviewer reasonably quickly. */
+#define VNC_MAX_UPDATE_INTERVAL   5000
 
 #include "vnc_keysym.h"
 #include "keymaps.c"
@@ -64,10 +76,11 @@ struct VncState
 struct VncState
 {
     QEMUTimer *timer;
+    int timer_interval;
+    int64_t last_update_time;
     int lsock;
     int csock;
     DisplayState *ds;
-    int need_update;
     int width;
     int height;
     uint64_t *dirty_row;       /* screen regions which are possibly dirty */
@@ -97,8 +110,6 @@ struct VncState
     int visible_y;
     int visible_w;
     int visible_h;
-
-    int slow_client;
 
     int ctl_keys;               /* Ctrl+Alt starts calibration */
 };
@@ -380,7 +391,7 @@ static void vnc_copy(DisplayState *ds, i
     int y = 0;
     int pitch = ds->linesize;
     VncState *vs = ds->opaque;
-    int updating_client = !vs->slow_client;
+    int updating_client = 1;
 
     if (src_x < vs->visible_x || src_y < vs->visible_y ||
        dst_x < vs->visible_x || dst_y < vs->visible_y ||
@@ -389,11 +400,6 @@ static void vnc_copy(DisplayState *ds, i
        (dst_x + w) > (vs->visible_x + vs->visible_w) ||
        (dst_y + h) > (vs->visible_y + vs->visible_h))
        updating_client = 0;
-
-    if (updating_client) {
-       vs->need_update = 1;
-       _vnc_update_client(vs);
-    }
 
     if (dst_y > src_y) {
        y = h - 1;
@@ -445,110 +451,145 @@ static void _vnc_update_client(void *opa
 static void _vnc_update_client(void *opaque)
 {
     VncState *vs = opaque;
-    int64_t now = qemu_get_clock(rt_clock);
-
-    if (vs->need_update && vs->csock != -1) {
-       int y;
-       char *row;
-       char *old_row;
-       uint64_t width_mask;
-       int n_rectangles;
-       int saved_offset;
-       int maxx, maxy;
-       int tile_bytes = vs->depth * DP2X(vs, 1);
-
-       qemu_mod_timer(vs->timer, now + VNC_REFRESH_INTERVAL);
-
-       if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS))
-           width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1;
-       else
-           width_mask = ~(0ULL);
-
-       /* Walk through the dirty map and eliminate tiles that
-          really aren't dirty */
-       row = vs->ds->data;
-       old_row = vs->old_data;
-
-       for (y = 0; y < vs->ds->height; y++) {
-           if (vs->dirty_row[y] & width_mask) {
-               int x;
-               char *ptr, *old_ptr;
-
-               ptr = row;
-               old_ptr = old_row;
-
-               for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) {
-                   if (vs->dirty_row[y] & (1ULL << x)) {
-                       if (memcmp(old_ptr, ptr, tile_bytes)) {
-                           vs->has_update = 1;
-                           vs->update_row[y] |= (1ULL << x);
-                           memcpy(old_ptr, ptr, tile_bytes);
-                       }
-                       vs->dirty_row[y] &= ~(1ULL << x);
+    int64_t now;
+    int y;
+    char *row;
+    char *old_row;
+    uint64_t width_mask;
+    int n_rectangles;
+    int saved_offset;
+    int maxx, maxy;
+    int tile_bytes = vs->depth * DP2X(vs, 1);
+
+    if (vs->csock == -1)
+       return;
+
+    now = qemu_get_clock(rt_clock);
+
+    if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS))
+       width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1;
+    else
+       width_mask = ~(0ULL);
+
+    /* Walk through the dirty map and eliminate tiles that really
+       aren't dirty */
+    row = vs->ds->data;
+    old_row = vs->old_data;
+
+    for (y = 0; y < vs->ds->height; y++) {
+       if (vs->dirty_row[y] & width_mask) {
+           int x;
+           char *ptr, *old_ptr;
+
+           ptr = row;
+           old_ptr = old_row;
+
+           for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) {
+               if (vs->dirty_row[y] & (1ULL << x)) {
+                   if (memcmp(old_ptr, ptr, tile_bytes)) {
+                       vs->has_update = 1;
+                       vs->update_row[y] |= (1ULL << x);
+                       memcpy(old_ptr, ptr, tile_bytes);
                    }
-
-                   ptr += tile_bytes;
-                   old_ptr += tile_bytes;
+                   vs->dirty_row[y] &= ~(1ULL << x);
                }
+
+               ptr += tile_bytes;
+               old_ptr += tile_bytes;
            }
-
-           row += vs->ds->linesize;
-           old_row += vs->ds->linesize;
        }
-
-       if (!vs->has_update || vs->visible_y >= vs->ds->height ||
-           vs->visible_x >= vs->ds->width)
-           return;
-
-       /* Count rectangles */
-       n_rectangles = 0;
-       vnc_write_u8(vs, 0);  /* msg id */
-       vnc_write_u8(vs, 0);
-       saved_offset = vs->output.offset;
-       vnc_write_u16(vs, 0);
-
-       maxy = vs->visible_y + vs->visible_h;
-       if (maxy > vs->ds->height)
-           maxy = vs->ds->height;
-       maxx = vs->visible_x + vs->visible_w;
-       if (maxx > vs->ds->width)
-           maxx = vs->ds->width;
-
-       for (y = vs->visible_y; y < maxy; y++) {
-           int x;
-           int last_x = -1;
-           for (x = X2DP_DOWN(vs, vs->visible_x);
-                x < X2DP_UP(vs, maxx); x++) {
-               if (vs->update_row[y] & (1ULL << x)) {
-                   if (last_x == -1)
-                       last_x = x;
-                   vs->update_row[y] &= ~(1ULL << x);
-               } else {
-                   if (last_x != -1) {
-                       int h = find_update_height(vs, y, maxy, last_x, x);
+  
+       row += vs->ds->linesize;
+       old_row += vs->ds->linesize;
+    }
+
+    if (!vs->has_update || vs->visible_y >= vs->ds->height ||
+       vs->visible_x >= vs->ds->width)
+       goto backoff;
+
+    /* Count rectangles */
+    n_rectangles = 0;
+    vnc_write_u8(vs, 0);  /* msg id */
+    vnc_write_u8(vs, 0);
+    saved_offset = vs->output.offset;
+    vnc_write_u16(vs, 0);
+    
+    maxy = vs->visible_y + vs->visible_h;
+    if (maxy > vs->ds->height)
+       maxy = vs->ds->height;
+    maxx = vs->visible_x + vs->visible_w;
+    if (maxx > vs->ds->width)
+       maxx = vs->ds->width;
+
+    for (y = vs->visible_y; y < maxy; y++) {
+       int x;
+       int last_x = -1;
+       for (x = X2DP_DOWN(vs, vs->visible_x);
+            x < X2DP_UP(vs, maxx); x++) {
+           if (vs->update_row[y] & (1ULL << x)) {
+               if (last_x == -1)
+                   last_x = x;
+               vs->update_row[y] &= ~(1ULL << x);
+           } else {
+               if (last_x != -1) {
+                   int h = find_update_height(vs, y, maxy, last_x, x);
+                   if (h != 0) {
                        send_framebuffer_update(vs, DP2X(vs, last_x), y,
                                                DP2X(vs, (x - last_x)), h);
                        n_rectangles++;
                    }
-                   last_x = -1;
                }
+               last_x = -1;
            }
-           if (last_x != -1) {
-               int h = find_update_height(vs, y, maxy, last_x, x);
+       }
+       if (last_x != -1) {
+           int h = find_update_height(vs, y, maxy, last_x, x);
+           if (h != 0) {
                send_framebuffer_update(vs, DP2X(vs, last_x), y,
                                        DP2X(vs, (x - last_x)), h);
                n_rectangles++;
            }
        }
-       vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
-       vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
-
-       vs->has_update = 0;
-       vs->need_update = 0;
-       vnc_flush(vs);
-       vs->slow_client = 0;
-    } else
-       vs->slow_client = 1;
+    }
+    vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
+    vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
+
+    if (n_rectangles == 0)
+       goto backoff;
+
+    vs->has_update = 0;
+    vnc_flush(vs);
+    vs->last_update_time = now;
+
+    vs->timer_interval /= 2;
+    if (vs->timer_interval < VNC_REFRESH_INTERVAL_BASE)
+       vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+
+    return;
+
+ backoff:
+    /* No update -> back off a bit */
+    vs->timer_interval += VNC_REFRESH_INTERVAL_INC;
+    if (vs->timer_interval > VNC_REFRESH_INTERVAL_MAX) {
+       vs->timer_interval = VNC_REFRESH_INTERVAL_MAX;
+       if (now - vs->last_update_time >= VNC_MAX_UPDATE_INTERVAL) {
+           /* Send a null update.  If the client is no longer
+              interested (e.g. minimised) it'll ignore this, and we
+              can stop scanning the buffer until it sends another
+              update request. */
+           /* Note that there are bugs in xvncviewer which prevent
+              this from actually working.  Leave the code in place
+              for correct clients. */
+           vnc_write_u8(vs, 0);
+           vnc_write_u8(vs, 0);
+           vnc_write_u16(vs, 0);
+           vnc_flush(vs);
+           vs->last_update_time = now;
+           return;
+       }
+    }
+    qemu_mod_timer(vs->timer, now + vs->timer_interval);
+    return;
 }
 
 static void vnc_update_client(void *opaque)
@@ -561,8 +602,10 @@ static void vnc_update_client(void *opaq
 
 static void vnc_timer_init(VncState *vs)
 {
-    if (vs->timer == NULL)
+    if (vs->timer == NULL) {
        vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
+       vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+    }
 }
 
 static void vnc_dpy_refresh(DisplayState *ds)
@@ -622,7 +665,6 @@ static int vnc_client_io_error(VncState 
        vs->csock = -1;
        buffer_reset(&vs->input);
        buffer_reset(&vs->output);
-       vs->need_update = 0;
        return 0;
     }
     return ret;
@@ -892,7 +934,6 @@ static void framebuffer_update_request(V
                                       int x_position, int y_position,
                                       int w, int h)
 {
-    vs->need_update = 1;
     if (!incremental)
        framebuffer_set_updated(vs, x_position, y_position, w, h);
     vs->visible_x = x_position;
@@ -1015,6 +1056,7 @@ static int protocol_client_msg(VncState 
 {
     int i;
     uint16_t limit;
+    int64_t now;
 
     switch (data[0]) {
     case 0:
@@ -1054,12 +1096,18 @@ static int protocol_client_msg(VncState 
        if (len == 1)
            return 8;
 
+       vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+       qemu_advance_timer(vs->timer,
+                          qemu_get_clock(rt_clock) + vs->timer_interval);
        key_event(vs, read_u8(data, 1), read_u32(data, 4));
        break;
     case 5:
        if (len == 1)
            return 6;
 
+       vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+       qemu_advance_timer(vs->timer,
+                          qemu_get_clock(rt_clock) + vs->timer_interval);
        pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 
4));
        break;
     case 6:
@@ -1268,7 +1316,7 @@ int vnc_start_viewer(int port)
        exit(1);
 
     case 0:    /* child */
-       execlp("vncviewer", "vncviewer", s, 0);
+       execlp("vncviewer", "vncviewer", s, NULL);
        fprintf(stderr, "vncviewer execlp failed\n");
        exit(1);
 

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.