5d522f0143397e702e7ab125da480314b7a7ecce
[akaros.git] / kern / src / atomic.c
1 #include <arch/apic.h>
2 #include <arch/atomic.h>
3 #include <ros/error.h>
4 #include <string.h>
5 #include <assert.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         // abort if the list is locked.  this will protect us from trying to commit
12         // and thus spin on a checklist that we are already waiting on.  it is
13         // still possible to not get the lock, but the holder is on another core.
14         // Or, bail out if we can see the list is already in use.  This check is
15         // just an optimization before we try to use the list for real.
16         if ((checklist_is_locked(list)) || !checklist_is_clear(list))
17                 return E_BUSY;
18
19         // possession of this lock means you can wait on it and set it
20         spin_lock_irqsave(&list->lock);
21         // wait til the list is available.  could have some adaptive thing here
22         // where it fails after X tries (like 500), gives up the lock, and returns
23         // an error code
24         while (!checklist_is_clear(list))
25                 cpu_relax();
26
27         // list is ours and clear, set it to the settings of our list
28         COPY_BITMASK(list->mask.bits, mask->bits, mask->size); 
29         return 0;
30 }
31
32 int commit_checklist_nowait(checklist_t* list, checklist_mask_t* mask)
33 {
34         int e = 0;
35         if (e = commit_checklist_wait(list, mask))
36                 return e;
37         // give up the lock, since we won't wait for completion
38         spin_unlock_irqsave(&list->lock);
39         return e;
40 }
41 // The deal with the lock:
42 // what if two different actors are waiting on the list, but for different reasons?
43 // part of the problem is we are doing both set and check via the same path
44 //
45 // aside: we made this a lot more difficult than the usual barriers or even 
46 // the RCU grace-period checkers, since we have to worry about this construct
47 // being used by others before we are done with it.
48 //
49 // how about this: if we want to wait on this later, we just don't release the
50 // lock.  if we release it, then we don't care who comes in and grabs and starts
51 // checking the list.  
52 //      - regardless, there are going to be issues with people looking for a free 
53 //      item.  even if they grab the lock, they may end up waiting a while and 
54 //      wantint to bail (like test for a while, give up, move on, etc).  
55 //      - still limited in that only the setter can check, and only one person
56 //      can spinwait / check for completion.  if someone else tries to wait (wanting
57 //      completion), they may miss it if someone else comes in and grabs the lock
58 //      to use it for a new checklist
59 //              - if we had the ability to sleep and get woken up, we could have a 
60 //              queue.  actually, we could do a queue anyway, but they all spin
61 //              and it's the bosses responsibility to *wake* them
62
63 // Must be called after commit_checklist
64 // Assumed we held the lock if we ever call this
65 int waiton_checklist(checklist_t* list)
66 {
67         extern uint32_t outstanding_calls;
68         // can consider breakout out early, like above, and erroring out
69         while (!checklist_is_clear(list))
70                 cpu_relax();
71         spin_unlock_irqsave(&list->lock);
72         // global counter of wrappers either waited on or being contended for.
73         atomic_dec(&outstanding_calls);
74         return 0;
75 }
76
77 // like waiton, but don't bother waiting either
78 int release_checklist(checklist_t* list)
79 {
80         spin_unlock_irqsave(&list->lock);
81         return 0;
82 }
83
84 // peaks in and sees if the list is locked with it's spinlock
85 int checklist_is_locked(checklist_t* list)
86 {
87         // remember the lock status is the lowest byte of the lock
88         return list->lock & 0xff;
89 }
90
91 // no synch guarantees - just looks at the list
92 int checklist_is_clear(checklist_t* list)
93 {
94         return BITMASK_IS_CLEAR(list->mask.bits, list->mask.size);
95 }
96
97 // no synch guarantees - just resets the list to empty
98 void reset_checklist(checklist_t* list)
99 {
100         CLR_BITMASK(list->mask.bits, list->mask.size);
101 }
102
103 // CPU mask specific - this is how cores report in
104 void down_checklist(checklist_t* list)
105 {
106         CLR_BITMASK_BIT_ATOMIC(list->mask.bits, lapic_get_id());
107 }
108
109 /* Barriers */
110 void init_barrier(barrier_t* barrier, uint32_t count)
111 {
112         barrier->lock = 0;
113         barrier->init_count = count;
114         barrier->current_count = count;
115         barrier->ready = 0;
116 }
117
118 void reset_barrier(barrier_t* barrier)
119 {
120         barrier->current_count = barrier->init_count;
121 }
122
123 // primitive barrier function.  all cores call this.
124 void waiton_barrier(barrier_t* barrier)
125 {
126         uint8_t local_ready = barrier->ready;
127
128         spin_lock_irqsave(&barrier->lock);
129         barrier->current_count--;
130         if (barrier->current_count) {
131                 spin_unlock_irqsave(&barrier->lock);
132                 while (barrier->ready == local_ready)
133                         cpu_relax();
134         } else {
135                 spin_unlock_irqsave(&barrier->lock);
136                 reset_barrier(barrier);
137                 // if we need to wmb(), it'll be here
138                 barrier->ready++;
139         }
140 }