aboutsummaryrefslogtreecommitdiffstats
path: root/tools/ioemu/patches/serial-port-rate-limit
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ioemu/patches/serial-port-rate-limit')
-rw-r--r--tools/ioemu/patches/serial-port-rate-limit116
1 files changed, 116 insertions, 0 deletions
diff --git a/tools/ioemu/patches/serial-port-rate-limit b/tools/ioemu/patches/serial-port-rate-limit
new file mode 100644
index 0000000000..17eac33951
--- /dev/null
+++ b/tools/ioemu/patches/serial-port-rate-limit
@@ -0,0 +1,116 @@
+# HG changeset patch
+# User Steven Smith <ssmith@xensource.com>
+# Node ID 1d3f52eb256e3522edc12daca91039b319dbbbe5
+# Parent b7b653e36d20811831f26bb951ea66dca5854b17
+[HVM] Rate limit guest accesses to the qemu virtual serial port. This stops
+grub's boot menu from hammering dom0.
+
+Signed-off-by: Steven Smith <sos22@cam.ac.uk>
+
+--- ioemu/hw/serial.c Mon Sep 25 16:31:02 2006 +0100
++++ ioemu/hw/serial.c Mon Sep 25 17:27:18 2006 +0100
+@@ -22,6 +22,9 @@
+ * THE SOFTWARE.
+ */
+ #include "vl.h"
++#include <sys/time.h>
++#include <time.h>
++#include <assert.h>
+
+ //#define DEBUG_SERIAL
+
+@@ -138,6 +141,67 @@ static void serial_update_parameters(Ser
+ printf("speed=%d parity=%c data=%d stop=%d\n",
+ speed, parity, data_bits, stop_bits);
+ #endif
++}
++
++/* Rate limit serial requests so that e.g. grub on a serial console
++ doesn't kill dom0. Simple token bucket. If we get some actual
++ data from the user, instantly refil the bucket. */
++
++/* How long it takes to generate a token, in microseconds. */
++#define TOKEN_PERIOD 1000
++/* Maximum and initial size of token bucket */
++#define TOKENS_MAX 100000
++
++static int tokens_avail;
++
++static void serial_get_token(void)
++{
++ static struct timeval last_refil_time;
++ static int started;
++
++ assert(tokens_avail >= 0);
++ if (!tokens_avail) {
++ struct timeval delta, now;
++ int generated;
++
++ if (!started) {
++ gettimeofday(&last_refil_time, NULL);
++ tokens_avail = TOKENS_MAX;
++ started = 1;
++ return;
++ }
++ retry:
++ gettimeofday(&now, NULL);
++ delta.tv_sec = now.tv_sec - last_refil_time.tv_sec;
++ delta.tv_usec = now.tv_usec - last_refil_time.tv_usec;
++ if (delta.tv_usec < 0) {
++ delta.tv_usec += 1000000;
++ delta.tv_sec--;
++ }
++ assert(delta.tv_usec >= 0 && delta.tv_sec >= 0);
++ if (delta.tv_usec < TOKEN_PERIOD) {
++ struct timespec ts;
++ /* Wait until at least one token is available. */
++ ts.tv_sec = TOKEN_PERIOD / 1000000;
++ ts.tv_nsec = (TOKEN_PERIOD % 1000000) * 1000;
++ while (nanosleep(&ts, &ts) < 0 && errno == EINTR)
++ ;
++ goto retry;
++ }
++ generated = (delta.tv_sec * 1000000) / TOKEN_PERIOD;
++ generated +=
++ ((delta.tv_sec * 1000000) % TOKEN_PERIOD + delta.tv_usec) / TOKEN_PERIOD;
++ assert(generated > 0);
++
++ last_refil_time.tv_usec += (generated * TOKEN_PERIOD) % 1000000;
++ last_refil_time.tv_sec += last_refil_time.tv_usec / 1000000;
++ last_refil_time.tv_usec %= 1000000;
++ last_refil_time.tv_sec += (generated * TOKEN_PERIOD) / 1000000;
++ if (generated > TOKENS_MAX)
++ generated = TOKENS_MAX;
++ tokens_avail = generated;
++ }
++ tokens_avail--;
+ }
+
+ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+@@ -245,9 +309,11 @@ static uint32_t serial_ioport_read(void
+ ret = s->mcr;
+ break;
+ case 5:
++ serial_get_token();
+ ret = s->lsr;
+ break;
+ case 6:
++ serial_get_token();
+ if (s->mcr & UART_MCR_LOOP) {
+ /* in loopback, the modem output pins are connected to the
+ inputs */
+@@ -296,12 +362,14 @@ static void serial_receive1(void *opaque
+ static void serial_receive1(void *opaque, const uint8_t *buf, int size)
+ {
+ SerialState *s = opaque;
++ tokens_avail = TOKENS_MAX;
+ serial_receive_byte(s, buf[0]);
+ }
+
+ static void serial_event(void *opaque, int event)
+ {
+ SerialState *s = opaque;
++ tokens_avail = TOKENS_MAX;
+ if (event == CHR_EVENT_BREAK)
+ serial_receive_break(s);
+ }