summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Levy <alevy@redhat.com>2011-03-22 12:27:59 +0200
committerHans de Goede <hdegoede@redhat.com>2011-05-03 15:56:33 +0200
commit4d1ded86bb6ccab92f32a2ea630f8e4728819925 (patch)
treeadd1145cf8210434a4a3c9f8efde9cff07fcf4ea
parent852ec9cfa213ee2d48110057b25aea5cdef057de (diff)
spice-qemu-char.c: add throttling
BZ: 672191 upstream: not submitted (explained below) Adds throttling support to spicevmc chardev. Uses a timer to avoid recursing: 1. spice-server: reds.c: read_from_vdi_port 2. qemu: spice-qemu-char.c: vmc_read 3. chr_write_unblocked (calls virtio_serial_throttle_port(port, false)) 4. qemu: virtio ... 5. qemu: spice-qemu-char.c: spice_chr_write 6. qemu: spice-qemu-char.c: wakeup (calls into spice-server) 7. spice-server: ... 8. qemu: spice-qemu-char.c: vmc_read Instead, in vmc_read if we were throttled and we are just about to return all the bytes we will set a timer to be triggered immediately to call chr_write_unblocked. Then we return after 2 above, and 3 is called from the timer callback. This also means we can later remove some ugly recursion protection from spice-server. The other tricky point in this patch is not returning the leftover chunk twice. When we throttle, by definition we have data that spice server didn't consume. It is being kept by virtio-serial, and by us. The next vmc_read callback needs to not return it, but just do unthrottling. Then virtio will give us the remaining chunk as usual in spice_chr_write, and we will pass it to spice server in the next vmc_read. This patch relies on Amit's series to expose throttling to chardev's, which was not accepted upstream, and will not be accepted upstream until the mainloop is reworked to use glib.
-rw-r--r--spice-qemu-char.c39
1 files changed, 35 insertions, 4 deletions
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 517f337c43..509ec867a2 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -1,4 +1,6 @@
#include "config-host.h"
+#include "qemu-common.h"
+#include "qemu-timer.h"
#include "trace.h"
#include "ui/qemu-spice.h"
#include <spice.h>
@@ -25,6 +27,7 @@ typedef struct SpiceCharDriver {
uint8_t *datapos;
ssize_t bufsize, datalen;
uint32_t debug;
+ QEMUTimer *unblock_timer;
} SpiceCharDriver;
static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
@@ -51,6 +54,17 @@ static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
return out;
}
+static void spice_chr_unblock(void *opaque)
+{
+ SpiceCharDriver *scd = opaque;
+
+ if (scd->chr->chr_write_unblocked == NULL) {
+ dprintf(scd, 1, "%s: backend doesn't support unthrottling.\n", __func__);
+ return;
+ }
+ scd->chr->chr_write_unblocked(scd->chr->handler_opaque);
+}
+
static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
{
SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
@@ -62,9 +76,16 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
scd->datapos += bytes;
scd->datalen -= bytes;
assert(scd->datalen >= 0);
- if (scd->datalen == 0) {
- scd->datapos = 0;
- }
+ }
+ if (scd->datalen == 0 && scd->chr->write_blocked) {
+ dprintf(scd, 1, "%s: unthrottling (%d)\n", __func__, bytes);
+ scd->chr->write_blocked = false;
+ /*
+ * set a timer instead of calling scd->chr->chr_write_unblocked directly,
+ * because that will call back into spice_chr_write (see
+ * virtio-console.c:chr_write_unblocked), which is unwanted.
+ */
+ qemu_mod_timer(scd->unblock_timer, 0);
}
trace_spice_vmc_read(bytes, len);
return bytes;
@@ -107,6 +128,7 @@ static void vmc_unregister_interface(SpiceCharDriver *scd)
static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
SpiceCharDriver *s = chr->opaque;
+ int read_bytes;
dprintf(s, 2, "%s: %d\n", __func__, len);
vmc_register_interface(s);
@@ -119,7 +141,15 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
s->datapos = s->buffer;
s->datalen = len;
spice_server_char_device_wakeup(&s->sin);
- return len;
+ read_bytes = len - s->datalen;
+ if (read_bytes != len) {
+ dprintf(s, 1, "%s: throttling: %d < %d (%zd)\n", __func__,
+ read_bytes, len, s->bufsize);
+ s->chr->write_blocked = true;
+ /* We'll get passed in the unconsumed data with the next call */
+ s->datalen = 0;
+ }
+ return read_bytes;
}
static void spice_chr_close(struct CharDriverState *chr)
@@ -183,6 +213,7 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts)
chr->opaque = s;
chr->chr_write = spice_chr_write;
chr->chr_close = spice_chr_close;
+ s->unblock_timer = qemu_new_timer_ms(vm_clock, spice_chr_unblock, s);
qemu_chr_generic_open(chr);