| /* |
| * Copyright (c) 2009 The Regents of the University of California |
| * Barret Rhoden <brho@cs.berkeley.edu> |
| * See LICENSE for details. |
| */ |
| |
| #include <arch/arch.h> |
| #include <arch/topology.h> |
| #include <bitmask.h> |
| #include <smp.h> |
| |
| #include <atomic.h> |
| #include <error.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <pmap.h> |
| #include <env.h> |
| #include <trap.h> |
| |
| /*************************** IPI Wrapper Stuff ********************************/ |
| // checklists to protect the global interrupt_handlers for 0xf0, f1, f2, f3, f4 |
| // need to be global, since there is no function that will always exist for them |
| handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS]; |
| |
| static int smp_call_function(uint8_t type, uint32_t dest, isr_t handler, |
| void *data, handler_wrapper_t **wait_wrapper) |
| { |
| int8_t state = 0; |
| uint32_t wrapper_num; |
| handler_wrapper_t* wrapper; |
| extern atomic_t outstanding_calls; |
| |
| // prevents us from ever having more than NUM_HANDLER_WRAPPERS callers |
| // in the process of competing for vectors. not decremented until both |
| // after the while(1) loop and after it's been waited on. |
| atomic_inc(&outstanding_calls); |
| if (atomic_read(&outstanding_calls) > NUM_HANDLER_WRAPPERS) { |
| atomic_dec(&outstanding_calls); |
| return -EBUSY; |
| } |
| |
| // assumes our cores are numbered in order |
| if ((type == 4) && (dest >= num_cores)) |
| panic("Destination CPU %d does not exist!", dest); |
| |
| // build the mask based on the type and destination |
| INIT_CHECKLIST_MASK(cpu_mask, MAX_NUM_CORES); |
| // set checklist mask's size dynamically to the num cpus actually |
| // present |
| cpu_mask.size = num_cores; |
| switch (type) { |
| case 1: // self |
| SET_BITMASK_BIT(cpu_mask.bits, core_id()); |
| break; |
| case 2: // all |
| FILL_BITMASK(cpu_mask.bits, num_cores); |
| break; |
| case 3: // all but self |
| FILL_BITMASK(cpu_mask.bits, num_cores); |
| CLR_BITMASK_BIT(cpu_mask.bits, core_id()); |
| break; |
| case 4: // physical mode |
| // note this only supports sending to one specific physical id |
| // (only sets one bit, so if multiple cores have the same phys |
| // id the first one through will set this). |
| SET_BITMASK_BIT(cpu_mask.bits, dest); |
| break; |
| case 5: // logical mode |
| // TODO |
| warn("Logical mode bitmask handler protection unimplemented!"); |
| break; |
| default: |
| panic("Invalid type for cross-core function call!"); |
| } |
| |
| // Find an available vector/wrapper. Starts with this core's id (mod |
| // the number of wrappers). Walk through on conflict. |
| // Commit returns an error if it wanted to give up for some reason, |
| // like taking too long to acquire the lock or clear the mask, at which |
| // point, we try the next one. |
| // When we are done, wrapper points to the one we finally got. |
| // this wrapper_num trick doesn't work as well if you send a bunch in a |
| // row and wait, since you always check your main one (which is |
| // currently busy). |
| wrapper_num = core_id() % NUM_HANDLER_WRAPPERS; |
| while(1) { |
| wrapper = &handler_wrappers[wrapper_num]; |
| if (!commit_checklist_wait(wrapper->cpu_list, &cpu_mask)) |
| break; |
| wrapper_num = (wrapper_num + 1) % NUM_HANDLER_WRAPPERS; |
| /* |
| uint32_t count = 0; |
| // instead of deadlock, smp_call can fail with this. makes it |
| // harder to use (have to check your return value). consider |
| // putting a delay here too (like if wrapper_num == |
| // initial_wrapper_num) |
| // note 1000 isn't enough... |
| if (count++ > NUM_HANDLER_WRAPPERS * 1000) |
| return -EBUSY; |
| */ |
| } |
| |
| // Wanting to wait is expressed by having a non-NULL handler_wrapper_t** |
| // passed in. Pass out our reference to wrapper, to wait later. If we |
| // don't want to wait, release the checklist (though it is still not |
| // clear, so it can't be used til everyone checks in). |
| if (wait_wrapper) |
| *wait_wrapper = wrapper; |
| else { |
| release_checklist(wrapper->cpu_list); |
| atomic_dec(&outstanding_calls); |
| } |
| |
| /* TODO: once we can unregister, we can reregister. This here assumes |
| * that there is only one IRQ registered, and its the one for SMP call |
| * function. We're waiting on RCU to do a nice unregister. */ |
| extern struct irq_handler *irq_handlers[]; |
| |
| if (!irq_handlers[wrapper->vector]) { |
| register_irq(wrapper->vector, handler, data, MKBUS(BusIPI, 0, 0, |
| 0)); |
| } else { |
| /* we're replacing the old one. hope it was ours, and the IRQ |
| * is firing concurrently (if it is, there's an smp_call bug)! |
| * */ |
| irq_handlers[wrapper->vector]->isr = handler; |
| irq_handlers[wrapper->vector]->data = data; |
| } |
| |
| // WRITE MEMORY BARRIER HERE |
| enable_irqsave(&state); |
| // Send the proper type of IPI. I made up these numbers. |
| switch (type) { |
| case 1: |
| send_self_ipi(wrapper->vector); |
| break; |
| case 2: |
| send_broadcast_ipi(wrapper->vector); |
| break; |
| case 3: |
| send_all_others_ipi(wrapper->vector); |
| break; |
| case 4: // physical mode |
| send_ipi(dest, wrapper->vector); |
| break; |
| case 5: // logical mode |
| send_group_ipi(dest, wrapper->vector); |
| break; |
| default: |
| panic("Invalid type for cross-core function call!"); |
| } |
| // wait long enough to receive our own broadcast (PROBABLY WORKS) TODO |
| disable_irqsave(&state); |
| return 0; |
| } |
| |
| // Wrapper functions. Add more as they are needed. |
| int smp_call_function_self(isr_t handler, void *data, |
| handler_wrapper_t **wait_wrapper) |
| { |
| return smp_call_function(1, 0, handler, data, wait_wrapper); |
| } |
| |
| int smp_call_function_all(isr_t handler, void *data, |
| handler_wrapper_t **wait_wrapper) |
| { |
| return smp_call_function(2, 0, handler, data, wait_wrapper); |
| } |
| |
| int smp_call_function_single(uint32_t dest, isr_t handler, void *data, |
| handler_wrapper_t **wait_wrapper) |
| { |
| return smp_call_function(4, dest, handler, data, wait_wrapper); |
| } |
| |
| // If you want to wait, pass the address of a pointer up above, then call |
| // this to do the actual waiting. Be somewhat careful about uninitialized |
| // or old wrapper pointers. |
| int smp_call_wait(handler_wrapper_t* wrapper) |
| { |
| if (wrapper) { |
| waiton_checklist(wrapper->cpu_list); |
| return 0; |
| } else { |
| warn("Attempting to wait on null wrapper! Check your return values!"); |
| return -EFAIL; |
| } |
| } |
| |