Skip to content

Commit 961b1e9

Browse files
committed
mem: HG_MALLOC - hugepage slab allocator with elastic, auto-scaling arenas
Combines the v1 allocator (OpenSIPS#4126: size-class slabs over a self-made hugepage reservation, per-process lock-free free caches, the CPU affinity subsystem that keeps them warm) with the v2 rewrite (in-page buddy grid, class-dedicated blocks, ownership checks on every header read, event-driven GC, corruption counters) and extends them with v3: arenas that grow and shrink at runtime between -m/-M INIT and a pre-fork reserved CAP. Growth commits whole 16 MB granules by mlock, bounded by three independent limits (profile target, backing-tier capacity, host-RAM floor with pkg deltas charged per worker); shrink releases whole-free top granules back to the kernel (MADV_REMOVE returns hugetlb pages to the pool). Policy reuses the auto_scaling_profile grammar via shm_auto_scaling_profile / pkg_auto_scaling_profile; a latched GROW-BLOCKED state raises E_CORE_SHM_GROW_BLOCKED; a dry-run mode narrates decisions without acting. With no cap configured the arena is fixed and behaviour is unchanged. Includes modules/hgstress (multi-process stamp/verify soak driver) and the full operator/design documentation under mem/.
1 parent d5d1760 commit 961b1e9

44 files changed

Lines changed: 10721 additions & 15 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎Makefile.conf.template‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#db_oracle= Provides Oracle connectivity for OpenSIPS. | Development library of OCI, typically instantclient-sdk-10.2.0.3
1919
#db_perlvdb= Provides a virtualization framework for OpenSIPS's database access. | Perl library development files, typically libperl-dev
2020
#db_postgres= Provides Postgres connectivity for OpenSIPS | PostgreSQL library and development library - typically libpq5 and libpq-dev
21+
#db_redis= Provides Redis (single instance or cluster) row storage for OpenSIPS | Redis client library, hiredis
2122
#db_sqlite= Provides SQLite connectivity for OpenSIPS | SQLite library and development library - typically libsqlite3 and libsqlite3-dev
2223
#db_unixodbc= Allows to use the unixodbc package with OpenSIPS | ODBC library and ODBC development library
2324
#dialplan= Implements generic string translations based on matching and replacement rules | PCRE development library, typically libpcre2-dev
@@ -76,7 +77,7 @@
7677
#uuid= UUID generator | uuid-dev
7778

7879
# Modules omitted from the default build, generally due to external dependencies.
79-
exclude_modules?= aaa_diameter aaa_radius auth_jwt auth_web3 b2b_logic_xml cachedb_cassandra cachedb_couchbase cachedb_dynamodb cachedb_memcached cachedb_mongodb cachedb_redis carrierroute cgrates compression cpl_c db_berkeley db_http db_mysql db_oracle db_perlvdb db_postgres db_sqlite db_unixodbc dialplan emergency event_rabbitmq event_kafka event_sqs h350 httpd http2d identity jabber json launch_darkly ldap lua mi_xmlrpc mmgeoip opentelemetry osp perl pi_http presence presence_dialoginfo presence_mwi presence_reginfo presence_xml presence_dfks proto_ipsec proto_sctp proto_tls proto_wss pua pua_bla pua_dialoginfo pua_mi pua_reginfo pua_usrloc pua_xmpp python regex rabbitmq_consumer rest_client rls rtp.io siprec sngtc snmpstats stir_shaken tls_mgm tls_openssl tls_wolfssl uuid xcap xcap_client xml xmpp
80+
exclude_modules?= aaa_diameter aaa_radius auth_jwt auth_web3 b2b_logic_xml cachedb_cassandra cachedb_couchbase cachedb_dynamodb cachedb_memcached cachedb_mongodb cachedb_redis carrierroute cgrates clusterer_controller compression cpl_c db_berkeley db_http db_mysql db_oracle db_perlvdb db_postgres db_redis db_sqlite db_unixodbc dialplan emergency event_kafka event_rabbitmq event_sqs h350 http2d httpd identity jabber json launch_darkly ldap lua mi_xmlrpc mmgeoip opentelemetry osp perl pi_http presence presence_dfks presence_dialoginfo presence_mwi presence_reginfo presence_xml proto_ipsec proto_sctp proto_tls proto_wss pua pua_bla pua_dialoginfo pua_mi pua_reginfo pua_usrloc pua_xmpp python rabbitmq_consumer regex rest_client rls rtp.io siprec sngtc snmpstats stir_shaken tls_mgm tls_openssl tls_wolfssl uuid xcap xcap_client xml xmpp
8081

8182
# Modules forced into the build, even when also listed in exclude_modules.
8283
include_modules?=
@@ -97,6 +98,17 @@ DEFS+= -DQ_MALLOC #Quality assurance memory allocator with runtime safety checks
9798
DEFS+= -DHP_MALLOC #High performance allocator with fine-grained locking
9899
DEFS+= -DDBG_MALLOC #Include additional, debug-enabled allocator flavors
99100
DEFS+= -DF_PARALLEL_MALLOC #Fast memory allocator with paralel buckets
101+
DEFS+= -DHG_MALLOC #Hugepage-backed slab allocator with a lock-free fast path
102+
# HG_MALLOC reserves its arena up front on 2 MB huge pages, pre-faults and
103+
# mlock()s it, and serves fixed size classes from per-thread caches so the
104+
# common path takes no lock. A block belongs to one size class while in use;
105+
# once it drains completely it returns to the class-agnostic buddy and can be
106+
# re-carved for a different class, so the carve shrinks again after a burst.
107+
# Because the arena is pinned, resident memory equals -m plus -M x processes
108+
# from the first second - unlike the mmap-backed allocators, oversizing is not
109+
# cheap. It needs a huge-page pool of at least (-m + -M x procs) / 2 pages and
110+
# a raised memlock limit; without them it drops to a lower tier and keeps
111+
# running. See mem/README.hg_malloc and mem/README.hg_arena_v2
100112
#DEFS+= -DQM_DBG_MALLOC_HIST=3 #Size of debug history for Q_MALLOC (default is 1)
101113
#DEFS+= -DNO_DEBUG #Compile out all debug messages
102114
#DEFS+= -DNO_LOG #Compile out all logging

‎Makefile.defs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,10 +524,12 @@ endif
524524
ifeq (,$(findstring F_MALLOC, $(DEFS)))
525525
ifeq (,$(findstring Q_MALLOC, $(DEFS)))
526526
ifeq (,$(findstring HP_MALLOC, $(DEFS)))
527+
ifeq (,$(findstring HG_MALLOC, $(DEFS)))
527528
DEFS+= -DQ_MALLOC
528529
endif
529530
endif
530531
endif
532+
endif
531533

532534
ifneq (,$(MEM_STATS_HDR))
533535
EXPECTED_MD5=$(shell [ -f $(MEM_STATS_HDR) ] && tail -n 1 $(MEM_STATS_HDR) | awk '{print $$4}')
@@ -558,6 +560,10 @@ MKTAGS=ctags -R .
558560
# decently fast allocator with extra sanity checks + safety buffers
559561
# -DHP_MALLOC
560562
# high performance allocator optimized for shared memory multiprocessing
563+
# -DHG_MALLOC
564+
# hugepage-backed slab allocator; fixed size classes bump-carved from
565+
# its own huge-page (or best-effort degraded) reservation, no fallback
566+
# to another allocator on exhaustion - increase -m/-M instead
561567
# -DDBG_MALLOC
562568
# will cause each of the enabled allocators to also include a "debugging"
563569
# flavor of itself; by running a debugging version of an allocator, you

‎cfg.lex‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,11 @@ RPM_MEM_FILE "restart_persistency_cache_file"
246246
RPM_MEM_SIZE "restart_persistency_size"
247247
MEMLOG "memlog"|"mem_log"
248248
MEMDUMP "memdump"|"mem_dump"
249+
PIN_WORKERS "pin_workers"|"cpu_pinning"
250+
PIN_UDP_CPUS "pin_udp_cpus"
251+
PIN_TCP_CPUS "pin_tcp_cpus"
252+
PIN_TIMER_CPUS "pin_timer_cpus"
253+
PIN_MODULE_CPUS "pin_module_cpus"
249254
SHM_MEMLOG_SIZE "shm_memlog_size"
250255
EXECMSGTHRESHOLD "execmsgthreshold"|"exec_msg_threshold"
251256
WORKER_REACTOR_TIMEOUT "workerreactortimeout"|"worker_reactor_timeout"
@@ -290,6 +295,10 @@ DB_DEFAULT_URL "db_default_url"
290295
DB_MAX_ASYNC_CONNECTIONS "db_max_async_connections"
291296
DISABLE_503_TRANSLATION "disable_503_translation"
292297
AUTO_SCALING_PROFILE "auto_scaling_profile"
298+
SHM_AUTO_SCALING_PROFILE "shm_auto_scaling_profile"
299+
PKG_AUTO_SCALING_PROFILE "pkg_auto_scaling_profile"
300+
HG_RAM_FLOOR_MB "hg_ram_floor_mb"
301+
HG_AUTOSCALE_DRY_RUN "hg_autoscale_dry_run"
293302
AUTO_SCALING_CYCLE "auto_scaling_cycle"
294303
TIMER_WORKERS "timer_workers"
295304

@@ -319,6 +328,7 @@ TICK \'
319328
SLASH "/"
320329
AS {EAT_ABLE}("as"|"AS"){EAT_ABLE}
321330
USE_WORKERS {EAT_ABLE}("use_workers"|"USE_WORKERS"){EAT_ABLE}
331+
PIN_CPUS {EAT_ABLE}("pin_cpus"|"PIN_CPUS"){EAT_ABLE}
322332
SOCK_TOS {EAT_ABLE}("tos"|"TOS"){EAT_ABLE}
323333
USE_AUTO_SCALING_PROFILE {EAT_ABLE}("use_auto_scaling_profile"|"USE_AUTO_SCALING_PROFILE"){EAT_ABLE}
324334
SCALE_UP_TO {EAT_ABLE}("scale"|"SCALE"){EAT_ABLE}+("up"|"UP"){EAT_ABLE}+("to"|"TO"){EAT_ABLE}
@@ -481,6 +491,11 @@ SPACE [ ]
481491
<INITIAL>{RPM_MEM_SIZE} { count(); yylval.strval=yytext; return RPM_MEM_SIZE; }
482492
<INITIAL>{MEMLOG} { count(); yylval.strval=yytext; return MEMLOG; }
483493
<INITIAL>{MEMDUMP} { count(); yylval.strval=yytext; return MEMDUMP; }
494+
<INITIAL>{PIN_WORKERS} { count(); yylval.strval=yytext; return PIN_WORKERS; }
495+
<INITIAL>{PIN_UDP_CPUS} { count(); yylval.strval=yytext; return PIN_UDP_CPUS; }
496+
<INITIAL>{PIN_TCP_CPUS} { count(); yylval.strval=yytext; return PIN_TCP_CPUS; }
497+
<INITIAL>{PIN_TIMER_CPUS} { count(); yylval.strval=yytext; return PIN_TIMER_CPUS; }
498+
<INITIAL>{PIN_MODULE_CPUS} { count(); yylval.strval=yytext; return PIN_MODULE_CPUS; }
484499
<INITIAL>{SHM_MEMLOG_SIZE} { count(); yylval.strval=yytext; return SHM_MEMLOG_SIZE; }
485500
<INITIAL>{EXECMSGTHRESHOLD} { count(); yylval.strval=yytext; return EXECMSGTHRESHOLD; }
486501
<INITIAL>{WORKER_REACTOR_TIMEOUT} { count(); yylval.strval=yytext; return WORKER_REACTOR_TIMEOUT; }
@@ -544,6 +559,14 @@ SPACE [ ]
544559
return DISABLE_503_TRANSLATION; }
545560
<INITIAL>{AUTO_SCALING_PROFILE} { count(); yylval.strval=yytext;
546561
return AUTO_SCALING_PROFILE; }
562+
<INITIAL>{SHM_AUTO_SCALING_PROFILE} { count(); yylval.strval=yytext;
563+
return SHM_AUTO_SCALING_PROFILE; }
564+
<INITIAL>{PKG_AUTO_SCALING_PROFILE} { count(); yylval.strval=yytext;
565+
return PKG_AUTO_SCALING_PROFILE; }
566+
<INITIAL>{HG_RAM_FLOOR_MB} { count(); yylval.strval=yytext;
567+
return HG_RAM_FLOOR_MB; }
568+
<INITIAL>{HG_AUTOSCALE_DRY_RUN} { count(); yylval.strval=yytext;
569+
return HG_AUTOSCALE_DRY_RUN; }
547570
<INITIAL>{AUTO_SCALING_CYCLE} { count(); yylval.strval=yytext;
548571
return AUTO_SCALING_CYCLE; }
549572
<INITIAL>{TIMER_WORKERS} { count(); yylval.strval=yytext;
@@ -603,6 +626,7 @@ SPACE [ ]
603626
<INITIAL>{COMMA} { count(); return COMMA; }
604627
<INITIAL>{SEMICOLON} { count(); return SEMICOLON; }
605628
<INITIAL>{USE_WORKERS} { count(); return USE_WORKERS; }
629+
<INITIAL>{PIN_CPUS} { count(); return PIN_CPUS; }
606630
<INITIAL>{SOCK_TOS} { count(); return SOCK_TOS; }
607631
<INITIAL>{USE_AUTO_SCALING_PROFILE} { count(); return USE_AUTO_SCALING_PROFILE; }
608632
<INITIAL>{COLON} { count(); return COLON; }

‎cfg.y‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ extern char *finame;
183183
struct listen_param {
184184
enum si_flags flags;
185185
int workers;
186+
char *pin_cpus;
186187
int tos;
187188
struct socket_id *socket;
188189
char *tag;
@@ -353,6 +354,11 @@ extern int cfg_parse_only_routes;
353354
%token RPM_MEM_SIZE
354355
%token MEMLOG
355356
%token MEMDUMP
357+
%token PIN_WORKERS
358+
%token PIN_UDP_CPUS
359+
%token PIN_TCP_CPUS
360+
%token PIN_TIMER_CPUS
361+
%token PIN_MODULE_CPUS
356362
%token SHM_MEMLOG_SIZE
357363
%token EXECMSGTHRESHOLD
358364
%token WORKER_REACTOR_TIMEOUT
@@ -407,6 +413,10 @@ extern int cfg_parse_only_routes;
407413
%token ASYNC_TOKEN
408414
%token LAUNCH_TOKEN
409415
%token AUTO_SCALING_PROFILE
416+
%token SHM_AUTO_SCALING_PROFILE
417+
%token PKG_AUTO_SCALING_PROFILE
418+
%token HG_RAM_FLOOR_MB
419+
%token HG_AUTOSCALE_DRY_RUN
410420
%token AUTO_SCALING_CYCLE
411421
%token TIMER_WORKERS
412422

@@ -461,6 +471,7 @@ extern int cfg_parse_only_routes;
461471
%token SLASH
462472
%token AS
463473
%token USE_WORKERS
474+
%token PIN_CPUS
464475
%token SOCK_TOS
465476
%token USE_AUTO_SCALING_PROFILE
466477
%token MAX
@@ -763,6 +774,9 @@ socket_def_param: ANYCAST { IFOR();
763774
| USE_WORKERS NUMBER { IFOR();
764775
p_tmp.workers=$2;
765776
}
777+
| PIN_CPUS STRING { IFOR();
778+
p_tmp.pin_cpus=$2;
779+
}
766780
| SOCK_TOS NUMBER { IFOR();
767781
p_tmp.tos=$2;
768782
}
@@ -1259,6 +1273,16 @@ assign_stm: LOGLEVEL EQUAL snumber { IFOR();
12591273
| SHM_MEMLOG_SIZE EQUAL error { yyerror("int value expected"); }
12601274
| MEMDUMP EQUAL snumber { IFOR(); memdump=$3; }
12611275
| MEMDUMP EQUAL error { yyerror("int value expected"); }
1276+
| PIN_WORKERS EQUAL snumber { IFOR(); pin_workers=$3; }
1277+
| PIN_WORKERS EQUAL error { yyerror("int value expected"); }
1278+
| PIN_UDP_CPUS EQUAL STRING { IFOR(); pin_udp_cpus=$3; pin_workers=1; }
1279+
| PIN_UDP_CPUS EQUAL error { yyerror("string value expected"); }
1280+
| PIN_TCP_CPUS EQUAL STRING { IFOR(); pin_tcp_cpus=$3; pin_workers=1; }
1281+
| PIN_TCP_CPUS EQUAL error { yyerror("string value expected"); }
1282+
| PIN_TIMER_CPUS EQUAL STRING { IFOR(); pin_timer_cpus=$3; pin_workers=1; }
1283+
| PIN_TIMER_CPUS EQUAL error { yyerror("string value expected"); }
1284+
| PIN_MODULE_CPUS EQUAL STRING { IFOR(); pin_module_cpus=$3; pin_workers=1; }
1285+
| PIN_MODULE_CPUS EQUAL error { yyerror("string value expected"); }
12621286
| EXECMSGTHRESHOLD EQUAL NUMBER { IFOR();execmsgthreshold=$3; }
12631287
| EXECMSGTHRESHOLD EQUAL error { yyerror("int value expected"); }
12641288
| WORKER_REACTOR_TIMEOUT EQUAL NUMBER { IFOR(); worker_reactor_timeout=$3; }
@@ -1644,6 +1668,26 @@ assign_stm: LOGLEVEL EQUAL snumber { IFOR();
16441668
| AUTO_SCALING_PROFILE EQUAL error {
16451669
yyerror("bad auto-scaling profile definition");
16461670
}
1671+
| SHM_AUTO_SCALING_PROFILE EQUAL ID { IFOR();
1672+
hg_shm_profile_name=$3; }
1673+
| SHM_AUTO_SCALING_PROFILE EQUAL error {
1674+
yyerror("profile name expected");
1675+
}
1676+
| PKG_AUTO_SCALING_PROFILE EQUAL ID { IFOR();
1677+
hg_pkg_profile_name=$3; }
1678+
| PKG_AUTO_SCALING_PROFILE EQUAL error {
1679+
yyerror("profile name expected");
1680+
}
1681+
| HG_RAM_FLOOR_MB EQUAL NUMBER { IFOR();
1682+
hg_ram_floor_mb=$3; }
1683+
| HG_RAM_FLOOR_MB EQUAL error {
1684+
yyerror("integer value expected");
1685+
}
1686+
| HG_AUTOSCALE_DRY_RUN EQUAL NUMBER { IFOR();
1687+
hg_autoscale_dry_run=$3; }
1688+
| HG_AUTOSCALE_DRY_RUN EQUAL error {
1689+
yyerror("integer value expected");
1690+
}
16471691
| AUTO_SCALING_CYCLE EQUAL NUMBER { IFOR();
16481692
auto_scaling_cycle=$3; }
16491693
| AUTO_SCALING_CYCLE EQUAL error {
@@ -2797,6 +2841,7 @@ static void fill_socket_id(struct listen_param *param, struct socket_id *s)
27972841
while (s) {
27982842
s->flags |= param->flags;
27992843
s->workers = param->workers;
2844+
s->pin_cpus = param->pin_cpus;
28002845
s->tos = param->tos;
28012846
s->auto_scaling_profile = param->auto_scaling_profile;
28022847
s->tag = param->tag;

0 commit comments

Comments
 (0)