IncludeOS icon indicating copy to clipboard operation
IncludeOS copied to clipboard

TCP retransmission and New Reno

Open AndreasAakesson opened this issue 5 years ago • 0 comments

I'm experience a an "issue" which I can't find the correct answer for.

In short, New Reno RFC6582 introduces something called "fast recovery", which is entered on 3 DUP ACKs. On all partial ACK (i.e. ACK for data up until we discovered packet loss) it will retransmit the next unacked segment, and also transmit a new packet (if there is data to be sent).

In an example where I start sending data, and then in the end of the stream drops all packets, I'm experiencing really bad performance. I guess it also could be that the issue I'm seeing is what's mentioned that "New Reno doesn't handle multiple packet losses very well".

1 -> (ACK)
2 -> (ACK)
3 -> (ACK)
4 -> (ACK)
5 -> X
6 -> X
7 -> X
8 -> X
9 -> X
10 -> X

5 RTX 1 RTO
5 RTX 2 RTO
5 RTX 4 RTO
5 RTX 8 RTO (ACK)

So after a while the traffic is let through again, and the retransmission finally gets ACK'ed by the peer. Problem now the congestion control restricts the rest of the retransmissions being sent in reply to this ACK. Can't find anywhere in text if I'm supposed to transmit them here.

6 RTX 1 RTO (ACK)
7 RTX 1 RTO (ACK)
8 RTX 1 RTO (ACK)
9 RTX 1 RTO (ACK)
10 RTX 1 RTO (ACK)

As described here it's the retransmission governing the flow, even tho we could've retransmitted the non ACK'ed data here on every ACK, which makes it horrendously slow (1 second per packet).

void Connection::congestion_control(const Packet_view& in)
{
  const size_t bytes_acked = highest_ack_ - prev_highest_ack_;

  // update recover
  cb.recover = cb.SND.NXT;

  // slow start
  if(cb.slow_start())
  {
    reno_increase_cwnd(bytes_acked);
    printf("<Connection::handle_ack> Slow start. cwnd=%u uw=%u\n",
      cb.cwnd, usable_window());
  }
  // congestion avoidance
  else
  {
    // increase cwnd once per RTT
    cb.cwnd += std::max(SMSS()*SMSS()/cb.cwnd, (uint32_t)1);
    debug2("<Connection::handle_ack> Congestion avoidance. cwnd=%u uw=%u\n",
      cb.cwnd, usable_window());
  } // < congestion avoidance

  // try to write
  if(can_send() and !in.has_tcp_data())
  {
    debug2("<Connection::handle_ack> Can send UW: %u SMSS: %u\n", usable_window(), SMSS());
    send_much();
  }
}

This is the path the incoming ACK is traveling due to no 3 DUP ACK (we will never get it in this case). So we're only opening to send new data due to the congestion window increasing. Neither can we retransmit data in this path since the next segment could contain the ACK for the packet we just retransmitted.

AndreasAakesson avatar Sep 18 '18 10:09 AndreasAakesson