aboutsummaryrefslogtreecommitdiff
path: root/sys/x86/xen/xen_intr.c
diff options
context:
space:
mode:
authorRoger Pau Monné <royger@FreeBSD.org>2016-10-31 13:00:53 +0000
committerRoger Pau Monné <royger@FreeBSD.org>2016-10-31 13:00:53 +0000
commit0f4d7d9fd78bd396c1c768bdcc2a298991115433 (patch)
tree86a828b16db62ab74d764c995d51a6eeb531b5e7 /sys/x86/xen/xen_intr.c
parentb2fd6999db269fd54228068dd0a719f86c4e002d (diff)
downloadsrc-0f4d7d9fd78bd396c1c768bdcc2a298991115433.tar.gz
src-0f4d7d9fd78bd396c1c768bdcc2a298991115433.zip
xen/intr: add reference counts to event channels
Add a reference count to xenisrc. This is required for implementation of unmap-notifications in the grant table userspace device (gntdev). We need to hold a reference to the event channel port, in case the user deallocates the port before we send the notification. Submitted by: jaggi Reviewed by: royger Differential review: https://reviews.freebsd.org/D7429
Notes
Notes: svn path=/head/; revision=308127
Diffstat (limited to 'sys/x86/xen/xen_intr.c')
-rw-r--r--sys/x86/xen/xen_intr.c41
1 files changed, 37 insertions, 4 deletions
diff --git a/sys/x86/xen/xen_intr.c b/sys/x86/xen/xen_intr.c
index 13ada84a2b34..66870b4c37f2 100644
--- a/sys/x86/xen/xen_intr.c
+++ b/sys/x86/xen/xen_intr.c
@@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
#include <sys/interrupt.h>
#include <sys/pcpu.h>
#include <sys/smp.h>
+#include <sys/refcount.h>
#include <vm/vm.h>
#include <vm/pmap.h>
@@ -128,6 +129,7 @@ struct xenisrc {
u_int xi_activehi:1;
u_int xi_edgetrigger:1;
u_int xi_masked:1;
+ volatile u_int xi_refcount;
};
static void xen_intr_suspend(struct pic *);
@@ -343,10 +345,8 @@ xen_intr_release_isrc(struct xenisrc *isrc)
{
mtx_lock(&xen_intr_isrc_lock);
- if (isrc->xi_intsrc.is_handlers != 0) {
- mtx_unlock(&xen_intr_isrc_lock);
- return (EBUSY);
- }
+ KASSERT(isrc->xi_intsrc.is_handlers == 0,
+ ("Release called, but xenisrc still in use"));
evtchn_mask_port(isrc->xi_port);
evtchn_clear_port(isrc->xi_port);
@@ -417,6 +417,7 @@ xen_intr_bind_isrc(struct xenisrc **isrcp, evtchn_port_t local_port,
}
isrc->xi_port = local_port;
xen_intr_port_to_isrc[local_port] = isrc;
+ refcount_init(&isrc->xi_refcount, 1);
mtx_unlock(&xen_intr_isrc_lock);
/* Assign the opaque handler (the event channel port) */
@@ -1508,6 +1509,13 @@ xen_intr_unbind(xen_intr_handle_t *port_handlep)
if (isrc == NULL)
return;
+ mtx_lock(&xen_intr_isrc_lock);
+ if (refcount_release(&isrc->xi_refcount) == 0) {
+ mtx_unlock(&xen_intr_isrc_lock);
+ return;
+ }
+ mtx_unlock(&xen_intr_isrc_lock);
+
if (isrc->xi_cookie != NULL)
intr_remove_handler(isrc->xi_cookie);
xen_intr_release_isrc(isrc);
@@ -1563,6 +1571,31 @@ xen_intr_add_handler(device_t dev, driver_filter_t filter,
return (error);
}
+int
+xen_intr_get_evtchn_from_port(evtchn_port_t port, xen_intr_handle_t *handlep)
+{
+
+ if (!is_valid_evtchn(port) || port >= NR_EVENT_CHANNELS)
+ return (EINVAL);
+
+ if (handlep == NULL) {
+ return (EINVAL);
+ }
+
+ mtx_lock(&xen_intr_isrc_lock);
+ if (xen_intr_port_to_isrc[port] == NULL) {
+ mtx_unlock(&xen_intr_isrc_lock);
+ return (EINVAL);
+ }
+ refcount_acquire(&xen_intr_port_to_isrc[port]->xi_refcount);
+ mtx_unlock(&xen_intr_isrc_lock);
+
+ /* Assign the opaque handler (the event channel port) */
+ *handlep = &xen_intr_port_to_isrc[port]->xi_vector;
+
+ return (0);
+}
+
#ifdef DDB
static const char *
xen_intr_print_type(enum evtchn_type type)