fix(client): use kernel receive time for downstream OWD #100

Merged
Lochnair merged 1 commits from fix/client-kernel-rx-owd into main 2026-08-15 05:53:41 +00:00
Lochnair commented 2026-08-15 05:45:08 +00:00 (Migrated from github.com)

Summary

  • downstream one-way delay now prefers the Linux SCM_TIMESTAMPNS receive
    timestamp when it is available and plausible;
  • the userspace receive wall time remains the fallback;
  • the kernel timestamp remains exposed unchanged as packet metadata.

The kernel timestamp was already captured, carried through ReceiveMeta into
PacketMeta.kernel_rx_timestamp, and printable as kernel_rx_ns, but it was
not used by any measurement. It is now the preferred client receive endpoint
for the downstream direction only.

Old downstream OWD:

userspace_receive_wall - server_send_wall

New downstream OWD:

preferred_client_receive_wall - server_send_wall

SCM_TIMESTAMPNS is a kernel software receive timestamp, not NIC hardware
ingress time. Preferring it removes socket wakeup latency from the measured
server-to-client delay; it does not remove client/server clock synchronization
error.

Measurement semantics

RTT (raw/adjusted/effective):
    unchanged — client monotonic userspace timestamps
    (received_at.mono - sent_at.mono)

server processing:
    unchanged

upstream OWD (client_to_server):
    unchanged — server receive wall - client send wall

downstream OWD (server_to_client):
    kernel RX wall when valid, userspace receive wall otherwise

IPDV (send / receive / round-trip):
    unchanged — receive IPDV still uses the client userspace monotonic
    receive timestamp; no kernel wall value is passed into `IpdvSample`

No kernel realtime -> monotonic mapping is performed or estimated anywhere.

Validation

Selection lives in one crate-private helper,
ReceiveMeta::preferred_receive_wall(userspace_wall). The kernel timestamp is
used only when:

  1. it exists;
  2. it is not later than the paired userspace receive sample — a datagram
    cannot be kernel-received after the userspace sample that observed it;
  3. its lag behind that sample is at most MAX_KERNEL_RX_LAG (1 s, inclusive).

The bound is a sanity guard against realtime clock discontinuities and stale or
unusable metadata, not an expected scheduler delay — ordinary kernel-to-
userspace latency is orders of magnitude smaller. All arithmetic is checked
(SystemTime::duration_since), so extreme values cannot panic.

An implausible kernel timestamp never invalidates the datagram: the reply is
still measured, using the userspace receive wall time, and
PacketMeta.kernel_rx_timestamp still reports the raw observed value, since
observed metadata and accepted measurement input are different concepts.

The same policy applies everywhere a reply can produce a OneWayDelaySample:
normal echo replies and matched (measurable) late replies, including late
replies matched from timed-out probes. An untracked late reply without retained
send metadata still reports one_way: None.

Where the server supplies only a midpoint wall time, the improved client
receive endpoint is used against it (kernel_client_rx_wall - server_midpoint_wall); the server midpoint itself is untouched.

Compatibility

  • Linux with the ancillary feature gains improved downstream OWD;
  • Linux without ancillary, macOS, FreeBSD and Windows have
    kernel_rx_timestamp == None and therefore preserve exact old behavior;
  • no public API change: ClientTimestamp, PacketMeta, OneWayDelaySample,
    RttSample, ServerTiming and ClientEvent are unchanged in shape;
  • no Cargo feature, nix configuration, irtt-stats production code or
    irtt-server code changed. Receive-delay statistics improve purely as a
    consequence of a better OneWayDelaySample.server_to_client.

Scope

This PR deliberately does NOT address:

  • server-side kernel receive timestamping;
  • RTT receive timestamping;
  • kernel wall -> monotonic mapping;
  • the cross-instant OWD fallback (StampAt::Receive still yields no downstream
    OWD, StampAt::Send still yields no upstream OWD — pinned by tests here);
  • client send timestamp placement relative to the send syscall;
  • kernel transmit timestamping.

