0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

Allocator parameters can be configured now.

This commit is contained in:
Maria Matejka 2024-10-01 10:24:04 +02:00
parent d27c2b6ec5
commit 0fc0e4618f
7 changed files with 104 additions and 25 deletions

View File

@ -722,6 +722,37 @@ include "tablename.conf";;
Evaluates given filter expression. It is used by the developers for testing of filters. Evaluates given filter expression. It is used by the developers for testing of filters.
</descrip> </descrip>
<sect>Global performance options
<label id="perf-opts">
<p>The internal scheduler and allocator can be tweaked if needed. You probably
don't want to do this, yet if you encounter some weird performance problem,
these knobs may be handy. For now, all the options are concentrated in the
<cf/memory {}/ block.
<descrip>
<tag><label id="memory-global-keep-hot">global keep hot <m/number/</tag>
How much memory is kept hot at most in the global memory storage.
Overflowing memory is returned back to the OS. The virtual memory,
aka address space, is kept in the cold storage and may be later reused,
to prevent address space fragmentation problems.
Aligned automatically to the system page size.
This knob must be higher than the following memory settings.
Default: 16777216.
<tag><label id="memory-local-keep-hot">local keep hot <m/number/</tag>
How much memory is kept hot at most in every thread-local memory storage.
Overflowing memory is moved to the global hot storage.
Aligned automatically to the system page size.
This knob must be higher than the following memory settings.
Default: 524288.
<tag><label id="memory-local-keep-hot">allocate block <m/number/</tag>
How much memory is allocated at once when no more memory is neither
in available hot nor cold storages.
Aligned automatically to the system page size.
Default: 131072.
</descrip>
<sect>Routing table options <sect>Routing table options
<label id="rtable-opts"> <label id="rtable-opts">

View File

