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