tcp: optimize tcp output
[vpp.git] / src / vnet / tcp / tcp.c
index 1c44ef0..854577b 100644 (file)
 
 tcp_main_t tcp_main;
 
+typedef struct
+{
+  fib_protocol_t nh_proto;
+  vnet_link_t link_type;
+  ip46_address_t ip;
+  u32 sw_if_index;
+  u8 is_add;
+} tcp_add_del_adj_args_t;
+
+static void
+tcp_add_del_adj_cb (tcp_add_del_adj_args_t * args)
+{
+  u32 ai;
+  if (args->is_add)
+    {
+      adj_nbr_add_or_lock (args->nh_proto, args->link_type, &args->ip,
+                          args->sw_if_index);
+    }
+  else
+    {
+      ai = adj_nbr_find (FIB_PROTOCOL_IP6, VNET_LINK_IP6, &args->ip,
+                        args->sw_if_index);
+      if (ai != ADJ_INDEX_INVALID)
+       adj_unlock (ai);
+    }
+}
+
+static void
+tcp_add_del_adjacency (tcp_connection_t * tc, u8 is_add)
+{
+  tcp_add_del_adj_args_t args = {
+    .nh_proto = FIB_PROTOCOL_IP6,
+    .link_type = VNET_LINK_IP6,
+    .ip = tc->c_rmt_ip,
+    .sw_if_index = tc->sw_if_index,
+    .is_add = is_add
+  };
+  vlib_rpc_call_main_thread (tcp_add_del_adj_cb, (u8 *) & args,
+                            sizeof (args));
+}
+
 static u32
 tcp_connection_bind (u32 session_index, transport_endpoint_t * lcl)
 {
@@ -178,6 +219,9 @@ tcp_connection_cleanup (tcp_connection_t * tc)
       /* Make sure all timers are cleared */
       tcp_connection_timers_reset (tc);
 
+      if (!tc->c_is_ip4 && ip6_address_is_link_local_unicast (&tc->c_rmt_ip6))
+       tcp_add_del_adjacency (tc, 0);
+
       /* Poison the entry */
       if (CLIB_DEBUG > 0)
        memset (tc, 0xFA, sizeof (*tc));
@@ -233,18 +277,19 @@ tcp_connection_reset (tcp_connection_t * tc)
       tcp_connection_cleanup (tc);
       break;
     case TCP_STATE_ESTABLISHED:
+      tcp_connection_timers_reset (tc);
+      /* Set the cleanup timer, in case the session layer/app don't
+       * cleanly close the connection */
+      tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME);
+      stream_session_reset_notify (&tc->connection);
+      break;
     case TCP_STATE_CLOSE_WAIT:
     case TCP_STATE_FIN_WAIT_1:
     case TCP_STATE_FIN_WAIT_2:
     case TCP_STATE_CLOSING:
       tc->state = TCP_STATE_CLOSED;
       TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc);
-
-      /* Make sure all timers are cleared */
       tcp_connection_timers_reset (tc);
-      stream_session_reset_notify (&tc->connection);
-
-      /* Wait for cleanup from session layer but not forever */
       tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME);
       break;
     case TCP_STATE_CLOSED:
@@ -281,17 +326,22 @@ tcp_connection_close (tcp_connection_t * tc)
       tc->state = TCP_STATE_FIN_WAIT_1;
       break;
     case TCP_STATE_ESTABLISHED:
-      if (!stream_session_tx_fifo_max_dequeue (&tc->connection))
+      if (!session_tx_fifo_max_dequeue (&tc->connection))
        tcp_send_fin (tc);
       else
        tc->flags |= TCP_CONN_FINPNDG;
       tc->state = TCP_STATE_FIN_WAIT_1;
       break;
     case TCP_STATE_CLOSE_WAIT:
