+void
+tcp_program_fastretransmit (tcp_connection_t * tc)
+{
+ tcp_worker_ctx_t *wrk = &tcp_main.wrk_ctx[tc->c_thread_index];
+ if (!(tc->flags & TCP_CONN_FRXT_PENDING))
+ {
+ vec_add1 (wrk->pending_fast_rxt, tc->c_c_index);
+ tc->flags |= TCP_CONN_FRXT_PENDING;
+ }
+}
+
+void
+tcp_do_fastretransmits (u32 thread_index)
+{
+ tcp_worker_ctx_t *wrk = &tcp_main.wrk_ctx[thread_index];
+ u32 max_burst_size, burst_size, n_segs = 0;
+ tcp_connection_t *tc;
+ int i;
+
+ if (vec_len (wrk->pending_fast_rxt) == 0)
+ return;
+
+ vec_append (wrk->ongoing_fast_rxt, wrk->pending_fast_rxt);
+ vec_reset_length (wrk->pending_fast_rxt);
+
+ max_burst_size = VLIB_FRAME_SIZE / vec_len (wrk->ongoing_fast_rxt);
+ max_burst_size = clib_max (max_burst_size, 1);
+
+ for (i = 0; i < vec_len (wrk->ongoing_fast_rxt); i++)
+ {
+ tc = tcp_connection_get (wrk->ongoing_fast_rxt[i], thread_index);
+ tc->flags &= ~TCP_CONN_FRXT_PENDING;
+
+ if (!tcp_in_fastrecovery (tc))
+ continue;
+
+ /* TODO tx pacer instead of this */
+ if (n_segs >= VLIB_FRAME_SIZE)
+ {
+ tcp_program_fastretransmit (tc);
+ continue;
+ }
+
+ burst_size = clib_min (max_burst_size, VLIB_FRAME_SIZE - n_segs);
+
+ if (tc->cwnd > tc->ssthresh + 3 * tc->snd_mss)
+ {
+ /* The first segment MUST be retransmitted */
+ if (tcp_retransmit_first_unacked (tc))
+ {
+ tcp_program_fastretransmit (tc);
+ continue;
+ }
+
+ /* 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;
+
+ /* If cwnd allows, send more data */
+ if (tcp_opts_sack_permitted (&tc->rcv_opts))
+ {
+ scoreboard_init_high_rxt (&tc->sack_sb,
+ tc->snd_una + tc->snd_mss);
+ tc->sack_sb.rescue_rxt = tc->snd_una - 1;
+ n_segs += tcp_fast_retransmit_sack (tc, burst_size);
+ }
+ else
+ {
+ n_segs += tcp_fast_retransmit_no_sack (tc, burst_size);
+ }
+ }
+ else
+ n_segs += tcp_fast_retransmit (tc, burst_size);
+ }
+ vec_reset_length (wrk->ongoing_fast_rxt);
+}
+