From f8455aab65fe84fe90e9c6928703398da629ccdb Mon Sep 17 00:00:00 2001
From: Roger Pau Monne <roger.pau@citrix.com>
Date: Thu, 19 Jun 2014 13:06:00 +0200
Subject: [PATCH v5 19/29] xen: allow to register event channels without
 handlers

This is needed by the event channel user-space device, that requires
registering event channels without unmasking them. intr_add_handler
will unconditionally unmask the event channel, so we avoid calling it
if no filter/handler is provided, and then the user will be in charge
of calling it when ready.

In order to do this, we need to change the opaque type
xen_intr_handle_t to contain the event channel port instead of the
opaque cookie returned by intr_add_handler, since now registration of
event channels without handlers are allowed. The cookie will now be
stored inside of the private xenisrc struct. Also, introduce a new
function called xen_intr_add_handler that allows adding a
filter/handler after the event channel has been registered.

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

x86/xen/xen_intr.c:
 - Leave the event channel without a handler if no filter/handler is
   provided to xen_intr_bind_isrc.
 - Don't perform an evtchn_mask_port, intr_add_handler will already do
   it.
 - Change the opaque type xen_intr_handle_t to contain a pointer to
   the event channel port number, and make the necessary changes to
   related functions.
 - Introduce a new function called xen_intr_add_handler that can be
   used to add filter/handlers to an event channel after registration.

xen/xen_intr.h:
 - Add prototype of xen_intr_add_handler.
---
 sys/x86/xen/xen_intr.c |   73 ++++++++++++++++++++++++++++++++++++-----------
 sys/xen/xen_intr.h     |   19 ++++++++++++
 2 files changed, 75 insertions(+), 17 deletions(-)

