qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 28/40] xenner: libxc emu: evtchn


From: Alexander Graf
Subject: [Qemu-devel] [PATCH 28/40] xenner: libxc emu: evtchn
Date: Mon, 1 Nov 2010 16:01:41 +0100

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 <address@hidden>
---
 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 <address@hidden>
+ *             Alexander Graf <address@hidden>
+ *
+ *  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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]