nx_ioae_lib_test_20260907.nx
buildroot/runtime/nx_ioae_lib_test_20260907.nx
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 · 4 importers
imports: nx_syscalls.nxnx_resmon_lib.nxnx_itoa_lib.nx
imported by: nx_ioae_confread_test_20260907.nxnx_ioae_confread_v2_20260907.nxnx_ioae_wiring_test_20260907.nxnx_ioae_wiring_v2_20260907.nx
structs
| none |
consts
| 22 | const IOA_STAT: *u8 = "/proc/stat" |
| 26 | const IOA_STAT_CAP: i64 = 65536 |
| 27 | const IOA_UNREADABLE: i64 = 0 - 1 |
| 31 | const IOA_BLOCKED_PER_CPU: i64 = 1 |
| 35 | const IOA_RESERVE_SLOTS: i64 = 1 |
| 38 | const IOA_COMM_MAX: i64 = 15 |
| 39 | const IOA_DIRBUF: i64 = 65536 // ONE getdents64 transfer; the walk loops until it returns 0 |
| 40 | const IOA_STATBUF: i64 = 1024 // one /proc/<pid>/stat line; comm sits inside the first 64 bytes |
| 41 | const IOA_PATHBUF: i64 = 64 // "/proc/<pid>/stat" |
| 42 | const IOA_RECLEN_OFF: i64 = 16 // linux_dirent64: d_reclen at 16, d_name at 19 |
| 43 | const IOA_NAME_OFF: i64 = 19 |
| 103 | const IOA_ROOTS_ONLY: i64 = 1 |
| 104 | const IOA_ALL_PROCS: i64 = 0 |
| 247 | const IOA_MEMINFO: *u8 = "/proc/meminfo" |
| 248 | const IOA_MEMINFO_CAP: i64 = 65536 |
| 250 | const IOA_CONG_CLEAR: i64 = 0 |
| 251 | const IOA_CONG_RISING: i64 = 1 |
| 252 | const IOA_CONG_STORM: i64 = 2 |
| 253 | const IOA_CONG_UNREADABLE: i64 = 0 - 1 |
| 312 | const IOA_DISKSTATS: *u8 = "/proc/diskstats" |
| 316 | const IOA_DISKSTATS_CAP: i64 = 262144 |
| 317 | const IOA_DS_SCRATCH_BYTES: i64 = 16 |
| 319 | const IOA_DS_FIELDS: i64 = 11 |
| 320 | const IOA_DS_SECTORS_READ: i64 = 2 |
| 321 | const IOA_DS_SECTORS_WRITTEN: i64 = 6 |
| 322 | const IOA_DS_IOS_IN_FLIGHT: i64 = 8 |
| 323 | const IOA_DS_MS_DOING_IO: i64 = 9 |
| 324 | const IOA_SECTOR_BYTES: i64 = 512 |
| 325 | const IOA_MS_PER_S: i64 = 1000 |
| 326 | const IOA_BYTES_PER_KB: i64 = 1024 |
| 328 | const IOA_CH_SPACE: i64 = 32 |
| 329 | const IOA_CH_NL: i64 = 10 |
| 330 | const IOA_CH_D0: i64 = 48 |
| 331 | const IOA_CH_D9: i64 = 57 |
| 488 | const IOA_MEDIAN_MAX_K: i64 = 15 |
| 489 | const IOA_MEDIAN_SLOTS: i64 = 128 // IOA_MEDIAN_MAX_K i64 slots with headroom |
| 490 | const IOA_MEAS_SLOTS: i64 = 32 // the out[] ioa_measure fills |
| 537 | const IOA_OWN_SLOTS: i64 = 64 |
| 538 | const IOA_STATE_OFF: i64 = 2 // "(comm) S ..." -- the state char sits 2 bytes past the closing paren |
| 539 | const IOA_CH_D: i64 = 68 // 'D', uninterruptible sleep: the state admission counts |
| 623 | const IOA_PERMIL: i64 = 1000 |
| 695 | const IOADM_GREEN: i64 = 0 |
| 696 | const IOADM_RED: i64 = 1 |
| 697 | const IOADM_AMBER: i64 = 2 |
| 698 | const IOADM_UNOBS: i64 = 3 |
| 699 | const IOADM_BUF: i64 = 65536 |
| 700 | const IOADM_NAME: i64 = 512 |
| 701 | const IOADM_PATH: i64 = 1024 |
| 702 | const IOADM_RECLEN_OFF: i64 = 16 |
| 703 | const IOADM_NAME_OFF: i64 = 19 |
| 704 | const IOADM_GETDENTS: i64 = 217 |
| 705 | const IOADM_OPENDIR: i64 = 0x10000 |
| 706 | const IOADM_AT_FDCWD: i64 = 0 - 100 |
| 707 | const IOADM_PLUS: i64 = 43 |
| 708 | const IOADM_DOT: i64 = 46 |
| 709 | const IOADM_LBRACK: i64 = 91 |
| 710 | const IOADM_SLASH: i64 = 47 |
| 711 | const IOADM_RBRACK: i64 = 93 |
| 712 | const IOADM_D0: i64 = 48 |
| 713 | const IOADM_D9: i64 = 57 |
| 714 | const IOADM_OUT_SLOTS: i64 = 5 |
| 891 | const IOAE_CAP: i64 = 4096 |
| 892 | const IOAE_CONF_CAP: i64 = 8192 |
| 893 | const IOAE_PATH_CAP: i64 = 256 |
| 894 | const IOAE_I64_MAX: i64 = 9223372036854775807 |
| 895 | const IOAE_OFF: i64 = 0 |
| 896 | const IOAE_CONFIRMED: i64 = 1 |
| 897 | const IOAE_STALE: i64 = 2 |
| 898 | const IOAE_FUTURE: i64 = 3 |
| 899 | const IOAE_INVALID: i64 = 4 |
| 900 | const IOAE_ABSENT: i64 = 5 |
| 901 | const IOAE_SENSOR: i64 = 6 |
| 902 | const IOAE_IO: i64 = 7 |
| 903 | const IOAE_POLICY: i64 = 8 |
| 904 | const IOAE_READ_ABSENT: i64 = 0 - 2 |
| 905 | const IOAE_READ_IO: i64 = 0 - 3 |
| 906 | const IOAE_READ_FULL: i64 = 0 - 4 |
| 907 | const IOAE_SELECTED: i64 = 0 |
| 908 | const IOAE_RAW: i64 = 1 |
| 909 | const IOAE_CPU: i64 = 2 |
| 910 | const IOAE_STATE: i64 = 3 |
| 911 | const IOAE_TS: i64 = 4 |
| 912 | const IOAE_AGE: i64 = 5 |
| 913 | const IOAE_MAXAGE: i64 = 6 |
| 914 | const IOAE_SAMPLES: i64 = 7 |
| 915 | const IOAE_REQUESTED: i64 = 8 |
| 916 | const IOAE_GAP: i64 = 9 |
| 917 | const IOAE_SPAN: i64 = 10 |
| 918 | const IOAE_N: i64 = 11 |
| 919 | const IOAE_BYTES: i64 = 88 |
functions
| 45 | func ioa_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 47 | func ioa_ncpu(buf: *u8, n: i64) -> i64 called by 1: ioa_measure |
| 63 | func ioa_measure(out: *i64) -> i64 |
| 89 | func ioa_spawn_budget(ncpu: i64, procs_blk: i64, per_cpu: i64, reserve: i64) -> i64 |
| 108 | func ioa_stat_parse(sbuf: *u8, sn: i64, out: *i64) -> i64 |
| 142 | func ioa_read_pid_stat(pid: i64, sbuf: *u8, path: *u8) -> i64 |
| 159 | func ioa_comm_eq(sbuf: *u8, cs: i64, cl: i64, name: *u8, nl: i64) -> i64 called by 1: ioa_count_comm_mode |
| 169 | func ioa_count_comm_mode(name: *u8, mode: i64) -> i64 |
| 225 | func ioa_count_comm(name: *u8) -> i64 { return ioa_count_comm_mode(name, IOA_ALL_PROCS) } calls 1: ioa_count_comm_mode |
| 226 | func ioa_count_comm_roots(name: *u8) -> i64 { return ioa_count_comm_mode(name, IOA_ROOTS_ONLY) } calls 1: ioa_count_comm_mode |
| 261 | func ioa_dirty(out: *i64) -> i64 |
| 283 | func ioa_congestion(dirty_kb: i64, wb_kb: i64, warn_kb: i64, storm_kb: i64) -> i64 |
| 295 | func ioa_cong_name(state: i64) -> *u8 |
| 334 | func ioa_ds_skip_sp(buf: *u8, n: i64, p: i64) -> i64 called by 1: ioa_ds_parse |
| 346 | func ioa_ds_int(buf: *u8, n: i64, p: i64, out: *i64) -> i64 called by 1: ioa_ds_parse |
| 366 | func ioa_ds_tok_eq(buf: *u8, n: i64, p: i64, name: *u8) -> i64 called by 1: ioa_ds_parse |
| 392 | func ioa_ds_parse(buf: *u8, n: i64, dev: *u8, out: *i64) -> i64 |
| 444 | func ioa_diskstats(dev: *u8, out: *i64) -> i64 |
| 462 | func ioa_sector_rate_kbs(sectors_delta: i64, elapsed_ms: i64) -> i64 |
| 493 | func ioa_sleep_ms(ms: i64) -> i64 { return sys_poll(0 as *u8, 0, ms) } |
| 501 | func ioa_median(vals: *i64, k: i64) -> i64 called by 1: ioa_measure_median |
| 543 | func ioa_comm_starts(sbuf: *u8, cs: i64, cl: i64, name: *u8, nl: i64) -> i64 called by 1: ioa_ownership_census |
| 557 | func ioa_ownership_census(prefix: *u8, out: *i64) -> i64 |
| 627 | func ioa_share_permil(part: i64, whole: i64) -> i64 |
| 633 | func ioa_measure_median(out: *i64, k: i64, gap_ms: i64) -> i64 |
| 716 | func 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 |
| 717 | func ioadm_cat(dst: *u8, a: *u8, b: *u8, c: *u8) -> i64 |
| 728 | func ioadm_read(path: *u8, buf: *u8, cap: i64) -> i64 |
| 743 | func ioadm_find(buf: *u8, n: i64, needle: *u8) -> i64 |
| 759 | func ioadm_int_after(buf: *u8, n: i64, key: *u8) -> i64 |
| 779 | func ioadm_degraded(buf: *u8, n: i64) -> i64 called by 1: ioa_dmcache |
| 802 | func ioadm_instance(root: *u8, out: *u8) -> i64 |
| 846 | func ioa_dmcache(root: *u8, out: *i64) -> i64 |
| 921 | func ioae_eqspan(b: *u8, s: i64, e: i64, text: *u8) -> i64 |
| 928 | func ioae_uint(b: *u8, s: i64, e: i64) -> i64 called by 1: ioae_field |
| 939 | func ioae_eol(b: *u8, p: i64, n: i64) -> i64 |
| 946 | func ioae_field(b: *u8, n: i64, key: *u8) -> i64 |
| 963 | func ioae_producer(b: *u8, n: i64) -> i64 |
| 979 | func ioae_reset(ncpu: i64, fallback: i64, raw: i64, maxage: i64, out: *i64) -> i64 called by 1: ioae_select |
| 987 | func ioae_select(b: *u8, n: i64, ncpu: i64, raw: i64, fallback: i64, now: i64, maxage: i64, out: *i64) -> i64 |
| 1024 | func ioae_read(path: *u8, b: *u8, cap: i64) -> i64 |
| 1038 | func ioae_join(root: *u8, leaf: *u8, out: *u8) -> i64 |
| 1047 | func ioae_policy_record(b: *u8, n: i64) -> i64 |
| 1055 | func ioae_policy(root: *u8) -> i64 |
| 1071 | func ioae_measure_selected(root: *u8, maxage: i64, ncpu: i64, raw: i64, fallback: i64, out: *i64) -> i64 called by 2: mainmain calls 6: ioae_selectsys_mmapioae_joinioae_readsys_clock_gettime_realsys_munmap |
| 1086 | func ioae_source(state: i64) -> *u8 called by 1: ioae_source_evidence |
| 1097 | func ioae_source_evidence(out: *i64) -> *u8 |