diff options
Diffstat (limited to 'drivers/xen/evtchn.c')
-rw-r--r-- | drivers/xen/evtchn.c | 31 |
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); |