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