aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/evtchn.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/xen/evtchn.c')
-rw-r--r--drivers/xen/evtchn.c31
1 files changed, 29 insertions, 2 deletions
diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c
index ce3a0f5..c93d59e 100644
--- a/drivers/xen/evtchn.c
+++ b/drivers/xen/evtchn.c
@@ -269,6 +269,14 @@ static int evtchn_bind_to_user(struct per_user_data *u, int port)
u->name, (void *)(unsigned long)port);
if (rc >= 0)
rc = 0;
+ else {
+ /* bind failed, should close the port now */
+ struct evtchn_close close;
+ close.port = port;
+ if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0)
+ BUG();
+ set_port_user(port, NULL);
+ }
return rc;
}
@@ -277,6 +285,8 @@ static void evtchn_unbind_from_user(struct per_user_data *u, int port)
{
int irq = irq_from_evtchn(port);
+ BUG_ON(irq < 0);
+
unbind_from_irqhandler(irq, (void *)(unsigned long)port);
set_port_user(port, NULL);
@@ -367,12 +377,18 @@ static long evtchn_ioctl(struct file *file,
if (unbind.port >= NR_EVENT_CHANNELS)
break;
+ spin_lock_irq(&port_user_lock);
+
rc = -ENOTCONN;
- if (get_port_user(unbind.port) != u)
+ if (get_port_user(unbind.port) != u) {
+ spin_unlock_irq(&port_user_lock);
break;
+ }
disable_irq(irq_from_evtchn(unbind.port));
+ spin_unlock_irq(&port_user_lock);
+
evtchn_unbind_from_user(u, unbind.port);
rc = 0;
@@ -472,15 +488,26 @@ static int evtchn_release(struct inode *inode, struct file *filp)
int i;
struct per_user_data *u = filp->private_data;
+ spin_lock_irq(&port_user_lock);
+
+ free_page((unsigned long)u->ring);
+
for (i = 0; i < NR_EVENT_CHANNELS; i++) {
if (get_port_user(i) != u)
continue;
disable_irq(irq_from_evtchn(i));
+ }
+
+ spin_unlock_irq(&port_user_lock);
+
+ for (i = 0; i < NR_EVENT_CHANNELS; i++) {
+ if (get_port_user(i) != u)
+ continue;
+
evtchn_unbind_from_user(get_port_user(i), i);
}
- free_page((unsigned long)u->ring);
kfree(u->name);
kfree(u);