[PATCH] atomic: cmpxchg
authorNick Piggin <nickpiggin@yahoo.com.au>
Mon, 14 Nov 2005 00:07:24 +0000 (16:07 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 14 Nov 2005 02:14:16 +0000 (18:14 -0800)
Introduce an atomic_cmpxchg operation.

Signed-off-by: Nick Piggin <npiggin@suse.de>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
23 files changed:
Documentation/atomic_ops.txt
arch/sparc/lib/atomic32.c
include/asm-alpha/atomic.h
include/asm-arm/atomic.h
include/asm-arm26/atomic.h
include/asm-cris/atomic.h
include/asm-frv/atomic.h
include/asm-h8300/atomic.h
include/asm-i386/atomic.h
include/asm-ia64/atomic.h
include/asm-m68k/atomic.h
include/asm-m68knommu/atomic.h
include/asm-mips/atomic.h
include/asm-parisc/atomic.h
include/asm-powerpc/atomic.h
include/asm-s390/atomic.h
include/asm-sh/atomic.h
include/asm-sh64/atomic.h
include/asm-sparc/atomic.h
include/asm-sparc64/atomic.h
include/asm-v850/atomic.h
include/asm-x86_64/atomic.h
include/asm-xtensa/atomic.h

index 8eedaa24f5e284f09dcfce6b2164bc53cfaed579..f1744161ef0694cef8dab811d25e0efea9c1ea7a 100644 (file)
@@ -115,6 +115,21 @@ boolean is return which indicates whether the resulting counter value
 is negative.  It requires explicit memory barrier semantics around the
 operation.
 
+Finally:
+
+       int atomic_cmpxchg(atomic_t *v, int old, int new);
+
+This performs an atomic compare exchange operation on the atomic value v,
+with the given old and new values. Like all atomic_xxx operations,
+atomic_cmpxchg will only satisfy its atomicity semantics as long as all
+other accesses of *v are performed through atomic_xxx operations.
+
+atomic_cmpxchg requires explicit memory barriers around the operation.
+
+The semantics for atomic_cmpxchg are the same as those defined for 'cas'
+below.
+
+
 If a caller requires memory barrier semantics around an atomic_t
 operation which does not return a value, a set of interfaces are
 defined which accomplish this:
index 2e64e8c3e8e54c1aafbe66e34d3d64cf0f96f895..be46f65451846af2eef721a0c1d111082cbeec86 100644 (file)
@@ -37,17 +37,28 @@ int __atomic_add_return(int i, atomic_t *v)
        spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
        return ret;
 }
+EXPORT_SYMBOL(__atomic_add_return);
 
-void atomic_set(atomic_t *v, int i)
+int atomic_cmpxchg(atomic_t *v, int old, int new)
 {
+       int ret;
        unsigned long flags;
-       spin_lock_irqsave(ATOMIC_HASH(v), flags);
 
-       v->counter = i;
+       spin_lock_irqsave(ATOMIC_HASH(v), flags);
+       ret = v->counter;
+       if (likely(ret == old))
+               v->counter = new;
 
        spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
+       return ret;
 }
 
-EXPORT_SYMBOL(__atomic_add_return);
-EXPORT_SYMBOL(atomic_set);
+void atomic_set(atomic_t *v, int i)
+{
+       unsigned long flags;
 
+       spin_lock_irqsave(ATOMIC_HASH(v), flags);
+       v->counter = i;
+       spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
+}
+EXPORT_SYMBOL(atomic_set);
index 20ac3d95ecd9ae34ab3fcba2c58a5656c72be6d7..a6660809a879c99c14b2d935a2fe941ff48bdc96 100644 (file)
@@ -177,6 +177,8 @@ static __inline__ long atomic64_sub_return(long i, atomic64_t * v)
        return result;
 }
 
+#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
+
 #define atomic_dec_return(v) atomic_sub_return(1,(v))
 #define atomic64_dec_return(v) atomic64_sub_return(1,(v))
 
