Compare and Swap
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 26 Mar 2010 01:34:12 +0000 (18:34 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:40 +0000 (17:35 -0700)
Provides atomic primitives for compare and swap.  The sparc ones are
horrendous, relying on a global lock.  Hopefully there's some hardware
support for that, and if not, then we'll need a better implementation.

kern/arch/i686/atomic.h
kern/arch/sparc/atomic.h

index 11cda25..9266ce3 100644 (file)
@@ -22,6 +22,9 @@ static inline int32_t atomic_read(atomic_t *number);
 static inline void atomic_set(atomic_t *number, int32_t val);
 static inline void atomic_inc(atomic_t *number);
 static inline void atomic_dec(atomic_t *number);
+static inline uint32_t atomic_swap(uint32_t *addr, uint32_t val);
+static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
+                                    uint32_t new_val);
 static inline void atomic_andb(volatile uint8_t RACY* number, uint8_t mask);
 static inline void atomic_orb(volatile uint8_t RACY* number, uint8_t mask);
 static inline uint32_t spin_locked(spinlock_t *SAFE lock);
@@ -57,9 +60,30 @@ static inline void atomic_inc(atomic_t *number)
 
 static inline void atomic_dec(atomic_t *number)
 {
+       // for instance, this doesn't work:
+       //asm volatile("lock decl (%0)" : "=r"(number) : : "cc");
        asm volatile("lock decl %0" : "=m"(*number) : : "cc");
 }
 
+static inline uint32_t atomic_swap(uint32_t *addr, uint32_t val)
+{
+       // this would work, but its code is bigger, and it's not like the others
+       //asm volatile("xchgl %0,(%2)" : "=r"(val) : "0"(val), "r"(addr) : "memory");
+       asm volatile("xchgl %0,%1" : "=r"(val), "=m"(*addr) : "0"(val), "m"(*addr));
+       return val;
+}
+
+/* reusing exp_val for the bool return */
+static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
+                                    uint32_t new_val)
+{
+       asm volatile("lock cmpxchgl %4,%1; sete %%al"
+                    : "=a"(exp_val), "=m"(*addr)
+                    : "m"(*addr), "a"(exp_val), "r"(new_val)
+                    : "cc");
+       return exp_val;
+}
+
 static inline void atomic_andb(volatile uint8_t RACY*number, uint8_t mask)
 {
        asm volatile("lock andb %1,%0" : "=m"(*number) : "r"(mask) : "cc");
index 9e56927..372ac15 100644 (file)
@@ -9,6 +9,14 @@ typedef struct
        volatile uint32_t rlock;
 } spinlock_t;
 
+/* This needs to be declared over here so that comp_and_swap down below can use
+ * it.  Remove this when RAMP has a proper atomic compare and swap.  (TODO) */
+static inline void
+(SLOCK(0) spin_lock_irqsave)(spinlock_t RACY*SAFE lock);
+static inline void
+(SUNLOCK(0) spin_unlock_irqsave)(spinlock_t RACY*SAFE lock);
+static inline bool spin_lock_irq_enabled(spinlock_t *SAFE lock);
+
 #define SPINLOCK_INITIALIZER {0}
 
 // atomic_t is void*, so we can't accidentally dereference it
@@ -21,6 +29,8 @@ static inline void atomic_add(atomic_t* number, int32_t inc);
 static inline void atomic_inc(atomic_t* number);
 static inline void atomic_dec(atomic_t* number);
 static inline uint32_t atomic_swap(uint32_t* addr, uint32_t val);
+static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
+                                    uint32_t new_val);
 static inline uint32_t spin_trylock(spinlock_t*SAFE lock);
 static inline uint32_t spin_locked(spinlock_t*SAFE lock);
 static inline void spin_lock(spinlock_t*SAFE lock);
@@ -77,6 +87,25 @@ static inline uint32_t atomic_swap(uint32_t* addr, uint32_t val)
        return val;
 }
 
+// TODO: make this better! (no global locks, etc)
+static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
+                                    uint32_t new_val)
+{
+       bool retval = 0;
+       uint32_t temp;
+       static spinlock_t cas_lock = SPINLOCK_INITIALIZER;
+
+       if (*addr != exp_val)
+               return 0;
+       spin_lock_irqsave(&cas_lock);
+       if (*addr == exp_val) {
+               atomic_swap(addr, new_val);
+               retval = 1;
+       }
+       spin_unlock_irqsave(&cas_lock);
+       return retval;
+}
+
 static inline uint32_t spin_trylock(spinlock_t*SAFE lock)
 {
        uint32_t reg;