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