#include "../libnetdata.h"
#include "aral.h"
#ifdef NETDATA_TRACE_ALLOCATIONS
#define TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS , const char *file, const char *function, size_t line
#define TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS , file, function, line
#else
#define TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS
#define TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS
#endif
#define ARAL_FREE_PAGES_DELTA_TO_REARRANGE_LIST 5
// max file size
#define ARAL_MAX_PAGE_SIZE_MMAP (1*1024*1024*1024)
// max malloc size
// optimal at current versions of libc is up to 256k
// ideal to have the same overhead as libc is 4k
#define ARAL_MAX_PAGE_SIZE_MALLOC (65*1024)
typedef struct aral_free {
size_t size;
struct aral_free *next;
} ARAL_FREE;
typedef struct aral_page {
size_t size; // the allocation size of the page
const char *filename;
uint8_t *data;
uint32_t free_elements_to_move_first;
uint32_t max_elements; // the number of elements that can fit on this page
struct {
uint32_t used_elements; // the number of used elements on this page
uint32_t free_elements; // the number of free elements on this page
} aral_lock;
struct {
SPINLOCK spinlock;
ARAL_FREE *list;
} free;
struct aral_page *prev; // the prev page on the list
struct aral_page *next; // the next page on the list
} ARAL_PAGE;
struct aral {
struct {
char name[ARAL_MAX_NAME + 1];
bool lockless;
bool defragment;
size_t element_size; // calculated to take into account ARAL overheads
size_t max_allocation_size; // calculated in bytes
size_t page_ptr_offset; // calculated
size_t natural_page_size; // calculated
size_t requested_element_size;
size_t initial_page_elements;
size_t max_page_elements;
struct {
bool enabled;
const char *filename;
char **cache_dir;
} mmap;
} config;
struct {
SPINLOCK spinlock;
size_t file_number; // for mmap
struct aral_page *pages; // linked list of pages
size_t user_malloc_operations;
size_t user_free_operations;
size_t defragment_operations;
size_t defragment_linked_list_traversals;
} aral_lock;
struct {
SPINLOCK spinlock;
size_t allocation_size; // current allocation size
} adders;
struct {
} atomic;
};
struct {
struct {
struct {
size_t allocations;
size_t allocated;
} structures;
struct {
size_t allocations;
size_t allocated;
size_t used;
} malloc;
struct {
size_t allocations;
size_t allocated;
size_t used;
} mmap;
} atomic;
} aral_globals = {};
void aral_get_size_statistics(size_t *structures, size_t *malloc_allocated, size_t *malloc_used, size_t *mmap_allocated, size_t *mmap_used) {
*structures = __atomic_load_n(&aral_globals.atomic.structures.allocated, __ATOMIC_RELAXED);
*malloc_allocated = __atomic_load_n(&aral_globals.atomic.malloc.allocated, __ATOMIC_RELAXED);
*malloc_used = __atomic_load_n(&aral_globals.atomic.malloc.used, __ATOMIC_RELAXED);
*mmap_allocated = __atomic_load_n(&aral_globals.atomic.mmap.allocated, __ATOMIC_RELAXED);
*mmap_used = __atomic_load_n(&aral_globals.atomic.mmap.used, __ATOMIC_RELAXED);
}
#define ARAL_NATURAL_ALIGNMENT (sizeof(uintptr_t) * 2)
static inline size_t natural_alignment(size_t size, size_t alignment) {
if(unlikely(size % alignment))
size = size + alignment - (size % alignment);
return size;
}
static size_t aral_align_alloc_size(ARAL *ar, uint64_t size) {
if(size % ar->config.natural_page_size)
size += ar->config.natural_page_size - (size % ar->config.natural_page_size) ;
if(size % ar->config.element_size)
size -= size % ar->config.element_size;
return size;
}
static inline void aral_lock(ARAL *ar) {
if(likely(!ar->config.lockless))
netdata_spinlock_lock(&ar->aral_lock.spinlock);
}
static inline void aral_unlock(ARAL *ar) {
if(likely(!ar->config.lockless))
netdata_spinlock_unlock(&ar->aral_lock.spinlock);
}
static void aral_delete_leftover_files(const