From ee41f1c835dd41a418b1afcd7394bcb4a16da6e1 Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Mon, 29 Oct 2007 12:56:27 +0000 Subject: [PATCH] Allow incremental access to hypervisor console log. Signed-off-by: Keir Fraser --- tools/console/daemon/io.c | 63 ++++++++++++++++++++++--------- tools/libxc/xc_misc.c | 16 ++++++-- tools/libxc/xenctrl.h | 2 +- tools/python/xen/lowlevel/xc/xc.c | 10 +++-- xen/common/sysctl.c | 5 +-- xen/drivers/char/console.c | 42 ++++++++++++++------- xen/include/public/sysctl.h | 21 ++++++++--- xen/include/xen/console.h | 3 +- 8 files changed, 113 insertions(+), 49 deletions(-) diff --git a/tools/console/daemon/io.c b/tools/console/daemon/io.c index c6dc6cd973..e463a61b09 100644 --- a/tools/console/daemon/io.c +++ b/tools/console/daemon/io.c @@ -53,7 +53,9 @@ extern int log_hv; extern char *log_dir; static int log_hv_fd = -1; +static evtchn_port_or_error_t log_hv_evtchn = -1; static int xc_handle = -1; +static int xce_handle = -1; struct buffer { @@ -76,8 +78,8 @@ struct domain char *serialpath; int use_consolepath; int ring_ref; - evtchn_port_t local_port; - evtchn_port_t remote_port; + evtchn_port_or_error_t local_port; + evtchn_port_or_error_t remote_port; int xce_handle; struct xencons_interface *interface; }; @@ -713,7 +715,7 @@ static void handle_tty_write(struct domain *dom) static void handle_ring_read(struct domain *dom) { - evtchn_port_t port; + evtchn_port_or_error_t port; if ((port = xc_evtchn_pending(dom->xce_handle)) == -1) return; @@ -752,12 +754,20 @@ static void handle_hv_logs(void) char buffer[1024*16]; char *bufptr = buffer; unsigned int size = sizeof(buffer); - if (xc_readconsolering(xc_handle, &bufptr, &size, 1) == 0) { + static uint32_t index = 0; + evtchn_port_or_error_t port; + + if ((port = xc_evtchn_pending(xce_handle)) == -1) + return; + + if (xc_readconsolering(xc_handle, &bufptr, &size, 0, 1, &index) == 0) { int len = write(log_hv_fd, buffer, size); if (len < 0) dolog(LOG_ERR, "Failed to write hypervisor log: %d (%s)", errno, strerror(errno)); } + + (void)xc_evtchn_unmask(xce_handle, port); } static void handle_log_reload(void) @@ -785,16 +795,30 @@ void handle_io(void) if (log_hv) { xc_handle = xc_interface_open(); - if (xc_handle == -1) + if (xc_handle == -1) { dolog(LOG_ERR, "Failed to open xc handle: %d (%s)", errno, strerror(errno)); - else - log_hv_fd = create_hv_log(); + goto out; + } + xce_handle = xc_evtchn_open(); + if (xce_handle == -1) { + dolog(LOG_ERR, "Failed to open xce handle: %d (%s)", + errno, strerror(errno)); + goto out; + } + log_hv_fd = create_hv_log(); + if (log_hv_fd == -1) + goto out; + log_hv_evtchn = xc_evtchn_bind_virq(xce_handle, VIRQ_CON_RING); + if (log_hv_evtchn == -1) { + dolog(LOG_ERR, "Failed to bind to VIRQ_CON_RING: " + "%d (%s)", errno, strerror(errno)); + goto out; + } } for (;;) { struct domain *d, *n; - struct timeval timeout = { 1, 0 }; /* Read HV logs every 1 second */ int max_fd = -1; FD_ZERO(&readfds); @@ -803,6 +827,11 @@ void handle_io(void) FD_SET(xs_fileno(xs), &readfds); max_fd = MAX(xs_fileno(xs), max_fd); + if (log_hv) { + FD_SET(xc_evtchn_fd(xce_handle), &readfds); + max_fd = MAX(xc_evtchn_fd(xce_handle), max_fd); + } + for (d = dom_head; d; d = d->next) { if (d->xce_handle != -1) { int evtchn_fd = xc_evtchn_fd(d->xce_handle); @@ -820,11 +849,7 @@ void handle_io(void) } } - /* XXX I wish we didn't have to busy wait for hypervisor logs - * but there's no obvious way to get event channel notifications - * for new HV log data as we can with guest */ - ret = select(max_fd + 1, &readfds, &writefds, 0, - log_hv_fd != -1 ? &timeout : NULL); + ret = select(max_fd + 1, &readfds, &writefds, 0, NULL); if (log_reload) { handle_log_reload(); @@ -841,12 +866,10 @@ void handle_io(void) break; } - /* Always process HV logs even if not a timeout */ - if (log_hv_fd != -1) + if (log_hv && FD_ISSET(xc_evtchn_fd(xce_handle), &readfds)) handle_hv_logs(); - /* Must not check returned FDSET if it was a timeout */ - if (ret == 0) + if (ret <= 0) continue; if (FD_ISSET(xs_fileno(xs), &readfds)) @@ -869,6 +892,7 @@ void handle_io(void) } } + out: if (log_hv_fd != -1) { close(log_hv_fd); log_hv_fd = -1; @@ -877,6 +901,11 @@ void handle_io(void) xc_interface_close(xc_handle); xc_handle = -1; } + if (xce_handle != -1) { + xc_evtchn_close(xce_handle); + xce_handle = -1; + } + log_hv_evtchn = -1; } /* diff --git a/tools/libxc/xc_misc.c b/tools/libxc/xc_misc.c index ba50866b56..c79e14a563 100644 --- a/tools/libxc/xc_misc.c +++ b/tools/libxc/xc_misc.c @@ -10,7 +10,7 @@ int xc_readconsolering(int xc_handle, char **pbuffer, unsigned int *pnr_chars, - int clear) + int clear, int incremental, uint32_t *pindex) { int ret; DECLARE_SYSCTL; @@ -19,14 +19,24 @@ int xc_readconsolering(int xc_handle, sysctl.cmd = XEN_SYSCTL_readconsole; set_xen_guest_handle(sysctl.u.readconsole.buffer, buffer); - sysctl.u.readconsole.count = nr_chars; - sysctl.u.readconsole.clear = clear; + sysctl.u.readconsole.count = nr_chars; + sysctl.u.readconsole.clear = clear; + sysctl.u.readconsole.incremental = 0; + if ( pindex ) + { + sysctl.u.readconsole.index = *pindex; + sysctl.u.readconsole.incremental = incremental; + } if ( (ret = lock_pages(buffer, nr_chars)) != 0 ) return ret; if ( (ret = do_sysctl(xc_handle, &sysctl)) == 0 ) + { *pnr_chars = sysctl.u.readconsole.count; + if ( pindex ) + *pindex = sysctl.u.readconsole.index; + } unlock_pages(buffer, nr_chars); diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h index 207328e6ee..0e82a5fbd3 100644 --- a/tools/libxc/xenctrl.h +++ b/tools/libxc/xenctrl.h @@ -549,7 +549,7 @@ int xc_physdev_pci_access_modify(int xc_handle, int xc_readconsolering(int xc_handle, char **pbuffer, unsigned int *pnr_chars, - int clear); + int clear, int incremental, uint32_t *pindex); int xc_send_debug_keys(int xc_handle, char *keys); diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c index 6a2e848b0b..2a09947be3 100644 --- a/tools/python/xen/lowlevel/xc/xc.c +++ b/tools/python/xen/lowlevel/xc/xc.c @@ -716,17 +716,19 @@ static PyObject *pyxc_readconsolering(XcObject *self, PyObject *args, PyObject *kwds) { - unsigned int clear = 0; + unsigned int clear = 0, index = 0, incremental = 0; char _str[32768], *str = _str; unsigned int count = 32768; int ret; - static char *kwd_list[] = { "clear", NULL }; + static char *kwd_list[] = { "clear", "index", "incremental", NULL }; - if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwd_list, &clear) ) + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwd_list, + &clear, &index, &incremental) ) return NULL; - ret = xc_readconsolering(self->xc_handle, &str, &count, clear); + ret = xc_readconsolering(self->xc_handle, &str, &count, clear, + incremental, &index); if ( ret < 0 ) return pyxc_error_to_exception(); diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c index cde2ca8b36..1b9c6a58a8 100644 --- a/xen/common/sysctl.c +++ b/xen/common/sysctl.c @@ -53,10 +53,7 @@ long do_sysctl(XEN_GUEST_HANDLE(xen_sysctl_t) u_sysctl) if ( ret ) break; - ret = read_console_ring( - guest_handle_cast(op->u.readconsole.buffer, char), - &op->u.readconsole.count, - op->u.readconsole.clear); + ret = read_console_ring(&op->u.readconsole); if ( copy_to_guest(u_sysctl, op, 1) ) ret = -EFAULT; } diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c index 5d47071cd6..0bd11fc33d 100644 --- a/xen/drivers/char/console.c +++ b/xen/drivers/char/console.c @@ -33,6 +33,7 @@ #include #include #include +#include /* console: comma-separated list of console outputs. */ static char opt_console[30] = OPT_CONSOLE_STR; @@ -60,7 +61,7 @@ boolean_param("console_timestamps", opt_console_timestamps); #define CONRING_SIZE 16384 #define CONRING_IDX_MASK(i) ((i)&(CONRING_SIZE-1)) static char conring[CONRING_SIZE]; -static unsigned int conringc, conringp; +static uint32_t conringc, conringp; static int sercon_handle = -1; @@ -175,20 +176,25 @@ static char * __init loglvl_str(int lvl) static void putchar_console_ring(int c) { + ASSERT(spin_is_locked(&console_lock)); conring[CONRING_IDX_MASK(conringp++)] = c; - if ( (conringp - conringc) > CONRING_SIZE ) + if ( (uint32_t)(conringp - conringc) > CONRING_SIZE ) conringc = conringp - CONRING_SIZE; } -long read_console_ring(XEN_GUEST_HANDLE(char) str, u32 *pcount, int clear) +long read_console_ring(struct xen_sysctl_readconsole *op) { - unsigned int idx, len, max, sofar, c; - unsigned long flags; + XEN_GUEST_HANDLE(char) str; + uint32_t idx, len, max, sofar, c; - max = *pcount; + str = guest_handle_cast(op->buffer, char), + max = op->count; sofar = 0; c = conringc; + if ( op->incremental && ((int32_t)(op->index - c) < 0) ) + c = op->index; + while ( (c != conringp) && (sofar < max) ) { idx = CONRING_IDX_MASK(c); @@ -203,17 +209,19 @@ long read_console_ring(XEN_GUEST_HANDLE(char) str, u32 *pcount, int clear) c += len; } - if ( clear ) + if ( op->clear ) { - spin_lock_irqsave(&console_lock, flags); - if ( (conringp - c) > CONRING_SIZE ) + spin_lock_irq(&console_lock); + if ( (uint32_t)(conringp - c) > CONRING_SIZE ) conringc = conringp - CONRING_SIZE; else conringc = c; - spin_unlock_irqrestore(&console_lock, flags); + spin_unlock_irq(&console_lock); } - *pcount = sofar; + op->count = sofar; + op->index = c; + return 0; } @@ -333,15 +341,19 @@ static long guest_console_write(XEN_GUEST_HANDLE(char) buffer, int count) return -EFAULT; kbuf[kcount] = '\0'; + spin_lock_irq(&console_lock); + sercon_puts(kbuf); vga_puts(kbuf); if ( opt_console_to_ring ) + { for ( kptr = kbuf; *kptr != '\0'; kptr++ ) putchar_console_ring(*kptr); - - if ( opt_console_to_ring ) send_guest_global_virq(dom0, VIRQ_CON_RING); + } + + spin_unlock_irq(&console_lock); guest_handle_add_offset(buffer, kcount); count -= kcount; @@ -408,6 +420,8 @@ static void __putstr(const char *str) { int c; + ASSERT(spin_is_locked(&console_lock)); + sercon_puts(str); vga_puts(str); @@ -540,7 +554,9 @@ void __init init_console(void) serial_set_rx_handler(sercon_handle, serial_rx); /* HELLO WORLD --- start-of-day banner text. */ + spin_lock(&console_lock); __putstr(xen_banner()); + spin_unlock(&console_lock); printk("Xen version %d.%d%s (%s@%s) (%s) %s\n", xen_major_version(), xen_minor_version(), xen_extra_version(), xen_compile_by(), xen_compile_domain(), diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h index 0ea67c5b5f..59db4c49d3 100644 --- a/xen/include/public/sysctl.h +++ b/xen/include/public/sysctl.h @@ -34,18 +34,27 @@ #include "xen.h" #include "domctl.h" -#define XEN_SYSCTL_INTERFACE_VERSION 0x00000005 +#define XEN_SYSCTL_INTERFACE_VERSION 0x00000006 /* * Read console content from Xen buffer ring. */ #define XEN_SYSCTL_readconsole 1 struct xen_sysctl_readconsole { - /* IN variables. */ - uint32_t clear; /* Non-zero -> clear after reading. */ - XEN_GUEST_HANDLE_64(char) buffer; /* Buffer start */ - /* IN/OUT variables. */ - uint32_t count; /* In: Buffer size; Out: Used buffer size */ + /* IN: Non-zero -> clear after reading. */ + uint8_t clear; + /* IN: Non-zero -> start index specified by @index field. */ + uint8_t incremental; + uint8_t pad0, pad1; + /* + * IN: Start index for consuming from ring buffer (if @incremental); + * OUT: End index after consuming from ring buffer. + */ + uint32_t index; + /* IN: Virtual address to write console data. */ + XEN_GUEST_HANDLE_64(char) buffer; + /* IN: Size of buffer; OUT: Bytes written to buffer. */ + uint32_t count; }; typedef struct xen_sysctl_readconsole xen_sysctl_readconsole_t; DEFINE_XEN_GUEST_HANDLE(xen_sysctl_readconsole_t); diff --git a/xen/include/xen/console.h b/xen/include/xen/console.h index bb5fe07e93..5817f74958 100644 --- a/xen/include/xen/console.h +++ b/xen/include/xen/console.h @@ -11,7 +11,8 @@ #include #include -long read_console_ring(XEN_GUEST_HANDLE(char), u32 *, int); +struct xen_sysctl_readconsole; +long read_console_ring(struct xen_sysctl_readconsole *op); void init_console(void); void console_endboot(void); -- 2.30.2