re-work the fast retransmission code to follow algorithm from TCP/IP

Illustrated
This commit is contained in:
kieranm 2009-10-29 15:48:57 +00:00
parent 6d73f82f41
commit dee1d82c11
4 changed files with 94 additions and 42 deletions

View File

@ -45,6 +45,9 @@ HISTORY
to a human-readable string. to a human-readable string.
++ Bugfixes: ++ Bugfixes:
2009-10-28: Kieran Mansley
* tcp_in.c, tcp_out.c, tcp.h: re-work the fast retransmission code
to follow algorithm from TCP/IP Illustrated
2009-10-27: Kieran Mansley 2009-10-27: Kieran Mansley
* tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK * tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK

View File

@ -763,6 +763,7 @@ tcp_receive(struct tcp_pcb *pcb)
u32_t right_wnd_edge; u32_t right_wnd_edge;
u16_t new_tot_len; u16_t new_tot_len;
u8_t accepted_inseq = 0; u8_t accepted_inseq = 0;
int found_dupack = 0;
if (flags & TCP_ACK) { if (flags & TCP_ACK) {
right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2; right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
@ -789,55 +790,62 @@ tcp_receive(struct tcp_pcb *pcb)
#endif /* TCP_WND_DEBUG */ #endif /* TCP_WND_DEBUG */
} }
if (pcb->lastack == ackno) { /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
* duplicate ack if:
* 1) It doesn't ACK new data
* 2) length of received packet is zero (i.e. no payload)
* 3) the advertised window hasn't changed
* 4) There is outstanding unacknowledged data (retransmission timer running)
* 5) The ACK is == biggest ACK sequence number so far seen (snd_una)
*
* If it passes all five, should process as a dupack:
* a) dupacks < 3: do nothing
* b) dupacks == 3: fast retransmit
* c) dupacks > 3: increase cwnd
*
* If it only passes 1-3, should reset dupack counter (and add to
* stats, which we don't do in lwIP)
*
* If it only passes 1, should reset dupack counter
*
*/
/* Clause 1 */
if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
pcb->acked = 0; pcb->acked = 0;
/* Clause 2 */
if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){ if (tcplen == 0) {
++pcb->dupacks; /* Clause 3 */
if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){
if (pcb->dupacks >= 3) { /* Clause 4 */
if (!(pcb->flags & TF_INFR) && pcb->unacked != NULL) { if (pcb->rtime >= 0) {
/* This is fast retransmit. Retransmit the first unacked segment. */ /* Clause 5 */
LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupacks %"U16_F" (%"U32_F"), fast retransmit %"U32_F"\n", if (pcb->lastack == ackno) {
(u16_t)pcb->dupacks, pcb->lastack, found_dupack = 1;
ntohl(pcb->unacked->tcphdr->seqno))); if (pcb->dupacks + 1 > pcb->dupacks)
tcp_rexmit(pcb); ++pcb->dupacks;
/* Set ssthresh to max (FlightSize / 2, 2*SMSS) */ if (pcb->dupacks > 3) {
/*pcb->ssthresh = LWIP_MAX((pcb->snd_max - /* Inflate the congestion window, but not if it means that
pcb->lastack) / 2, the value overflows. */
2 * pcb->mss);*/ if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
/* Set ssthresh to half of the minimum of the current cwnd and the advertised window */ pcb->cwnd += pcb->mss;
if (pcb->cwnd > pcb->snd_wnd) }
pcb->ssthresh = pcb->snd_wnd / 2; } else if (pcb->dupacks == 3) {
else /* Do fast retransmit */
pcb->ssthresh = pcb->cwnd / 2; tcp_rexmit_fast(pcb);
}
/* The minimum value for ssthresh should be 2 MSS */
if (pcb->ssthresh < 2*pcb->mss) {
LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: The minimum value for ssthresh %"U16_F" should be min 2 mss %"U16_F"...\n", pcb->ssthresh, 2*pcb->mss));
pcb->ssthresh = 2*pcb->mss;
}
pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
pcb->flags |= TF_INFR;
} else {
/* Inflate the congestion window, but not if it means that
the value overflows. */
if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
pcb->cwnd += pcb->mss;
} }
} }
} }
}
if (pcb->unacked == NULL && pcb->unsent == NULL) /* If Clause (1) or more is true, but not a duplicate ack, reset
pcb->dupacks = 0; * count of consecutive duplicate acks */
} else { if (!found_dupack) {
LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupack averted %"U32_F" %"U32_F"\n", pcb->dupacks = 0;
pcb->snd_wl2 + pcb->snd_wnd, right_wnd_edge));
} }
} else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){ } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){
/* We come here when the ACK acknowledges new data. */ /* We come here when the ACK acknowledges new data. */
/* Reset the "IN Fast Retransmit" flag, since we are no longer /* Reset the "IN Fast Retransmit" flag, since we are no longer
in fast retransmit. Also reset the congestion window to the in fast retransmit. Also reset the congestion window to the
slow start threshold. */ slow start threshold. */

View File

@ -858,6 +858,46 @@ tcp_rexmit(struct tcp_pcb *pcb)
tcp_output(pcb); tcp_output(pcb);
} }
/**
* Handle retransmission after three dupacks received
*
* @param pcb the tcp_pcb for which to retransmit the first unacked segment
*/
void
tcp_rexmit_fast(struct tcp_pcb *pcb)
{
if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) {
/* This is fast retransmit. Retransmit the first unacked segment. */
LWIP_DEBUGF(TCP_FR_DEBUG,
("tcp_receive: dupacks %"U16_F" (%"U32_F
"), fast retransmit %"U32_F"\n",
(u16_t)pcb->dupacks, pcb->lastack,
ntohl(pcb->unacked->tcphdr->seqno)));
tcp_rexmit(pcb);
/* Set ssthresh to half of the minimum of the current
* cwnd and the advertised window */
if (pcb->cwnd > pcb->snd_wnd)
pcb->ssthresh = pcb->snd_wnd / 2;
else
pcb->ssthresh = pcb->cwnd / 2;
/* The minimum value for ssthresh should be 2 MSS */
if (pcb->ssthresh < 2*pcb->mss) {
LWIP_DEBUGF(TCP_FR_DEBUG,
("tcp_receive: The minimum value for ssthresh %"U16_F
" should be min 2 mss %"U16_F"...\n",
pcb->ssthresh, 2*pcb->mss));
pcb->ssthresh = 2*pcb->mss;
}
pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
pcb->flags |= TF_INFR;
}
}
/** /**
* Send keepalive packets to keep a connection active although * Send keepalive packets to keep a connection active although
* no data is sent over it. * no data is sent over it.

View File

@ -125,6 +125,7 @@ void tcp_input (struct pbuf *p, struct netif *inp);
err_t tcp_output (struct tcp_pcb *pcb); err_t tcp_output (struct tcp_pcb *pcb);
void tcp_rexmit (struct tcp_pcb *pcb); void tcp_rexmit (struct tcp_pcb *pcb);
void tcp_rexmit_rto (struct tcp_pcb *pcb); void tcp_rexmit_rto (struct tcp_pcb *pcb);
void tcp_rexmit_fast (struct tcp_pcb *pcb);
u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb); u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb);
/** /**