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