nat: ED: global session LRU list 79/26379/8
authorKlement Sekera <ksekera@cisco.com>
Sun, 5 Apr 2020 08:22:47 +0000 (10:22 +0200)
committerKlement Sekera <ksekera@cisco.com>
Wed, 8 Apr 2020 09:07:49 +0000 (11:07 +0200)
Maintain a global session LRU allowing reuse of expired session instead
of relying on a scavenging mechanism to periodically walk sessions.
Whenever a new session is being allocated in slow path, also attempt to
free an expired session from global LRU list.

Type: improvement

Signed-off-by: Klement Sekera <ksekera@cisco.com>
Change-Id: I9edde9ec138de67c9a4888e915b0490ec16415fa

src/plugins/nat/in2out_ed.c
src/plugins/nat/nat.c
src/plugins/nat/nat.h
src/plugins/nat/nat44/inlines.h
src/plugins/nat/nat_inlines.h
src/plugins/nat/out2in_ed.c

index 0ca1dd8..807a716 100644 (file)
@@ -951,7 +951,6 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm,
   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
   u32 tcp_packets = 0, udp_packets = 0, icmp_packets = 0, other_packets =
     0, def_slow;
-  u32 tcp_closed_drops = 0;
 
   def_slow = is_output_feature ? NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH :
     NAT_NEXT_IN2OUT_ED_SLOW_PATH;
@@ -1065,7 +1064,6 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm,
              else
                {
                  // session in transitory timeout, drop
-                 ++tcp_closed_drops;
                  b0->error = node->errors[NAT_IN2OUT_ED_ERROR_TCP_CLOSED];
                  next0 = NAT_NEXT_DROP;
                }
@@ -1078,11 +1076,9 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm,
            (f64) nat44_session_get_timeout (sm, s0);
          if (now >= sess_timeout_time)
            {
-             // delete session
              nat_free_session_data (sm, s0, thread_index, 0);
              nat44_delete_session (sm, s0, thread_index);
-
-             // session no longer exists, go slow path
+             // session is closed, go slow path
              next0 = def_slow;
              goto trace0;
            }
index 5fc3eef..fdf6334 100755 (executable)
@@ -593,6 +593,14 @@ nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u,
                          s->per_user_list_head_index,
                          per_user_translation_list_elt - tsm->list_pool);
 
+      dlist_elt_t *global_lru_list_elt;
+      pool_get (tsm->global_lru_pool, global_lru_list_elt);
+      global_lru_list_elt->value = s - tsm->sessions;
+      s->global_lru_index = global_lru_list_elt - tsm->global_lru_pool;
+      clib_dlist_addtail (tsm->global_lru_pool, tsm->global_lru_head_index,
+                         s->global_lru_index);
+      s->last_lru_update = now;
+
       s->user_index = u - tsm->users;
       vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
                               pool_elts (tsm->sessions));
@@ -607,7 +615,7 @@ snat_session_t *
 nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index,
                      f64 now)
 {
-  snat_session_t *s;
+  snat_session_t *s = NULL;
   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
 
   dlist_elt_t *oldest_elt;
@@ -633,6 +641,7 @@ nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index,
       return 0;
     }
 
+  /* first try to reuse an expired session from this ip */
   oldest_index =
     clib_dlist_remove_head (tsm->list_pool,
                            u->sessions_per_user_list_head_index);
@@ -647,13 +656,44 @@ nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index,
       clib_dlist_addtail (tsm->list_pool,
                          u->sessions_per_user_list_head_index, oldest_index);
       s = nat44_session_reuse_old (sm, u, s, thread_index, now);
+      s->last_lru_update = now;
     }
   else
     {
-      // alloc new session
       clib_dlist_addhead (tsm->list_pool,
                          u->sessions_per_user_list_head_index, oldest_index);
-    alloc_new:
+      s = NULL;
+    }
+
+alloc_new:
+  /* try to free an expired session from global LRU list */
+  if (!s)
+    {
+      oldest_index = clib_dlist_remove_head (tsm->global_lru_pool,
+                                            tsm->global_lru_head_index);
+      if (~0 != oldest_index)
+       {
+         oldest_elt = pool_elt_at_index (tsm->global_lru_pool, oldest_index);
+         s = pool_elt_at_index (tsm->sessions, oldest_elt->value);
+
+         sess_timeout_time =
+           s->last_heard + (f64) nat44_session_get_timeout (sm, s);
+         if (now >= sess_timeout_time
+             || (s->tcp_close_timestamp && now >= s->tcp_close_timestamp))
+           {
+             nat_free_session_data (sm, s, thread_index, 0);
+             nat44_ed_delete_session (sm, s, thread_index, 0);
+           }
+         else
+           {
+             clib_dlist_addhead (tsm->global_lru_pool,
+                                 tsm->global_lru_head_index, oldest_index);
+           }
+         s = NULL;
+       }
+    }
+  if (!s)
+    {
       s = nat44_session_alloc_new (tsm, u, now);
       vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
                               pool_elts (tsm->sessions));
@@ -4075,6 +4115,13 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
 
               pool_alloc (tsm->sessions, sm->max_translations);
               pool_alloc (tsm->list_pool, sm->max_translations);
