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