diff options
author | Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com> | 2009-02-12 16:03:24 -0500 |
---|---|---|
committer | Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com> | 2009-03-30 12:26:49 -0400 |
commit | 0a4666b539a0e896ec4e8396a034a479e3573125 (patch) | |
tree | bfe63e7b6d3da3d8415c53919059bda72d4cf86a | |
parent | c5cfef0f79cacc3aa438fc28f4747f0d10c54d0d (diff) |
xen/dev-evtchn: clean up locking in evtchn
Define a new per_user_data mutex to serialize bind/unbind operations
to prevent them from racing with each other. Fix error returns
and don't do a bind while holding a spinlock.
Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
-rw-r--r-- | drivers/xen/evtchn.c | 37 |
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 | ||
56 | struct per_user_data { | 56 | struct 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? */ |
71 | static struct per_user_data *port_user[NR_EVENT_CHANNELS]; | 73 | static struct per_user_data *port_user[NR_EVENT_CHANNELS]; |
72 | static DEFINE_SPINLOCK(port_user_lock); | 74 | static DEFINE_SPINLOCK(port_user_lock); /* protects port_user[] and ring_prod */ |
73 | 75 | ||
74 | irqreturn_t evtchn_interrupt(int irq, void *data) | 76 | irqreturn_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 | ||
211 | static int evtchn_bind_to_user(struct per_user_data *u, int port) | 213 | static 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 | ||
227 | fail: | 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; |