Repository navigation
The T14 downloads over HTTPS through ToyOS's own stack inside its list's bound, and netstack's 64 KiB receive window is filed as the ceiling its rate sits on - #818
Conversation
… the requests, and smoltcp is gone `userland/netstack` is now the card, the clock, the kernel's random source, the kernel's pipes and the clients' connections around `toyos-net-node`: every frame goes to the node as the card handed it over, a frame leaves only into room the card said it has, and one request is one call of the node's. - `main.rs` is the loop. `serve.rs` is the requests onto node calls, the id table and `inspect`. `pipes.rs` is the kernel's pipe ends as the node's `ToClient`, `FromClient` and `Wake`. - `dhcp.rs`, `listen.rs`, `mdns.rs`, `resolve.rs` and the two smoltcp test harnesses are deleted; `smoltcp` and `managed` leave `Cargo.lock`. - The node's places come from a memory figure in which a place costs what a listener can be made to hold. - A listener's wake pipe and a datagram socket's receive pipe are watched for their owner's leaving; the 1 ms pass while a request was pending is gone. - `netstack_socket_churn` reads the node's counts. `netstack_streams` and `netstack_lookup` are new, on virtio and on QEMU's e1000e. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
…he streams job reads its stream end Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
…d against the node, and the track says what stage 5 left Closed, each by its own exit: - a-handshake-nobody-finishes-holds-a-listeners-port-shut: the node's `a_handshake_nobody_finishes_leaves_the_port_open_and_is_given_up`. - a-handshake-reset-before-it-ends-hands-its-option-to-the-next-connection: the one-socket listener left with smoltcp; the node's `a_handshake_reset_before_it_ends_leaves_the_next_connection_its_listeners_option`. - netstack-passes-every-millisecond-while-a-request-is-pending: the loop's timeout is the node's next deadline and no 1 ms constant remains. - netstack-keeps-the-datagram-socket-of-a-client-that-sent-no-close: the wake pipe of a listener and the receive pipe of a datagram socket are watched for their owner's leaving; `netstack_socket_churn` reads both counts back. - netstack-removes-a-closed-stream-before-its-fin-leaves: a host peer of `netstack_streams` reads its stream's end after the guest's close. - a-lease-kept-across-a-link-flap-is-not-verified-until-its-renewal: `toyos-dhcp` verifies a kept lease by INIT-REBOOT when the link returns, and netstack reports the change (`Node::link`). - a-shutdown-of-the-sending-half-drops-what-the-send-pipe-still-holds: the node's `a_shutdown_sends_what_the_pipe_held_and_then_the_fin`, and `netstack_streams` shuts down at once and reads every byte back. - netstack-cuts-a-departed-clients-unsent-tail-at-the-ceiling: the node's `a_departed_clients_tail_arrives_whole_while_its_peer_takes_it`. netstack-datagram-sockets-and-listeners-have-no-bound is renamed to what is still true of it: the places are one number for every client. Filed: a DHCP message the client refuses is a log line each. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
QEMU's user network queues one connection on a forwarded port and resets a dial that arrives while one is queued. Under load the harness's two dials, back to back, met that: 3 of 8 runs of the whole suite red at a host load of about 30, the second dial ending `Connection reset by peer (os error 54)` and the frames recorded on the guest's card (`-object filter-dump`) holding one SYN for the listener's port. The harness now reads QEMU's table of connections after each dial and dials the next once it shows the last one carried; 5 of 5 runs of the suite are green at the same load. A failed dial is the test's first word, since it is why the job's wakes do not come. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
The shell that #793 edited is gone: its mdns.rs told the responder its link, drew the delay of each probing and said what became of the name. On the node the first two are `name`'s, and netstack hands `Node::answer_as` its draw and writes the two lines for `Event::Name` as that file wrote them, which `boot_netcase` now waits on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
…dary (#796) and the NVMe install (#797), into the move Conflicts, every hunk accounted for: - Cargo.lock: netstack's dependencies take both sides, toyos-dhcp and the stack's crates from the move, toyos-device-memory and toyos-virtio from main; smoltcp stays gone. - tests/common/qemu.rs: both new profiles kept, HeadlessE1000e beside HeadlessNoUsb, in the enum, the architecture match and the shape table. - userland/netstack/src/virtio_net.rs: main's file, which moved the queue and its tests to toyos-virtio and deleted the test comment the move had reworded; the move's one remaining hunk, "smoltcp" to "the stack" in the transmit queue's comment, is applied again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…a rule in any 10 s, with a count of the rest Any host on the link could send replies the client refuses, one a frame, and each became a line of a log logkeeper keeps on the stick: the node handed every refusal `toyos-dhcp` queued straight to netstack as `Event::Dhcp`. The shard already bounds its crates' refusals with each crate's `RefusalLog` (toyos-net-wire's `counters!`), and `toyos-dhcp` declares its counters with the same macro, so it has one: the node now holds it and admits each of the client's refusals through it where it drains the stack's (`Node::log`), and `Event::Dhcp` carries the count of the rule's refusals since its last line, which netstack writes as the stack's lines are written. `a_hundred_refused_replies_are_one_line_and_a_count` (node, lease) is the issue's exit test: a hundred BOOTP replies give one line and a counter of 100, and one more after 10 s gives a line carrying 99. Closes issues/a-dhcp-message-the-client-refuses-is-a-log-line-each.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
… is named and passed over, and a wake bridges the streams once A client could end netstack with requests alone: every UdpRecvFrom on an idle socket kept its connection's handle in `receiving`, which nothing bounded, and once netstack's handle table was full the next `acceptor.accept().expect(..)` panicked it, and every program lost the network. - `receiving` is a map from socket id to its one waiting receive. A second receive while the first waits is refused `ERR_RESOURCE_EXHAUSTED`; a waiter that hung up is replaced, so a client that left does not shut its socket's receives out. A client's requests now hold no more of netstack's handles than its sockets do. - An accept the kernel refuses has already taken the connection off the port's queue and told its client (`sys_accept`), so netstack names the first refusal of a run, says how many followed when an accept succeeds again, and goes on. - `netstack_socket_churn` asks a second receive on the socket whose first waits and reads `ResourceExhausted`. Swept once more for anything a client or the wire reaches in `serve.rs`, `pipes.rs`, `main.rs` and `client.rs`: every index is bounded by its producer (`FrameRx`'s kept length, [udp]'s cut to the buffer), every cast fits, and each `unreachable!` is the node's contract, not a client's choice. Two NOTEs of the review: - A ready stream watch no longer calls `node.bridge`: it marks the wake, and `Sockets::bridge`, after the poller's answers, passes the streams once however many answers the wake carried. - `shutdown(Read)` is answered from the id table, which already says the id names a stream; it no longer asks `node.nodelay` as an existence check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…eam line is a guest count, and the harness backs off between its QMP queries - `issues/the-pipe-abi-has-no-word-for-an-unreachable-host-or-a-lookup-to-try-again.md`: an ICMP-ended connect, a lookup ending `Unreachable` and one ending `LeaseChanged`, each with the word owed, owner the pipe ABI, exit an ABI change after the move. `serve.rs` cites it where it answers them. - The track's line on a pass over every stream exited on a T14 measurement at 1,000 idle streams, which cannot exist: the places end at 103 and the bench holds no streams. Its exit is now a guest count of netstack's pipe reads per frame at 1 and at 100 idle streams. - `netstack_streams`'s dial asked QEMU's `info usernet` back to back until the forward carried its peer; it now waits 1 ms after each answer, doubling to 64 ms, under the same 30 s ceiling. QEMU raises no event for a carried connection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
… move The node now keeps a send pipe whose FIN is queued until its writer leaves and lets a failed stream's send pipe go first, so std reads a peer's FIN after the client's shutdown as the end. The code merges clean. Issues: `a-shutdown-of-the-sending-half-drops-what-the-send-pipe-still-holds` stays deleted, its exit met by `netstack_streams` (every byte of 4 MiB read back after a shutdown at once); #803's hunk to it was its measured evidence on smoltcp's shell, which leaves with the shell. `a-netstack-client-cannot-tell- a-reset-from-the-peers-fin` takes #803's text; the track keeps the move's idle-stream line and takes #803's pipe-order line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
… IORT decode with the SMMUv3 and ITS encodings (#798) and the shipping members' rows (#799), into the move No conflict; netstack draws from `toyos_abi::syscall::random`, whose source #802 changes beneath the call and not the call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…n a guest ring is ToyOSOrg/ring's toyos-0.17.14, pinned by the lock: the published 0.17.14 package imported byte for byte, a build script that tells a packaged tree from a source tree by `pregenerated/` instead of `.git` (so as a git dependency it uses the package's assembly, runs no perl, and leaves C asserts and warnings-as-errors off, so nothing calls `__assert_fail`), and `toyos` in its Linux-ABI and getrandom lists. The root workspace and the guest test crate both patch it; the test crate also takes the getrandom 0.2 fork, which reaches SYS_RANDOM. The build writes webpki-root-certs' Mozilla roots to every ROOT as /system/etc/ssl/cert.pem (build::TRUST_ROOTS). A test image stages that file whole, the build's roots with its harness's authority after them, and the build refuses one that does not start with its own. A test crate's C is compiled by the toolchain's clang against libc's sysroot, as a program's is: TestBuild hands cargo the cc crate's variables. https_fetch boots tests/netcase with three rustls servers on the host, each under an authority made for the run: https_get, ureq 3 on rustls 0.23 with the roots file and the project's User-Agent, fetches 384 KiB over TLS 1.3 and its ring SHA-256 matches the harness's sha2 one; it refuses a certificate for another address and one from an authority the file does not hold, by name. ring_kat runs on the shared boot: ring against RFC and NIST known answers. doom's build script downloads with ureq on ring and the project's User-Agent; nothing names rustls-rustcrypto. Filed: a refused connection's close that never reached the host's peer, and the roots being data no licence gate reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…s the pipe order With #803's order merged, a client that shut its sending half down keeps the send pipe's reader until it lets the pipe go, so the read after the bulk's last byte meets the server's FIN as the end, which the job asserts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…reads every end in C and in std against a host peer #803's libc half, as posted on that pull request (comment 6082081208), applied unchanged: `recv`'s read of 0 asks the send pipe with a zero-byte write and answers `ECONNRESET` where its reader is gone; `send` after `SHUT_WR` answers `EPIPE` before reaching the pipe; `recv` after `SHUT_RD` answers 0; a refused write is `ECONNRESET` or `EAGAIN`. Its rule is `streamend.rs`, host-tested by `toyos-libc-copies` (48 tests). `libc_sockets` gains a host peer whose first byte names how it ends a stream, and two jobs on it: `tests/netcase/stream_ends.c` reads recv 0 at the peer's FIN after SHUT_WR, EPIPE for a send after it, ECONNRESET on a reset mid-stream and after SHUT_WR, and recv 0 after SHUT_RD; `stream_ends_std` reports six ends in std, and the harness first runs the same source on its host's TCP and requires that report. The rows are #803's measurement's, less `half_close`, which `netstack_streams` holds on both cards. The C job closes no socket: libc's close of one ends the program, already filed. `a-netstack-client-cannot-tell-a-reset-from-the-peers-fin` is closed by its exit: netstack runs on the node, the std job reads each end as the host does, and the C case reads all five rows. Its two citing issues and the track's line now point at the code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…ody reads clippy denies the unread field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
… stream-end order (#803), into the move #803 is the branch merged earlier here, now on main; nothing of it conflicts. #800 changed `userland/netstack/src/mdns.rs`, which the move deletes with the shell on smoltcp. Its two hunks are carried where the move writes the name's events, `userland/netstack/src/serve.rs`: the loss line now says the name is asked for again every `toyos_mdns::RETRY_MS / 1000` s, byte for byte the text #800 wrote. The track's mDNS line takes #800's record (the owner's ruling and what was built) under the move's wording of where the responder runs and where `HOSTNAME` lives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…etcase boot is filed The handshake is rustls on ring at both ends, so it alone would pass a ring wrong at both; ring's known answers are the oracle, and ring picks its code by the CPU's features, so they run on the same guest CPU as the fetch. ring_kat's shared run stays the T14's. The whole suite went red once on netstack_socket_churn: the firmware cleared the screen and said nothing more for 487 s under a load average near 90, and the test was green alone. Filed with the load, as a red under load is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…y's collapsible_match asks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…nd's order (#803) and the .local name's probing (#800), into the HTTPS client ring_kat's line in DRIVEN_AND_SHARED met random_draws' there; the merge keeps main's, and the next commit decides ring_kat's place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
… guests go The move made the node shipped code, and three of its getters had no caller but its own tests: `Node::nodelay` (its last production caller left with serve.rs's `shutdown(Read)`), `Node::listener_nodelay` and `Node::dhcp`. Each is deleted. The listener tests read the option from the accept's answer (`Accepted::nodelay`) and the stream tests on the wire, as `nodelay_reaches_the_stack` did already; the deadline test tells the client's wait from the stack's by `Node::next_deadline` against `Node::shard`'s, and the refusal test counts the client's refusals by the `Event::Dhcp` lines the node drains, the second line's 99 suppressed. `netstack_lookup` and `netstack_lookup_e1000e` are cut. The e1000e arm reached nothing that `netstack_streams_e1000e` (the Intel driver in a guest) and `netstack_lookup` did not. The virtio arm passed on any word a server gave, so a T14 row asking the same `.invalid` name is as stable: the outbound rows' `outbound_internet` reads `lookup=addresses` through the same serve.rs lookup path on the I219, and the resolver's decisions are the node's host tests'. Issues: the pipe ABI's owed words are the network track's; the track's linear-pass line carries `Node::pipe_gone`'s pass of its own; two false sentences, the node's source unchanged by the move and no T14 lease on `toyos-dhcp`, are corrected, the second with the four leases taken since. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…s them, and ring_kat holds RSA, P-384 and SHA-384 to published answers The owner allowed CDLA-Permissive-2.0 for the Mozilla roots. The licence gate now judges them: by the licence of the crate the build copies them out of (`build::TRUST_ROOTS_CRATE`), as data, the one place `licence::DATA_ONLY` passes, and refused unless the text the image carries beside them, /system/etc/ssl/CDLA-Permissive-2.0.txt, is that crate's own LICENSE byte for byte. The licence asks for the text to travel with the data; it is committed under licenses/ and NOTICE names it. The tooling issue that tracked the gap is closed. The `font: bool` the gate threaded through `refused`, `judge_licence` and `Report::judge` becomes `Scope`, since a licence allowed for data alone is a third case. `https_fetch` stages the roots and its harness's authority under a name of its own and passes that path, so `build_and_assemble` writes the roots unconditionally and the test-only match and assert go. The C environment is `GuestEnv`'s, applied by `cargo_build` to every guest build, so `build_programs` and `TestBuild` no longer each derive it. `trust_roots` writes PEM with `pem`, already in the lock through rcgen, and the hand-written encoder, `base64` and the test of the encoder's round trip go. `ring_kat` adds SHA-384 of "abc" (FIPS 180-2 D.1), ECDSA P-384 with SHA-384 (RFC 6979 A.2.6), and RSA PKCS#1 v1.5 and PSS with SHA-256 at 2048 bits (the first passing `[mod = 2048]` SHA256 vector of NIST CAVP's FIPS 186-3 SigVer15 and SigVerPSS), each signature refused with a bit flipped. It runs in `https_fetch` alone now: the shared boot ran it on the same QEMU CPU model as the fetch's boot, so the second run bought nothing, and the fetch's run keeps the oracle beside the handshake it vouches for. The host-tools rows name the host `cc` compiling ring for doom's build script and the toolchain's clang compiling a test crate's ring, and the lost-FIN issue says slirp is not ruled out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
The gate globs every section's path through git before it reads a section as prose, and /system/etc/ssl/cert.pem is a path on ROOT, not in the tree: the licence step of `cargo run -- --ci host` was red on it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…een clears `https_fetch` alone at 216f33b, at load 89 to 99, timed out waiting for ===READY=== after 64 s with the same 58 bytes of firmware escapes on its 16550 and nothing after them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…ardware, and cites no deleted getter The owner's ruling defers the T14 cable pull on the condition that link-down and INIT-REBOOT are recorded as unread on hardware, with that pull as the exit. Round 4's review found no such record: the deleted late-lease issue was closed by host tests, the mDNS paragraph's attended session names when and not what a log must show, and the Intel link issue is about the driver's ring. The track now carries the line, with the exit the review spelled out: one attended boot, cable pulled and returned, whose log shows the link-down line, the held lease verified or taken again with no no-address line, then the name's claim again. The reset-stream line cited `Node::nodelay`, which round 3 deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
… document it already holds `judge_trust_roots_of` ran a fourth `cargo metadata --locked` over the root workspace and built a `Graph` over it only to find one package by name. `judge` already holds a document of that workspace: the one whose `members` holds the root `Cargo.toml`. `webpki-root-certs` is a non-optional dependency of the root package, so whichever feature set that document was resolved with, it resolves the crate, and the gate reads it from there. A shipped tree with no crate in the root workspace is refused by name rather than guessed at. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
The conflict in tests/toyos.rs was two additions at the same sites, stream_ends_std and netstack_streams from #801 and https_get, ring_kat and https_fetch from #810; both kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
|
Mutation at --- a/tests/toyos-rust-tests/src/bin/https_download.rs
+++ b/tests/toyos-rust-tests/src/bin/https_download.rs
@@ -142,7 +142,7 @@
}
let hex: String = digest.finish().as_ref().iter().map(|b| format!("{b:02x}")).collect();
assert!(
- bytes == fetch.bytes && hex == fetch.sha256,
+ bytes == fetch.bytes,
"{} came as {bytes} bytes hashing {hex}, and is {} bytes hashing {}",
fetch.url,
fetch.bytes,Run (exit 1, red as required): |
|
T14 at |
|
Review of #818 at BLOCKER
NOTE
SEND BACK |
…t's bound, and netstack's 64 KiB receive window is filed as the ceiling its rate sits on The owner asked whether ToyOS reaches gigabit and ruled the measurement an internet download. `https_download` fetches one public file with the client `https_get` builds, now one module both read (`https_client.rs`): an unchanged ureq on rustls on ring, the project's User-Agent and nothing else identifying. The file is Rust 1.81.0's x86_64 Linux archive on static.rust-lang.org, 170439044 bytes; the `internet_download` metal row holds a whole body to that length and the SHA-256 the project publishes beside it, so the job holds no pin and prints `sha256=` as `https_get` does. The transfer is bounded by time, not by size. The runner hands each job of a list the bound it gave the list (`toyos_tco::LIST_BOUND_ENV`), and the job's window is that bound less 5 s for its line to reach the stick. A body read to its end says `whole`; one the window closes on says `cut` with what came, and both end 0. The row's verdict is the transport: the job ended 0, some of the body came, a whole body equals the pin, and the T14's MPERF was read. Its rate is printed and never judged. Before the transfer the job times five TCP handshakes to the file's host, `rtt_ms`: the round trip measured in ToyOS, since netstack exposes no connection's SRTT. The first T14 boot read 35.1 Mb/s at 1.4 % busy, which is 65,535 bytes every 14.9 ms: netstack's per-connection receive buffer (`TCP_BUFFER`) with a window shift of 0, one window per round trip. That is filed as the stack's ceiling under the network track, with an exit a T14 reading can fail. Until a boot reads `rtt_ms` beside the rate, the window is the ceiling by arithmetic and not by measurement, and the rate is said to be neither the line's nor the path's. netstack's lease line is declared once in toyos-tco, which netstack says and the harness and the job read. The guest test of this job is cut: the HTTPS GET with the project's User-Agent against the harness's server is `https_fetch`'s, its lease wait cannot be reached in QEMU, and its counters read nothing there. The records that said no T14 row reads the wired card, and the mDNS claim on it unread, now say what the row read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
|
Round 2 evidence at The cut path, measured once with a short window (QEMU, the live internet through slirp)A temporary patch gives diff --git a/tests/downloadcase/system.toml b/tests/downloadcase/system.toml
index ead213477..30443585a 100644
--- a/tests/downloadcase/system.toml
+++ b/tests/downloadcase/system.toml
@@ -15,14 +15,15 @@ serves = ["log"]
[programs.netstack]
service = true
serves = ["netstack"]
-devices = ["pci:8086:15fc"]
+devices = ["pci:8086:15fc", "pci:1af4:1041"]
# `netstack` for the job's lookup and connection, `log` for netstack's word on
# its lease; `counters` and `trace` for every CPU's MPERF and stamp either
# side of the transfer, MPERF being answered only beside `trace`; `dup`
# because the runner hands the job a duplicate.
[programs.test-runner]
-receives = ["netstack", "log"]
+receives = ["netstack", "log", "power"]
+args = ["--bound-ms=12000", "test_rs_https_download"]
syscap = ["counters", "trace", "dup"]
# The block service: the NVMe controller this machine's DATA is on, driven
diff --git a/tests/toyos.rs b/tests/toyos.rs
index 3d3be4d75..60dbd7e16 100644
--- a/tests/toyos.rs
+++ b/tests/toyos.rs
@@ -317,6 +317,7 @@ const MACHINE_TESTS: &[&str] = &[
// certificate by and the sockets under it exist in a ToyOS guest alone,
// and the T14's row is the next stage's, against a server on the bench.
"https_fetch",
+ "https_download_measure",
// The nested-NMI report is a raw write to the 16550, which the T14 does not
// have.
"nested_nmi_is_loud",
@@ -3529,6 +3530,15 @@ fn https_fetch() -> Result<(), String> {
Ok(())
}
+fn https_download_measure() -> Result<(), String> {
+ let crate_path = compile::repo_root().join("tests/toyos-rust-tests");
+ let bins = [("https_download".to_string(), qemu::build_toyos_bin(qemu::SUITE_ARCH, &crate_path, "https_download"))];
+ let mut qemu = QemuInstance::boot_with_options(&compile::repo_root().join("tests/downloadcase"), &[], &bins, BootOptions::default());
+ let mut console = qemu.boot_log().to_string();
+ let end = await_marker(&mut qemu, &mut console, "===TEST_END test_rs_https_download", "the job's end");
+ Err(format!("MEASURED {end:?}\n{console}"))
+}
+
/// Run the machine-shape test, which owns its QEMU: the machine shape *is* the
/// test.
fn run_machine_test(name: &str, test_config: &Path) -> Result<(), String> {
@@ -3539,6 +3549,7 @@ fn run_machine_test(name: &str, test_config: &Path) -> Result<(), String> {
"netstack_streams_e1000e" => netstack_streams(qemu::Profile::HeadlessE1000e),
"libc_sockets" => libc_sockets(),
"https_fetch" => https_fetch(),
+ "https_download_measure" => https_download_measure(),
"nested_nmi_is_loud" => faults::nested_nmi_is_loud(test_config),
"machine_shutdown" => power::machine_shutdown(test_config),
"acpi_power_button" => power::acpi_power_button(test_config),The job's lines (slirp's addresses elided): The window closes 7 000 ms after the kernel's zero by construction, the zero the runner's bound counts from too ( The same with Under slirp the rates are not read: slirp answers the guest's segments itself, so its The judge, offline, on the T14's
|
| case | the line | exit | verdict |
|---|---|---|---|
| whole | whole bytes=170439044 sha256=<pin> secs=… rtt_ms=[…] busy=0.014 … |
0 | PASS |
| another hash | whole bytes=170439044 sha256=000…0 … |
1 | https_download's body is not 170439044 bytes hashing 1a9e… |
| a byte short | whole bytes=170439043 sha256=<pin> … |
1 | the same |
| cut | cut bytes=9682897 secs=… busy=0.014 … |
0 | PASS |
| cut, nothing | cut before the first byte of the body |
1 | https_download read none of the body |
| MPERF unread | cut bytes=9682897 … busy=unread |
1 | https_download read no MPERF on the T14 |
|
T14 at |
|
Review of #818 at Round 1
The design departure (environment variable instead of argv) is judged sound. The runner is the only holder of the bound, so it hands that value on and nothing restates it. Argv would have made BLOCKER
NOTE
SEND BACK |
… cite the T14 reading at 83ef5a9 The receive-window issue's exit asked for a T14 rate above 524.3 over the smallest handshake time. Each handshake time bounds the round trip from above, so that figure bounds the 64 KiB window's ceiling from below, and the reading at 83ef5a9 (35.3 Mb/s against 34.95) met it with the defect unchanged. The exit now asks for both a toyos-net-tcp host test that a stream's SYN carries a window shift above 0 and its empty-buffer window exceeds 65,535, and a T14 rate above twice that figure, which a 64 KiB window reaches only if the fastest handshake takes twice the round trip; the five read spread by 12 %. The issue's Measured section records that boot: rtt_ms [16.8 15.7 15.0 16.0 16.5], 35.3 Mb/s, busy 0.016. The network track's citations of b323e70, a commit amended into 83ef5a9 and reachable from no branch, cite that boot; its log shows the lease line and the mDNS claim with no loss line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
|
Review of #818 at Round 2
The T14 reading stands for this head. BLOCKERNone. NOTE
LAND AFTER NAMED CHANGES |
…and its T14 exit is a reading after the fix with no rate bar The review's note: the issue's T14 arm assumed a line rate nobody had measured. The owner, asked the line's speed, answered "No bar but its a gigabit connection and i would like close to that". Measured now records that the T14 sits on a gigabit connection wired to its router, by the owner's statement, and that the goal is close to it. The exit keeps the toyos-net-tcp host-test arm; its T14 arm becomes a recorded internet_download reading after the fix, with no rate threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
Carries #801 (
wt/toyos-move, ToyOS's own stack) and #810 (wt/toyos-tls, HTTPS onring), which have not landed: this branch mergesmainagain once they have. Its own change is83ef5a9ae,089c9b713and3b196d075, records only (git diff 9b16d2938..HEAD).What changed and why
https_download(tests/toyos-rust-tests/src/bin/https_download.rs, a job) fetches Rust 1.81.0'srust-1.81.0-x86_64-unknown-linux-gnu.tar.xzfromstatic.rust-lang.org/dist/2024-09-05/with the clienthttps_getbuilds. That client is now one module both jobs read (tests/toyos-rust-tests/src/https_client.rs, the wayserved_log.rsis shared): an unchangedureqonrustlsonringthat trusts one roots file, sends exactly theUser-Agenttoyos-build (https://github.com/ToyOSOrg/ToyOS)and nothing else identifying, and hex-encodes a digest. The shared module was the smaller choice. One job would have had to carry the lease wait, the counters, the window and the handshakes as modes ofhttps_get.TOYOS_LIST_BOUND_MS(toyos_tco::LIST_BOUND_ENV) on each job to the bound it gave the list, counted from the kernel's zero, and the harness computes that bound per boot (list_bound_ms). The job's window is that bound less 5 s for its line to reach the stick, and it covers the lease wait, the handshakes and the body. A body read to its end sayswhole bytes=<n> sha256=<hex> …. One the window closes on sayscut bytes=<n> …, orcut before the first byte of the body. Every one of these ends 0. The job holds no pin and asserts nothing about the body: an I/O or TLS error panics, and that is all.internet_download,tests/toyos.rs). The job ended 0 and some of the body came. Awholebody must equal the pin, 170439044 bytes with SHA-2561a9ee8caaa18a3e433fef93cea8a55dc1ebd478ed761b2fef69d4565f9d00e7f, the hash the project publishes beside the file (<url>.sha256), and a full download through slirp at this head hashed to it. The T14's MPERF must have been read. A cut is a reading and is green: TLS's AEAD has already authenticated every record of a cut prefix. The rate is printed as the[download]line and never judged. It is kept out ofmetaltimings, where every number is a ceiling.inspectsnapshot counts sockets and nothing of any one. So before the transfer the job times five TCP handshakes to the file's host's first address, fromconnectcalled to returned, asrtt_ms=[…]. Each is one round trip plus netstack's own time for a connect, so each is an upper bound on the path's round trip. netstack is unchanged apart from the lease line below.83ef5a9aeread 35.3 Mb/s withrtt_ms=[16.8 15.7 15.0 16.0 16.5]at 1.6 % busy. One window of 65,535 bytes per round trip is 524.3 /rtt_msMb/s, 31.2 to 35.0 across those handshakes: netstack's per-connection receive buffer (TCP_BUFFER) with a window shift of 0.toyos-net-tcpdoes negotiate RFC 7323 window scaling, but it takes the smallest shift that fits the buffer in 16 bits, which is 0 for this buffer. Filed asissues/netstacks-receive-buffer-caps-every-connection-at-one-64-kib-window-per-round-trip.mdand owned by the network track; another branch (wt/toyos-wscale) is fixing it. Its exit is both atoyos-net-tcphost test that a stream's SYN carries a window shift above 0 and its empty-buffer window exceeds 65,535, and a T14internet_downloadreading recorded after the fix, with no rate threshold. The owner, asked the line's speed, answered "No bar but its a gigabit connection and i would like close to that": the issue records that the T14 sits on a gigabit connection wired to its router, by his statement, and that the goal is close to it. The line's own rate is not measured, and neither the row, the commits nor this body calls the rate the line's or the path's.toyos_tco::LEASE_SAIDandNO_LEASE_SAIDsit besideLEASE_BOUND_MS. netstack says them, and bothboot_netcaseand the job read them.tests/downloadcaseis its own boot, so the transfer has the machine alone. It runs on every--metalrun: the tree has no nightly tier. Its netstack claims only the I219 (8086:15fc).https_downloadguest test is cut, along with itsMACHINE_TESTSrow, its arm, the job's argv mode and the config's virtio NIC. A cheaper or existing tier already holds what it held. The TLS 1.3 GET with the project'sUser-Agentagainst the harness's server ishttps_fetch's, and that client is now the shared modulehttps_fetchruns throughhttps_get. The lease wait cannot be reached in QEMU. The counters readunreadthere. The pin now lives in the host judge, and the cases below exercise that judge.internet_downloadread at83ef5a9ae: the lease line, thennetstack: mDNS: no host answered for toyos-t14.local; this machine answers as it, and no loss line. Theacpi_holdissue's exit names the bound its runner now hands each list job.Net lines against
9b16d2938: +403 / −39. Production: +23 / −8 (test-runner8/4,netstack4/4,toyos-tco11/0). Tests and config: +314 / −20. Issues: +66 / −11, of which the new issue is 51.Gates
cargo run -- --ci host, at3b196d075Host: 78 step(s), all green)cargo run -- --ci host, at089c9b713Host: 78 step(s), all green)cargo run -- --ci host, at83ef5a9aeHost: 78 step(s), all green)cargo run -- --build-only, at83ef5a9aecargo test, at83ef5a9ae(the whole guest suite: the runner change reaches every boot, list-mode boots among them)44 passed, 44 total)cargo test --test toyos-build -- --metal --metal-readback <scratch>/metal-r2/ internet_download, at83ef5a9aedownload, list bound 60000 ms; nothing touched)PASS internet_download)089c9b713and3b196d075change issue files and nothing else, so the build, guest and T14 gates at83ef5a9aestand for it.Measured once and posted to this PR (#818 (comment)) with the patches. The cut path ran in QEMU against the live internet through slirp, under a temporary
--bound-ms=12000job list:cut bytes=9682897 secs=4.179 mbps=18.5 rtt_ms=[42.2 46.5 34.1 48.6 43.8] busy=unread, the job ending 0 and the list ending inside its bound. The whole path ran the same way at--bound-ms=150000:whole bytes=170439044 sha256=1a9ee8ca…d00e7f, the pin. The judge ran offline on an earlier T14 readback of this branch with the job's line rewritten for each case: whole 0, another hash 1, a byte short 1, cut 0, cut before the first byte 1, MPERF unread 1.The T14 at
83ef5a9ae, run by the orchestrator (#818 (comment)): thedownloadimage, sha256d003f3e3aeb57cff65bc7f0e1f9244fca2cffce1722a170d5357cda95fcbce02as staged,toyos-metal --fat32-checkexit 0, the judge exit 0 withPASS internet_download. The reading:[download] whole bytes=170439044 sha256=1a9ee8ca…f9d00e7f secs=38.657 mbps=35.3 rtt_ms=[16.8 15.7 15.0 16.0 16.5] busy=0.016. The window bound 524.3 / 15.0 is 34.95 Mb/s, and the rate sits at it with the CPUs 1.6 % busy. The boot's log shows the lease line, then the mDNS claim, and no loss line.Unsure
rtt_msis a handshake, not the transfer's SRTT. It includes netstack's connect path and the client's pipe setup, and it is taken idle, before the transfer. A queue that fills under load would make the loaded round trip longer than this. So a rate well under 524.3 / min(rtt_ms) does not by itself clear the window. Under slirp it is not the data path's round trip at all, because slirp answers the guest's segments itself.🤖 Generated with Claude Code
https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C