feat(client): capture kernel transmit timestamps
On Linux, best-effort upgrade the socket's SO_TIMESTAMPING flags to TX_SOFTWARE | OPT_ID | OPT_TSONLY after a successful Open and before the first Echo probe. Promote the #103 error-queue foundation to production: MSG_ERRQUEUE is drained opportunistically with a fixed per-call budget at several natural choke points (after a probe send, before processing a reply, before evaluating timeouts), and each automatic OPT_ID timestamp is correlated directly with wire_seq — safe because Linux rolls back its speculative sk_tskey increment on every send failure, wire_seq and the kernel counter start together at every Open, and a socket can never be reopened for a second session. The observed kernel TX wall timestamp is retained on PendingProbe (surviving pending -> timed-out retention) as dormant metadata only; RTT, OWD, and IPDV are unchanged and continue to use the userspace send timestamp exclusively. Capability failure is fully non-fatal and RX timestamping is unaffected either way. Non-Linux and non-ancillary builds are unchanged.
This commit is contained in:
10 files changed
+948
-69
No files matched your search
@@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet, TryReserveError, VecDeque},
|
||||
time::Instant,
|
||||
time::{Instant, SystemTime},
|
||||
};
|
||||
|
||||
use crate::{error::ClientError, timing::ClientTimestamp};
|
||||
@@ -10,6 +10,12 @@ pub(crate) struct PendingProbe {
|
||||
pub wire_seq: u32,
|
||||
pub sent_at: ClientTimestamp,
|
||||
pub timeout_at: Instant,
|
||||
/// Observed Linux kernel TX_SOFTWARE wall timestamp for this probe's
|
||||
/// send, when the socket has TX timestamping enabled and a matching
|
||||
/// `MSG_ERRQUEUE` record has been drained. Dormant metadata only: no
|
||||
/// measurement in this change reads it. `sent_at` remains authoritative
|
||||
/// for RTT, timeout, and current one-way delay.
|
||||
pub kernel_tx_timestamp: Option<SystemTime>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -158,6 +164,14 @@ impl PendingMap {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
/// Mutable access to a still-pending probe by wire sequence, without
|
||||
/// disturbing its position in the timeout order. Used to attach an
|
||||
/// observed kernel TX timestamp to a probe that has not yet completed or
|
||||
/// timed out.
|
||||
pub fn get_mut(&mut self, wire_seq: u32) -> Option<&mut PendingProbe> {
|
||||
self.map.get_mut(&wire_seq).map(|entry| &mut entry.probe)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "tokio", test))]
|
||||
pub fn next_timeout_deadline(&self) -> Option<Instant> {
|
||||
self.first
|
||||
@@ -266,6 +280,13 @@ impl TimedOutMap {
|
||||
removed
|
||||
}
|
||||
|
||||
/// Mutable access to a still-retained timed-out probe by wire sequence,
|
||||
/// without disturbing eviction order. Used to attach an observed kernel
|
||||
/// TX timestamp that arrives after the probe has already timed out.
|
||||
pub fn get_mut(&mut self, wire_seq: u32) -> Option<&mut PendingProbe> {
|
||||
self.map.get_mut(&wire_seq)
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.map.clear();
|
||||
self.insertion_order.clear();
|
||||
@@ -367,6 +388,7 @@ mod tests {
|
||||
wire_seq: seq,
|
||||
sent_at: ts(timeout_at - Duration::from_secs(1)),
|
||||
timeout_at,
|
||||
kernel_tx_timestamp: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user