863e4d30477d98d95de3af52a32524ce39c6c2da
[akaros.git] / kern / arch / x86 / smp.c
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  * See LICENSE for details.
5  */
6
7 #include <arch/arch.h>
8 #include <bitmask.h>
9 #include <smp.h>
10
11 #include <atomic.h>
12 #include <error.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <assert.h>
16 #include <pmap.h>
17 #include <env.h>
18 #include <trap.h>
19
20 /* Lookup table for core_id and per_cpu_inf, indexed by real __core_id() */
21 int hw_coreid_lookup[MAX_NUM_CPUS] = {[0 ... (MAX_NUM_CPUS - 1)] -1};
22 int os_coreid_lookup[MAX_NUM_CPUS] = {[0 ... (MAX_NUM_CPUS - 1)] -1};
23
24 /*************************** IPI Wrapper Stuff ********************************/
25 // checklists to protect the global interrupt_handlers for 0xf0, f1, f2, f3, f4
26 // need to be global, since there is no function that will always exist for them
27 handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
28
29 static int smp_call_function(uint8_t type, uint32_t dest, isr_t handler,
30                              void *data, handler_wrapper_t **wait_wrapper)
31 {
32         int8_t state = 0;
33         uint32_t wrapper_num;
34         handler_wrapper_t* wrapper;
35         extern atomic_t outstanding_calls;
36
37         // prevents us from ever having more than NUM_HANDLER_WRAPPERS callers in
38         // the process of competing for vectors.  not decremented until both after
39         // the while(1) loop and after it's been waited on.
40         atomic_inc(&outstanding_calls);
41         if (atomic_read(&outstanding_calls) > NUM_HANDLER_WRAPPERS) {
42                 atomic_dec(&outstanding_calls);
43                 return -EBUSY;
44         }
45
46         // assumes our cores are numbered in order
47         if ((type == 4) && (dest >= num_cpus))
48                 panic("Destination CPU %d does not exist!", dest);
49
50         // build the mask based on the type and destination
51         INIT_CHECKLIST_MASK(cpu_mask, MAX_NUM_CPUS);
52         // set checklist mask's size dynamically to the num cpus actually present
53         cpu_mask.size = num_cpus;
54         switch (type) {
55                 case 1: // self
56                         SET_BITMASK_BIT(cpu_mask.bits, core_id());
57                         break;
58                 case 2: // all
59                         FILL_BITMASK(cpu_mask.bits, num_cpus);
60                         break;
61                 case 3: // all but self
62                         FILL_BITMASK(cpu_mask.bits, num_cpus);
63                         CLR_BITMASK_BIT(cpu_mask.bits, core_id());
64                         break;
65                 case 4: // physical mode
66                         // note this only supports sending to one specific physical id
67                         // (only sets one bit, so if multiple cores have the same phys id
68                         // the first one through will set this).
69                         SET_BITMASK_BIT(cpu_mask.bits, dest);
70                         break;
71                 case 5: // logical mode
72                         // TODO
73                         warn("Logical mode bitmask handler protection not implemented!");
74                         break;
75                 default:
76                         panic("Invalid type for cross-core function call!");
77         }
78
79         // Find an available vector/wrapper.  Starts with this core's id (mod the
80         // number of wrappers).  Walk through on conflict.
81         // Commit returns an error if it wanted to give up for some reason,
82         // like taking too long to acquire the lock or clear the mask, at which
83         // point, we try the next one.
84         // When we are done, wrapper points to the one we finally got.
85         // this wrapper_num trick doesn't work as well if you send a bunch in a row
86         // and wait, since you always check your main one (which is currently busy).
87         wrapper_num = core_id() % NUM_HANDLER_WRAPPERS;
88         while(1) {
89                 wrapper = &handler_wrappers[wrapper_num];
90                 if (!commit_checklist_wait(wrapper->cpu_list, &cpu_mask))
91                         break;
92                 wrapper_num = (wrapper_num + 1) % NUM_HANDLER_WRAPPERS;
93                 /*
94                 uint32_t count = 0;
95                 // instead of deadlock, smp_call can fail with this.  makes it harder
96                 // to use (have to check your return value).  consider putting a delay
97                 // here too (like if wrapper_num == initial_wrapper_num)
98                 if (count++ > NUM_HANDLER_WRAPPERS * 1000) // note 1000 isn't enough...
99                         return -EBUSY;
100                 */
101         }
102
103         // Wanting to wait is expressed by having a non-NULL handler_wrapper_t**
104         // passed in.  Pass out our reference to wrapper, to wait later.
105         // If we don't want to wait, release the checklist (though it is still not
106         // clear, so it can't be used til everyone checks in).
107         if (wait_wrapper)
108                 *wait_wrapper = wrapper;
109         else {
110                 release_checklist(wrapper->cpu_list);
111                 atomic_dec(&outstanding_calls);
112         }
113
114         /* TODO: once we can unregister, we can reregister.  This here assumes that
115          * there is only one IRQ registered, and its the one for SMP call function.
116          * We're waiting on RCU to do a nice unregister. */
117         extern struct irq_handler *irq_handlers[];
118         if (!irq_handlers[wrapper->vector]) {
119                 register_irq(wrapper->vector, handler, data, MKBUS(BusIPI, 0, 0, 0));
120         } else {
121                 /* we're replacing the old one.  hope it was ours, and the IRQ is firing
122                  * concurrently (if it is, there's an smp_call bug)! */
123                 irq_handlers[wrapper->vector]->isr = handler;
124                 irq_handlers[wrapper->vector]->data = data;
125         }
126
127         // WRITE MEMORY BARRIER HERE
128         enable_irqsave(&state);
129         // Send the proper type of IPI.  I made up these numbers.
130         switch (type) {
131                 case 1:
132                         send_self_ipi(wrapper->vector);
133                         break;
134                 case 2:
135                         send_broadcast_ipi(wrapper->vector);
136                         break;
137                 case 3:
138                         send_all_others_ipi(wrapper->vector);
139                         break;
140                 case 4: // physical mode
141                         send_ipi(dest, wrapper->vector);
142                         break;
143                 case 5: // logical mode
144                         send_group_ipi(dest, wrapper->vector);
145                         break;
146                 default:
147                         panic("Invalid type for cross-core function call!");
148         }
149         // wait long enough to receive our own broadcast (PROBABLY WORKS) TODO
150         lapic_wait_to_send();
151         disable_irqsave(&state);
152         return 0;
153 }
154
155 // Wrapper functions.  Add more as they are needed.
156 int smp_call_function_self(isr_t handler, void *data,
157                            handler_wrapper_t **wait_wrapper)
158 {
159         return smp_call_function(1, 0, handler, data, wait_wrapper);
160 }
161
162 int smp_call_function_all(isr_t handler, void *data,
163                           handler_wrapper_t **wait_wrapper)
164 {
165         return smp_call_function(2, 0, handler, data, wait_wrapper);
166 }
167
168 int smp_call_function_single(uint32_t dest, isr_t handler, void *data,
169                              handler_wrapper_t **wait_wrapper)
170 {
171         return smp_call_function(4, dest, handler, data, wait_wrapper);
172 }
173
174 // If you want to wait, pass the address of a pointer up above, then call
175 // this to do the actual waiting.  Be somewhat careful about uninitialized 
176 // or old wrapper pointers.
177 int smp_call_wait(handler_wrapper_t* wrapper)
178 {
179         if (wrapper) {
180                 waiton_checklist(wrapper->cpu_list);
181                 return 0;
182         } else {
183                 warn("Attempting to wait on null wrapper!  Check your return values!");
184                 return -EFAIL;
185         }
186 }
187