From ba5f9bc7534bcf58225b0658993728b1f4d47a67 Mon Sep 17 00:00:00 2001 From: Klement Sekera Date: Sun, 5 Apr 2020 10:22:47 +0200 Subject: [PATCH] nat: ED: global session LRU list 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 Change-Id: I9edde9ec138de67c9a4888e915b0490ec16415fa --- src/plugins/nat/in2out_ed.c | 6 +---- src/plugins/nat/nat.c | 53 ++++++++++++++++++++++++++++++++++++++--- src/plugins/nat/nat.h | 8 +++++++ src/plugins/nat/nat44/inlines.h | 10 ++++++++ src/plugins/nat/nat_inlines.h | 46 ++++++++++++++++++++++++++++++----- src/plugins/nat/out2in_ed.c | 6 +---- 6 files changed, 110 insertions(+), 19 deletions(-) diff --git a/src/plugins/nat/in2out_ed.c b/src/plugins/nat/in2out_ed.c index 0ca1dd89ca8..807a716e942 100644 --- a/src/plugins/nat/in2out_ed.c +++ b/src/plugins/nat/in2out_ed.c @@ -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; } diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 5fc3eefe106..fdf6334dd83 100755 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -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) { diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index 46dc040c574..d2b114afbbe 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -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; diff --git a/src/plugins/nat/nat44/inlines.h b/src/plugins/nat/nat44/inlines.h index fcaf57383ef..631c4cd4104 100644 --- a/src/plugins/nat/nat44/inlines.h +++ b/src/plugins/nat/nat44/inlines.h @@ -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)); diff --git a/src/plugins/nat/nat_inlines.h b/src/plugins/nat/nat_inlines.h index bdcfd39dfb7..e5f2d96b33f 100644 --- a/src/plugins/nat/nat_inlines.h +++ b/src/plugins/nat/nat_inlines.h @@ -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 diff --git a/src/plugins/nat/out2in_ed.c b/src/plugins/nat/out2in_ed.c index e1eda3208e3..5b70b0c672a 100644 --- a/src/plugins/nat/out2in_ed.c +++ b/src/plugins/nat/out2in_ed.c @@ -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; } -- 2.16.6