From c4c3609aeba667498cbaee93506f87ff79bb40ea Mon Sep 17 00:00:00 2001
From: Roger Pau Monne <roger.pau@citrix.com>
Date: Thu, 19 Jun 2014 13:06:01 +0200
Subject: [PATCH v5 20/29] xen: import a proper event channel user-space
 device

The user-space event channel device is used by applications to receive
and send event channel interrupts. This device is based on the Linux
evtchn device.

Approved by: xxx
Sponsored by: Citrix Systems R&D

xen/evtchn/evtchn_dev.c:
 - Remove the old event channel device, which was already disabled in
   the build system.

dev/xen/evtchn/evtchn_dev.c:
 - Import a new event channel device based on the one present in
   Linux.
 - This device allows the following operations:
   - Bind VIRQ event channels (ioctl).
   - Bind regular event channels (ioctl).
   - Create and bind new event channels (ioctl).
   - Unbind event channels (ioctl).
   - Send notifications to event channels (ioctl).
   - Reset the device shared memory ring (ioctl).
   - Unmask event channels (write).
   - Receive event channel upcalls (read).
 - The new code is MP safe, and can be used concurrently.

conf/files:
 - Add the new device to the build system.
---
 sys/conf/files                  |    1 +
 sys/dev/xen/evtchn/evtchn_dev.c |  605 +++++++++++++++++++++++++++++++++++++++
 sys/xen/evtchn/evtchn_dev.c     |  358 -----------------------
 3 files changed, 606 insertions(+), 358 deletions(-)
 create mode 100644 sys/dev/xen/evtchn/evtchn_dev.c
 delete mode 100644 sys/xen/evtchn/evtchn_dev.c

diff --git a/sys/conf/files b/sys/conf/files
index 147452e..ec03504 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -2593,6 +2593,7 @@ dev/xen/pvcpu/pvcpu.c		optional xen | xenhvm
 dev/xen/xenstore/xenstore.c	optional xen | xenhvm
 dev/xen/xenstore/xenstore_dev.c	optional xen | xenhvm
 dev/xen/xenstore/xenstored_dev.c	optional xen | xenhvm
+dev/xen/evtchn/evtchn_dev.c	optional xen | xenhvm
 dev/xl/if_xl.c			optional xl pci
 dev/xl/xlphy.c			optional xl pci
 fs/deadfs/dead_vnops.c		standard
