Fixes and improved tcp/session debugging
[vpp.git] / src / vnet / session / session.c
index d17c93f..2c2a27c 100644 (file)
@@ -198,21 +198,28 @@ stream_session_lookup_listener4 (ip4_address_t * lcl, u16 lcl_port, u8 proto)
  */
 stream_session_t *
 stream_session_lookup4 (ip4_address_t * lcl, ip4_address_t * rmt,
-                       u16 lcl_port, u16 rmt_port, u8 proto,
-                       u32 my_thread_index)
+                       u16 lcl_port, u16 rmt_port, u8 proto)
 {
   session_manager_main_t *smm = &session_manager_main;
   session_kv4_t kv4;
+  stream_session_t *s;
   int rv;
 
   /* Lookup session amongst established ones */
   make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
   rv = clib_bihash_search_inline_16_8 (&smm->v4_session_hash, &kv4);
   if (rv == 0)
-    return stream_session_get_tsi (kv4.value, my_thread_index);
+    return stream_session_get_from_handle (kv4.value);
 
   /* If nothing is found, check if any listener is available */
-  return stream_session_lookup_listener4 (lcl, lcl_port, proto);
+  if ((s = stream_session_lookup_listener4 (lcl, lcl_port, proto)))
+    return s;
+
+  /* Finally, try half-open connections */
+  rv = clib_bihash_search_inline_16_8 (&smm->v4_half_open_hash, &kv4);
+  if (rv == 0)
+    return stream_session_get_from_handle (kv4.value);
+  return 0;
 }
 
 stream_session_t *
@@ -242,20 +249,27 @@ stream_session_lookup_listener6 (ip6_address_t * lcl, u16 lcl_port, u8 proto)
  * wildcarded local source (listener bound to all interfaces) */
 stream_session_t *
 stream_session_lookup6 (ip6_address_t * lcl, ip6_address_t * rmt,
-                       u16 lcl_port, u16 rmt_port, u8 proto,
-                       u32 my_thread_index)
+                       u16 lcl_port, u16 rmt_port, u8 proto)
 {
   session_manager_main_t *smm = vnet_get_session_manager_main ();
   session_kv6_t kv6;
+  stream_session_t *s;
   int rv;
 
   make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
   rv = clib_bihash_search_inline_48_8 (&smm->v6_session_hash, &kv6);
   if (rv == 0)
-    return stream_session_get_tsi (kv6.value, my_thread_index);
+    return stream_session_get_from_handle (kv6.value);
 
   /* If nothing is found, check if any listener is available */
-  return stream_session_lookup_listener6 (lcl, lcl_port, proto);
+  if ((s = stream_session_lookup_listener6 (lcl, lcl_port, proto)))
+    return s;
+
+  /* Finally, try half-open connections */
+  rv = clib_bihash_search_inline_48_8 (&smm->v6_half_open_hash, &kv6);
+  if (rv == 0)
+    return stream_session_get_from_handle (kv6.value);
+  return 0;
 }
 
 stream_session_t *
@@ -311,9 +325,9 @@ stream_session_half_open_lookup (session_manager_main_t * smm,
 }
 
 transport_connection_t *
-stream_session_lookup_transport4 (ip4_address_t * lcl, ip4_address_t * rmt,
-                                 u16 lcl_port, u16 rmt_port, u8 proto,
-                                 u32 my_thread_index)
+stream_session_lookup_transport_wt4 (ip4_address_t * lcl, ip4_address_t * rmt,
+                                    u16 lcl_port, u16 rmt_port, u8 proto,
+                                    u32 my_thread_index)
 {
   session_manager_main_t *smm = &session_manager_main;
   session_kv4_t kv4;
@@ -340,14 +354,44 @@ stream_session_lookup_transport4 (ip4_address_t * lcl, ip4_address_t * rmt,
   rv = clib_bihash_search_inline_16_8 (&smm->v4_half_open_hash, &kv4);
   if (rv == 0)
     return tp_vfts[proto].get_half_open (kv4.value & 0xFFFFFFFF);
+  return 0;
+}
+
+transport_connection_t *
+stream_session_lookup_transport4 (ip4_address_t * lcl, ip4_address_t * rmt,
+                                 u16 lcl_port, u16 rmt_port, u8 proto)
+{
+  session_manager_main_t *smm = &session_manager_main;
+  session_kv4_t kv4;
+  stream_session_t *s;
+  int rv;
 
+  /* Lookup session amongst established ones */
+  make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto);
+  rv = clib_bihash_search_inline_16_8 (&smm->v4_session_hash, &kv4);
+  if (rv == 0)
+    {
+      s = stream_session_get_from_handle (kv4.value);
+      return tp_vfts[s->session_type].get_connection (s->connection_index,
+                                                     s->thread_index);
+    }
+
+  /* If nothing is found, check if any listener is available */
+  s = stream_session_lookup_listener4 (lcl, lcl_port, proto);
+  if (s)
+    return tp_vfts[s->session_type].get_listener (s->connection_index);
+
+  /* Finally, try half-open connections */
+  rv = clib_bihash_search_inline_16_8 (&smm->v4_half_open_hash, &kv4);
+  if (rv == 0)
+    return tp_vfts[proto].get_half_open (kv4.value & 0xFFFFFFFF);
   return 0;
 }
 
 transport_connection_t *
