From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LwcjX-0007Ny-6K for qemu-devel@nongnu.org; Wed, 22 Apr 2009 09:43:27 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LwcjR-0007Lv-VQ for qemu-devel@nongnu.org; Wed, 22 Apr 2009 09:43:26 -0400 Received: from [199.232.76.173] (port=57153 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LwcjR-0007Lf-F5 for qemu-devel@nongnu.org; Wed, 22 Apr 2009 09:43:21 -0400 Received: from mx2.redhat.com ([66.187.237.31]:47157) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1LwcjQ-0002cC-DA for qemu-devel@nongnu.org; Wed, 22 Apr 2009 09:43:21 -0400 Message-ID: <49EF1EE6.5080900@redhat.com> Date: Wed, 22 Apr 2009 15:43:02 +0200 From: Gerd Hoffmann MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------060901040901060308050500" Subject: [Qemu-devel] [RfC / Patch] xenner: event channel implementation. List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: "qemu-devel@nongnu.org" , Xen Development Mailing List This is a multi-part message in MIME format. --------------060901040901060308050500 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Hi, Merging the xen bits seems to be on a good way. Time to look at un-bitrotting the xenner bits ... Here is a first patch for comments. Not useful on its own. Right now I'm looking more for comments on the way the integration is done. Event channels on Xen are managed by calling the xc_evtchn_* functions provided by libxenctrl. The library in turn does does hypercalls into the xen kernel. xenner obviously has to provide an alternative implementation for these functions. Also for others. This patch starts with just the event channels though. It works this way: There is a struct with function pointers to the event channel functions. The struct can be switched at runtime to the xen or xenner version of the functions depending on the qemu operation mode. The struct is named "xc_evtchn", the function pointer are named like the xc_evtchn_* functions, but without the xc_evtchn_ prefix, i.e. "xc_evtchn_open(...)" becomes xc_evtchn.open(...). 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. Comments? cheers, Gerd --------------060901040901060308050500 Content-Type: text/plain; name="0005-xenner-event-channel-implementation.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0005-xenner-event-channel-implementation.patch" >>From a0a4cfe5040d3792875d7cd105aa2b832f6d8967 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 22 Apr 2009 12:56:04 +0200 Subject: [PATCH 5/5] xenner: event channel implementation. Signed-off-by: Gerd Hoffmann --- Makefile.target | 6 + configure | 13 ++- hw/xen.h | 2 + hw/xen_backend.h | 1 + hw/xenner_hooks.h | 21 ++ hw/xenner_interfaces.h | 28 +++ hw/xenner_libxc_evtchn.c | 455 ++++++++++++++++++++++++++++++++++++++++++++++ monitor.c | 5 + 8 files changed, 530 insertions(+), 1 deletions(-) create mode 100644 hw/xenner_hooks.h create mode 100644 hw/xenner_interfaces.h create mode 100644 hw/xenner_libxc_evtchn.c diff --git a/Makefile.target b/Makefile.target index 2171587..e630d21 100644 --- a/Makefile.target +++ b/Makefile.target @@ -568,6 +568,12 @@ ifeq ($(CONFIG_XEN), yes) LIBS += $(XEN_LIBS) endif +# xenner support +XENNER_OBJS := xenner_libxc_evtchn.o +ifeq ($(CONFIG_XENNER), yes) + OBJS += $(XENNER_OBJS) +endif + # SCSI layer OBJS+= lsi53c895a.o esp.o diff --git a/configure b/configure index b7b7b01..9eb8221 100755 --- a/configure +++ b/configure @@ -192,6 +192,7 @@ blobs="yes" fdt="yes" sdl_x11="no" xen="yes" +xenner="yes" pkgversion="" # OS specific @@ -422,7 +423,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" ;; @@ -590,6 +593,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" @@ -818,6 +822,7 @@ EOF : else xen="no" + xenner="no" fi fi @@ -1317,6 +1322,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" ] && \ @@ -1806,6 +1812,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..e9863cd 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 "xenner_hooks.h" #include "sysemu.h" #include "net.h" #include "block_int.h" diff --git a/hw/xenner_hooks.h b/hw/xenner_hooks.h new file mode 100644 index 0000000..168a474 --- /dev/null +++ b/hw/xenner_hooks.h @@ -0,0 +1,21 @@ +#ifndef QEMU_HW_XENNER_HOOKS_H +#define QEMU_HW_XENNER_HOOKS_H 1 + +#include "xenner_interfaces.h" + +#if defined(CONFIG_XENNER) + +/* 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 /* CONFIG_XENNER */ +#endif /* QEMU_HW_XENNER_HOOKS_H */ diff --git a/hw/xenner_interfaces.h b/hw/xenner_interfaces.h new file mode 100644 index 0000000..2a96ff4 --- /dev/null +++ b/hw/xenner_interfaces.h @@ -0,0 +1,28 @@ +#ifndef QEMU_HW_XENNER_INTERFACES_H +#define QEMU_HW_XENNER_INTERFACES_H 1 + +#if defined(CONFIG_XENNER) + +/* ------------------------------------------------------------- */ +/* 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; + +void xenner_evtchn_init(void); + +#endif /* CONFIG_XENNER */ +#endif /* QEMU_HW_XENNER_INTERFACES_H */ diff --git a/hw/xenner_libxc_evtchn.c b/hw/xenner_libxc_evtchn.c new file mode 100644 index 0000000..1fc49f3 --- /dev/null +++ b/hw/xenner_libxc_evtchn.c @@ -0,0 +1,455 @@ +#include + +#include "hw.h" +#include "qemu-log.h" +#include "console.h" +#include "monitor.h" +#include "xen.h" +#include "xenner_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; + int refcount; + struct port p[NR_EVENT_CHANNELS]; + TAILQ_ENTRY(domain) list; +}; +static TAILQ_HEAD(domain_head, domain) domains = TAILQ_HEAD_INITIALIZER(domains); + +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) +{ + struct domain *domain; + + TAILQ_FOREACH(domain, &domains, list) { + if (domain->domid == domid) + goto done; + } + + domain = qemu_mallocz(sizeof(*domain)); + if (domid) + domain->domid = domid; + TAILQ_INSERT_TAIL(&domains, domain, list); + if (debug) + qemu_log("xen ev: ?: new domain id %d\n", domain->domid); + +done: + domain->refcount++; + return domain; +} + +static void put_domain(struct domain *domain) +{ + domain->refcount--; + if (domain->refcount) + return; + if (debug) + qemu_log("xen ev: ?: del domain id %d\n", domain->domid); + TAILQ_REMOVE(&domains, domain, list); + qemu_free(domain); +} + +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); + put_domain(domain); +} + +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); + } + put_domain(priv->domain); + + 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; + put_domain(priv->domain); + priv->domain = get_domain(domid); + return 0; +} + +static struct XenEvtOps xenner_evtchn = { + .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, +}; + +/* ------------------------------------------------------------- */ + +static int xc_evtchn_domid(int handle, int domid) +{ + return -1; +} + +struct XenEvtOps xc_evtchn = { + .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, +}; + +/* ------------------------------------------------------------- */ + +static int evtchn_emulation; + +void do_info_evtchn(Monitor *mon) +{ + struct evtpriv *priv; + struct port *port; + int i; + + if (!evtchn_emulation) { + 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"); + } + } +} + +void xenner_evtchn_init(void) +{ + xc_evtchn = xenner_evtchn; + evtchn_emulation = 1; +} diff --git a/monitor.c b/monitor.c index 3e945db..dc7ce7a 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 --------------060901040901060308050500--