|
@@ -45,26 +45,52 @@
|
|
|
*
|
|
|
* not including memory barriers:
|
|
|
*
|
|
|
- * void atomic_set(atomic_t* v, long i) - v->val=i
|
|
|
- * long atomic_get(atomic_t* v) - return v->val
|
|
|
+ * void atomic_set(atomic_t* v, int i) - v->val=i
|
|
|
+ * int atomic_get(atomic_t* v) - return v->val
|
|
|
+ * int atomic_get_and_set(atomic_t *v, i) - return old v->val, v->val=i
|
|
|
* void atomic_inc(atomic_t* v)
|
|
|
* void atomic_dec(atomic_t* v)
|
|
|
- * long atomic_inc_and_test(atomic_t* v) - returns 1 if the result is 0
|
|
|
- * long atomic_dec_and_test(atomic_t* v) - returns 1 if the result is 0
|
|
|
- * void atomic_or (atomic_t* v, long mask) - v->val|=mask
|
|
|
- * void atomic_and(atomic_t* v, long mask) - v->val&=mask
|
|
|
+ * int atomic_inc_and_test(atomic_t* v) - returns 1 if the result is 0
|
|
|
+ * int atomic_dec_and_test(atomic_t* v) - returns 1 if the result is 0
|
|
|
+ * void atomic_or (atomic_t* v, int mask) - v->val|=mask
|
|
|
+ * void atomic_and(atomic_t* v, int mask) - v->val&=mask
|
|
|
*
|
|
|
* same ops, but with builtin memory barriers:
|
|
|
*
|
|
|
- * void mb_atomic_set(atomic_t* v, long i) - v->val=i
|
|
|
- * long mb_atomic_get(atomic_t* v) - return v->val
|
|
|
+ * void mb_atomic_set(atomic_t* v, int i) - v->val=i
|
|
|
+ * int mb_atomic_get(atomic_t* v) - return v->val
|
|
|
+ * int mb_atomic_get_and_set(atomic_t *v, i) - return old v->val, v->val=i
|
|
|
* void mb_atomic_inc(atomic_t* v)
|
|
|
* void mb_atomic_dec(atomic_t* v)
|
|
|
- * long mb_atomic_inc_and_test(atomic_t* v) - returns 1 if the result is 0
|
|
|
- * long mb_atomic_dec_and_test(atomic_t* v) - returns 1 if the result is 0
|
|
|
- * void mb_atomic_or(atomic_t* v, long mask - v->val|=mask
|
|
|
- * void mb_atomic_and(atomic_t* v, long mask)- v->val&=mask
|
|
|
- *
|
|
|
+ * int mb_atomic_inc_and_test(atomic_t* v) - returns 1 if the result is 0
|
|
|
+ * int mb_atomic_dec_and_test(atomic_t* v) - returns 1 if the result is 0
|
|
|
+ * void mb_atomic_or(atomic_t* v, int mask - v->val|=mask
|
|
|
+ * void mb_atomic_and(atomic_t* v, int mask)- v->val&=mask
|
|
|
+ *
|
|
|
+ * Same operations are available for int and long. The functions are named
|
|
|
+ * after the following rules:
|
|
|
+ * - add an int or long suffix to the correspondent atomic function
|
|
|
+ * - volatile int* or volatile long* replace atomic_t* in the functions
|
|
|
+ * declarations
|
|
|
+ * - long and int replace the parameter type (if the function has an extra
|
|
|
+ * parameter) and the return value
|
|
|
+ * E.g.:
|
|
|
+ * long atomic_get_long(volatile long* v)
|
|
|
+ * int atomic_get_int( volatile int* v)
|
|
|
+ * long atomic_get_and_set(volatile long* v, long l)
|
|
|
+ * int atomic_get_and_set(volatile int* v, int i)
|
|
|
+ *
|
|
|
+ * Config defines: CC_GCC_LIKE_ASM - the compiler support gcc style
|
|
|
+ * inline asm
|
|
|
+ * NOSMP - the code will be a little faster, but not SMP
|
|
|
+ * safe
|
|
|
+ * __CPU_i386, __CPU_x86_64, X86_OOSTORE - see
|
|
|
+ * atomic/atomic_x86.h
|
|
|
+ * __CPU_mips, __CPU_mip2, __CPU_mip64, MIPS_HAS_LLSC - see
|
|
|
+ * atomic/atomic_mip2.h
|
|
|
+ * __CPU_ppc, __CPU_ppc64 - see atomic/atomic_ppc.h
|
|
|
+ * __CPU_sparc - see atomic/atomic_sparc.h
|
|
|
+ * __CPU_sparc64, SPARC64_MODE - see atomic/atomic_sparc64.h
|
|
|
*/
|
|
|
/*
|
|
|
* History:
|
|
@@ -76,433 +102,59 @@
|
|
|
|
|
|
/* atomic_t defined as a struct to easily catch non atomic ops. on it,
|
|
|
* e.g. atomic_t foo; foo++ will generate a compile error */
|
|
|
-typedef struct{ volatile long val; } atomic_t;
|
|
|
+typedef struct{ volatile int val; } atomic_t;
|
|
|
|
|
|
|
|
|
/* store and load operations are atomic on all cpus, note however that they
|
|
|
* don't include memory barriers so if you want to use atomic_{get,set}
|
|
|
- * to implement mutexes you must explicitely use the barriers */
|
|
|
-#define atomic_set(at_var, value) ((at_var)->val=(value))
|
|
|
-#define atomic_get(at_var) ((at_var)->val)
|
|
|
-
|
|
|
-/* init atomic ops */
|
|
|
-int atomic_ops_init();
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-#if defined(__CPU_i386) || defined(__CPU_x86_64)
|
|
|
-
|
|
|
-#define HAVE_ASM_INLINE_ATOMIC_OPS
|
|
|
-
|
|
|
-#ifdef NOSMP
|
|
|
-#define __LOCK_PREF
|
|
|
-#else
|
|
|
-#define __LOCK_PREF "lock ;"
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
-/* memory barriers */
|
|
|
-
|
|
|
-#ifdef NOSMP
|
|
|
-
|
|
|
-#define membar() asm volatile ("" : : : "memory")
|
|
|
-#define membar_read() membar()
|
|
|
-#define membar_write() membar()
|
|
|
+ * to implement mutexes you must use the mb_* versions or explicitely use
|
|
|
+ * the barriers */
|
|
|
|
|
|
-#else
|
|
|
+#define atomic_set_int(pvar, i) (*(pvar)=i)
|
|
|
+#define atomic_set_long(pvar, i) (*(pvar)=i)
|
|
|
+#define atomic_get_int(pvar) (*(pvar))
|
|
|
+#define atomic_get_long(pvar) (*(pvar))
|
|
|
|
|
|
-/* although most x86 do stores in order, we're playing it safe and use
|
|
|
- * oostore ready write barriers */
|
|
|
-#define X86_OOSTORE
|
|
|
+#define atomic_set(at_var, value) (atomic_set_int(&((at_var)->val), (value)))
|
|
|
|
|
|
-/* membar: lfence, mfence, sfence available only on newer cpus, so for now
|
|
|
- * stick to lock addl */
|
|
|
-#define membar() \
|
|
|
- asm volatile( \
|
|
|
- " lock; addl $0, 0(%%esp) \n\t " \
|
|
|
- : : : "memory" \
|
|
|
- )
|
|
|
-
|
|
|
-#define membar_read() membar()
|
|
|
-
|
|
|
-#ifdef X86_OOSTORE
|
|
|
-/* out of order store version */
|
|
|
-#define membar_write() membar()
|
|
|
-#else
|
|
|
-/* no oostore, most x86 cpus => do nothing, just a gcc do_not_cache barrier*/
|
|
|
-#define membar_write() asm volatile ("" : : : "memory")
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
-#endif /* NOSMP */
|
|
|
-
|
|
|
-
|
|
|
-#define atomic_inc(var) \
|
|
|
- asm volatile( \
|
|
|
- __LOCK_PREF " incl %0 \n\t" \
|
|
|
- : "=m"((var)->val) : "m"((var)->val) : "cc", "memory" \
|
|
|
- )
|
|
|
-
|
|
|
-#define atomic_dec(var) \
|
|
|
- asm volatile( \
|
|
|
- __LOCK_PREF " decl %0 \n\t" \
|
|
|
- : "=m"((var)->val) : "m"((var)->val) : "cc", "memory" \
|
|
|
- )
|
|
|
-
|
|
|
-#define atomic_and(var, i) \
|
|
|
- asm volatile( \
|
|
|
- __LOCK_PREF " andl %1, %0 \n\t" \
|
|
|
- : "=m"((var)->val) : "ri"((i)), "m"((var)->val) : "cc", "memory" \
|
|
|
- )
|
|
|
-#define atomic_or(var, i) \
|
|
|
- asm volatile( \
|
|
|
- __LOCK_PREF " orl %1, %0 \n\t" \
|
|
|
- : "=m"((var)->val) : "ri"((i)), "m"((var)->val) : "cc", "memory" \
|
|
|
- )
|
|
|
-
|
|
|
-
|
|
|
-/* returns 1 if the result is 0 */
|
|
|
-inline static long atomic_inc_and_test(atomic_t* var)
|
|
|
+inline static int atomic_get(atomic_t *v)
|
|
|
{
|
|
|
- char ret;
|
|
|
-
|
|
|
- asm volatile(
|
|
|
- __LOCK_PREF " incl %0 \n\t"
|
|
|
- "setz %1 \n\t"
|
|
|
- : "=m"(var->val), "=qm"(ret) : "m" (var->val) : "cc", "memory"
|
|
|
- );
|
|
|
- return ret;
|
|
|
+ return atomic_get_int(&(v->val));
|
|
|
}
|
|
|
|
|
|
|
|
|
-/* returns 1 if the result is 0 */
|
|
|
-inline static long atomic_dec_and_test(atomic_t* var)
|
|
|
-{
|
|
|
- char ret;
|
|
|
-
|
|
|
- asm volatile(
|
|
|
- __LOCK_PREF " decl %0 \n\t"
|
|
|
- "setz %1 \n\t"
|
|
|
- : "=m"(var->val), "=qm"(ret) : "m" (var->val) : "cc", "memory"
|
|
|
- );
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-#ifdef NOSMP
|
|
|
-
|
|
|
-#define mb_atomic_set(v, i) \
|
|
|
- do{ \
|
|
|
- membar(); \
|
|
|
- atomic_set(v, i); \
|
|
|
- }while(0)
|
|
|
-
|
|
|
-
|
|
|
-inline static long mb_atomic_get(atomic_t* v)
|
|
|
-{
|
|
|
- membar();
|
|
|
- return atomic_get(v);
|
|
|
-}
|
|
|
-#else /* NOSMP */
|
|
|
-
|
|
|
-
|
|
|
-inline static void mb_atomic_set(atomic_t* v, long i)
|
|
|
-{
|
|
|
- asm volatile(
|
|
|
- "xchgl %1, %0 \n\t"
|
|
|
- : "+q"(i), "=m"(v->val) : "m"((v)->val) : "memory"
|
|
|
- );
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-inline static long mb_atomic_get(atomic_t* var)
|
|
|
-{
|
|
|
- long ret;
|
|
|
-
|
|
|
- asm volatile(
|
|
|
- __LOCK_PREF " cmpxchg %0, %1 \n\t"
|
|
|
- : "=a"(ret) : "m"(var->val) : "cc", "memory"
|
|
|
- );
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-#endif /* NOSMP */
|
|
|
-
|
|
|
-
|
|
|
-/* on x86 atomic intructions act also as barriers */
|
|
|
-#define mb_atomic_inc(v) atomic_inc(v)
|
|
|
-#define mb_atomic_dec(v) atomic_dec(v)
|
|
|
-#define mb_atomic_or(v, m) atomic_or(v, m)
|
|
|
-#define mb_atomic_and(v, m) atomic_and(v, m)
|
|
|
-#define mb_atomic_inc_and_test(v) atomic_inc_and_test(v)
|
|
|
-#define mb_atomic_dec_and_test(v) atomic_dec_and_test(v)
|
|
|
-
|
|
|
-
|
|
|
-#elif defined __CPU_mips2 || ( defined __CPU_mips && defined MIPS_HAS_LLSC )
|
|
|
-
|
|
|
-#define HAVE_ASM_INLINE_ATOMIC_OPS
|
|
|
-
|
|
|
-#ifdef NOSMP
|
|
|
-#define membar() asm volatile ("" : : : "memory") /* gcc do not cache barrier*/
|
|
|
-#define membar_read() membar()
|
|
|
-#define membar_write() membar()
|
|
|
-#else
|
|
|
-
|
|
|
-#define membar() \
|
|
|
- asm volatile( \
|
|
|
- ".set push \n\t" \
|
|
|
- ".set noreorder \n\t" \
|
|
|
- ".set mips2 \n\t" \
|
|
|
- " sync\n\t" \
|
|
|
- ".set pop \n\t" \
|
|
|
- : : : "memory" \
|
|
|
- )
|
|
|
-
|
|
|
-#define membar_read() membar()
|
|
|
-#define membar_write() membar()
|
|
|
-
|
|
|
-#endif /* NOSMP */
|
|
|
-
|
|
|
|
|
|
+#ifdef CC_GCC_LIKE_ASM
|
|
|
|
|
|
-/* main asm block */
|
|
|
-#define ATOMIC_ASM_OP(op) \
|
|
|
- ".set push \n\t" \
|
|
|
- ".set noreorder \n\t" \
|
|
|
- ".set mips2 \n\t" \
|
|
|
- "1: ll %1, %0 \n\t" \
|
|
|
- " " op "\n\t" \
|
|
|
- " sc %2, %0 \n\t" \
|
|
|
- " beqz %2, 1b \n\t" \
|
|
|
- " nop \n\t" \
|
|
|
- ".set pop \n\t"
|
|
|
+#if defined __CPU_i386 || defined __CPU_x86_64
|
|
|
|
|
|
+#include "atomic/atomic_x86.h"
|
|
|
|
|
|
-#define ATOMIC_FUNC_DECL(NAME, OP, RET_TYPE, RET_EXPR) \
|
|
|
- inline static RET_TYPE atomic_##NAME (atomic_t *var) \
|
|
|
- { \
|
|
|
- long ret, tmp; \
|
|
|
- asm volatile( \
|
|
|
- ATOMIC_ASM_OP(OP) \
|
|
|
- : "=m"((var)->val), "=&r"(ret), "=&r"(tmp) \
|
|
|
- : "m"((var)->val) \
|
|
|
- \
|
|
|
- ); \
|
|
|
- return RET_EXPR; \
|
|
|
- }
|
|
|
+#elif defined __CPU_mips2 || defined __CPU_mips64 || \
|
|
|
+ ( defined __CPU_mips && defined MIPS_HAS_LLSC )
|
|
|
|
|
|
+#include "atomic/atomic_mips2.h"
|
|
|
|
|
|
-/* same as above, but with CT in %3 */
|
|
|
-#define ATOMIC_FUNC_DECL_CT(NAME, OP, CT, RET_TYPE, RET_EXPR) \
|
|
|
- inline static RET_TYPE atomic_##NAME (atomic_t *var) \
|
|
|
- { \
|
|
|
- long ret, tmp; \
|
|
|
- asm volatile( \
|
|
|
- ATOMIC_ASM_OP(OP) \
|
|
|
- : "=m"((var)->val), "=&r"(ret), "=&r"(tmp) \
|
|
|
- : "r"((CT)), "m"((var)->val) \
|
|
|
- \
|
|
|
- ); \
|
|
|
- return RET_EXPR; \
|
|
|
- }
|
|
|
+#elif defined __CPU_ppc || defined __CPU_ppc64
|
|
|
|
|
|
+#include "atomic/atomic_ppc.h"
|
|
|
|
|
|
-/* takes an extra param, i which goes in %3 */
|
|
|
-#define ATOMIC_FUNC_DECL1(NAME, OP, RET_TYPE, RET_EXPR) \
|
|
|
- inline static RET_TYPE atomic_##NAME (atomic_t *var, long i) \
|
|
|
- { \
|
|
|
- long ret, tmp; \
|
|
|
- asm volatile( \
|
|
|
- ATOMIC_ASM_OP(OP) \
|
|
|
- : "=m"((var)->val), "=&r"(ret), "=&r"(tmp) \
|
|
|
- : "r"((i)), "m"((var)->val) \
|
|
|
- \
|
|
|
- ); \
|
|
|
- return RET_EXPR; \
|
|
|
- }
|
|
|
+#elif defined __CPU_sparc64
|
|
|
|
|
|
+#include "atomic/atomic_sparc64.h"
|
|
|
|
|
|
-ATOMIC_FUNC_DECL(inc, "addiu %2, %1, 1", void, /* no return */ )
|
|
|
-ATOMIC_FUNC_DECL(inc_and_test, "addiu %2, %1, 1", long, (ret+1)==0 )
|
|
|
-
|
|
|
-ATOMIC_FUNC_DECL_CT(dec, "subu %2, %1, %3", 1, void, /* no return */ )
|
|
|
-ATOMIC_FUNC_DECL_CT(dec_and_test, "subu %2, %1, %3", 1, long, (ret-1)==0 )
|
|
|
-
|
|
|
-ATOMIC_FUNC_DECL1(and, "and %2, %1, %3", void, /* no return */ )
|
|
|
-ATOMIC_FUNC_DECL1(or, "or %2, %1, %3", void, /* no return */ )
|
|
|
-
|
|
|
-
|
|
|
-/* with integrated membar */
|
|
|
-
|
|
|
-#define mb_atomic_set(v, i) \
|
|
|
- do{ \
|
|
|
- membar(); \
|
|
|
- atomic_set(v, i); \
|
|
|
- }while(0)
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-inline static long mb_atomic_get(atomic_t* v)
|
|
|
-{
|
|
|
- membar();
|
|
|
- return atomic_get(v);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-#define mb_atomic_inc(v) \
|
|
|
- do{ \
|
|
|
- membar(); \
|
|
|
- atomic_inc(v); \
|
|
|
- }while(0)
|
|
|
-
|
|
|
-#define mb_atomic_dec(v) \
|
|
|
- do{ \
|
|
|
- membar(); \
|
|
|
- atomic_dec(v); \
|
|
|
- }while(0)
|
|
|
-
|
|
|
-#define mb_atomic_or(v, m) \
|
|
|
- do{ \
|
|
|
- membar(); \
|
|
|
- atomic_or(v, m); \
|
|
|
- }while(0)
|
|
|
-
|
|
|
-#define mb_atomic_and(v, m) \
|
|
|
- do{ \
|
|
|
- membar(); \
|
|
|
- atomic_and(v, m); \
|
|
|
- }while(0)
|
|
|
-
|
|
|
-inline static int mb_atomic_inc_and_test(atomic_t* v)
|
|
|
-{
|
|
|
- membar();
|
|
|
- return atomic_inc_and_test(v);
|
|
|
-}
|
|
|
-
|
|
|
-inline static int mb_atomic_dec_and_test(atomic_t* v)
|
|
|
-{
|
|
|
- membar();
|
|
|
- return atomic_dec_and_test(v);
|
|
|
-}
|
|
|
-
|
|
|
+#elif defined __CPU_sparc
|
|
|
|
|
|
+#include "atomic/atomic_sparc.h"
|
|
|
|
|
|
#endif /* __CPU_xxx => no known cpu */
|
|
|
|
|
|
+#endif /* CC_GCC_LIKE_ASM */
|
|
|
|
|
|
-#ifndef HAVE_ASM_INLINE_ATOMIC_OPS
|
|
|
-
|
|
|
-#include "locking.h"
|
|
|
-
|
|
|
-#define ATOMIC_USE_LOCK
|
|
|
-
|
|
|
-extern gen_lock_t* _atomic_lock;
|
|
|
-
|
|
|
-
|
|
|
-#define atomic_lock lock_get(_atomic_lock)
|
|
|
-#define atomic_unlock lock_release(_atomic_lock)
|
|
|
-
|
|
|
-
|
|
|
-/* memory barriers
|
|
|
- * not a known cpu -> fall back lock/unlock: safe but costly (it should
|
|
|
- * include a memory barrier effect) */
|
|
|
-#define membar() \
|
|
|
- do{\
|
|
|
- atomic_lock; \
|
|
|
- atomic_unlock; \
|
|
|
- } while(0)
|
|
|
-
|
|
|
-
|
|
|
-#define membar_write() membar()
|
|
|
-
|
|
|
-#define membar_read() membar()
|
|
|
-
|
|
|
-
|
|
|
-/* atomic ops */
|
|
|
-
|
|
|
-#define atomic_inc(var) \
|
|
|
- do{ \
|
|
|
- atomic_lock; \
|
|
|
- (var)->val++;\
|
|
|
- atomic_unlock;\
|
|
|
- }while(0)
|
|
|
-
|
|
|
-
|
|
|
-#define atomic_dec(var) \
|
|
|
- do{ \
|
|
|
- atomic_lock; \
|
|
|
- (var)->val--; \
|
|
|
- atomic_unlock; \
|
|
|
- }while(0)
|
|
|
-
|
|
|
-
|
|
|
-#define atomic_and(var, i) \
|
|
|
- do{ \
|
|
|
- atomic_lock; \
|
|
|
- (var)->val&=i; \
|
|
|
- atomic_unlock; \
|
|
|
- }while(0)
|
|
|
-
|
|
|
-#define atomic_or(var, i) \
|
|
|
- do{ \
|
|
|
- atomic_lock; \
|
|
|
- (var)->val|=i; \
|
|
|
- atomic_unlock; \
|
|
|
- }while(0)
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-/* returns true if result is 0 */
|
|
|
-inline static long atomic_inc_and_test(atomic_t* var)
|
|
|
-{
|
|
|
- long ret;
|
|
|
-
|
|
|
- atomic_lock;
|
|
|
- var->val++;
|
|
|
- ret=var->val;
|
|
|
- atomic_unlock;
|
|
|
-
|
|
|
- return (ret==0);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* returns true if result is 0 */
|
|
|
-inline static long atomic_dec_and_test(atomic_t* var)
|
|
|
-{
|
|
|
- long ret;
|
|
|
-
|
|
|
- atomic_lock;
|
|
|
- var->val++;
|
|
|
- ret=var->val;
|
|
|
- atomic_unlock;
|
|
|
-
|
|
|
- return (ret==0);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* memory barrier versions, the same as "normal" versions, except fot
|
|
|
- * the set/get
|
|
|
- * (the * atomic_lock/unlock part should act as a barrier)
|
|
|
- */
|
|
|
-
|
|
|
-#define mb_atomic_set(v, i) \
|
|
|
- do{ \
|
|
|
- membar(); \
|
|
|
- atomic_set(v, i); \
|
|
|
- }while(0)
|
|
|
|
|
|
-#define mb_atomic_get(v) \
|
|
|
- do{ \
|
|
|
- membar(); \
|
|
|
- atomic_get(v); \
|
|
|
- }while(0)
|
|
|
+#if ! defined HAVE_ASM_INLINE_ATOMIC_OPS || ! defined HAVE_ASM_INLINE_MEMBAR
|
|
|
|
|
|
-#define mb_atomic_inc(v) atomic_inc(v)
|
|
|
-#define mb_atomic_dec(v) atomic_dec(v)
|
|
|
-#define mb_atomic_or(v, m) atomic_or(v, m)
|
|
|
-#define mb_atomic_and(v, m) atomic_and(v, m)
|
|
|
-#define mb_atomic_inc_and_test(v) atomic_inc_and_test(v)
|
|
|
-#define mb_atomic_dec_and_test(v) atomic_dec_and_test(v)
|
|
|
+#include "atomic/atomic_unknown.h"
|
|
|
|
|
|
#endif /* if HAVE_ASM_INLINE_ATOMIC_OPS */
|
|
|
|