nx_poolgov.nx source
↩ module page · 185 lines · 8531 B
1// nx_poolgov.nx -- THE POOL-WIDTH GOVERNOR, in the PRIMITIVE layer where production organs can reach it.
2//
3// WHY IT LIVES HERE (seq1450): I first built this in _hdl_build/ and wiring it into nx_torrent_get failed
4// with `nx_compile_x86: expand_imports failed`. The layering edge is DIRECTED and was undocumented:
5// _hdl_build/ organs MAY import runtime/ libs, but a runtime/ organ may NOT import _hdl_build/.
6// runtime/ = PRIMITIVE layer, _hdl_build/ = ORGAN/TOOL layer, and primitives may not depend on tools.
7// LAW: BUILD THE PRIMITIVE IN THE LAYER ITS CONSUMERS CAN REACH -- the import graph, not taste, decides.
8//
9// WHAT IT FIXES (seq1402/seq1410): nx_torrent_get.nx carried `let MAXP: i64 = 32` -- a hardcoded literal
10// (rule-11) that made every machine identical: NAS (4c/8t), west server, laptop, any future box all ran
11// 32 slots, sized for hardware nobody measured. And 32 was PER-INSTANCE: nx_procchurn measured 143
12// forks/sec = ~9 concurrent instances = ~288 concurrent slots on an 8-thread box. NOBODY CHOSE 143.
13// LAW: A PER-INSTANCE LIMIT IS NOT A LIMIT -- N instances multiply it and nobody ever picks N.
14//
15// WHAT IT DELIBERATELY DOES NOT DO: throttle the connection CYCLING. Constant cycling IS the BitTorrent
16// throughput strategy (continuous optimistic-unchokes vs fast-burst-then-freeze). Slowing it would attack
17// the feature to flatten a graph -- operator rule 25. Only WIDTH is governed; inside the budget the pool
18// stays exactly as aggressive as before.
19//
20// The load parse is duplicated from nx_ctxtop_lib BY LAYER NECESSITY, not by neglect -- the organ-layer
21// copy cannot be imported down here. Correct resolution: the organ layer should import THIS one (the
22// allowed direction) and its copy retire. Tracked, not pretended away.
23// license_tier: ORIGINAL Read-only. No hw writes (Rule 26).
24import "nx_syscalls.nx"
25import "nx_hw.nx"
26// dirent64 accessors inlined DELIBERATELY: nx_proc_ctl.nx lives ONLY in _hdl_build/ (the runtime/ copy is
27// .dupe-reconciled), and a primitive may not import the organ layer -- the same seq1450 rule, hit
28// TRANSITIVELY through a dependency rather than directly. Layout: d_ino(8) d_off(8) d_reclen(u16@16)
29// d_type(u8@18) d_name(@19).
30func pg_dreclen(rec: *u8) -> i64 { return (rec[16] as i64) | ((rec[17] as i64) << 8) }
31func pg_dname(rec: *u8) -> *u8 { return ((rec as i64) + 19) as *u8 }
32
33const PG_UNKNOWN_LOAD: i64 = 0 - 1
34const PG_DIRBUF: i64 = 65536
35const PG_PATHBUF: i64 = 256
36const PG_CMDBUF: i64 = 8192
37const PG_LOADBUF: i64 = 128
38const PG_SCALE: i64 = 100
39const PG_DOT: i64 = 46
40
41// ---- the machine we are ACTUALLY on ----
42// sched_getaffinity (not /proc/cpuinfo): returns the CPUs THIS PROCESS may run on, so it is correct under
43// cgroups / taskset / containers where cpuinfo reports host cores the process can never touch.
44func pg_budget_from_hw(slots_per_cpu: i64, floor: i64) -> i64 {
45 if floor < 1 { return 0 - 1 }
46 if slots_per_cpu < 1 { return floor }
47 let n: i64 = nx_hw_cpu_count()
48 if n < 1 { return floor }
49 let b: i64 = n * slots_per_cpu
50 if b < floor { return floor }
51 return b
52}
53
54func pg_load_centi() -> i64 {
55 let b: *u8 = sys_mmap(PG_LOADBUF)
56 let fd: i64 = sys_openat_rd("/proc/loadavg" as *u8)
57 if fd < 0 { sys_munmap(b, PG_LOADBUF); return PG_UNKNOWN_LOAD }
58 let n: i64 = sys_read(fd, b, PG_LOADBUF - 1)
59 sys_close(fd)
60 if n <= 0 { sys_munmap(b, PG_LOADBUF); return PG_UNKNOWN_LOAD }
61 var whole: i64 = 0
62 var seen: i64 = 0
63 var i: i64 = 0
64 while i < n {
65 let c: i64 = b[i] as i64
66 var d: i64 = 0
67 if c >= 48 { if c <= 57 { d = 1 } }
68 if d == 1 { whole = whole * 10 + (c - 48); seen = 1; i = i + 1 } else { i = n }
69 }
70 if seen == 0 { sys_munmap(b, PG_LOADBUF); return PG_UNKNOWN_LOAD }
71 var frac: i64 = 0
72 var got: i64 = 0
73 var q: i64 = 0
74 while q < n {
75 if b[q] == (PG_DOT as u8) {
76 var k: i64 = q + 1
77 while got < 2 {
78 if k >= n { got = 2 } else {
79 let e: i64 = b[k] as i64
80 var dd: i64 = 0
81 if e >= 48 { if e <= 57 { dd = 1 } }
82 if dd == 1 { frac = frac * 10 + (e - 48); got = got + 1 } else { got = 2 }
83 k = k + 1
84 }
85 }
86 q = n
87 } else { q = q + 1 }
88 }
89 sys_munmap(b, PG_LOADBUF)
90 return whole * PG_SCALE + frac
91}
92
93// How many processes share this budget RIGHT NOW. This is what makes the limit a limit.
94// ONE /proc walk, paid ONCE at pool construction -- never per request, never inside a loop.
95// Returns >=1 (this process counts), so a caller can always divide by it.
96func pg_count_procs(needle: *u8) -> i64 {
97 var nn: i64 = 0
98 while needle[nn] != (0 as u8) { nn = nn + 1 }
99 if nn == 0 { return 1 }
100 let dbuf: *u8 = sys_mmap(PG_DIRBUF)
101 let path: *u8 = sys_mmap(PG_PATHBUF)
102 let cbuf: *u8 = sys_mmap(PG_CMDBUF)
103 var cnt: i64 = 0
104 let fd: i64 = sys_openat_rd("/proc" as *u8)
105 if fd >= 0 {
106 var run: i64 = 1
107 while run == 1 {
108 let dn: i64 = sys_getdents64(fd, dbuf, PG_DIRBUF)
109 if dn <= 0 { run = 0 } else {
110 var off: i64 = 0
111 while off < dn {
112 let rec: *u8 = ((dbuf as i64) + off) as *u8
113 let reclen: i64 = pg_dreclen(rec)
114 if reclen <= 0 { off = dn } else {
115 let nm: *u8 = pg_dname(rec)
116 if nm[0] >= (48 as u8) { if nm[0] <= (57 as u8) {
117 var p: i64 = 0
118 let pre: *u8 = "/proc/" as *u8
119 var a: i64 = 0
120 while pre[a] != (0 as u8) { path[p] = pre[a]; p = p + 1; a = a + 1 }
121 a = 0
122 while nm[a] != (0 as u8) { path[p] = nm[a]; p = p + 1; a = a + 1 }
123 let suf: *u8 = "/cmdline" as *u8
124 a = 0
125 while suf[a] != (0 as u8) { path[p] = suf[a]; p = p + 1; a = a + 1 }
126 path[p] = 0 as u8
127 let cf: i64 = sys_openat_rd(path)
128 if cf >= 0 {
129 let cn: i64 = sys_read(cf, cbuf, PG_CMDBUF - 1)
130 sys_close(cf)
131 if cn > 0 {
132 var x: i64 = 0
133 var found: i64 = 0
134 while x + nn <= cn {
135 if found == 0 {
136 var j: i64 = 0
137 var m: i64 = 1
138 while j < nn { if cbuf[x + j] != needle[j] { m = 0; j = nn } else { j = j + 1 } }
139 if m == 1 { found = 1 }
140 }
141 x = x + 1
142 }
143 if found == 1 { cnt = cnt + 1 }
144 }
145 }
146 } }
147 off = off + reclen
148 }
149 }
150 }
151 }
152 sys_close(fd)
153 }
154 sys_munmap(dbuf, PG_DIRBUF)
155 sys_munmap(path, PG_PATHBUF)
156 sys_munmap(cbuf, PG_CMDBUF)
157 if cnt < 1 { return 1 }
158 return cnt
159}
160
161// Squeeze is PROPORTIONAL, not a cliff: above the ceiling width scales by max/load, so 2x the ceiling
162// halves the slots. A step function would oscillate pools between saturation and idle.
163// FAIL-SAFE: an unreadable load yields wmin -- not knowing the pressure is not permission to grab slots.
164func pg_width(total_budget: i64, n_instances: i64, load_centi: i64,
165 max_centi: i64, wmin: i64, wmax: i64) -> i64 {
166 if wmin < 1 { return 0 - 1 }
167 if wmax < wmin { return 0 - 1 }
168 if total_budget < 1 { return wmin }
169 if max_centi < 1 { return wmin }
170 if load_centi < 0 { return wmin }
171 var n: i64 = n_instances
172 if n < 1 { n = 1 }
173 var w: i64 = total_budget / n
174 if load_centi > max_centi { w = (w * max_centi) / load_centi }
175 if w < wmin { w = wmin }
176 if w > wmax { w = wmax }
177 return w
178}
179
180func pg_aggregate(width: i64, n_instances: i64) -> i64 {
181 if width < 0 { return 0 - 1 }
182 var n: i64 = n_instances
183 if n < 1 { n = 1 }
184 return width * n
185}