1 | # HG changeset patch |
---|
2 | # User Steven Smith <ssmith@xensource.com> |
---|
3 | # Node ID 1d3f52eb256e3522edc12daca91039b319dbbbe5 |
---|
4 | # Parent b7b653e36d20811831f26bb951ea66dca5854b17 |
---|
5 | [HVM] Rate limit guest accesses to the qemu virtual serial port. This stops |
---|
6 | grub's boot menu from hammering dom0. |
---|
7 | |
---|
8 | Signed-off-by: Steven Smith <sos22@cam.ac.uk> |
---|
9 | |
---|
10 | --- ioemu/hw/serial.c Mon Sep 25 16:31:02 2006 +0100 |
---|
11 | +++ ioemu/hw/serial.c Mon Sep 25 17:27:18 2006 +0100 |
---|
12 | @@ -22,6 +22,9 @@ |
---|
13 | * THE SOFTWARE. |
---|
14 | */ |
---|
15 | #include "vl.h" |
---|
16 | +#include <sys/time.h> |
---|
17 | +#include <time.h> |
---|
18 | +#include <assert.h> |
---|
19 | |
---|
20 | //#define DEBUG_SERIAL |
---|
21 | |
---|
22 | @@ -138,6 +141,67 @@ static void serial_update_parameters(Ser |
---|
23 | printf("speed=%d parity=%c data=%d stop=%d\n", |
---|
24 | speed, parity, data_bits, stop_bits); |
---|
25 | #endif |
---|
26 | +} |
---|
27 | + |
---|
28 | +/* Rate limit serial requests so that e.g. grub on a serial console |
---|
29 | + doesn't kill dom0. Simple token bucket. If we get some actual |
---|
30 | + data from the user, instantly refil the bucket. */ |
---|
31 | + |
---|
32 | +/* How long it takes to generate a token, in microseconds. */ |
---|
33 | +#define TOKEN_PERIOD 1000 |
---|
34 | +/* Maximum and initial size of token bucket */ |
---|
35 | +#define TOKENS_MAX 100000 |
---|
36 | + |
---|
37 | +static int tokens_avail; |
---|
38 | + |
---|
39 | +static void serial_get_token(void) |
---|
40 | +{ |
---|
41 | + static struct timeval last_refil_time; |
---|
42 | + static int started; |
---|
43 | + |
---|
44 | + assert(tokens_avail >= 0); |
---|
45 | + if (!tokens_avail) { |
---|
46 | + struct timeval delta, now; |
---|
47 | + int generated; |
---|
48 | + |
---|
49 | + if (!started) { |
---|
50 | + gettimeofday(&last_refil_time, NULL); |
---|
51 | + tokens_avail = TOKENS_MAX; |
---|
52 | + started = 1; |
---|
53 | + return; |
---|
54 | + } |
---|
55 | + retry: |
---|
56 | + gettimeofday(&now, NULL); |
---|
57 | + delta.tv_sec = now.tv_sec - last_refil_time.tv_sec; |
---|
58 | + delta.tv_usec = now.tv_usec - last_refil_time.tv_usec; |
---|
59 | + if (delta.tv_usec < 0) { |
---|
60 | + delta.tv_usec += 1000000; |
---|
61 | + delta.tv_sec--; |
---|
62 | + } |
---|
63 | + assert(delta.tv_usec >= 0 && delta.tv_sec >= 0); |
---|
64 | + if (delta.tv_usec < TOKEN_PERIOD) { |
---|
65 | + struct timespec ts; |
---|
66 | + /* Wait until at least one token is available. */ |
---|
67 | + ts.tv_sec = TOKEN_PERIOD / 1000000; |
---|
68 | + ts.tv_nsec = (TOKEN_PERIOD % 1000000) * 1000; |
---|
69 | + while (nanosleep(&ts, &ts) < 0 && errno == EINTR) |
---|
70 | + ; |
---|
71 | + goto retry; |
---|
72 | + } |
---|
73 | + generated = (delta.tv_sec * 1000000) / TOKEN_PERIOD; |
---|
74 | + generated += |
---|
75 | + ((delta.tv_sec * 1000000) % TOKEN_PERIOD + delta.tv_usec) / TOKEN_PERIOD; |
---|
76 | + assert(generated > 0); |
---|
77 | + |
---|
78 | + last_refil_time.tv_usec += (generated * TOKEN_PERIOD) % 1000000; |
---|
79 | + last_refil_time.tv_sec += last_refil_time.tv_usec / 1000000; |
---|
80 | + last_refil_time.tv_usec %= 1000000; |
---|
81 | + last_refil_time.tv_sec += (generated * TOKEN_PERIOD) / 1000000; |
---|
82 | + if (generated > TOKENS_MAX) |
---|
83 | + generated = TOKENS_MAX; |
---|
84 | + tokens_avail = generated; |
---|
85 | + } |
---|
86 | + tokens_avail--; |
---|
87 | } |
---|
88 | |
---|
89 | static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
---|
90 | @@ -245,9 +309,11 @@ static uint32_t serial_ioport_read(void |
---|
91 | ret = s->mcr; |
---|
92 | break; |
---|
93 | case 5: |
---|
94 | + serial_get_token(); |
---|
95 | ret = s->lsr; |
---|
96 | break; |
---|
97 | case 6: |
---|
98 | + serial_get_token(); |
---|
99 | if (s->mcr & UART_MCR_LOOP) { |
---|
100 | /* in loopback, the modem output pins are connected to the |
---|
101 | inputs */ |
---|
102 | @@ -296,12 +362,14 @@ static void serial_receive1(void *opaque |
---|
103 | static void serial_receive1(void *opaque, const uint8_t *buf, int size) |
---|
104 | { |
---|
105 | SerialState *s = opaque; |
---|
106 | + tokens_avail = TOKENS_MAX; |
---|
107 | serial_receive_byte(s, buf[0]); |
---|
108 | } |
---|
109 | |
---|
110 | static void serial_event(void *opaque, int event) |
---|
111 | { |
---|
112 | SerialState *s = opaque; |
---|
113 | + tokens_avail = TOKENS_MAX; |
---|
114 | if (event == CHR_EVENT_BREAK) |
---|
115 | serial_receive_break(s); |
---|
116 | } |
---|