+
+static void
+tcp_dispatch_pending_timers (tcp_worker_ctx_t * wrk)
+{
+ u32 n_timers, connection_index, timer_id, thread_index, timer_handle;
+ tcp_connection_t *tc;
+ int i;
+
+ if (!(n_timers = clib_fifo_elts (wrk->pending_timers)))
+ return;
+
+ thread_index = wrk->vm->thread_index;
+ for (i = 0; i < clib_min (n_timers, wrk->max_timers_per_loop); i++)
+ {
+ clib_fifo_sub1 (wrk->pending_timers, timer_handle);
+ connection_index = timer_handle & 0x0FFFFFFF;
+ timer_id = timer_handle >> 28;
+
+ if (PREDICT_TRUE (timer_id != TCP_TIMER_RETRANSMIT_SYN))
+ tc = tcp_connection_get (connection_index, thread_index);
+ else
+ tc = tcp_half_open_connection_get (connection_index);
+
+ if (PREDICT_FALSE (!tc))
+ continue;
+
+ /* Skip if the timer is not pending. Probably it was reset while
+ * waiting for dispatch */
+ if (PREDICT_FALSE (!(tc->pending_timers & (1 << timer_id))))
+ continue;
+
+ tc->pending_timers &= ~(1 << timer_id);
+
+ /* Skip timer if it was rearmed while pending dispatch */
+ if (PREDICT_FALSE (tc->timers[timer_id] != TCP_TIMER_HANDLE_INVALID))
+ continue;
+
+ (*timer_expiration_handlers[timer_id]) (tc);
+ }
+
+ if (thread_index == 0 && clib_fifo_elts (wrk->pending_timers))
+ session_queue_run_on_main_thread (wrk->vm);
+}
+
+static void
+tcp_handle_cleanups (tcp_worker_ctx_t * wrk, clib_time_type_t now)
+{
+ u32 thread_index = wrk->vm->thread_index;
+ tcp_cleanup_req_t *req;
+ tcp_connection_t *tc;
+
+ while (clib_fifo_elts (wrk->pending_cleanups))
+ {
+ req = clib_fifo_head (wrk->pending_cleanups);
+ if (req->free_time > now)
+ break;
+ clib_fifo_sub2 (wrk->pending_cleanups, req);
+ tc = tcp_connection_get (req->connection_index, thread_index);
+ if (PREDICT_FALSE (!tc))
+ continue;
+ session_transport_delete_notify (&tc->connection);
+ tcp_connection_cleanup (tc);
+ }
+}
+
+static void
+tcp_update_time (f64 now, u8 thread_index)
+{
+ tcp_worker_ctx_t *wrk = tcp_get_worker (thread_index);
+
+ tcp_set_time_now (wrk, now);
+ tcp_handle_cleanups (wrk, now);
+ tcp_timer_expire_timers (&wrk->timer_wheel, now);
+ tcp_dispatch_pending_timers (wrk);
+}
+
+static void
+tcp_session_flush_data (transport_connection_t * tconn)
+{
+ tcp_connection_t *tc = (tcp_connection_t *) tconn;
+ if (tc->flags & TCP_CONN_PSH_PENDING)
+ return;
+ tc->flags |= TCP_CONN_PSH_PENDING;
+ tc->psh_seq = tc->snd_una + transport_max_tx_dequeue (tconn) - 1;
+}
+
+static int
+tcp_session_app_rx_evt (transport_connection_t *conn)
+{
+ tcp_connection_t *tc = (tcp_connection_t *) conn;
+ u32 min_free, lo = 4 << 10, hi = 128 << 10;
+
+ if (!(tc->flags & TCP_CONN_ZERO_RWND_SENT))
+ return 0;
+
+ min_free = clib_clamp (transport_rx_fifo_size (conn) >> 3, lo, hi);
+ if (transport_max_rx_enqueue (conn) < min_free)
+ {
+ transport_rx_fifo_req_deq_ntf (conn);
+ return 0;
+ }
+
+ tcp_send_ack (tc);
+
+ return 0;
+}
+
+const static transport_proto_vft_t tcp_proto = {
+ .enable = vnet_tcp_enable_disable,
+ .start_listen = tcp_session_bind,
+ .stop_listen = tcp_session_unbind,
+ .push_header = tcp_session_push_header,
+ .get_connection = tcp_session_get_transport,
+ .get_listener = tcp_session_get_listener,
+ .get_half_open = tcp_half_open_session_get_transport,
+ .attribute = tcp_session_attribute,
+ .connect = tcp_session_open,
+ .half_close = tcp_session_half_close,
+ .close = tcp_session_close,
+ .cleanup = tcp_session_cleanup,
+ .cleanup_ho = tcp_session_cleanup_ho,
+ .reset = tcp_session_reset,
+ .send_params = tcp_session_send_params,
+ .update_time = tcp_update_time,
+ .flush_data = tcp_session_flush_data,
+ .custom_tx = tcp_session_custom_tx,
+ .app_rx_evt = tcp_session_app_rx_evt,
+ .format_connection = format_tcp_session,
+ .format_listener = format_tcp_listener_session,
+ .format_half_open = format_tcp_half_open_session,
+ .transport_options = {
+ .name = "tcp",
+ .short_name = "T",
+ .tx_type = TRANSPORT_TX_PEEK,
+ .service_type = TRANSPORT_SERVICE_VC,
+ },
+};
+
+void
+tcp_connection_tx_pacer_update (tcp_connection_t * tc)
+{
+ if (!transport_connection_is_tx_paced (&tc->connection))
+ return;
+
+ f64 srtt = clib_min ((f64) tc->srtt * TCP_TICK, tc->mrtt_us);
+
+ transport_connection_tx_pacer_update (&tc->connection,
+ tcp_cc_get_pacing_rate (tc),
+ srtt * CLIB_US_TIME_FREQ);
+}
+
+void
+tcp_connection_tx_pacer_reset (tcp_connection_t * tc, u32 window,
+ u32 start_bucket)
+{
+ f64 srtt = clib_min ((f64) tc->srtt * TCP_TICK, tc->mrtt_us);
+ transport_connection_tx_pacer_reset (&tc->connection,
+ tcp_cc_get_pacing_rate (tc),
+ start_bucket,
+ srtt * CLIB_US_TIME_FREQ);
+}
+
+void
+tcp_reschedule (tcp_connection_t * tc)
+{
+ if (tcp_in_cong_recovery (tc) || tcp_snd_space_inline (tc))
+ transport_connection_reschedule (&tc->connection);
+}