-stream_session_lookup_transport6 (ip6_address_t * lcl, ip6_address_t * rmt,
-                                 u16 lcl_port, u16 rmt_port, u8 proto,
-                                 u32 my_thread_index)
+stream_session_lookup_transport_wt6 (ip6_address_t * lcl, ip6_address_t * rmt,
+                                    u16 lcl_port, u16 rmt_port, u8 proto,
+                                    u32 my_thread_index)
 {
   session_manager_main_t *smm = &session_manager_main;
   stream_session_t *s;
@@ -377,6 +421,37 @@ stream_session_lookup_transport6 (ip6_address_t * lcl, ip6_address_t * rmt,
   return 0;
 }
 
+transport_connection_t *
+stream_session_lookup_transport6 (ip6_address_t * lcl, ip6_address_t * rmt,
+                                 u16 lcl_port, u16 rmt_port, u8 proto)
+{
+  session_manager_main_t *smm = &session_manager_main;
+  stream_session_t *s;
+  session_kv6_t kv6;
+  int rv;
+
+  make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto);
+  rv = clib_bihash_search_inline_48_8 (&smm->v6_session_hash, &kv6);
+  if (rv == 0)
+    {
+      s = stream_session_get_from_handle (kv6.value);
+      return tp_vfts[s->session_type].get_connection (s->connection_index,
+                                                     s->thread_index);
+    }
+
+  /* If nothing is found, check if any listener is available */
+  s = stream_session_lookup_listener6 (lcl, lcl_port, proto);
+  if (s)
+    return tp_vfts[s->session_type].get_listener (s->connection_index);
+
+  /* Finally, try half-open connections */
+  rv = clib_bihash_search_inline_48_8 (&smm->v6_half_open_hash, &kv6);
+  if (rv == 0)
+    return tp_vfts[proto].get_half_open (kv6.value & 0xFFFFFFFF);
+
+  return 0;
+}
+
 int
 stream_session_create_i (segment_manager_t * sm, transport_connection_t * tc,
                         stream_session_t ** ret_s)
