9e2dd437d6be8f2589c1a92e765922b9fd0122dd
[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_all(barrier_t* cpu_barrier)
74 {
75         extern uint8_t num_cpus;
76         uint8_t i;
77         cpu_barrier->ready = 0;
78         for(i = 0; i < num_cpus; i++)
79                 cpu_barrier->cpu_array[i] = 1;
80 }
81
82 // primitive barrier function.  all cores call this.
83 // consider changing this to use bits and lock bit ops.
84 // currently uses a byte per core, and assumes it was 
85 // initialized by a core such that num_cpus entries
86 // are all 1
87 void barrier_all(barrier_t* cpu_barrier)
88 {
89         extern uint8_t num_cpus;
90         uint8_t i;
91         uint8_t local_ready = cpu_barrier->ready;
92
93         cpu_barrier->cpu_array[lapic_get_id()] = 0;
94         if (lapic_get_id())
95                 while(cpu_barrier->ready == local_ready)
96                         cpu_relax();
97         else {
98                 for(i = 0; i < num_cpus; i++) {
99                         while(cpu_barrier->cpu_array[i]) 
100                                 cpu_relax();
101                         cpu_barrier->cpu_array[i] = 1;
102                 }
103                 // if we need to wmb(), it'll be here
104                 cpu_barrier->ready++;
105         }
106 }