#include "exec-all.h"
#include "qemu-xen.h"
+#include <assert.h>
#include <xenguest.h>
+static int throttling_disabled;
static char log_buffer[4096];
static int log_buffer_off;
uint64_t vga_stolen_ram;
} PCIXenPlatformState;
+/* We throttle access to dom0 syslog, to avoid DOS attacks. This is
+ modelled as a token bucket, with one token for every byte of log.
+ The bucket size is 128KB (->1024 lines of 128 bytes each) and
+ refills at 256B/s. It starts full. The guest is blocked if no
+ tokens are available when it tries to generate a log message. */
+#define BUCKET_MAX_SIZE (128*1024)
+#define BUCKET_FILL_RATE 256
+
+static void throttle(unsigned count)
+{
+ static unsigned available;
+ static struct timespec last_refil;
+ static int started;
+ static int warned;
+
+ struct timespec waiting_for, now;
+ double delay;
+ struct timespec ts;
+
+ if (throttling_disabled)
+ return;
+
+ if (!started) {
+ clock_gettime(CLOCK_MONOTONIC, &last_refil);
+ available = BUCKET_MAX_SIZE;
+ started = 1;
+ }
+
+ if (count > BUCKET_MAX_SIZE) {
+ fprintf(logfile, "tried to get %d tokens, but bucket size is %d\n",
+ BUCKET_MAX_SIZE, count);
+ exit(1);
+ }
+
+ if (available < count) {
+ /* The bucket is empty. Refil it */
+
+ /* When will it be full enough to handle this request? */
+ delay = (double)(count - available) / BUCKET_FILL_RATE;
+ waiting_for = last_refil;
+ waiting_for.tv_sec += delay;
+ waiting_for.tv_nsec += (delay - (int)delay) * 1e9;
+ if (waiting_for.tv_nsec >= 1000000000) {
+ waiting_for.tv_nsec -= 1000000000;
+ waiting_for.tv_sec++;
+ }
+
+ /* How long do we have to wait? (might be negative) */
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ ts.tv_sec = waiting_for.tv_sec - now.tv_sec;
+ ts.tv_nsec = waiting_for.tv_nsec - now.tv_nsec;
+ if (ts.tv_nsec < 0) {
+ ts.tv_sec--;
+ ts.tv_nsec += 1000000000;
+ }
+
+ /* Wait for it. */
+ if (ts.tv_sec > 0 ||
+ (ts.tv_sec == 0 && ts.tv_nsec > 0)) {
+ if (!warned) {
+ fprintf(logfile, "throttling guest access to syslog");
+ warned = 1;
+ }
+ while (nanosleep(&ts, &ts) < 0 && errno == EINTR)
+ ;
+ }
+
+ /* Refil */
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ delay = (now.tv_sec - last_refil.tv_sec) +
+ (now.tv_nsec - last_refil.tv_nsec) * 1.0e-9;
+ available += BUCKET_FILL_RATE * delay;
+ if (available > BUCKET_MAX_SIZE)
+ available = BUCKET_MAX_SIZE;
+ last_refil = now;
+ }
+
+ assert(available >= count);
+
+ available -= count;
+}
+
static uint32_t xen_platform_ioport_readb(void *opaque, uint32_t addr)
{
PCIXenPlatformState *s = opaque;
if (val == '\n' || log_buffer_off == sizeof(log_buffer) - 1) {
/* Flush buffer */
log_buffer[log_buffer_off] = 0;
+ throttle(log_buffer_off);
fprintf(logfile, "%s\n", log_buffer);
log_buffer_off = 0;
break;
if (val == '\n' || log_buffer_off == sizeof(log_buffer) - 1) {
/* Flush buffer */
log_buffer[log_buffer_off] = 0;
+ throttle(log_buffer_off);
fprintf(logfile, "%s\n", log_buffer);
log_buffer_off = 0;
break;
{
PCIXenPlatformState *d;
struct pci_config_header *pch;
+ struct stat stbuf;
printf("Register xen platform.\n");
d = (PCIXenPlatformState *)pci_register_device(
register_ioport_write(0x10, 16, 1, platform_fixed_ioport_write1, NULL);
register_ioport_read(0x10, 16, 2, platform_fixed_ioport_read2, NULL);
register_ioport_read(0x10, 16, 1, platform_fixed_ioport_read1, NULL);
+
+ if (stat("/etc/disable-guest-log-throttle", &stbuf) == 0)
+ throttling_disabled = 1;
+
}