mirror of
https://github.com/clearlinux/graphene.git
synced 2026-09-06 05:41:37 +00:00
328 lines
8.8 KiB
C
328 lines
8.8 KiB
C
/* Copyright (C) 2014 Stony Brook University
|
|
This file is part of Graphene Library OS.
|
|
|
|
Graphene Library OS is free software: you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public License
|
|
as published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
Graphene Library OS is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
/*
|
|
* shim_malloc.c
|
|
*
|
|
* This file implements page allocation for the library OS-internal SLAB
|
|
* memory allocator. The slab allocator is in Pal/lib/slabmgr.h.
|
|
*
|
|
* When existing slabs are not sufficient, or a large (4k or greater)
|
|
* allocation is requested, it ends up here (__system_alloc and __system_free).
|
|
*/
|
|
|
|
#include <asm/mman.h>
|
|
#include <pal.h>
|
|
#include <pal_debug.h>
|
|
#include <shim_checkpoint.h>
|
|
#include <shim_internal.h>
|
|
#include <shim_profile.h>
|
|
#include <shim_utils.h>
|
|
#include <shim_vma.h>
|
|
|
|
static struct shim_lock slab_mgr_lock;
|
|
|
|
#define SYSTEM_LOCK() lock(&slab_mgr_lock)
|
|
#define SYSTEM_UNLOCK() unlock(&slab_mgr_lock)
|
|
|
|
#ifdef SLAB_DEBUG_TRACE
|
|
#define SLAB_DEBUG
|
|
#endif
|
|
|
|
#define SLAB_CANARY
|
|
#define STARTUP_SIZE 16
|
|
|
|
#include <slabmgr.h>
|
|
|
|
static SLAB_MGR slab_mgr = NULL;
|
|
|
|
DEFINE_PROFILE_CATEGORY(memory, );
|
|
|
|
/* Returns NULL on failure */
|
|
void* __system_malloc(size_t size) {
|
|
size_t alloc_size = PAGE_ALIGN_UP(size);
|
|
void* addr;
|
|
void* ret_addr;
|
|
int flags = MAP_PRIVATE | MAP_ANONYMOUS | VMA_INTERNAL;
|
|
|
|
/*
|
|
* If vmas are initialized, we need to request a free address range
|
|
* using bkeep_unmapped_any(). The current mmap code uses this function
|
|
* to synchronize all address allocation, via a "publication"
|
|
* pattern. It is not safe to just call DkVirtualMemoryAlloc directly
|
|
* without reserving the vma region first.
|
|
*/
|
|
addr = bkeep_unmapped_any(alloc_size, PROT_READ | PROT_WRITE, flags, 0, "slab");
|
|
|
|
if (!addr)
|
|
return NULL;
|
|
|
|
do {
|
|
ret_addr = DkVirtualMemoryAlloc(addr, alloc_size, 0, PAL_PROT_WRITE | PAL_PROT_READ);
|
|
|
|
if (!ret_addr) {
|
|
/* If the allocation is interrupted by signal, try to handle the
|
|
* signal and then retry the allocation. */
|
|
if (PAL_NATIVE_ERRNO == PAL_ERROR_INTERRUPTED) {
|
|
handle_signal();
|
|
continue;
|
|
}
|
|
|
|
debug("failed to allocate memory (%ld)\n", -PAL_ERRNO);
|
|
bkeep_munmap(addr, alloc_size, flags);
|
|
return NULL;
|
|
}
|
|
} while (!ret_addr);
|
|
assert(addr == ret_addr);
|
|
return addr;
|
|
}
|
|
|
|
void __system_free(void* addr, size_t size) {
|
|
DkVirtualMemoryFree(addr, PAGE_ALIGN_UP(size));
|
|
|
|
if (bkeep_munmap(addr, PAGE_ALIGN_UP(size), VMA_INTERNAL) < 0)
|
|
BUG();
|
|
}
|
|
|
|
int init_slab(void) {
|
|
create_lock(&slab_mgr_lock);
|
|
slab_mgr = create_slab_mgr();
|
|
return 0;
|
|
}
|
|
|
|
EXTERN_ALIAS(init_slab);
|
|
|
|
int reinit_slab(void) {
|
|
if (slab_mgr) {
|
|
destroy_slab_mgr(slab_mgr);
|
|
slab_mgr = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_PROFILE_OCCURENCE(malloc_0, memory);
|
|
DEFINE_PROFILE_OCCURENCE(malloc_1, memory);
|
|
DEFINE_PROFILE_OCCURENCE(malloc_2, memory);
|
|
DEFINE_PROFILE_OCCURENCE(malloc_3, memory);
|
|
DEFINE_PROFILE_OCCURENCE(malloc_4, memory);
|
|
DEFINE_PROFILE_OCCURENCE(malloc_5, memory);
|
|
DEFINE_PROFILE_OCCURENCE(malloc_6, memory);
|
|
DEFINE_PROFILE_OCCURENCE(malloc_7, memory);
|
|
DEFINE_PROFILE_OCCURENCE(malloc_big, memory);
|
|
|
|
#if defined(SLAB_DEBUG_PRINT) || defined(SLABD_DEBUG_TRACE)
|
|
void* __malloc_debug(size_t size, const char* file, int line)
|
|
#else
|
|
void* malloc(size_t size)
|
|
#endif
|
|
{
|
|
#ifdef PROFILE
|
|
int level = -1;
|
|
|
|
for (size_t i = 0; i < SLAB_LEVEL; i++)
|
|
if (size < slab_levels[i]) {
|
|
level = i;
|
|
break;
|
|
}
|
|
switch (level) {
|
|
case 0:
|
|
INC_PROFILE_OCCURENCE(malloc_0);
|
|
break;
|
|
case 1:
|
|
INC_PROFILE_OCCURENCE(malloc_1);
|
|
break;
|
|
case 2:
|
|
INC_PROFILE_OCCURENCE(malloc_2);
|
|
break;
|
|
case 3:
|
|
INC_PROFILE_OCCURENCE(malloc_3);
|
|
break;
|
|
case 4:
|
|
INC_PROFILE_OCCURENCE(malloc_4);
|
|
break;
|
|
case 5:
|
|
INC_PROFILE_OCCURENCE(malloc_5);
|
|
break;
|
|
case 6:
|
|
INC_PROFILE_OCCURENCE(malloc_6);
|
|
break;
|
|
case 7:
|
|
INC_PROFILE_OCCURENCE(malloc_7);
|
|
break;
|
|
case -1:
|
|
INC_PROFILE_OCCURENCE(malloc_big);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef SLAB_DEBUG_TRACE
|
|
void* mem = slab_alloc_debug(slab_mgr, size, file, line);
|
|
#else
|
|
void* mem = slab_alloc(slab_mgr, size);
|
|
#endif
|
|
|
|
if (!mem) {
|
|
/*
|
|
* Normally, the library OS should not run out of memory.
|
|
* If malloc() failed internally, we cannot handle the
|
|
* condition and must terminate the current process.
|
|
*/
|
|
SYS_PRINTF("******** Out-of-memory in library OS ********\n");
|
|
__abort();
|
|
}
|
|
|
|
#ifdef SLAB_DEBUG_PRINT
|
|
debug("malloc(%d) = %p (%s:%d)\n", size, mem, file, line);
|
|
#endif
|
|
return mem;
|
|
}
|
|
#if !defined(SLAB_DEBUG_PRINT) && !defined(SLAB_DEBUG_TRACE)
|
|
EXTERN_ALIAS(malloc);
|
|
#endif
|
|
|
|
void* calloc(size_t nmemb, size_t size) {
|
|
// This overflow checking is not a UB, because the operands are unsigned.
|
|
size_t total = nmemb * size;
|
|
if (total / size != nmemb)
|
|
return NULL;
|
|
void* ptr = malloc(total);
|
|
if (ptr)
|
|
memset(ptr, 0, total);
|
|
return ptr;
|
|
}
|
|
EXTERN_ALIAS(calloc);
|
|
|
|
#if 0 /* Temporarily disabling this code */
|
|
void * realloc(void * ptr, size_t new_size)
|
|
{
|
|
/* TODO: We can't deal with this case right now */
|
|
assert(!memory_migrated(ptr));
|
|
|
|
size_t old_size = slab_get_buf_size(slab_mgr, ptr);
|
|
|
|
/*
|
|
* TODO: this realloc() implementation follows the GLIBC design, which
|
|
* will avoid reallocation when the buffer is large enough. Potentially
|
|
* this design can cause memory draining if user resizes an extremely
|
|
* large object to much smaller.
|
|
*/
|
|
if (old_size >= new_size)
|
|
return ptr;
|
|
|
|
void * new_buf = malloc(new_size);
|
|
if (!new_buf)
|
|
return NULL;
|
|
|
|
memcpy(new_buf, ptr, old_size);
|
|
/* realloc() does not zero the rest of the object */
|
|
free(ptr);
|
|
return new_buf;
|
|
}
|
|
EXTERN_ALIAS(realloc);
|
|
#endif
|
|
|
|
// Copies data from `mem` to a newly allocated buffer of a specified size.
|
|
#if defined(SLAB_DEBUG_PRINT) || defined(SLABD_DEBUG_TRACE)
|
|
void* __malloc_copy_debug(const void* mem, size_t size, const char* file, int line)
|
|
#else
|
|
void* malloc_copy(const void* mem, size_t size)
|
|
#endif
|
|
{
|
|
#if defined(SLAB_DEBUG_PRINT) || defined(SLABD_DEBUG_TRACE)
|
|
void* buff = __malloc_debug(size, file, line);
|
|
#else
|
|
void* buff = malloc(size);
|
|
#endif
|
|
if (buff)
|
|
memcpy(buff, mem, size);
|
|
return buff;
|
|
}
|
|
#if !defined(SLAB_DEBUG_PRINT) && !defined(SLABD_DEBUG_TRACE)
|
|
EXTERN_ALIAS(malloc_copy);
|
|
#endif
|
|
|
|
DEFINE_PROFILE_OCCURENCE(free_0, memory);
|
|
DEFINE_PROFILE_OCCURENCE(free_1, memory);
|
|
DEFINE_PROFILE_OCCURENCE(free_2, memory);
|
|
DEFINE_PROFILE_OCCURENCE(free_3, memory);
|
|
DEFINE_PROFILE_OCCURENCE(free_4, memory);
|
|
DEFINE_PROFILE_OCCURENCE(free_5, memory);
|
|
DEFINE_PROFILE_OCCURENCE(free_6, memory);
|
|
DEFINE_PROFILE_OCCURENCE(free_7, memory);
|
|
DEFINE_PROFILE_OCCURENCE(free_big, memory);
|
|
DEFINE_PROFILE_OCCURENCE(free_migrated, memory);
|
|
|
|
#if defined(SLAB_DEBUG_PRINT) || defined(SLABD_DEBUG_TRACE)
|
|
void __free_debug(void* mem, const char* file, int line)
|
|
#else
|
|
void free(void* mem)
|
|
#endif
|
|
{
|
|
if (!mem)
|
|
return;
|
|
if (memory_migrated(mem)) {
|
|
INC_PROFILE_OCCURENCE(free_migrated);
|
|
return;
|
|
}
|
|
|
|
#ifdef PROFILE
|
|
int level = RAW_TO_LEVEL(mem);
|
|
switch (level) {
|
|
case 0:
|
|
INC_PROFILE_OCCURENCE(free_0);
|
|
break;
|
|
case 1:
|
|
INC_PROFILE_OCCURENCE(free_1);
|
|
break;
|
|
case 2:
|
|
INC_PROFILE_OCCURENCE(free_2);
|
|
break;
|
|
case 3:
|
|
INC_PROFILE_OCCURENCE(free_3);
|
|
break;
|
|
case 4:
|
|
INC_PROFILE_OCCURENCE(free_4);
|
|
break;
|
|
case 5:
|
|
INC_PROFILE_OCCURENCE(free_5);
|
|
break;
|
|
case 6:
|
|
INC_PROFILE_OCCURENCE(free_6);
|
|
break;
|
|
case 7:
|
|
INC_PROFILE_OCCURENCE(free_7);
|
|
break;
|
|
case -1:
|
|
case 255:
|
|
INC_PROFILE_OCCURENCE(free_big);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef SLAB_DEBUG_PRINT
|
|
debug("free(%p) (%s:%d)\n", mem, file, line);
|
|
#endif
|
|
|
|
#ifdef SLAB_DEBUG_TRACE
|
|
slab_free_debug(slab_mgr, mem, file, line);
|
|
#else
|
|
slab_free(slab_mgr, mem);
|
|
#endif
|
|
}
|
|
#if !defined(SLAB_DEBUG_PRINT) && !defined(SLABD_DEBUG_TRACE)
|
|
EXTERN_ALIAS(free);
|
|
#endif
|