blob: cd45cf4500581326d43064498b2eab4827adc5cc [file] [log] [blame]
/* Copyright (c) 2015, 2018 Google Inc
* Davide Libenzi <dlibenzi@google.com>
* Barret Rhoden <brho@google.com>
* See LICENSE for details.
*/
#include <sys/types.h>
#include <arch/topology.h>
#include <kmalloc.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <percpu.h>
#include <arena.h>
#include <page_alloc.h>
#include <smp.h>
char *percpu_base;
static struct arena *pcpu_dyn_arena;
static void run_init_functions(void)
{
extern char __attribute__((weak)) PERCPU_INIT_START_VAR[];
extern char __attribute__((weak)) PERCPU_INIT_STOP_VAR[];
if (PERCPU_INIT_START_VAR) {
void (**pfunc)(void) = (void (**)(void)) PERCPU_INIT_START_VAR;
void (**pfunc_top)(void) =
(void (**)(void)) PERCPU_INIT_STOP_VAR;
for (; pfunc < pfunc_top; pfunc++)
(*pfunc)();
}
}
void percpu_init(void)
{
assert(num_cores > 0);
percpu_base = kpages_alloc(num_cores * PERCPU_SIZE, MEM_WAIT);
if (PERCPU_START_VAR) {
for (int i = 0; i < num_cores; i++)
memcpy(percpu_base + i * PERCPU_SIZE, PERCPU_START_VAR,
PERCPU_STATIC_SIZE);
}
/* We hand out addresses starting right above the static section, which
* ends at PERCPU_STOP_VAR. */
pcpu_dyn_arena = arena_create("pcpu_dyn", PERCPU_STOP_VAR,
PERCPU_DYN_SIZE, 1, NULL, NULL, NULL, 0,
MEM_WAIT);
assert(pcpu_dyn_arena);
run_init_functions();
}
/* We return pointers, but our users need to dereference them when using any of
* the PERCPU_ helpers so that they are treated like the static vars. */
void *__percpu_alloc(size_t size, size_t align, int flags)
{
assert(pcpu_dyn_arena);
/* our alignment is limited to the alignment of percpu_base */
warn_on(align > PGSIZE);
return arena_xalloc(pcpu_dyn_arena, size, align, 0, 0, NULL, NULL,
flags | ARENA_BESTFIT);
}
void *__percpu_zalloc(size_t size, size_t align, int flags)
{
/* Yikes! */
struct {
uint8_t data[size];
} *ret;
ret = __percpu_alloc(size, align, flags);
if (!ret)
return NULL;
for_each_core(i)
memset(_PERCPU_VARPTR(*ret, i), 0, size);
return ret;
}
void __percpu_free(void *base, size_t size)
{
arena_free(pcpu_dyn_arena, base, size);
}