-      tcp_connection_timers_reset (tc);
-      tcp_send_fin (tc);
-      tc->state = TCP_STATE_LAST_ACK;
-      tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
+      if (!session_tx_fifo_max_dequeue (&tc->connection))
+       {
+         tcp_send_fin (tc);
+         tcp_connection_timers_reset (tc);
+         tc->state = TCP_STATE_LAST_ACK;
+         tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
+       }
+      else
+       tc->flags |= TCP_CONN_FINPNDG;
       break;
     case TCP_STATE_FIN_WAIT_1:
       tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
@@ -322,11 +372,9 @@ tcp_session_cleanup (u32 conn_index, u32 thread_index)
   tcp_connection_t *tc;
   tc = tcp_connection_get (conn_index, thread_index);
   tcp_connection_timers_reset (tc);
-
-  /* Wait for the session tx events to clear */
   tc->state = TCP_STATE_CLOSED;
   TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc);
-  tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME);
+  tcp_connection_cleanup (tc);
 }
 
 /**
@@ -493,6 +541,9 @@ tcp_connection_init_vars (tcp_connection_t * tc)
   if (tc->state == TCP_STATE_SYN_RCVD)
     tcp_init_snd_vars (tc);
 
+  if (!tc->c_is_ip4 && ip6_address_is_link_local_unicast (&tc->c_rmt_ip6))
+    tcp_add_del_adjacency (tc, 1);
+
   //  tcp_connection_fib_attach (tc);
 }
 
@@ -686,9 +737,9 @@ format_tcp_vars (u8 * s, va_list * args)
   s = format (s, " snd_wnd %u rcv_wnd %u snd_wl1 %u snd_wl2 %u\n",
              tc->snd_wnd, tc->rcv_wnd, tc->snd_wl1 - tc->irs,
              tc->snd_wl2 - tc->iss);
-  s = format (s, " flight size %u send space %u rcv_wnd_av %d\n",
+  s = format (s, " flight size %u out space %u cc space %u rcv_wnd_av %u\n",
              tcp_flight_size (tc), tcp_available_output_snd_space (tc),
-             tcp_rcv_wnd_available (tc));
+             tcp_available_cc_snd_space (tc), tcp_rcv_wnd_available (tc));
   s = format (s, " cong %U ", format_tcp_congestion_status, tc);
   s = format (s, "cwnd %u ssthresh %u rtx_bytes %u bytes_acked %u\n",
              tc->cwnd, tc->ssthresh, tc->snd_rxt_bytes, tc->bytes_acked);
@@ -974,7 +1025,7 @@ tcp_snd_space (tcp_connection_t * tc)
    * bytes of previously unsent data. */
   if (tcp_in_fastrecovery (tc) && !tcp_fastrecovery_sent_1_smss (tc))
     {
-      if (tcp_available_output_snd_space (tc) < tc->snd_mss)
+      if (tcp_available_cc_snd_space (tc) < tc->snd_mss)
        return 0;
       tcp_fastrecovery_1_smss_on (tc);
       return tc->snd_mss;
@@ -1008,11 +1059,21 @@ tcp_session_tx_fifo_offset (transport_connection_t * trans_conn)
   return (tc->snd_nxt - tc->snd_una);
 }
 
