genode/repos/libports/src/lib/libc/libc_mem_alloc.cc
Norman Feske 648bcd1505 libc: unify use of namespaces
This patch unifies the patterns of using the 'Genode' and 'Libc'
namespaces.

Types defined in the 'internal/' headers reside in the 'Libc'
namespace. The code in the headers does not need to use the
'Libc::' prefix.

Compilation units import the 'Libc' namespace after the definition of
local types. Local types reside in the 'Libc' namespace (and should
eventually move to an 'internal/' header).

Since the 'Libc' namespace imports the 'Genode' namespace, there is
no need to use the 'Genode::' prefix. Consequently, code in the
compilation units rarely need to qualify the 'Genode' or 'Libc'
namespaces.

There are a few cases where the 'Libc', the 'Genode', and the global
(libc) namespaces are ambigious. In these cases, an explicit
clarification is needed:

- 'Genode::Allocator' differs from 'Libc::Allocator'.
- 'Genode::Env' differs from 'Libc::Env'.
- Genode's string functions (strcmp, memcpy, strcpy) conflict
  with the names of the (global) libc functions.
- There exist both 'Genode::uint64_t' and the libc'c 'uint64_t'.

Issue #3497
2019-11-19 14:10:55 +01:00

175 lines
4.6 KiB
C++

/*
* \brief Allocator for anonymous memory used by libc
* \author Norman Feske
* \date 2012-05-18
*
* The libc uses a dedicated allocator instead of 'env()->heap()' because the
* 'Allocator' interface of 'env()->heap()' does not allow for aligned
* allocations. Some libc functions, however, rely on aligned memory. For
* example the blocks returned by mmap for allocating anonymous memory are
* assumed to be page-aligned.
*
* The code is largely based on 'base/include/base/heap.h' and
* 'base/src/lib/base/heap.cc'.
*/
/* Genode includes */
#include <base/env.h>
#include <base/allocator_avl.h>
/* libc-internal includes */
#include <internal/mem_alloc.h>
#include <internal/init.h>
using namespace Genode;
Libc::Mem_alloc_impl::Dataspace_pool::~Dataspace_pool()
{
/* free all ram_dataspaces */
for (Dataspace *ds; (ds = first()); ) {
/*
* read dataspace capability and modify _ds_list before detaching
* possible backing store for Dataspace - we rely on LIFO list
* manipulation here!
*/
Ram_dataspace_capability ds_cap = ds->cap;
void const * const local_addr = ds->local_addr;
remove(ds);
delete ds;
_region_map->detach(local_addr);
_ram->free(ds_cap);
}
}
int Libc::Mem_alloc_impl::Dataspace_pool::expand(size_t size, Range_allocator *alloc)
{
Ram_dataspace_capability new_ds_cap;
void *local_addr, *ds_addr = 0;
/* make new ram dataspace available at our local address space */
try {
new_ds_cap = _ram->alloc(size);
enum { MAX_SIZE = 0, NO_OFFSET = 0, ANY_LOCAL_ADDR = false };
local_addr = _region_map->attach(new_ds_cap, MAX_SIZE, NO_OFFSET,
ANY_LOCAL_ADDR, nullptr, _executable);
}
catch (Out_of_ram) { return -2; }
catch (Out_of_caps) { return -4; }
catch (Region_map::Region_conflict) {
_ram->free(new_ds_cap);
return -3;
}
/* add new local address range to our local allocator */
alloc->add_range((addr_t)local_addr, size);
/* now that we have new backing store, allocate Dataspace structure */
if (alloc->alloc_aligned(sizeof(Dataspace), &ds_addr, 2).error()) {
warning("libc: could not allocate meta data - this should never happen");
return -1;
}
/* add dataspace information to list of dataspaces */
Dataspace *ds = new (ds_addr) Dataspace(new_ds_cap, local_addr);
insert(ds);
return 0;
}
void *Libc::Mem_alloc_impl::alloc(size_t size, size_t align_log2)
{
/* serialize access of heap functions */
Lock::Guard lock_guard(_lock);
/* try allocation at our local allocator */
void *out_addr = 0;
if (_alloc.alloc_aligned(size, &out_addr, align_log2).ok())
return out_addr;
/*
* Calculate block size of needed backing store. The block must hold the
* requested 'size' with the requested alignment, a new Dataspace structure
* and space for AVL-node slab blocks if the allocation above failed.
* Finally, we align the size to a 4K page.
*/
size_t request_size = size + max((1 << align_log2), 1024) +
Allocator_avl::slab_block_size() + sizeof(Dataspace);
if (request_size < _chunk_size*sizeof(umword_t)) {
request_size = _chunk_size*sizeof(umword_t);
/*
* Exponentially increase chunk size with each allocated chunk until
* we hit 'MAX_CHUNK_SIZE'.
*/
_chunk_size = min(2*_chunk_size, (size_t)MAX_CHUNK_SIZE);
}
if (_ds_pool.expand(align_addr(request_size, 12), &_alloc) < 0) {
warning("libc: could not expand dataspace pool");
return 0;
}
/* allocate originally requested block */
return _alloc.alloc_aligned(size, &out_addr, align_log2).ok() ? out_addr : 0;
}
void Libc::Mem_alloc_impl::free(void *addr)
{
/* serialize access of heap functions */
Lock::Guard lock_guard(_lock);
/* forward request to our local allocator */
_alloc.free(addr);
}
size_t Libc::Mem_alloc_impl::size_at(void const *addr) const
{
/* serialize access of heap functions */
Lock::Guard lock_guard(_lock);
/* forward request to our local allocator */
return _alloc.size_at(addr);
}
static Libc::Mem_alloc *_libc_mem_alloc_rw = nullptr;
static Libc::Mem_alloc *_libc_mem_alloc_rwx = nullptr;
static void _init_mem_alloc(Region_map &rm, Ram_allocator &ram)
{
enum { MEMORY_EXECUTABLE = true };
static Libc::Mem_alloc_impl inst_rw(rm, ram, !MEMORY_EXECUTABLE);
static Libc::Mem_alloc_impl inst_rwx(rm, ram, MEMORY_EXECUTABLE);
_libc_mem_alloc_rw = &inst_rw;
_libc_mem_alloc_rwx = &inst_rwx;
}
void Libc::init_mem_alloc(Genode::Env &env)
{
_init_mem_alloc(env.rm(), env.ram());
}
Libc::Mem_alloc *Libc::mem_alloc(bool executable)
{
if (!_libc_mem_alloc_rw || !_libc_mem_alloc_rwx)
error("attempt to use 'Libc::mem_alloc' before call of 'init_mem_alloc'");
return executable ? _libc_mem_alloc_rwx : _libc_mem_alloc_rw;
}