Atomics rewrite (XCC)
[akaros.git] / user / parlib / include / sparc / atomic.h
1 #ifndef PARLIB_ARCH_ATOMIC_H
2 #define PARLIB_ARCH_ATOMIC_H
3
4 /* Unlike in x86, we need to include spinlocks in the user atomic ops file.
5  * Since compare and swap isn't truely non-blocking, and we can't disable
6  * interrupts in userspace, there is a slight chance of deadlock. */
7
8 #include <ros/common.h>
9 #include <ros/atomic.h>
10 #include <ros/arch/membar.h>
11
12 typedef struct
13 {
14         volatile uint32_t rlock;
15 } spinlock_t;
16
17 #define SPINLOCK_INITIALIZER {0}
18
19 static inline void atomic_init(atomic_t *number, long val);
20 static inline long atomic_read(atomic_t *number);
21 static inline void atomic_set(atomic_t *number, long val);
22 static inline void atomic_inc(atomic_t *number);
23 static inline void atomic_dec(atomic_t *number);
24 static inline long atomic_fetch_and_add(atomic_t *number, long val);
25 static inline long atomic_swap(atomic_t *addr, long val);
26 static inline void *atomic_swap_ptr(void **addr, void *val);
27 static inline uint32_t atomic_swap_u32(uint32_t *addr, uint32_t val);
28 static inline bool atomic_cas(atomic_t *addr, long exp_val, long new_val);
29 static inline bool atomic_cas_ptr(void **addr, void *exp_val, void *new_val);
30 static inline bool atomic_cas_u32(uint32_t *addr, uint32_t exp_val,
31                                   uint32_t new_val);
32 static inline void atomic_or_int(volatile int *number, int mask);
33 static inline uint32_t spin_trylock(spinlock_t*SAFE lock);
34 static inline uint32_t spin_locked(spinlock_t*SAFE lock);
35 static inline void spin_lock(spinlock_t*SAFE lock);
36 static inline void spin_unlock(spinlock_t*SAFE lock);
37
38 /* Inlined functions declared above */
39
40 static inline void atomic_init(atomic_t *number, long val)
41 {
42         val <<= 8;
43         __asm__ __volatile__ ("st %0,[%1]" : : "r"(val), "r"(number) : "memory");
44 }
45
46 static inline long atomic_read(atomic_t *number)
47 {
48         long val;
49         __asm__ __volatile__ ("ld [%1],%0" : "=r"(val) : "r"(number));
50         return val >> 8;
51 }
52
53 /* Sparc needs atomic add, but the regular ROS atomic add conflicts with
54  * glibc's internal one. */
55 static inline void ros_atomic_add(atomic_t *number, long inc)
56 {
57         atomic_fetch_and_add(number, inc);
58 }
59
60 static inline void atomic_set(atomic_t *number, long val)
61 {
62         // this works basically the same as atomic_add... but without the add
63         spin_lock((spinlock_t*)number);
64         atomic_init(number,val);
65 }
66
67 static inline void atomic_inc(atomic_t *number)
68 {
69         ros_atomic_add(number,1);
70 }
71
72 static inline void atomic_dec(atomic_t *number)
73 {
74         ros_atomic_add(number,-1);
75 }
76
77 /* Adds val to number, returning number's original value */
78 static inline long atomic_fetch_and_add(atomic_t *number, long val)
79 {
80         long retval;
81         /* this is pretty clever.  the lower 8 bits (i.e byte 3)
82          * of the atomic_t serve as a spinlock.  let's acquire it. */
83         spin_lock((spinlock_t*)number);
84         retval = atomic_read(number);
85         /* compute new counter value. */
86         val += retval;
87         /* set the new counter value.  the lock is cleared (for free) */
88         atomic_init(number, val);
89         return retval;
90 }
91
92 static inline long atomic_swap(atomic_t *addr, long val)
93 {
94         __asm__ __volatile__ ("swap [%2],%0" : "=r"(val) : "0"(val),"r"(addr) : "memory");
95         return val;
96 }
97
98 static inline void *atomic_swap_ptr(void **addr, void *val)
99 {
100         __asm__ __volatile__ ("swap [%2],%0" : "=r"(val) : "0"(val),"r"(addr) : "memory");
101         return val;
102 }
103
104 static inline uint32_t atomic_swap_u32(uint32_t *addr, uint32_t val)
105 {
106         __asm__ __volatile__ ("swap [%2],%0" : "=r"(val) : "0"(val),"r"(addr) : "memory");
107         return val;
108 }
109
110 static inline bool atomic_cas(atomic_t *addr, long exp_val, long new_val)
111 {
112         bool retval = 0;
113         long temp;
114         static spinlock_t cas_lock = SPINLOCK_INITIALIZER;
115
116         if ((long)*addr != exp_val)
117                 return 0;
118         spin_lock(&cas_lock);
119         if ((long)*addr == exp_val) {
120                 atomic_swap(addr, new_val);
121                 retval = 1;
122         }
123         spin_unlock(&cas_lock);
124         return retval;
125 }
126
127 static inline bool atomic_cas_ptr(void **addr, void *exp_val, void *new_val)
128 {
129         return atomic_cas((atomic_t*)addr, (long)exp_val, (long)new_val);
130 }
131
132 static inline bool atomic_cas_u32(uint32_t *addr, uint32_t exp_val,
133                                   uint32_t new_val)
134 {
135         return atomic_cas((atomic_t*)addr, (long)exp_val, (long)new_val);
136 }
137
138 static inline void atomic_or_int(volatile int *number, int mask)
139 {
140         int val;
141         /* this is pretty clever.  the lower 8 bits (i.e byte 3)
142          * of the atomic_t serve as a spinlock.  let's acquire it. */
143         spin_lock((spinlock_t*)number);
144         val = atomic_read((atomic_t*)number);
145         /* compute new counter value. */
146         val |= mask;
147         /* set the new counter value.  the lock is cleared (for free) */
148         atomic_init((atomic_t*)number, val);
149 }
150
151 static inline uint32_t spin_trylock(spinlock_t*SAFE lock)
152 {
153         uint32_t reg;
154         __asm__ __volatile__ ("ldstub [%1+3],%0" : "=r"(reg) : "r"(&lock->rlock) : "memory");
155         return reg;
156 }
157
158 static inline uint32_t spin_locked(spinlock_t*SAFE lock)
159 {
160         uint32_t reg;
161         __asm__ __volatile__ ("ldub [%1+3],%0" : "=r"(reg) : "r"(&lock->rlock));
162         return reg;
163 }
164
165 static inline void spin_lock(spinlock_t*SAFE lock)
166 {
167         while(spin_trylock(lock))
168                 while(spin_locked(lock));
169 }
170
171 static inline void spin_unlock(spinlock_t*SAFE lock)
172 {
173         wmb();
174         lock->rlock = 0;
175 }
176
177 static inline void spinlock_init(spinlock_t* lock)
178 {
179         lock->rlock = 0;
180 }
181
182 #endif /* !PARLIB_ARCH_ATOMIC_H */