index 2885972b0855cafb87e922641560d3fcd9c4c2d8..8ab1689ef56a1c3388fd0d25801230fd3b6516e7 100644 (file)
@@ -80,6 +80,23 @@ static inline int atomic_sub_return(int i, atomic_t *v)
        return result;
 }
 
+static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new)
+{
+       u32 oldval, res;
+
+       do {
+               __asm__ __volatile__("@ atomic_cmpxchg\n"
+               "ldrex  %1, [%2]\n"
+               "teq    %1, %3\n"
+               "strexeq %0, %4, [%2]\n"
+                   : "=&r" (res), "=&r" (oldval)
+                   : "r" (&ptr->counter), "Ir" (old), "r" (new)
+                   : "cc");
+       } while (res);
+
+       return oldval;
+}
+
 static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
 {
        unsigned long tmp, tmp2;
@@ -131,6 +148,20 @@ static inline int atomic_sub_return(int i, atomic_t *v)
        return val;
 }
 
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+       int ret;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       ret = v->counter;
+       if (likely(ret == old))
+               v->counter = new;
+       local_irq_restore(flags);
+
+       return ret;
+}
+
 static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
 {
        unsigned long flags;
index 4a88235c0e76a4fc41ca372d6df413ba811ded02..54b24ead7132520692d54ea45eec9329780b3e12 100644 (file)
@@ -62,6 +62,20 @@ static inline int atomic_sub_return(int i, atomic_t *v)
         return val;
 }
 
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+       int ret;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       ret = v->counter;
+       if (likely(ret == old))
+               v->counter = new;
+       local_irq_restore(flags);
+
+       return ret;
+}
+
 static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
 {
         unsigned long flags;
index 8c2e78304523c24da5904dc9b3d85d2988383979..45891f7de00fc1c58a9bcc48158c75dc2a386746 100644 (file)
@@ -123,6 +123,19 @@ static inline int atomic_inc_and_test(volatile atomic_t *v)
        return retval;
 }
 
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+       int ret;
+       unsigned long flags;
+
+       cris_atomic_save(v, flags);
+       ret = v->counter;
+       if (likely(ret == old))
+               v->counter = new;
+       cris_atomic_restore(v, flags);
+       return ret;
+}
+
 /* Atomic operations are already serializing */
 #define smp_mb__before_atomic_dec()    barrier()
 #define smp_mb__after_atomic_dec()     barrier()
index e7596846342879c7bcaa21c1b082a3d3bc07b27b..55f06a0e949f57046869dc0174ea6fb2d25441a7 100644 (file)
@@ -414,4 +414,6 @@ extern uint32_t __cmpxchg_32(uint32_t *v, uint32_t test, uint32_t new);
 
 #endif
 
+#define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new))
+
 #endif /* _ASM_ATOMIC_H */
index 7230f650799501e59044322fd7196f72eddb4400..d504392594919e3389918ab8210c84eb520bc0e2 100644 (file)
@@ -82,6 +82,19 @@ static __inline__ int atomic_dec_and_test(atomic_t *v)
        return ret == 0;
 }
 
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+       int ret;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       ret = v->counter;
+       if (likely(ret == old))
+               v->counter = new;
+       local_irq_restore(flags);
+       return ret;
+}
+
 static __inline__ void atomic_clear_mask(unsigned long mask, unsigned long *v)
 {
        __asm__ __volatile__("stc ccr,r1l\n\t"
index 509720be772a4121aad122b2ec5fd9cf83a7cdf0..5ff698e9d2c2f79738fe5d2de237320138bf7f3c 100644 (file)
@@ -215,6 +215,8 @@ static __inline__ int atomic_sub_return(int i, atomic_t *v)
        return atomic_add_return(-i,v);
 }
 
+#define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new))
+
 #define atomic_inc_return(v)  (atomic_add_return(1,v))
 #define atomic_dec_return(v)  (atomic_sub_return(1,v))
 
index 874a6f890e75c14fc2677c1b29252ef60b662234..593d3da9f3c20f6a03e96165fd8395e465497e24 100644 (file)
@@ -88,6 +88,8 @@ ia64_atomic64_sub (__s64 i, atomic64_t *v)
        return new;
 }
 
