blob: 8287bf33332241f9df5e63fbf48e8653b6ac8e88 [file] [log] [blame]
/*
* 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;
}
}