diff --git a/sys/x86/xen/xen_intr.c b/sys/x86/xen/xen_intr.c
index e652511..1805f31 100644
--- a/sys/x86/xen/xen_intr.c
+++ b/sys/x86/xen/xen_intr.c
@@ -104,10 +104,11 @@ DPCPU_DEFINE(struct xen_intr_pcpu_data, xen_intr_pcpu) = {
 
 DPCPU_DECLARE(struct vcpu_info *, vcpu_info);
 
-#define is_valid_evtchn(x)	((x) != 0)
-
 #define	EEXIST	17	/* Xen "already exists" error */
 #define	XEN_ALLOCATE_VECTOR	0 /* Allocate a vector for this event channel */
+#define XEN_INVALID_EVTCHN	0 /* Invalid event channel */
+
+#define is_valid_evtchn(x)	((x) != XEN_INVALID_EVTCHN)
 
 struct xenisrc {
 	struct intsrc	xi_intsrc;
@@ -117,6 +118,7 @@ struct xenisrc {
 	evtchn_port_t	xi_port;
 	int		xi_pirq;
 	int		xi_virq;
+	void		*xi_cookie;
 	u_int		xi_close:1;	/* close on unbind? */
 	u_int		xi_shared:1;	/* Shared with other domains. */
 	u_int 		xi_activehi:1;
@@ -361,6 +363,7 @@ xen_intr_release_isrc(struct xenisrc *isrc)
 	isrc->xi_cpu = 0;
 	isrc->xi_type = EVTCHN_TYPE_UNBOUND;
 	isrc->xi_port = 0;
+	isrc->xi_cookie = NULL;
 	mtx_unlock(&xen_intr_isrc_lock);
 	return (0);
 }
@@ -415,17 +418,26 @@ xen_intr_bind_isrc(struct xenisrc **isrcp, evtchn_port_t local_port,
 	xen_intr_port_to_isrc[local_port] = isrc;
 	mtx_unlock(&xen_intr_isrc_lock);
 
-	error = intr_add_handler(device_get_nameunit(intr_owner),
-				 isrc->xi_vector, filter, handler, arg,
-				 flags|INTR_EXCL, port_handlep);
+	/* Assign the opaque handler (the event channel port) */
+	*port_handlep = &isrc->xi_port;
+
+	if (filter == NULL && handler == NULL) {
+		/*
+		 * No filter/handler provided, leave the event channel
+		 * masked and without a valid handler, the caller is
+		 * in charge of setting that up.
+		 */
+		*isrcp = isrc;
+		return (0);
+	}
+
+	error = xen_intr_add_handler(intr_owner, filter, handler, arg, flags,
+	    *port_handlep);
 	if (error != 0) {
-		device_printf(intr_owner,
-			      "xen_intr_bind_irq: intr_add_handler failed\n");
 		xen_intr_release_isrc(isrc);
 		return (error);
 	}
 	*isrcp = isrc;
-	evtchn_unmask_port(local_port);
 	return (0);
 }
 
@@ -442,13 +454,16 @@ xen_intr_bind_isrc(struct xenisrc **isrcp, evtchn_port_t local_port,
 static struct xenisrc *
 xen_intr_isrc(xen_intr_handle_t handle)
 {
-	struct intr_handler *ih;
+	evtchn_port_t port;
 
-	ih = handle;
-	if (ih == NULL || ih->ih_event == NULL)
+	if (handle == NULL)
 		return (NULL);
 
-	return (ih->ih_event->ie_source);
+	port = *(evtchn_port_t *)handle;
+	if (!is_valid_evtchn(port) || port >= NR_EVENT_CHANNELS)
+		return (NULL);
+
+	return (xen_intr_port_to_isrc[port]);
 }
 
 /**
@@ -1442,22 +1457,24 @@ xen_intr_describe(xen_intr_handle_t port_handle, const char *fmt, ...)
 	va_start(ap, fmt);
 	vsnprintf(descr, sizeof(descr), fmt, ap);
 	va_end(ap);
-	return (intr_describe(isrc->xi_vector, port_handle, descr));
+	return (intr_describe(isrc->xi_vector, isrc->xi_cookie, descr));
 }
 
 void
 xen_intr_unbind(xen_intr_handle_t *port_handlep)
 {
-	struct intr_handler *handler;
 	struct xenisrc *isrc;
 
-	handler = *port_handlep;
+	KASSERT(port_handlep != NULL,
+	    ("NULL xen_intr_handle_t passed to xen_intr_unbind"));
+
+	isrc = xen_intr_isrc(*port_handlep);
 	*port_handlep = NULL;
-	isrc = xen_intr_isrc(handler);
 	if (isrc == NULL)
 		return;
 
-	intr_remove_handler(handler);
+	if (isrc->xi_cookie != NULL)
+		intr_remove_handler(isrc->xi_cookie);
 	xen_intr_release_isrc(isrc);
 }
 
@@ -1488,6 +1505,28 @@ xen_intr_port(xen_intr_handle_t handle)
 	return (isrc->xi_port);
 }
 
+int
+xen_intr_add_handler(device_t dev, driver_filter_t filter,
+    driver_intr_t handler, void *arg, enum intr_type flags,
+    xen_intr_handle_t handle)
+{
+	struct xenisrc *isrc;
+	int error;
+
+	isrc = xen_intr_isrc(handle);
+	if (isrc == NULL || isrc->xi_cookie != NULL)
+		return (EINVAL);
+
+	error = intr_add_handler(device_get_nameunit(dev),
+				 isrc->xi_vector, filter, handler, arg,
+				 flags|INTR_EXCL, &isrc->xi_cookie);
+	if (error != 0)
+		device_printf(dev,
+		    "xen_intr_add_handler: intr_add_handler failed\n");
+
+	return (error);
+}
+
 #include "opt_ddb.h"
 
 #ifdef DDB
diff --git a/sys/xen/xen_intr.h b/sys/xen/xen_intr.h
index a29414d9..d2d6470 100644
--- a/sys/xen/xen_intr.h
+++ b/sys/xen/xen_intr.h
@@ -246,4 +246,23 @@ int xen_register_msi(device_t dev, int vector, int count);
  */
 int xen_release_msi(int vector);
 
+/**
+ * Bind an event channel port with a handler
+ *
+ * \param dev       The device making this bind request.
+ * \param filter    An interrupt filter handler.  Specify NULL
+ *                  to always dispatch to the ithread handler.
+ * \param handler   An interrupt ithread handler.  Optional (can
+ *                  specify NULL) if all necessary event actions
+ *                  are performed by filter.
+ * \param arg       Argument to present to both filter and handler.
+ * \param irqflags  Interrupt handler flags.  See sys/bus.h.
+ * \param handle    Opaque handle used to manage this registration.
+ *
+ * \returns  0 on success, otherwise an errno.
+ */
+int xen_intr_add_handler(device_t dev, driver_filter_t filter,
+	driver_intr_t handler, void *arg, enum intr_type flags,
+	xen_intr_handle_t handle);
+
 #endif /* _XEN_INTR_H_ */
-- 
1.7.7.5 (Apple Git-26)

