d4cdad5
From d4066655fc866ac0e57420b32dec3b37277b374c Mon Sep 17 00:00:00 2001
d4cdad5
From: Alon Levy <alevy@redhat.com>
d4cdad5
Date: Tue, 22 Mar 2011 12:27:59 +0200
d4cdad5
Subject: [PATCH 114/118] spice-qemu-char.c: add throttling
d4cdad5
d4cdad5
BZ: 672191
d4cdad5
d4cdad5
upstream: not submitted (explained below)
d4cdad5
d4cdad5
Adds throttling support to spicevmc chardev. Uses a timer to avoid recursing:
d4cdad5
1. spice-server: reds.c:            read_from_vdi_port
d4cdad5
2. qemu:         spice-qemu-char.c: vmc_read
d4cdad5
3.                                  chr_write_unblocked
d4cdad5
                                (calls virtio_serial_throttle_port(port, false))
d4cdad5
4. qemu:         virtio ...
d4cdad5
5. qemu:         spice-qemu-char.c: spice_chr_write
d4cdad5
6. qemu:         spice-qemu-char.c: wakeup (calls into spice-server)
d4cdad5
7. spice-server: ...
d4cdad5
8. qemu:         spice-qemu-char.c: vmc_read
d4cdad5
d4cdad5
Instead, in vmc_read if we were throttled and we are just about to return
d4cdad5
all the bytes we will set a timer to be triggered immediately to call
d4cdad5
chr_write_unblocked. Then we return after 2 above, and 3 is called from the
d4cdad5
timer callback. This also means we can later remove some ugly recursion protection
d4cdad5
from spice-server.
d4cdad5
d4cdad5
The other tricky point in this patch is not returning the leftover chunk twice.
d4cdad5
When we throttle, by definition we have data that spice server didn't consume.
d4cdad5
It is being kept by virtio-serial, and by us. The next vmc_read callback needs
d4cdad5
to not return it, but just do unthrottling. Then virtio will give us the remaining
d4cdad5
chunk as usual in spice_chr_write, and we will pass it to spice server in the
d4cdad5
next vmc_read.
d4cdad5
d4cdad5
This patch relies on Amit's series to expose throttling to chardev's, which
d4cdad5
was not accepted upstream, and will not be accepted upstream until the mainloop
d4cdad5
is reworked to use glib.
d4cdad5
---
d4cdad5
 spice-qemu-char.c |   39 +++++++++++++++++++++++++++++++++++----
d4cdad5
 1 files changed, 35 insertions(+), 4 deletions(-)
d4cdad5
d4cdad5
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
d4cdad5
index 7e8eaa9..eeeb32e 100644
d4cdad5
--- a/spice-qemu-char.c
d4cdad5
+++ b/spice-qemu-char.c
d4cdad5
@@ -1,4 +1,6 @@
d4cdad5
 #include "config-host.h"
d4cdad5
+#include "qemu-common.h"
d4cdad5
+#include "qemu-timer.h"
d4cdad5
 #include "trace.h"
d4cdad5
 #include "ui/qemu-spice.h"
d4cdad5
 #include <spice.h>
d4cdad5
@@ -25,6 +27,7 @@ typedef struct SpiceCharDriver {
d4cdad5
     uint8_t               *datapos;
d4cdad5
     ssize_t               bufsize, datalen;
d4cdad5
     uint32_t              debug;
d4cdad5
+    QEMUTimer             *unblock_timer;
d4cdad5
 } SpiceCharDriver;
d4cdad5
d4cdad5
 static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
d4cdad5
@@ -50,6 +53,17 @@ static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
d4cdad5
     return out;
d4cdad5
 }
d4cdad5
d4cdad5
+static void spice_chr_unblock(void *opaque)
d4cdad5
+{
d4cdad5
+    SpiceCharDriver *scd = opaque;
d4cdad5
+
d4cdad5
+    if (scd->chr->chr_write_unblocked == NULL) {
d4cdad5
+        dprintf(scd, 1, "%s: backend doesn't support unthrottling.\n", __func__);
d4cdad5
+        return;
d4cdad5
+    }
d4cdad5
+    scd->chr->chr_write_unblocked(scd->chr->handler_opaque);
d4cdad5
+}
d4cdad5
+
d4cdad5
 static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
d4cdad5
 {
d4cdad5
     SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
d4cdad5
@@ -61,9 +75,16 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
d4cdad5
         scd->datapos += bytes;
d4cdad5
         scd->datalen -= bytes;
d4cdad5
         assert(scd->datalen >= 0);
d4cdad5
-        if (scd->datalen == 0) {
d4cdad5
-            scd->datapos = 0;
d4cdad5
-        }
d4cdad5
+    }
d4cdad5
+    if (scd->datalen == 0 && scd->chr->write_blocked) {
d4cdad5
+        dprintf(scd, 1, "%s: unthrottling (%d)\n", __func__, bytes);
d4cdad5
+        scd->chr->write_blocked = false;
d4cdad5
+        /*
d4cdad5
+         * set a timer instead of calling scd->chr->chr_write_unblocked directly,
d4cdad5
+         * because that will call back into spice_chr_write (see
d4cdad5
+         * virtio-console.c:chr_write_unblocked), which is unwanted.
d4cdad5
+         */
d4cdad5
+        qemu_mod_timer(scd->unblock_timer, 0);
d4cdad5
     }
d4cdad5
     trace_spice_vmc_read(bytes, len);
d4cdad5
     return bytes;
d4cdad5
@@ -135,6 +156,7 @@ static void vmc_unregister_interface(SpiceCharDriver *scd)
d4cdad5
 static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
d4cdad5
 {
d4cdad5
     SpiceCharDriver *s = chr->opaque;
d4cdad5
+    int read_bytes;
d4cdad5
d4cdad5
     dprintf(s, 2, "%s: %d\n", __func__, len);
d4cdad5
     vmc_register_interface(s);
d4cdad5
@@ -147,7 +169,15 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
d4cdad5
     s->datapos = s->buffer;
d4cdad5
     s->datalen = len;
d4cdad5
     spice_server_char_device_wakeup(&s->sin);
d4cdad5
-    return len;
d4cdad5
+    read_bytes = len - s->datalen;
d4cdad5
+    if (read_bytes != len) {
d4cdad5
+        dprintf(s, 1, "%s: throttling: %d < %d (%zd)\n", __func__,
d4cdad5
+                read_bytes, len, s->bufsize);
d4cdad5
+        s->chr->write_blocked = true;
d4cdad5
+        /* We'll get passed in the unconsumed data with the next call */
d4cdad5
+        s->datalen = 0;
d4cdad5
+    }
d4cdad5
+    return read_bytes;
d4cdad5
 }
d4cdad5
d4cdad5
 static void spice_chr_close(struct CharDriverState *chr)
d4cdad5
@@ -225,6 +255,7 @@ int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr)
d4cdad5
     chr->chr_close = spice_chr_close;
d4cdad5
     chr->chr_guest_open = spice_chr_guest_open;
d4cdad5
     chr->chr_guest_close = spice_chr_guest_close;
d4cdad5
+    s->unblock_timer = qemu_new_timer_ms(vm_clock, spice_chr_unblock, s);
d4cdad5
d4cdad5
 #if SPICE_SERVER_VERSION < 0x000901
d4cdad5
     /* See comment in vmc_state() */
d4cdad5
-- 
d4cdad5
1.7.7.5
d4cdad5