Don't delay ACKs for significant window updates

This commit is contained in:
Ruihan Li 2024-05-24 11:41:40 +08:00
parent ef67e7b46c
commit fe179c9184

View File

@ -656,7 +656,6 @@ impl<'a> Socket<'a> {
/// Return the current window field value, including scaling according to RFC 1323.
///
/// Used in internal calculations as well as packet generation.
///
#[inline]
fn scaled_window(&self) -> u16 {
cmp::min(
@ -665,6 +664,25 @@ impl<'a> Socket<'a> {
) as u16
}
/// Return the last window field value, including scaling according to RFC 1323.
///
/// Used in internal calculations as well as packet generation.
///
/// Unlike `remote_last_win`, we take into account new packets received (but not acknowledged)
/// since the last window update and adjust the window length accordingly. This ensures a fair
/// comparison between the last window length and the new window length we're going to
/// advertise.
#[inline]
fn last_scaled_window(&self) -> Option<u16> {
let last_ack = self.remote_last_ack?;
let next_ack = self.remote_seq_no + self.rx_buffer.len();
let last_win = (self.remote_last_win as usize) << self.remote_win_shift;
let last_win_adjusted = last_ack + last_win - next_ack;
Some(cmp::min(last_win_adjusted >> self.remote_win_shift, (1 << 16) - 1) as u16)
}
/// Set the timeout duration.
///
/// A socket with a timeout duration set will abort the connection if either of the following
@ -2130,13 +2148,26 @@ impl<'a> Socket<'a> {
}
}
/// Return whether we should send ACK immediately due to significant window updates.
///
/// ACKs with significant window updates should be sent immediately to let the sender know that
/// more data can be sent. According to the Linux kernel implementation, "significant" means
/// doubling the receive window. The Linux kernel implementation can be found at
/// <https://elixir.bootlin.com/linux/v6.9.9/source/net/ipv4/tcp.c#L1472>.
fn window_to_update(&self) -> bool {
match self.state {
State::SynSent
| State::SynReceived
| State::Established
| State::FinWait1
| State::FinWait2 => self.scaled_window() > self.remote_last_win,
| State::FinWait2 => {
let new_win = self.scaled_window();
if let Some(last_win) = self.last_scaled_window() {
new_win > 0 && new_win / 2 >= last_win
} else {
false
}
}
_ => false,
}
}
@ -2202,7 +2233,7 @@ impl<'a> Socket<'a> {
} else if self.ack_to_transmit() && self.delayed_ack_expired(cx.now()) {
// If we have data to acknowledge, do it.
tcp_trace!("outgoing segment will acknowledge");
} else if self.window_to_update() && self.delayed_ack_expired(cx.now()) {
} else if self.window_to_update() {
// If we have window length increase to advertise, do it.
tcp_trace!("outgoing segment will update window");
} else if self.state == State::Closed {
@ -2452,8 +2483,11 @@ impl<'a> Socket<'a> {
} else if self.seq_to_transmit(cx) {
// We have a data or flag packet to transmit.
PollAt::Now
} else if self.window_to_update() {
// The receive window has been raised significantly.
PollAt::Now
} else {
let want_ack = self.ack_to_transmit() || self.window_to_update();
let want_ack = self.ack_to_transmit();
let delayed_ack_poll_at = match (want_ack, self.ack_delay_timer) {
(false, _) => PollAt::Ingress,
@ -2785,7 +2819,7 @@ mod test {
s.local_seq_no = LOCAL_SEQ + 1;
s.remote_last_seq = LOCAL_SEQ + 1;
s.remote_last_ack = Some(REMOTE_SEQ + 1);
s.remote_last_win = 64;
s.remote_last_win = s.scaled_window();
s
}
@ -6325,6 +6359,63 @@ mod test {
}));
}
#[test]
fn test_window_update_with_delay_ack() {
let mut s = socket_established_with_buffer_sizes(6, 6);
s.ack_delay = Some(Duration::from_millis(10));
send!(
s,
TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"abcdef"[..],
..SEND_TEMPL
}
);
recv_nothing!(s, time 5);
s.recv(|buffer| {
assert_eq!(&buffer[..2], b"ab");
(2, ())
})
.unwrap();
recv!(
s,
time 5,
Ok(TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1 + 6),
window_len: 2,
..RECV_TEMPL
})
);
s.recv(|buffer| {
assert_eq!(&buffer[..1], b"c");
(1, ())
})
.unwrap();
recv_nothing!(s, time 5);
s.recv(|buffer| {
assert_eq!(&buffer[..1], b"d");
(1, ())
})
.unwrap();
recv!(
s,
time 5,
Ok(TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1 + 6),
window_len: 4,
..RECV_TEMPL
})
);
}
#[test]
fn test_fill_peer_window() {
let mut s = socket_established();