Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions quinn-proto/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ impl Connection {
assert!(max_datagrams != 0);
let max_datagrams = match self.config.enable_segmentation_offload {
false => 1,
true => max_datagrams.min(MAX_TRANSMIT_SEGMENTS),
true => max_datagrams,
};

let mut num_datagrams = 0;
Expand Down Expand Up @@ -564,7 +564,7 @@ impl Connection {
// We need to send 1 more datagram and extend the buffer for that.

// Is 1 more datagram allowed?
if buf_capacity >= segment_size * max_datagrams {
if num_datagrams >= max_datagrams {
// No more datagrams allowed
break;
}
Expand All @@ -577,7 +577,7 @@ impl Connection {
// (see https://github.com/quinn-rs/quinn/issues/1082)
if self
.path
.anti_amplification_blocked(segment_size as u64 * num_datagrams + 1)
.anti_amplification_blocked(segment_size as u64 * (num_datagrams as u64) + 1)
{
trace!("blocked by anti-amplification");
break;
Expand Down Expand Up @@ -965,7 +965,7 @@ impl Connection {
trace!("sending {} bytes in {} datagrams", buf.len(), num_datagrams);
self.path.total_sent = self.path.total_sent.saturating_add(buf.len() as u64);

self.stats.udp_tx.on_sent(num_datagrams, buf.len());
self.stats.udp_tx.on_sent(num_datagrams as u64, buf.len());

Some(Transmit {
destination: self.path.remote,
Expand Down Expand Up @@ -3950,13 +3950,6 @@ const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32;
const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize =
1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4;

/// The maximum amount of datagrams that are sent in a single transmit
///
/// This can be lower than the maximum platform capabilities, to avoid excessive
/// memory allocations when calling `poll_transmit()`. Benchmarks have shown
/// that numbers around 10 are a good compromise.
const MAX_TRANSMIT_SEGMENTS: usize = 10;

/// Perform key updates this many packets before the AEAD confidentiality limit.
///
/// Chosen arbitrarily, intended to be large enough to prevent spurious connection loss.
Expand Down
45 changes: 45 additions & 0 deletions quinn-proto/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1829,6 +1829,51 @@ fn tail_loss_small_segment_size() {
assert_eq!(server_stats.frame_rx.datagram, DGRAM_NUM);
}

// Respect max_datagrams when TLP happens
#[test]
fn tail_loss_respect_max_datagrams() {
let _guard = subscribe();
let client_config = {
let mut c_config = client_config();
let mut t_config = TransportConfig::default();
//Disabling GSO, so only a single segment should be sent per iops
t_config.enable_segmentation_offload(false);
c_config.transport_config(t_config.into());
c_config
};
let mut pair = Pair::default();
let (client_ch, _) = pair.connect_with(client_config);

const DGRAM_LEN: usize = 1000; // High enough so GSO batch could be built
const DGRAM_NUM: u64 = 5; // Enough to build a GSO batch.

info!("Sending an ack-eliciting datagram");
pair.client_conn_mut(client_ch).ping();
pair.drive_client();

// Drop these packets on the server side.
assert!(!pair.server.inbound.is_empty());
pair.server.inbound.clear();

// Doing one step makes the client advance time to the PTO fire time.
info!("stepping forward to PTO");
pair.step();

// start sending datagram batches but the first should be a TLP
info!("Sending datagram batch");
for _ in 0..DGRAM_NUM {
pair.client_datagrams(client_ch)
.send(vec![0; DGRAM_LEN].into(), false)
.unwrap();
}

pair.drive();

// Finally checking the number of sent udp datagrams match the number of iops
let client_stats = pair.client_conn_mut(client_ch).stats();
assert_eq!(client_stats.udp_tx.ios, client_stats.udp_tx.datagrams);
}

#[test]
fn datagram_send_recv() {
let _guard = subscribe();
Expand Down
12 changes: 11 additions & 1 deletion quinn/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,10 @@ impl State {
let now = self.runtime.now();
let mut transmits = 0;

let max_datagrams = self.socket.max_transmit_segments();
let max_datagrams = self
.socket
.max_transmit_segments()
.min(MAX_TRANSMIT_SEGMENTS);

loop {
// Retry the last transmit, or get a new one.
Expand Down Expand Up @@ -1288,3 +1291,10 @@ pub enum SendDatagramError {
/// This limits the amount of CPU resources consumed by datagram generation,
/// and allows other tasks (like receiving ACKs) to run in between.
const MAX_TRANSMIT_DATAGRAMS: usize = 20;

/// The maximum amount of datagrams that are sent in a single transmit
///
/// This can be lower than the maximum platform capabilities, to avoid excessive
/// memory allocations when calling `poll_transmit()`. Benchmarks have shown
/// that numbers around 10 are a good compromise.
const MAX_TRANSMIT_SEGMENTS: usize = 10;