Spinlock irqsave usage checks
[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         uintptr_t call_site;
46         uint32_t calling_core;
47         bool irq_okay;
48 #endif
49 };
50 typedef struct spinlock spinlock_t;
51 #define SPINLOCK_INITIALIZER {0}
52
53 #ifdef __CONFIG_SPINLOCK_DEBUG__
54 #define SPINLOCK_INITIALIZER_IRQSAVE {0, .irq_okay = TRUE}
55 #else
56 #define SPINLOCK_INITIALIZER_IRQSAVE SPINLOCK_INITIALIZER
57 #endif
58
59 /* Arch dependent helpers/funcs: */
60 extern inline void __spinlock_init(spinlock_t *lock);
61 extern inline bool spin_locked(spinlock_t *lock);
62 extern inline void __spin_lock(spinlock_t *lock);
63 extern inline void __spin_unlock(spinlock_t *lock);
64 extern inline void spinlock_debug(spinlock_t *lock);
65
66 /* Arch indep, in k/s/atomic.c */
67 void spin_lock(spinlock_t *lock);
68 void spin_unlock(spinlock_t *lock);
69
70 /* Inlines, defined below */
71 static inline void spinlock_init(spinlock_t *lock);
72 static inline void spinlock_init_irqsave(spinlock_t *lock);
73 static inline void spin_lock_irqsave(spinlock_t *lock);
74 static inline void spin_unlock_irqsave(spinlock_t *lock);
75 static inline bool spin_lock_irq_enabled(spinlock_t *lock);
76
77 /* Hash locks (array of spinlocks).  Most all users will want the default one,
78  * so point your pointer to one of them, though you could always kmalloc a
79  * bigger one.  In the future, they might be growable, etc, which init code may
80  * care about. */
81 struct hashlock {
82         unsigned int            nr_entries;
83         struct spinlock         locks[];
84 };
85 #define HASHLOCK_DEFAULT_SZ 53          /* nice prime, might be a bit large */
86 struct small_hashlock {
87         unsigned int            nr_entries;
88         struct spinlock         locks[HASHLOCK_DEFAULT_SZ];
89 };
90
91 void hashlock_init(struct hashlock *hl, unsigned int nr_entries);
92 void hashlock_init_irqsave(struct hashlock *hl, unsigned int nr_entries);
93 void hash_lock(struct hashlock *hl, long key);
94 void hash_unlock(struct hashlock *hl, long key);
95 void hash_lock_irqsave(struct hashlock *hl, long key);
96 void hash_unlock_irqsave(struct hashlock *hl, long key);
97
98 /* Seq locks */
99 /* An example seq lock, built from the counter.  I don't particularly like this,
100  * since it forces you to use a specific locking type.  */
101 typedef struct seq_lock {
102         spinlock_t                      w_lock;
103         seq_ctr_t                       r_ctr;
104 } seqlock_t;
105
106 static inline void __seq_start_write(seq_ctr_t *seq_ctr);
107 static inline void __seq_end_write(seq_ctr_t *seq_ctr);
108 static inline void write_seqlock(seqlock_t *lock);
109 static inline void write_sequnlock(seqlock_t *lock);
110 static inline seq_ctr_t read_seqbegin(seqlock_t *lock);
111 static inline bool read_seqretry(seqlock_t *lock, seq_ctr_t ctr);
112
113 /* Post work and poke synchronization.  This is a wait-free way to make sure
114  * some code is run, usually by the calling core, but potentially by any core.
115  * Under contention, everyone just posts work, and one core will carry out the
116  * work.  Callers post work (the meaning of which is particular to their
117  * subsystem), then call this function.  The function is not run concurrently
118  * with itself.
119  *
120  * In the future, this may send RKMs to LL cores to ensure the work is done
121  * somewhere, but not necessarily on the calling core.  Will reserve 'flags'
122  * for that. */
123 struct poke_tracker {
124         atomic_t                        need_to_run;
125         atomic_t                        run_in_progress;
126         void                            (*func)(void *);
127 };
128 void poke(struct poke_tracker *tracker, void *arg);
129
130 /* Arch-specific implementations / declarations go here */
131 #include <arch/atomic.h>
132
133 #define MAX_SPINS 1000000000
134
135 /* Will spin for a little while, but not deadlock if it never happens */
136 #define spin_on(x)                                                             \
137         for (int i = 0; (x); i++) {                                                \
138                 cpu_relax();                                                           \
139                 if (i == MAX_SPINS) {                                                  \
140                         printk("Probably timed out/failed.\n");                            \
141                         break;                                                             \
142                 }                                                                      \
143         }
144
145 /*********************** Checklist stuff **********************/
146 typedef struct checklist_mask {
147         // only need an uint8_t, but we need the bits[] to be word aligned
148         uint32_t size;
149         volatile uint8_t (COUNT(BYTES_FOR_BITMASK(size)) bits)[MAX_NUM_CPUS];
150 } checklist_mask_t;
151
152 // mask contains an unspecified array, so it needs to be at the bottom
153 struct checklist {
154         spinlock_t lock;
155         checklist_mask_t mask;
156         // eagle-eyed readers may know why this might have been needed. 2009-09-04
157         //volatile uint8_t (COUNT(BYTES_FOR_BITMASK(size)) bits)[];
158 };
159 typedef struct checklist RACY checklist_t;
160
161 #define ZEROS_ARRAY(size) {[0 ... ((size)-1)] 0}
162
163 #define DEFAULT_CHECKLIST_MASK(sz) {(sz), ZEROS_ARRAY(BYTES_FOR_BITMASK(sz))}
164 #define DEFAULT_CHECKLIST(sz) {SPINLOCK_INITIALIZER_IRQSAVE,                   \
165                                DEFAULT_CHECKLIST_MASK(sz)}
166 #define INIT_CHECKLIST(nm, sz)  \
167         checklist_t nm = DEFAULT_CHECKLIST(sz);
168 #define INIT_CHECKLIST_MASK(nm, sz)     \
169         checklist_mask_t nm = DEFAULT_CHECKLIST_MASK(sz);
170
171 int commit_checklist_wait(checklist_t* list, checklist_mask_t* mask);
172 int commit_checklist_nowait(checklist_t* list, checklist_mask_t* mask);
173 int waiton_checklist(checklist_t* list);
174 int release_checklist(checklist_t* list);
175 int checklist_is_locked(checklist_t* list);
176 int checklist_is_clear(checklist_t* list);
177 void reset_checklist(checklist_t* list);
178 void down_checklist(checklist_t* list);
179 // TODO - do we want to adjust the size?  (YES, don't want to check it all)
180 // TODO - do we want to be able to call waiton without having called commit?
181 //      - in the case of protected checklists
182 // TODO - want a destroy checklist (when we have kmalloc, or whatever)
183 // TODO - some sort of dynamic allocation of them in the future
184 // TODO - think about deadlock issues with one core spinning on a lock for
185 // something that it is the hold out for...
186 //      - probably should have interrupts enabled, and never grab these locks
187 //      from interrupt context (and not use irq_save)
188 /**************************************************************/
189
190 /* Barrier: currently made for everyone barriering.  Change to use checklist */
191 struct barrier {
192         spinlock_t lock;
193         uint32_t init_count;
194         uint32_t current_count;
195         volatile uint8_t ready;
196 };
197
198 typedef struct barrier RACY barrier_t;
199
200 void init_barrier(barrier_t*COUNT(1) barrier, uint32_t count);
201 void reset_barrier(barrier_t* barrier);
202 void waiton_barrier(barrier_t* barrier);
203
204 /* Spinlock bit flags */
205 #define SPINLOCK_IRQ_EN                 0x80000000
206
207 static inline void spinlock_init(spinlock_t *lock)
208 {
209         __spinlock_init(lock);
210 #ifdef __CONFIG_SPINLOCK_DEBUG__
211         lock->call_site = 0;
212         lock->calling_core = 0;
213         lock->irq_okay = FALSE;
214 #endif
215 }
216
217 static inline void spinlock_init_irqsave(spinlock_t *lock)
218 {
219         __spinlock_init(lock);
220 #ifdef __CONFIG_SPINLOCK_DEBUG__
221         lock->call_site = 0;
222         lock->calling_core = 0;
223         lock->irq_okay = TRUE;
224 #endif
225 }
226
227 // If ints are enabled, disable them and note it in the top bit of the lock
228 // There is an assumption about releasing locks in order here...
229 static inline void spin_lock_irqsave(spinlock_t *SAFE lock)
230 {
231         uint32_t irq_en;
232         irq_en = irq_is_enabled();
233         disable_irq();
234         spin_lock(lock);
235         if (irq_en)
236                 lock->rlock |= SPINLOCK_IRQ_EN;
237 }
238
239 // if the high bit of the lock is set, then re-enable interrupts
240 // (note from asw: you're lucky this works, you little-endian jerks)
241 static inline void spin_unlock_irqsave(spinlock_t *SAFE lock)
242 {
243         if (spin_lock_irq_enabled(lock)) {
244                 spin_unlock(lock);
245                 enable_irq();
246         } else
247                 spin_unlock(lock);
248 }
249
250 /* Returns whether or not unlocking this lock should enable interrupts or not.
251  * Is meaningless on locks that weren't locked with irqsave. */
252 static inline bool spin_lock_irq_enabled(spinlock_t *SAFE lock)
253 {
254         return lock->rlock & SPINLOCK_IRQ_EN;
255 }
256
257 /* Note, the seq_ctr is not a full seq lock - just the counter guts.  Write
258  * access can be controlled by another lock (like the proc-lock).  start_ and
259  * end_write are the writer's responsibility to signal the readers of a
260  * concurrent write. */
261 static inline void __seq_start_write(seq_ctr_t *seq_ctr)
262 {
263 #ifdef _CONFIG_SEQLOCK_DEBUG_
264         assert(*seq_ctr % 2 == 0);
265 #endif
266         (*seq_ctr)++;
267         /* We're the only writer, so we need to prevent the compiler (and some
268          * arches) from reordering writes before this point. */
269         wmb();
270 }
271
272 static inline void __seq_end_write(seq_ctr_t *seq_ctr)
273 {
274 #ifdef _CONFIG_SEQLOCK_DEBUG_
275         assert(*seq_ctr % 2 == 1);
276 #endif
277         /* Need to prevent the compiler (and some arches) from reordering older
278          * stores */
279         wmb();
280         (*seq_ctr)++;
281 }
282
283 /* Untested reference implementation of a seq lock.  As mentioned above, we
284  * might need a variety of these (for instance, this doesn't do an irqsave).  Or
285  * there may be other invariants that we need the lock to protect. */
286 static inline void write_seqlock(seqlock_t *lock)
287 {
288         spin_lock(&lock->w_lock);
289         __seq_start_write(&lock->r_ctr);
290 }
291
292 static inline void write_sequnlock(seqlock_t *lock)
293 {
294         __seq_end_write(&lock->r_ctr);
295         spin_unlock(&lock->w_lock);
296 }
297
298 static inline seq_ctr_t read_seqbegin(seqlock_t *lock)
299 {
300         seq_ctr_t retval = lock->r_ctr;
301         rmb();  /* don't want future reads to come before our ctr read */
302         return retval;
303 }
304
305 static inline bool read_seqretry(seqlock_t *lock, seq_ctr_t ctr)
306 {
307         return seqctr_retry(lock->r_ctr, ctr);
308 }
309
310 #endif /* ROS_KERN_ATOMIC_H */