diff options
Diffstat (limited to 'libs/pbd3/pbd/atomic.h')
-rw-r--r-- | libs/pbd3/pbd/atomic.h | 1232 |
1 files changed, 1232 insertions, 0 deletions
diff --git a/libs/pbd3/pbd/atomic.h b/libs/pbd3/pbd/atomic.h new file mode 100644 index 0000000000..81e76f41fa --- /dev/null +++ b/libs/pbd3/pbd/atomic.h @@ -0,0 +1,1232 @@ +/* + Copyright (C) 2001 Paul Davis and others (see below) + Code derived from various headers from the Linux kernel. + Copyright attributions maintained where present. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __libpbd_atomic_h__ +#define __libpbd_atomic_h__ + +#ifdef HAVE_SMP /* a macro we control, to manage ... */ +#define CONFIG_SMP /* ... the macro the kernel headers use */ +#endif + +#if defined(__powerpc__) || defined(__ppc__) + +/* + * BK Id: SCCS/s.atomic.h 1.15 10/28/01 10:37:22 trini + */ +/* + * PowerPC atomic operations + */ + +#ifndef _ASM_PPC_ATOMIC_H_ +#define _ASM_PPC_ATOMIC_H_ + +typedef struct { volatile int counter; } atomic_t; + + +#define ATOMIC_INIT(i) { (i) } + +#define atomic_read(v) ((v)->counter) +#define atomic_set(v,i) (((v)->counter) = (i)) + +extern void atomic_clear_mask(unsigned long mask, unsigned long *addr); +extern void atomic_set_mask(unsigned long mask, unsigned long *addr); + +#ifdef CONFIG_SMP +#define SMP_ISYNC "\n\tisync" +#else +#define SMP_ISYNC +#endif + +static __inline__ void atomic_add(int a, atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%3\n\ + add %0,%2,%0\n\ + stwcx. %0,0,%3\n\ + bne- 1b" + : "=&r" (t), "=m" (v->counter) + : "r" (a), "r" (&v->counter), "m" (v->counter) + : "cc"); +} + +static __inline__ int atomic_add_return(int a, atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%2\n\ + add %0,%1,%0\n\ + stwcx. %0,0,%2\n\ + bne- 1b" + SMP_ISYNC + : "=&r" (t) + : "r" (a), "r" (&v->counter) + : "cc", "memory"); + + return t; +} + +static __inline__ void atomic_sub(int a, atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%3\n\ + subf %0,%2,%0\n\ + stwcx. %0,0,%3\n\ + bne- 1b" + : "=&r" (t), "=m" (v->counter) + : "r" (a), "r" (&v->counter), "m" (v->counter) + : "cc"); +} + +static __inline__ int atomic_sub_return(int a, atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%2\n\ + subf %0,%1,%0\n\ + stwcx. %0,0,%2\n\ + bne- 1b" + SMP_ISYNC + : "=&r" (t) + : "r" (a), "r" (&v->counter) + : "cc", "memory"); + + return t; +} + +static __inline__ void atomic_inc(atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%2\n\ + addic %0,%0,1\n\ + stwcx. %0,0,%2\n\ + bne- 1b" + : "=&r" (t), "=m" (v->counter) + : "r" (&v->counter), "m" (v->counter) + : "cc"); +} + +static __inline__ int atomic_inc_return(atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%1\n\ + addic %0,%0,1\n\ + stwcx. %0,0,%1\n\ + bne- 1b" + SMP_ISYNC + : "=&r" (t) + : "r" (&v->counter) + : "cc", "memory"); + + return t; +} + +static __inline__ void atomic_dec(atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%2\n\ + addic %0,%0,-1\n\ + stwcx. %0,0,%2\n\ + bne- 1b" + : "=&r" (t), "=m" (v->counter) + : "r" (&v->counter), "m" (v->counter) + : "cc"); +} + +static __inline__ int atomic_dec_return(atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%1\n\ + addic %0,%0,-1\n\ + stwcx. %0,0,%1\n\ + bne- 1b" + SMP_ISYNC + : "=&r" (t) + : "r" (&v->counter) + : "cc", "memory"); + + return t; +} + +#define atomic_sub_and_test(a, v) (atomic_sub_return((a), (v)) == 0) +#define atomic_dec_and_test(v) (atomic_dec_return((v)) == 0) + +/* + * Atomically test *v and decrement if it is greater than 0. + * The function returns the old value of *v minus 1. + */ +static __inline__ int atomic_dec_if_positive(atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%1\n\ + addic. %0,%0,-1\n\ + blt- 2f\n\ + stwcx. %0,0,%1\n\ + bne- 1b" + SMP_ISYNC + "\n\ +2:" : "=&r" (t) + : "r" (&v->counter) + : "cc", "memory"); + + return t; +} + +#define smp_mb__before_atomic_dec() smp_mb() +#define smp_mb__after_atomic_dec() smp_mb() +#define smp_mb__before_atomic_inc() smp_mb() +#define smp_mb__after_atomic_inc() smp_mb() + +#endif /* _ASM_PPC_ATOMIC_H_ */ + +/***********************************************************************/ + +# else /* !PPC */ + +#if defined(__i386__) || defined(__x86_64__) + +#ifndef __ARCH_I386_ATOMIC__ +#define __ARCH_I386_ATOMIC__ + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + */ + +#ifdef CONFIG_SMP +#define SMP_LOCK "lock ; " +#else +#define SMP_LOCK "" +#endif + +/* + * Make sure gcc doesn't try to be clever and move things around + * on us. We need to use _exactly_ the address the user gave us, + * not some alias that contains the same information. + */ +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) { (i) } + +/** + * atomic_read - read atomic variable + * @v: pointer of type atomic_t + * + * Atomically reads the value of @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_read(v) ((v)->counter) + +/** + * atomic_set - set atomic variable + * @v: pointer of type atomic_t + * @i: required value + * + * Atomically sets the value of @v to @i. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_set(v,i) (((v)->counter) = (i)) + +/** + * atomic_add - add integer to atomic variable + * @i: integer value to add + * @v: pointer of type atomic_t + * + * Atomically adds @i to @v. Note that the guaranteed useful range + * of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_add(int i, atomic_t *v) +{ + __asm__ __volatile__( + SMP_LOCK "addl %1,%0" + :"=m" (v->counter) + :"ir" (i), "m" (v->counter)); +} + +/** + * atomic_sub - subtract the atomic variable + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_sub(int i, atomic_t *v) +{ + __asm__ __volatile__( + SMP_LOCK "subl %1,%0" + :"=m" (v->counter) + :"ir" (i), "m" (v->counter)); +} + +/** + * atomic_sub_and_test - subtract value from variable and test result + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v and returns + * true if the result is zero, or false for all + * other cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ int atomic_sub_and_test(int i, atomic_t *v) +{ + unsigned char c; + + __asm__ __volatile__( + SMP_LOCK "subl %2,%0; sete %1" + :"=m" (v->counter), "=qm" (c) + :"ir" (i), "m" (v->counter) : "memory"); + return c; +} + +/** + * atomic_inc - increment atomic variable + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_inc(atomic_t *v) +{ + __asm__ __volatile__( + SMP_LOCK "incl %0" + :"=m" (v->counter) + :"m" (v->counter)); +} + +/** + * atomic_dec - decrement atomic variable + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_dec(atomic_t *v) +{ + __asm__ __volatile__( + SMP_LOCK "decl %0" + :"=m" (v->counter) + :"m" (v->counter)); +} + +/** + * atomic_dec_and_test - decrement and test + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1 and + * returns true if the result is 0, or false for all other + * cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ int atomic_dec_and_test(atomic_t *v) +{ + unsigned char c; + + __asm__ __volatile__( + SMP_LOCK "decl %0; sete %1" + :"=m" (v->counter), "=qm" (c) + :"m" (v->counter) : "memory"); + return c != 0; +} + +/** + * atomic_inc_and_test - increment and test + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1 + * and returns true if the result is zero, or false for all + * other cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ int atomic_inc_and_test(atomic_t *v) +{ + unsigned char c; + + __asm__ __volatile__( + SMP_LOCK "incl %0; sete %1" + :"=m" (v->counter), "=qm" (c) + :"m" (v->counter) : "memory"); + return c != 0; +} + +/** + * atomic_add_negative - add and test if negative + * @v: pointer of type atomic_t + * @i: integer value to add + * + * Atomically adds @i to @v and returns true + * if the result is negative, or false when + * result is greater than or equal to zero. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ int atomic_add_negative(int i, atomic_t *v) +{ + unsigned char c; + + __asm__ __volatile__( + SMP_LOCK "addl %2,%0; sets %1" + :"=m" (v->counter), "=qm" (c) + :"ir" (i), "m" (v->counter) : "memory"); + return c; +} + +/* These are x86-specific, used by some header files */ +#define atomic_clear_mask(mask, addr) \ +__asm__ __volatile__(SMP_LOCK "andl %0,%1" \ +: : "r" (~(mask)),"m" (*addr) : "memory") + +#define atomic_set_mask(mask, addr) \ +__asm__ __volatile__(SMP_LOCK "orl %0,%1" \ +: : "r" (mask),"m" (*addr) : "memory") + +/* Atomic operations are already serializing on x86 */ +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + +#endif /* __ARCH_I386_ATOMIC__ */ + +/***********************************************************************/ + +#else /* !PPC && !i386 */ + +#ifdef __sparc__ + +/* atomic.h: These still suck, but the I-cache hit rate is higher. + * + * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 2000 Anton Blanchard (anton@linuxcare.com.au) + */ + +#ifndef __ARCH_SPARC_ATOMIC__ +#define __ARCH_SPARC_ATOMIC__ + +typedef struct { volatile int counter; } atomic_t; + +#ifndef CONFIG_SMP + +#define ATOMIC_INIT(i) { (i) } +#define atomic_read(v) ((v)->counter) +#define atomic_set(v, i) (((v)->counter) = i) + +#else +/* We do the bulk of the actual work out of line in two common + * routines in assembler, see arch/sparc/lib/atomic.S for the + * "fun" details. + * + * For SMP the trick is you embed the spin lock byte within + * the word, use the low byte so signedness is easily retained + * via a quick arithmetic shift. It looks like this: + * + * ---------------------------------------- + * | signed 24-bit counter value | lock | atomic_t + * ---------------------------------------- + * 31 8 7 0 + */ + +#define ATOMIC_INIT(i) { (i << 8) } + +static __inline__ int atomic_read(atomic_t *v) +{ + int ret = v->counter; + + while(ret & 0xff) + ret = v->counter; + + return ret >> 8; +} + +#define atomic_set(v, i) (((v)->counter) = ((i) << 8)) +#endif + +static __inline__ int __atomic_add(int i, atomic_t *v) +{ + register volatile int *ptr asm("g1"); + register int increment asm("g2"); + + ptr = &v->counter; + increment = i; + + __asm__ __volatile__( + "mov %%o7, %%g4\n\t" + "call ___atomic_add\n\t" + " add %%o7, 8, %%o7\n" + : "=&r" (increment) + : "0" (increment), "r" (ptr) + : "g3", "g4", "g7", "memory", "cc"); + + return increment; +} + +static __inline__ int __atomic_sub(int i, atomic_t *v) +{ + register volatile int *ptr asm("g1"); + register int increment asm("g2"); + + ptr = &v->counter; + increment = i; + + __asm__ __volatile__( + "mov %%o7, %%g4\n\t" + "call ___atomic_sub\n\t" + " add %%o7, 8, %%o7\n" + : "=&r" (increment) + : "0" (increment), "r" (ptr) + : "g3", "g4", "g7", "memory", "cc"); + + return increment; +} + +#define atomic_add(i, v) ((void)__atomic_add((i), (v))) +#define atomic_sub(i, v) ((void)__atomic_sub((i), (v))) + +#define atomic_dec_return(v) __atomic_sub(1, (v)) +#define atomic_inc_return(v) __atomic_add(1, (v)) + +#define atomic_sub_and_test(i, v) (__atomic_sub((i), (v)) == 0) +#define atomic_dec_and_test(v) (__atomic_sub(1, (v)) == 0) + +#define atomic_inc(v) ((void)__atomic_add(1, (v))) +#define atomic_dec(v) ((void)__atomic_sub(1, (v))) + +#define atomic_add_negative(i, v) (__atomic_add((i), (v)) < 0) + +/* Atomic operations are already serializing */ +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + + +#endif /* !(__ARCH_SPARC_ATOMIC__) */ + +/***********************************************************************/ + +#else + +#ifdef __ia64__ + +#ifndef __ARCH_IA64_ATOMIC__ +#define __ARCH_IA64_ATOMIC__ + +typedef volatile int atomic_t; + +inline +int +atomic_read (const atomic_t * a) +{ + return *a; +} + +inline +void +atomic_set(atomic_t *a, int v) +{ + *a = v; +} + +inline +void +atomic_inc (atomic_t *v) +{ + int old, r; + + do { + old = atomic_read(v); + __asm__ __volatile__ ("mov ar.ccv=%0;;" :: "rO" (old)); + __asm__ __volatile__ ("cmpxchg4.acq %0=[%1],%2,ar.ccv" + : "=r"(r) : "r"(v), "r"(old + 1) + : "memory"); + } while (r != old); +} + +inline +void +atomic_dec (atomic_t *v) +{ + int old, r; + + do { + old = atomic_read(v); + __asm__ __volatile__ ("mov ar.ccv=%0;;" :: "rO" (old)); + __asm__ __volatile__ ("cmpxchg4.acq %0=[%1],%2,ar.ccv" + : "=r"(r) : "r"(v), "r"(old - 1) + : "memory"); + } while (r != old); +} + +inline +int +atomic_dec_and_test (atomic_t *v) +{ + int old, r; + + do { + old = atomic_read(v); + __asm__ __volatile__ ("mov ar.ccv=%0;;" :: "rO" (old)); + __asm__ __volatile__ ("cmpxchg4.acq %0=[%1],%2,ar.ccv" + : "=r"(r) : "r"(v), "r"(old - 1) + : "memory"); + } while (r != old); + return old != 1; +} + +#endif /* !(__ARCH_IA64_ATOMIC__) */ + +#else + +#ifdef __alpha__ + +#ifndef _ALPHA_ATOMIC_H +#define _ALPHA_ATOMIC_H + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc... + * + * But use these as seldom as possible since they are much slower + * than regular operations. + */ + + +/* + * Counter is volatile to make sure gcc doesn't try to be clever + * and move things around on us. We need to use _exactly_ the address + * the user gave us, not some alias that contains the same information. + */ +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) ( (atomic_t) { (i) } ) + +#define atomic_read(v) ((v)->counter) +#define atomic_set(v,i) ((v)->counter = (i)) + +/* + * To get proper branch prediction for the main line, we must branch + * forward to code at the end of this object's .text section, then + * branch back to restart the operation. + */ + +static __inline__ void atomic_add(int i, atomic_t * v) +{ + unsigned long temp; + __asm__ __volatile__( + "1: ldl_l %0,%1\n" + " addl %0,%2,%0\n" + " stl_c %0,%1\n" + " beq %0,2f\n" + ".subsection 2\n" + "2: br 1b\n" + ".previous" + :"=&r" (temp), "=m" (v->counter) + :"Ir" (i), "m" (v->counter)); +} + +static __inline__ void atomic_sub(int i, atomic_t * v) +{ + unsigned long temp; + __asm__ __volatile__( + "1: ldl_l %0,%1\n" + " subl %0,%2,%0\n" + " stl_c %0,%1\n" + " beq %0,2f\n" + ".subsection 2\n" + "2: br 1b\n" + ".previous" + :"=&r" (temp), "=m" (v->counter) + :"Ir" (i), "m" (v->counter)); +} + +/* + * Same as above, but return the result value + */ +static __inline__ long atomic_add_return(int i, atomic_t * v) +{ + long temp, result; + __asm__ __volatile__( + "1: ldl_l %0,%1\n" + " addl %0,%3,%2\n" + " addl %0,%3,%0\n" + " stl_c %0,%1\n" + " beq %0,2f\n" + " mb\n" + ".subsection 2\n" + "2: br 1b\n" + ".previous" + :"=&r" (temp), "=m" (v->counter), "=&r" (result) + :"Ir" (i), "m" (v->counter) : "memory"); + return result; +} + +static __inline__ long atomic_sub_return(int i, atomic_t * v) +{ + long temp, result; + __asm__ __volatile__( + "1: ldl_l %0,%1\n" + " subl %0,%3,%2\n" + " subl %0,%3,%0\n" + " stl_c %0,%1\n" + " beq %0,2f\n" + " mb\n" + ".subsection 2\n" + "2: br 1b\n" + ".previous" + :"=&r" (temp), "=m" (v->counter), "=&r" (result) + :"Ir" (i), "m" (v->counter) : "memory"); + return result; +} + +#define atomic_dec_return(v) atomic_sub_return(1,(v)) +#define atomic_inc_return(v) atomic_add_return(1,(v)) + +#define atomic_sub_and_test(i,v) (atomic_sub_return((i), (v)) == 0) +#define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0) + +#define atomic_inc(v) atomic_add(1,(v)) +#define atomic_dec(v) atomic_sub(1,(v)) + +#define smp_mb__before_atomic_dec() smp_mb() +#define smp_mb__after_atomic_dec() smp_mb() +#define smp_mb__before_atomic_inc() smp_mb() +#define smp_mb__after_atomic_inc() smp_mb() + +#endif /* _ALPHA_ATOMIC_H */ + +#else + +#ifdef __s390__ + +#ifndef __ARCH_S390_ATOMIC__ +#define __ARCH_S390_ATOMIC__ + +/* + * include/asm-s390/atomic.h + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Denis Joseph Barrow + * + * Derived from "include/asm-i386/bitops.h" + * Copyright (C) 1992, Linus Torvalds + * + */ + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + * S390 uses 'Compare And Swap' for atomicity in SMP enviroment + */ + +typedef struct { volatile int counter; } __attribute__ ((aligned (4))) atomic_t; +#define ATOMIC_INIT(i) { (i) } + +#define atomic_eieio() __asm__ __volatile__ ("BCR 15,0") + +#define __CS_LOOP(old_val, new_val, ptr, op_val, op_string) \ + __asm__ __volatile__(" l %0,0(%2)\n" \ + "0: lr %1,%0\n" \ + op_string " %1,%3\n" \ + " cs %0,%1,0(%2)\n" \ + " jl 0b" \ + : "=&d" (old_val), "=&d" (new_val) \ + : "a" (ptr), "d" (op_val) : "cc" ); + +#define atomic_read(v) ((v)->counter) +#define atomic_set(v,i) (((v)->counter) = (i)) + +static __inline__ void atomic_add(int i, atomic_t *v) +{ + int old_val, new_val; + __CS_LOOP(old_val, new_val, v, i, "ar"); +} + +static __inline__ int atomic_add_return (int i, atomic_t *v) +{ + int old_val, new_val; + __CS_LOOP(old_val, new_val, v, i, "ar"); + return new_val; +} + +static __inline__ int atomic_add_negative(int i, atomic_t *v) +{ + int old_val, new_val; + __CS_LOOP(old_val, new_val, v, i, "ar"); + return new_val < 0; +} + +static __inline__ void atomic_sub(int i, atomic_t *v) +{ + int old_val, new_val; + __CS_LOOP(old_val, new_val, v, i, "sr"); +} + +static __inline__ void atomic_inc(volatile atomic_t *v) +{ + int old_val, new_val; + __CS_LOOP(old_val, new_val, v, 1, "ar"); +} + +static __inline__ int atomic_inc_return(volatile atomic_t *v) +{ + int old_val, new_val; + __CS_LOOP(old_val, new_val, v, 1, "ar"); + return new_val; +} + +static __inline__ int atomic_inc_and_test(volatile atomic_t *v) +{ + int old_val, new_val; + __CS_LOOP(old_val, new_val, v, 1, "ar"); + return new_val != 0; +} + +static __inline__ void atomic_dec(volatile atomic_t *v) +{ + int old_val, new_val; + __CS_LOOP(old_val, new_val, v, 1, "sr"); +} + +static __inline__ int atomic_dec_return(volatile atomic_t *v) +{ + int old_val, new_val; + __CS_LOOP(old_val, new_val, v, 1, "sr"); + return new_val; +} + +static __inline__ int atomic_dec_and_test(volatile atomic_t *v) +{ + int old_val, new_val; + __CS_LOOP(old_val, new_val, v, 1, "sr"); + return new_val == 0; +} + +static __inline__ void atomic_clear_mask(unsigned long mask, atomic_t *v) +{ + int old_val, new_val; + __CS_LOOP(old_val, new_val, v, ~mask, "nr"); +} + +static __inline__ void atomic_set_mask(unsigned long mask, atomic_t *v) +{ + int old_val, new_val; + __CS_LOOP(old_val, new_val, v, mask, "or"); +} + +/* + returns 0 if expected_oldval==value in *v ( swap was successful ) + returns 1 if unsuccessful. +*/ +static __inline__ int +atomic_compare_and_swap(int expected_oldval,int new_val,atomic_t *v) +{ + int retval; + + __asm__ __volatile__( + " lr 0,%2\n" + " cs 0,%3,0(%1)\n" + " ipm %0\n" + " srl %0,28\n" + "0:" + : "=&d" (retval) + : "a" (v), "d" (expected_oldval) , "d" (new_val) + : "0", "cc"); + return retval; +} + +/* + Spin till *v = expected_oldval then swap with newval. + */ +static __inline__ void +atomic_compare_and_swap_spin(int expected_oldval,int new_val,atomic_t *v) +{ + __asm__ __volatile__( + "0: lr 0,%1\n" + " cs 0,%2,0(%0)\n" + " jl 0b\n" + : : "a" (v), "d" (expected_oldval) , "d" (new_val) + : "cc", "0" ); +} + +#define smp_mb__before_atomic_dec() smp_mb() +#define smp_mb__after_atomic_dec() smp_mb() +#define smp_mb__before_atomic_inc() smp_mb() +#define smp_mb__after_atomic_inc() smp_mb() + +#endif /* __ARCH_S390_ATOMIC __ */ + +#else + +#ifdef __mips__ + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + * + * But use these as seldom as possible since they are much more slower + * than regular operations. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1996, 1997, 2000 by Ralf Baechle + */ +#ifndef __ASM_ATOMIC_H +#define __ASM_ATOMIC_H + +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) { (i) } + +/* + * atomic_read - read atomic variable + * @v: pointer of type atomic_t + * + * Atomically reads the value of @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_read(v) ((v)->counter) + +/* + * atomic_set - set atomic variable + * @v: pointer of type atomic_t + * @i: required value + * + * Atomically sets the value of @v to @i. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_set(v,i) ((v)->counter = (i)) + +/* + * ... while for MIPS II and better we can use ll/sc instruction. This + * implementation is SMP safe ... + */ + +/* + * atomic_add - add integer to atomic variable + * @i: integer value to add + * @v: pointer of type atomic_t + * + * Atomically adds @i to @v. Note that the guaranteed useful range + * of an atomic_t is only 24 bits. + */ +extern __inline__ void atomic_add(int i, atomic_t * v) +{ + unsigned long temp; + + __asm__ __volatile__( + ".set push # atomic_add\n" + ".set mips2 \n" + "1: ll %0, %1 \n" + " addu %0, %2 \n" + " sc %0, %1 \n" + " beqz %0, 1b \n" + ".set pop \n" + : "=&r" (temp), "=m" (v->counter) + : "Ir" (i), "m" (v->counter)); +} + +/* + * atomic_sub - subtract the atomic variable + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +extern __inline__ void atomic_sub(int i, atomic_t * v) +{ + unsigned long temp; + + __asm__ __volatile__( + ".set push # atomic_sub\n" + ".set mips2 \n" + "1: ll %0, %1 \n" + " subu %0, %2 \n" + " sc %0, %1 \n" + " beqz %0, 1b \n" + ".set pop \n" + : "=&r" (temp), "=m" (v->counter) + : "Ir" (i), "m" (v->counter)); +} + +/* + * Same as above, but return the result value + */ +extern __inline__ int atomic_add_return(int i, atomic_t * v) +{ + unsigned long temp, result; + + __asm__ __volatile__( + ".set push # atomic_add_return\n" + ".set mips2 \n" + ".set noreorder \n" + "1: ll %1, %2 \n" + " addu %0, %1, %3 \n" + " sc %0, %2 \n" + " beqz %0, 1b \n" + " addu %0, %1, %3 \n" + " sync \n" + ".set pop \n" + : "=&r" (result), "=&r" (temp), "=m" (v->counter) + : "Ir" (i), "m" (v->counter) + : "memory"); + + return result; +} + +extern __inline__ int atomic_sub_return(int i, atomic_t * v) +{ + unsigned long temp, result; + + __asm__ __volatile__( + ".set push # atomic_sub_return\n" + ".set mips2 \n" + ".set noreorder \n" + "1: ll %1, %2 \n" + " subu %0, %1, %3 \n" + " sc %0, %2 \n" + " beqz %0, 1b \n" + " subu %0, %1, %3 \n" + " sync \n" + ".set pop \n" + : "=&r" (result), "=&r" (temp), "=m" (v->counter) + : "Ir" (i), "m" (v->counter) + : "memory"); + + return result; +} + +#define atomic_dec_return(v) atomic_sub_return(1,(v)) +#define atomic_inc_return(v) atomic_add_return(1,(v)) + +/* + * atomic_sub_and_test - subtract value from variable and test result + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v and returns + * true if the result is zero, or false for all + * other cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_sub_and_test(i,v) (atomic_sub_return((i), (v)) == 0) + +/* + * atomic_inc_and_test - increment and test + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1 + * and returns true if the result is zero, or false for all + * other cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_inc_and_test(v) (atomic_inc_return(1, (v)) == 0) + +/* + * atomic_dec_and_test - decrement by 1 and test + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1 and + * returns true if the result is 0, or false for all other + * cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0) + +/* + * atomic_inc - increment atomic variable + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_inc(v) atomic_add(1,(v)) + +/* + * atomic_dec - decrement and test + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_dec(v) atomic_sub(1,(v)) + +/* + * atomic_add_negative - add and test if negative + * @v: pointer of type atomic_t + * @i: integer value to add + * + * Atomically adds @i to @v and returns true + * if the result is negative, or false when + * result is greater than or equal to zero. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + * + * Currently not implemented for MIPS. + */ + +/* Atomic operations are already serializing */ +#define smp_mb__before_atomic_dec() smp_mb() +#define smp_mb__after_atomic_dec() smp_mb() +#define smp_mb__before_atomic_inc() smp_mb() +#define smp_mb__after_atomic_inc() smp_mb() + +#endif /* __ASM_ATOMIC_H */ + +#else + +#if defined(__m68k__) + +#ifndef __ARCH_M68K_ATOMIC__ +#define __ARCH_M68K_ATOMIC__ + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + */ + +/* + * We do not have SMP m68k systems, so we don't have to deal with that. + */ + +typedef struct { int counter; } atomic_t; +#define ATOMIC_INIT(i) { (i) } + +#define atomic_read(v) ((v)->counter) +#define atomic_set(v, i) (((v)->counter) = i) + +static __inline__ void atomic_add(int i, atomic_t *v) +{ + __asm__ __volatile__("addl %1,%0" : "=m" (*v) : "id" (i), "0" (*v)); +} + +static __inline__ void atomic_sub(int i, atomic_t *v) +{ + __asm__ __volatile__("subl %1,%0" : "=m" (*v) : "id" (i), "0" (*v)); +} + +static __inline__ void atomic_inc(volatile atomic_t *v) +{ + __asm__ __volatile__("addql #1,%0" : "=m" (*v): "0" (*v)); +} + +static __inline__ void atomic_dec(volatile atomic_t *v) +{ + __asm__ __volatile__("subql #1,%0" : "=m" (*v): "0" (*v)); +} + +static __inline__ int atomic_dec_and_test(volatile atomic_t *v) +{ + char c; + __asm__ __volatile__("subql #1,%1; seq %0" : "=d" (c), "=m" (*v): "1" (*v)); + return c != 0; +} + +#define atomic_clear_mask(mask, v) \ + __asm__ __volatile__("andl %1,%0" : "=m" (*v) : "id" (~(mask)),"0"(*v)) + +#define atomic_set_mask(mask, v) \ + __asm__ __volatile__("orl %1,%0" : "=m" (*v) : "id" (mask),"0"(*v)) + +/* Atomic operations are already serializing */ +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + +#endif /* __ARCH_M68K_ATOMIC __ */ + +#else + +#warning libs/pbd has no implementation of strictly atomic operations for your hardware. + +#define __NO_STRICT_ATOMIC +#ifdef __NO_STRICT_ATOMIC + +/* + * Because the implementations from the kernel (where all these come + * from) use cli and spinlocks for hppa and arm... + */ + +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) ( (atomic_t) { (i) } ) + +#define atomic_read(v) ((v)->counter) +#define atomic_set(v,i) ((v)->counter = (i)) + +static __inline__ void atomic_inc(atomic_t *v) +{ + v->counter++; +} + +static __inline__ void atomic_dec(atomic_t *v) +{ + v->counter--; +} + +static __inline__ int atomic_dec_and_test(atomic_t *v) +{ + int res; + v->counter--; + res = v->counter; + return res == 0; +} + +static __inline__ int atomic_inc_and_test(atomic_t *v) +{ + int res; + v->counter++; + res = v->counter; + return res == 0; +} + +# endif /* __NO_STRICT_ATOMIC */ +# endif /* m68k */ +# endif /* mips */ +# endif /* s390 */ +# endif /* alpha */ +# endif /* ia64 */ +# endif /* sparc */ +# endif /* i386 */ +# endif /* ppc */ + +#endif /* __libpbd_atomic_h__ */ + |