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 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.
This commit is contained in:
parent
040f3ac494
commit
bca2091349
@ -110,10 +110,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 XEN_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;
|
||||
@ -123,6 +124,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;
|
||||
@ -365,6 +367,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);
|
||||
}
|
||||
@ -419,17 +422,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);
|
||||
}
|
||||
|
||||
@ -446,13 +458,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]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1446,22 +1461,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);
|
||||
}
|
||||
|
||||
@ -1492,6 +1509,29 @@ 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: %d\n",
|
||||
error);
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
#ifdef DDB
|
||||
static const char *
|
||||
xen_intr_print_type(enum evtchn_type type)
|
||||
|
@ -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_ */
|
||||
|
Loading…
Reference in New Issue
Block a user