xen/evtchn: don't do unbind_from_irqhandler under spinlock
authorJeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Fri, 28 May 2010 22:28:27 +0000 (15:28 -0700)
committerJeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Fri, 28 May 2010 22:28:27 +0000 (15:28 -0700)
unbind_from_irqhandler can end up doing /proc operations, which can't
happen under a spinlock.  So before removing the IRQ handler,
disable the irq under the port_user lock (masking the underlying event
channel and making sure the irq handler isn't running concurrently and
won't start running), then remove the handler without the lock.

Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
drivers/xen/evtchn.c

index f79ac5ca3793f12a513cac0357fb4a417971e2cb..6a3a12945d0216f1191f1ac1730b09c3ebfc8dec 100644 (file)
@@ -375,10 +375,12 @@ static long evtchn_ioctl(struct file *file,
                        break;
                }
 
-               evtchn_unbind_from_user(u, unbind.port);
+               disable_irq(irq_from_evtchn(unbind.port));
 
                spin_unlock_irq(&port_user_lock);
 
+               evtchn_unbind_from_user(u, unbind.port);
+
                rc = 0;
                break;
        }
@@ -484,11 +486,18 @@ static int evtchn_release(struct inode *inode, struct file *filp)
                if (get_port_user(i) != u)
                        continue;
 
-               evtchn_unbind_from_user(get_port_user(i), i);
+               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);
+       }
+
        kfree(u->name);
        kfree(u);