aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/xen/evtchn.c37
1 files changed, 25 insertions, 12 deletions
diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c
index 517b9ee63e18..af031950f9b1 100644
--- a/drivers/xen/evtchn.c
+++ b/drivers/xen/evtchn.c
@@ -54,6 +54,8 @@
54#include <asm/xen/hypervisor.h> 54#include <asm/xen/hypervisor.h>
55 55
56struct per_user_data { 56struct per_user_data {
57 struct mutex bind_mutex; /* serialize bind/unbind operations */
58
57 /* Notification ring, accessed via /dev/xen/evtchn. */ 59 /* Notification ring, accessed via /dev/xen/evtchn. */
58#define EVTCHN_RING_SIZE (PAGE_SIZE / sizeof(evtchn_port_t)) 60#define EVTCHN_RING_SIZE (PAGE_SIZE / sizeof(evtchn_port_t))
59#define EVTCHN_RING_MASK(_i) ((_i)&(EVTCHN_RING_SIZE-1)) 61#define EVTCHN_RING_MASK(_i) ((_i)&(EVTCHN_RING_SIZE-1))
@@ -69,7 +71,7 @@ struct per_user_data {
69 71
70/* Who's bound to each port? */ 72/* Who's bound to each port? */
71static struct per_user_data *port_user[NR_EVENT_CHANNELS]; 73static struct per_user_data *port_user[NR_EVENT_CHANNELS];
72static DEFINE_SPINLOCK(port_user_lock); 74static DEFINE_SPINLOCK(port_user_lock); /* protects port_user[] and ring_prod */
73 75
74irqreturn_t evtchn_interrupt(int irq, void *data) 76irqreturn_t evtchn_interrupt(int irq, void *data)
75{ 77{
@@ -210,22 +212,24 @@ static ssize_t evtchn_write(struct file *file, const char __user *buf,
210 212
211static int evtchn_bind_to_user(struct per_user_data *u, int port) 213static int evtchn_bind_to_user(struct per_user_data *u, int port)
212{ 214{
213 int irq;
214 int rc = 0; 215 int rc = 0;
215 216
216 spin_lock_irq(&port_user_lock); 217 /*
217 218 * Ports are never reused, so every caller should pass in a
219 * unique port.
220 *
221 * (Locking not necessary because we haven't registered the
222 * interrupt handler yet, and our caller has already
223 * serialized bind operations.)
224 */
218 BUG_ON(port_user[port] != NULL); 225 BUG_ON(port_user[port] != NULL);
219
220 irq = bind_evtchn_to_irqhandler(port, evtchn_interrupt, IRQF_DISABLED,
221 u->name, (void *)(unsigned long)port);
222 if (rc < 0)
223 goto fail;
224
225 port_user[port] = u; 226 port_user[port] = u;
226 227
227fail: 228 rc = bind_evtchn_to_irqhandler(port, evtchn_interrupt, IRQF_DISABLED,
228 spin_unlock_irq(&port_user_lock); 229 u->name, (void *)(unsigned long)port);
230 if (rc >= 0)
231 rc = 0;
232
229 return rc; 233 return rc;
230} 234}
231 235
@@ -234,6 +238,10 @@ static void evtchn_unbind_from_user(struct per_user_data *u, int port)
234 int irq = irq_from_evtchn(port); 238 int irq = irq_from_evtchn(port);
235 239
236 unbind_from_irqhandler(irq, (void *)(unsigned long)port); 240 unbind_from_irqhandler(irq, (void *)(unsigned long)port);
241
242 /* make sure we unbind the irq handler before clearing the port */
243 barrier();
244
237 port_user[port] = NULL; 245 port_user[port] = NULL;
238} 246}
239 247
@@ -244,6 +252,9 @@ static long evtchn_ioctl(struct file *file,
244 struct per_user_data *u = file->private_data; 252 struct per_user_data *u = file->private_data;
245 void __user *uarg = (void __user *) arg; 253 void __user *uarg = (void __user *) arg;
246 254
255 /* Prevent bind from racing with unbind */
256 mutex_lock(&u->bind_mutex);
257
247 switch (cmd) { 258 switch (cmd) {
248 case IOCTL_EVTCHN_BIND_VIRQ: { 259 case IOCTL_EVTCHN_BIND_VIRQ: {
249 struct ioctl_evtchn_bind_virq bind; 260 struct ioctl_evtchn_bind_virq bind;
@@ -368,6 +379,7 @@ static long evtchn_ioctl(struct file *file,
368 rc = -ENOSYS; 379 rc = -ENOSYS;
369 break; 380 break;
370 } 381 }
382 mutex_unlock(&u->bind_mutex);
371 383
372 return rc; 384 return rc;
373} 385}
@@ -414,6 +426,7 @@ static int evtchn_open(struct inode *inode, struct file *filp)
414 return -ENOMEM; 426 return -ENOMEM;
415 } 427 }
416 428
429 mutex_init(&u->bind_mutex);
417 mutex_init(&u->ring_cons_mutex); 430 mutex_init(&u->ring_cons_mutex);
418 431
419 filp->private_data = u; 432 filp->private_data = u;