]> err.no Git - linux-2.6/commitdiff
MTRR: Fix race causing set_mtrr to go into infinite loop
authorLoic Prylli <loic@myri.com>
Fri, 6 Jul 2007 09:39:52 +0000 (02:39 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Fri, 6 Jul 2007 17:23:43 +0000 (10:23 -0700)
Processors synchronization in set_mtrr requires the .gate field to be set
after .count field is properly initialized.  Without an explicit barrier,
the compiler was reordering those memory stores.  That was sometimes
causing a processor (in ipi_handler) to see the .gate change and decrement
.count before the latter is set by set_mtrr() (which then hangs in a
infinite loop with irqs disabled).

Signed-off-by: Loic Prylli <loic@myri.com>
Cc: Andi Kleen <ak@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
arch/i386/kernel/cpu/mtrr/main.c

index 55b005152a11678e6644b5cbffdbbfcbbfac45dd..75dc6d5214bc5271b07b1d389d96670fed758d64 100644 (file)
@@ -229,6 +229,8 @@ static void set_mtrr(unsigned int reg, unsigned long base,
        data.smp_size = size;
        data.smp_type = type;
        atomic_set(&data.count, num_booting_cpus() - 1);
+       /* make sure data.count is visible before unleashing other CPUs */
+       smp_wmb();
        atomic_set(&data.gate,0);
 
        /*  Start the ball rolling on other CPUs  */
@@ -242,6 +244,7 @@ static void set_mtrr(unsigned int reg, unsigned long base,
 
        /* ok, reset count and toggle gate */
        atomic_set(&data.count, num_booting_cpus() - 1);
+       smp_wmb();
        atomic_set(&data.gate,1);
 
        /* do our MTRR business */
@@ -260,6 +263,7 @@ static void set_mtrr(unsigned int reg, unsigned long base,
                cpu_relax();
 
        atomic_set(&data.count, num_booting_cpus() - 1);
+       smp_wmb();
        atomic_set(&data.gate,0);
 
        /*