Another round at reorganization
[akaros.git] / include / atomic.h
1 #ifndef ROS_KERN_ATOMIC_H
2 #define ROS_KERN_ATOMIC_H
3
4 #include <arch/types.h>
5 #include <arch/mmu.h>
6 #include <arch/x86.h>
7
8 #define mb() {rmb(); wmb();}
9 #define rmb() ({ asm volatile("lfence"); })
10 #define wmb() 
11
12 /* //linux style atomic ops
13 typedef struct {uint32_t real_num;} atomic_t;
14 #define atomic_read(atom) ((atom)->real_num)
15 #define atomic_set(atom, val) (((atom)->real_num) = (val))
16 #define atomic_init(i) {(i)}
17 //and the atomic incs, etc take an atomic_t ptr, deref inside
18 */
19
20 static inline void spin_lock(volatile uint32_t* lock);
21 static inline void spin_unlock(volatile uint32_t* lock);
22 static inline void spin_lock_irqsave(volatile uint32_t* lock);
23 static inline void spin_unlock_irqsave(volatile uint32_t* lock);
24 static inline void atomic_inc(volatile uint32_t* number);
25 static inline void atomic_dec(volatile uint32_t* number);
26 static inline void atomic_andb(volatile uint8_t* number, uint8_t mask);
27
28 /*********************** Checklist stuff **********************/
29 typedef struct checklist_mask {
30         // only need an uint8_t, but we need the bits[] to be word aligned
31         uint32_t size;
32         volatile uint8_t (COUNT(BYTES_FOR_BITMASK(size)) bits)[];
33 } checklist_mask_t;
34
35 // mask contains an unspecified array, so it needs to be at the bottom
36 typedef struct checklist {
37         volatile uint32_t lock;
38         checklist_mask_t mask;
39 } checklist_t;
40
41 #define ZEROS_ARRAY(size) {[0 ... ((size)-1)] 0}
42
43 #define DEFAULT_CHECKLIST_MASK(sz) {(sz), ZEROS_ARRAY(BYTES_FOR_BITMASK(sz))}
44 #define DEFAULT_CHECKLIST(sz) {0, DEFAULT_CHECKLIST_MASK(sz)}
45 #define INIT_CHECKLIST(nm, sz)  \
46         checklist_t nm = DEFAULT_CHECKLIST(sz);
47 #define INIT_CHECKLIST_MASK(nm, sz)     \
48         checklist_mask_t nm = DEFAULT_CHECKLIST_MASK(sz);
49
50 int commit_checklist_wait(checklist_t* list, checklist_mask_t* mask);
51 int commit_checklist_nowait(checklist_t* list, checklist_mask_t* mask);
52 int waiton_checklist(checklist_t* list);
53 int release_checklist(checklist_t* list);
54 int checklist_is_locked(checklist_t* list);
55 int checklist_is_clear(checklist_t* list);
56 void reset_checklist(checklist_t* list);
57 void down_checklist(checklist_t* list);
58 // TODO - do we want to adjust the size?  (YES, don't want to check it all)
59 // TODO - do we want to be able to call waiton without having called commit?
60 //      - in the case of protected checklists
61 // TODO - want a destroy checklist (when we have kmalloc, or whatever)
62 // TODO - some sort of dynamic allocation of them in the future
63 // TODO - think about deadlock issues with one core spinning on a lock for
64 // something that it is the hold out for...
65 //      - probably should have interrupts enabled, and never grab these locks
66 //      from interrupt context (and not use irq_save)
67 /**************************************************************/
68
69 /* Barrier: currently made for everyone barriering.  Change to use checklist */
70 typedef struct barrier {
71         volatile uint32_t lock;
72         uint32_t init_count;
73         uint32_t current_count;
74     volatile uint8_t ready;
75 } barrier_t;
76
77 void init_barrier(barrier_t* barrier, uint32_t count);
78 void reset_barrier(barrier_t* barrier);
79 void waiton_barrier(barrier_t* barrier);
80
81 /* Inlined functions declared above */
82 static inline void spin_lock(volatile uint32_t* lock)
83 {
84         asm volatile(
85                         "1:                       "
86                         "       cmpb $0, %0;          "
87                         "       je 2f;                "
88                         "       pause;                "
89                         "       jmp 1b;               "
90                         "2:                       " 
91                         "       movb $1, %%al;        "
92                         "       xchgb %%al, %0;       "
93                         "       cmpb $0, %%al;        "
94                         "       jne 1b;               "
95                 : : "m"(*lock) : "eax", "cc");
96 }
97
98 static inline void spin_unlock(volatile uint32_t* lock)
99 {
100         *lock = 0;
101 }
102
103 // If ints are enabled, disable them and note it in the top bit of the lock
104 // There is an assumption about releasing locks in order here...
105 static inline void spin_lock_irqsave(volatile uint32_t* lock)
106 {
107         uint32_t eflags;
108         eflags = read_eflags();
109         disable_irq();
110         spin_lock(lock);
111         if (eflags & FL_IF)
112                 *lock |= 0x80000000;
113 }
114
115 // if the top bit of the lock is set, then re-enable interrupts
116 static inline void spin_unlock_irqsave(volatile uint32_t* lock)
117 {
118         if (*lock & 0x80000000) {
119                 *lock = 0;
120                 enable_irq();
121         } else
122                 *lock = 0;
123 }
124
125 // need to do this with pointers and deref.  %0 needs to be the memory address
126 static inline void atomic_inc(volatile uint32_t* number)
127 {
128         asm volatile("lock incl %0" : "=m"(*number) : : "cc");
129 }
130
131 static inline void atomic_dec(volatile uint32_t* number)
132 {
133         asm volatile("lock decl %0" : "=m"(*number) : : "cc");
134 }
135
136 static inline void atomic_andb(volatile uint8_t* number, uint8_t mask)
137 {
138         asm volatile("lock andb %1,%0" : "=m"(*number) : "r"(mask) : "cc");
139 }
140 #endif /* !ROS_KERN_ATOMIC_H */