Skip to content

Commit

Permalink
fix: Mark all packets TX'ed before PTO as lost
Browse files Browse the repository at this point in the history
We'd previously only mark 1 one or two packets as lost when a PTO fired.
That meant that we potentially didn't RTX all data that we could have
(i.e., that was in lost packets that we didn't mark lost).

This also changes the probing code to suppress redundant keep-alives,
i.e., PINGs that we sent for other reasons, which could double as
keep-alives but did not.

Broken out of mozilla#1998
  • Loading branch information
larseggert committed Sep 19, 2024
1 parent d6279bf commit 392bbac
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 30 deletions.
23 changes: 11 additions & 12 deletions neqo-transport/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2133,30 +2133,29 @@ impl Connection {
return true;
}

let pto = path.borrow().rtt().pto(PacketNumberSpace::ApplicationData);
let probe = if untracked && builder.packet_empty() || force_probe {
// If we received an untracked packet and we aren't probing already
// or the PTO timer fired: probe.
true
} else if !builder.packet_empty() {
// The packet only contains an ACK. Check whether we want to
// force an ACK with a PING so we can stop tracking packets.
self.loss_recovery.should_probe(pto, now)
} else if self.streams.need_keep_alive() {
// We need to keep the connection alive, including sending a PING again.
self.idle_timeout.send_keep_alive(now, pto, tokens)
} else {
let pto = path.borrow().rtt().pto(PacketNumberSpace::ApplicationData);
if !builder.packet_empty() {
// The packet only contains an ACK. Check whether we want to
// force an ACK with a PING so we can stop tracking packets.
self.loss_recovery.should_probe(pto, now)
} else if self.streams.need_keep_alive() {
// We need to keep the connection alive, including sending
// a PING again.
self.idle_timeout.send_keep_alive(now, pto, tokens)
} else {
false
}
false
};
if probe {
// Nothing ack-eliciting and we need to probe; send PING.
debug_assert_ne!(builder.remaining(), 0);
builder.encode_varint(crate::frame::FRAME_TYPE_PING);
let stats = &mut self.stats.borrow_mut().frame_tx;
stats.ping += 1;
// Any PING we send here can also double as a keep-alive.
self.idle_timeout.send_keep_alive(now, pto, tokens);
}
probe
}
Expand Down
29 changes: 11 additions & 18 deletions neqo-transport/src/recovery/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,18 +165,15 @@ impl LossRecoverySpace {
self.in_flight_outstanding > 0
}

pub fn pto_packets(&mut self, count: usize) -> impl Iterator<Item = &SentPacket> {
self.sent_packets
.iter_mut()
.filter_map(|sent| {
if sent.pto() {
qtrace!("PTO: marking packet {} lost ", sent.pn());
Some(&*sent)
} else {
None
}
})
.take(count)
pub fn pto_packets(&mut self) -> impl Iterator<Item = &SentPacket> {
self.sent_packets.iter_mut().filter_map(|sent| {
if sent.pto() {
qtrace!("PTO: marking packet {} lost ", sent.pn());
Some(&*sent)
} else {
None
}
})
}

pub fn pto_base_time(&self) -> Option<Instant> {
Expand Down Expand Up @@ -823,7 +820,7 @@ impl LossRecovery {
}

/// This checks whether the PTO timer has fired and fires it if needed.
/// When it has, mark a few packets as "lost" for the purposes of having frames
/// When it has, mark packets as "lost" for the purposes of having frames
/// regenerated in subsequent packets. The packets aren't truly lost, so
/// we have to clone the `SentPacket` instance.
fn maybe_fire_pto(&mut self, rtt: &RttEstimate, now: Instant, lost: &mut Vec<SentPacket>) {
Expand All @@ -836,11 +833,7 @@ impl LossRecovery {
if t <= now {
qdebug!([self], "PTO timer fired for {}", pn_space);
let space = self.spaces.get_mut(*pn_space).unwrap();
lost.extend(
space
.pto_packets(PtoState::pto_packet_count(*pn_space))
.cloned(),
);
lost.extend(space.pto_packets().cloned());

pto_space = pto_space.or(Some(*pn_space));
}
Expand Down

0 comments on commit 392bbac

Please sign in to comment.