Adds useful atomics
[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_sub_and_test(atomic_t *number, long val);
33 static inline uint32_t atomic_swap(uint32_t* addr, uint32_t val);
34 static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
35                                     uint32_t new_val);
36 static inline uint32_t spin_trylock(spinlock_t*SAFE lock);
37 static inline uint32_t spin_locked(spinlock_t*SAFE lock);
38 static inline void spin_lock(spinlock_t*SAFE lock);
39 static inline void spin_unlock(spinlock_t*SAFE lock);
40
41 /* Inlined functions declared above */
42
43 static inline void atomic_init(atomic_t* number, int32_t val)
44 {
45         val <<= 8;
46         __asm__ __volatile__ ("st %0,[%1]" : : "r"(val), "r"(number) : "memory");
47 }
48
49 static inline int32_t atomic_read(atomic_t* number)
50 {
51         int32_t val;
52         __asm__ __volatile__ ("ld [%1],%0" : "=r"(val) : "r"(number));
53         return val >> 8;
54 }
55
56 static inline void atomic_add(atomic_t* number, int32_t inc)
57 {
58         atomic_fetch_and_add(number, inc);
59 }
60
61 static inline void atomic_set(atomic_t* number, int32_t val)
62 {
63         // this works basically the same as atomic_add... but without the add
64         spin_lock((spinlock_t*)number);
65         atomic_init(number,val);
66 }
67
68 static inline void atomic_inc(atomic_t* number)
69 {
70         atomic_add(number,1);
71 }
72
73 static inline void atomic_dec(atomic_t* number)
74 {
75         atomic_add(number,-1);
76 }
77
78 /* Adds val to number, returning number's original value */
79 static inline long atomic_fetch_and_add(atomic_t *number, long val)
80 {
81         long retval;
82         /* this is pretty clever.  the lower 8 bits (i.e byte 3)
83          * of the atomic_t serve as a spinlock.  let's acquire it. */
84         spin_lock((spinlock_t*)number);
85         retval = atomic_read(number);
86         /* compute new counter value. */
87         val += retval;
88         /* set the new counter value.  the lock is cleared (for free) */
89         atomic_init(number, val);
90         return retval;
91 }
92
93 /* Subtraces val from number, returning True if the new value is 0. */
94 static inline bool atomic_sub_and_test(atomic_t *number, long val)
95 {
96         long retval = atomic_fetch_and_add(number, -val);
97         if (retval)
98                 return FALSE;
99         else
100                 return TRUE;
101 }
102
103 static inline uint32_t atomic_swap(uint32_t* addr, uint32_t val)
104 {
105         __asm__ __volatile__ ("swap [%2],%0" : "=r"(val) : "0"(val),"r"(addr) : "memory");
106         return val;
107 }
108
109 // TODO: make this better! (no global locks, etc)
110 static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
111                                     uint32_t new_val)
112 {
113         bool retval = 0;
114         uint32_t temp;
115         static spinlock_t cas_lock = SPINLOCK_INITIALIZER;
116
117         if (*addr != exp_val)
118                 return 0;
119         spin_lock_irqsave(&cas_lock);
120         if (*addr == exp_val) {
121                 atomic_swap(addr, new_val);
122                 retval = 1;
123         }
124         spin_unlock_irqsave(&cas_lock);
125         return retval;
126 }
127
128 static inline uint32_t spin_trylock(spinlock_t*SAFE lock)
129 {
130         uint32_t reg;
131         __asm__ __volatile__ ("ldstub [%1+3],%0" : "=r"(reg) : "r"(&lock->rlock) : "memory");
132         return reg;
133 }
134
135 static inline uint32_t spin_locked(spinlock_t*SAFE lock)
136 {
137         uint32_t reg;
138         __asm__ __volatile__ ("ldub [%1+3],%0" : "=r"(reg) : "r"(&lock->rlock));
139         return reg;
140 }
141
142 static inline void spin_lock(spinlock_t*SAFE lock)
143 {
144         while(spin_trylock(lock))
145                 while(spin_locked(lock));
146 }
147
148 static inline void spin_unlock(spinlock_t*SAFE lock)
149 {
150         wmb();
151         lock->rlock = 0;
152 }
153
154 static inline void spinlock_init(spinlock_t* lock)
155 {
156         lock->rlock = 0;
157 }
158
159 static inline void spinlock_debug(spinlock_t* lock)
160 {
161 }
162
163 #endif /* !ROS_INCLUDE_ATOMIC_H */