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 | 144 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 144 insertions(+)
include/asm-arm/mutex.h new file +144 -0
@@ -0,0 +1,144 @@ +/* +* 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" \ + "sub %0, %0, #1 \n" \ + "strex %1, %0, [%2] \n" \ + \ + : "=&r" (__res), "=&r" (__ex_flag) \ + : "r" (&(count)->counter) \ + : "cc","memory" ); \ + \ + if (unlikely(__res || __ex_flag)) \ + 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" \ + "sub %0, %0, #1 \n" \ + "strex %1, %0, [%2] \n" \ + \ + : "=&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" \ + "add %1, %0, #1 \n" \ + "strex %2, %1, [%3] \n" \ + \ + : "=&r" (__orig), "=&r" (__res), "=&r" (__ex_flag) \ + : "r" (&(count)->counter) \ + : "cc","memory" ); \ + \ + if (unlikely(__orig || __ex_flag)) \ + 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 + +/* +* For __mutex_fastpath_trylock we use another construct which could be +* described as an "incomplete atomic decrement" or a "single value cmpxchg" +* since it has two modes of failure: +* +* 1) if the exclusive store fails we fail, and +* +* 2) if the decremented value is not zero we don't even attempt the store. +* +* This provides the needed trylock semantics like cmpxchg would, but it is +* lighter and less generic than a true cmpxchg implementation. +*/ +static inline int +__mutex_fastpath_trylock (atomic_t *count,int (*fn_name)(atomic_t *)) +{ +int __ex_flag, __res; + + __asm__ ( + +"ldrex %0, [%2] \n" +"subs %0, %0, #1 \n" +"strexeq %1, %0, [%2] \n" + + :"=&r" (__res),"=&r" (__ex_flag) + :"r" (&count->counter) + :"cc" ,"memory" ); + +/* +* We must not return a synthetic 'failure' if the conditional +* did not succeed - drop back into the generic slowpath if +* this happens (should be rare): +*/ +if (unlikely (__ex_flag)) +return fn_name (count); + +return __res ==0 ; +} + +#endif +#endif