Skip to content

Commit cd8abc8

Browse files
committed
mem: add the missing Build section to README.hg_malloc_v3.md
Build/run had no coverage at all - Quickstart jumped straight into -m/-M flags assuming a binary already existed. New Section 1 covers: it's core not a module, why -DDBG_MALLOC must stay in Makefile.conf, that -a picks the allocator at runtime (opensips -V only shows what is compiled in), the optional hugetlb host prerequisites and their graceful degradation, and cross-compile notes. Every other numbered section shifts by one; all TOC entries and in-doc "Section N" cross-references updated to match.
1 parent 2525876 commit cd8abc8

1 file changed

Lines changed: 113 additions & 51 deletions

File tree

‎mem/README.hg_malloc_v3.md‎

Lines changed: 113 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ xychart-beta
5858
clock. Middle, stepped: what v3 keeps committed — granule steps up
5959
under load, shrink lagging the peak by the quiet window. Bottom: live
6060
demand. (Illustrative shapes; a measured cycle is charted in
61-
Section 8.)*
61+
Section 9.)*
6262

6363
v3 replaces both bets with a **range**: `-m 64:512 -M 16:32`. Start at
6464
the size you can defend, reserve the ceiling (address space — free),
@@ -78,30 +78,92 @@ mockups.
7878
**Contents**
7979

8080
0. [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

138200
The pkg profile governs **every worker's private arena individually**,
139201
so 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

142204
Watch 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

220282
The 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

246308
The arena tries four backings in order, each **attempted and then
247309
verified 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

281343
Two triggers, one mechanism.
282344

@@ -295,12 +357,12 @@ ticks.
295357

296358
The 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;
299361
2. `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;
304366
4. 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

377439
A **resource** refusal (host RAM, mlock limit — not an admin ceiling)
378440
should 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
498560
ordered walk would cost.
499561

500562
Floor: 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
502564
and `-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
505567
under MI-driven load, cooldown, the shrink train once the quiet window
506568
passes, regrowth on the next pulse — committed walking 32→96→16→80 MB
507569
while 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

523585
v3 reuses the worker autoscaler's grammar — same tokens, same shape
524586
your 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
```
576638
hg_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

734796
Two 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
736798
recipe untouched; the gateway was deliberately re-cut mid-soak to a
737799
near-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
846908
held; 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

852914
All 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

874936
1. **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

9701032
All on Linux 5.4 (the fleet's oldest) unless noted; every claim
9711033
re-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

Comments
 (0)