diff --git a/sys/dev/xen/evtchn/evtchn_dev.c b/sys/dev/xen/evtchn/evtchn_dev.c
new file mode 100644
index 0000000..aadd581
--- /dev/null
+++ b/sys/dev/xen/evtchn/evtchn_dev.c
@@ -0,0 +1,605 @@
+/******************************************************************************
+ * evtchn.c
+ *
+ * Driver for receiving and demuxing event-channel signals.
+ *
+ * Copyright (c) 2004-2005, K A Fraser
+ * Multi-process extensions Copyright (c) 2004, Steven Smith
+ * FreeBSD port Copyright (c) 2014, Roger Pau Monné
+ * Fetched from git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
+ * File: drivers/xen/evtchn.c
+ * Git commit: 0dc0064add422bc0ef5165ebe9ece3052bbd457d
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+#include <sys/selinfo.h>
+#include <sys/poll.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/ioccom.h>
+#include <sys/rman.h>
+#include <sys/tree.h>
+#include <sys/module.h>
+#include <sys/filio.h>
+#include <sys/vnode.h>
+
+#include <machine/intr_machdep.h>
+#include <machine/xen/synch_bitops.h>
+
+#include <xen/xen-os.h>
+#include <xen/evtchn.h>
+#include <xen/xen_intr.h>
+
+#include <xen/evtchn/evtchnvar.h>
+
+MALLOC_DEFINE(M_EVTCHN, "evtchn_dev", "Xen event channel user-space device");
+
+struct user_evtchn;
+
+static int evtchn_cmp(struct user_evtchn *u1, struct user_evtchn *u2);
+
+RB_HEAD(evtchn_tree, user_evtchn);
+
+struct per_user_data {
+	struct mtx bind_mutex; /* serialize bind/unbind operations */
+	struct evtchn_tree evtchns;
+
+	/* Notification ring, accessed via /dev/xen/evtchn. */
+#define EVTCHN_RING_SIZE     (PAGE_SIZE / sizeof(evtchn_port_t))
+#define EVTCHN_RING_MASK(_i) ((_i)&(EVTCHN_RING_SIZE-1))
+	evtchn_port_t *ring;
+	unsigned int ring_cons, ring_prod, ring_overflow;
+	struct sx ring_cons_mutex; /* protect against concurrent readers */
+	struct mtx ring_prod_mutex; /* product against concurrent interrupts */
+	struct selinfo ev_rsel;
+};
+
+struct user_evtchn {
+	RB_ENTRY(user_evtchn) node;
+	struct per_user_data *user;
+	evtchn_port_t port;
+	xen_intr_handle_t handle;
+	bool enabled;
+};
+
+RB_GENERATE_STATIC(evtchn_tree, user_evtchn, node, evtchn_cmp);
+
+static device_t evtchn_dev;
+
+static d_read_t      evtchn_read;
+static d_write_t     evtchn_write;
+static d_ioctl_t     evtchn_ioctl;
+static d_poll_t      evtchn_poll;
+static d_open_t      evtchn_open;
+
+static void evtchn_release(void *arg);
+
+static struct cdevsw evtchn_devsw = {
+	.d_version = D_VERSION,
+	.d_open = evtchn_open,
+	.d_read = evtchn_read,
+	.d_write = evtchn_write,
+	.d_ioctl = evtchn_ioctl,
+	.d_poll = evtchn_poll,
+	.d_name = "evtchn",
+};
+
+/*------------------------- Red-black tree helpers ---------------------------*/
+static int
+evtchn_cmp(struct user_evtchn *u1, struct user_evtchn *u2)
+{
+
+	return (u1->port - u2->port);
+}
+
+static struct user_evtchn *
+find_evtchn(struct per_user_data *u, evtchn_port_t port)
+{
+	struct user_evtchn tmp = {
+		.port = port,
+	};
+
+	return (RB_FIND(evtchn_tree, &u->evtchns, &tmp));
+}
+
+/*--------------------------- Interrupt handlers -----------------------------*/
+static int
+evtchn_filter(void *arg)
+{
+	struct user_evtchn *evtchn;
+
+	evtchn = arg;
+
+	if (!evtchn->enabled && bootverbose) {
+		device_printf(evtchn_dev,
+		    "Received upcall for disabled event channel %d\n",
+		    evtchn->port);
+	}
+
+	evtchn_mask_port(evtchn->port);
+	evtchn->enabled = false;
+
+	return (FILTER_SCHEDULE_THREAD);
+}
+
+static void
+evtchn_interrupt(void *arg)
+{
+	struct user_evtchn *evtchn;
+	struct per_user_data *u;
+
+	evtchn = arg;
+	u = evtchn->user;
+
+	/*
+	 * Protect against concurrent events using this handler
+	 * on different CPUs.
+	 */
+	mtx_lock(&u->ring_prod_mutex);
+	if ((u->ring_prod - u->ring_cons) < EVTCHN_RING_SIZE) {
+		u->ring[EVTCHN_RING_MASK(u->ring_prod)] = evtchn->port;
+		wmb(); /* Ensure ring contents visible */
+		if (u->ring_cons == u->ring_prod++) {
+			wakeup(u);
+			selwakeup(&u->ev_rsel);
+		}
+	} else
+		u->ring_overflow = 1;
+	mtx_unlock(&u->ring_prod_mutex);
+}
+
+/*------------------------- Character device methods -------------------------*/
+static int
+evtchn_open(struct cdev *dev, int flag, int otyp, struct thread *td)
+{
+	struct per_user_data *u;
+	int error;
+
+	u = malloc(sizeof(*u), M_EVTCHN, M_WAITOK | M_ZERO);
+	u->ring = malloc(PAGE_SIZE, M_EVTCHN, M_WAITOK | M_ZERO);
+
+	/* Initialize locks */
+	mtx_init(&u->bind_mutex, "evtchn_bind_mutex", NULL, MTX_DEF);
+	sx_init(&u->ring_cons_mutex, "evtchn_ringc_sx");
+	mtx_init(&u->ring_prod_mutex, "evtchn_ringp_mutex", NULL, MTX_DEF);
+
+	/* Initialize red-black tree. */
+	RB_INIT(&u->evtchns);
+
+	/* Assign the allocated per_user_data to this open instance. */
+	error = devfs_set_cdevpriv(u, evtchn_release);
+	if (error != 0) {
+		mtx_destroy(&u->bind_mutex);
+		mtx_destroy(&u->ring_prod_mutex);
+		sx_destroy(&u->ring_cons_mutex);
+		free(u->ring, M_EVTCHN);
+		free(u, M_EVTCHN);
+	}
+
+	return (error);
+}
+
+static void
+evtchn_release(void *arg)
+{
+	struct per_user_data *u;
+	struct user_evtchn *evtchn, *tmp;
+
+	u = arg;
+
+	seldrain(&u->ev_rsel);
+
+	RB_FOREACH_SAFE(evtchn, evtchn_tree, &u->evtchns, tmp) {
+		xen_intr_unbind(&evtchn->handle);
+
+		RB_REMOVE(evtchn_tree, &u->evtchns, evtchn);
+		free(evtchn, M_EVTCHN);
+	}
+
+	mtx_destroy(&u->bind_mutex);
+	mtx_destroy(&u->ring_prod_mutex);
+	sx_destroy(&u->ring_cons_mutex);
+	free(u->ring, M_EVTCHN);
+	free(u, M_EVTCHN);
+}
+
+static int
+evtchn_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+	int error, count;
+	unsigned int c, p, bytes1 = 0, bytes2 = 0;
+	struct per_user_data *u;
+
+	error = devfs_get_cdevpriv((void **)&u);
+	if (error != 0)
+		return (EINVAL);
+
+	/* Whole number of ports. */
+	count = uio->uio_resid;
+	count &= ~(sizeof(evtchn_port_t)-1);
+
+	if (count == 0)
+		return (0);
+
+	if (count > PAGE_SIZE)
+		count = PAGE_SIZE;
+
+	sx_xlock(&u->ring_cons_mutex);
+	for (;;) {
+		error = EFBIG;
+		if (u->ring_overflow)
+			goto unlock_out;
+
+		c = u->ring_cons;
+		p = u->ring_prod;
+		if (c != p)
+			break;
+
+		if (ioflag & IO_NDELAY) {
+			sx_xunlock(&u->ring_cons_mutex);
+			return (EWOULDBLOCK);
+		}
+
+		error = sx_sleep(u, &u->ring_cons_mutex, PCATCH, "evtchw", 0);
+		if ((error != 0) && (error != EWOULDBLOCK))
+			return (error);
+	}
+
+	/* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
+	if (((c ^ p) & EVTCHN_RING_SIZE) != 0) {
+		bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) *
+		    sizeof(evtchn_port_t);
+		bytes2 = EVTCHN_RING_MASK(p) * sizeof(evtchn_port_t);
+	} else {
+		bytes1 = (p - c) * sizeof(evtchn_port_t);
+		bytes2 = 0;
+	}
+
+	/* Truncate chunks according to caller's maximum byte count. */
+	if (bytes1 > count) {
+		bytes1 = count;
+		bytes2 = 0;
+	} else if ((bytes1 + bytes2) > count) {
+		bytes2 = count - bytes1;
+	}
+
+	error = EFAULT;
+	rmb(); /* Ensure that we see the port before we copy it. */
+
+	if (uiomove(&u->ring[EVTCHN_RING_MASK(c)], bytes1, uio) ||
+	    ((bytes2 != 0) && uiomove(&u->ring[0], bytes2, uio)))
+		goto unlock_out;
+
+	u->ring_cons += (bytes1 + bytes2) / sizeof(evtchn_port_t);
+	error = 0;
+
+unlock_out:
+	sx_xunlock(&u->ring_cons_mutex);
+	return (error);
+}
+
+static int
+evtchn_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+	int error, i, count;
+	evtchn_port_t *kbuf;
+	struct per_user_data *u;
+
+	error = devfs_get_cdevpriv((void **)&u);
+	if (error != 0)
+		return (EINVAL);
+
+	kbuf = malloc(PAGE_SIZE, M_EVTCHN, M_WAITOK);
+	if (kbuf == NULL)
+		return (ENOMEM);
+
+	count = uio->uio_resid;
+	/* Whole number of ports. */
+	count &= ~(sizeof(evtchn_port_t)-1);
+
+	error = 0;
+	if (count == 0)
+		goto out;
+
+	if (count > PAGE_SIZE)
+		count = PAGE_SIZE;
+
+	error = uiomove(kbuf, count, uio);
+	if (error != 0)
+		goto out;
+
+	mtx_lock(&u->bind_mutex);
+
+	for (i = 0; i < (count/sizeof(evtchn_port_t)); i++) {
+		evtchn_port_t port = kbuf[i];
+		struct user_evtchn *evtchn;
+
+		evtchn = find_evtchn(u, port);
+		if (evtchn && !evtchn->enabled) {
+			evtchn->enabled = true;
+			evtchn_unmask_port(evtchn->port);
+		}
+	}
+
+	mtx_unlock(&u->bind_mutex);
+	error = 0;
+
+out:
+	free(kbuf, M_EVTCHN);
+	return (error);
+}
+
+static inline int
+evtchn_bind_user_port(struct per_user_data *u, struct user_evtchn *evtchn)
+{
+	int error;
+
+	evtchn->port = xen_intr_port(evtchn->handle);
+	evtchn->user = u;
+	evtchn->enabled = true;
+	mtx_lock(&u->bind_mutex);
+	RB_INSERT(evtchn_tree, &u->evtchns, evtchn);
+	mtx_unlock(&u->bind_mutex);
+	error = xen_intr_add_handler(evtchn_dev, evtchn_filter,
+	    evtchn_interrupt, evtchn, INTR_TYPE_MISC, evtchn->handle);
+	if (error != 0) {
+		xen_intr_unbind(&evtchn->handle);
+		mtx_lock(&u->bind_mutex);
+		RB_REMOVE(evtchn_tree, &u->evtchns, evtchn);
+		mtx_unlock(&u->bind_mutex);
+		free(evtchn, M_EVTCHN);
+	}
+	return (error);
+}
+
+static int
+evtchn_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg,
+    int mode, struct thread *td __unused)
+{
+	struct per_user_data *u;
+	int error;
+
+	error = devfs_get_cdevpriv((void **)&u);
+	if (error != 0)
+		return (EINVAL);
+
+	switch (cmd) {
+	case IOCTL_EVTCHN_BIND_VIRQ: {
+		struct ioctl_evtchn_bind_virq *bind;
+		struct user_evtchn *evtchn;
+
+		evtchn = malloc(sizeof(*evtchn), M_EVTCHN, M_WAITOK | M_ZERO);
+
+		bind = (struct ioctl_evtchn_bind_virq *)arg;
+
+		error = xen_intr_bind_virq(evtchn_dev, bind->virq, 0,
+		    NULL, NULL, NULL, 0, &evtchn->handle);
+		if (error != 0) {
+			free(evtchn, M_EVTCHN);
+			break;
+		}
+		error = evtchn_bind_user_port(u, evtchn);
+		if (error != 0)
+			break;
+		bind->port = evtchn->port;
+		break;
+	}
+
+	case IOCTL_EVTCHN_BIND_INTERDOMAIN: {
+		struct ioctl_evtchn_bind_interdomain *bind;
+		struct user_evtchn *evtchn;
+
+		evtchn = malloc(sizeof(*evtchn), M_EVTCHN, M_WAITOK | M_ZERO);
+
+		bind = (struct ioctl_evtchn_bind_interdomain *)arg;
+
+		error = xen_intr_bind_remote_port(evtchn_dev,
+		    bind->remote_domain, bind->remote_port, NULL,
+		    NULL, NULL, 0, &evtchn->handle);
+		if (error != 0) {
+			free(evtchn, M_EVTCHN);
+			break;
+		}
+		error = evtchn_bind_user_port(u, evtchn);
+		if (error != 0)
+			break;
+		bind->port = evtchn->port;
+		break;
+	}
+
+	case IOCTL_EVTCHN_BIND_UNBOUND_PORT: {
+		struct ioctl_evtchn_bind_unbound_port *bind;
+		struct user_evtchn *evtchn;
+
+		evtchn = malloc(sizeof(*evtchn), M_EVTCHN, M_WAITOK | M_ZERO);
+
+		bind = (struct ioctl_evtchn_bind_unbound_port *)arg;
+
+		error = xen_intr_alloc_and_bind_local_port(evtchn_dev,
+		    bind->remote_domain, NULL, NULL, NULL, 0, &evtchn->handle);
+		if (error != 0) {
+			free(evtchn, M_EVTCHN);
+			break;
+		}
+		error = evtchn_bind_user_port(u, evtchn);
+		if (error != 0)
+			break;
+		bind->port = evtchn->port;
+		break;
+	}
+
+	case IOCTL_EVTCHN_UNBIND: {
+		struct ioctl_evtchn_unbind *unbind;
+		struct user_evtchn *evtchn;
+
+		unbind = (struct ioctl_evtchn_unbind *)arg;
+
+		mtx_lock(&u->bind_mutex);
+		evtchn = find_evtchn(u, unbind->port);
+		if (evtchn == NULL) {
+			error = ENOTCONN;
+			break;
+		}
+
+		xen_intr_unbind(&evtchn->handle);
+		RB_REMOVE(evtchn_tree, &u->evtchns, evtchn);
+		mtx_unlock(&u->bind_mutex);
+		free(evtchn, M_EVTCHN);
+		error = 0;
+		break;
+	}
+
+	case IOCTL_EVTCHN_NOTIFY: {
+		struct ioctl_evtchn_notify *notify;
+		struct user_evtchn *evtchn;
+
+		notify = (struct ioctl_evtchn_notify *)arg;
+
+		mtx_lock(&u->bind_mutex);
+		evtchn = find_evtchn(u, notify->port);
+		if (evtchn == NULL) {
+			error = ENOTCONN;
+			break;
+		}
+
+		xen_intr_signal(evtchn->handle);
+		mtx_unlock(&u->bind_mutex);
+		error = 0;
+		break;
+	}
+
+	case IOCTL_EVTCHN_RESET: {
+		/* Initialise the ring to empty. Clear errors. */
+		sx_xlock(&u->ring_cons_mutex);
+		mtx_lock(&u->ring_prod_mutex);
+		u->ring_cons = u->ring_prod = u->ring_overflow = 0;
+		mtx_unlock(&u->ring_prod_mutex);
+		sx_xunlock(&u->ring_cons_mutex);
+		error = 0;
+		break;
+	}
+
+	case FIONBIO:
+	case FIOASYNC:
+		/* Handled in an upper layer */
+		error = 0;
+		break;
+
+	default:
+		error = ENOTTY;
+		break;
+	}
+
+	return (error);
+}
+
+static int
+evtchn_poll(struct cdev *dev, int events, struct thread *td)
+{
+	struct per_user_data *u;
+	int error, mask;
+
+	error = devfs_get_cdevpriv((void **)&u);
+	if (error != 0)
+		return (POLLERR);
+
+	/* we can always write */
+	mask = events & (POLLOUT | POLLWRNORM);
+
+	mtx_lock(&u->ring_prod_mutex);
+	if (events & (POLLIN | POLLRDNORM)) {
+		if (u->ring_cons != u->ring_prod) {
+			mask |= events & (POLLIN | POLLRDNORM);
+		} else {
+			/* Record that someone is waiting */
+			selrecord(td, &u->ev_rsel);
+		}
+	}
+	mtx_unlock(&u->ring_prod_mutex);
+
+	return (mask);
+}
+
+/*------------------ Private Device Attachment Functions  --------------------*/
+static void
+evtchn_identify(driver_t *driver, device_t parent)
+{
+
+	KASSERT((xen_domain()),
+	    ("Trying to attach evtchn device on non Xen domain"));
+
+	evtchn_dev = BUS_ADD_CHILD(parent, 0, "evtchn", 0);
+	if (evtchn_dev == NULL)
+		panic("unable to attach evtchn user-space device");
+}
+
+static int
+evtchn_probe(device_t dev)
+{
+
+	device_set_desc(dev, "Xen event channel user-space device");
+	return (BUS_PROBE_NOWILDCARD);
+}
+
+static int
+evtchn_attach(device_t dev)
+{
+
+	make_dev(&evtchn_devsw, 0, UID_ROOT, GID_WHEEL, 0600, "xen/evtchn");
+	return (0);
+}
+
+/*-------------------- Private Device Attachment Data  -----------------------*/
+static device_method_t evtchn_methods[] = {
+	DEVMETHOD(device_identify, evtchn_identify),
+	DEVMETHOD(device_probe, evtchn_probe),
+	DEVMETHOD(device_attach, evtchn_attach),
+
+	DEVMETHOD_END
+};
+
+static driver_t evtchn_driver = {
+	"evtchn",
+	evtchn_methods,
+	0,
+};
+
+devclass_t evtchn_devclass;
+
+DRIVER_MODULE(evtchn, xenpv, evtchn_driver, evtchn_devclass, 0, 0);
+MODULE_DEPEND(evtchn, xenpv, 1, 1, 1);
diff --git a/sys/xen/evtchn/evtchn_dev.c b/sys/xen/evtchn/evtchn_dev.c
deleted file mode 100644
index 9da2642..0000000
--- a/sys/xen/evtchn/evtchn_dev.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/******************************************************************************
- * evtchn.c
- * 
- * Xenolinux driver for receiving and demuxing event-channel signals.
- * 
- * Copyright (c) 2004, K A Fraser
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/uio.h>
-#include <sys/bus.h>
-#include <sys/malloc.h>
-#include <sys/kernel.h>
-#include <sys/lock.h>
-#include <sys/mutex.h>
-#include <sys/selinfo.h>
-#include <sys/poll.h>
-#include <sys/conf.h>
-#include <sys/fcntl.h>
-#include <sys/ioccom.h>
-#include <sys/rman.h>
-
-#include <xen/xen-os.h>
-#include <xen/evtchn.h>
-#include <xen/xen_intr.h>
-
-#include <machine/bus.h>
-#include <machine/resource.h>
-#include <machine/xen/synch_bitops.h>
-
-#include <xen/evtchn/evtchnvar.h>
-
-typedef struct evtchn_sotfc {
-
-	struct selinfo  ev_rsel;
-} evtchn_softc_t;
-
-/* Only one process may open /dev/xen/evtchn at any time. */
-static unsigned long evtchn_dev_inuse;
-
-/* Notification ring, accessed via /dev/xen/evtchn. */
-
-#define EVTCHN_RING_SIZE     2048  /* 2048 16-bit entries */
-
-#define EVTCHN_RING_MASK(_i) ((_i)&(EVTCHN_RING_SIZE-1))
-static uint16_t *ring;
-static unsigned int ring_cons, ring_prod, ring_overflow;
-
-/* Which ports is user-space bound to? */
-static uint32_t bound_ports[32];
-
-/* Unique address for processes to sleep on */
-static void *evtchn_waddr = &ring;
-
-static struct mtx lock, upcall_lock;
-
-static d_read_t      evtchn_read;
-static d_write_t     evtchn_write;
-static d_ioctl_t     evtchn_ioctl;
-static d_poll_t      evtchn_poll;
-static d_open_t      evtchn_open;
-static d_close_t     evtchn_close;
-
-
-void 
-evtchn_device_upcall(evtchn_port_t port)
-{
-	mtx_lock(&upcall_lock);
-
-	evtchn_mask_port(port);
-	evtchn_clear_port(port);
-
-	if ( ring != NULL ) {
-		if ( (ring_prod - ring_cons) < EVTCHN_RING_SIZE ) {
-			ring[EVTCHN_RING_MASK(ring_prod)] = (uint16_t)port;
-			if ( ring_cons == ring_prod++ ) {
-				wakeup(evtchn_waddr);
-			}
-		}
-		else {
-			ring_overflow = 1;
-		}
-	}
-
-	mtx_unlock(&upcall_lock);
-}
-
-static void 
-__evtchn_reset_buffer_ring(void)
-{
-	/* Initialise the ring to empty. Clear errors. */
-	ring_cons = ring_prod = ring_overflow = 0;
-}
-
-static int
-evtchn_read(struct cdev *dev, struct uio *uio, int ioflag)
-{
-	int rc;
-	unsigned int count, c, p, sst = 0, bytes1 = 0, bytes2 = 0;
-	count = uio->uio_resid;
-    
-	count &= ~1; /* even number of bytes */
-
-	if ( count == 0 )
-	{
-		rc = 0;
-		goto out;
-	}
-
-	if ( count > PAGE_SIZE )
-		count = PAGE_SIZE;
-
-	for ( ; ; ) {
-		if ( (c = ring_cons) != (p = ring_prod) )
-			break;
-
-		if ( ring_overflow ) {
-			rc = EFBIG;
-			goto out;
-		}
-
-		if (sst != 0) {
-			rc = EINTR;
-			goto out;
-		}
-
-		/* PCATCH == check for signals before and after sleeping 
-		 * PWAIT == priority of waiting on resource 
-		 */
-		sst = tsleep(evtchn_waddr, PWAIT|PCATCH, "evchwt", 10);
-	}
-
-	/* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
-	if ( ((c ^ p) & EVTCHN_RING_SIZE) != 0 ) {
-		bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) * sizeof(uint16_t);
-		bytes2 = EVTCHN_RING_MASK(p) * sizeof(uint16_t);
-	}
-	else {
-		bytes1 = (p - c) * sizeof(uint16_t);
-		bytes2 = 0;
-	}
-
-	/* Truncate chunks according to caller's maximum byte count. */
-	if ( bytes1 > count ) {
-		bytes1 = count;
-		bytes2 = 0;
-	}
-	else if ( (bytes1 + bytes2) > count ) {
-		bytes2 = count - bytes1;
-	}
-    
-	if ( uiomove(&ring[EVTCHN_RING_MASK(c)], bytes1, uio) ||
-	     ((bytes2 != 0) && uiomove(&ring[0], bytes2, uio)))
-		/* keeping this around as its replacement is not equivalent 
-		 * copyout(&ring[0], &buf[bytes1], bytes2) 
-		 */
-	{
-		rc = EFAULT;
-		goto out;
-	}
-
-	ring_cons += (bytes1 + bytes2) / sizeof(uint16_t);
-
-	rc = bytes1 + bytes2;
-
- out:
-    
-	return rc;
-}
-
-static int 
-evtchn_write(struct cdev *dev, struct uio *uio, int ioflag)
-{
-	int  rc, i, count;
-    
-	count = uio->uio_resid;
-    
-	uint16_t *kbuf = (uint16_t *)malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK);
-
-
-	if ( kbuf == NULL )
-		return ENOMEM;
-
-	count &= ~1; /* even number of bytes */
-
-	if ( count == 0 ) {
-		rc = 0;
-		goto out;
-	}
-
-	if ( count > PAGE_SIZE )
-		count = PAGE_SIZE;
-
-	if ( uiomove(kbuf, count, uio) != 0 ) {
-		rc = EFAULT;
-		goto out;
-	}
-
-	mtx_lock_spin(&lock);
-	for ( i = 0; i < (count/2); i++ )
-		if ( test_bit(kbuf[i], &bound_ports[0]) )
-			evtchn_unmask_port(kbuf[i]);
-	mtx_unlock_spin(&lock);
-
-	rc = count;
-
- out:
-	free(kbuf, M_DEVBUF);
-	return rc;
-}
-
-static int 
-evtchn_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg, 
-	     int mode, struct thread *td __unused)
-{
-	int rc = 0;
-    
-#ifdef NOTYET
-	mtx_lock_spin(&lock);
-    
-	switch ( cmd )
-	{
-	case EVTCHN_RESET:
-		__evtchn_reset_buffer_ring();
-		break;
-	case EVTCHN_BIND:
-		if ( !synch_test_and_set_bit((uintptr_t)arg, &bound_ports[0]) )
-			unmask_evtchn((uintptr_t)arg);
-		else
-			rc = EINVAL;
-		break;
-	case EVTCHN_UNBIND:
-		if ( synch_test_and_clear_bit((uintptr_t)arg, &bound_ports[0]) )
-			mask_evtchn((uintptr_t)arg);
-		else
-			rc = EINVAL;
-		break;
-	default:
-		rc = ENOSYS;
-		break;
-	}
-
-	mtx_unlock_spin(&lock);   
-#endif
-
-	return rc;
-}
-
-static int
-evtchn_poll(struct cdev *dev, int poll_events, struct thread *td)
-{
-
-	evtchn_softc_t *sc;
-	unsigned int mask = POLLOUT | POLLWRNORM;
-    
-	sc = dev->si_drv1;
-    
-	if ( ring_cons != ring_prod )
-		mask |= POLLIN | POLLRDNORM;
-	else if ( ring_overflow )
-		mask = POLLERR;
-	else
-		selrecord(td, &sc->ev_rsel);
-
-
-	return mask;
-}
-
-
-static int 
-evtchn_open(struct cdev *dev, int flag, int otyp, struct thread *td)
-{
-	uint16_t *_ring;
-    
-	if (flag & O_NONBLOCK)
-		return EBUSY;
-
-	if ( synch_test_and_set_bit(0, &evtchn_dev_inuse) )
-		return EBUSY;
-
-	if ( (_ring = (uint16_t *)malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK)) == NULL )
-		return ENOMEM;
-
-	mtx_lock_spin(&lock);
-	ring = _ring;
-	__evtchn_reset_buffer_ring();
-	mtx_unlock_spin(&lock);
-
-
-	return 0;
-}
-
-static int 
-evtchn_close(struct cdev *dev, int flag, int otyp, struct thread *td __unused)
-{
-	int i;
-
-	if (ring != NULL) {
-		free(ring, M_DEVBUF);
-		ring = NULL;
-	}
-	mtx_lock_spin(&lock);
-	for ( i = 0; i < NR_EVENT_CHANNELS; i++ )
-		if ( synch_test_and_clear_bit(i, &bound_ports[0]) )
-			evtchn_mask_port(i);
-	mtx_unlock_spin(&lock);
-
-	evtchn_dev_inuse = 0;
-
-	return 0;
-}
-
-static struct cdevsw evtchn_devsw = {
-	.d_version =	D_VERSION,
-	.d_open =	evtchn_open,
-	.d_close =	evtchn_close,
-	.d_read =	evtchn_read,
-	.d_write =	evtchn_write,
-	.d_ioctl =	evtchn_ioctl,
-	.d_poll =	evtchn_poll,
-	.d_name =	"evtchn",
-};
-
-
-/* XXX  - if this device is ever supposed to support use by more than one process
- * this global static will have to go away
- */
-static struct cdev *evtchn_dev;
-
-
-
-static int 
-evtchn_dev_init(void *dummy __unused)
-{
-	/* XXX I believe we don't need these leaving them here for now until we 
-	 * have some semblance of it working 
-	 */
-	mtx_init(&upcall_lock, "evtchup", NULL, MTX_DEF);
-
-	/* (DEVFS) create '/dev/misc/evtchn'. */
-	evtchn_dev = make_dev(&evtchn_devsw, 0, UID_ROOT, GID_WHEEL, 0600, "xen/evtchn");
-
-	mtx_init(&lock, "evch", NULL, MTX_SPIN | MTX_NOWITNESS);
-
-	evtchn_dev->si_drv1 = malloc(sizeof(evtchn_softc_t), M_DEVBUF, M_WAITOK);
-	bzero(evtchn_dev->si_drv1, sizeof(evtchn_softc_t));
-
-	if (bootverbose)
-		printf("Event-channel device installed.\n");
-
-	return 0;
-}
-
-SYSINIT(evtchn_dev_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, evtchn_dev_init, NULL);
-- 
1.7.7.5 (Apple Git-26)

