Revision e91467ecd1ef381377fd327c0ded922835ec52ab authored by Christian Borntraeger on 05 August 2006, 19:13:52 UTC, committed by Linus Torvalds on 06 August 2006, 15:57:46 UTC
This patch adds a barrier() in futex unqueue_me to avoid aliasing of two pointers. On my s390x system I saw the following oops: Unable to handle kernel pointer dereference at virtual kernel address 0000000000000000 Oops: 0004 [#1] CPU: 0 Not tainted Process mytool (pid: 13613, task: 000000003ecb6ac0, ksp: 00000000366bdbd8) Krnl PSW : 0704d00180000000 00000000003c9ac2 (_spin_lock+0xe/0x30) Krnl GPRS: 00000000ffffffff 000000003ecb6ac0 0000000000000000 0700000000000000 0000000000000000 0000000000000000 000001fe00002028 00000000000c091f 000001fe00002054 000001fe00002054 0000000000000000 00000000366bddc0 00000000005ef8c0 00000000003d00e8 0000000000144f91 00000000366bdcb8 Krnl Code: ba 4e 20 00 12 44 b9 16 00 3e a7 84 00 08 e3 e0 f0 88 00 04 Call Trace: ([<0000000000144f90>] unqueue_me+0x40/0xe4) [<0000000000145a0c>] do_futex+0x33c/0xc40 [<000000000014643e>] sys_futex+0x12e/0x144 [<000000000010bb00>] sysc_noemu+0x10/0x16 [<000002000003741c>] 0x2000003741c The code in question is: static int unqueue_me(struct futex_q *q) { int ret = 0; spinlock_t *lock_ptr; /* In the common case we don't take the spinlock, which is nice. */ retry: lock_ptr = q->lock_ptr; if (lock_ptr != 0) { spin_lock(lock_ptr); /* * q->lock_ptr can change between reading it and * spin_lock(), causing us to take the wrong lock. This * corrects the race condition. [...] and my compiler (gcc 4.1.0) makes the following out of it: 00000000000003c8 <unqueue_me>: 3c8: eb bf f0 70 00 24 stmg %r11,%r15,112(%r15) 3ce: c0 d0 00 00 00 00 larl %r13,3ce <unqueue_me+0x6> 3d0: R_390_PC32DBL .rodata+0x2a 3d4: a7 f1 1e 00 tml %r15,7680 3d8: a7 84 00 01 je 3da <unqueue_me+0x12> 3dc: b9 04 00 ef lgr %r14,%r15 3e0: a7 fb ff d0 aghi %r15,-48 3e4: b9 04 00 b2 lgr %r11,%r2 3e8: e3 e0 f0 98 00 24 stg %r14,152(%r15) 3ee: e3 c0 b0 28 00 04 lg %r12,40(%r11) /* write q->lock_ptr in r12 */ 3f4: b9 02 00 cc ltgr %r12,%r12 3f8: a7 84 00 4b je 48e <unqueue_me+0xc6> /* if r12 is zero then jump over the code.... */ 3fc: e3 20 b0 28 00 04 lg %r2,40(%r11) /* write q->lock_ptr in r2 */ 402: c0 e5 00 00 00 00 brasl %r14,402 <unqueue_me+0x3a> 404: R_390_PC32DBL _spin_lock+0x2 /* use r2 as parameter for spin_lock */ So the code becomes more or less: if (q->lock_ptr != 0) spin_lock(q->lock_ptr) instead of if (lock_ptr != 0) spin_lock(lock_ptr) Which caused the oops from above. After adding a barrier gcc creates code without this problem: [...] (the same) 3ee: e3 c0 b0 28 00 04 lg %r12,40(%r11) 3f4: b9 02 00 cc ltgr %r12,%r12 3f8: b9 04 00 2c lgr %r2,%r12 3fc: a7 84 00 48 je 48c <unqueue_me+0xc4> 400: c0 e5 00 00 00 00 brasl %r14,400 <unqueue_me+0x38> 402: R_390_PC32DBL _spin_lock+0x2 As a general note, this code of unqueue_me seems a bit fishy. The retry logic of unqueue_me only works if we can guarantee, that the original value of q->lock_ptr is always a spinlock (Otherwise we overwrite kernel memory). We know that q->lock_ptr can change. I dont know what happens with the original spinlock, as I am not an expert with the futex code. Cc: Martin Schwidefsky <schwidefsky@de.ibm.com> Cc: Rusty Russell <rusty@rustcorp.com.au> Acked-by: Ingo Molnar <mingo@redhat.com> Cc: Thomas Gleixner <tglx@timesys.com> Signed-off-by: Christian Borntraeger <borntrae@de.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
1 parent 72f0b4e
compiler.h
#ifndef __ALPHA_COMPILER_H
#define __ALPHA_COMPILER_H
/*
* Herein are macros we use when describing various patterns we want to GCC.
* In all cases we can get better schedules out of the compiler if we hide
* as little as possible inside inline assembly. However, we want to be
* able to know what we'll get out before giving up inline assembly. Thus
* these tests and macros.
*/
#if __GNUC__ == 3 && __GNUC_MINOR__ >= 4 || __GNUC__ > 3
# define __kernel_insbl(val, shift) __builtin_alpha_insbl(val, shift)
# define __kernel_inswl(val, shift) __builtin_alpha_inswl(val, shift)
# define __kernel_insql(val, shift) __builtin_alpha_insql(val, shift)
# define __kernel_inslh(val, shift) __builtin_alpha_inslh(val, shift)
# define __kernel_extbl(val, shift) __builtin_alpha_extbl(val, shift)
# define __kernel_extwl(val, shift) __builtin_alpha_extwl(val, shift)
# define __kernel_cmpbge(a, b) __builtin_alpha_cmpbge(a, b)
# define __kernel_cttz(x) __builtin_ctzl(x)
# define __kernel_ctlz(x) __builtin_clzl(x)
# define __kernel_ctpop(x) __builtin_popcountl(x)
#else
# define __kernel_insbl(val, shift) \
({ unsigned long __kir; \
__asm__("insbl %2,%1,%0" : "=r"(__kir) : "rI"(shift), "r"(val)); \
__kir; })
# define __kernel_inswl(val, shift) \
({ unsigned long __kir; \
__asm__("inswl %2,%1,%0" : "=r"(__kir) : "rI"(shift), "r"(val)); \
__kir; })
# define __kernel_insql(val, shift) \
({ unsigned long __kir; \
__asm__("insql %2,%1,%0" : "=r"(__kir) : "rI"(shift), "r"(val)); \
__kir; })
# define __kernel_inslh(val, shift) \
({ unsigned long __kir; \
__asm__("inslh %2,%1,%0" : "=r"(__kir) : "rI"(shift), "r"(val)); \
__kir; })
# define __kernel_extbl(val, shift) \
({ unsigned long __kir; \
__asm__("extbl %2,%1,%0" : "=r"(__kir) : "rI"(shift), "r"(val)); \
__kir; })
# define __kernel_extwl(val, shift) \
({ unsigned long __kir; \
__asm__("extwl %2,%1,%0" : "=r"(__kir) : "rI"(shift), "r"(val)); \
__kir; })
# define __kernel_cmpbge(a, b) \
({ unsigned long __kir; \
__asm__("cmpbge %r2,%1,%0" : "=r"(__kir) : "rI"(b), "rJ"(a)); \
__kir; })
# define __kernel_cttz(x) \
({ unsigned long __kir; \
__asm__("cttz %1,%0" : "=r"(__kir) : "r"(x)); \
__kir; })
# define __kernel_ctlz(x) \
({ unsigned long __kir; \
__asm__("ctlz %1,%0" : "=r"(__kir) : "r"(x)); \
__kir; })
# define __kernel_ctpop(x) \
({ unsigned long __kir; \
__asm__("ctpop %1,%0" : "=r"(__kir) : "r"(x)); \
__kir; })
#endif
/*
* Beginning with EGCS 1.1, GCC defines __alpha_bwx__ when the BWX
* extension is enabled. Previous versions did not define anything
* we could test during compilation -- too bad, so sad.
*/
#if defined(__alpha_bwx__)
#define __kernel_ldbu(mem) (mem)
#define __kernel_ldwu(mem) (mem)
#define __kernel_stb(val,mem) ((mem) = (val))
#define __kernel_stw(val,mem) ((mem) = (val))
#else
#define __kernel_ldbu(mem) \
({ unsigned char __kir; \
__asm__("ldbu %0,%1" : "=r"(__kir) : "m"(mem)); \
__kir; })
#define __kernel_ldwu(mem) \
({ unsigned short __kir; \
__asm__("ldwu %0,%1" : "=r"(__kir) : "m"(mem)); \
__kir; })
#define __kernel_stb(val,mem) \
__asm__("stb %1,%0" : "=m"(mem) : "r"(val))
#define __kernel_stw(val,mem) \
__asm__("stw %1,%0" : "=m"(mem) : "r"(val))
#endif
/* Some idiots over in <linux/compiler.h> thought inline should imply
always_inline. This breaks stuff. We'll include this file whenever
we run into such problems. */
#include <linux/compiler.h>
#undef inline
#undef __inline__
#undef __inline
#undef __always_inline
#define __always_inline inline __attribute__((always_inline))
#endif /* __ALPHA_COMPILER_H */
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...