Atomics rewrite (XCC)
[akaros.git] / kern / include / 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  * Kernel atomics and locking functions.
6  *
7  * The extern inline declarations are arch-dependent functions.  We do this
8  * so that each arch can either static inline or just have a regular function,
9  * whichever is appropriate. The actual implementation usually will be in
10  * arch/atomic.h (for inlines).
11  *
12  * The static inlines are defined farther down in the file (as always). */
13
14 #ifndef ROS_KERN_ATOMIC_H
15 #define ROS_KERN_ATOMIC_H
16
17 #include <ros/common.h>
18 #include <ros/atomic.h>
19 #include <arch/mmu.h>
20 #include <arch/arch.h>
21 #include <assert.h>
22
23 /* Atomics */
24 extern inline void atomic_init(atomic_t *number, long val);
25 extern inline long atomic_read(atomic_t *number);
26 extern inline void atomic_set(atomic_t *number, long val);
27 extern inline void atomic_add(atomic_t *number, long val);
28 extern inline void atomic_inc(atomic_t *number);
29 extern inline void atomic_dec(atomic_t *number);
30 extern inline long atomic_fetch_and_add(atomic_t *number, long val);
31 extern inline void atomic_and(atomic_t *number, long mask);
32 extern inline void atomic_or(atomic_t *number, long mask);
33 extern inline long atomic_swap(atomic_t *addr, long val);
34 extern inline bool atomic_cas(atomic_t *addr, long exp_val, long new_val);
35 extern inline bool atomic_cas_ptr(void **addr, void *exp_val, void *new_val);
36 extern inline bool atomic_cas_u32(uint32_t *addr, uint32_t exp_val,
37                                   uint32_t new_val);
38 extern inline bool atomic_add_not_zero(atomic_t *number, long val);
39 extern inline bool atomic_sub_and_test(atomic_t *number, long val);
40
41 /* Spin locks */
42 struct spinlock {
43         volatile uint32_t RACY rlock;
44 #ifdef __CONFIG_SPINLOCK_DEBUG__
45         void *call_site;        
46         uint32_t calling_core;
47 #endif
48 };
49 typedef struct spinlock spinlock_t;
50 #define SPINLOCK_INITIALIZER {0}
51
52 extern inline void spinlock_init(spinlock_t *lock);
53 extern inline bool spin_locked(spinlock_t *lock);
54 extern inline void spin_lock(spinlock_t *lock);
55 extern inline void spin_unlock(spinlock_t *lock);
56 extern inline void spinlock_debug(spinlock_t *lock);
57
58 static inline void spin_lock_irqsave(spinlock_t *lock);
59 static inline void spin_unlock_irqsave(spinlock_t *lock);
60 static inline bool spin_lock_irq_enabled(spinlock_t *lock);
61
62 /* Seq locks */
63 /* An example seq lock, built from the counter.  I don't particularly like this,
64  * since it forces you to use a specific locking type.  */
65 typedef struct seq_lock {
66         spinlock_t                      w_lock;
67         seq_ctr_t                       r_ctr;
68 } seqlock_t;
69
70 static inline void __seq_start_write(seq_ctr_t *seq_ctr);
71 static inline void __seq_end_write(seq_ctr_t *seq_ctr);
72 static inline void write_seqlock(seqlock_t *lock);
73 static inline void write_sequnlock(seqlock_t *lock);
74 static inline seq_ctr_t read_seqbegin(seqlock_t *lock);
75 static inline bool read_seqretry(seqlock_t *lock, seq_ctr_t ctr);
76
77 /* Arch-specific implementations / declarations go here */
78 #include <arch/atomic.h>
79
80 #define MAX_SPINS 1000000000
81
82 /* Will spin for a little while, but not deadlock if it never happens */
83 #define spin_on(x)                                                             \
84         for (int i = 0; (x); i++) {                                                \
85                 cpu_relax();                                                           \
86                 if (i == MAX_SPINS) {                                                  \
87                         printk("Probably timed out/failed.\n");                            \
88                         break;                                                             \
89                 }                                                                      \
90         }
91
92 /*********************** Checklist stuff **********************/
93 typedef struct checklist_mask {
94         // only need an uint8_t, but we need the bits[] to be word aligned
95         uint32_t size;
96         volatile uint8_t (COUNT(BYTES_FOR_BITMASK(size)) bits)[MAX_NUM_CPUS];
97 } checklist_mask_t;
98
99 // mask contains an unspecified array, so it needs to be at the bottom
100 struct checklist {
101         spinlock_t lock;
102         checklist_mask_t mask;
103         // eagle-eyed readers may know why this might have been needed. 2009-09-04
104         //volatile uint8_t (COUNT(BYTES_FOR_BITMASK(size)) bits)[];
105 };
106 typedef struct checklist RACY checklist_t;
107
108 #define ZEROS_ARRAY(size) {[0 ... ((size)-1)] 0}
109
110 #define DEFAULT_CHECKLIST_MASK(sz) {(sz), ZEROS_ARRAY(BYTES_FOR_BITMASK(sz))}
111 #define DEFAULT_CHECKLIST(sz) {SPINLOCK_INITIALIZER, DEFAULT_CHECKLIST_MASK(sz)}
112 #define INIT_CHECKLIST(nm, sz)  \
113         checklist_t nm = DEFAULT_CHECKLIST(sz);
114 #define INIT_CHECKLIST_MASK(nm, sz)     \
115         checklist_mask_t nm = DEFAULT_CHECKLIST_MASK(sz);
116
117 int commit_checklist_wait(checklist_t* list, checklist_mask_t* mask);
118 int commit_checklist_nowait(checklist_t* list, checklist_mask_t* mask);
119 int waiton_checklist(checklist_t* list);
120 int release_checklist(checklist_t* list);
121 int checklist_is_locked(checklist_t* list);
122 int checklist_is_clear(checklist_t* list);
123 void reset_checklist(checklist_t* list);
124 void down_checklist(checklist_t* list);
125 // TODO - do we want to adjust the size?  (YES, don't want to check it all)
126 // TODO - do we want to be able to call waiton without having called commit?
127 //      - in the case of protected checklists
128 // TODO - want a destroy checklist (when we have kmalloc, or whatever)
129 // TODO - some sort of dynamic allocation of them in the future
130 // TODO - think about deadlock issues with one core spinning on a lock for
131 // something that it is the hold out for...
132 //      - probably should have interrupts enabled, and never grab these locks
133 //      from interrupt context (and not use irq_save)
134 /**************************************************************/
135
136 /* Barrier: currently made for everyone barriering.  Change to use checklist */
137 struct barrier {
138         spinlock_t lock;
139         uint32_t init_count;
140         uint32_t current_count;
141         volatile uint8_t ready;
142 };
143
144 typedef struct barrier RACY barrier_t;
145
146 void init_barrier(barrier_t*COUNT(1) barrier, uint32_t count);
147 void reset_barrier(barrier_t* barrier);
148 void waiton_barrier(barrier_t* barrier);
149
150 /* Spinlock bit flags */
151 #define SPINLOCK_IRQ_EN                 0x80000000
152
153 // If ints are enabled, disable them and note it in the top bit of the lock
154 // There is an assumption about releasing locks in order here...
155 static inline void spin_lock_irqsave(spinlock_t *SAFE lock)
156 {
157         uint32_t irq_en;
158         irq_en = irq_is_enabled();
159         disable_irq();
160         spin_lock(lock);
161         if (irq_en)
162                 lock->rlock |= SPINLOCK_IRQ_EN;
163 }
164
165 // if the high bit of the lock is set, then re-enable interrupts
166 // (note from asw: you're lucky this works, you little-endian jerks)
167 static inline void spin_unlock_irqsave(spinlock_t *SAFE lock)
168 {
169         if (spin_lock_irq_enabled(lock)) {
170                 spin_unlock(lock);
171                 enable_irq();
172         } else
173                 spin_unlock(lock);
174 }
175
176 /* Returns whether or not unlocking this lock should enable interrupts or not.
177  * Is meaningless on locks that weren't locked with irqsave. */
178 static inline bool spin_lock_irq_enabled(spinlock_t *SAFE lock)
179 {
180         return lock->rlock & SPINLOCK_IRQ_EN;
181 }
182
183 /* Note, the seq_ctr is not a full seq lock - just the counter guts.  Write
184  * access can be controlled by another lock (like the proc-lock).  start_ and
185  * end_write are the writer's responsibility to signal the readers of a
186  * concurrent write. */
187 static inline void __seq_start_write(seq_ctr_t *seq_ctr)
188 {
189 #ifdef _CONFIG_SEQLOCK_DEBUG_
190         assert(*seq_ctr % 2 == 0);
191 #endif
192         (*seq_ctr)++;
193         /* We're the only writer, so we need to prevent the compiler (and some
194          * arches) from reordering writes before this point. */
195         wmb();
196 }
197
198 static inline void __seq_end_write(seq_ctr_t *seq_ctr)
199 {
200 #ifdef _CONFIG_SEQLOCK_DEBUG_
201         assert(*seq_ctr % 2 == 1);
202 #endif
203         /* Need to prevent the compiler (and some arches) from reordering older
204          * stores */
205         wmb();
206         (*seq_ctr)++;
207 }
208
209 /* Untested reference implementation of a seq lock.  As mentioned above, we
210  * might need a variety of these (for instance, this doesn't do an irqsave).  Or
211  * there may be other invariants that we need the lock to protect. */
212 static inline void write_seqlock(seqlock_t *lock)
213 {
214         spin_lock(&lock->w_lock);
215         __seq_start_write(&lock->r_ctr);
216 }
217
218 static inline void write_sequnlock(seqlock_t *lock)
219 {
220         __seq_end_write(&lock->r_ctr);
221         spin_unlock(&lock->w_lock);
222 }
223
224 static inline seq_ctr_t read_seqbegin(seqlock_t *lock)
225 {
226         return lock->r_ctr;
227 }
228
229 static inline bool read_seqretry(seqlock_t *lock, seq_ctr_t ctr)
230 {
231         return seqctr_retry(lock->r_ctr, ctr);
232 }
233
234 #endif /* ROS_KERN_ATOMIC_H */