d96b190720a73f57c02e5cf184bee87f07fccb44
[akaros.git] / kern / atomic.c
1 #include <inc/string.h>
2 #include <inc/assert.h>
3
4 #include <kern/atomic.h>
5 #include <kern/apic.h>
6
7 // Must be called in a pair with waiton_checklist
8 int commit_checklist_wait(checklist_t* list, checklist_mask_t* mask)
9 {
10         assert(list->mask.size == mask->size);
11         // possession of this lock means you can wait on it and set it
12         spin_lock_irqsave(&list->lock);
13         // wait til the list is available.  could have some adaptive thing here
14         // where it fails after X tries (like 500), gives up the lock, and returns
15         // an error code
16         while (!(BITMASK_IS_CLEAR(list->mask.bits, list->mask.size)))
17                 cpu_relax();
18
19         // list is ours and clear, set it to the settings of our list
20         COPY_BITMASK(list->mask.bits, mask->bits, mask->size); 
21         return 0;
22 }
23
24 int commit_checklist_nowait(checklist_t* list, checklist_mask_t* mask)
25 {
26         int e = 0;
27         if (e = commit_checklist_wait(list, mask))
28                 return e;
29         // give up the lock, since we won't wait for completion
30         spin_unlock_irqsave(&list->lock);
31         return e;
32 }
33 // The deal with the lock:
34 // what if two different actors are waiting on the list, but for different reasons?
35 // part of the problem is we are doing both set and check via the same path
36 //
37 // aside: we made this a lot more difficult than the usual barriers or even 
38 // the RCU grace-period checkers, since we have to worry about this construct
39 // being used by others before we are done with it.
40 //
41 // how about this: if we want to wait on this later, we just don't release the
42 // lock.  if we release it, then we don't care who comes in and grabs and starts
43 // checking the list.  
44 //      - regardless, there are going to be issues with people looking for a free 
45 //      item.  even if they grab the lock, they may end up waiting a while and 
46 //      wantint to bail (like test for a while, give up, move on, etc).  
47 //      - still limited in that only the setter can check, and only one person
48 //      can spinwait / check for completion.  if someone else tries to wait (wanting
49 //      completion), they may miss it if someone else comes in and grabs the lock
50 //      to use it for a new checklist
51 //              - if we had the ability to sleep and get woken up, we could have a 
52 //              queue.  actually, we could do a queue anyway, but they all spin
53 //              and it's the bosses responsibility to *wake* them
54
55 // Must be called after commit_checklist
56 // Assumed we held the lock if we ever call this
57 int waiton_checklist(checklist_t* list)
58 {
59         // can consider breakout out early, like above, and erroring out
60         while (!(BITMASK_IS_CLEAR(list->mask.bits, list->mask.size)))
61                 cpu_relax();
62         spin_unlock_irqsave(&list->lock);
63         return 0;
64 }
65
66 // CPU mask specific - this is how cores report in
67 void down_checklist(checklist_t* list)
68 {
69         CLR_BITMASK_BIT_ATOMIC(list->mask.bits, lapic_get_id());
70 }
71
72 // byte per cpu, as mentioned below
73 void init_barrier(barrier_t* barrier, uint32_t count)
74 {
75         barrier->lock = 0;
76         barrier->init_count = count;
77         barrier->current_count = count;
78         barrier->ready = 0;
79 }
80
81 void reset_barrier(barrier_t* barrier)
82 {
83         barrier->current_count = barrier->init_count;
84 }
85
86 // primitive barrier function.  all cores call this.
87 void waiton_barrier(barrier_t* barrier)
88 {
89         uint8_t local_ready = barrier->ready;
90
91         spin_lock_irqsave(&barrier->lock);
92         barrier->current_count--;
93         if (barrier->current_count) {
94                 spin_unlock_irqsave(&barrier->lock);
95                 while (barrier->ready == local_ready)
96                         cpu_relax();
97         } else {
98                 spin_unlock_irqsave(&barrier->lock);
99                 reset_barrier(barrier);
100                 // if we need to wmb(), it'll be here
101                 barrier->ready++;
102         }
103 }