From 3055ab0d128a0359d4c956817676f74bdfc19b28 Mon Sep 17 00:00:00 2001 From: japabu Date: Fri, 9 Oct 2026 13:27:47 +0200 Subject: [PATCH 1/4] Every shipping-kernel member rides testcases behind its rows' jobs, and the two debug timing rows ride shared-debug: 23 boots to 20 Stage 3b of step B of the one-boot plan for the T14's metal rows. `shared` and `ccorpus` are no boots of their own: the 85 discovered Rust binaries, the 137 C cases and the three bounds programs are `testcases`' members, in that order, behind the ten jobs its rows name. `testcases-debug` is gone: `tlb_shootdown_waits` and `trace_record_cost` name `shared-debug`, whose kernel is the one they need, and run before its seven members. A shared boot's members each say what they add to the list's bound (`metal::Member`), since one boot now carries Rust tests at 300 ms and C cases at 100. `testcases` is told `--bound-ms=100100` and armed with `boot-deadline=200200`; `shared-debug` keeps 62100 and 124200. A boot's last job ends its rows' jobs and no longer the whole list. Stage 3a put it behind the members, with no boot that had both. `acpi_hold` gives up 54 s after boot, and by the readings on record the rows' jobs end 43.2 s in and the members take 8.7 to 9.3 s more: behind them the hold would start with under two seconds left, and past that time it panics before it reads a line that has been in the log for twenty seconds. Before them it starts where stage 1 measured it, and the three bounds programs stay the last jobs of the boot, which is the only place a machine has run them. Two jobs of one boot the kernel records under one name are refused in `batches`, for every boot; the C corpus's own check of its cases is that one now. The judge prints a shared boot's members' time summed between each one's own markers, against what they add to the bound: the time from `Boot: complete` to the last record now holds the rows' jobs too. The record rows of `shared`, `ccorpus` and `testcases-debug` are deleted. The allowance issue carries the merged list summed from the boots it was: 51.8 to 52.5 s of a 100.1 s bound, past half by the rows' jobs. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A --- ...nels-deadline-at-several-times-the-work.md | 29 ++++- ...4-reboots-through-ubuntu-for-every-test.md | 6 +- tests/checks/metal.rs | 47 ++++--- tests/common/metal.rs | 117 +++++++++++++----- tests/metal/lenovo-20w0003amz.toml | 9 -- tests/toyos.rs | 100 ++++++--------- 6 files changed, 177 insertions(+), 131 deletions(-) diff --git a/issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md b/issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md index 81e83d2a4cb..398ed32a496 100644 --- a/issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md +++ b/issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md @@ -67,10 +67,31 @@ the 60 000 ms list bound and 120 000 ms deadline of a boot no member rides: | `shared` | 86.4 s | 172.8 s | 473 s | **It grows with every member and has no ceiling**: 0.6 s of deadline a Rust -member and 0.2 s a C case. The 88 Rust members and 137 C cases on one boot, -which is what joining `shared` and `ccorpus` to `testcases` makes, arm a list -bound of 100 100 ms and a deadline of 200 200 ms, waited 501 s. A clamp is no -answer: a ceiling under the derived bound ends a healthy list. +member and 0.2 s a C case. The 88 Rust members and 137 C cases now ride +`testcases` behind its rows' jobs, and arm a list bound of 100 100 ms, a +deadline of 200 200 ms and a hard-lockup bound of 100 100 ms, waited 501 s. A +clamp is no answer: a ceiling under the derived bound ends a healthy list. + +## The merged list, summed from the boots it was + +No machine has run the merged `testcases`. Each part between its own markers, +over three readings of the boot that carried it: `testcases` at `9e70cd2e3`, +`473efea22` and `accbd79dd`, `shared` and `ccorpus` at `eff8b20ee`, +`d6d008e88` and `49e12f23b`. + +| part | least | most | its share of the bound | +|---|---|---|---| +| the boot, to its first job | 1 190 ms | 1 196 ms | | +| the rows' ten jobs | 41 913 ms | 41 983 ms | 60 000 ms | +| the 88 Rust members | 7 068 ms | 7 115 ms | 26 400 ms | +| the 137 C cases | 1 667 ms | 2 183 ms | 13 700 ms | +| the list's last record | 51 838 ms | 52 477 ms | 100 100 ms | + +**Half the bound is 50 050 ms, and the sum is 1.8 to 2.4 s past it**, by the +rows' jobs: they take seven tenths of the base no allowance widens, +`counters_metal` alone 32.7 s of it, where the members take under a quarter of +what they add. The exit's second line reads the whole list against half its +bound, so a boot whose members are well inside their share does not meet it. **A late expiry adds to the longer bound.** `issues/a-120000-ms-boot-deadline-fired-132859-ms-late-on-the-t14.md` is open: diff --git a/issues/the-t14-reboots-through-ubuntu-for-every-test.md b/issues/the-t14-reboots-through-ubuntu-for-every-test.md index 2278d88133e..b80d322d7d0 100644 --- a/issues/the-t14-reboots-through-ubuntu-for-every-test.md +++ b/issues/the-t14-reboots-through-ubuntu-for-every-test.md @@ -44,9 +44,9 @@ metal rows. Every stage below has the host drive a T14 that runs ToyOS, so the track is blocked on `issues/the-host-cannot-reach-the-t14-while-it-runs-toyos.md`. -**Next: two sessions in place of five boots.** `shared`, `ccorpus`, -`testcases`, `testcases-mkdir` and `testcases-readdir` share a config, a -parameter line and the shipping kernel. The session image is `tests/testcases` +**Next: two sessions in place of three boots.** `testcases`, which every +shipping-kernel member rides, `testcases-mkdir` and `testcases-readdir` share +a config, a parameter line and the shipping kernel. The session image is `tests/testcases` with the netd, sshd and streaming `logd` of `tests/lantalkcase`, which is deleted (`issues/the-host-cannot-reach-the-t14-while-it-runs-toyos.md`), under a `boot-deadline=` derived as a metal boot's is: twice its list's bound diff --git a/tests/checks/metal.rs b/tests/checks/metal.rs index a3fa5d3b35f..9b20fc8c72d 100644 --- a/tests/checks/metal.rs +++ b/tests/checks/metal.rs @@ -370,6 +370,10 @@ pub fn a_run_under_another_bios_fails_and_records_nothing() { assert_eq!(judged_against(&lacking(&bios)), (false, committed(&bios, 1165))); } +fn member(job: &str, adds_ms: u64) -> metal::Member { + metal::Member { job: job.to_string(), adds_ms } +} + /// **A failing shared member fails itself alone**: the run is red, and the /// boot it rode records its own numbers as the boot whose members all passed /// does. @@ -391,8 +395,7 @@ pub fn a_failing_shared_member_fails_itself_alone() { config: "tests/testcases", params: &[], features: &[], - member_ms: toyos_tco::RUST_MEMBER_MS, - jobs: Vec::from(jobs.map(String::from)), + members: jobs.iter().map(|job| member(job, toyos_tco::RUST_MEMBER_MS)).collect(), files: Vec::new(), links: Vec::new(), }; @@ -436,12 +439,13 @@ pub fn a_boots_last_job_is_behind_every_other() { assert!(refused.contains("other ends the boot \"own\" on later and another row ends it on hold"), "{refused}"); } -/// **A boot's rows' jobs run before its members, its last job behind both, -/// and its bounds follow from who rides it**: the runner's bound over the list -/// is the rows' and what each member adds, the kernel's over the boot twice -/// that, and a boot no member rides keeps the bounds a row's boot had. A +/// **A boot's rows' jobs run before its members, the rows' last job among +/// them, and its bounds follow from who rides it**: the runner's bound over +/// the list is the rows' and what each member adds, the kernel's over the boot +/// twice that, and a boot no member rides keeps the bounds a row's boot had. A /// shared boot that disagrees with a row about the image is refused, and so -/// are two under one name. +/// are two under one name, and two jobs of one boot the kernel records under +/// one name. pub fn rows_run_before_members_under_a_bound_the_members_widen() { static EARLY: Metal = Metal { arms: &[metal::once("shared", "tests/testcases", &[], &["tone", "cost"])], judge: |_| Ok(()) }; @@ -456,23 +460,25 @@ pub fn rows_run_before_members_under_a_bound_the_members_widen() { arms: &[metal::once("wedge", "tests/jobcase", &[toyos_build::metal::WEDGE_ARM], &[])], judge: |_| Ok(()), }; - let members = |boot: &str, member_ms: u64, jobs: &[&str]| metal::SharedBoot { + let members = |boot: &str, members: &[(&str, u64)]| metal::SharedBoot { boot: boot.to_string(), config: "tests/testcases", params: &[], features: &[], - member_ms, - jobs: jobs.iter().map(ToString::to_string).collect(), - files: vec![(format!("expect/{}", jobs[0]), Vec::new())], + members: members.iter().map(|(job, adds_ms)| member(job, *adds_ms)).collect(), + files: vec![(format!("expect/{}", members[0].0), Vec::new())], links: Vec::new(), }; let rows = [("early", &EARLY), ("held", &HELD), ("wedges", &WEDGES)]; - let shared = [members("shared", 860, &["m1", "m2", "m3"]), members("corpus", 260, &["c1", "c2"])]; + let shared = [ + members("shared", &[("m1", 860), ("c1", 260), ("m2", 860)]), + members("corpus", &[("c2", 260), ("c3", 260)]), + ]; let boots = metal::batches(&rows, &shared).expect("one image a boot"); let bounds = |boot: &str| (boots[boot].bound_ms(), boots[boot].deadline_ms()); - assert_eq!(boots["shared"].jobs, ["tone", "cost", "late", "m1", "m2", "m3", "hold"]); - assert_eq!(bounds("shared"), (62_580, 125_160)); - assert_eq!(boots["corpus"].jobs, ["c1", "c2"]); + assert_eq!(boots["shared"].jobs, ["tone", "cost", "late", "hold", "m1", "c1", "m2"]); + assert_eq!(bounds("shared"), (61_980, 123_960)); + assert_eq!(boots["corpus"].jobs, ["c2", "c3"]); assert_eq!(bounds("corpus"), (60_520, 121_040)); // No member: the bounds every row's boot had before a list could widen. assert_eq!(bounds("own"), (60_000, 120_000)); @@ -488,6 +494,11 @@ pub fn rows_run_before_members_under_a_bound_the_members_widen() { panic!("two shared boots under one name were batched"); }; assert!(refused.contains("two shared boots are both named \"shared\""), "{refused}"); + let long = members("shared", &[("test_rs_a_name_past_what_is_kept", 860), ("test_rs_a_name_past_what_is_lost", 860)]); + let Err(refused) = metal::batches(&rows, &[long]) else { + panic!("two jobs the kernel records under one name were batched"); + }; + assert!(refused.contains("the kernel records both as \"test_rs_a_name_past_what_is\""), "{refused}"); } /// **What a run's words take**: no word the whole profile; a name word every @@ -505,8 +516,7 @@ pub fn words_take_rows_members_and_whole_boots() { config: "tests/testcases", params: &[], features: &[], - member_ms: toyos_tco::RUST_MEMBER_MS, - jobs: jobs.iter().map(ToString::to_string).collect(), + members: jobs.iter().map(|job| member(job, toyos_tco::RUST_MEMBER_MS)).collect(), files: jobs .iter() .flat_map(|job| [(format!("expect/{job}"), Vec::new()), (format!("bin/test_c_{job}"), Vec::new())]) @@ -514,11 +524,12 @@ pub fn words_take_rows_members_and_whole_boots() { links: jobs.iter().map(|job| (format!("bin/{job}"), "/system/bin/test_rs_ccheck".to_string())).collect(), }; let profile = [boot("shared", &["test_rs_a1", "test_rs_a2", "test_rs_b1"]), boot("ccorpus", &["c1", "c2"])]; + let jobs_of = |boot: &metal::SharedBoot| boot.members.iter().map(|m| m.job.clone()).collect::>(); let taken = |names: &[&str], boots: &[&str]| { metal::select(names, boots, &ROWS, &profile).map(|(rows, shared)| { let rows: Vec<&str> = rows.iter().map(|(name, _)| *name).collect(); let shared: Vec = - shared.iter().map(|boot| format!("{}={}", boot.boot, boot.jobs.join("+"))).collect(); + shared.iter().map(|boot| format!("{}={}", boot.boot, jobs_of(boot).join("+"))).collect(); (rows.join(","), shared.join(",")) }) }; diff --git a/tests/common/metal.rs b/tests/common/metal.rs index 4fab02f6f74..b3ed983707a 100644 --- a/tests/common/metal.rs +++ b/tests/common/metal.rs @@ -56,9 +56,10 @@ pub struct Arm { /// kernel, and that is what most of the suite wants: it is the artifact the /// owner flashes. pub features: &'static [&'static str], - /// The job this boot ends on, before `reboot`: [`batches`] puts it after - /// every job any arm on the boot names and every member riding it, so none - /// can land behind it, and refuses two arms that name different ones. + /// The job this boot's rows end on: [`batches`] puts it after every job any + /// arm on the boot names, so no row's can land behind it, and refuses two + /// arms that name different ones. The members riding the boot run behind + /// it: it is a row's job, and a row's jobs have the machine first. pub last: Option<&'static str>, } @@ -97,12 +98,8 @@ pub struct SharedBoot { pub params: &'static [&'static str], /// The kernel build, empty for the one an image ships. pub features: &'static [&'static str], - /// What each member adds to the bound the runner gives the boot's whole - /// list (`toyos_tco::list_bound_ms`), in milliseconds. - pub member_ms: u64, - /// What the runner spawns, in order — the whole binary name, `test_rs_` - /// prefix and all, because that is what the kernel records it under. - pub jobs: Vec, + /// What the runner spawns behind the rows' jobs, in order. + pub members: Vec, /// Files this boot needs on ROOT beside the binaries: a corpus's committed /// expectations, which the guest compares against because on this machine /// no host can read what a case printed. @@ -112,10 +109,21 @@ pub struct SharedBoot { pub links: Vec<(String, String)>, } +/// One discovered member of a [`SharedBoot`]. +#[derive(Clone)] +pub struct Member { + /// The whole binary name, `test_rs_` prefix and all, because that is what + /// the kernel records it under. + pub job: String, + /// What it adds to the bound the runner gives its boot's whole list + /// (`toyos_tco::list_bound_ms`), in milliseconds. + pub adds_ms: u64, +} + impl SharedBoot { /// What the members add to their boot's list bound. fn members_ms(&self) -> u64 { - self.jobs.len() as u64 * self.member_ms + self.members.iter().map(|member| member.adds_ms).sum() } /// This boot with the members `named` and none else, or `None` where that @@ -123,15 +131,15 @@ impl SharedBoot { /// name**: the C corpus stages a binary and an expectation per case, and a /// stick is written over `ssh`. fn keeping(&self, named: impl Fn(&str) -> bool) -> Option { - let jobs: Vec = self.jobs.iter().filter(|job| named(job)).cloned().collect(); + let members: Vec = self.members.iter().filter(|member| named(&member.job)).cloned().collect(); let mine = |path: &str| { let last = path.rsplit('/').next().unwrap_or(path); let case = last.strip_prefix("test_c_").unwrap_or(last); - jobs.iter().any(|job| job == case) + members.iter().any(|member| member.job == case) }; let files = self.files.iter().filter(|(path, _)| mine(path)).cloned().collect(); let links = self.links.iter().filter(|(from, _)| mine(from)).cloned().collect(); - (!jobs.is_empty()).then(|| SharedBoot { jobs, files, links, ..self.clone() }) + (!members.is_empty()).then(|| SharedBoot { members, files, links, ..self.clone() }) } } @@ -155,7 +163,7 @@ pub fn select( job.strip_prefix("test_rs_").unwrap_or(job) } let all = names.is_empty() && boots.is_empty(); - let jobs = || shared.iter().flat_map(|boot| &boot.jobs).map(|job| bare(job)); + let jobs = || shared.iter().flat_map(|boot| &boot.members).map(|member| bare(&member.job)); if let Some(dead) = names.iter().find(|word| { !rows.iter().any(|(name, _)| name.contains(**word)) && !jobs().any(|job| job.contains(**word)) }) { @@ -431,6 +439,24 @@ impl Readback { }) } + /// How long the runner had each job running, between the job's own two + /// markers on the log's clock; a job with no such pair has no entry. + fn jobs_ms(&self) -> BTreeMap<&str, u64> { + let mut started: BTreeMap<&str, u64> = BTreeMap::new(); + let mut took = BTreeMap::new(); + for line in self.log.lines() { + let Some(at) = toyos_logstream::parse(line).and_then(|parsed| parsed.ms) else { continue }; + if let Some(job) = line.split("===TEST_START ").nth(1).and_then(|rest| rest.strip_suffix("===")) { + started.insert(job, at); + } else if let Some(job) = line.split("===TEST_END ").nth(1).and_then(|rest| rest.split(' ').next()) { + if let Some(from) = started.get(job) { + took.insert(job, at.saturating_sub(*from)); + } + } + } + took + } + /// One number measured on this boot. It is its measurer's, the row whose /// judge called this or the boot itself for what [`judge_readbacks`] reads /// off every boot, and is judged against this machine's record once every @@ -581,6 +607,10 @@ pub fn at(dir: &Path, label: &str) -> PathBuf { /// **A boot's rows' jobs run before its members**, so a row that measures has /// the machine its jobs alone would give it, and what the members write to the /// log lands behind every row's. +/// +/// **Two jobs of one boot the kernel records under one name are refused** +/// ([`bootlog::recorded_name`]): the second one's verdict would be read off +/// the first one's exit. pub fn batches( tests: &[(&str, &'static Metal)], shared: &[SharedBoot], @@ -604,6 +634,12 @@ pub fn batches( } } } + for batch in out.values_mut() { + if let Some(last) = batch.last { + batch.jobs.retain(|job| job != last); + batch.jobs.push(last.to_string()); + } + } let mut ridden: BTreeSet<&str> = BTreeSet::new(); for boot in shared { if !ridden.insert(&boot.boot) { @@ -611,15 +647,21 @@ pub fn batches( } let who = format!("the shared boot {:?}", boot.boot); let batch = ride(&mut out, &who, &boot.boot, boot.config, boot.params, boot.features)?; - batch.add(boot.jobs.iter().cloned()); + batch.add(boot.members.iter().map(|member| member.job.clone())); batch.members_ms += boot.members_ms(); batch.files.extend(boot.files.iter().cloned()); batch.links.extend(boot.links.iter().cloned()); } - for batch in out.values_mut() { - if let Some(last) = batch.last { - batch.jobs.retain(|job| job != last); - batch.jobs.push(last.to_string()); + for (label, batch) in &out { + let mut recorded: BTreeMap = BTreeMap::new(); + for job in &batch.jobs { + if let Some(other) = recorded.insert(bootlog::recorded_name(job), job) { + return Err(format!( + "the boot {label:?} runs {other} and {job}, and the kernel records both as {:?}: one \ + boot's log cannot tell their verdicts apart", + bootlog::recorded_name(job) + )); + } } } Ok(out) @@ -893,7 +935,7 @@ pub fn run( eprintln!( "[metal] {} registration(s) and {} shared member(s) over {} boot(s)", runs.len(), - shared.iter().map(|b| b.jobs.len()).sum::(), + shared.iter().map(|b| b.members.len()).sum::(), batches.len(), ); @@ -1140,10 +1182,10 @@ pub fn judge_readbacks( } let mut members = 0usize; for boot in shared { - eprintln!("\n[metal] {}: {} member(s)", boot.boot, boot.jobs.len()); + eprintln!("\n[metal] {}: {} member(s)", boot.boot, boot.members.len()); let back = readbacks.get(&boot.boot).expect("every shared boot was batched"); let mut ran = 0usize; - for job in &boot.jobs { + for Member { job, .. } in &boot.members { members += 1; let verdict = match back { Err(why) => Err(why.clone()), @@ -1163,18 +1205,25 @@ pub fn judge_readbacks( } } if let (Ok(back), true) = (back, ran > 0) { - if let (Some(complete), Some(last)) = (back.complete_record_ms(), back.last_record_ms()) { - let each = last.saturating_sub(complete) / ran as u64; - eprintln!(" {} ms per member over the {ran} that ran", each); - // On the clock the runner counts its bound on, the kernel's - // own, whose reading `Boot: complete` states beside its stamp. - if let Some(boot_ms) = back.boot_ms { - eprintln!( - " its last record came {} ms into a list bound of {} ms", - (last + boot_ms).saturating_sub(complete), - toyos_tco::list_bound_ms(boot.members_ms()) - ); - } + // Each member between its own markers: the rows' jobs share the list. + let jobs = back.jobs_ms(); + let took: u64 = boot.members.iter().filter_map(|member| jobs.get(member.job.as_str())).sum(); + eprintln!( + " its members took {took} ms of the {} ms they add to the list's bound, {} ms a member over \ + the {ran} that ran", + boot.members_ms(), + took / ran as u64 + ); + // On the clock the runner counts its bound on, the kernel's own, + // whose reading `Boot: complete` states beside its stamp. + if let (Some(complete), Some(last), Some(boot_ms)) = + (back.complete_record_ms(), back.last_record_ms(), back.boot_ms) + { + eprintln!( + " its last record came {} ms into a list bound of {} ms", + (last + boot_ms).saturating_sub(complete), + toyos_tco::list_bound_ms(boot.members_ms()) + ); } } } diff --git a/tests/metal/lenovo-20w0003amz.toml b/tests/metal/lenovo-20w0003amz.toml index fc3981df3d9..b846dd8239a 100644 --- a/tests/metal/lenovo-20w0003amz.toml +++ b/tests/metal/lenovo-20w0003amz.toml @@ -6,9 +6,6 @@ bios = "N34ET71W (1.71 )" "boot.acpicase.complete_ms" = 1207 "boot.acpicase.panel_max_us" = 2495 "boot.acpicase.panel_us" = 12966 -"boot.ccorpus.complete_ms" = 1165 -"boot.ccorpus.panel_max_us" = 3759 -"boot.ccorpus.panel_us" = 22178 "boot.deadlinewedge.complete_ms" = 1165 "boot.deadlinewedge.panel_max_us" = 3850 "boot.deadlinewedge.panel_us" = 21662 @@ -40,15 +37,9 @@ bios = "N34ET71W (1.71 )" "boot.shared-debug.complete_ms" = 1165 "boot.shared-debug.panel_max_us" = 3791 "boot.shared-debug.panel_us" = 21483 -"boot.shared.complete_ms" = 1154 -"boot.shared.panel_max_us" = 3608 -"boot.shared.panel_us" = 21429 "boot.testcases-deaf.complete_ms" = 1165 "boot.testcases-deaf.panel_max_us" = 5247 "boot.testcases-deaf.panel_us" = 26659 -"boot.testcases-debug.complete_ms" = 1165 -"boot.testcases-debug.panel_max_us" = 3824 -"boot.testcases-debug.panel_us" = 22032 "boot.testcases-mkdir.complete_ms" = 1166 "boot.testcases-mkdir.panel_max_us" = 3839 "boot.testcases-mkdir.panel_us" = 22100 diff --git a/tests/toyos.rs b/tests/toyos.rs index 39add3ae4b3..322ceae01e2 100644 --- a/tests/toyos.rs +++ b/tests/toyos.rs @@ -177,9 +177,10 @@ const RUST_SKIP: &[&str] = &[ /// The shared boot's last members, in this order: each fills a bound of its /// own process — tens of thousands of mappings, thousands of threads, a /// thousand 2 MiB images — and its exit gives all of it back. Behind every -/// discovered member, so none of those runs after one of these. **This is -/// their one declaration**: [`discover_rust_tests`] leaves them to it, so a -/// name taken off this list is a discovered member again. +/// discovered member and every corpus case, so nothing but the reboot runs +/// after one of these. **This is their one declaration**: +/// [`discover_rust_tests`] leaves them to it, so a name taken off this list is +/// a discovered member again. const LAST_MEMBERS: &[&str] = &["abuse_mmap_regions", "abuse_thread_table", "abuse_dlopen_ledger"]; /// Binaries a metal row or a guest test drives that the shared boot also runs @@ -509,12 +510,13 @@ const METAL: &[(&str, metal::Metal)] = &[ ), ( // `SYS_DEBUG` holds each other CPU's shootdown acknowledgement back, - // so the kernel that carries it; the verdict is the guest's own exit. + // so the kernel that carries it; the verdict is the guest's own exit, + // and every wait it asserts is a floor, which no load shortens. "tlb_shootdown_waits", metal::Metal { arms: &[metal::Arm { - features: toyos_build::build::TEST_KERNEL, - ..metal::once("testcases-debug", "tests/testcases", &[], &["test_rs_tlb_shootdown_waits"]) + features: ACTUATOR_KERNEL, + ..metal::once("shared-debug", "tests/testcases", SELFTEST_PARAMS, &["test_rs_tlb_shootdown_waits"]) }], judge: |b| b[0].job_passed("test_rs_tlb_shootdown_waits"), }, @@ -526,8 +528,8 @@ const METAL: &[(&str, metal::Metal)] = &[ "trace_record_cost", metal::Metal { arms: &[metal::Arm { - features: toyos_build::build::TEST_KERNEL, - ..metal::once("testcases-debug", "tests/testcases", &[], &["test_rs_trace_flood"]) + features: ACTUATOR_KERNEL, + ..metal::once("shared-debug", "tests/testcases", SELFTEST_PARAMS, &["test_rs_trace_flood"]) }], judge: |b| { b[0].job_passed("test_rs_trace_flood")?; @@ -900,8 +902,8 @@ const ISA_WITHHELD: &[metal::Arm] = &[metal::once( &["test_rs_isa_grant", "test_rs_isa_lines"], )]; -/// The boot most of the first tranche rides: the plain `tests/testcases` shape -/// with a job list that ends it. +/// The boot most rows ride, and every shipping-kernel member behind them +/// ([`shared_metal`]): the plain `tests/testcases` shape. const TESTCASES: &[metal::Arm] = &[metal::once( "testcases", "tests/testcases", @@ -918,8 +920,10 @@ const TESTCASES: &[metal::Arm] = &[metal::once( ], )]; -/// The same boot, ended on the hold: the count it waits for comes thirty -/// seconds after the server arms, and a job behind it would wait that out too. +/// The same boot, its rows ended on the hold: the count it waits for comes +/// thirty seconds after the server arms, and a row's job behind it would wait +/// that out too. The members run behind it: the hold gives up at a time since +/// boot, which a list of members before it would spend. const TESTCASES_HELD: &[metal::Arm] = &[metal::Arm { last: Some("test_rs_acpi_hold"), ..metal::once("testcases", "tests/testcases", &[], &[]) @@ -1019,37 +1023,29 @@ const SELFTESTS: &[metal::Arm] = &[metal::Arm { ..metal::once("shared-debug", "tests/testcases", SELFTEST_PARAMS, &[]) }]; -/// The boots every discovered Rust binary rides on the T14: the shipping -/// kernel's, with [`LAST_MEMBERS`] behind them, and [`ACTUATOR_TESTS`] on the -/// kernel that carries `SYS_DEBUG`, armed with [`SELFTEST_PARAMS`]. -fn shared_metal() -> Vec { - let (debug, mut shipping): (Vec, Vec) = discover_rust_tests() +/// The two boots every discovered member rides on the T14, each behind its +/// rows' jobs: `testcases`, the shipping kernel's, carries every discovered +/// Rust binary, then the C corpus, then [`LAST_MEMBERS`]; `shared-debug` +/// carries [`ACTUATOR_TESTS`] on the kernel that has `SYS_DEBUG`, armed with +/// [`SELFTEST_PARAMS`]. A second boot rather than one image for the whole +/// set: what those need is a syscall number the rest must not have. +fn shared_metal(c_bins: &[(String, Vec)]) -> Vec { + let rust = |names: Vec| { + names.into_iter().map(|n| metal::Member { job: format!("test_rs_{n}"), adds_ms: toyos_tco::RUST_MEMBER_MS }) + }; + let (debug, shipping): (Vec, Vec) = discover_rust_tests() .into_iter() .partition(|name| ACTUATOR_TESTS.contains(&name.as_str())); - shipping.extend(LAST_MEMBERS.iter().map(ToString::to_string)); + let corpus = c_corpus_metal(c_bins); + let last = rust(LAST_MEMBERS.iter().map(ToString::to_string).collect()); vec![ - metal::SharedBoot { - boot: "shared".to_string(), - config: "tests/testcases", - params: &[], - features: &[], - member_ms: toyos_tco::RUST_MEMBER_MS, - jobs: shipping.iter().map(|n| format!("test_rs_{n}")).collect(), - files: Vec::new(), - links: Vec::new(), - }, - // The same list's other half, on the kernel that carries `SYS_DEBUG`. - // A second boot rather than a second image for the whole set: what - // these need is a syscall number the rest must not have, and a boot - // where every binary could call it would stop being the shipping - // machine for the other seventy. + metal::SharedBoot { members: rust(shipping).chain(corpus.members.clone()).chain(last).collect(), ..corpus }, metal::SharedBoot { boot: "shared-debug".to_string(), config: "tests/testcases", params: SELFTEST_PARAMS, features: ACTUATOR_KERNEL, - member_ms: toyos_tco::RUST_MEMBER_MS, - jobs: debug.iter().map(|n| format!("test_rs_{n}")).collect(), + members: rust(debug).collect(), files: Vec::new(), links: Vec::new(), }, @@ -1069,8 +1065,8 @@ const LATENCYCASE: &[metal::Arm] = &[metal::once( &["test_rs_cyclictest", "test_rs_sched_stress"], )]; -/// The C corpus on the T14: one boot, one job per case, each judged in the -/// guest. +/// The C corpus's part of the `testcases` boot, which [`shared_metal`] puts +/// the Rust members around: one job per case, each judged in the guest. /// /// **Every case in the corpus `return 0`s unconditionally**, so a bare /// exit-code verdict would be vacuous — the comparison is the whole point. It @@ -1084,40 +1080,19 @@ const LATENCYCASE: &[metal::Arm] = &[metal::once( /// job list of a hundred and nineteen `ccheck`s would leave one name and a /// hundred and nineteen records told apart only by position. fn c_corpus_metal(c_bins: &[(String, Vec)]) -> metal::SharedBoot { - let mut jobs = Vec::new(); + let mut members = Vec::new(); let mut files = Vec::new(); let mut links = Vec::new(); - let mut seen: BTreeMap = BTreeMap::new(); for (case, data) in c_bins { // A case with no committed expectation is one nothing could judge, and // shipping it would be a job that passes by comparing nothing. let Some(expected) = c_expectation(case) else { continue }; - // **The kernel truncates a process name**, so two cases whose names - // agree that far would land under one record. Refused rather than - // reported, because the second one's verdict would be read as the - // first's. - let recorded = bootlog::recorded_name(case); - if let Some(other) = seen.insert(recorded.clone(), case.clone()) { - panic!( - "the C cases {other:?} and {case:?} are both recorded as {recorded:?}, so one \ - boot's log cannot tell their verdicts apart" - ); - } files.push((format!("expect/{case}"), expected.into_bytes())); files.push((format!("bin/test_c_{case}"), data.clone())); links.push((format!("bin/{case}"), format!("/system/bin/{CCHECK}"))); - jobs.push(case.clone()); - } - metal::SharedBoot { - boot: "ccorpus".to_string(), - config: "tests/testcases", - params: &[], - features: &[], - member_ms: toyos_tco::C_MEMBER_MS, - jobs, - files, - links, + members.push(metal::Member { job: case.clone(), adds_ms: toyos_tco::C_MEMBER_MS }); } + metal::SharedBoot { boot: "testcases".to_string(), config: "tests/testcases", params: &[], features: &[], members, files, links } } /// The comparator's own staged name. It is a `RUST_SKIP` helper, so discovery @@ -5223,8 +5198,7 @@ fn main() { if let Some(mode) = parsed.metal { let (c_bins, rust_bins) = build_shared_bins(); - let mut boots = shared_metal(); - boots.push(c_corpus_metal(&c_bins)); + let boots = shared_metal(&c_bins); let (selected, boots) = match metal::select(filters, &parsed.boots, METAL, &boots) { Ok(selection) => selection, Err(refusal) => { From 2c7e1be1a3febda4099d3939870ac430b66fef1c Mon Sep 17 00:00:00 2001 From: japabu Date: Fri, 9 Oct 2026 14:01:15 +0200 Subject: [PATCH 2/4] The members' line says how many it summed and names one without both markers; the hold's boot-relative give-up is filed; the allowance issue's exit reads per part The judge's line for a shared boot's members summed only the members with a start and an end marker and then said "over the N that ran", N being the members that passed: one cut inside its run, the longest of them, left the sum in silence. The line now reads "summed over the N of M with both markers", and a second line, "K without a start and an end marker, the first ", is printed where K is not zero. The mean is gone with the count it was divided by. It stays a line a reader reads. `acpi_hold`'s `UNTIL_MS` is 54 000 ms from boot while its runner is given 100 100 ms: the round-1 review found that this branch ordered the list around it and recorded nothing. It is filed, owner the harness, not fixed. The allowance issue's exit read one sum against half of one bound, which hid two opposite errors: members at about 4.3 times their work, rows' jobs at about 1.4 times theirs. By the orchestrator's ruling it is three parts now: the members' share, the rows' jobs' share, and `testcases` reading both inside. None is built and the issue stays open. Its price table and its late-expiry sentence named `shared` and `ccorpus`, which are no boots any more, and the retention issue called the hold `testcases`' last job. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A --- ...nels-deadline-at-several-times-the-work.md | 56 +++++++++++-------- ...ddle-and-the-rows-whose-lines-sat-there.md | 8 ++- ...from-boot-whatever-its-runner-was-given.md | 39 +++++++++++++ tests/common/metal.rs | 18 ++++-- 4 files changed, 89 insertions(+), 32 deletions(-) create mode 100644 issues/acpi-hold-gives-up-at-a-time-counted-from-boot-whatever-its-runner-was-given.md diff --git a/issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md b/issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md index 398ed32a496..babe17a3399 100644 --- a/issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md +++ b/issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md @@ -63,14 +63,13 @@ the 60 000 ms list bound and 120 000 ms deadline of a boot no member rides: |---|---|---|---| | no member | 60.0 s | 120.0 s | 420 s | | `shared-debug` | 62.1 s | 124.2 s | 425 s | -| `ccorpus` | 73.7 s | 147.4 s | 448 s | -| `shared` | 86.4 s | 172.8 s | 473 s | +| `testcases` | 100.1 s | 200.2 s | 501 s | **It grows with every member and has no ceiling**: 0.6 s of deadline a Rust -member and 0.2 s a C case. The 88 Rust members and 137 C cases now ride -`testcases` behind its rows' jobs, and arm a list bound of 100 100 ms, a -deadline of 200 200 ms and a hard-lockup bound of 100 100 ms, waited 501 s. A -clamp is no answer: a ceiling under the derived bound ends a healthy list. +member and 0.2 s a C case. The 88 Rust members and 137 C cases the readings +above took on `shared` and `ccorpus` ride `testcases` behind its rows' jobs, +and those two boots are gone. A clamp is no answer: a ceiling under the +derived bound ends a healthy list. ## The merged list, summed from the boots it was @@ -87,17 +86,21 @@ over three readings of the boot that carried it: `testcases` at `9e70cd2e3`, | the 137 C cases | 1 667 ms | 2 183 ms | 13 700 ms | | the list's last record | 51 838 ms | 52 477 ms | 100 100 ms | -**Half the bound is 50 050 ms, and the sum is 1.8 to 2.4 s past it**, by the -rows' jobs: they take seven tenths of the base no allowance widens, -`counters_metal` alone 32.7 s of it, where the members take under a quarter of -what they add. The exit's second line reads the whole list against half its -bound, so a boot whose members are well inside their share does not meet it. +**The two shares are wide by different factors, and the rows' is the +tighter.** The members take 8 735 to 9 298 ms of the 40 100 ms they add: the +allowances stand at about 4.3 times their work. The rows' jobs end 43 103 to +43 173 ms into the kernel's clock, of the 60 000 ms `toyos_tco::JOB_BOUND_MS` +gives them: about 1.4 times theirs, `counters_metal` alone 32.7 s of it. That +constant is the same for a boot of no job and for this boot of ten, and no +allowance widens it. By the same sum the list's last record leaves 47.6 s of +its bound, and nothing reads that margin. **A late expiry adds to the longer bound.** `issues/a-120000-ms-boot-deadline-fired-132859-ms-late-on-the-t14.md` is open: -that deadline was reached at 252 859 ms, and the same lateness on `shared` is -305 659 ms. The wait moves with the deadline, so what is left of it after such -an expiry is the same 167 s; the machine is held 52.8 s longer. +that deadline was reached at 252 859 ms, and the same lateness on `testcases` +is 333 059 ms. The wait moves with the deadline, so what is left of it after +such an expiry is 167 s there and 168 s here; the machine is held 80.2 s +longer. ## Owner @@ -106,13 +109,18 @@ The metal suite: `tests/common/metal.rs`, which derives the bounds, and ## Exit -Both, on the T14: - -- Each allowance is derived at its declaration from what the T14 measures of a - list's total over its members, by one stated factor: a factor over a - per-member mean says nothing of a list of slow members. And the judge reds a - shared boot whose list's last record came past half its bound, which today - is a line a reader reads. -- The boot that carries `shared`, `ccorpus` and `testcases` together reads its - list's last record within half its list bound, with the pair read from the - kernel's own `boot deadline:` and `hard lockup:` lines. +Three parts, each on the T14. None is built. + +1. **The members.** Each allowance is derived at its declaration from the + members' sum between their own markers, by one stated factor: a factor over + a per-member mean says nothing of a list of slow members. And the judge + reds a boot whose members' sum is past the share they add, which today is a + line a reader reads. +2. **The rows' jobs.** What a boot's rows' jobs are given is derived the same + way from what they take, where today it is one constant, + `toyos_tco::JOB_BOUND_MS`, for a boot of no job and a boot of ten, one of + them 32.7 s. And the judge reds a boot whose rows' jobs end past their + share, which today nothing reads. +3. **`testcases` reads both inside their shares**, with the armed pair read + from the kernel's own `boot deadline:` and `hard lockup:` lines and the + margin from the list's last record to its list bound stated beside them. diff --git a/issues/a-t14-boot-that-outlogs-its-retention-loses-its-middle-and-the-rows-whose-lines-sat-there.md b/issues/a-t14-boot-that-outlogs-its-retention-loses-its-middle-and-the-rows-whose-lines-sat-there.md index ccfe2c6fdbf..1f69982c6dc 100644 --- a/issues/a-t14-boot-that-outlogs-its-retention-loses-its-middle-and-the-rows-whose-lines-sat-there.md +++ b/issues/a-t14-boot-that-outlogs-its-retention-loses-its-middle-and-the-rows-whose-lines-sat-there.md @@ -80,9 +80,11 @@ count line is there at 42.579 s (`28 SCIs; embedded controller queries taken: in seconds 36 to 47. `acpi_server_events` and `acpi_tables_loaded` ride `testcases` again, with -`test_rs_acpi_hold` its last job, and `testcases-hold` is deleted. The job -waits on the `log` port for the server's count line, bounded by the runner's -bound less a tenth, and exits non-zero without it. That readback judged +`test_rs_acpi_hold` the last of its rows' jobs and the shared members behind +it, and `testcases-hold` is deleted. The job waits on the `log` port for the +server's count line until 54 000 ms after boot +(`issues/acpi-hold-gives-up-at-a-time-counted-from-boot-whatever-its-runner-was-given.md`), +and exits non-zero without it. That readback judged offline by this harness reds by name: `testcases's own log is missing its parts 2 to 26; no row is judged on it`. diff --git a/issues/acpi-hold-gives-up-at-a-time-counted-from-boot-whatever-its-runner-was-given.md b/issues/acpi-hold-gives-up-at-a-time-counted-from-boot-whatever-its-runner-was-given.md new file mode 100644 index 00000000000..9e68e6df57a --- /dev/null +++ b/issues/acpi-hold-gives-up-at-a-time-counted-from-boot-whatever-its-runner-was-given.md @@ -0,0 +1,39 @@ +--- +status: open +kind: tooling +opened: 2026-10-09 +--- + +# `acpi_hold` gives up at a time counted from boot, whatever its runner was given + +`tests/toyos-rust-tests/src/bin/acpi_hold.rs` holds `testcases` open until +`/system/bin/acpiserver` has logged its count, which `acpi_server_events` +judges. Its wait ends at `UNTIL_MS`, `toyos_tco::JOB_BOUND_MS` less a tenth: +54 000 ms, counted from the kernel's zero and not from the hold's own start. +Two things follow. + +**What the hold is given shrinks with every job before it.** It is the last of +its boot's rows' jobs. On the T14 at `accbd79dd` it started 43 161 ms in, with +10.8 s left. A rows' job that adds 10.8 s to that list leaves it nothing: +`served_log::Log::until` then panics before it reads a line, with `the log did +not show … within 0ns`, and `acpi_server_events` reds over a count line that +was written some 20 s earlier and is in the log. The red names the server's +count, not the list that grew. + +**The constant is not the bound its runner was given.** `UNTIL_MS` is written +as "the runner's bound less a tenth", and `testcases`' runner is given +`--bound-ms=100100`: the 60 000 ms and what its 225 members add. The members +were ordered behind the hold for this reason and no other. Behind them it +would start 51.8 to 52.5 s in by the readings on record, with 1.5 to 2.2 s +left. + +## Owner + +The harness: `tests/toyos-rust-tests/src/bin/acpi_hold.rs`, and +`TESTCASES_HELD` in `tests/toyos.rs`, which places it. + +## Exit + +The hold is bounded from its own start, or by the bound its runner was given, +and a job added before it cannot red `acpi_server_events` over a line the log +holds. diff --git a/tests/common/metal.rs b/tests/common/metal.rs index b3ed983707a..7810ecabe11 100644 --- a/tests/common/metal.rs +++ b/tests/common/metal.rs @@ -1205,15 +1205,23 @@ pub fn judge_readbacks( } } if let (Ok(back), true) = (back, ran > 0) { - // Each member between its own markers: the rows' jobs share the list. + // Each member between its own markers: the rows' jobs share the + // list. One cut inside its run has no pair and is named, not + // summed as nothing. let jobs = back.jobs_ms(); - let took: u64 = boot.members.iter().filter_map(|member| jobs.get(member.job.as_str())).sum(); + let (timed, unpaired): (Vec<&Member>, Vec<&Member>) = + boot.members.iter().partition(|member| jobs.contains_key(member.job.as_str())); + let took: u64 = timed.iter().map(|member| jobs[member.job.as_str()]).sum(); eprintln!( - " its members took {took} ms of the {} ms they add to the list's bound, {} ms a member over \ - the {ran} that ran", + " its members took {took} ms of the {} ms they add to the list's bound, summed over the {} of \ + {} with both markers", boot.members_ms(), - took / ran as u64 + timed.len(), + boot.members.len() ); + if let Some(first) = unpaired.first() { + eprintln!(" {} without a start and an end marker, the first {}", unpaired.len(), first.job); + } // On the clock the runner counts its bound on, the kernel's own, // whose reading `Boot: complete` states beside its stamp. if let (Some(complete), Some(last), Some(boot_ms)) = From 02b1322bc1bf156a6781f237013fc9aad0f3bf3a Mon Sep 17 00:00:00 2001 From: japabu Date: Fri, 9 Oct 2026 14:39:55 +0200 Subject: [PATCH 3/4] endowment_denied holds a link against toybox's policy only where its target has a row The T14 read the merged testcases boot at 2c7e1be1a red on this member: "/system/bin/134_double_to_signed" is a link to something else. The C corpus's cases ride that boot now, each a link to the comparator test_rs_ccheck, and the walk asserted every link in /system/bin lands on toybox. What a link carries is its target's row: the supervisor's `declared` follows one link and matches a row by its whole path, and a target no row names is answered undeclared, which the caller spawns itself with what it holds (the build's `unnamed_program` says the same of every harness binary). A link to test_rs_ccheck therefore buys no authority a policy table could limit, and a second multicall binary that does carry a row still reds the assertion. The walk now skips a link whose target the manifest names no row for, matched by the whole path as the supervisor matches it, and the line counts those links. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C --- tests/toyos-rust-tests/src/bin/endowment_denied.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/toyos-rust-tests/src/bin/endowment_denied.rs b/tests/toyos-rust-tests/src/bin/endowment_denied.rs index 5971baff5e5..717c6f03835 100644 --- a/tests/toyos-rust-tests/src/bin/endowment_denied.rs +++ b/tests/toyos-rust-tests/src/bin/endowment_denied.rs @@ -50,7 +50,10 @@ //! //! **The applets nobody compared.** A row's granularity is the binary and //! `/system/bin/toybox` is many programs behind many links, so every applet is endowed -//! the union; the last half holds each against a policy table. +//! the union; the last half holds each against a policy table. A link is an +//! applet only where its target has a row: a link to a binary no row names +//! buys no row's authority, since the supervisor answers it undeclared and its +//! spawner endows it, and the C corpus's cases are such links. //! //! **A wrong-typed handle is refused with a word here, and that is a property of //! the check rather than an exception to the policy.** The table resolves rights @@ -533,6 +536,7 @@ fn every_applet_holds_only_what_its_policy_names() { let rows = manifest_rows(&manifest); let mut applets: Vec = Vec::new(); + let mut unrowed = 0; for entry in std::fs::read_dir(BIN).expect("read /system/bin") { let path = entry.expect("a /system/bin entry").path(); let meta = std::fs::symlink_metadata(&path).expect("lstat a /system/bin entry"); @@ -540,6 +544,11 @@ fn every_applet_holds_only_what_its_policy_names() { continue; } let target = std::fs::read_link(&path).expect("read a /system/bin link"); + // A row is matched by its whole path, as the supervisor matches one. + if !target.to_str().is_some_and(|to| rows.contains_key(to)) { + unrowed += 1; + continue; + } assert_eq!( target.to_str(), Some(MULTICALL), @@ -574,7 +583,8 @@ fn every_applet_holds_only_what_its_policy_names() { list this test declares — a row was split, or one grew", ); println!( - " applets: {} links behind {MULTICALL}, {} declared over-grants and no undeclared one", + " applets: {} links behind {MULTICALL}, {} declared over-grants and no undeclared one; \ + {unrowed} links to a binary no row names", applets.len(), over.len(), ); From 01fd3aeddcc3ae2dd770c51b9604d37e2b09ab63 Mon Sep 17 00:00:00 2001 From: japabu Date: Fri, 9 Oct 2026 14:41:16 +0200 Subject: [PATCH 4/4] The allowance issue reads the merged testcases as the T14 measured it, and the hold issue its start there At 2c7e1be1a: the first job 1 191 ms after the kernel's zero, the rows' ten jobs 41 924 ms, ending 43 115 ms in, the 225 members 9 253 ms of the 40 100 ms they add, the last record 52 352 ms of 100 100. acpi_hold started 43 095 ms in, 10.9 s before its give-up. Stamps from that boot's kernel.log, the zero being the `Boot: complete (1135ms)` line's stamp less 1 135 ms. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C --- ...nels-deadline-at-several-times-the-work.md | 33 ++++++++++--------- ...from-boot-whatever-its-runner-was-given.md | 3 +- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md b/issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md index babe17a3399..3ae9e28fd58 100644 --- a/issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md +++ b/issues/a-shared-members-allowance-sets-the-kernels-deadline-at-several-times-the-work.md @@ -71,29 +71,30 @@ above took on `shared` and `ccorpus` ride `testcases` behind its rows' jobs, and those two boots are gone. A clamp is no answer: a ceiling under the derived bound ends a healthy list. -## The merged list, summed from the boots it was +## The merged list -No machine has run the merged `testcases`. Each part between its own markers, -over three readings of the boot that carried it: `testcases` at `9e70cd2e3`, -`473efea22` and `accbd79dd`, `shared` and `ccorpus` at `eff8b20ee`, -`d6d008e88` and `49e12f23b`. +The T14 ran the merged `testcases` once, at `2c7e1be1a`, armed 200 200 and +100 100 ms by the kernel's own lines; each part between its own markers, the +kernel's clock counted from its `Boot: complete (1135ms)` line. Beside it, the +sum of the three boots it was, three readings each: `testcases` at +`9e70cd2e3`, `473efea22` and `accbd79dd`, `shared` and `ccorpus` at +`eff8b20ee`, `d6d008e88` and `49e12f23b`. -| part | least | most | its share of the bound | +| part | merged, `2c7e1be1a` | its parts apart | its share of the bound | |---|---|---|---| -| the boot, to its first job | 1 190 ms | 1 196 ms | | -| the rows' ten jobs | 41 913 ms | 41 983 ms | 60 000 ms | -| the 88 Rust members | 7 068 ms | 7 115 ms | 26 400 ms | -| the 137 C cases | 1 667 ms | 2 183 ms | 13 700 ms | -| the list's last record | 51 838 ms | 52 477 ms | 100 100 ms | +| the boot, to its first job | 1 191 ms | 1 190 to 1 196 ms | | +| the rows' ten jobs | 41 924 ms | 41 913 to 41 983 ms | 60 000 ms | +| the 225 members | 9 253 ms | 8 735 to 9 298 ms | 40 100 ms | +| the list's last record | 52 352 ms | 51 838 to 52 477 ms | 100 100 ms | **The two shares are wide by different factors, and the rows' is the -tighter.** The members take 8 735 to 9 298 ms of the 40 100 ms they add: the -allowances stand at about 4.3 times their work. The rows' jobs end 43 103 to -43 173 ms into the kernel's clock, of the 60 000 ms `toyos_tco::JOB_BOUND_MS` +tighter.** The members took 9 253 ms of the 40 100 ms they add: the +allowances stand at about 4.3 times their work. The rows' jobs ended +43 115 ms into the kernel's clock, of the 60 000 ms `toyos_tco::JOB_BOUND_MS` gives them: about 1.4 times theirs, `counters_metal` alone 32.7 s of it. That constant is the same for a boot of no job and for this boot of ten, and no -allowance widens it. By the same sum the list's last record leaves 47.6 s of -its bound, and nothing reads that margin. +allowance widens it. The list's last record left 47.7 s of its bound, and +nothing reads that margin. **A late expiry adds to the longer bound.** `issues/a-120000-ms-boot-deadline-fired-132859-ms-late-on-the-t14.md` is open: diff --git a/issues/acpi-hold-gives-up-at-a-time-counted-from-boot-whatever-its-runner-was-given.md b/issues/acpi-hold-gives-up-at-a-time-counted-from-boot-whatever-its-runner-was-given.md index 9e68e6df57a..c1283ae44fe 100644 --- a/issues/acpi-hold-gives-up-at-a-time-counted-from-boot-whatever-its-runner-was-given.md +++ b/issues/acpi-hold-gives-up-at-a-time-counted-from-boot-whatever-its-runner-was-given.md @@ -14,7 +14,8 @@ Two things follow. **What the hold is given shrinks with every job before it.** It is the last of its boot's rows' jobs. On the T14 at `accbd79dd` it started 43 161 ms in, with -10.8 s left. A rows' job that adds 10.8 s to that list leaves it nothing: +10.8 s left; on the merged `testcases` at `2c7e1be1a`, 43 095 ms in, with +10.9 s left. A rows' job that adds 10.8 s to that list leaves it nothing: `served_log::Log::until` then panics before it reads a line, with `the log did not show … within 0ns`, and `acpi_server_events` reds over a count line that was written some 20 s earlier and is in the log. The red names the server's