improve logic for fast-retransmitting packets on incoming SACK

This commit is contained in:
arvidn 2018-12-31 15:50:12 +01:00 committed by Arvid Norberg
parent 48a7ab13d5
commit 6f1f466832
1 changed files with 80 additions and 34 deletions

View File

@ -1533,40 +1533,37 @@ void utp_socket_impl::parse_sack(boost::uint16_t const packet_ack, boost::uint8_
mask <<= 1; mask <<= 1;
} }
} }
UTP_LOGV("%8p: got SACK first:%d %s our_seq_nr:%u\n" UTP_LOGV("%8p: got SACK first:%d %s our_seq_nr:%u fast_resend_seq_nr:%d\n"
, static_cast<void*>(this), ack_nr, bitmask.c_str(), m_seq_nr); , static_cast<void*>(this), ack_nr, bitmask.c_str(), m_seq_nr, m_fast_resend_seq_nr);
#endif #endif
// the number of acked packets past the fast re-send sequence number boost::array<boost::uint16_t, 5> resend;
// this is used to determine if we should trigger more fast re-sends int num_to_resend = 0;
int dups = 0;
// the sequence number of the last ACKed packet // this was implicitly lost
int last_ack = packet_ack;
if (!compare_less_wrap((packet_ack + 1) & ACK_MASK, m_fast_resend_seq_nr, ACK_MASK))
{
resend[num_to_resend++] = (packet_ack + 1) & ACK_MASK;
}
// for each byte // for each byte
for (boost::uint8_t const* end = ptr + size; ptr != end; ++ptr) boost::uint8_t const* const start = ptr;
boost::uint8_t const* const end = ptr + size;
for (; ptr != end; ++ptr)
{ {
unsigned char bitfield = unsigned(*ptr); unsigned char const bitfield = unsigned(*ptr);
unsigned char mask = 1; unsigned char mask = 1;
// for each bit // for each bit
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
if (mask & bitfield) if (mask & bitfield)
{ {
last_ack = ack_nr;
if (m_fast_resend_seq_nr == ack_nr)
m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK;
if (compare_less_wrap(m_fast_resend_seq_nr, ack_nr, ACK_MASK)) ++dups;
// this bit was set, ack_nr was received // this bit was set, ack_nr was received
packet* p = m_outbuf.remove(ack_nr); packet* p = m_outbuf.remove(ack_nr);
if (p) if (p)
{ {
*acked_bytes += p->size - p->header_size; *acked_bytes += p->size - p->header_size;
// each ACKed packet counts as a duplicate ack
UTP_LOGV("%8p: duplicate_acks:%u fast_resend_seq_nr:%u\n"
, static_cast<void*>(this), m_duplicate_acks, m_fast_resend_seq_nr);
ack_packet(p, now, min_rtt, ack_nr); ack_packet(p, now, min_rtt, ack_nr);
} }
else else
@ -1576,6 +1573,11 @@ void utp_socket_impl::parse_sack(boost::uint16_t const packet_ack, boost::uint8_
maybe_inc_acked_seq_nr(); maybe_inc_acked_seq_nr();
} }
} }
else if (!compare_less_wrap(ack_nr, m_fast_resend_seq_nr, ACK_MASK)
&& num_to_resend < resend.size())
{
resend[num_to_resend++] = ack_nr;
}
mask <<= 1; mask <<= 1;
ack_nr = (ack_nr + 1) & ACK_MASK; ack_nr = (ack_nr + 1) & ACK_MASK;
@ -1588,30 +1590,74 @@ void utp_socket_impl::parse_sack(boost::uint16_t const packet_ack, boost::uint8_
if (ack_nr == m_seq_nr) break; if (ack_nr == m_seq_nr) break;
} }
// now, scan the bits in reverse, and count the number of ACKed packets. Only
// lost packets followed by 'dup_ack_limit' packets may be resent
// start with the sequence number represented by the last bit in the SACK
// bitmask
int last_resend = (packet_ack + 1 + size * 8) & ACK_MASK;
// the number of acked packets past the fast re-send sequence number
// this is used to determine if we should trigger more fast re-sends
int dups = 0;
for (boost::uint8_t const* i = end; i != start; --i)
{
unsigned char const bitfield = unsigned(i[-1]);
unsigned char mask = 0x80;
// for each bit
for (int k = 0; k < 8; ++k)
{
if (mask & bitfield) ++dups;
if (dups > dup_ack_limit) break;
last_resend = (last_resend - 1) & ACK_MASK;
mask >>= 1;
}
if (dups > dup_ack_limit) break;
}
// we did not get enough packets acked in this message to warrant a resend
if (dups <= dup_ack_limit)
{
UTP_LOGV("%8p: only %d ACKs in SACK, requires more than %d to trigger fast retransmit\n"
, static_cast<void*>(this), dups, dup_ack_limit);
num_to_resend = 0;
}
// now we need to (likely) prune the tail of the resend list, since all
// "unacked" packets that weren't followed by an acked one, don't count
while (num_to_resend > 0 && !compare_less_wrap(resend[num_to_resend - 1], last_resend, ACK_MASK))
{
--num_to_resend;
}
TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1);
// we received more than dup_ack_limit ACKs in this SACK message. bool cut_cwnd = true;
// trigger fast re-send
if (dups >= dup_ack_limit && compare_less_wrap(m_fast_resend_seq_nr, last_ack, ACK_MASK))
{
experienced_loss(m_fast_resend_seq_nr);
int num_resent = 0;
// only re-sending a single packet per sack // we received more than dup_ack_limit ACKs in this SACK message.
// appears to improve performance by making it // trigger fast re-send. This is not an equal check because 3 identical ACKS
// less likely to loose the re-sent packet. Because // are only 2 duplicates
// when that happens, we must time-out in order for (int i = 0; i < num_to_resend; ++i)
// to continue, which takes a long time.
if (m_fast_resend_seq_nr != last_ack)
{ {
packet* p = m_outbuf.at(m_fast_resend_seq_nr); boost::uint16_t const pkt_seq = resend[i];
m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK;
packet* p = m_outbuf.at(pkt_seq);
UTP_LOGV("%8p: Packet %d lost. (fast_resend_seq_nr:%d trigger fast-resend)\n"
, static_cast<void*>(this), pkt_seq, m_fast_resend_seq_nr);
if (p) if (p)
{ {
++num_resent; if (resend_packet(p, true))
if (resend_packet(p, true)) m_duplicate_acks = 0; {
m_duplicate_acks = 0;
m_fast_resend_seq_nr = (pkt_seq + 1) & ACK_MASK;
} }
} }
if (cut_cwnd)
{
experienced_loss(pkt_seq);
cut_cwnd = false;
}
} }
} }