@ -146,16 +146,19 @@ void buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_siz
/* Allocator of whole pages; for use in slabs and other high-level allocators. */ /* Allocator of whole pages; for use in slabs and other high-level allocators. */
#define PAGE_HEAD(x) ((void *) (((uintptr_t) (x)) & ~(page_size-1))) #define PAGE_HEAD(x) ((void *) (((uintptr_t) (x)) & ~(page_size-1)))
extern long page_size; extern long page_size;
extern _Atomic int pages_kept; extern _Atomic uint pages_kept;
extern _Atomic int pages_kept_locally; extern _Atomic uint pages_kept_locally;
extern _Atomic int pages_kept_cold; extern _Atomic uint pages_kept_cold;
extern _Atomic int pages_kept_cold_index; extern _Atomic uint pages_kept_cold_index;
void *alloc_page(void); void *alloc_page(void);
void free_page(void *); void free_page(void *);
void flush_local_pages(void); void flush_local_pages(void);
void resource_sys_init(void); void resource_sys_init(void);
struct alloc_config;
void alloc_preconfig(struct alloc_config *);
#ifdef HAVE_LIBDMALLOC #ifdef HAVE_LIBDMALLOC
/* /*
* The standard dmalloc macros tend to produce lots of namespace * The standard dmalloc macros tend to produce lots of namespace

View File

@ -24,6 +24,12 @@ enum latency_debug_flags {
DL_TIMERS = 0x40, DL_TIMERS = 0x40,
}; };
struct alloc_config {
uint keep_mem_max_global; /* How much free memory is kept hot in total */
uint keep_mem_max_local; /* How much free memory is kept hot in every thread */
uint at_once; /* How much memory to allocate at once */
};
#define GLOBAL_RUNTIME_CONTENTS \ #define GLOBAL_RUNTIME_CONTENTS \
struct timeformat tf_log; /* Time format for the logfile */ \ struct timeformat tf_log; /* Time format for the logfile */ \
struct timeformat tf_base; /* Time format for other purposes */ \ struct timeformat tf_base; /* Time format for other purposes */ \
@ -32,6 +38,7 @@ enum latency_debug_flags {
u32 latency_limit; /* Events with longer duration are logged (us) */ \ u32 latency_limit; /* Events with longer duration are logged (us) */ \
u32 watchdog_warning; /* I/O loop watchdog limit for warning (us) */ \ u32 watchdog_warning; /* I/O loop watchdog limit for warning (us) */ \
const char *hostname; /* Hostname */ \ const char *hostname; /* Hostname */ \
struct alloc_config alloc; /* Allocation settings */ \
struct global_runtime { GLOBAL_RUNTIME_CONTENTS }; struct global_runtime { GLOBAL_RUNTIME_CONTENTS };
extern struct global_runtime * _Atomic global_runtime; extern struct global_runtime * _Atomic global_runtime;

View File

@ -32,13 +32,19 @@
long page_size = 0; long page_size = 0;
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
# define KEEP_PAGES_MAX 16384
# define KEEP_PAGES_MIN 32
# define KEEP_PAGES_MAX_LOCAL 128
# define ALLOC_PAGES_AT_ONCE 32
STATIC_ASSERT(KEEP_PAGES_MIN * 4 < KEEP_PAGES_MAX); void
STATIC_ASSERT(ALLOC_PAGES_AT_ONCE < KEEP_PAGES_MAX_LOCAL); alloc_preconfig(struct alloc_config *ac)
{
ac->keep_mem_max_global = 16777216;
ac->keep_mem_max_local = 524288;
ac->at_once = 131072;
}
# define ALLOC_INFO (&(atomic_load_explicit(&global_runtime, memory_order_relaxed)->alloc))
# define KEEP_MEM_MAX ALLOC_INFO->keep_mem_max_global
# define KEEP_MEM_MAX_LOCAL ALLOC_INFO->keep_mem_max_local
# define ALLOC_MEM_AT_ONCE ALLOC_INFO->at_once
static bool use_fake = 0; static bool use_fake = 0;
static bool initialized = 0; static bool initialized = 0;
@ -124,8 +130,8 @@ long page_size = 0;
static DOMAIN(resource) empty_pages_domain; static DOMAIN(resource) empty_pages_domain;
static struct empty_pages *empty_pages = NULL; static struct empty_pages *empty_pages = NULL;
_Atomic int pages_kept_cold = 0; _Atomic uint pages_kept_cold = 0;
_Atomic int pages_kept_cold_index = 0; _Atomic uint pages_kept_cold_index = 0;
static struct free_page * _Atomic page_stack = NULL; static struct free_page * _Atomic page_stack = NULL;
static _Thread_local struct free_page * local_page_stack = NULL; static _Thread_local struct free_page * local_page_stack = NULL;
@ -143,14 +149,14 @@ long page_size = 0;
static event page_cleanup_event = { .hook = page_cleanup, }; static event page_cleanup_event = { .hook = page_cleanup, };
# define SCHEDULE_CLEANUP do if (initialized && !shutting_down) ev_send(&global_event_list, &page_cleanup_event); while (0) # define SCHEDULE_CLEANUP do if (initialized && !shutting_down) ev_send(&global_event_list, &page_cleanup_event); while (0)
_Atomic int pages_kept = 0; _Atomic uint pages_kept = 0;
_Atomic int pages_kept_locally = 0; _Atomic uint pages_kept_locally = 0;
static _Thread_local int pages_kept_here = 0; static _Thread_local uint pages_kept_here = 0;
static void * static void *
alloc_sys_page(void) alloc_sys_page(void)
{ {
void *ptr = mmap(NULL, page_size * ALLOC_PAGES_AT_ONCE, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); void *ptr = mmap(NULL, ALLOC_MEM_AT_ONCE, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED) if (ptr == MAP_FAILED)
die("mmap(%ld) failed: %m", (s64) page_size); die("mmap(%ld) failed: %m", (s64) page_size);
@ -258,8 +264,8 @@ alloc_page(void)
void *ptr = alloc_sys_page(); void *ptr = alloc_sys_page();
ajlog(ptr, NULL, 0, AJT_ALLOC_MMAP); ajlog(ptr, NULL, 0, AJT_ALLOC_MMAP);
for (int i=1; i<ALLOC_PAGES_AT_ONCE; i++) for (unsigned long skip = page_size; skip<ALLOC_MEM_AT_ONCE; skip += page_size)
free_page(ptr + page_size * i); free_page(ptr + skip);
return ptr; return ptr;
#endif #endif
@ -278,7 +284,7 @@ free_page(void *ptr)
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
/* We primarily try to keep the pages locally. */ /* We primarily try to keep the pages locally. */
struct free_page *fp = ptr; struct free_page *fp = ptr;
if (pages_kept_here < KEEP_PAGES_MAX_LOCAL) if (pages_kept_here * page_size < KEEP_MEM_MAX_LOCAL)
{ {
struct free_page *next = local_page_stack; struct free_page *next = local_page_stack;
atomic_store_explicit(&fp->next, next, memory_order_relaxed); atomic_store_explicit(&fp->next, next, memory_order_relaxed);
@ -304,7 +310,7 @@ free_page(void *ptr)
ajlog(fp, next, pk, AJT_FREE_GLOBAL_HOT); ajlog(fp, next, pk, AJT_FREE_GLOBAL_HOT);
/* And if there are too many global hot free pages, we ask for page cleanup */ /* And if there are too many global hot free pages, we ask for page cleanup */
if (pk >= KEEP_PAGES_MAX) if (pk * page_size >= KEEP_MEM_MAX)
SCHEDULE_CLEANUP; SCHEDULE_CLEANUP;
#endif #endif
} }
@ -322,7 +328,7 @@ flush_local_pages(void)
/* We first count the pages to enable consistency checking. /* We first count the pages to enable consistency checking.
* Also, we need to know the last page. */ * Also, we need to know the last page. */
struct free_page *last = local_page_stack, *next; struct free_page *last = local_page_stack, *next;
int check_count = 1; uint check_count = 1;
while (next = atomic_load_explicit(&last->next, memory_order_relaxed)) while (next = atomic_load_explicit(&last->next, memory_order_relaxed))
{ {
check_count++; check_count++;
@ -348,7 +354,7 @@ flush_local_pages(void)
/* Check the state of global page cache and maybe schedule its cleanup. */ /* Check the state of global page cache and maybe schedule its cleanup. */
atomic_fetch_sub_explicit(&pages_kept_locally, check_count, memory_order_relaxed); atomic_fetch_sub_explicit(&pages_kept_locally, check_count, memory_order_relaxed);
if (atomic_fetch_add_explicit(&pages_kept, check_count, memory_order_relaxed) >= KEEP_PAGES_MAX) if (atomic_fetch_add_explicit(&pages_kept, check_count, memory_order_relaxed) * page_size >= KEEP_MEM_MAX)
SCHEDULE_CLEANUP; SCHEDULE_CLEANUP;
} }
@ -362,7 +368,7 @@ page_cleanup(void *_ UNUSED)
/* Pages allocated inbetween */ /* Pages allocated inbetween */
uint pk = atomic_load_explicit(&pages_kept, memory_order_relaxed); uint pk = atomic_load_explicit(&pages_kept, memory_order_relaxed);
if (pk < KEEP_PAGES_MAX) if (pk * page_size < KEEP_MEM_MAX)
return; return;
/* Walk the pages */ /* Walk the pages */
@ -420,7 +426,7 @@ page_cleanup(void *_ UNUSED)
UNLOCK_DOMAIN(resource, empty_pages_domain); UNLOCK_DOMAIN(resource, empty_pages_domain);
count++; count++;
} }
while (atomic_fetch_sub_explicit(&pages_kept, 1, memory_order_relaxed) >= KEEP_PAGES_MAX / 2); while (atomic_fetch_sub_explicit(&pages_kept, 1, memory_order_relaxed) * page_size >= KEEP_MEM_MAX / 2);
ALLOC_TRACE("Moved %u pages to cold storage, now %u cold, %u index", count, ALLOC_TRACE("Moved %u pages to cold storage, now %u cold, %u index", count,
atomic_load_explicit(&pages_kept_cold, memory_order_relaxed), atomic_load_explicit(&pages_kept_cold, memory_order_relaxed),
@ -450,9 +456,12 @@ resource_sys_init(void)
/* We assume that page size has only one bit and is between 1K and 256K (incl.). /* We assume that page size has only one bit and is between 1K and 256K (incl.).
* Otherwise, the assumptions in lib/slab.c (sl_head's num_full range) aren't met. */ * Otherwise, the assumptions in lib/slab.c (sl_head's num_full range) aren't met. */
alloc_preconfig(&(atomic_load_explicit(&global_runtime, memory_order_relaxed)->alloc));
empty_pages_domain = DOMAIN_NEW(resource); empty_pages_domain = DOMAIN_NEW(resource);
DOMAIN_SETUP(resource, empty_pages_domain, "Empty Pages", NULL); DOMAIN_SETUP(resource, empty_pages_domain, "Empty Pages", NULL);
initialized = 1; initialized = 1;
return; return;
} }
@ -462,5 +471,7 @@ resource_sys_init(void)
#endif #endif
page_size = 4096; page_size = 4096;
alloc_preconfig(&(atomic_load_explicit(&global_runtime, memory_order_relaxed)->alloc));
initialized = 1; initialized = 1;
} }