Tests

Deterministic, fabricated timestamps, no sleeps.

Selection helper (metadata.rs): missing kernel timestamp; valid earlier
timestamp; timestamp exactly equal to the userspace sample (accepted);
timestamp later than the userspace sample (rejected); implausibly old
timestamp (rejected); the inclusive lag bound and one nanosecond past it;
extreme/pre-epoch values without panic; and metadata preservation when the
timestamp is rejected.

Measurement semantics (session/machine.rs): downstream OWD 20 ms userspace
vs 15 ms kernel for the same reply; no-metadata case unchanged; future and
implausibly old kernel timestamps falling back; measurable late reply using the
same policy; untracked late reply still one_way: None; RTT (raw, adjusted,
effective) and upstream OWD identical with and without kernel metadata;
midpoint; and receive-only/send-only cross-instant behavior preserved.

The existing Linux SCM_TIMESTAMPNS smoke test is unchanged — timing-magnitude
assertions there would be flaky and environmentally meaningless.

Verification

Run on macOS (aarch64-apple-darwin), all successful:

cargo test -p irtt-client --all-features --locked
cargo test -p irtt-stats --all-features --locked
cargo test -p irtt-cli --all-features --locked
cargo fmt --all -- --check
cargo check --workspace --all-targets --all-features --locked
cargo test --workspace --all-targets --all-features --locked
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --all-features --no-deps --locked
cargo check -p irtt-cli --no-default-features --features client --all-targets --locked
cargo check -p irtt-cli --no-default-features --features tui --all-targets --locked
cargo check -p irtt-cli --no-default-features --features server --all-targets --locked
git diff --check

Cross-target cargo check -p irtt-client (compile only):