+#define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new))
+
 #define atomic_add_return(i,v)                                         \
 ({                                                                     \
        int __ia64_aar_i = (i);                                         \
index 38f3043e7fe1acfb9be1b8c8286eb4f488c1672b..b821975a361a1e7af8caba44d56ffb29d8a0083a 100644 (file)
@@ -139,6 +139,8 @@ static inline void atomic_set_mask(unsigned long mask, unsigned long *v)
        __asm__ __volatile__("orl %1,%0" : "+m" (*v) : "id" (mask));
 }
 
+#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
+
 /* Atomic operations are already serializing */
 #define smp_mb__before_atomic_dec()    barrier()
 #define smp_mb__after_atomic_dec()     barrier()
index a83631ed8c8f7a487b446893562b80e12172b346..2fd33a56b603a2bc63e93c26bbb94801d7b3dd14 100644 (file)
@@ -128,6 +128,8 @@ static inline int atomic_sub_return(int i, atomic_t * v)
        return temp;
 }
 
+#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
+
 #define atomic_dec_return(v) atomic_sub_return(1,(v))
 #define atomic_inc_return(v) atomic_add_return(1,(v))
 
index 6202eb8a14b75ac161db078b6566a15aad325ad0..4fba0d003c995b1b8c4ed3313a6b8d715c1b073c 100644 (file)
@@ -287,6 +287,8 @@ static __inline__ int atomic_sub_if_positive(int i, atomic_t * v)
        return result;
 }
 
+#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
+
 #define atomic_dec_return(v) atomic_sub_return(1,(v))
 #define atomic_inc_return(v) atomic_add_return(1,(v))
 
index 048a2c7fd0c0a54530fdabb5350915652fa1e2c0..52c9a45b5f871211bfd4aa7bc28f65be82735ffe 100644 (file)
@@ -164,6 +164,7 @@ static __inline__ int atomic_read(const atomic_t *v)
 }
 
 /* exported interface */
+#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
 
 #define atomic_add(i,v)        ((void)(__atomic_add_return( ((int)i),(v))))
 #define atomic_sub(i,v)        ((void)(__atomic_add_return(-((int)i),(v))))
index 9c0b372a46e1ffb4f0160857ff0f21192f401692..37205faa9d7c7a872f4a711ee3b162ec1858ab6d 100644 (file)
@@ -164,6 +164,8 @@ static __inline__ int atomic_dec_return(atomic_t *v)
        return t;
 }
 
+#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
+
 #define atomic_sub_and_test(a, v)      (atomic_sub_return((a), (v)) == 0)
 #define atomic_dec_and_test(v)         (atomic_dec_return((v)) == 0)
 
index 9d86ba6f12d004031b0338c2e1ffd9ccb5f1fae2..631014d5de9084ba843454ee136f4288703e452d 100644 (file)
@@ -198,6 +198,8 @@ atomic_compare_and_swap(int expected_oldval,int new_val,atomic_t *v)
         return retval;
 }
 
+#define atomic_cmpxchg(v, o, n) (atomic_compare_and_swap((o), (n), &((v)->counter)))
+
 #define smp_mb__before_atomic_dec()    smp_mb()
 #define smp_mb__after_atomic_dec()     smp_mb()
 #define smp_mb__before_atomic_inc()    smp_mb()
