b778275ab0d16635ae9a0e0d27134aa9bea4567c
[akaros.git] / kern / arch / i686 / 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 #include <arch/x86.h>
7 #include <arch/arch.h>
8 typedef void * RACY atomic_t;
9 struct spinlock {
10         volatile uint32_t RACY rlock;
11 #ifdef __CONFIG_SPINLOCK_DEBUG__
12         void *call_site;        
13         uint32_t calling_core;
14 #endif
15 };
16 typedef struct spinlock spinlock_t;
17 #define SPINLOCK_INITIALIZER {0}
18
19 static inline void atomic_init(atomic_t *number, int32_t val);
20 static inline int32_t atomic_read(atomic_t *number);
21 static inline void atomic_set(atomic_t *number, int32_t val);
22 static inline void atomic_add(atomic_t* number, long val);
23 static inline void atomic_inc(atomic_t *number);
24 static inline void atomic_dec(atomic_t *number);
25 static inline long atomic_fetch_and_add(atomic_t *number, long val);
26 static inline bool atomic_add_not_zero(atomic_t *number, long val);
27 static inline bool atomic_sub_and_test(atomic_t *number, long val);
28 static inline void atomic_and(atomic_t *number, long mask);
29 static inline void atomic_or(atomic_t *number, int mask);
30 static inline uint32_t atomic_swap(uint32_t *addr, uint32_t val);
31 static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
32                                     uint32_t new_val);
33 static inline void atomic_andb(volatile uint8_t RACY* number, uint8_t mask);
34 static inline void atomic_orb(volatile uint8_t RACY* number, uint8_t mask);
35 static inline uint32_t spin_locked(spinlock_t *SAFE lock);
36 static inline void __spin_lock(volatile uint32_t SRACY*CT(1) rlock);
37 static inline void spin_lock(spinlock_t *lock);
38 static inline void spin_unlock(spinlock_t *lock);
39 static inline void spinlock_init(spinlock_t *lock);
40 void spinlock_debug(spinlock_t *lock);
41
42 /* Inlined functions declared above */
43 static inline void atomic_init(atomic_t *number, int32_t val)
44 {
45         asm volatile("movl %1,%0" : "=m"(*number) : "r"(val));
46 }
47
48 static inline int32_t atomic_read(atomic_t *number)
49 {
50         int32_t val;
51         asm volatile("movl %1,%0" : "=r"(val) : "m"(*number));
52         return val;
53 }
54
55 static inline void atomic_set(atomic_t *number, int32_t val)
56 {
57         asm volatile("movl %1,%0" : "=m"(*number) : "r"(val));
58 }
59
60 static inline void atomic_add(atomic_t* number, long val)
61 {
62         asm volatile("lock addl %1,%0" : "=m"(*number) : "r"(val) : "cc");
63 }
64
65 // need to do this with pointers and deref.  %0 needs to be the memory address
66 static inline void atomic_inc(atomic_t *number)
67 {
68         asm volatile("lock incl %0" : "=m"(*number) : : "cc");
69 }
70
71 static inline void atomic_dec(atomic_t *number)
72 {
73         // for instance, this doesn't work:
74         //asm volatile("lock decl (%0)" : "=r"(number) : : "cc");
75         asm volatile("lock decl %0" : "=m"(*number) : : "cc");
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         asm volatile("lock xadd %0,%1" : "=r"(val), "=m"(*number)
82                                        : "0"(val), "m"(*number)
83                                        : "cc" );
84         return val;
85 }
86
87 /* Adds val to number, so long as number was not zero.  Returns TRUE if the
88  * operation succeeded (added, not zero), returns FALSE if number is zero. */
89 static inline bool atomic_add_not_zero(atomic_t *number, long val)
90 {
91         long old_num, new_num;
92         do {
93                 old_num = atomic_read(number);
94                 if (!old_num)
95                         return FALSE;
96                 new_num = old_num + val;
97         } while (!atomic_comp_swap((uint32_t*)number, old_num, new_num));
98         return TRUE;
99 }
100
101 /* Subtraces val from number, returning True if the new value is 0. */
102 static inline bool atomic_sub_and_test(atomic_t *number, long val)
103 {
104         bool b;
105         asm volatile("lock sub %2,%1; setz %0" : "=q"(b), "=m"(*number)
106                                                : "r"(val), "m"(*number)
107                                                : "cc" );
108         return b;
109 }
110
111 static inline void atomic_and(atomic_t *number, long mask)
112 {
113         asm volatile("lock andl %1,%0" : "=m"(*number) : "q"(mask) : "cc");
114 }
115
116 static inline void atomic_or(atomic_t *number, int mask)
117 {
118         asm volatile("lock orl %1,%0" : "=m"(*number) : "q"(mask) : "cc");
119 }
120
121 static inline uint32_t atomic_swap(uint32_t *addr, uint32_t val)
122 {
123         // this would work, but its code is bigger, and it's not like the others
124         //asm volatile("xchgl %0,(%2)" : "=r"(val) : "0"(val), "r"(addr) : "memory");
125         asm volatile("xchgl %0,%1" : "=r"(val), "=m"(*addr) : "0"(val), "m"(*addr));
126         return val;
127 }
128
129 /* reusing exp_val for the bool return.  1 (TRUE) for success (like test).  Need
130  * to zero eax, since it will get set if the cmpxchgl failed. */
131 static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
132                                     uint32_t new_val)
133 {
134         asm volatile("lock cmpxchgl %4,%1; movl $0,%%eax; sete %%al"
135                      : "=a"(exp_val), "=m"(*addr)
136                      : "m"(*addr), "a"(exp_val), "r"(new_val)
137                      : "cc", "memory");
138         return exp_val;
139 }
140
141 /* Be sure to use "q" for byte operations (compared to longs), since this
142  * constrains the asm to use e{a,b,c,d}x instead of esi and edi.  32 bit x86
143  * cannot access the lower parts of esi or edi (will get warnings like "no such
144  * register %sil or %dil." */
145 static inline void atomic_andb(volatile uint8_t RACY*number, uint8_t mask)
146 {
147         asm volatile("lock andb %1,%0" : "=m"(*number) : "q"(mask) : "cc");
148 }
149
150 static inline void atomic_orb(volatile uint8_t RACY*number, uint8_t mask)
151 {
152         asm volatile("lock orb %1,%0" : "=m"(*number) : "q"(mask) : "cc");
153 }
154
155 static inline uint32_t spin_locked(spinlock_t *SAFE lock)
156 {
157         // the lock status is the lowest byte of the lock
158         return lock->rlock & 0xff;
159 }
160
161 static inline void __spin_lock(volatile uint32_t *rlock)
162 {
163         asm volatile(
164                         "1:                       "
165                         "       cmpb $0, %0;          "
166                         "       je 2f;                "
167                         "       pause;                "
168                         "       jmp 1b;               "
169                         "2:                       " 
170                         "       movb $1, %%al;        "
171                         "       xchgb %%al, %0;       "
172                         "       cmpb $0, %%al;        "
173                         "       jne 1b;               "
174                 : : "m"(*rlock) : "eax", "cc");
175 }
176
177 static inline void spin_lock(spinlock_t *lock)
178 {
179         __spin_lock(&lock->rlock);
180 #ifdef __CONFIG_SPINLOCK_DEBUG__
181         lock->call_site = (void RACY*CT(1))TC(read_eip());
182         lock->calling_core = core_id();
183 #endif
184 }
185
186 static inline void spin_unlock(spinlock_t *lock)
187 {
188         /* Need to prevent the compiler (and some arches) from reordering older
189          * stores */
190         wmb();
191         lock->rlock = 0;
192 }
193
194 static inline void spinlock_init(spinlock_t *lock)
195 #ifdef __CONFIG_SPINLOCK_DEBUG__
196 WRITES(lock->rlock,lock->call_site,lock->calling_core)
197 #else
198 WRITES(lock->rlock)
199 #endif
200 {
201         lock->rlock = 0;
202 #ifdef __CONFIG_SPINLOCK_DEBUG__
203         lock->call_site = 0;
204         lock->calling_core = 0;
205 #endif
206 }
207
208 #endif /* !ROS_INCLUDE_ATOMIC_H */