--all-features --all-targets --target x86_64-unknown-linux-gnu
--no-default-features --all-targets --target x86_64-unknown-linux-gnu
--all-features --target x86_64-unknown-freebsd
--all-features --target x86_64-pc-windows-gnu
## Summary - downstream one-way delay now prefers the Linux `SCM_TIMESTAMPNS` receive timestamp when it is available and plausible; - the userspace receive wall time remains the fallback; - the kernel timestamp remains exposed unchanged as packet metadata. The kernel timestamp was already captured, carried through `ReceiveMeta` into `PacketMeta.kernel_rx_timestamp`, and printable as `kernel_rx_ns`, but it was not used by any measurement. It is now the preferred client receive endpoint for the downstream direction only. Old downstream OWD: userspace_receive_wall - server_send_wall New downstream OWD: preferred_client_receive_wall - server_send_wall `SCM_TIMESTAMPNS` is a kernel *software* receive timestamp, not NIC hardware ingress time. Preferring it removes socket wakeup latency from the measured server-to-client delay; it does not remove client/server clock synchronization error. ## Measurement semantics RTT (raw/adjusted/effective): unchanged — client monotonic userspace timestamps (received_at.mono - sent_at.mono) server processing: unchanged upstream OWD (client_to_server): unchanged — server receive wall - client send wall downstream OWD (server_to_client): kernel RX wall when valid, userspace receive wall otherwise IPDV (send / receive / round-trip): unchanged — receive IPDV still uses the client userspace monotonic receive timestamp; no kernel wall value is passed into `IpdvSample` No kernel realtime -> monotonic mapping is performed or estimated anywhere. ## Validation Selection lives in one crate-private helper, `ReceiveMeta::preferred_receive_wall(userspace_wall)`. The kernel timestamp is used only when: 1. it exists; 2. it is not later than the paired userspace receive sample — a datagram cannot be kernel-received after the userspace sample that observed it; 3. its lag behind that sample is at most `MAX_KERNEL_RX_LAG` (1 s, inclusive). The bound is a sanity guard against realtime clock discontinuities and stale or unusable metadata, not an expected scheduler delay — ordinary kernel-to- userspace latency is orders of magnitude smaller. All arithmetic is checked (`SystemTime::duration_since`), so extreme values cannot panic. An implausible kernel timestamp never invalidates the datagram: the reply is still measured, using the userspace receive wall time, and `PacketMeta.kernel_rx_timestamp` still reports the raw observed value, since observed metadata and accepted measurement input are different concepts. The same policy applies everywhere a reply can produce a `OneWayDelaySample`: normal echo replies and matched (measurable) late replies, including late replies matched from timed-out probes. An untracked late reply without retained send metadata still reports `one_way: None`. Where the server supplies only a midpoint wall time, the improved client receive endpoint is used against it (`kernel_client_rx_wall - server_midpoint_wall`); the server midpoint itself is untouched. ## Compatibility - Linux with the `ancillary` feature gains improved downstream OWD; - Linux without `ancillary`, macOS, FreeBSD and Windows have `kernel_rx_timestamp == None` and therefore preserve exact old behavior; - no public API change: `ClientTimestamp`, `PacketMeta`, `OneWayDelaySample`, `RttSample`, `ServerTiming` and `ClientEvent` are unchanged in shape; - no Cargo feature, nix configuration, `irtt-stats` production code or `irtt-server` code changed. Receive-delay statistics improve purely as a consequence of a better `OneWayDelaySample.server_to_client`. ## Scope This PR deliberately does NOT address: - server-side kernel receive timestamping; - RTT receive timestamping; - kernel wall -> monotonic mapping; - the cross-instant OWD fallback (`StampAt::Receive` still yields no downstream OWD, `StampAt::Send` still yields no upstream OWD — pinned by tests here); - client send timestamp placement relative to the send syscall; - kernel transmit timestamping. ## Tests Deterministic, fabricated timestamps, no sleeps. Selection helper (`metadata.rs`): missing kernel timestamp; valid earlier timestamp; timestamp exactly equal to the userspace sample (accepted); timestamp later than the userspace sample (rejected); implausibly old timestamp (rejected); the inclusive lag bound and one nanosecond past it; extreme/pre-epoch values without panic; and metadata preservation when the timestamp is rejected. Measurement semantics (`session/machine.rs`): downstream OWD 20 ms userspace vs 15 ms kernel for the same reply; no-metadata case unchanged; future and implausibly old kernel timestamps falling back; measurable late reply using the same policy; untracked late reply still `one_way: None`; RTT (raw, adjusted, effective) and upstream OWD identical with and without kernel metadata; midpoint; and receive-only/send-only cross-instant behavior preserved. The existing Linux `SCM_TIMESTAMPNS` smoke test is unchanged — timing-magnitude assertions there would be flaky and environmentally meaningless. ## Verification Run on macOS (aarch64-apple-darwin), all successful: ``` cargo test -p irtt-client --all-features --locked cargo test -p irtt-stats --all-features --locked cargo test -p irtt-cli --all-features --locked cargo fmt --all -- --check cargo check --workspace --all-targets --all-features --locked cargo test --workspace --all-targets --all-features --locked cargo clippy --workspace --all-targets --all-features --locked -- -D warnings RUSTDOCFLAGS="-D warnings" cargo doc --workspace --all-features --no-deps --locked cargo check -p irtt-cli --no-default-features --features client --all-targets --locked cargo check -p irtt-cli --no-default-features --features tui --all-targets --locked cargo check -p irtt-cli --no-default-features --features server --all-targets --locked git diff --check ``` Cross-target `cargo check -p irtt-client` (compile only): ``` --all-features --all-targets --target x86_64-unknown-linux-gnu --no-default-features --all-targets --target x86_64-unknown-linux-gnu --all-features --target x86_64-unknown-freebsd --all-features --target x86_64-pc-windows-gnu ```
Sign in to join this conversation.