View File

@ -21,6 +21,7 @@ CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH,
CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, STATUS) CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, STATUS)
CF_KEYWORDS(PING, WAKEUP, SOCKETS, SCHEDULING, EVENTS, TIMERS, ALLOCATOR) CF_KEYWORDS(PING, WAKEUP, SOCKETS, SCHEDULING, EVENTS, TIMERS, ALLOCATOR)
CF_KEYWORDS(GRACEFUL, RESTART, FIXED) CF_KEYWORDS(GRACEFUL, RESTART, FIXED)
CF_KEYWORDS(MEMORY, GLOBAL, LOCAL, KEEP, HOT, ALLOCATE, BLOCK)
%type <i> log_mask log_mask_list log_cat cfg_timeout debug_unix latency_debug_mask latency_debug_flag latency_debug_list %type <i> log_mask log_mask_list log_cat cfg_timeout debug_unix latency_debug_mask latency_debug_flag latency_debug_list
%type <t> cfg_name %type <t> cfg_name
@ -156,6 +157,28 @@ latency_debug_flag:
| TIMERS { $$ = DL_TIMERS; } | TIMERS { $$ = DL_TIMERS; }
; ;
conf: MEMORY '{' memory_items '}'
{
if (new_config->runtime.alloc.keep_mem_max_global <= new_config->runtime.alloc.keep_mem_max_local)
cf_error("Global (%u) hot memory limit must be higher than local (%u)",
new_config->runtime.alloc.keep_mem_max_global,
new_config->runtime.alloc.keep_mem_max_local);
if (new_config->runtime.alloc.keep_mem_max_local < new_config->runtime.alloc.at_once)
cf_error("Can't allocate more memory at once (%u) than local hot limit (%u)",
new_config->runtime.alloc.at_once,
new_config->runtime.alloc.keep_mem_max_local);
}
memory_items:
| memory_items GLOBAL KEEP HOT NUM ';' {
new_config->runtime.alloc.keep_mem_max_global = BIRD_ALIGN($5, page_size); }
| memory_items LOCAL KEEP HOT NUM ';' {
new_config->runtime.alloc.keep_mem_max_local = BIRD_ALIGN($5, page_size); }
| memory_items ALLOCATE BLOCK NUM ';' {
new_config->runtime.alloc.at_once = BIRD_ALIGN($4, page_size); }
;
/* Unix specific commands */ /* Unix specific commands */

