| /* 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); |
| } |