smp_call wait / backend work
[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 (!checklist_is_clear(list))
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 (!checklist_is_clear(list))
61                 cpu_relax();
62         spin_unlock_irqsave(&list->lock);
63         return 0;
64 }
65
66 // peaks in and sees if the list is locked with it's spinlock
67 int checklist_is_locked(checklist_t* list)
68 {
69         // remember the lock status is the lowest byte of the lock
70         return list->lock & 0xff;
71 }
72
73 // no synch guarantees - just looks at the list
74 int checklist_is_clear(checklist_t* list)
75 {
76         return BITMASK_IS_CLEAR(list->mask.bits, list->mask.size);
77 }
78
79 // no synch guarantees - just resets the list to empty
80 void reset_checklist(checklist_t* list)
81 {
82         CLR_BITMASK(list->mask.bits, list->mask.size);
83 }
84
85 // CPU mask specific - this is how cores report in
86 void down_checklist(checklist_t* list)
87 {
88         CLR_BITMASK_BIT_ATOMIC(list->mask.bits, lapic_get_id());
89 }
90
91 /* Barriers */
92 void init_barrier(barrier_t* barrier, uint32_t count)
93 {
94         barrier->lock = 0;
95         barrier->init_count = count;
96         barrier->current_count = count;
97         barrier->ready = 0;
98 }
99
100 void reset_barrier(barrier_t* barrier)
101 {
102         barrier->current_count = barrier->init_count;
103 }
104
105 // primitive barrier function.  all cores call this.
106 void waiton_barrier(barrier_t* barrier)
107 {
108         uint8_t local_ready = barrier->ready;
109
110         spin_lock_irqsave(&barrier->lock);
111         barrier->current_count--;
112         if (barrier->current_count) {
113                 spin_unlock_irqsave(&barrier->lock);
114                 while (barrier->ready == local_ready)
115                         cpu_relax();
116         } else {
117                 spin_unlock_irqsave(&barrier->lock);
118                 reset_barrier(barrier);
119                 // if we need to wmb(), it'll be here
120                 barrier->ready++;
121         }
122 }