__startcore now takes the vcoreid as a parameter
[akaros.git] / kern / src / atomic.c
1 #ifdef __SHARC__
2 #pragma nosharc
3 #endif
4
5 #include <arch/arch.h>
6
7 #include <bitmask.h>
8 #include <atomic.h>
9 #include <error.h>
10 #include <string.h>
11 #include <assert.h>
12 #include <hashtable.h>
13
14 /* Inits a hashlock. */
15 void hashlock_init(struct hashlock *hl, unsigned int nr_entries)
16 {
17         hl->nr_entries = nr_entries;
18         /* this is the right way to do it, though memset is faster.  If we ever
19          * find that this is taking a lot of time, we can change it. */
20         for (int i = 0; i < hl->nr_entries; i++) {
21                 spinlock_init(&hl->locks[i]);
22         }
23 }
24
25 /* Helper, gets the specific spinlock for a hl/key combo. */
26 static spinlock_t *get_spinlock(struct hashlock *hl, long key)
27 {
28         /* using the hashtable's generic hash function */
29         return &hl->locks[__generic_hash((void*)key) % hl->nr_entries];
30 }
31
32 void hash_lock(struct hashlock *hl, long key)
33 {
34         spin_lock(get_spinlock(hl, key));
35 }
36
37 void hash_unlock(struct hashlock *hl, long key)
38 {
39         spin_unlock(get_spinlock(hl, key));
40 }
41
42 void hash_lock_irqsave(struct hashlock *hl, long key)
43 {
44         spin_lock_irqsave(get_spinlock(hl, key));
45 }
46
47 void hash_unlock_irqsave(struct hashlock *hl, long key)
48 {
49         spin_unlock_irqsave(get_spinlock(hl, key));
50 }
51
52 // Must be called in a pair with waiton_checklist
53 int commit_checklist_wait(checklist_t* list, checklist_mask_t* mask)
54 {
55         assert(list->mask.size == mask->size);
56         // abort if the list is locked.  this will protect us from trying to commit
57         // and thus spin on a checklist that we are already waiting on.  it is
58         // still possible to not get the lock, but the holder is on another core.
59         // Or, bail out if we can see the list is already in use.  This check is
60         // just an optimization before we try to use the list for real.
61         if ((checklist_is_locked(list)) || !checklist_is_clear(list))
62                 return -EBUSY;
63
64         // possession of this lock means you can wait on it and set it
65         spin_lock_irqsave(&list->lock);
66         // wait til the list is available.  could have some adaptive thing here
67         // where it fails after X tries (like 500), gives up the lock, and returns
68         // an error code
69         while (!checklist_is_clear(list))
70                 cpu_relax();
71
72         // list is ours and clear, set it to the settings of our list
73         COPY_BITMASK(list->mask.bits, mask->bits, mask->size); 
74         return 0;
75 }
76
77 int commit_checklist_nowait(checklist_t* list, checklist_mask_t* mask)
78 {
79         int e = 0;
80         if ((e = commit_checklist_wait(list, mask)))
81                 return e;
82         // give up the lock, since we won't wait for completion
83         spin_unlock_irqsave(&list->lock);
84         return e;
85 }
86 // The deal with the lock:
87 // what if two different actors are waiting on the list, but for different reasons?
88 // part of the problem is we are doing both set and check via the same path
89 //
90 // aside: we made this a lot more difficult than the usual barriers or even 
91 // the RCU grace-period checkers, since we have to worry about this construct
92 // being used by others before we are done with it.
93 //
94 // how about this: if we want to wait on this later, we just don't release the
95 // lock.  if we release it, then we don't care who comes in and grabs and starts
96 // checking the list.  
97 //      - regardless, there are going to be issues with people looking for a free 
98 //      item.  even if they grab the lock, they may end up waiting a while and 
99 //      wantint to bail (like test for a while, give up, move on, etc).  
100 //      - still limited in that only the setter can check, and only one person
101 //      can spinwait / check for completion.  if someone else tries to wait (wanting
102 //      completion), they may miss it if someone else comes in and grabs the lock
103 //      to use it for a new checklist
104 //              - if we had the ability to sleep and get woken up, we could have a 
105 //              queue.  actually, we could do a queue anyway, but they all spin
106 //              and it's the bosses responsibility to *wake* them
107
108 // Must be called after commit_checklist
109 // Assumed we held the lock if we ever call this
110 int waiton_checklist(checklist_t* list)
111 {
112         extern atomic_t outstanding_calls;
113         // can consider breakout out early, like above, and erroring out
114         while (!checklist_is_clear(list))
115                 cpu_relax();
116         spin_unlock_irqsave(&list->lock);
117         // global counter of wrappers either waited on or being contended for.
118         atomic_dec(&outstanding_calls);
119         return 0;
120 }
121
122 // like waiton, but don't bother waiting either
123 int release_checklist(checklist_t* list)
124 {
125         spin_unlock_irqsave(&list->lock);
126         return 0;
127 }
128
129 // peaks in and sees if the list is locked with it's spinlock
130 int checklist_is_locked(checklist_t* list)
131 {
132         return spin_locked(&list->lock);
133 }
134
135 // no synch guarantees - just looks at the list
136 int checklist_is_clear(checklist_t* list)
137 {
138         return BITMASK_IS_CLEAR(list->mask.bits, list->mask.size);
139 }
140
141 // no synch guarantees - just resets the list to empty
142 void reset_checklist(checklist_t* list)
143 {
144         CLR_BITMASK(list->mask.bits, list->mask.size);
145 }
146
147 // CPU mask specific - this is how cores report in
148 void down_checklist(checklist_t* list)
149 {
150         CLR_BITMASK_BIT_ATOMIC(list->mask.bits, core_id());
151 }
152
153 /* Barriers */
154 void init_barrier(barrier_t* barrier, uint32_t count)
155 {
156         spinlock_init(&barrier->lock);
157         barrier->init_count = count;
158         barrier->current_count = count;
159         barrier->ready = 0;
160 }
161
162 void reset_barrier(barrier_t* barrier)
163 {
164         barrier->current_count = barrier->init_count;
165 }
166
167 // primitive barrier function.  all cores call this.
168 void waiton_barrier(barrier_t* barrier)
169 {
170         uint8_t local_ready = barrier->ready;
171
172         spin_lock_irqsave(&barrier->lock);
173         barrier->current_count--;
174         if (barrier->current_count) {
175                 spin_unlock_irqsave(&barrier->lock);
176                 while (barrier->ready == local_ready)
177                         cpu_relax();
178         } else {
179                 spin_unlock_irqsave(&barrier->lock);
180                 reset_barrier(barrier);
181                 wmb();
182                 barrier->ready++;
183         }
184 }