Compare and Swap
[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 <arch/x86.h>
6 #include <arch/arch.h>
7 #include <arch/membar.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_inc(atomic_t *number);
24 static inline void atomic_dec(atomic_t *number);
25 static inline uint32_t atomic_swap(uint32_t *addr, uint32_t val);
26 static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
27                                     uint32_t new_val);
28 static inline void atomic_andb(volatile uint8_t RACY* number, uint8_t mask);
29 static inline void atomic_orb(volatile uint8_t RACY* number, uint8_t mask);
30 static inline uint32_t spin_locked(spinlock_t *SAFE lock);
31 static inline void __spin_lock(volatile uint32_t SRACY*CT(1) rlock);
32 static inline void spin_lock(spinlock_t *lock);
33 static inline void spin_unlock(spinlock_t *lock);
34 static inline void spinlock_init(spinlock_t *lock);
35 void spinlock_debug(spinlock_t *lock);
36
37 /* Inlined functions declared above */
38 static inline void atomic_init(atomic_t *number, int32_t val)
39 {
40         asm volatile("movl %1,%0" : "=m"(*number) : "r"(val));
41 }
42
43 static inline int32_t atomic_read(atomic_t *number)
44 {
45         int32_t val;
46         asm volatile("movl %1,%0" : "=r"(val) : "m"(*number));
47         return val;
48 }
49
50 static inline void atomic_set(atomic_t *number, int32_t val)
51 {
52         asm volatile("movl %1,%0" : "=m"(*number) : "r"(val));
53 }
54
55 // need to do this with pointers and deref.  %0 needs to be the memory address
56 static inline void atomic_inc(atomic_t *number)
57 {
58         asm volatile("lock incl %0" : "=m"(*number) : : "cc");
59 }
60
61 static inline void atomic_dec(atomic_t *number)
62 {
63         // for instance, this doesn't work:
64         //asm volatile("lock decl (%0)" : "=r"(number) : : "cc");
65         asm volatile("lock decl %0" : "=m"(*number) : : "cc");
66 }
67
68 static inline uint32_t atomic_swap(uint32_t *addr, uint32_t val)
69 {
70         // this would work, but its code is bigger, and it's not like the others
71         //asm volatile("xchgl %0,(%2)" : "=r"(val) : "0"(val), "r"(addr) : "memory");
72         asm volatile("xchgl %0,%1" : "=r"(val), "=m"(*addr) : "0"(val), "m"(*addr));
73         return val;
74 }
75
76 /* reusing exp_val for the bool return */
77 static inline bool atomic_comp_swap(uint32_t *addr, uint32_t exp_val,
78                                     uint32_t new_val)
79 {
80         asm volatile("lock cmpxchgl %4,%1; sete %%al"
81                      : "=a"(exp_val), "=m"(*addr)
82                      : "m"(*addr), "a"(exp_val), "r"(new_val)
83                      : "cc");
84         return exp_val;
85 }
86
87 static inline void atomic_andb(volatile uint8_t RACY*number, uint8_t mask)
88 {
89         asm volatile("lock andb %1,%0" : "=m"(*number) : "r"(mask) : "cc");
90 }
91
92 static inline void atomic_orb(volatile uint8_t RACY*number, uint8_t mask)
93 {
94         asm volatile("lock orb %1,%0" : "=m"(*number) : "r"(mask) : "cc");
95 }
96
97 static inline uint32_t spin_locked(spinlock_t *SAFE lock)
98 {
99         // the lock status is the lowest byte of the lock
100         return lock->rlock & 0xff;
101 }
102
103 static inline void __spin_lock(volatile uint32_t *rlock)
104 {
105         asm volatile(
106                         "1:                       "
107                         "       cmpb $0, %0;          "
108                         "       je 2f;                "
109                         "       pause;                "
110                         "       jmp 1b;               "
111                         "2:                       " 
112                         "       movb $1, %%al;        "
113                         "       xchgb %%al, %0;       "
114                         "       cmpb $0, %%al;        "
115                         "       jne 1b;               "
116                 : : "m"(*rlock) : "eax", "cc");
117 }
118
119 static inline void spin_lock(spinlock_t *lock)
120 {
121         __spin_lock(&lock->rlock);
122 #ifdef __CONFIG_SPINLOCK_DEBUG__
123         lock->call_site = (void RACY*CT(1))TC(read_eip());
124         lock->calling_core = core_id();
125 #endif
126 }
127
128 static inline void spin_unlock(spinlock_t *lock)
129 {
130         lock->rlock = 0;
131 }
132
133 static inline void spinlock_init(spinlock_t *lock)
134 #ifdef __CONFIG_SPINLOCK_DEBUG__
135 WRITES(lock->rlock,lock->call_site,lock->calling_core)
136 #else
137 WRITES(lock->rlock)
138 #endif
139 {
140         lock->rlock = 0;
141 #ifdef __CONFIG_SPINLOCK_DEBUG__
142         lock->call_site = 0;
143         lock->calling_core = 0;
144 #endif
145 }
146
147 #endif /* !ROS_INCLUDE_ATOMIC_H */