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