code wiki / (root) / nx_ioadmit_lib.nx

nx_ioadmit_lib.nx

buildroot/runtime/nx_ioadmit_lib.nx

43663 B886 linesdepth 4pulls 5 transitivereach 61 importersview sourcekind library
docsdependenciesstructsconstsfunctions

about

nx_ioadmit_lib.nx -- THE I/O-STORM ADMISSION RULER, importable: ONE ruler for every process that is about to ADD uninterruptible I/O to a shared array (torrent workers, crawlers, bulk writers) -- the same witness nx_build_admit already uses to refuse a compiler fork, so a spawner and the build gate can never disagree about whether the box is in an I/O storm. WHY (measured 2026-08-19): the box sat at load 12-17 on 8 CPUs for days with procs_blocked=8, and 5 of the 8 blocked pids were nx_torrent_get workers -- the torrent daemon resumed EVERY active torrent at once (no budget, no resource awareness), and that alone pushed procs_blocked to ncpu, where the build admission's I/O-storm witness correctly refused every lane's builds. The cure is not a higher ceiling; it is that an I/O spawner asks the same question the build gate asks BEFORE it forks. THE RULER (unchanged from nx_build_admit.ba_verdict, 2026-08-17): procs_blocked >= per_cpu x ncpu is an I/O storm. ioa_spawn_budget answers "how many MORE blocked processes may I add and stay under that line, leaving reserve slots for everyone else" -- a number DERIVED from the box, never chosen. A third state is first-class: /proc/stat unreadable or truncated -> IOA_UNREADABLE, never a guess (the caller owns its fallback and must NAME it). license_tier: ORIGINAL No hw writes (Rule 26). lib (no main)

dependencies 3 imports · 16 importers

nx_syscalls.nx nx_resmon_lib.nx nx_itoa_lib.nx nx_ioadmit_lib.nx nx_blkprofile.nx nx_build_admit.nx nx_clock_driver_sched_ar.nx nx_clock_sched.nx nx_clock_sched_reserved_fault_v2_a nx_clock_sched_reserved_v2_ar.nx nx_compare_regen.nx nx_compare_regen_scoped.nx nx_compare_regen_t138.nx nx_heavyio_lib.nx

diagram shows first 10 each side; +0 more imports, +6 more importers in the complete lists below.

imports: nx_syscalls.nxnx_resmon_lib.nxnx_itoa_lib.nx

imported by: nx_blkprofile.nxnx_build_admit.nxnx_clock_driver_sched_ar.nxnx_clock_sched.nxnx_clock_sched_reserved_fault_v2_ar.nxnx_clock_sched_reserved_v2_ar.nxnx_compare_regen.nxnx_compare_regen_scoped.nxnx_compare_regen_t138.nxnx_heavyio_lib.nxnx_ioadmit_gate.nxnx_ioconfirm.nxnx_ioconfirm_checked_test_20260907.nxnx_memvel.nxnx_procchurn_lib.nxnx_torrent_daemon.nx

structs

none

consts

22const IOA_STAT: *u8 = "/proc/stat"
26const IOA_STAT_CAP: i64 = 65536
27const IOA_UNREADABLE: i64 = 0 - 1
31const IOA_BLOCKED_PER_CPU: i64 = 1
35const IOA_RESERVE_SLOTS: i64 = 1
38const IOA_COMM_MAX: i64 = 15
39const IOA_DIRBUF: i64 = 65536 // ONE getdents64 transfer; the walk loops until it returns 0
40const IOA_STATBUF: i64 = 1024 // one /proc/<pid>/stat line; comm sits inside the first 64 bytes
41const IOA_PATHBUF: i64 = 64 // "/proc/<pid>/stat"
42const IOA_RECLEN_OFF: i64 = 16 // linux_dirent64: d_reclen at 16, d_name at 19
43const IOA_NAME_OFF: i64 = 19
103const IOA_ROOTS_ONLY: i64 = 1
104const IOA_ALL_PROCS: i64 = 0
247const IOA_MEMINFO: *u8 = "/proc/meminfo"
248const IOA_MEMINFO_CAP: i64 = 65536
250const IOA_CONG_CLEAR: i64 = 0
251const IOA_CONG_RISING: i64 = 1
252const IOA_CONG_STORM: i64 = 2
253const IOA_CONG_UNREADABLE: i64 = 0 - 1
312const IOA_DISKSTATS: *u8 = "/proc/diskstats"
316const IOA_DISKSTATS_CAP: i64 = 262144
317const IOA_DS_SCRATCH_BYTES: i64 = 16
319const IOA_DS_FIELDS: i64 = 11
320const IOA_DS_SECTORS_READ: i64 = 2
321const IOA_DS_SECTORS_WRITTEN: i64 = 6
322const IOA_DS_IOS_IN_FLIGHT: i64 = 8
323const IOA_DS_MS_DOING_IO: i64 = 9
324const IOA_SECTOR_BYTES: i64 = 512
325const IOA_MS_PER_S: i64 = 1000
326const IOA_BYTES_PER_KB: i64 = 1024
328const IOA_CH_SPACE: i64 = 32
329const IOA_CH_NL: i64 = 10
330const IOA_CH_D0: i64 = 48
331const IOA_CH_D9: i64 = 57
488const IOA_MEDIAN_MAX_K: i64 = 15
489const IOA_MEDIAN_SLOTS: i64 = 128 // IOA_MEDIAN_MAX_K i64 slots with headroom
490const IOA_MEAS_SLOTS: i64 = 32 // the out[] ioa_measure fills
537const IOA_OWN_SLOTS: i64 = 64
538const IOA_STATE_OFF: i64 = 2 // "(comm) S ..." -- the state char sits 2 bytes past the closing paren
539const IOA_CH_D: i64 = 68 // 'D', uninterruptible sleep: the state admission counts
623const IOA_PERMIL: i64 = 1000
695const IOADM_GREEN: i64 = 0
696const IOADM_RED: i64 = 1
697const IOADM_AMBER: i64 = 2
698const IOADM_UNOBS: i64 = 3
699const IOADM_BUF: i64 = 65536
700const IOADM_NAME: i64 = 512
701const IOADM_PATH: i64 = 1024
702const IOADM_RECLEN_OFF: i64 = 16
703const IOADM_NAME_OFF: i64 = 19
704const IOADM_GETDENTS: i64 = 217
705const IOADM_OPENDIR: i64 = 0x10000
706const IOADM_AT_FDCWD: i64 = 0 - 100
707const IOADM_PLUS: i64 = 43
708const IOADM_DOT: i64 = 46
709const IOADM_LBRACK: i64 = 91
710const IOADM_SLASH: i64 = 47
711const IOADM_RBRACK: i64 = 93
712const IOADM_D0: i64 = 48
713const IOADM_D9: i64 = 57
714const IOADM_OUT_SLOTS: i64 = 5

