- u32 rxt_delivered;
-
- if (tcp_in_fastrecovery (tc) && tcp_opts_sack_permitted (&tc->rcv_opts))
- {
- if (tc->bytes_acked)
- goto partial_ack;
- tcp_program_fastretransmit (tcp_get_worker (tc->c_thread_index), tc);
- return;
- }
- /*
- * Duplicate ACK. Check if we should enter fast recovery, or if already in
- * it account for the bytes that left the network.
- */
- else if (is_dack && !tcp_in_recovery (tc))
- {
- TCP_EVT_DBG (TCP_EVT_DUPACK_RCVD, tc, 1);
- ASSERT (tc->snd_una != tc->snd_nxt || tc->sack_sb.last_sacked_bytes);
-
- tc->rcv_dupacks++;
-
- /* Pure duplicate ack. If some data got acked, it's handled lower */
- if (tc->rcv_dupacks > TCP_DUPACK_THRESHOLD && !tc->bytes_acked)
- {
- ASSERT (tcp_in_fastrecovery (tc));
- tc->cc_algo->rcv_cong_ack (tc, TCP_CC_DUPACK);
- return;
- }
- else if (tcp_should_fastrecover (tc))
- {
- u32 pacer_wnd;
-
- ASSERT (!tcp_in_fastrecovery (tc));
-
- /* Heuristic to catch potential late dupacks
- * after fast retransmit exits */
- if (is_dack && tc->snd_una == tc->snd_congestion
- && timestamp_leq (tc->rcv_opts.tsecr, tc->tsecr_last_ack))
- {
- tc->rcv_dupacks = 0;
- return;
- }
-
- tcp_cc_init_congestion (tc);
- tc->cc_algo->rcv_cong_ack (tc, TCP_CC_DUPACK);
-
- if (tcp_opts_sack_permitted (&tc->rcv_opts))
- {
- tc->cwnd = tc->ssthresh;
- scoreboard_init_high_rxt (&tc->sack_sb, tc->snd_una);
- }
- else
- {
- /* Post retransmit update cwnd to ssthresh and account for the
- * three segments that have left the network and should've been
- * buffered at the receiver XXX */
- tc->cwnd = tc->ssthresh + 3 * tc->snd_mss;
- }
-
- /* Constrain rate until we get a partial ack */
- pacer_wnd = clib_max (0.1 * tc->cwnd, 2 * tc->snd_mss);
- tcp_connection_tx_pacer_reset (tc, pacer_wnd,
- 0 /* start bucket */ );
- tcp_program_fastretransmit (tcp_get_worker (tc->c_thread_index),
- tc);
- return;
- }
- else if (!tc->bytes_acked
- || (tc->bytes_acked && !tcp_in_cong_recovery (tc)))
- {
- tc->cc_algo->rcv_cong_ack (tc, TCP_CC_DUPACK);
- return;
- }
- else
- goto partial_ack;
- }
- /* Don't allow entry in fast recovery if still in recovery, for now */
- else if (0 && is_dack && tcp_in_recovery (tc))
- {
- /* If of of the two conditions lower hold, reset dupacks because
- * we're probably after timeout (RFC6582 heuristics).
- * If Cumulative ack does not cover more than congestion threshold,
- * and:
- * 1) The following doesn't hold: The congestion window is greater
- * than SMSS bytes and the difference between highest_ack
- * and prev_highest_ack is at most 4*SMSS bytes
- * 2) Echoed timestamp in the last non-dup ack does not equal the
- * stored timestamp
- */
- if (seq_leq (tc->snd_una, tc->snd_congestion)
- && ((!(tc->cwnd > tc->snd_mss
- && tc->bytes_acked <= 4 * tc->snd_mss))
- || (tc->rcv_opts.tsecr != tc->tsecr_last_ack)))
- {
- tc->rcv_dupacks = 0;
- return;
- }
- }
-
- if (!tc->bytes_acked)
- return;
-
-partial_ack:
- TCP_EVT_DBG (TCP_EVT_CC_PACK, tc);
-
- /*
- * Legitimate ACK. 1) See if we can exit recovery
- */
-
- /* Update the pacing rate. For the first partial ack we move from
- * the artificially constrained rate to the one after congestion */
- tcp_connection_tx_pacer_update (tc);
-
- if (seq_geq (tc->snd_una, tc->snd_congestion))
- {
- tcp_retransmit_timer_update (tc);
-
- /* If spurious return, we've already updated everything */
- if (tcp_cc_recover (tc))
- {
- tc->tsecr_last_ack = tc->rcv_opts.tsecr;
- return;
- }
-
- /* Treat as congestion avoidance ack */
- tcp_cc_rcv_ack (tc);
- return;
- }
-
- /*
- * Legitimate ACK. 2) If PARTIAL ACK try to retransmit
- */
-
- /* XXX limit this only to first partial ack? */
- tcp_retransmit_timer_update (tc);
-
- /* RFC6675: If the incoming ACK is a cumulative acknowledgment,
- * reset dupacks to 0. Also needed if in congestion recovery */
- tc->rcv_dupacks = 0;
-
- /* Post RTO timeout don't try anything fancy */
- if (tcp_in_recovery (tc))
- {
- tcp_cc_rcv_ack (tc);
- transport_add_tx_event (&tc->connection);
- return;
- }
-
- /* Remove retransmitted bytes that have been delivered */
- if (tcp_opts_sack_permitted (&tc->rcv_opts))
- {
- ASSERT (tc->bytes_acked + tc->sack_sb.snd_una_adv
- >= tc->sack_sb.last_bytes_delivered
- || (tc->flags & TCP_CONN_FINSNT));
-
- /* If we have sacks and we haven't gotten an ack beyond high_rxt,
- * remove sacked bytes delivered */
- if (seq_lt (tc->snd_una, tc->sack_sb.high_rxt))
- {
- rxt_delivered = tc->bytes_acked + tc->sack_sb.snd_una_adv
- - tc->sack_sb.last_bytes_delivered;
- ASSERT (tc->snd_rxt_bytes >= rxt_delivered);
- tc->snd_rxt_bytes -= rxt_delivered;
- }
- else
- {
- /* Apparently all retransmitted holes have been acked */
- tc->snd_rxt_bytes = 0;
- tc->sack_sb.high_rxt = tc->snd_una;
- }
- }
- else
- {
- tcp_fastrecovery_first_on (tc);
- if (tc->snd_rxt_bytes > tc->bytes_acked)
- tc->snd_rxt_bytes -= tc->bytes_acked;
- else
- tc->snd_rxt_bytes = 0;
- }