- {
- /* Hole must be split */
- if (seq_lt (blk->end, hole->end))
- {
- hole_index = scoreboard_hole_index (sb, hole);
- next_hole = scoreboard_insert_hole (sb, hole_index, blk->end,
- hole->end);
-
- /* Pool might've moved */
- hole = scoreboard_get_hole (sb, hole_index);
- hole->end = blk->start;
- blk_index++;
- ASSERT (hole->next == scoreboard_hole_index (sb, next_hole));
- }
- else if (seq_lt (blk->start, hole->end))
- {
- hole->end = blk->start;
- }
- hole = scoreboard_next_hole (sb, hole);
- }
- }
-
- if (pool_elts (sb->holes) == 1)
- {
- hole = scoreboard_first_hole (sb);
- if (hole->start == ack + sb->snd_una_adv
- && hole->end == tc->snd_una_max)
- scoreboard_remove_hole (sb, hole);
- }
-
- scoreboard_update_bytes (tc, sb);
- sb->last_sacked_bytes = sb->sacked_bytes
- - (old_sacked_bytes - sb->last_bytes_delivered);
- ASSERT (sb->last_sacked_bytes <= sb->sacked_bytes || tcp_in_recovery (tc));
- ASSERT (sb->sacked_bytes == 0 || tcp_in_recovery (tc)
- || sb->sacked_bytes < tc->snd_una_max - seq_max (tc->snd_una, ack));
- ASSERT (sb->last_sacked_bytes + sb->lost_bytes <= tc->snd_una_max
- - seq_max (tc->snd_una, ack) || tcp_in_recovery (tc));
- ASSERT (sb->head == TCP_INVALID_SACK_HOLE_INDEX || tcp_in_recovery (tc)
- || sb->holes[sb->head].start == ack + sb->snd_una_adv);
- TCP_EVT_DBG (TCP_EVT_CC_SCOREBOARD, tc);
-}
-#endif /* CLIB_MARCH_VARIANT */
-
-/**
- * Try to update snd_wnd based on feedback received from peer.
- *
- * If successful, and new window is 'effectively' 0, activate persist
- * timer.
- */
-static void
-tcp_update_snd_wnd (tcp_connection_t * tc, u32 seq, u32 ack, u32 snd_wnd)
-{
- /* If (SND.WL1 < SEG.SEQ or (SND.WL1 = SEG.SEQ and SND.WL2 =< SEG.ACK)), set
- * SND.WND <- SEG.WND, set SND.WL1 <- SEG.SEQ, and set SND.WL2 <- SEG.ACK */
- if (seq_lt (tc->snd_wl1, seq)
- || (tc->snd_wl1 == seq && seq_leq (tc->snd_wl2, ack)))
- {
- tc->snd_wnd = snd_wnd;
- tc->snd_wl1 = seq;
- tc->snd_wl2 = ack;
- TCP_EVT_DBG (TCP_EVT_SND_WND, tc);
-
- if (PREDICT_FALSE (tc->snd_wnd < tc->snd_mss))
- {
- /* Set persist timer if not set and we just got 0 wnd */
- if (!tcp_timer_is_active (tc, TCP_TIMER_PERSIST)
- && !tcp_timer_is_active (tc, TCP_TIMER_RETRANSMIT))
- tcp_persist_timer_set (tc);
- }
- else
- {
- tcp_persist_timer_reset (tc);
- if (PREDICT_FALSE (!tcp_in_recovery (tc) && tc->rto_boff > 0))
- {
- tc->rto_boff = 0;
- tcp_update_rto (tc);
- }
- }
- }
-}
-
-#ifndef CLIB_MARCH_VARIANT
-/**
- * Init loss recovery/fast recovery.
- *
- * Triggered by dup acks as opposed to timer timeout. Note that cwnd is
- * updated in @ref tcp_cc_handle_event after fast retransmit
- */
-void
-tcp_cc_init_congestion (tcp_connection_t * tc)
-{
- tcp_fastrecovery_on (tc);
- tc->snd_congestion = tc->snd_una_max;
- tc->cwnd_acc_bytes = 0;
- tc->snd_rxt_bytes = 0;
- tc->prev_ssthresh = tc->ssthresh;
- tc->prev_cwnd = tc->cwnd;
- tc->cc_algo->congestion (tc);
- TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 4);
-}
-#endif /* CLIB_MARCH_VARIANT */
-
-static void
-tcp_cc_recovery_exit (tcp_connection_t * tc)
-{
- tc->rto_boff = 0;
- tcp_update_rto (tc);
- tc->snd_rxt_ts = 0;
- tc->snd_nxt = tc->snd_una_max;
- tc->rtt_ts = 0;
- tcp_recovery_off (tc);
- TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 3);
-}
-
-#ifndef CLIB_MARCH_VARIANT
-void
-tcp_cc_fastrecovery_exit (tcp_connection_t * tc)
-{
- tc->cc_algo->recovered (tc);
- tc->snd_rxt_bytes = 0;
- tc->rcv_dupacks = 0;
- tc->snd_nxt = tc->snd_una_max;
- tc->snd_rxt_bytes = 0;
- tc->rtt_ts = 0;
-
- tcp_fastrecovery_off (tc);
- tcp_fastrecovery_first_off (tc);
-
- TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 3);
-}
-#endif /* CLIB_MARCH_VARIANT */
-
-static void
-tcp_cc_congestion_undo (tcp_connection_t * tc)
-{
- tc->cwnd = tc->prev_cwnd;
- tc->ssthresh = tc->prev_ssthresh;
- tc->snd_nxt = tc->snd_una_max;
- tc->rcv_dupacks = 0;
- if (tcp_in_recovery (tc))
- tcp_cc_recovery_exit (tc);
- else if (tcp_in_fastrecovery (tc))
- tcp_cc_fastrecovery_exit (tc);
- ASSERT (tc->rto_boff == 0);
- TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 5);
-}
-
-static inline u8
-tcp_cc_is_spurious_timeout_rxt (tcp_connection_t * tc)
-{
- return (tcp_in_recovery (tc) && tc->rto_boff == 1
- && tc->snd_rxt_ts
- && tcp_opts_tstamp (&tc->rcv_opts)
- && timestamp_lt (tc->rcv_opts.tsecr, tc->snd_rxt_ts));
-}
-
-static inline u8
-tcp_cc_is_spurious_fast_rxt (tcp_connection_t * tc)
-{
- return (tcp_in_fastrecovery (tc)
- && tc->cwnd > tc->ssthresh + 3 * tc->snd_mss);
-}
-
-static u8
-tcp_cc_is_spurious_retransmit (tcp_connection_t * tc)
-{
- return (tcp_cc_is_spurious_timeout_rxt (tc)
- || tcp_cc_is_spurious_fast_rxt (tc));
-}
-
-static int
-tcp_cc_recover (tcp_connection_t * tc)
-{
- ASSERT (tcp_in_cong_recovery (tc));
- if (tcp_cc_is_spurious_retransmit (tc))
- {
- tcp_cc_congestion_undo (tc);
- return 1;
- }
-
- if (tcp_in_recovery (tc))
- tcp_cc_recovery_exit (tc);
- else if (tcp_in_fastrecovery (tc))
- tcp_cc_fastrecovery_exit (tc);
-
- ASSERT (tc->rto_boff == 0);
- ASSERT (!tcp_in_cong_recovery (tc));
- ASSERT (tcp_scoreboard_is_sane_post_recovery (tc));
- return 0;
-}
-
-static void
-tcp_cc_update (tcp_connection_t * tc, vlib_buffer_t * b)
-{
- ASSERT (!tcp_in_cong_recovery (tc) || tcp_is_lost_fin (tc));
-
- /* Congestion avoidance */
- tcp_cc_rcv_ack (tc);
-
- /* If a cumulative ack, make sure dupacks is 0 */
- tc->rcv_dupacks = 0;
-
- /* When dupacks hits the threshold we only enter fast retransmit if
- * cumulative ack covers more than snd_congestion. Should snd_una
- * wrap this test may fail under otherwise valid circumstances.
- * Therefore, proactively update snd_congestion when wrap detected. */
- if (PREDICT_FALSE
- (seq_leq (tc->snd_congestion, tc->snd_una - tc->bytes_acked)
- && seq_gt (tc->snd_congestion, tc->snd_una)))
- tc->snd_congestion = tc->snd_una - 1;
-}
-
-static u8
-tcp_should_fastrecover_sack (tcp_connection_t * tc)
-{
- return (TCP_DUPACK_THRESHOLD - 1) * tc->snd_mss < tc->sack_sb.sacked_bytes;
-}
-
-static u8
-tcp_should_fastrecover (tcp_connection_t * tc)
-{
- return (tc->rcv_dupacks == TCP_DUPACK_THRESHOLD
- || tcp_should_fastrecover_sack (tc));
-}