@@ -58,7 +58,7 @@ xychart-beta
5858clock. Middle, stepped: what v3 keeps committed — granule steps up
5959under load, shrink lagging the peak by the quiet window. Bottom: live
6060demand. (Illustrative shapes; a measured cycle is charted in
61- Section 8 .)*
61+ Section 9 .)*
6262
6363v3 replaces both bets with a ** range** : ` -m 64:512 -M 16:32 ` . Start at
6464the size you can defend, reserve the ceiling (address space — free),
@@ -78,30 +78,92 @@ mockups.
7878** Contents**
7979
80800 . [ Why an elastic arena] ( #0-why-an-elastic-arena )
81- 1 . [ Quickstart] ( #1-quickstart )
82- 2 . [ Concepts and architecture] ( #2-concepts-and-architecture )
83- 3 . [ Why the cap is on the command line] ( #3-why-the-cap-is-on-the-command-line )
84- 4 . [ The memory tiers] ( #4-the-memory-tiers )
85- 5 . [ Growth] ( #5-growth )
86- 6 . [ The three-limit ceiling] ( #6-the-three-limit-ceiling )
87- 7 . [ GROW-BLOCKED — the alertable state] ( #7-grow-blocked--the-alertable-state )
88- 8 . [ Shrink] ( #8-shrink )
89- 9 . [ The profile — configuration reference] ( #9-the-profile--configuration-reference )
90- 10 . [ Dry-run mode] ( #10-dry-run-mode )
91- 11 . [ pkg arenas — what is different] ( #11-pkg-arenas--what-is-different )
92- 12 . [ Deployment cookbook] ( #12-deployment-cookbook )
93- 13 . [ Monitoring and alerting] ( #13-monitoring-and-alerting )
94- 14 . [ Log line reference] ( #14-log-line-reference )
95- 15 . [ Sizing rules that are not obvious] ( #15-sizing-rules-that-are-not-obvious )
96- 16 . [ Troubleshooting] ( #16-troubleshooting )
97- 17 . [ Testing — the rig and how to reproduce the proofs] ( #17-testing--the-rig-and-how-to-reproduce-the-proofs )
98- 18 . [ Appendix A — measured kernel facts] ( #18-appendix-a--measured-kernel-facts )
99- 19 . [ Appendix B — internals map for developers] ( #19-appendix-b--internals-map-for-developers )
100- 20 . [ Limitations] ( #20-limitations )
81+ 1 . [ Build] ( #1-build )
82+ 2 . [ Quickstart] ( #2-quickstart )
83+ 3 . [ Concepts and architecture] ( #3-concepts-and-architecture )
84+ 4 . [ Why the cap is on the command line] ( #4-why-the-cap-is-on-the-command-line )
85+ 5 . [ The memory tiers] ( #5-the-memory-tiers )
86+ 6 . [ Growth] ( #6-growth )
87+ 7 . [ The three-limit ceiling] ( #7-the-three-limit-ceiling )
88+ 8 . [ GROW-BLOCKED — the alertable state] ( #8-grow-blocked--the-alertable-state )
89+ 9 . [ Shrink] ( #9-shrink )
90+ 10 . [ The profile — configuration reference] ( #10-the-profile--configuration-reference )
91+ 11 . [ Dry-run mode] ( #11-dry-run-mode )
92+ 12 . [ pkg arenas — what is different] ( #12-pkg-arenas--what-is-different )
93+ 13 . [ Deployment cookbook] ( #13-deployment-cookbook )
94+ 14 . [ Monitoring and alerting] ( #14-monitoring-and-alerting )
95+ 15 . [ Log line reference] ( #15-log-line-reference )
96+ 16 . [ Sizing rules that are not obvious] ( #16-sizing-rules-that-are-not-obvious )
97+ 17 . [ Troubleshooting] ( #17-troubleshooting )
98+ 18 . [ Testing — the rig and how to reproduce the proofs] ( #18-testing--the-rig-and-how-to-reproduce-the-proofs )
99+ 19 . [ Appendix A — measured kernel facts] ( #19-appendix-a--measured-kernel-facts )
100+ 20 . [ Appendix B — internals map for developers] ( #20-appendix-b--internals-map-for-developers )
101+ 21 . [ Limitations] ( #21-limitations )
101102
102103---
103104
104- ## 1. Quickstart
105+ ## 1. Build
106+
107+ HG_MALLOC lives in core (` mem/ ` ), not a loadable module — there is no
108+ ` include_modules ` entry for it and no separate module build step. Any
109+ standard OpenSIPS build already contains it:
110+
111+ ``` bash
112+ make proper
113+ make -j$( nproc) install PREFIX=/opt/opensips-4.1.0-< rev>
114+ ```
115+
116+ ** Leave ` -DDBG_MALLOC ` in ` Makefile.conf ` 's ` DEFS ` ** (it ships on by
117+ default in the template). It does not turn on debug tracking for the
118+ allocator you run — it compiles in the * debug-instrumented flavours*
119+ (` HG_MALLOC_DBG ` , alongside ` F_MALLOC_DBG ` etc.) as additional runtime
120+ choices, selected the same way as the plain ones. Stripping it to "clean
121+ up" a build does not skip anything HG_MALLOC-specific; the tree fails to
122+ build without it, from an unrelated include-chain dependency several
123+ other core files have on it. Every build in this repo's CI carries it.
124+
125+ ** Build and runtime allocator selection are two different steps.**
126+ ` opensips -V ` lists every allocator * compiled in* — it says nothing about
127+ which one is running. Selection happens at process start, via `-a
128+ HG_MALLOC` (or ` -a HG_MALLOC_DBG` for the instrumented flavour), or
129+ ` MALLOC=HG_MALLOC ` in whatever env file your unit's ` ExecStart ` reads
130+ ` -a ` from. Confirm what actually started with the log line, not the
131+ build:
132+
133+ ```
134+ NOTICE:core:main: using 128 MB of shared memory, allocator: HG_MALLOC_V3
135+ ```
136+
137+ or ` opensips-cli -x mi core:hg_stats ` , which errors cleanly
138+ (` "HG_MALLOC is not the active allocator" ` ) if something else is running
139+ — a fixed-size hugetlb pool sitting at ` Free == Total ` after a restart is
140+ the same tell from the outside, if you'd rather check without MI.
141+
142+ ** Host prerequisites are all optional and the arena degrades gracefully
143+ without them** — this is the tier ladder in Section 5, not a build
144+ requirement. If you want the hugetlb tier specifically:
145+
146+ - Reserve ` vm.nr_hugepages ` sized to the ** cap** , not the init size — a
147+ grow event needs the whole reservation available in the pool the
148+ moment it fires, not just the starting commitment.
149+ - Set ` LimitMEMLOCK ` (systemd) or ` ulimit -l ` to at least the cap too,
150+ and remember pkg is ** per worker** : the real number is the pkg cap
151+ times worker count, added to the shm cap.
152+
153+ Skip both and HG_MALLOC starts on THP, then ` MADV_COLLAPSE ` , then plain
154+ 4 KB pages automatically — see Section 5 for what each tier costs.
155+
156+ ** Cross-compiling** needs nothing allocator-specific: HG_MALLOC is
157+ written against standard POSIX ` mmap ` /` mlock ` /` madvise ` , no
158+ architecture-specific code path. Verified via cross + qemu for
159+ arm32/arm64/i386/mips64, and full native-userland container builds for
160+ arm32/arm64 (Section 18).
161+
162+ Next: Section 2 for the smallest working ` -m ` /` -M ` /` -a ` invocation.
163+
164+ ---
165+
166+ ## 2. Quickstart
105167
106168``` bash
107169# 128 MB now, allowed to grow to 1 GB:
@@ -137,7 +199,7 @@ pkg_auto_scaling_profile = MEM_PKG
137199
138200The pkg profile governs ** every worker's private arena individually** ,
139201so its numbers are per-worker scale — an order of magnitude below the
140- shared arena's (Section 11 ).
202+ shared arena's (Section 12 ).
141203
142204Watch it work:
143205
@@ -147,7 +209,7 @@ opensips-cli -x mi core:hg_stats # committed / cap / grows / shrinks /
147209
148210---
149211
150- ## 2 . Concepts and architecture
212+ ## 3 . Concepts and architecture
151213
152214### 2.1 Committed vs reserved
153215
@@ -215,7 +277,7 @@ exists.
215277
216278---
217279
218- ## 3 . Why the cap is on the command line
280+ ## 4 . Why the cap is on the command line
219281
220282The shm arena is created ** before the config file is parsed**
221283(` init_shm_mallocs() ` runs before ` parse_opensips_cfg() ` in ` main() ` ),
@@ -241,7 +303,7 @@ it can only choose how the space inside it is used.
241303
242304---
243305
244- ## 4 . The memory tiers
306+ ## 5 . The memory tiers
245307
246308The arena tries four backings in order, each ** attempted and then
247309verified through ` /proc ` ** — never inferred from kernel version or
@@ -265,7 +327,7 @@ Two v3-specific rules:
265327 (measured: mapping 64 MB moved ` HugePages_Rsvd ` by exactly 32 pages
266328 before any fault). Consequences: tier-1 growth can never fail
267329 mid-flight — the pages are earmarked — and the pool must be sized for
268- the ** caps** (Section 15 ). If the pool cannot fit the cap, the arena
330+ the ** caps** (Section 16 ). If the pool cannot fit the cap, the arena
269331 falls back to a cap-less hugetlb reservation: it keeps huge pages,
270332 cannot grow, and says so.
271333
@@ -276,7 +338,7 @@ win; losing a delta to 4 K is a percent, not a disaster.
276338
277339---
278340
279- ## 5 . Growth
341+ ## 6 . Growth
280342
281343Two triggers, one mechanism.
282344
@@ -295,12 +357,12 @@ ticks.
295357
296358The commit itself (` hg_mem_commit() ` ):
297359
298- 1 . optional host-RAM check (Section 6 ) — before any work;
360+ 1 . optional host-RAM check (Section 7 ) — before any work;
2993612 . ` mlock() ` of the delta — chosen because it populates * exactly* that
300362 range, pins it, and reports failure through ` errno ` instead of
301363 letting a worker SIGBUS later on half-committed memory. Growth is
302364 ** refused, never degraded** ;
303- 3 . per-delta tier verification (Section 4 ), ` tier_bytes ` accounting;
365+ 3 . per-delta tier verification (Section 5 ), ` tier_bytes ` accounting;
3043664 . only then: ` hsize ` moves, pages are published, the reserve floor is
305367 recomputed, counters tick.
306368
@@ -313,7 +375,7 @@ Granule: 16 MB (huge-page rounded).
313375
314376---
315377
316- ## 6 . The three-limit ceiling
378+ ## 7 . The three-limit ceiling
317379
318380` min(admin, tier, host RAM) ` — each limb enforced where it is real:
319381
@@ -367,12 +429,12 @@ flowchart TD
367429 C --> W["publish pages to the buddy grid
368430 grows++, headroom NOTICE, cooldown armed"]
369431 R -- "resource refusals only" --> B["GROW-BLOCKED machinery
370- (Section 7 )"]
432+ (Section 8 )"]
371433```
372434
373435---
374436
375- ## 7 . GROW-BLOCKED — the alertable state
437+ ## 8 . GROW-BLOCKED — the alertable state
376438
377439A ** resource** refusal (host RAM, mlock limit — not an admin ceiling)
378440should page someone * only if it means something* . The state machine:
@@ -430,7 +492,7 @@ the log carries the fact.
430492
431493---
432494
433- ## 8 . Shrink
495+ ## 9 . Shrink
434496
435497### 8.1 Why it is safe with zero cross-process coordination
436498
@@ -498,10 +560,10 @@ concentration policy, and their lists are the long ones where an
498560ordered walk would cost.
499561
500562Floor: never below ` -m ` /` -M ` 's initial size — unless a profile says
501- otherwise (Section 9 ): with a profile attached, the profile is the ask
563+ otherwise (Section 10 ): with a profile attached, the profile is the ask
502564and ` -m ` is just the starting size.
503565
504- A full cycle, measured on a live process (rig arm K, Section 17 ): grow
566+ A full cycle, measured on a live process (rig arm K, Section 18 ): grow
505567under MI-driven load, cooldown, the shrink train once the quiet window
506568passes, regrowth on the next pulse — committed walking 32→96→16→80 MB
507569while the reservation never moves and the hugetlb pool gets every
@@ -518,7 +580,7 @@ xychart-beta
518580
519581---
520582
521- ## 9 . The profile — configuration reference
583+ ## 10 . The profile — configuration reference
522584
523585v3 reuses the worker autoscaler's grammar — same tokens, same shape
524586your configs already use for ` use_auto_scaling_profile ` :
@@ -570,7 +632,7 @@ NOTICE:core:hg_autoscale_apply: shm auto-scaling profile 'MEM_SHM': 32..160 MB (
570632
571633---
572634
573- ## 10 . Dry-run mode
635+ ## 11 . Dry-run mode
574636
575637```
576638hg_autoscale_dry_run = 1
@@ -596,7 +658,7 @@ tune thresholds against the advice lines, flip to 0.
596658
597659---
598660
599- ## 11 . pkg arenas — what is different
661+ ## 12 . pkg arenas — what is different
600662
601663* ** Per-child, post-config.** Each worker's private arena is created at
602664 fork time (after the config is parsed), so it takes the full policy:
@@ -642,7 +704,7 @@ word intact.
642704
643705---
644706
645- ## 12 . Deployment cookbook
707+ ## 13 . Deployment cookbook
646708
647709### 12.1 Billing gateway, hugetlb tier
648710
@@ -717,7 +779,7 @@ arena returns even part of the initial allocation.
717779
718780### 12.3 First rollout — dry-run
719781
720- Section 10 . Profile + ` hg_autoscale_dry_run = 1 ` , observe, tune, flip.
782+ Section 11 . Profile + ` hg_autoscale_dry_run = 1 ` , observe, tune, flip.
721783
722784### 12.4 Cap only, no profile
723785
@@ -732,7 +794,7 @@ surface at all.
732794### 12.5 The first soak, charted
733795
734796Two nodes, first 14 hours on v3 (2026-08-14/15), every point taken from
735- the arena's own grow/shrink NOTICE lines. The LB ran the section 12 .2
797+ the arena's own grow/shrink NOTICE lines. The LB ran the section 13 .2
736798recipe untouched; the gateway was deliberately re-cut mid-soak to a
737799near-empty start (` -m 8:512 -M 2:32 ` ) to make growth earn everything.
738800
@@ -776,7 +838,7 @@ corruption counters 0, tier-1 hugetlb throughout, and the
776838
777839---
778840
779- ## 13 . Monitoring and alerting
841+ ## 14 . Monitoring and alerting
780842
781843### 13.1 MI
782844
@@ -841,13 +903,13 @@ Suggested alerts:
841903
842904### 13.3 The event
843905
844- Section 7 — ` E_CORE_SHM_GROW_BLOCKED ` , params ` arena ` , ` committed_mb ` ,
906+ Section 8 — ` E_CORE_SHM_GROW_BLOCKED ` , params ` arena ` , ` committed_mb ` ,
845907` cap_mb ` , ` grow_refused ` ; raised on latch and every 5 minutes while
846908held; with no subscriber, a WARN says so and points at the gauge.
847909
848910---
849911
850- ## 14 . Log line reference
912+ ## 15 . Log line reference
851913
852914All at their exact severities; ` % ` values are illustrative.
853915
@@ -869,7 +931,7 @@ All at their exact severities; `%` values are illustrative.
869931
870932---
871933
872- ## 15 . Sizing rules that are not obvious
934+ ## 16 . Sizing rules that are not obvious
873935
8749361 . ** Tier 1: the pool must fit the caps.** The whole ` -m ` cap is
875937 reserved from the hugetlb pool at map time, and every per-child pkg
@@ -905,7 +967,7 @@ All at their exact severities; `%` values are illustrative.
905967
906968---
907969
908- ## 16 . Troubleshooting
970+ ## 17 . Troubleshooting
909971
910972| symptom | cause | fix |
911973| ---| ---| ---|
@@ -924,7 +986,7 @@ All at their exact severities; `%` values are illustrative.
924986
925987---
926988
927- ## 17 . Testing — the rig and how to reproduce the proofs
989+ ## 18 . Testing — the rig and how to reproduce the proofs
928990
929991### 17.1 hgstress
930992
@@ -965,7 +1027,7 @@ their output.
9651027
9661028---
9671029
968- ## 18 . Appendix A — measured kernel facts
1030+ ## 19 . Appendix A — measured kernel facts
9691031
9701032All on Linux 5.4 (the fleet's oldest) unless noted; every claim
9711033re-verified rather than assumed from documentation.
@@ -990,7 +1052,7 @@ re-verified rather than assumed from documentation.
9901052
9911053---
9921054
993- ## 19 . Appendix B — internals map for developers
1055+ ## 20 . Appendix B — internals map for developers
9941056
9951057| file | owns |
9961058| ---| ---|
@@ -1023,7 +1085,7 @@ Development notes that cost real time, recorded so they are paid once:
10231085
10241086---
10251087
1026- ## 20 . Limitations
1088+ ## 21 . Limitations
10271089
10281090* The ** pre-fork parent** pkg arena predates the config and stays fixed
10291091 at ` -M ` 's initial size; profiles govern every per-child arena.
0 commit comments