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