@@ -390,13 +465,15 @@ stream_session_create_i (segment_manager_t * sm, transport_connection_t * tc,
   u32 thread_index = tc->thread_index;
   int rv;
 
+  ASSERT (thread_index == vlib_get_thread_index ());
+
   if ((rv = segment_manager_alloc_session_fifos (sm, &server_rx_fifo,
                                                 &server_tx_fifo,
                                                 &fifo_segment_index)))
     return rv;
 
   /* Create the session */
-  pool_get (smm->sessions[thread_index], s);
+  pool_get_aligned (smm->sessions[thread_index], s, CLIB_CACHE_LINE_BYTES);
   memset (s, 0, sizeof (*s));
 
   /* Initialize backpointers */
@@ -432,33 +509,97 @@ stream_session_create_i (segment_manager_t * sm, transport_connection_t * tc,
   return 0;
 }
 
+/** Enqueue buffer chain tail */
+always_inline int
+session_enqueue_chain_tail (stream_session_t * s, vlib_buffer_t * b,
+                           u32 offset, u8 is_in_order)
+{
+  vlib_buffer_t *chain_b;
+  u32 chain_bi = b->next_buffer;
+  vlib_main_t *vm = vlib_get_main ();
+  u8 *data, len;
+  u16 written = 0;
+  int rv = 0;
+
+  do
+    {
+      chain_b = vlib_get_buffer (vm, chain_bi);
+      data = vlib_buffer_get_current (chain_b);
+      len = chain_b->current_length;
+      if (is_in_order)
+       {
+         rv = svm_fifo_enqueue_nowait (s->server_rx_fifo, len, data);
+         if (rv < len)
+           {
+             return (rv > 0) ? (written + rv) : written;
+           }
+         written += rv;
+       }
+      else
+       {
+         rv = svm_fifo_enqueue_with_offset (s->server_rx_fifo, offset, len,
+                                            data);
+         if (rv)
+           return -1;
+         offset += len;
+       }
+    }
+  while ((chain_bi = (chain_b->flags & VLIB_BUFFER_NEXT_PRESENT)
+         ? chain_b->next_buffer : 0));
+
+  if (is_in_order)
+    return written;
+
+  return 0;
+}
+
 /*
  * Enqueue data for delivery to session peer. Does not notify peer of enqueue
  * event but on request can queue notification events for later delivery by
  * calling stream_server_flush_enqueue_events().
  *
  * @param tc Transport connection which is to be enqueued data
- * @param data Data to be enqueued
- * @param len Length of data to be enqueued
+ * @param b Buffer to be enqueued
+ * @param offset Offset at which to start enqueueing if out-of-order
  * @param queue_event Flag to indicate if peer is to be notified or if event
  *                    is to be queued. The former is useful when more data is
  *                    enqueued and only one event is to be generated.
+ * @param is_in_order Flag to indicate if data is in order
  * @return Number of bytes enqueued or a negative value if enqueueing failed.
  */
 int
-stream_session_enqueue_data (transport_connection_t * tc, u8 * data, u16 len,
-                            u8 queue_event)
+stream_session_enqueue_data (transport_connection_t * tc, vlib_buffer_t * b,
+                            u32 offset, u8 queue_event, u8 is_in_order)
 {
   stream_session_t *s;
-  int enqueued;
+  int enqueued = 0, rv;
 
   s = stream_session_get (tc->s_index, tc->thread_index);
 
-  /* Make sure there's enough space left. We might've filled the pipes */
-  if (PREDICT_FALSE (len > svm_fifo_max_enqueue (s->server_rx_fifo)))
-    return -1;
-
-  enqueued = svm_fifo_enqueue_nowait (s->server_rx_fifo, len, data);
+  if (is_in_order)
+    {
+      enqueued =
+       svm_fifo_enqueue_nowait (s->server_rx_fifo, b->current_length,
+                                vlib_buffer_get_current (b));
+      if (PREDICT_FALSE
+         ((b->flags & VLIB_BUFFER_NEXT_PRESENT) && enqueued > 0))
+       {
+         rv = session_enqueue_chain_tail (s, b, 0, 1);
+         if (rv <= 0)
+           return enqueued;
+         enqueued += rv;
+       }
+    }
+  else
+    {
+      rv = svm_fifo_enqueue_with_offset (s->server_rx_fifo, offset,
+                                        b->current_length,
+                                        vlib_buffer_get_current (b));
+      if (PREDICT_FALSE ((b->flags & VLIB_BUFFER_NEXT_PRESENT) && !rv))
+       rv = session_enqueue_chain_tail (s, b, offset + b->current_length, 0);
+      if (rv)
+       return -1;
+    }
 
   if (queue_event)
     {
@@ -476,7 +617,10 @@ stream_session_enqueue_data (transport_connection_t * tc, u8 * data, u16 len,
        }
     }
 
-  return enqueued;
+  if (is_in_order)
+    return enqueued;
+
+  return 0;
 }
 
 /** Check if we have space in rx fifo to push more bytes */
@@ -484,7 +628,7 @@ u8
 stream_session_no_space (transport_connection_t * tc, u32 thread_index,
                         u16 data_len)
 {
-  stream_session_t *s = stream_session_get (tc->c_index, thread_index);
+  stream_session_t *s = stream_session_get (tc->s_index, thread_index);
 
   if (PREDICT_FALSE (s->session_state != SESSION_STATE_READY))
     return 1;
@@ -496,6 +640,15 @@ stream_session_no_space (transport_connection_t * tc, u32 thread_index,
 }
 
 u32
+stream_session_tx_fifo_max_dequeue (transport_connection_t * tc)
+{
+  stream_session_t *s = stream_session_get (tc->s_index, tc->thread_index);
+  if (s->session_state != SESSION_STATE_READY)
+    return 0;
+  return svm_fifo_max_dequeue (s->server_tx_fifo);
+}
+
+int
 stream_session_peek_bytes (transport_connection_t * tc, u8 * buffer,
                           u32 offset, u32 max_bytes)
 {
@@ -609,7 +762,22 @@ session_manager_flush_enqueue_events (u32 thread_index)
   return errors;
 }
 
+/**
+ * Init fifo tail and head pointers
+ *
+ * Useful if transport uses absolute offsets for tracking ooo segments.
+ */
 void
+stream_session_init_fifos_pointers (transport_connection_t * tc,
+                                   u32 rx_pointer, u32 tx_pointer)
+{
+  stream_session_t *s;
+  s = stream_session_get (tc->s_index, tc->thread_index);
+  svm_fifo_init_pointers (s->server_rx_fifo, rx_pointer);
+  svm_fifo_init_pointers (s->server_tx_fifo, tx_pointer);
+}
+
+int
 stream_session_connect_notify (transport_connection_t * tc, u8 sst,
                               u8 is_fail)
 {
@@ -618,6 +786,7 @@ stream_session_connect_notify (transport_connection_t * tc, u8 sst,
   stream_session_t *new_s = 0;
   u64 handle;
   u32 api_context = 0;
+  int error = 0;
 
   handle = stream_session_half_open_lookup (smm, &tc->lcl_ip, &tc->rmt_ip,
                                            tc->lcl_port, tc->rmt_port,
@@ -625,7 +794,7 @@ stream_session_connect_notify (transport_connection_t * tc, u8 sst,
   if (handle == HALF_OPEN_LOOKUP_INVALID_VALUE)
     {
       clib_warning ("This can't be good!");
-      return;
+      return -1;
     }
 
   /* Get the app's index from the handle we stored when opening connection */
@@ -639,9 +808,12 @@ stream_session_connect_notify (transport_connection_t * tc, u8 sst,
 
       /* Create new session (svm segments are allocated if needed) */
       if (stream_session_create_i (sm, tc, &new_s))
-       return;
-
-      new_s->app_index = app->index;
+       {
+         is_fail = 1;
+         error = -1;
+       }
+      else
+       new_s->app_index = app->index;
     }
 
   /* Notify client */
@@ -650,6 +822,8 @@ stream_session_connect_notify (transport_connection_t * tc, u8 sst,
 
   /* Cleanup session lookup */
   stream_session_half_open_table_del (smm, sst, tc);
+
+  return error;
 }
 
 void
@@ -757,6 +931,7 @@ stream_session_accept (transport_connection_t * tc, u32 listener_index,
 
   s->app_index = server->index;
   s->listener_index = listener_index;
+  s->session_state = SESSION_STATE_ACCEPTING;
 
   /* Shoulder-tap the server */
   if (notify)
@@ -890,8 +1065,13 @@ session_send_session_evt_to_thread (u64 session_handle,
 
   /* Based on request block (or not) for lack of space */
   if (PREDICT_TRUE (q->cursize < q->maxsize))
-    unix_shared_memory_queue_add (q, (u8 *) & evt,
-                                 0 /* do wait for mutex */ );
+    {
+      if (unix_shared_memory_queue_add (q, (u8 *) & evt,
+                                       1 /* do wait for mutex */ ))
+       {
+         clib_warning ("failed to enqueue evt");
+       }
+    }
   else
     {
       clib_warning ("queue full");
@@ -966,24 +1146,47 @@ session_vpp_event_queue_allocate (session_manager_main_t * smm,
 {
   api_main_t *am = &api_main;
   void *oldheap;
+  u32 event_queue_length = 2048;
 
   if (smm->vpp_event_queues[thread_index] == 0)
     {
       /* Allocate event fifo in the /vpe-api shared-memory segment */
       oldheap = svm_push_data_heap (am->vlib_rp);
 
+      if (smm->configured_event_queue_length)
+       event_queue_length = smm->configured_event_queue_length;
+
       smm->vpp_event_queues[thread_index] =
-       unix_shared_memory_queue_init (2048 /* nels $$$$ config */ ,
-                                      sizeof (session_fifo_event_t),
-                                      0 /* consumer pid */ ,
-                                      0
-                                      /* (do not) send signal when queue non-empty */
-       );
+       unix_shared_memory_queue_init
+       (event_queue_length,
+        sizeof (session_fifo_event_t), 0 /* consumer pid */ ,
+        0 /* (do not) send signal when queue non-empty */ );
 
       svm_pop_heap (oldheap);
     }
 }
 
+session_type_t
+session_type_from_proto_and_ip (transport_proto_t proto, u8 is_ip4)
+{
+  if (proto == TRANSPORT_PROTO_TCP)
+    {
+      if (is_ip4)
+       return SESSION_TYPE_IP4_TCP;
+      else
+       return SESSION_TYPE_IP6_TCP;
+    }
+  else
+    {
+      if (is_ip4)
+       return SESSION_TYPE_IP4_UDP;
+      else
+       return SESSION_TYPE_IP6_UDP;
+    }
+
+  return SESSION_N_TYPES;
+}
+
 static clib_error_t *
 session_manager_main_enable (vlib_main_t * vm)
 {
@@ -1005,11 +1208,19 @@ session_manager_main_enable (vlib_main_t * vm)
   vec_validate (smm->sessions, num_threads - 1);
   vec_validate (smm->session_indices_to_enqueue_by_thread, num_threads - 1);
   vec_validate (smm->tx_buffers, num_threads - 1);
-  vec_validate (smm->fifo_events, num_threads - 1);
-  vec_validate (smm->evts_partially_read, num_threads - 1);
+  vec_validate (smm->pending_event_vector, num_threads - 1);
+  vec_validate (smm->free_event_vector, num_threads - 1);
   vec_validate (smm->current_enqueue_epoch, num_threads - 1);
   vec_validate (smm->vpp_event_queues, num_threads - 1);
 
+  for (i = 0; i < num_threads; i++)
+    {
+      vec_validate (smm->free_event_vector[i], 0);
+      _vec_len (smm->free_event_vector[i]) = 0;
+      vec_validate (smm->pending_event_vector[i], 0);
+      _vec_len (smm->pending_event_vector[i]) = 0;
+    }
+
 #if SESSION_DBG
   vec_validate (smm->last_event_poll_by_thread, num_threads - 1);
 #endif
@@ -1019,14 +1230,13 @@ session_manager_main_enable (vlib_main_t * vm)
     session_vpp_event_queue_allocate (smm, i);
 
   /* $$$$ preallocate hack config parameter */
-  for (i = 0; i < 200000; i++)
+  for (i = 0; i < smm->preallocated_sessions; i++)
     {
-      stream_session_t *ss;
-      pool_get (smm->sessions[0], ss);
-      memset (ss, 0, sizeof (*ss));
+      stream_session_t *ss __attribute__ ((unused));
+      pool_get_aligned (smm->sessions[0], ss, CLIB_CACHE_LINE_BYTES);
     }
 
-  for (i = 0; i < 200000; i++)
+  for (i = 0; i < smm->preallocated_sessions; i++)
     pool_put_index (smm->sessions[0], i);
 
   clib_bihash_init_16_8 (&smm->v4_session_hash, "v4 session table",
@@ -1096,7 +1306,35 @@ session_manager_main_init (vlib_main_t * vm)
   return 0;
 }
 
-VLIB_INIT_FUNCTION (session_manager_main_init)
+VLIB_INIT_FUNCTION (session_manager_main_init);
+
+static clib_error_t *
+session_config_fn (vlib_main_t * vm, unformat_input_t * input)
+{
+  session_manager_main_t *smm = &session_manager_main;
+  u32 nitems;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "event-queue-length %d", &nitems))
+       {
+         if (nitems >= 2048)
+           smm->configured_event_queue_length = nitems;
+         else
+           clib_warning ("event queue length %d too small, ignored", nitems);
+       }
+      if (unformat (input, "preallocated-sessions %d",
+                   &smm->preallocated_sessions))
+       ;
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+  return 0;
+}
+
+VLIB_CONFIG_FUNCTION (session_config_fn, "session");
+
 /*
  * fd.io coding-style-patch-verification: ON
  *