7a03176a1725359e1d80924d272564b170de0715
[akaros.git] / kern / arch / sparc / atomic.h
1 #ifndef ROS_INCLUDE_ATOMIC_H
2 #define ROS_INCLUDE_ATOMIC_H
3
4 #include <ros/common.h>
5 #include <ros/arch/membar.h>
6
7 typedef struct
8 {
9         volatile uint32_t rlock;
10 } spinlock_t;
11
12 /* This needs to be declared over here so that comp_and_swap down below can use
13  * it.  Remove this when RAMP has a proper atomic compare and swap.  (TODO) */
14 static inline void
15 (SLOCK(0) spin_lock_irqsave)(spinlock_t RACY*SAFE lock);
16 static inline void
17 (SUNLOCK(0) spin_unlock_irqsave)(spinlock_t RACY*SAFE lock);
18 static inline bool spin_lock_irq_enabled(spinlock_t *SAFE lock);
19
20 #define SPINLOCK_INITIALIZER {0}
21
22 // atomic_t is void*, so we can't accidentally dereference it
23 typedef void* atomic_t;
24
25 static inline void atomic_init(atomic_t* number, int32_t val);
26 static inline int32_t atomic_read(atomic_t* number);
27 static inline void atomic_set(atomic_t* number, int32_t val);
28 static inline void atomic_add(atomic_t* number, int32_t inc);
29 static inline void atomic_inc(atomic_t* number);
30 static inline void atomic_dec(atomic_t* number);
31 static inline long atomic_fetch_and_add(atomic_t *number, long val);
32 static inline bool atomic_add_not_zero(atomic_t *number, long val);
33 static inline bool atomic_sub_and_test(atomic_t *number, long val);
34 static inline void atomic_and(atomic_t *number, long mask);
35 static inline void atomic_or(atomic_t *number, int mask);
36 static inline uint32_t atomic_swap(uint32_t* addr, uint32_t val);
37 static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
38                                     uint32_t new_val);
39 static inline uint32_t spin_trylock(spinlock_t*SAFE lock);
40 static inline uint32_t spin_locked(spinlock_t*SAFE lock);
41 static inline void spin_lock(spinlock_t*SAFE lock);
42 static inline void spin_unlock(spinlock_t*SAFE lock);
43
44 /* Inlined functions declared above */
45
46 static inline void atomic_init(atomic_t* number, int32_t val)
47 {
48         val <<= 8;
49         __asm__ __volatile__ ("st %0,[%1]" : : "r"(val), "r"(number) : "memory");
50 }
51
52 static inline int32_t atomic_read(atomic_t* number)
53 {
54         int32_t val;
55         __asm__ __volatile__ ("ld [%1],%0" : "=r"(val) : "r"(number));
56         return val >> 8;
57 }
58
59 static inline void atomic_add(atomic_t* number, int32_t inc)
60 {
61         atomic_fetch_and_add(number, inc);
62 }
63
64 static inline void atomic_set(atomic_t* number, int32_t val)
65 {
66         // this works basically the same as atomic_add... but without the add
67         spin_lock((spinlock_t*)number);
68         atomic_init(number,val);
69 }
70
71 static inline void atomic_inc(atomic_t* number)
72 {
73         atomic_add(number,1);
74 }
75
76 static inline void atomic_dec(atomic_t* number)
77 {
78         atomic_add(number,-1);
79 }
80
81 /* Adds val to number, returning number's original value */
82 static inline long atomic_fetch_and_add(atomic_t *number, long val)
83 {
84         long retval;
85         /* this is pretty clever.  the lower 8 bits (i.e byte 3)
86          * of the atomic_t serve as a spinlock.  let's acquire it. */
87         spin_lock((spinlock_t*)number);
88         retval = atomic_read(number);
89         /* compute new counter value. */
90         val += retval;
91         /* set the new counter value.  the lock is cleared (for free) */
92         atomic_init(number, val);
93         return retval;
94 }
95
96 /* Adds val to number, so long as number was not zero.  Returns TRUE if the
97  * operation succeeded (added, not zero), returns FALSE if number is zero. */
98 static inline bool atomic_add_not_zero(atomic_t *number, long val)
99 {
100         long num;
101         bool retval = FALSE;
102         /* this is pretty clever.  the lower 8 bits (i.e byte 3)
103          * of the atomic_t serve as a spinlock.  let's acquire it. */
104         spin_lock((spinlock_t*)number);
105         num = atomic_read(number);
106         if (num) {
107                 num += val;
108                 retval = TRUE;
109         }
110         /* set the new (maybe old) counter value.  the lock is cleared (for free) */
111         atomic_init(number, num);
112         return retval;
113 }
114
115 /* Subtraces val from number, returning True if the new value is 0. */
116 static inline bool atomic_sub_and_test(atomic_t *number, long val)
117 {
118         long num;
119         bool retval = FALSE;
120         /* this is pretty clever.  the lower 8 bits (i.e byte 3)
121          * of the atomic_t serve as a spinlock.  let's acquire it. */
122         spin_lock((spinlock_t*)number);
123         num = atomic_read(number);
124         num -= val;
125         retval = num ? FALSE : TRUE;
126         /* set the new counter value.  the lock is cleared (for free) */
127         atomic_init(number, num);
128         return retval;
129 }
130
131 static inline void atomic_and(atomic_t *number, long mask)
132 {
133         long val;
134         /* this is pretty clever.  the lower 8 bits (i.e byte 3)
135          * of the atomic_t serve as a spinlock.  let's acquire it. */
136         spin_lock((spinlock_t*)number);
137         val = atomic_read(number);
138         /* compute new counter value. */
139         val &= mask;
140         /* set the new counter value.  the lock is cleared (for free) */
141         atomic_init(number, val);
142 }
143
144 static inline void atomic_or(atomic_t *number, int mask)
145 {
146         int val;
147         /* this is pretty clever.  the lower 8 bits (i.e byte 3)
148          * of the atomic_t serve as a spinlock.  let's acquire it. */
149         spin_lock((spinlock_t*)number);
150         val = atomic_read(number);
151         /* compute new counter value. */
152         val |= mask;
153         /* set the new counter value.  the lock is cleared (for free) */
154         atomic_init(number, val);
155 }
156
157 static inline uint32_t atomic_swap(uint32_t* addr, uint32_t val)
158 {
159         __asm__ __volatile__ ("swap [%2],%0" : "=r"(val) : "0"(val),"r"(addr) : "memory");
160         return val;
161 }
162
163 // TODO: make this better! (no global locks, etc)
164 static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
165                                     uint32_t new_val)
166 {
167         bool retval = 0;
168         uint32_t temp;
169         static spinlock_t cas_lock = SPINLOCK_INITIALIZER;
170
171         if (*addr != exp_val)
172                 return 0;
173         spin_lock_irqsave(&cas_lock);
174         if (*addr == exp_val) {
175                 atomic_swap(addr, new_val);
176                 retval = 1;
177         }
178         spin_unlock_irqsave(&cas_lock);
179         return retval;
180 }
181
182 static inline uint32_t spin_trylock(spinlock_t*SAFE lock)
183 {
184         uint32_t reg;
185         __asm__ __volatile__ ("ldstub [%1+3],%0" : "=r"(reg) : "r"(&lock->rlock) : "memory");
186         return reg;
187 }
188
189 static inline uint32_t spin_locked(spinlock_t*SAFE lock)
190 {
191         uint32_t reg;
192         __asm__ __volatile__ ("ldub [%1+3],%0" : "=r"(reg) : "r"(&lock->rlock));
193         return reg;
194 }
195
196 static inline void spin_lock(spinlock_t*SAFE lock)
197 {
198         while(spin_trylock(lock))
199                 while(spin_locked(lock));
200 }
201
202 static inline void spin_unlock(spinlock_t*SAFE lock)
203 {
204         wmb();
205         lock->rlock = 0;
206 }
207
208 static inline void spinlock_init(spinlock_t* lock)
209 {
210         lock->rlock = 0;
211 }
212
213 static inline void spinlock_debug(spinlock_t* lock)
214 {
215 }
216
217 #endif /* !ROS_INCLUDE_ATOMIC_H */