Phase 6.2: async outbound connect — eliminate 3s vCPU stall#74
Closed
dpsoft wants to merge 11 commits intosmoltcp-passt-port-phase6.1-half-closefrom
Closed
Phase 6.2: async outbound connect — eliminate 3s vCPU stall#74dpsoft wants to merge 11 commits intosmoltcp-passt-port-phase6.1-half-closefrom
dpsoft wants to merge 11 commits intosmoltcp-passt-port-phase6.1-half-closefrom
Conversation
9 bite-sized tasks covering the TcpStream::connect_timeout(3s) removal from the vCPU TX path: - New TcpNatState::Connecting state. - Non-blocking socket via socket2 + EINPROGRESS handling. - EPOLLOUT-driven completion in relay_pending_connects, called from drain_to_guest before relay_tcp_nat_data. - getsockopt(SO_ERROR) checks the actual connect outcome on EPOLLOUT readiness. - EpollDispatch::modify (EPOLL_CTL_MOD) flips Write→Read on successful connect. - CONNECT_TIMEOUT (3s) reaping for stuck Connecting flows (silent firewall drop). - Two new pins: connect-to-unreachable-doesn't-block-others (BROKEN_ON_PURPOSE → flips at Task 5) + async-RST-on-failure. - One new bench: process_syn_during_pending_connects parametric on N pending connecting flows (O(1) regression gate). Severity: MEDIUM-HIGH. Today TcpStream::connect_timeout(addr, 3s) on the vCPU thread freezes ALL guest networking for up to 3s when one destination is slow/unreachable.
…ows (BROKEN_ON_PURPOSE)
…ndshakes Replace the synchronous TcpStream::connect_timeout(3s) on the vCPU thread with a non-blocking socket2 connect that returns EINPROGRESS immediately. Flows are inserted with TcpNatState::Connecting and their fd registered for EPOLLOUT. EPOLLOUT-driven completion (Task 5: relay_pending_connects) will promote them to SynReceived and send SYN-ACK. An unreachable destination no longer blocks all other guest networking for 3 seconds.
…connects) Add EpollDispatch::modify (EPOLL_CTL_MOD) to atomically switch a registered fd's event interest from Write to Read without a DEL+ADD window. Add relay_pending_connects, called from drain_to_guest before relay_tcp_nat_data, which drives all pending Connecting flows: checks SO_ERROR, sends SYN-ACK and transitions to SynReceived on success, or RST and Closed on failure. Update rebuild_epoll_from_flow_table to reap Connecting entries post-snapshot (the underlying socket fd is dead after restore). The BROKEN_ON_PURPOSE pin tcp_connect_to_unreachable_does_not_block_other_flows now passes.
Verifies that connecting to a recently-dropped listener port eventually delivers a RST to the guest via relay_pending_connects's SO_ERROR path. Already passes after Task 5 lands; pinned now to guard the behavior.
Add Connecting-timeout detection to relay_tcp_nat_data's timeout sweep. Flows stuck in Connecting for longer than CONNECT_TIMEOUT (3 s — matching the pre-Phase-6.2 synchronous connect_timeout behavior) are reaped: a RST is sent to the guest and the flow table entry is removed. This handles the silent-firewall-drop case where EPOLLOUT never fires.
Add insert_synthetic_connecting_entry bench-helper to SlirpBackend and add the process_syn_during_pending_connects parametric bench (args: 0, 10, 100, 1000 pending connects). Validates that the SYN-handler cost is O(1) in pending-connect backlog size — only flow_table.insert + epoll.register, both O(1).
fmt: cargo fmt --all -- --check PASS
clippy: cargo clippy --workspace --all-targets --all-features PASS
tests: cargo test -p void-box --all-features PASS (353 unit tests)
cargo test --test network_baseline PASS (22 tests)
cargo test --test network_baseline --features bench-helpers
PASS (24 tests)
cargo test --lib network PASS (25 network tests)
bench: cargo bench --bench network --features bench-helpers --no-run
PASS (compiles)
build: cargo build --release PASS
Wall-clock (voidbox-network-bench --iterations 3 --bulk-mb 10):
g2h: ~5.7 Gbps (hardware-limited; no regression from Phase 6.1)
rr: p50=2 µs
crr: p50=10.1 ms
BROKEN_ON_PURPOSE pin (tcp_connect_to_unreachable_does_not_block_other_flows)
flipped from FAIL to PASS at Task 5 (relay_pending_connects landed).
VM suite (conformance, oci_integration, snapshot_integration) skipped:
VOID_BOX_INITRAMFS not available in this environment.
The import was only consumed at the bench-helpers-gated process_syn_during_pending_connects bench (Task 8). Default-feature clippy --bench network failed with -D warnings because the import is unused when bench-helpers is off. Quickest fix: qualify the single bare-name use as smoltcp::wire::Ipv4Address (matches the other call sites in the file) and drop Ipv4Address from the use list.
This was referenced May 5, 2026
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Status: DRAFT. Stacked on PR #73 (Phase 6.1 half-close).
What this branch does
Replaces the synchronous `TcpStream::connect_timeout(addr, 3s)` on the vCPU thread with a non-blocking connect + EPOLLOUT-driven completion on the net-poll thread. The vCPU thread is never blocked on connect again.
Severity: Medium-High — today, a guest opening a connection to ONE unreachable destination freezes ALL guest networking for up to 3 seconds (the connect_timeout). DNS misconfigurations, transient NAT failures, or one slow destination among many freeze the whole pipeline.
Headline win
The new BROKEN_ON_PURPOSE pin `tcp_connect_to_unreachable_does_not_block_other_flows` flips at Task 5 (`91947a3`) when EPOLLOUT-driven completion lands.
Architecture
Bench evidence
`scripts/bench-compare.sh --baseline 47868f0 --skip-vm`:
Wall-clock vs master
What changed (10 commits)
Validation
Open follow-ups