From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Lyoo2-0005XE-UJ for qemu-devel@nongnu.org; Tue, 28 Apr 2009 11:01:10 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Lyonx-0005X2-Vn for qemu-devel@nongnu.org; Tue, 28 Apr 2009 11:01:09 -0400 Received: from [199.232.76.173] (port=43436 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Lyonx-0005Wz-QM for qemu-devel@nongnu.org; Tue, 28 Apr 2009 11:01:05 -0400 Received: from mx2.redhat.com ([66.187.237.31]:43685) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1Lyonw-0005Kv-QM for qemu-devel@nongnu.org; Tue, 28 Apr 2009 11:01:05 -0400 Message-ID: <49F71A27.5050404@redhat.com> Date: Tue, 28 Apr 2009 17:00:55 +0200 From: Gerd Hoffmann MIME-Version: 1.0 Subject: Re: [Qemu-devel] [RfC / Patch] xenner: event channel implementation. References: <49EF1EE6.5080900@redhat.com> <49EF4485.4080700@redhat.com> In-Reply-To: <49EF4485.4080700@redhat.com> Content-Type: multipart/mixed; boundary="------------010503010304080408030601" List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Avi Kivity Cc: Xen Development Mailing List , "qemu-devel@nongnu.org" This is a multi-part message in MIME format. --------------010503010304080408030601 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit On 04/22/09 18:23, Avi Kivity wrote: > Gerd Hoffmann wrote: >> The function calls in the source code (xen backend drivers) are not >> changed directly, but using a include file with a bunch of #defines. >> That way I don't have to change s/xc_evtchn_/xc_evtchn./ all over the >> place. Also xenner can easily be disabled at compile time and the >> indirect function pointer calls simply go away then. > > I don't think the last bit is worthwhile. Function pointers these days > are pretty fast, their cost will be dwarfed by the syscall and hypercall > overhead. New revision of the patch. >> - --disable-xen) xen="no" >> + --disable-xen) xen="no"; xenner="no" > > It would be nice to be able to build without the original Xen libraries. Planned for. Will not work (yet) though as I'll have to emulate all xen bits, and the event channels are only a part of the whole story. > Curious, can there be any domain other than the guest and the fake dom0 > you're emulating? Simplified that. cheers, Gerd --------------010503010304080408030601 Content-Type: text/plain; name="0001-xenner-event-channel-implementation.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0001-xenner-event-channel-implementation.patch" >>From aa19020667d6ccab98eab2077042785e4a8559df Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 22 Apr 2009 12:56:04 +0200 Subject: [PATCH] xenner: event channel implementation. --- Makefile.target | 19 ++- configure | 18 ++- hw/xen.h | 2 + hw/xen_backend.h | 1 + hw/xen_interfaces.c | 53 ++++++ hw/xen_interfaces.h | 31 ++++ hw/xen_machine_pv.c | 1 + hw/xen_redirect.h | 18 ++ hw/xenner_libxc_evtchn.c | 401 ++++++++++++++++++++++++++++++++++++++++++++++ monitor.c | 5 + 10 files changed, 541 insertions(+), 8 deletions(-) create mode 100644 hw/xen_interfaces.c create mode 100644 hw/xen_interfaces.h create mode 100644 hw/xen_redirect.h create mode 100644 hw/xenner_libxc_evtchn.c diff --git a/Makefile.target b/Makefile.target index 82ada5a..4195026 100644 --- a/Makefile.target +++ b/Makefile.target @@ -561,14 +561,23 @@ ifdef CONFIG_BLUEZ LIBS += $(CONFIG_BLUEZ_LIBS) endif -# xen backend driver support -XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o xen_domainbuild.o -XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o -ifeq ($(CONFIG_XEN), yes) - OBJS += $(XEN_OBJS) +# xen backend drivers (shared by xenner+xen) +ifneq ($(CONFIG_XEN)$(CONFIG_XENNER),) + OBJS += xen_machine_pv.o xen_backend.o xen_devconfig.o xen_interfaces.o + OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o +endif + +# xen support +ifeq ($(CONFIG_XEN),yes) + OBJS += xen_domainbuild.o LIBS += $(XEN_LIBS) endif +# xenner support +ifeq ($(CONFIG_XENNER),yes) + OBJS += xenner_libxc_evtchn.o +endif + # SCSI layer OBJS+= lsi53c895a.o esp.o diff --git a/configure b/configure index 1cbeabc..b28a664 100755 --- a/configure +++ b/configure @@ -194,6 +194,7 @@ blobs="yes" fdt="yes" sdl_x11="no" xen="yes" +xenner="yes" pkgversion="" # OS specific @@ -424,7 +425,9 @@ for opt do ;; --disable-kqemu) kqemu="no" ;; - --disable-xen) xen="no" + --disable-xen) xen="no"; xenner="no" + ;; + --disable-xenner) xenner="no" ;; --disable-brlapi) brlapi="no" ;; @@ -596,6 +599,7 @@ echo " --audio-card-list=LIST set list of emulated audio cards [$audio_card_l echo " Available cards: $audio_possible_cards" echo " --enable-mixemu enable mixer emulation" echo " --disable-xen disable xen backend driver support" +echo " --disable-xenner disable xenner (xen emulation) support" echo " --disable-brlapi disable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" echo " --disable-vnc-sasl disable SASL encryption for VNC server" @@ -814,9 +818,10 @@ else fi ########################################## -# xen probe +# probe for xen libs and includes +# note: xenner included here as it needs the headers too. -if test "$xen" = "yes" ; then +if test "$xen" = "yes" -o "$xenner" = "yes"; then cat > $TMPC < #include @@ -826,6 +831,7 @@ EOF : else xen="no" + xenner="no" fi fi @@ -1330,6 +1336,7 @@ if test -n "$sparc_cpu"; then fi echo "kqemu support $kqemu" echo "xen support $xen" +echo "xenner support $xenner" echo "brlapi support $brlapi" echo "Documentation $build_docs" [ ! -z "$uname_release" ] && \ @@ -1825,6 +1832,11 @@ case "$target_cpu" in echo "CONFIG_XEN=yes" >> $config_mak echo "#define CONFIG_XEN 1" >> $config_h fi + if test "$xenner" = "yes" -a "$target_softmmu" = "yes"; + then + echo "CONFIG_XENNER=yes" >> $config_mak + echo "#define CONFIG_XENNER 1" >> $config_h + fi ;; x86_64) echo "TARGET_ARCH=x86_64" >> $config_mak diff --git a/hw/xen.h b/hw/xen.h index 3c8da41..525a179 100644 --- a/hw/xen.h +++ b/hw/xen.h @@ -17,4 +17,6 @@ enum xen_mode { extern uint32_t xen_domid; extern enum xen_mode xen_mode; +void do_info_evtchn(Monitor *mon); + #endif /* QEMU_HW_XEN_H */ diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 4dbfdb4..2011dee 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -2,6 +2,7 @@ #define QEMU_HW_XEN_BACKEND_H 1 #include "xen_common.h" +#include "xen_redirect.h" #include "sysemu.h" #include "net.h" #include "block_int.h" diff --git a/hw/xen_interfaces.c b/hw/xen_interfaces.c new file mode 100644 index 0000000..573c674 --- /dev/null +++ b/hw/xen_interfaces.c @@ -0,0 +1,53 @@ +#include + +#include "hw.h" +#include "xen.h" +#include "xen_interfaces.h" + +#ifdef CONFIG_XEN + +static int xc_evtchn_domid(int handle, int domid) +{ + return -1; +} + +static struct XenEvtOps xc_evtchn_xen = { + .open = xc_evtchn_open, + .domid = xc_evtchn_domid, + .close = xc_evtchn_close, + .fd = xc_evtchn_fd, + .notify = xc_evtchn_notify, + .bind_unbound_port = xc_evtchn_bind_unbound_port, + .bind_interdomain = xc_evtchn_bind_interdomain, + .bind_virq = xc_evtchn_bind_virq, + .unbind = xc_evtchn_unbind, + .pending = xc_evtchn_pending, + .unmask = xc_evtchn_unmask, +}; + +#endif + +struct XenEvtOps xc_evtchn; + +void xen_interfaces_init(void) +{ + switch (xen_mode) { + case XEN_ATTACH: + case XEN_CREATE: +#ifdef CONFIG_XEN + xc_evtchn = xc_evtchn_xen; +#else + fprintf(stderr, "ERROR: Compiled without xen support, sorry.\n"); + exit(1); +#endif + break; + case XEN_EMULATE: +#ifdef CONFIG_XENNER + xc_evtchn = xc_evtchn_xenner; +#else + fprintf(stderr, "ERROR: Compiled without xenner support, sorry.\n"); + exit(1); +#endif + break; + } +} diff --git a/hw/xen_interfaces.h b/hw/xen_interfaces.h new file mode 100644 index 0000000..119e0e0 --- /dev/null +++ b/hw/xen_interfaces.h @@ -0,0 +1,31 @@ +#ifndef QEMU_HW_XEN_INTERFACES_H +#define QEMU_HW_XEN_INTERFACES_H 1 + +/* ------------------------------------------------------------- */ +/* xen event channel interface */ + +struct XenEvtOps { + int (*open)(void); + int (*domid)(int xce_handle, int domid); + int (*close)(int xce_handle); + int (*fd)(int xce_handle); + int (*notify)(int xce_handle, evtchn_port_t port); + evtchn_port_or_error_t (*bind_unbound_port)(int xce_handle, int domid); + evtchn_port_or_error_t (*bind_interdomain)(int xce_handle, int domid, + evtchn_port_t remote_port); + evtchn_port_or_error_t (*bind_virq)(int xce_handle, unsigned int virq); + int (*unbind)(int xce_handle, evtchn_port_t port); + evtchn_port_or_error_t (*pending)(int xce_handle); + int (*unmask)(int xce_handle, evtchn_port_t port); +}; +extern struct XenEvtOps xc_evtchn; + +/* ------------------------------------------------------------- */ + +#ifdef CONFIG_XENNER +extern struct XenEvtOps xc_evtchn_xenner; +#endif + +void xen_interfaces_init(void); + +#endif /* QEMU_HW_XEN_INTERFACES_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 58209b8..0476ebe 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -54,6 +54,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, env->halted = 1; /* Initialize backend core & drivers */ + xen_interfaces_init(); if (xen_be_init() != 0) { fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); exit(1); diff --git a/hw/xen_redirect.h b/hw/xen_redirect.h new file mode 100644 index 0000000..ea08fd9 --- /dev/null +++ b/hw/xen_redirect.h @@ -0,0 +1,18 @@ +#ifndef QEMU_HW_XEN_REDIRECT_H +#define QEMU_HW_XEN_REDIRECT_H 1 + +#include "xen_interfaces.h" + +/* xen event channel interface */ +#define xc_evtchn_open xc_evtchn.open +#define xc_evtchn_close xc_evtchn.close +#define xc_evtchn_fd xc_evtchn.fd +#define xc_evtchn_notify xc_evtchn.notify +#define xc_evtchn_bind_unbound_port xc_evtchn.bind_unbound_port +#define xc_evtchn_bind_interdomain xc_evtchn.bind_interdomain +#define xc_evtchn_bind_virq xc_evtchn.bind_virq +#define xc_evtchn_unbind xc_evtchn.unbind +#define xc_evtchn_pending xc_evtchn.pending +#define xc_evtchn_unmask xc_evtchn.unmask + +#endif /* QEMU_HW_XEN_REDIRECT_H */ diff --git a/hw/xenner_libxc_evtchn.c b/hw/xenner_libxc_evtchn.c new file mode 100644 index 0000000..486674b --- /dev/null +++ b/hw/xenner_libxc_evtchn.c @@ -0,0 +1,401 @@ +#include +#include + +#include "hw.h" +#include "qemu-log.h" +#include "console.h" +#include "monitor.h" +#include "xen.h" +#include "xen_interfaces.h" + +/* ------------------------------------------------------------- */ + +struct evtpriv; + +struct port { + struct evtpriv *priv; + struct port *peer; + int port; + int pending; + int count_snd; + int count_fwd; + int count_msg; +}; + +struct domain { + int domid; + struct port p[NR_EVENT_CHANNELS]; +}; +static struct domain dom0; /* host */ +static struct domain domU; /* guest */ + +struct evtpriv { + int fd_read, fd_write; + struct domain *domain; + int ports; + int pending; + TAILQ_ENTRY(evtpriv) list; +}; +static TAILQ_HEAD(evtpriv_head, evtpriv) privs = TAILQ_HEAD_INITIALIZER(privs); + +static int debug = 1; + +/* ------------------------------------------------------------- */ + +static struct evtpriv *getpriv(int handle) +{ + struct evtpriv *priv; + + TAILQ_FOREACH(priv, &privs, list) { + if (priv->fd_read == handle) + return priv; + } + return NULL; +} + +static struct domain *get_domain(int domid) +{ + if (domid == 0) + return &dom0; + if (domU.domid == 0) + domU.domid = domid; + assert(domU.domid == domid); + return &domU; +} + +static struct port *alloc_port(struct evtpriv *priv, const char *reason) +{ + struct port *p = NULL; + int i; + + for (i = 1; i < NR_EVENT_CHANNELS; i++) { +#if 1 + /* debug hack */ +#define EA_START 20 + if (priv->domain->domid && i < EA_START) + i = EA_START; +#endif + if (priv->domain->p[i].priv != NULL) + continue; + p = priv->domain->p+i; + p->port = i; + p->priv = priv; + p->count_snd = 0; + p->count_fwd = 0; + p->count_msg = 1; + priv->ports++; + if (debug) + qemu_log("xen ev:%3d: alloc port %d, domain %d (%s)\n", + priv->fd_read, p->port, priv->domain->domid, reason); + return p; + } + return NULL; +} + +static void bind_port_peer(struct port *p, int domid, int port) +{ + struct domain *domain; + struct port *o; + const char *msg = "ok"; + + domain = get_domain(domid); + o = domain->p+port; + if (!o->priv) { + msg = "peer not allocated"; + } else if (o->peer) { + msg = "peer already bound"; + } else if (p->peer) { + msg = "port already bound"; + } else { + o->peer = p; + p->peer = o; + } + if (debug) + qemu_log("xen ev:%3d: bind port %d domain %d <-> port %d domain %d : %s\n", + p->priv->fd_read, + p->port, p->priv->domain->domid, + port, domid, msg); +} + +static void unbind_port(struct port *p) +{ + struct port *o; + + o = p->peer; + if (o) { + if (debug) + fprintf(stderr,"xen ev:%3d: unbind port %d domain %d <-> port %d domain %d\n", + p->priv->fd_read, + p->port, p->priv->domain->domid, + o->port, o->priv->domain->domid); + o->peer = NULL; + p->peer = NULL; + } +} + +static void notify_send_peer(struct port *peer) +{ + uint32_t evtchn = peer->port; + + peer->count_snd++; + if (peer->pending) + return; + + write(peer->priv->fd_write, &evtchn, sizeof(evtchn)); + peer->count_fwd++; + peer->pending++; + peer->priv->pending++; +} + +static void notify_port(struct port *p) +{ + if (p->peer) { + notify_send_peer(p->peer); + if (debug && p->peer->count_snd >= p->peer->count_msg) { + qemu_log("xen ev:%3d: notify port %d domain %d -> port %d domain %d | counts %d/%d\n", + p->priv->fd_read, p->port, p->priv->domain->domid, + p->peer->port, p->peer->priv->domain->domid, + p->peer->count_fwd, p->peer->count_snd); + p->peer->count_msg *= 10; + } + } else { + if (debug) + fprintf(stderr,"xen ev:%3d: notify port %d domain %d -> unconnected\n", + p->priv->fd_read, p->port, p->priv->domain->domid); + } +} + +static void unmask_port(struct port *p) +{ + /* nothing to do */ +} + +static void release_port(struct port *p) +{ + if (debug) + fprintf(stderr,"xen ev:%3d: release port %d, domain %d\n", + p->priv->fd_read, p->port, p->priv->domain->domid); + unbind_port(p); + p->priv->ports--; + p->port = 0; + p->priv = 0; +} + +/* ------------------------------------------------------------- */ + +static int qemu_open(void) +{ + struct evtpriv *priv; + int fd[2]; + + priv = qemu_mallocz(sizeof(*priv)); + TAILQ_INSERT_TAIL(&privs, priv, list); + + if (pipe(fd) < 0) + goto err; + priv->fd_read = fd[0]; + priv->fd_write = fd[1]; + fcntl(priv->fd_read,F_SETFL,O_NONBLOCK); + + priv->domain = get_domain(0); + return priv->fd_read; + +err: + qemu_free(priv); + return -1; +} + +static int qemu_close(int handle) +{ + struct evtpriv *priv = getpriv(handle); + struct port *p; + int i; + + if (!priv) + return -1; + + for (i = 1; i < NR_EVENT_CHANNELS; i++) { + p = priv->domain->p+i; + if (priv != p->priv) + continue; + release_port(p); + } + + close(priv->fd_read); + close(priv->fd_write); + TAILQ_REMOVE(&privs, priv, list); + qemu_free(priv); + return 0; +} + +static int qemu_fd(int handle) +{ + struct evtpriv *priv = getpriv(handle); + + if (!priv) + return -1; + return priv->fd_read; +} + +static int qemu_notify(int handle, evtchn_port_t port) +{ + struct evtpriv *priv = getpriv(handle); + struct port *p; + + if (!priv) + return -1; + if (port >= NR_EVENT_CHANNELS) + return -1; + p = priv->domain->p + port; + notify_port(p); + return -1; +} + +static evtchn_port_or_error_t qemu_bind_unbound_port(int handle, int domid) +{ + struct evtpriv *priv = getpriv(handle); + struct port *p; + + if (!priv) + return -1; + p = alloc_port(priv, "unbound"); + if (!p) + return -1; + return p->port; +} + +static evtchn_port_or_error_t qemu_bind_interdomain(int handle, int domid, + evtchn_port_t remote_port) +{ + struct evtpriv *priv = getpriv(handle); + struct port *p; + + if (!priv) + return -1; + if (remote_port >= NR_EVENT_CHANNELS) + return -1; + p = alloc_port(priv, "interdomain"); + if (!p) + return -1; + bind_port_peer(p, domid, remote_port); + return p->port; +} + +static evtchn_port_or_error_t qemu_bind_virq(int handle, unsigned int virq) +{ + struct evtpriv *priv = getpriv(handle); + struct port *p; + + if (!priv) + return -1; + p = alloc_port(priv, "virq"); + if (!p) + return -1; + /* + * Note: port not linked here, we only allocate some port. + */ + return p->port; +} + +static int qemu_unbind(int handle, evtchn_port_t port) +{ + struct evtpriv *priv = getpriv(handle); + struct port *p; + + if (!priv) + return -1; + if (port >= NR_EVENT_CHANNELS) + return -1; + p = priv->domain->p + port; + unbind_port(p); + release_port(p); + return 0; +} + +static evtchn_port_or_error_t qemu_pending(int handle) +{ + struct evtpriv *priv = getpriv(handle); + uint32_t evtchn; + int rc; + + if (!priv) + return -1; + rc = read(priv->fd_read, &evtchn, sizeof(evtchn)); + if (rc != sizeof(evtchn)) + return -1; + priv->pending--; + priv->domain->p[evtchn].pending--; + return evtchn; +} + +static int qemu_unmask(int handle, evtchn_port_t port) +{ + struct evtpriv *priv = getpriv(handle); + struct port *p; + + if (!priv) + return -1; + if (port >= NR_EVENT_CHANNELS) + return -1; + p = priv->domain->p + port; + unmask_port(p); + return 0; +} + +static int qemu_domid(int handle, int domid) +{ + struct evtpriv *priv = getpriv(handle); + + if (!priv) + return -1; + if (priv->ports) + return -1; + priv->domain = get_domain(domid); + return 0; +} + +struct XenEvtOps xc_evtchn_xenner = { + .open = qemu_open, + .domid = qemu_domid, + .close = qemu_close, + .fd = qemu_fd, + .notify = qemu_notify, + .bind_unbound_port = qemu_bind_unbound_port, + .bind_interdomain = qemu_bind_interdomain, + .bind_virq = qemu_bind_virq, + .unbind = qemu_unbind, + .pending = qemu_pending, + .unmask = qemu_unmask, +}; + +/* ------------------------------------------------------------- */ + +void do_info_evtchn(Monitor *mon) +{ + struct evtpriv *priv; + struct port *port; + int i; + + if (xen_mode != XEN_EMULATE) { + monitor_printf(mon, "Not emulating xen event channels.\n"); + return; + } + + TAILQ_FOREACH(priv, &privs, list) { + monitor_printf(mon, "%p: domid %d, fds %d,%d\n", priv, + priv->domain->domid, + priv->fd_read, priv->fd_write); + for (i = 1; i < NR_EVENT_CHANNELS; i++) { + port = priv->domain->p + i; + if (port->priv != priv) + continue; + monitor_printf(mon, " port #%d: ", port->port); + if (port->peer) + monitor_printf(mon, "peer #%d (%p, domid %d)\n", + port->peer->port, port->peer->priv, + port->peer->priv->domain->domid); + else + monitor_printf(mon, "no peer\n"); + } + } +} diff --git a/monitor.c b/monitor.c index b33fea1..33b0dcc 100644 --- a/monitor.c +++ b/monitor.c @@ -27,6 +27,7 @@ #include "hw/pcmcia.h" #include "hw/pc.h" #include "hw/pci.h" +#include "hw/xen.h" #include "gdbstub.h" #include "net.h" #include "qemu-char.h" @@ -1846,6 +1847,10 @@ static const mon_cmd_t info_cmds[] = { { "migrate", "", do_info_migrate, "", "show migration status" }, { "balloon", "", do_info_balloon, "", "show balloon information" }, +#if defined(CONFIG_XENNER) + { "evtchn", "", do_info_evtchn, + "", "show xenner event channel information" }, +#endif { NULL, NULL, }, }; -- 1.6.2.2 --------------010503010304080408030601--