aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen
diff options
context:
space:
mode:
authorDavid Vrabel <david.vrabel@citrix.com>2013-07-19 10:51:58 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-04 04:51:15 -0400
commit3d63d1e0fe8aeed27f6ef38fa6eaf518dee1bbab (patch)
tree2280dd8f39bcc26cb50ff0dc72a09a8b8be1682a /drivers/xen
parentd0f1a6e5be8d8ada73891832af1bd3ecb3f52bb7 (diff)
xen/evtchn: avoid a deadlock when unbinding an event channel
commit 179fbd5a45f0d4034cc6fd37b8d367a3b79663c4 upstream. Unbinding an event channel (either with the ioctl or when the evtchn device is closed) may deadlock because disable_irq() is called with port_user_lock held which is also locked by the interrupt handler. Think of the IOCTL_EVTCHN_UNBIND is being serviced, the routine has just taken the lock, and an interrupt happens. The evtchn_interrupt is invoked, tries to take the lock and spins forever. A quick glance at the code shows that the spinlock is a local IRQ variant. Unfortunately that does not help as "disable_irq() waits for the interrupt handler on all CPUs to stop running. If the irq occurs on another VCPU, it tries to take port_user_lock and can't because the unbind ioctl is holding it." (from David). Hence we cannot depend on the said spinlock to protect us. We could make it a system wide IRQ disable spinlock but there is a better way. We can piggyback on the fact that the existence of the spinlock is to make get_port_user() checks be up-to-date. And we can alter those checks to not depend on the spin lock (as it's protected by u->bind_mutex in the ioctl) and can remove the unnecessary locking (this is IOCTL_EVTCHN_UNBIND) path. In the interrupt handler we cannot use the mutex, but we do not need it. "The unbind disables the irq before making the port user stale, so when you clear it you are guaranteed that the interrupt handler that might use that port cannot be running." (from David). Hence this patch removes the spinlock usage on the teardown path and piggybacks on disable_irq happening before we muck with the get_port_user() data. This ensures that the interrupt handler will never run on stale data. Signed-off-by: David Vrabel <david.vrabel@citrix.com> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> [v1: Expanded the commit description a bit] Signed-off-by: Jonghwan Choi <jhbird.choi@samsung.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/xen')
-rw-r--r--drivers/xen/evtchn.c21
1 files changed, 2 insertions, 19 deletions
diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c
index 45c8efaa6b3e..34924fb9d02a 100644
--- a/drivers/xen/evtchn.c
+++ b/drivers/xen/evtchn.c
@@ -377,18 +377,12 @@ static long evtchn_ioctl(struct file *file,
377 if (unbind.port >= NR_EVENT_CHANNELS) 377 if (unbind.port >= NR_EVENT_CHANNELS)
378 break; 378 break;
379 379
380 spin_lock_irq(&port_user_lock);
381
382 rc = -ENOTCONN; 380 rc = -ENOTCONN;
383 if (get_port_user(unbind.port) != u) { 381 if (get_port_user(unbind.port) != u)
384 spin_unlock_irq(&port_user_lock);
385 break; 382 break;
386 }
387 383
388 disable_irq(irq_from_evtchn(unbind.port)); 384 disable_irq(irq_from_evtchn(unbind.port));
389 385
390 spin_unlock_irq(&port_user_lock);
391
392 evtchn_unbind_from_user(u, unbind.port); 386 evtchn_unbind_from_user(u, unbind.port);
393 387
394 rc = 0; 388 rc = 0;
@@ -488,26 +482,15 @@ static int evtchn_release(struct inode *inode, struct file *filp)
488 int i; 482 int i;
489 struct per_user_data *u = filp->private_data; 483 struct per_user_data *u = filp->private_data;
490 484
491 spin_lock_irq(&port_user_lock);
492
493 free_page((unsigned long)u->ring);
494
495 for (i = 0; i < NR_EVENT_CHANNELS; i++) { 485 for (i = 0; i < NR_EVENT_CHANNELS; i++) {
496 if (get_port_user(i) != u) 486 if (get_port_user(i) != u)
497 continue; 487 continue;
498 488
499 disable_irq(irq_from_evtchn(i)); 489 disable_irq(irq_from_evtchn(i));
500 }
501
502 spin_unlock_irq(&port_user_lock);
503
504 for (i = 0; i < NR_EVENT_CHANNELS; i++) {
505 if (get_port_user(i) != u)
506 continue;
507
508 evtchn_unbind_from_user(get_port_user(i), i); 490 evtchn_unbind_from_user(get_port_user(i), i);
509 } 491 }
510 492
493 free_page((unsigned long)u->ring);
511 kfree(u->name); 494 kfree(u->name);
512 kfree(u); 495 kfree(u);
513 496