+void
+tcp_update_time (f64 now, u8 thread_index)
+{
+  tcp_set_time_now (thread_index);
+  tw_timer_expire_timers_16t_2w_512sl (&tcp_main.timer_wheels[thread_index],
+                                      now);
+  tcp_flush_frames_to_output (thread_index);
+}
+
 /* *INDENT-OFF* */
 const static transport_proto_vft_t tcp_proto = {
+  .enable = vnet_tcp_enable_disable,
   .bind = tcp_session_bind,
   .unbind = tcp_session_unbind,
-  .push_header = tcp_push_header,
+  .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,
@@ -1021,10 +1082,13 @@ const static transport_proto_vft_t tcp_proto = {
   .cleanup = tcp_session_cleanup,
   .send_mss = tcp_session_send_mss,
   .send_space = tcp_session_send_space,
+  .update_time = tcp_update_time,
   .tx_fifo_offset = tcp_session_tx_fifo_offset,
   .format_connection = format_tcp_session,
   .format_listener = format_tcp_listener_session,
   .format_half_open = format_tcp_half_open_session,
+  .tx_type = TRANSPORT_TX_PEEK,
+  .service_type = TRANSPORT_SERVICE_VC,
 };
 /* *INDENT-ON* */
 
@@ -1151,8 +1215,6 @@ clib_error_t *
 tcp_main_enable (vlib_main_t * vm)
 {
   tcp_main_t *tm = vnet_get_tcp_main ();
-  ip_protocol_info_t *pi;
-  ip_main_t *im = &ip_main;
   vlib_thread_main_t *vtm = vlib_get_thread_main ();
   clib_error_t *error = 0;
   u32 num_threads;
@@ -1171,20 +1233,9 @@ tcp_main_enable (vlib_main_t * vm)
    * Registrations
    */
 
-  /* Register with IP */
-  pi = ip_get_protocol_info (im, IP_PROTOCOL_TCP);
-  if (pi == 0)
-    return clib_error_return (0, "TCP protocol info AWOL");
-  pi->format_header = format_tcp_header;
-  pi->unformat_pg_edit = unformat_pg_tcp_header;
-
   ip4_register_protocol (IP_PROTOCOL_TCP, tcp4_input_node.index);
   ip6_register_protocol (IP_PROTOCOL_TCP, tcp6_input_node.index);
 
-  /* Register as transport with session layer */
-  transport_register_protocol (TRANSPORT_PROTO_TCP, 1, &tcp_proto);
-  transport_register_protocol (TRANSPORT_PROTO_TCP, 0, &tcp_proto);
-
   /*
    * Initialize data structures
    */
@@ -1282,7 +1333,25 @@ clib_error_t *
 tcp_init (vlib_main_t * vm)
 {
   tcp_main_t *tm = vnet_get_tcp_main ();
+  ip_main_t *im = &ip_main;
+  ip_protocol_info_t *pi;
+
+  /* Session layer, and by implication tcp, are disabled by default */
   tm->is_enabled = 0;
+
+  /* Register with IP for header parsing */
+  pi = ip_get_protocol_info (im, IP_PROTOCOL_TCP);
+  if (pi == 0)
+    return clib_error_return (0, "TCP protocol info AWOL");
+  pi->format_header = format_tcp_header;
+  pi->unformat_pg_edit = unformat_pg_tcp_header;
+
+  /* Register as transport with session layer */
+  transport_register_protocol (TRANSPORT_PROTO_TCP, &tcp_proto,
+                              FIB_PROTOCOL_IP4, tcp4_output_node.index);
+  transport_register_protocol (TRANSPORT_PROTO_TCP, &tcp_proto,
+                              FIB_PROTOCOL_IP6, tcp6_output_node.index);
+
   tcp_api_reference ();
   return 0;
 }
@@ -1293,7 +1362,6 @@ static clib_error_t *
 tcp_config_fn (vlib_main_t * vm, unformat_input_t * input)
 {
   tcp_main_t *tm = vnet_get_tcp_main ();
-  u64 tmp;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
@@ -1304,19 +1372,9 @@ tcp_config_fn (vlib_main_t * vm, unformat_input_t * input)
       else if (unformat (input, "preallocated-half-open-connections %d",
                         &tm->preallocated_half_open_connections))
        ;
-      else if (unformat (input, "local-endpoints-table-memory %U",
-                        unformat_memory_size, &tmp))
-       {
-         if (tmp >= 0x100000000)
-           return clib_error_return (0, "memory size %llx (%lld) too large",
-                                     tmp, tmp);
-         tm->local_endpoints_table_memory = tmp;
-       }
-      else if (unformat (input, "local-endpoints-table-buckets %d",
-                        &tm->local_endpoints_table_buckets))
+      else if (unformat (input, "buffer-fail-fraction %f",
+                        &tm->buffer_fail_fraction))
        ;
-
-
       else
        return clib_error_return (0, "unknown input `%U'",
                                  format_unformat_error, input);