index 3c4f805da1ac929fd7e7d7bdcb562882a7847645..a148c762d3666567b70528c102c58152cabf0ca2 100644 (file)
@@ -87,6 +87,20 @@ static __inline__ int atomic_sub_return(int i, atomic_t * v)
 #define atomic_inc(v) atomic_add(1,(v))
 #define atomic_dec(v) atomic_sub(1,(v))
 
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+       int ret;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       ret = v->counter;
+       if (likely(ret == old))
+               v->counter = new;
+       local_irq_restore(flags);
+
+       return ret;
+}
+
 static __inline__ void atomic_clear_mask(unsigned int mask, atomic_t *v)
 {
        unsigned long flags;
index 8c3872d3e65f6f4e12ac10c3d2144ff14af7e22a..6eeb57b015ce50442cd5b3c17dad2e21d022980f 100644 (file)
@@ -99,6 +99,20 @@ static __inline__ int atomic_sub_return(int i, atomic_t * v)
 #define atomic_inc(v) atomic_add(1,(v))
 #define atomic_dec(v) atomic_sub(1,(v))
 
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+       int ret;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       ret = v->counter;
+       if (likely(ret == old))
+               v->counter = new;
+       local_irq_restore(flags);
+
+       return ret;
+}
+
 static __inline__ void atomic_clear_mask(unsigned int mask, atomic_t *v)
 {
        unsigned long flags;
index 37f6ab601c3ddf846392e1ef8d732a778e10a55e..52bdd1a895faaf0df90d57d42ec0140cdb5f45b8 100644 (file)
@@ -19,6 +19,7 @@ typedef struct { volatile int counter; } atomic_t;
 #define ATOMIC_INIT(i)  { (i) }
 
 extern int __atomic_add_return(int, atomic_t *);
+extern int atomic_cmpxchg(atomic_t *, int, int);
 extern void atomic_set(atomic_t *, int);
 
 #define atomic_read(v)          ((v)->counter)
index e175afcf2cdeb852d712a40f51b87678dbb26521..3a0b4383bbaca3147e97712dd896366b09c8ed0f 100644 (file)
@@ -70,6 +70,8 @@ extern int atomic64_sub_ret(int, atomic64_t *);
 #define atomic_add_negative(i, v) (atomic_add_ret(i, v) < 0)
 #define atomic64_add_negative(i, v) (atomic64_add_ret(i, v) < 0)
 
+#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
+
 /* Atomic operations are already serializing */
 #ifdef CONFIG_SMP
 #define smp_mb__before_atomic_dec()    membar_storeload_loadload();
index 395268a8c0dec1787fca9f39d29eab8df1d51797..e497166ca42b0946c5b5aa406c936ed8c3ab4acf 100644 (file)
@@ -90,6 +90,20 @@ static __inline__ void atomic_clear_mask (unsigned long mask, unsigned long *add
 #define atomic_dec_and_test(v)         (atomic_sub_return (1, (v)) == 0)
 #define atomic_add_negative(i,v)       (atomic_add_return ((i), (v)) < 0)
 
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+       int ret;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       ret = v->counter;
+       if (likely(ret == old))
+               v->counter = new;
+       local_irq_restore(flags);
+
+       return ret;
+}
+
 /* Atomic operations are already serializing on ARM */
 #define smp_mb__before_atomic_dec()    barrier()
 #define smp_mb__after_atomic_dec()     barrier()
index fc4c5956e1eaf82d76b4f1e1bc98de2b26bbfab3..75c8a1e96737adb4acc3ada66ce0acce8bb476d0 100644 (file)
@@ -360,6 +360,8 @@ static __inline__ int atomic_sub_return(int i, atomic_t *v)
        return atomic_add_return(-i,v);
 }
 
+#define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new))
+
 #define atomic_inc_return(v)  (atomic_add_return(1,v))
 #define atomic_dec_return(v)  (atomic_sub_return(1,v))
 
index 12b5732dc6e5986ef4966ca87afe7f019745ef3e..cd40c5e75160302e3e75d3af55febf513a3c04e2 100644 (file)
@@ -223,6 +223,7 @@ static inline int atomic_sub_return(int i, atomic_t * v)
  */
 #define atomic_add_negative(i,v) (atomic_add_return((i),(v)) < 0)
 
+#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
 
 static inline void atomic_clear_mask(unsigned int mask, atomic_t *v)
 {