mutex subsystem, add include/asm-arm/mutex.h
PATCH 5/11 mutex subsystem, add include/asm-arm/mutex.h
add the ARM version of mutex.h, which is optimized in assembly for ARMv6, and uses the xchg implementation on pre-ARMv6.
From: Nicolas Pitre <[email protected]>
Signed-off-by Ingo Molnar <[email protected]>
----
include/asm-arm/mutex.h | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 102 insertions(+)
include/asm-arm/mutex.h new file +102 -0
@@ -0,0 +1,102 @@ +/* +* include/asm-arm/mutex.h +* +* ARM optimized mutex locking primitives +* +* Please look into asm-generic/mutex-xchg.h for a formal definition. +*/ +#ifndef _ASM_MUTEX_H +#define _ASM_MUTEX_H + +#if __LINUX_ARM_ARCH__ <6 +/* On pre-ARMv6 hardware the swp based implementation is the most efficient. */ +# include <asm-generic/mutex-xchg.h> +#else + +/* +* Attempting to lock a mutex on ARMv6+ can be done with a bastardized +* atomic decrement (it is not a reliable atomic decrement but it satisfies +* the defined semantics for our purpose, while being smaller and faster +* than a real atomic decrement or atomic swap. The idea is to attempt +* decrementing the lock value only once. If once decremented it isn't zero, +* or if its store-back fails due to a dispute on the exclusive store, we +* simply bail out immediately through the slow path where the lock will be +* reattempted until it succeeds. +*/ +#define __mutex_fastpath_lock(count, fail_fn) \ +do { \ + /* type-check the function too: */ \ + void fastcall (*__tmp)(atomic_t *) = fail_fn; \ + int __ex_flag, __res; \ + \ + (void)__tmp; \ + typecheck(atomic_t *, count); \ + \ + __asm__ ( \ + "ldrex %0, [%2]\n\t" \ + "sub %0, %0, #1\n\t" \ + "strex %1, %0, [%2]" \ + : "=&r" (__res), "=&r" (__ex_flag) \ + : "r" (&(count)->counter) \ + : "cc","memory" ); \ + __res |= __ex_flag; \ + if (unlikely(__res != 0)) \ + fail_fn(count); \ +} while (0) + +#define __mutex_fastpath_lock_retval(count, fail_fn) \ +({ \ + /* type-check the function too: */ \ + int fastcall (*__tmp)(atomic_t *) = fail_fn; \ + int __ex_flag, __res; \ + \ + (void)__tmp; \ + typecheck(atomic_t *, count); \ + \ + __asm__ ( \ + "ldrex %0, [%2]\n\t" \ + "sub %0, %0, #1\n\t" \ + "strex %1, %0, [%2]" \ + : "=&r" (__res), "=&r" (__ex_flag) \ + : "r" (&(count)->counter) \ + : "cc","memory" ); \ + __res |= __ex_flag; \ + if (unlikely(__res != 0)) \ + __res = fail_fn(count); \ + __res; \ +}) + +/* +* Same trick is used for the unlock fast path. However the original value, +* rather than the result, is used to test for success in order to have +* better generated assembly. +*/ +#define __mutex_fastpath_unlock(count, fail_fn) \ +do { \ + /* type-check the function too: */ \ + void fastcall (*__tmp)(atomic_t *) = fail_fn; \ + int __ex_flag, __res, __orig; \ + \ + (void)__tmp; \ + typecheck(atomic_t *, count); \ + \ + __asm__ ( \ + "ldrex %0, [%3]\n\t" \ + "add %1, %0, #1\n\t" \ + "strex %2, %1, [%3]" \ + : "=&r" (__orig), "=&r" (__res), "=&r" (__ex_flag) \ + : "r" (&(count)->counter) \ + : "cc","memory" ); \ + __orig |= __ex_flag; \ + if (unlikely(__orig != 0)) \ + fail_fn(count); \ +} while (0) + +/* +* If the unlock was done on a contended lock, or if the unlock simply fails +* then the mutex remains locked. +*/ +#define __mutex_slowpath_needs_to_unlock () 1 + +#endif +#endif