qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [RfC / Patch] xenner: event channel implementation.
@ 2009-04-22 13:43 Gerd Hoffmann
  2009-04-22 16:23 ` Avi Kivity
  2009-04-22 16:45 ` Anthony Liguori
  0 siblings, 2 replies; 6+ messages in thread
From: Gerd Hoffmann @ 2009-04-22 13:43 UTC (permalink / raw)
  To: qemu-devel@nongnu.org, Xen Development Mailing List

[-- Attachment #1: Type: text/plain, Size: 1337 bytes --]

   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

[-- Attachment #2: 0005-xenner-event-channel-implementation.patch --]
[-- Type: text/plain, Size: 17423 bytes --]

>From a0a4cfe5040d3792875d7cd105aa2b832f6d8967 Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann <kraxel@redhat.com>
Date: Wed, 22 Apr 2009 12:56:04 +0200
Subject: [PATCH 5/5] xenner: event channel implementation.


Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 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 <xenctrl.h>
+
+#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


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2009-04-28 15:01 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-04-22 13:43 [Qemu-devel] [RfC / Patch] xenner: event channel implementation Gerd Hoffmann
2009-04-22 16:23 ` Avi Kivity
2009-04-23 10:30   ` Gerd Hoffmann
2009-04-28 15:00   ` Gerd Hoffmann
2009-04-22 16:45 ` Anthony Liguori
2009-04-23 10:34   ` Gerd Hoffmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).