# HG changeset patch
# User Steven Smith <ssmith@xxxxxxxxxxxxx>
# 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@xxxxxxxxx>
---
tools/ioemu/hw/serial.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 68 insertions(+)
diff -r b7b653e36d20 -r 1d3f52eb256e tools/ioemu/hw/serial.c
--- a/tools/ioemu/hw/serial.c Mon Sep 25 16:31:02 2006 +0100
+++ b/tools/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);
}
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|