Resyncs parlib's sparc's atomics with the kernel's
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 14 Apr 2011 09:25:46 +0000 (02:25 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:01 +0000 (17:36 -0700)
Note that while similar, userland atomics in sparc are slightly
different than their kernel counterparts.  One big difference is that
sparc (and x86 too) doesn't not have an atomic_add().  This name
conflicts with glibc, and is part of a larger problem of having two
sources of atomic primitives that might not line up right.
user/parlib/include needs to work with both ROS and glibc (for now), so
this causes problems.

Another difference is with irqsaves (or lack thereof).

Normally, userspace just needs to get the functions it needs, but it was
behind enough that it just received most of the ones from the kernel.

user/parlib/include/sparc/atomic.h

index 513ec2c..c0aceca 100644 (file)
@@ -16,16 +16,20 @@ typedef struct
 #define SPINLOCK_INITIALIZER {0}
 
 // atomic_t is void*, so we can't accidentally dereference it
-//typedef void* atomic_t;
-//
-//static inline void atomic_init(atomic_t* number, int32_t val);
-//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);
+typedef void* atomic_t;
+
+static inline void atomic_init(atomic_t* number, int32_t val);
+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 long atomic_fetch_and_add(atomic_t *number, long val);
+static inline bool atomic_add_not_zero(atomic_t *number, long val);
+static inline bool atomic_sub_and_test(atomic_t *number, long val);
 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_or_int(volatile int *number, int mask);
 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);
@@ -33,35 +37,92 @@ static inline void spin_unlock(spinlock_t*SAFE lock);
 
 /* Inlined functions declared above */
 
-//static inline void atomic_init(atomic_t* number, int32_t val)
-//{
-//     val <<= 8;
-//     __asm__ __volatile__ ("st %0,[%1]" : : "r"(val), "r"(number) : "memory");
-//}
-//
-//static inline int32_t atomic_read(atomic_t* number)
-//{
-//     int32_t val;
-//     __asm__ __volatile__ ("ld [%1],%0" : "=r"(val) : "r"(number));
-//     return val >> 8;
-//}
-//
-//static inline void atomic_set(atomic_t* number, int32_t val)
-//{
-//     // this works basically the same as atomic_add... but without the add
-//     spin_lock((spinlock_t*)number);
-//     atomic_init(number,val);
-//}
-//
-//static inline void atomic_inc(atomic_t* number)
-//{
-//     atomic_add(number,1);
-//}
-//
-//static inline void atomic_dec(atomic_t* number)
-//{
-//     atomic_add(number,-1);
-//}
+static inline void atomic_init(atomic_t* number, int32_t val)
+{
+       val <<= 8;
+       __asm__ __volatile__ ("st %0,[%1]" : : "r"(val), "r"(number) : "memory");
+}
+
+static inline int32_t atomic_read(atomic_t* number)
+{
+       int32_t val;
+       __asm__ __volatile__ ("ld [%1],%0" : "=r"(val) : "r"(number));
+       return val >> 8;
+}
+
+/* Sparc needs atomic add, but the regular ROS atomic add conflicts with
+ * glibc's internal one. */
+static inline void ros_atomic_add(atomic_t* number, int32_t inc)
+{
+       atomic_fetch_and_add(number, inc);
+}
+
+static inline void atomic_set(atomic_t* number, int32_t val)
+{
+       // this works basically the same as atomic_add... but without the add
+       spin_lock((spinlock_t*)number);
+       atomic_init(number,val);
+}
+
+static inline void atomic_inc(atomic_t* number)
+{
+       ros_atomic_add(number,1);
+}
+
+static inline void atomic_dec(atomic_t* number)
+{
+       ros_atomic_add(number,-1);
+}
+
+/* Adds val to number, returning number's original value */
+static inline long atomic_fetch_and_add(atomic_t *number, long val)
+{
+       long retval;
+       /* this is pretty clever.  the lower 8 bits (i.e byte 3)
+        * of the atomic_t serve as a spinlock.  let's acquire it. */
+       spin_lock((spinlock_t*)number);
+       retval = atomic_read(number);
+       /* compute new counter value. */
+       val += retval;
+       /* set the new counter value.  the lock is cleared (for free) */
+       atomic_init(number, val);
+       return retval;
+}
+
+/* Adds val to number, so long as number was not zero.  Returns TRUE if the
+ * operation succeeded (added, not zero), returns FALSE if number is zero. */
+static inline bool atomic_add_not_zero(atomic_t *number, long val)
+{
+       long num;
+       bool retval = FALSE;
+       /* this is pretty clever.  the lower 8 bits (i.e byte 3)
+        * of the atomic_t serve as a spinlock.  let's acquire it. */
+       spin_lock((spinlock_t*)number);
+       num = atomic_read(number);
+       if (num) {
+               num += val;
+               retval = TRUE;
+       }
+       /* set the new (maybe old) counter value.  the lock is cleared (for free) */
+       atomic_init(number, num);
+       return retval;
+}
+
+/* Subtraces val from number, returning True if the new value is 0. */
+static inline bool atomic_sub_and_test(atomic_t *number, long val)
+{
+       long num;
+       bool retval = FALSE;
+       /* this is pretty clever.  the lower 8 bits (i.e byte 3)
+        * of the atomic_t serve as a spinlock.  let's acquire it. */
+       spin_lock((spinlock_t*)number);
+       num = atomic_read(number);
+       num -= val;
+       retval = num ? FALSE : TRUE;
+       /* set the new counter value.  the lock is cleared (for free) */
+       atomic_init(number, num);
+       return retval;
+}
 
 static inline uint32_t atomic_swap(uint32_t* addr, uint32_t val)
 {
@@ -88,6 +149,19 @@ static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
        return retval;
 }
 
+static inline void atomic_or_int(volatile int *number, int mask)
+{
+       int val;
+       /* this is pretty clever.  the lower 8 bits (i.e byte 3)
+        * of the atomic_t serve as a spinlock.  let's acquire it. */
+       spin_lock((spinlock_t*)number);
+       val = atomic_read((atomic_t*)number);
+       /* compute new counter value. */
+       val |= mask;
+       /* set the new counter value.  the lock is cleared (for free) */
+       atomic_init((atomic_t*)number, val);
+}
+
 static inline uint32_t spin_trylock(spinlock_t*SAFE lock)
 {
        uint32_t reg;
@@ -111,10 +185,9 @@ static inline void spin_lock(spinlock_t*SAFE lock)
 static inline void spin_unlock(spinlock_t*SAFE lock)
 {
        wmb();
-       __asm__ __volatile__ ("stub %%g0,[%0+3]" : : "r"(&lock->rlock) : "memory");
+       lock->rlock = 0;
 }
 
-
 static inline void spinlock_init(spinlock_t* lock)
 {
        lock->rlock = 0;