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