]> err.no Git - linux-2.6/commitdiff
[PATCH] fix race with preempt_enable()
authorNicolas Pitre <nico@cam.org>
Wed, 21 Dec 2005 17:26:25 +0000 (12:26 -0500)
committerLinus Torvalds <torvalds@g5.osdl.org>
Thu, 22 Dec 2005 17:17:39 +0000 (09:17 -0800)
Currently a simple

void foo(void) { preempt_enable(); }

produces the following code on ARM:

foo:
bic r3, sp, #8128
bic r3, r3, #63
ldr r2, [r3, #4]
ldr r1, [r3, #0]
sub r2, r2, #1
tst r1, #4
str r2, [r3, #4]
blne preempt_schedule
mov pc, lr

The problem is that the TIF_NEED_RESCHED flag is loaded _before_ the
preemption count is stored back, hence any interrupt coming within that
3 instruction window causing TIF_NEED_RESCHED to be set won't be
seen and scheduling won't happen as it should.

Nothing currently prevents gcc from performing that reordering.  There
is already a barrier() before the decrement of the preemption count, but
another one is needed between this and the TIF_NEED_RESCHED flag test
for proper code ordering.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Acked-by: Nick Piggin <nickpiggin@yahoo.com.au>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
include/linux/preempt.h

index d9a2f5254a51194858091bec44bf6bfb43cb9168..5769d14d1e6ab658b7ab8b2a2c9de117ec2b4838 100644 (file)
@@ -48,6 +48,7 @@ do { \
 #define preempt_enable() \
 do { \
        preempt_enable_no_resched(); \
+       barrier(); \
        preempt_check_resched(); \
 } while (0)