View File

@ -194,6 +194,8 @@ sysdep_preconfig(struct config *c)
c->runtime.latency_limit = UNIX_DEFAULT_LATENCY_LIMIT; c->runtime.latency_limit = UNIX_DEFAULT_LATENCY_LIMIT;
c->runtime.watchdog_warning = UNIX_DEFAULT_WATCHDOG_WARNING; c->runtime.watchdog_warning = UNIX_DEFAULT_WATCHDOG_WARNING;
alloc_preconfig(&c->runtime.alloc);
#ifdef PATH_IPROUTE_DIR #ifdef PATH_IPROUTE_DIR
read_iproute_table(c, PATH_IPROUTE_DIR "/rt_protos", "ipp_", 255); read_iproute_table(c, PATH_IPROUTE_DIR "/rt_protos", "ipp_", 255);
read_iproute_table(c, PATH_IPROUTE_DIR "/rt_realms", "ipr_", 0xffffffff); read_iproute_table(c, PATH_IPROUTE_DIR "/rt_realms", "ipr_", 0xffffffff);

View File

@ -557,7 +557,9 @@ void cmd_reconfig_undo_notify(void) {}
#include "nest/bird.h" #include "nest/bird.h"
#include "lib/net.h" #include "lib/net.h"
#include "conf/conf.h" #include "conf/conf.h"
void sysdep_preconfig(struct config *c UNUSED) {} void sysdep_preconfig(struct config *c) {
alloc_preconfig(&c->runtime.alloc);
}
void bird_thread_commit(struct thread_config *new); void bird_thread_commit(struct thread_config *new);
void sysdep_commit(struct config *new, struct config *old UNUSED) void sysdep_commit(struct config *new, struct config *old UNUSED)