Commit a002945c authored by Swann Perarnau's avatar Swann Perarnau

[refactor] new version of the API

This is the result of countless interations on the internal design of
the various building blocks we want to have for this library.

At this point, I hope that this is stable enough. There are still some
tweaks needed here and there, but the core is implemented AND tested.

Some of the design decisions made:
- all functions are public, but most are not meant to be used directly.
- intended public functions take "generic" structs as arguments
- intended actual implementations rely on more complex structures, with
their own family of data and operators.
- split all objects between data and operator structs.

Exemple:
- area.c and arena.c are generic dispatch functions to call the actual,
  specific implementations.
- struct aml_area and struct_aml_arena are the same.

Currently implement:
- 2 area types: posix (malloc) and linux (numa).
- 1 arena type: jemalloc
parent 6122ede9
......@@ -16,6 +16,7 @@
# automake
Makefile.in
Makefile
# autoconf
/autom4te.cache
......@@ -40,6 +41,7 @@ stamp-h1
# autotest
tests/*.trs
tests/*.log
*.test.*
# libtool
/libtool
......
AM_CPPFLAGS = -I$(top_srcdir)/jemalloc/include
lib_LTLIBRARIES = libaml.la
LIBCSOURCES = aml.c area.c dma.c arena.c
ARENA_JEMALLOC_CSOURCES = arena_jemalloc.c
AREA_LINUX_CSOURCES = area_linux.c \
area_linux_manager.c \
area_linux_mbind.c \
area_linux_mmap.c
AREA_POSIX_CSOURCES = area_posix.c
LIBCSOURCES = aml.c area.c arena.c \
$(ARENA_JEMALLOC_CSOURCES) \
$(AREA_LINUX_CSOURCES) \
$(AREA_POSIX_CSOURCES)
LIBHSOURCES = aml.h
libaml_la_SOURCES = $(LIBCSOURCES) $(LIBHSOURCES)
......
#include <aml.h>
#include <assert.h>
#include <fcntl.h>
#include <numa.h>
#include <numaif.h>
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
#ifndef MAX_NUMNODES
#define MAX_NUMNODES 64
#endif
int aml_init(int *argc, char **argv[])
{
......
......@@ -9,34 +9,55 @@
* Forward Declarations:
******************************************************************************/
struct aml_arena;
struct aml_area;
struct aml_dma;
/*******************************************************************************
* Arenas:
* interface between areas (user-facing low-level mapping) and actual memory
* allocators (i.e. managers of buckets and so on).
* In-memory allocator implementation. Dispatches actual memory mappings back to
* areas.
******************************************************************************/
#define AML_ARENA_FLAG_ZERO 1
/* opaque handle to configuration data */
struct aml_arena_data;
struct aml_arena_ops {
int (*create)(struct aml_arena_data *, struct aml_area *);
int (*purge)(struct aml_arena_data *);
void *(*mallocx)(struct aml_arena_data *, size_t, int);
void (*dallocx)(struct aml_arena_data *, void *, int);
void *(*reallocx)(struct aml_arena_data *, void *, size_t, int);
};
struct aml_arena {
unsigned int uid;
int (*init)(struct aml_arena *, struct aml_area*);
int (*destroy)(struct aml_arena *);
void *(*malloc)(struct aml_arena *, size_t);
void (*free)(struct aml_arena *, void *);
void *(*calloc)(struct aml_arena *, size_t, size_t);
void *(*realloc)(struct aml_arena *, void *, size_t);
void *(*acquire)(struct aml_arena *, size_t);
void (*release)(struct aml_arena *, void *);
void *extra;
struct aml_arena_ops *ops;
struct aml_arena_data *data;
};
/* jemalloc arena template */
extern struct aml_arena aml_arena_jemalloc;
int aml_arena_create(struct aml_arena *, struct aml_area *);
int aml_arena_purge(struct aml_arena *);
void *aml_arena_mallocx(struct aml_arena *, size_t, int);
void aml_arena_dallocx(struct aml_arena *, void *, int);
void *aml_arena_reallocx(struct aml_arena *, void *, size_t, int);
/*******************************************************************************
* Jemalloc Arena:
******************************************************************************/
extern struct aml_arena_ops aml_arena_jemalloc_ops;
struct aml_arena_jemalloc_data {
unsigned int uid;
int flags;
};
int aml_arena_init(struct aml_arena *, struct aml_arena *, struct aml_area *);
int aml_arena_destroy(struct aml_arena *);
int aml_arena_jemalloc_regular_init(struct aml_arena_jemalloc_data *);
int aml_arena_jemalloc_regular_destroy(struct aml_arena_jemalloc_data *);
int aml_arena_jemalloc_aligned_init(struct aml_arena_jemalloc_data *, size_t);
int aml_arena_jemalloc_align_destroy(struct aml_arena_jemalloc_data *);
int aml_arena_jemalloc_generic_init(struct aml_arena_jemalloc_data *,
struct aml_arena_jemalloc_data *);
int aml_arena_jemalloc_generic_destroy(struct aml_arena_jemalloc_data *);
/*******************************************************************************
* Areas:
......@@ -44,26 +65,134 @@ int aml_arena_destroy(struct aml_arena *);
* as binding policies over it.
******************************************************************************/
/* opaque handle to configuration data */
struct aml_area_data;
struct aml_area_ops {
void *(*malloc)(struct aml_area_data *, size_t);
void (*free)(struct aml_area_data *, void *);
void *(*calloc)(struct aml_area_data *, size_t, size_t);
void *(*realloc)(struct aml_area_data *, void *, size_t);
void *(*acquire)(struct aml_area_data *, size_t);
void (*release)(struct aml_area_data *, void *);
void *(*mmap)(struct aml_area_data *, void *ptr, size_t);
int (*available)(struct aml_area_data *);
};
struct aml_area {
int (*init)(struct aml_area *);
int (*destroy)(struct aml_area *);
struct aml_arena* (*get_arena)(struct aml_area *);
void * (*mmap)(struct aml_area *, void *, size_t);
int (*mbind)(struct aml_area *, void *, size_t);
int (*available)(struct aml_area *);
void *extra;
struct aml_area_ops *ops;
struct aml_area_data *data;
};
/* templates for typical area types */
extern struct aml_area aml_area_hbm;
extern struct aml_area aml_area_regular;
/*******************************************************************************
* POSIX Area:
******************************************************************************/
extern struct aml_area_ops aml_area_posix_ops;
struct aml_area_posix_data {
};
int aml_area_init(struct aml_area *, struct aml_area *);
int aml_area_destroy(struct aml_area *);
int aml_area_posix_init(struct aml_area_posix_data *);
int aml_area_posix_destroy(struct aml_area_posix_data *);
/*******************************************************************************
* Area allocations:
* Low-level, direct allocation of memory from an area.
* Linux Area:
******************************************************************************/
extern struct aml_area_ops aml_area_linux_ops;
struct aml_area_linux_manager_data {
struct aml_arena *pool;
size_t pool_size;
};
struct aml_area_linux_manager_ops {
struct aml_arena *(*get_arena)(struct aml_area_linux_manager_data *data);
};
extern struct aml_area_linux_manager_ops aml_area_linux_manager_single_ops;
int aml_area_linux_manager_single_init(struct aml_area_linux_manager_data *,
struct aml_arena *);
int aml_area_linux_manager_single_destroy(struct aml_area_linux_manager_data *);
#define AML_MAX_NUMA_NODES 128
#define AML_NODEMASK_BYTES (AML_MAX_NUMA_NODES/8)
#define AML_NODEMASK_SZ (AML_NODEMASK_BYTES/sizeof(unsigned long))
struct aml_area_linux_mbind_data {
unsigned long nodemask[AML_NODEMASK_SZ];
int policy;
};
struct aml_area_linux_mbind_ops {
int (*pre_bind)(struct aml_area_linux_mbind_data *);
int (*post_bind)(struct aml_area_linux_mbind_data *, void *, size_t);
};
int aml_area_linux_mbind_setdata(struct aml_area_linux_mbind_data *, int,
unsigned long *);
int aml_area_linux_mbind_regular_pre_bind(struct aml_area_linux_mbind_data *);
int aml_area_linux_mbind_regular_post_bind(struct aml_area_linux_mbind_data *,
void *, size_t);
int aml_area_linux_mbind_mempolicy_pre_bind(struct aml_area_linux_mbind_data *);
int aml_area_linux_mbind_mempolicy_post_bind(struct aml_area_linux_mbind_data *,
void *, size_t);
int aml_area_linux_mbind_init(struct aml_area_linux_mbind_data *, int,
unsigned long *);
int aml_area_linux_mbind_destroy(struct aml_area_linux_mbind_data *);
extern struct aml_area_linux_mbind_ops aml_area_linux_mbind_regular_ops;
extern struct aml_area_linux_mbind_ops aml_area_linux_mbind_mempolicy_ops;
struct aml_area_linux_mmap_data {
int prot;
int flags;
int fildes;
off_t off;
};
struct aml_area_linux_mmap_ops {
void *(*mmap)(struct aml_area_linux_mmap_data *, void *, size_t);
};
void *aml_area_linux_mmap_generic(struct aml_area_linux_mmap_data *, void *,
size_t);
int aml_area_linux_mmap_anonymous_init(struct aml_area_linux_mmap_data *);
int aml_area_linux_mmap_fd_init(struct aml_area_linux_mmap_data *, int, size_t);
int aml_area_linux_mmap_tmpfile_init(struct aml_area_linux_mmap_data *, char *,
size_t);
int aml_area_linux_mmap_anonymous_destroy(struct aml_area_linux_mmap_data *);
int aml_area_linux_mmap_fd_destroy(struct aml_area_linux_mmap_data *);
int aml_area_linux_mmap_tmpfile_destroy(struct aml_area_linux_mmap_data *);
extern struct aml_area_linux_mmap_ops aml_area_linux_mmap_generic_ops;
struct aml_area_linux_data {
struct aml_area_linux_manager_data manager;
struct aml_area_linux_mbind_data mbind;
struct aml_area_linux_mmap_data mmap;
};
struct aml_area_linux_ops {
struct aml_area_linux_manager_ops manager;
struct aml_area_linux_mbind_ops mbind;
struct aml_area_linux_mmap_ops mmap;
};
struct aml_area_linux {
struct aml_area_linux_data data;
struct aml_area_linux_ops ops;
};
int aml_area_linux_init(struct aml_area_linux *);
int aml_area_linux_destroy(struct aml_area_linux *);
/*******************************************************************************
* Generic Area API:
* Low-level, direct access to area logic.
* For memory allocation function, follows the POSIX spec.
******************************************************************************/
void *aml_area_malloc(struct aml_area *, size_t);
......@@ -72,6 +201,8 @@ void *aml_area_calloc(struct aml_area *, size_t, size_t);
void *aml_area_realloc(struct aml_area *, void *, size_t);
void *aml_area_acquire(struct aml_area *, size_t);
void aml_area_release(struct aml_area *, void *);
void *aml_area_mmap(struct aml_area *, void *, size_t);
int aml_area_available(struct aml_area *);
/*******************************************************************************
* DMA Engines:
......@@ -92,6 +223,7 @@ int aml_dma_copy(struct aml_dma *, void *, const void *, size_t);
int aml_dma_move(struct aml_dma *, struct aml_area *, struct aml_area *,
void *, size_t);
/*******************************************************************************
* General functions:
* Initialize internal structures, cleanup everything at the end.
......
#include <aml.h>
#include <assert.h>
#include <sys/mman.h>
/*******************************************************************************
* Regular Area
* Handle memory allocation to DDR types of memory, no bindings whatsoever.
******************************************************************************/
int aml_area_regular_init(struct aml_area *area)
{
assert(area != NULL);
struct aml_arena *myarena = malloc(sizeof(struct aml_arena *));
assert(myarena != NULL);
area->extra = myarena;
return aml_arena_init(myarena, &aml_arena_jemalloc, area);
}
int aml_area_regular_destroy(struct aml_area *area)
{
assert(area != NULL);
assert(area->extra != NULL);
return aml_arena_destroy(area->extra);
}
struct aml_arena * aml_area_regular_get_arena(struct aml_area *area)
{
return area->extra;
}
void *aml_area_regular_mmap(struct aml_area *area, void *ptr, size_t sz)
{
return mmap(ptr, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0);
}
int aml_area_regular_mbind(struct aml_area *area, void *ptr, size_t sz)
{
return 0;
}
int aml_area_regular_available(struct aml_area *area)
{
return 1;
}
/*******************************************************************************
* Area Templates
* Area templates for typical types of areas.
******************************************************************************/
struct aml_area aml_area_hbm;
struct aml_area aml_area_regular = { aml_area_regular_init,
aml_area_regular_destroy,
aml_area_regular_get_arena,
aml_area_regular_mmap,
aml_area_regular_mbind,
aml_area_regular_available,
NULL};
/*******************************************************************************
* Area Generic functions
* Most of the stuff is dispatched to an arena, retrieved by type-specific
* Most of the stuff is dispatched to a different layer, using type-specific
* functions.
******************************************************************************/
int aml_area_init(struct aml_area *area, struct aml_area *template)
void *aml_area_malloc(struct aml_area *area, size_t size)
{
assert(area != NULL);
assert(template != NULL);
/* copy template ops to area, then initialize it. */
memcpy(area, template, sizeof(*area));
return template->init(area);
return area->ops->malloc(area->data, size);
}
int aml_area_destroy(struct aml_area *area)
void aml_area_free(struct aml_area *area, void *ptr)
{
assert(area != NULL);
return area->destroy(area);
area->ops->free(area->data, ptr);
}
void *aml_area_malloc(struct aml_area *area, size_t size)
void *aml_area_calloc(struct aml_area *area, size_t num, size_t size)
{
assert(area != NULL);
struct aml_arena *arena = area->get_arena(area);
assert(arena != NULL);
return arena->malloc(arena, size);
return area->ops->calloc(area->data, num, size);
}
void aml_area_free(struct aml_area *area, void *ptr)
void *aml_area_realloc(struct aml_area *area, void *ptr, size_t size)
{
assert(area != NULL);
struct aml_arena *arena = area->get_arena(area);
assert(arena != NULL);
arena->free(arena, ptr);
return area->ops->realloc(area->data, ptr, size);
}
void *aml_area_calloc(struct aml_area *area, size_t num, size_t size)
void *aml_area_acquire(struct aml_area *area, size_t size)
{
assert(area != NULL);
struct aml_arena *arena = area->get_arena(area);
assert(arena != NULL);
return arena->calloc(arena, num, size);
return area->ops->acquire(area->data, size);
}
void *aml_area_realloc(struct aml_area *area, void *ptr, size_t size)
void aml_area_release(struct aml_area *area, void *ptr)
{
assert(area != NULL);
struct aml_arena *arena = area->get_arena(area);
assert(arena != NULL);
return arena->realloc(arena, ptr, size);
area->ops->release(area->data, ptr);
}
void *aml_area_acquire(struct aml_area *area, size_t size)
void *aml_area_mmap(struct aml_area *area, void *ptr, size_t size)
{
assert(area != NULL);
struct aml_arena *arena = area->get_arena(area);
assert(arena != NULL);
return arena->acquire(arena,size);
return area->ops->mmap(area->data, ptr, size);
}
void aml_area_release(struct aml_area *area, void *ptr)
int aml_area_available(struct aml_area *area)
{
assert(area != NULL);
struct aml_arena *arena = area->get_arena(area);
assert(arena != NULL);
arena->release(arena, ptr);
return area->ops->available(area->data);
}
#include <aml.h>
#include <assert.h>
#include <sys/mman.h>
/*******************************************************************************
* Linux backed area:
* The area itself is organized into various two sets of data:
* - parameters that defines what policies are applied
* - methods that implement those policies
* This is designed similarly to component-entity-systems.
******************************************************************************/
/* two functions used by arenas to handle real allocations */
void *aml_area_linux_mmap(struct aml_area_data *a, void *ptr, size_t sz)
{
assert(a != NULL);
void *ret = NULL;
struct aml_area_linux *area = (struct aml_area_linux *)a;
area->ops.mbind.pre_bind(&area->data.mbind);
ret = area->ops.mmap.mmap(&area->data.mmap, ptr, sz);
area->ops.mbind.post_bind(&area->data.mbind, ret, sz);
return ret;
}
int aml_area_linux_available(struct aml_area_data *a)
{
return 1;
}
/*******************************************************************************
* Public API:
* The actual functions that will be called on the area from users
* Respect the POSIX spec for those functions.
******************************************************************************/
void *aml_area_linux_malloc(struct aml_area_data *a, size_t size)
{
assert(a != NULL);
struct aml_area_linux *area = (struct aml_area_linux *)a;
struct aml_arena *arena = (struct aml_arena *)
area->ops.manager.get_arena(&area->data.manager);
assert(arena != NULL);
if(size == 0)
return NULL;
return aml_arena_mallocx(arena, size, AML_ARENA_FLAG_ZERO);
}
void aml_area_linux_free(struct aml_area_data *a, void *ptr)
{
assert(a != NULL);
struct aml_area_linux *area = (struct aml_area_linux *)a;
struct aml_arena *arena = (struct aml_arena *)
area->ops.manager.get_arena(&area->data.manager);
assert(arena != NULL);
if(ptr == NULL)
return;
aml_arena_dallocx(arena, ptr, 0);
}
void *aml_area_linux_calloc(struct aml_area_data *a, size_t num, size_t size)
{
assert(a != NULL);
return aml_area_linux_malloc(a, num*size);
}
void *aml_area_linux_realloc(struct aml_area_data *a, void *ptr, size_t size)
{
assert(a != NULL);
struct aml_area_linux *area = (struct aml_area_linux *)a;
if(ptr == NULL)
return aml_area_linux_malloc(a, size);
struct aml_arena *arena = (struct aml_arena *)
area->ops.manager.get_arena(&area->data.manager);
assert(arena != NULL);
if(size == 0)
{
aml_arena_dallocx(arena, ptr, 0);
return NULL;
}
return aml_arena_reallocx(arena, ptr, size, 0);
}
void *aml_area_linux_acquire(struct aml_area_data *a, size_t size)
{
assert(a != NULL);
struct aml_area_linux *area = (struct aml_area_linux *)a;
struct aml_arena *arena = (struct aml_arena *)
area->ops.manager.get_arena(&area->data.manager);
assert(arena != NULL);
return aml_arena_mallocx(arena, size, 0);
}
void aml_area_linux_release(struct aml_area_data *a, void *ptr)
{
assert(a != NULL);
struct aml_area_linux *area = (struct aml_area_linux *)a;
struct aml_arena *arena = (struct aml_arena *)
area->ops.manager.get_arena(&area->data.manager);
assert(arena != NULL);
aml_arena_dallocx(arena, ptr, 0);
}
struct aml_area_ops aml_area_linux_ops = {
aml_area_linux_malloc,
aml_area_linux_free,
aml_area_linux_calloc,
aml_area_linux_realloc,
aml_area_linux_acquire,
aml_area_linux_release,
aml_area_linux_mmap,
aml_area_linux_available,
};
/*******************************************************************************
* Initialization/Destroy function:
* Collections of init/destroy functions for popular types of linux-based areas
******************************************************************************/
int aml_area_linux_init(struct aml_area_linux *area)
{
assert(area != NULL);
return 0;
}
int aml_area_linux_destroy(struct aml_area_linux *area)
{
assert(area != NULL);
return 0;
}
#include <assert.h>
#include <aml.h>
#include <sys/mman.h>
/*******************************************************************************
* Arena manager
* Uses the local context (tid for example) to decide which arena will be used
* for allocations.
******************************************************************************/
struct aml_arena * aml_area_linux_manager_single_get_arena(
struct aml_area_linux_manager_data *data)
{
return data->pool;
}
struct aml_area_linux_manager_ops aml_area_linux_manager_single_ops = {
aml_area_linux_manager_single_get_arena,
};
/*******************************************************************************
* Initialization/Destroy function:
******************************************************************************/
int aml_area_linux_manager_single_init(struct aml_area_linux_manager_data *data,
struct aml_arena *arena)
{
assert(data != NULL);
data->pool = arena;
data->pool_size = 1;
return 0;
}
int aml_area_linux_manager_single_destroy(
struct aml_area_linux_manager_data *data)
{
assert(data != NULL);
data->pool = NULL;
return 0;
}
#include <assert.h>
#include <aml.h>
#include <sys/mman.h>
#include <numaif.h>
/*******************************************************************************
* mbind methods for Linux systems
* Only handles the actual mbind/mempolicy calls
******************************************************************************/
int aml_area_linux_mbind_regular_pre_bind(struct aml_area_linux_mbind_data *data)
{
assert(data != NULL);
return 0;
}
int aml_area_linux_mbind_regular_post_bind(struct aml_area_linux_mbind_data *data,
void *ptr, size_t sz)
{
assert(data != NULL);
return mbind(ptr, sz, data->policy, data->nodemask, AML_MAX_NUMA_NODES, 0);
}
struct aml_area_linux_mbind_ops aml_area_linux_mbind_regular_ops = {
aml_area_linux_mbind_regular_pre_bind,
aml_area_linux_mbind_regular_post_bind,
};
int aml_area_linux_mbind_setdata(struct aml_area_linux_mbind_data *data,
int policy, unsigned long *nodemask)
{
assert(data != NULL);
data->policy = policy;
memcpy(data->nodemask, nodemask, AML_NODEMASK_BYTES);
return 0;
}
int aml_area_linux_mbind_mempolicy_pre_bind(struct aml_area_linux_mbind_data *data)
{
assert(data != NULL);
/* function is called before mmap, we save the "generic" mempolicy into
* our data, and apply the one the user actually want
*/
int policy;
unsigned long nodemask[AML_NODEMASK_SZ];
int err;
get_mempolicy(&policy, nodemask, AML_MAX_NUMA_NODES, NULL, 0);
err = set_mempolicy(data->policy, data->nodemask, AML_MAX_NUMA_NODES);
aml_area_linux_mbind_setdata(data, policy, nodemask);
return err;
}
int aml_area_linux_mbind_mempolicy_post_bind(struct aml_area_linux_mbind_data *data,
void *ptr, size_t sz)
{
assert(data != NULL);
/* function is called after mmap, we retrieve the mempolicy we applied
* to it, and restore the generic mempolicy we saved earlier.
*/
int policy;
unsigned long nodemask[AML_NODEMASK_SZ];
int err;
get_mempolicy(&policy, nodemask, AML_MAX_NUMA_NODES, NULL, 0);
err = set_mempolicy(data->policy, data->nodemask, AML_MAX_NUMA_NODES);
aml_area_linux_mbind_setdata(data, policy, nodemask);
return err;
}
struct aml_area_linux_mbind_ops aml_area_linux_mbind_mempolicy_ops = {
aml_area_linux_mbind_mempolicy_pre_bind,
aml_area_linux_mbind_mempolicy_post_bind,
};
int aml_area_linux_mbind_init(struct aml_area_linux_mbind_data *data,
int policy, unsigned long *nodemask)
{
assert(data != NULL);
aml_area_linux_mbind_setdata(data, policy, nodemask);
return 0;
}
int aml_area_linux_mbind_destroy(struct aml_area_linux_mbind_data *data)
{
assert(data != NULL);
return 0;
}
#include <assert.h>
#include <aml.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
/*******************************************************************************
* mmap methods for Linux systems
* Only handles the actual mmap call
******************************************************************************/
void *aml_area_linux_mmap_generic(struct aml_area_linux_mmap_data *data,
void *ptr, size_t sz)