Xenner emulates parts of libxc, so we can not use the real xen infrastructure when running xen pv guests without xen.
This patch adds support for event channel communication. Signed-off-by: Alexander Graf <ag...@suse.de> --- hw/xenner_libxc_evtchn.c | 467 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 467 insertions(+), 0 deletions(-) create mode 100644 hw/xenner_libxc_evtchn.c diff --git a/hw/xenner_libxc_evtchn.c b/hw/xenner_libxc_evtchn.c new file mode 100644 index 0000000..bb1984c --- /dev/null +++ b/hw/xenner_libxc_evtchn.c @@ -0,0 +1,467 @@ +/* + * Copyright (C) Red Hat 2007 + * Copyright (C) Novell Inc. 2010 + * + * Author(s): Gerd Hoffmann <kra...@redhat.com> + * Alexander Graf <ag...@suse.de> + * + * Xenner emulation -- event channels + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <xenctrl.h> + +#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; + QTAILQ_ENTRY(evtpriv) list; +}; +static QTAILQ_HEAD(evtpriv_head, evtpriv) privs = QTAILQ_HEAD_INITIALIZER(privs); + +static int debug = 0; + +/* ------------------------------------------------------------- */ + +static struct evtpriv *getpriv(int handle) +{ + struct evtpriv *priv; + + QTAILQ_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) { + 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++) { +#ifdef DEBUG + /* debug hack */ +#define EA_START 20 + if (priv->domain->domid && i < EA_START) + i = EA_START; +#undef 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; + int r; + + peer->count_snd++; + if (peer->pending) { + return; + } + + r = write(peer->priv->fd_write, &evtchn, sizeof(evtchn)); + if (r != sizeof(evtchn)) { + // XXX break + } + 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) { + fprintf(stderr, "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_xopen(void) +{ + struct evtpriv *priv; + int fd[2]; + + priv = qemu_mallocz(sizeof(*priv)); + QTAILQ_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); + QTAILQ_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_xopen, + .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, +}; + +/* ------------------------------------------------------------- */ + +#if 0 + +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; + } + + QTAILQ_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"); + } + } + } +} + +#endif + -- 1.6.0.2