Possible race condition in i386 global_irq_lock handling.
Quoted 11 lines · TeJun
static inline void
irq_enter(int cpu, int irq){ ++local_irq_count(cpu); while (test_bit(0,&global_irq_lock)) { cpu_relax(); } }Is it a race condition or am I getting it horribly wrong? Thx in
advance.
Yes, it's a race. Actually a variant of the race that lead to the introduction of set_current_state():
test_bit is a simple read instruction. i386 cpus are free to execute it early, i.e. they can execute it before the write part of "++local_irq_count(cpu)".
I think smp_rmb() is the right barrier - could you write a patch and send it to Marcelo?
--
Manfred
Quoted 16 lines · Manfred Spraul
Quoted 11 lines · TeJun
static inline void
irq_enter(int cpu, int irq){ ++local_irq_count(cpu); while (test_bit(0,&global_irq_lock)) { cpu_relax(); } }Is it a race condition or am I getting it horribly wrong? Thx in advance.
Yes, it's a race. Actually a variant of the race that lead to the introduction of
set_current_state():
Quoted 6 lines
test_bitis a simple read instruction. i386 cpus are free to execute it early, i.e. they can execute it before the write part of "++local_irq_count(cpu)".I think
smp_rmb()is the right barrier - could you write a patch and send it to Marcelo?
smb_rmb is enough in practice for x86 (in asm-i386), but not the right barrier in general because rmb only serializes reads against reads, so it would also make little sense while reading the i386 code. here you've to serialize a write against a read so it would be misleading unless you know exactly the lowlevel implementations of those barriers.
smp_mb() before the while loop should be the correct barrier for all archs and the asm generated on x86 will be the same.
alpha, ia64 and x86-64 (and probably others) needs it too.
Andrea
Quoted 11 lines
smb_rmbis enough in practice for x86 (in asm-i386), but not the right barrier in general because rmb only serializes reads against reads, so it would also make little sense while reading the i386 code. here you've to serialize a write against a read so it would be misleading unless you know exactly the lowlevel implementations of those barriers.
smp_mb()before the while loop should be the correct barrier for all archs and the asm generated on x86 will be the same.alpha, ia64 and x86-64 (and probably others) needs it too.
Can some kind soul please provide me with the needed mini-patch. I would like to try that on my constantly crashing SMP test box...
Regards,
Stephan
Quoted 7 lines · Stephan von Krawczynski
Quoted 7 lines
smb_rmbis enough in practice for x86 (in asm-i386), but not the right barrier in general because rmb only serializes reads against reads, so it would also make little sense while reading the i386 code. here you've to serialize a write against a read so it would be misleading unless you know exactly the lowlevel implementations of those barriers.
Quoted 7 lines
Quoted 4 lines
smp_mb()before the while loop should be the correct barrier for all archs and the asm generated on x86 will be the same.alpha, ia64 and x86-64 (and probably others) needs it too.
Can some kind soul please provide me with the needed mini-patch. I would like to try that on my constantly crashing SMP test box...
include/asm-i386/hardirq.h renamed from include/asm-i386/hardirq.h.~1~ +2 -0
@@ -71,6 +71,8 @@ static inline void irq_enter(int cpu, in { ++local_irq_count (cpu); +smp_mb (); +while (test_bit (0 ,&global_irq_lock)) {cpu_relax (); }
Andrea
I'm attaching patch for i386. It makes three changes.
1. add smp_mb() between local_irq_count++ and global_irq_lock test
in irq_enter().
2. add smp_mb__after_clear_bit() before irqs_running() test in
wait_on_irq(). 3. remove irqs_running() test from synchronize_irq()
Removing irqs_running() test from synchronize_irq() is needed for the
same reason. Other interrupts might be running on successful return from synchronize_irq().
smp_mb__after_clear_bit() should be smp_mb__after_test_and_set_bit()
which doesn't exist. Should I add this?
After determining smp_mb__after_clear_bit(), I'll make a patch for
every affected architecture. Please comment.
# ------------ patch follows --------------
arch/i386/kernel/irq.c +5 -5
@@ -271,6 +271,8 @@* for bottom half handlers unless we're * already executing in one.. */ +smp_mb__after_clear_bit ();/* Synchronize with irq_enter() */ +if (!irqs_running ())if (local_bh_count (cpu) || !spin_is_locked (&global_bh_lock))break ;
@@ -307,11 +309,9 @@*/ void synchronize_irq (void ) { -if (irqs_running ()) { -/* Stupid approach */ -cli (); -sti (); - } +/* Stupid approach */ +cli (); +sti (); }static inline void get_irqlock (int cpu)
include/asm-i386/hardirq.h +2 -0
@@ -67,6 +67,8 @@ { ++local_irq_count (cpu); +smp_mb ();/* Synchronize with wait_on_irq() */ +while (test_bit (0 ,&global_irq_lock)) {cpu_relax (); }
Quoted 7 lines · On Fri, 22 Aug 2003 10:18:40 +0900 TeJun Huh <[email protected]> wrote:
I'm attaching patch for i386. It makes three changes.
1. add
smp_mb()between local_irq_count++ andglobal_irq_locktestin irq_enter(). 2. add smp_mb__after_clear_bit() before irqs_running() test in wait_on_irq().3. remove
irqs_running()test fromsynchronize_irq()
Thank you TeJun,
I have started tests and will provide feedback if your patch has any influence on my problem. This may take some days.
Regards,
Stephan
thanks TeJun,
just one comment
Quoted 1 line · TeJun Huh
3. remove
irqs_running()test fromsynchronize_irq()
I'm not convinced this one is needed. An irq can still run on another cpu but the cli();sti() may execute while it's here:
irq running synchronize_irq() -------------- ----------------- do_IRQ handle_IRQ_event cli() sti() irq_enter -> way too late
in short, doing irqs_running() doesn't seem to weaken the semantics of synchronize_irq() to me.
I think it should be changed this way instead:
void synchronize_irq(void)
{
smp_mb();
if (irqs_running()) {
/* Stupid approach */
cli();
sti();
}
}to be sure to read the local irq area after the previous code (the test_and_set_bit of the global_irq_lock of a cli() in your version would achieve the same implicit smp_mb too, so maybe your only point for doing cli()/sti() was to execute the smp_mb before the irqs_running?). the above version is more finegrined and it looks equivalent to yours.
Andrea
Hello Andrea,
Quoted 34 lines · Andrea Arcangeli
thanks TeJun,
just one comment
Quoted 1 line · TeJun Huh
3. remove
irqs_running()test fromsynchronize_irq()I'm not convinced this one is needed. An irq can still run on another cpu but the cli();sti() may execute while it's here:
irq running synchronize_irq() -------------- ----------------- do_IRQ handle_IRQ_event cli() sti() irq_enter -> way too latein short, doing
irqs_running()doesn't seem to weaken the semantics ofsynchronize_irq()to me.I think it should be changed this way instead:
void
synchronize_irq(void){ smp_mb(); if (irqs_running()) { /* Stupid approach */ cli(); sti(); } }
Quoted 7 lines
to be sure to read the local irq area after the previous code (the
test_and_set_bitof theglobal_irq_lockof acli()in your version would achieve the same implicitsmp_mbtoo, so maybe your only point for doing cli()/sti() was to execute thesmp_mbbefore theirqs_running?). the above version is more finegrined and it looks equivalent to yours.Andrea
Yes, you're right. Adding just smp_mb() should guarantee that no cpu
is executing interrupt handler which may not see memory contents modified before synchronize_irq() after synchronize_irq() returns. I think we need some decent comments there. :-)
As now I know that test_and_set_bit() implies memory barrier,smb_mb__after_clear_bit() can be removed. I'll make and post a patch which fixes this race and the bh race of the other thread.
Thanks.
Signature · 1 line
tejun
Quoted 2 lines · TeJun Huh
As now I know that
test_and_set_bit()implies memory barrier,smb_mb__after_clear_bit()can be removed. I'll make and post a patch
;) right
Quoted 1 line
which fixes this race and the bh race of the other thread.
thanks,
Andrea