>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