]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
xen: only enable interrupts while actually blocking for spinlock
authorJeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Wed, 9 Sep 2009 19:33:51 +0000 (12:33 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 5 Oct 2009 16:31:44 +0000 (09:31 -0700)
commit 4d576b57b50a92801e6493e76e5243d6cff193d2 upstream.

Where possible we enable interrupts while waiting for a spinlock to
become free, in order to reduce big latency spikes in interrupt handling.

However, at present if we manage to pick up the spinlock just before
blocking, we'll end up holding the lock with interrupts enabled for a
while.  This will cause a deadlock if we recieve an interrupt in that
window, and the interrupt handler tries to take the lock too.

Solve this by shrinking the interrupt-enabled region to just around the
blocking call.

[ Impact: avoid race/deadlock when using Xen PV spinlocks ]

Reported-by: "Yang, Xiaowei" <xiaowei.yang@intel.com>
Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
arch/x86/xen/spinlock.c

index 5601506f2dd96d49031873216d7e8aa12c689a91..2f91e5651926cf2336fa5b4518e74bf7834202b9 100644 (file)
@@ -187,7 +187,6 @@ static noinline int xen_spin_lock_slow(struct raw_spinlock *lock, bool irq_enabl
        struct xen_spinlock *prev;
        int irq = __get_cpu_var(lock_kicker_irq);
        int ret;
-       unsigned long flags;
        u64 start;
 
        /* If kicker interrupts not initialized yet, just spin */
@@ -199,16 +198,12 @@ static noinline int xen_spin_lock_slow(struct raw_spinlock *lock, bool irq_enabl
        /* announce we're spinning */
        prev = spinning_lock(xl);
 
-       flags = __raw_local_save_flags();
-       if (irq_enable) {
-               ADD_STATS(taken_slow_irqenable, 1);
-               raw_local_irq_enable();
-       }
-
        ADD_STATS(taken_slow, 1);
        ADD_STATS(taken_slow_nested, prev != NULL);
 
        do {
+               unsigned long flags;
+
                /* clear pending */
                xen_clear_irq_pending(irq);
 
@@ -228,6 +223,12 @@ static noinline int xen_spin_lock_slow(struct raw_spinlock *lock, bool irq_enabl
                        goto out;
                }
 
+               flags = __raw_local_save_flags();
+               if (irq_enable) {
+                       ADD_STATS(taken_slow_irqenable, 1);
+                       raw_local_irq_enable();
+               }
+
                /*
                 * Block until irq becomes pending.  If we're
                 * interrupted at this point (after the trylock but
@@ -238,13 +239,15 @@ static noinline int xen_spin_lock_slow(struct raw_spinlock *lock, bool irq_enabl
                 * pending.
                 */
                xen_poll_irq(irq);
+
+               raw_local_irq_restore(flags);
+
                ADD_STATS(taken_slow_spurious, !xen_test_irq_pending(irq));
        } while (!xen_test_irq_pending(irq)); /* check for spurious wakeups */
 
        kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq));
 
 out:
-       raw_local_irq_restore(flags);
        unspinning_lock(xl, prev);
        spin_time_accum_blocked(start);