functions

45func ioa_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
47func ioa_ncpu(buf: *u8, n: i64) -> i64
63func ioa_measure(out: *i64) -> i64
89func ioa_spawn_budget(ncpu: i64, procs_blk: i64, per_cpu: i64, reserve: i64) -> i64
108func ioa_stat_parse(sbuf: *u8, sn: i64, out: *i64) -> i64
142func ioa_read_pid_stat(pid: i64, sbuf: *u8, path: *u8) -> i64
159func ioa_comm_eq(sbuf: *u8, cs: i64, cl: i64, name: *u8, nl: i64) -> i64
called by 1: ioa_count_comm_mode
169func ioa_count_comm_mode(name: *u8, mode: i64) -> i64
225func ioa_count_comm(name: *u8) -> i64 { return ioa_count_comm_mode(name, IOA_ALL_PROCS) }
called by 2: hio_runningmain calls 1: ioa_count_comm_mode
226func ioa_count_comm_roots(name: *u8) -> i64 { return ioa_count_comm_mode(name, IOA_ROOTS_ONLY) }
261func ioa_dirty(out: *i64) -> i64
283func ioa_congestion(dirty_kb: i64, wb_kb: i64, warn_kb: i64, storm_kb: i64) -> i64
called by 1: main
295func ioa_cong_name(state: i64) -> *u8
334func ioa_ds_skip_sp(buf: *u8, n: i64, p: i64) -> i64
called by 1: ioa_ds_parse
346func ioa_ds_int(buf: *u8, n: i64, p: i64, out: *i64) -> i64
called by 1: ioa_ds_parse
366func ioa_ds_tok_eq(buf: *u8, n: i64, p: i64, name: *u8) -> i64
called by 1: ioa_ds_parse
392func ioa_ds_parse(buf: *u8, n: i64, dev: *u8, out: *i64) -> i64
444func ioa_diskstats(dev: *u8, out: *i64) -> i64
462func ioa_sector_rate_kbs(sectors_delta: i64, elapsed_ms: i64) -> i64
called by 1: main
493func ioa_sleep_ms(ms: i64) -> i64 { return sys_poll(0 as *u8, 0, ms) }
called by 2: mainioa_measure_median calls 1: sys_poll
501func ioa_median(vals: *i64, k: i64) -> i64
543func ioa_comm_starts(sbuf: *u8, cs: i64, cl: i64, name: *u8, nl: i64) -> i64
557func ioa_ownership_census(prefix: *u8, out: *i64) -> i64
627func ioa_share_permil(part: i64, whole: i64) -> i64
called by 1: main
633func ioa_measure_median(out: *i64, k: i64, gap_ms: i64) -> i64
716func ioadm_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
called by 1: ioadm_find
717func ioadm_cat(dst: *u8, a: *u8, b: *u8, c: *u8) -> i64
728func ioadm_read(path: *u8, buf: *u8, cap: i64) -> i64
743func ioadm_find(buf: *u8, n: i64, needle: *u8) -> i64
759func ioadm_int_after(buf: *u8, n: i64, key: *u8) -> i64
called by 1: ioa_dmcache calls 1: ioadm_find
779func ioadm_degraded(buf: *u8, n: i64) -> i64
called by 1: ioa_dmcache
802func ioadm_instance(root: *u8, out: *u8) -> i64
846func ioa_dmcache(root: *u8, out: *i64) -> i64