+              pool_alloc (tsm->global_lru_pool, sm->max_translations);
+
+              dlist_elt_t *head;
+              pool_get (tsm->global_lru_pool, head);
+              tsm->global_lru_head_index = head - tsm->global_lru_pool;
+              clib_dlist_init (tsm->global_lru_pool,
+                               tsm->global_lru_head_index);
 
               if (sm->endpoint_dependent)
                 {
index 46dc040..d2b114a 100644 (file)
@@ -320,6 +320,10 @@ typedef CLIB_PACKED(struct
   u32 per_user_index;
   u32 per_user_list_head_index;
 
+  /* index in global LRU list */
+  u32 global_lru_index;
+  f64 last_lru_update;
+
   /* Last heard timer */
   f64 last_heard;
 
@@ -521,6 +525,10 @@ typedef struct
   /* Pool of doubly-linked list elements */
   dlist_elt_t *list_pool;
 
+  /* LRU session list - head is stale, tail is fresh */
+  dlist_elt_t *global_lru_pool;
+  u32 global_lru_head_index;
+
   /* NAT thread index */
   u32 snat_thread_index;
 
index fcaf573..631c4cd 100644 (file)
@@ -89,6 +89,14 @@ nat44_session_alloc_new (snat_main_per_thread_data_t * tsm, snat_user_t * u,
                      s->per_user_list_head_index,
                      per_user_translation_list_elt - tsm->list_pool);
 
+  dlist_elt_t *lru_list_elt;
+  pool_get (tsm->global_lru_pool, lru_list_elt);
+  s->global_lru_index = lru_list_elt - tsm->global_lru_pool;
+  clib_dlist_addtail (tsm->global_lru_pool, tsm->global_lru_head_index,
+                     s->global_lru_index);
+  lru_list_elt->value = s - tsm->sessions;
+  s->last_lru_update = now;
+
   s->ha_last_refreshed = now;
   return s;
 }
@@ -207,6 +215,8 @@ nat44_user_session_cleanup (snat_user_t * u, u32 thread_index, f64 now)
 
       clib_dlist_remove (tsm->list_pool, s->per_user_index);
       pool_put_index (tsm->list_pool, s->per_user_index);
+      clib_dlist_remove (tsm->global_lru_pool, s->global_lru_index);
+      pool_put_index (tsm->global_lru_pool, s->global_lru_index);
       pool_put (tsm->sessions, s);
       vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
                               pool_elts (tsm->sessions));
index bdcfd39..e5f2d96 100644 (file)
@@ -286,8 +286,9 @@ nat44_delete_user_with_no_session (snat_main_t * sm, snat_user_t * u,
 }
 
 always_inline void
-nat44_delete_session (snat_main_t * sm, snat_session_t * ses,
-                     u32 thread_index)
+nat44_delete_session_internal (snat_main_t * sm, snat_session_t * ses,
+                              u32 thread_index, int global_lru_delete
+                              /* delete from global LRU list */ )
 {
   snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data,
                                                       thread_index);
@@ -301,6 +302,11 @@ nat44_delete_session (snat_main_t * sm, snat_session_t * ses,
 
   clib_dlist_remove (tsm->list_pool, ses->per_user_index);
   pool_put_index (tsm->list_pool, ses->per_user_index);
+  if (global_lru_delete)
+    {
+      clib_dlist_remove (tsm->global_lru_pool, ses->global_lru_index);
+    }
+  pool_put_index (tsm->global_lru_pool, ses->global_lru_index);
   pool_put (tsm->sessions, ses);
   vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
                           pool_elts (tsm->sessions));
@@ -318,6 +324,22 @@ nat44_delete_session (snat_main_t * sm, snat_session_t * ses,
     }
 }
 
+always_inline void
+nat44_delete_session (snat_main_t * sm, snat_session_t * ses,
+                     u32 thread_index)
+{
+  return nat44_delete_session_internal (sm, ses, thread_index, 1);
+}
+
+always_inline void
+nat44_ed_delete_session (snat_main_t * sm, snat_session_t * ses,
+                        u32 thread_index, int global_lru_delete
+                        /* delete from global LRU list */ )
+{
+  return nat44_delete_session_internal (sm, ses, thread_index,
+                                       global_lru_delete);
+}
+
 /** \brief Set TCP session state.
     @return 1 if session was closed, otherwise 0
 */
@@ -430,10 +452,22 @@ always_inline void
 nat44_session_update_lru (snat_main_t * sm, snat_session_t * s,
                          u32 thread_index)
 {
-  clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
-                    s->per_user_index);
-  clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                     s->per_user_list_head_index, s->per_user_index);
+  /* don't update too often - timeout is in a magnitude of seconds anyway */
+  if (s->last_heard > s->last_lru_update + 1)
+    {
+      clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
+                        s->per_user_index);
+      clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
+                         s->per_user_list_head_index, s->per_user_index);
+
+      clib_dlist_remove (sm->per_thread_data[thread_index].global_lru_pool,
+                        s->global_lru_index);
+      clib_dlist_addtail (sm->per_thread_data[thread_index].global_lru_pool,
+                         sm->
+                         per_thread_data[thread_index].global_lru_head_index,
+                         s->global_lru_index);
+      s->last_lru_update = s->last_heard;
+    }
 }
 
 always_inline void
index e1eda32..5b70b0c 100644 (file)
@@ -693,7 +693,6 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm,
   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
   u32 tcp_packets = 0, udp_packets = 0, icmp_packets = 0, other_packets =
     0, fragments = 0;
-  u32 tcp_closed_drops = 0;
 
   stats_node_index = sm->ed_out2in_node_index;
 
@@ -789,7 +788,6 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm,
                {
                  // session in transitory timeout, drop
                  b0->error = node->errors[NAT_OUT2IN_ED_ERROR_TCP_CLOSED];
-                 ++tcp_closed_drops;
                  next0 = NAT_NEXT_DROP;
                }
              goto trace0;
@@ -801,11 +799,9 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm,
            (f64) nat44_session_get_timeout (sm, s0);
          if (now >= sess_timeout_time)
            {
-             // delete session
+             // session is closed, go slow path
              nat_free_session_data (sm, s0, thread_index, 0);
              nat44_delete_session (sm, s0, thread_index);
-
-             // session no longer exists, go slow path
              next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
              goto trace0;
            }