Blame 0009-spice-qemu-char.c-add-throttling.patch

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