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