summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKlement Sekera <ksekera@cisco.com>2020-04-05 10:22:47 +0200
committerKlement Sekera <ksekera@cisco.com>2020-04-08 11:07:49 +0200
commitba5f9bc7534bcf58225b0658993728b1f4d47a67 (patch)
treea3ca0cea8eea85910e050808c8df37bf802cd393 /src
parent248ce43c6d79b79597e28f19615e83d2bb0a9276 (diff)
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 <ksekera@cisco.com> Change-Id: I9edde9ec138de67c9a4888e915b0490ec16415fa
Diffstat (limited to 'src')
-rw-r--r--src/plugins/nat/in2out_ed.c6
-rwxr-xr-xsrc/plugins/nat/nat.c53
-rw-r--r--src/plugins/nat/nat.h8
-rw-r--r--src/plugins/nat/nat44/inlines.h10
-rw-r--r--src/plugins/nat/nat_inlines.h46
-rw-r--r--src/plugins/nat/out2in_ed.c6
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;
}
s, ev_idx)) ev = pool_elt_at_index (evt->vce_events, ev_idx); clib_spinlock_unlock (&(evt->events_lockp)); return ev; } vce_event_handler_reg_t * vce_get_event_handler (vce_event_thread_t *evt, vce_event_key_t *evk) { vce_event_handler_reg_t *handler = 0; uword *p; clib_spinlock_lock (&evt->handlers_lockp); p = hash_get (evt->handlers_index_by_event_key, evk->as_u64); if (p) handler = pool_elt_at_index (evt->vce_event_handlers, p[0]); clib_spinlock_unlock (&evt->handlers_lockp); return handler; } vce_event_handler_reg_t * vce_register_handler (vce_event_thread_t *evt, vce_event_key_t *evk, vce_event_callback_t cb, void *cb_args) { vce_event_handler_reg_t *handler; vce_event_handler_reg_t *old_handler = 0; uword *p; u32 handler_index; /* TODO - multiple handler support. For now we can replace * and re-instate, which is useful for event recycling */ clib_spinlock_lock (&evt->handlers_lockp); p = hash_get (evt->handlers_index_by_event_key, evk->as_u64); if (p) { old_handler = pool_elt_at_index (evt->vce_event_handlers, p[0]); /* If we are just re-registering, ignore and move on * else store the old handler_fn for unregister to re-instate */ if (old_handler->handler_fn == cb) { clib_spinlock_unlock (&evt->handlers_lockp); /* Signal event thread that a handler exists in case any * recycled events requiring this handler are pending */ pthread_mutex_lock (&(evt->generator_lock)); pthread_cond_signal (&(evt->generator_cond)); pthread_mutex_unlock (&(evt->generator_lock)); return old_handler; } } pool_get (evt->vce_event_handlers, handler); handler_index = (u32) (handler - evt->vce_event_handlers); handler->handler_fn = cb; handler->replaced_handler_idx = (p) ? p[0] : ~0; handler->ev_idx = ~0; //This will be set by the event thread if event happens handler->evk = evk->as_u64; handler->handler_fn_args = cb_args; hash_set (evt->handlers_index_by_event_key, evk->as_u64, handler_index); pthread_cond_init (&(handler->handler_cond), NULL); pthread_mutex_init (&(handler->handler_lock), NULL); clib_spinlock_unlock (&evt->handlers_lockp); /* Signal event thread that a new handler exists in case any * recycled events requiring this handler are pending */ pthread_mutex_lock (&(evt->generator_lock)); pthread_cond_signal (&(evt->generator_cond)); pthread_mutex_unlock (&(evt->generator_lock)); return handler; } int vce_unregister_handler (vce_event_thread_t *evt, vce_event_handler_reg_t *handler) { uword *p; u64 evk = handler->evk; u8 generate_signal = 0; clib_spinlock_lock (&evt->handlers_lockp); p = hash_get (evt->handlers_index_by_event_key, evk); if (!p) { clib_spinlock_unlock (&evt->handlers_lockp); return VNET_API_ERROR_NO_SUCH_ENTRY; } handler = pool_elt_at_index (evt->vce_event_handlers, p[0]); /* If this handler replaced another handler, re-instate it */ if (handler->replaced_handler_idx != ~0) { hash_set (evt->handlers_index_by_event_key, evk, handler->replaced_handler_idx); generate_signal = 1; } else { hash_unset (evt->handlers_index_by_event_key, evk); } pthread_mutex_destroy (&(handler->handler_lock)); pthread_cond_destroy (&(handler->handler_cond)); pool_put (evt->vce_event_handlers, handler); clib_spinlock_unlock (&evt->handlers_lockp); if (generate_signal) { /* Signal event thread that a new handler exists in case any * recycled events requiring this handler are pending */ pthread_mutex_lock (&(evt->generator_lock)); pthread_cond_signal (&(evt->generator_cond)); pthread_mutex_unlock (&(evt->generator_lock)); } return 0; } void * vce_event_thread_fn (void *arg) { vce_event_thread_t *evt = (vce_event_thread_t *) arg; vce_event_t *ev; u32 ev_idx; vce_event_handler_reg_t *handler; uword *p; pthread_mutex_lock (&(evt->generator_lock)); clib_spinlock_lock (&(evt->events_lockp)); evt->recycle_event = 1; // Used for recycling events with no handlers clib_spinlock_unlock (&(evt->events_lockp)); do { while ( (clib_fifo_elts (evt->event_index_fifo) == 0) || evt->recycle_event) { clib_spinlock_lock (&(evt->events_lockp)); evt->recycle_event = 0; clib_spinlock_unlock (&(evt->events_lockp)); pthread_cond_wait (&(evt->generator_cond), &(evt->generator_lock)); } /* Remove event */ clib_spinlock_lock (&(evt->events_lockp)); clib_fifo_sub1 (evt->event_index_fifo, ev_idx); ev = pool_elt_at_index (evt->vce_events, ev_idx); ASSERT(ev); clib_spinlock_lock (&evt->handlers_lockp); p = hash_get (evt->handlers_index_by_event_key, ev->evk.as_u64); if (!p) { /* If an event falls in the woods, and there is no handler to hear it, * does it make any sound? * I don't know either, so lets try recycling the event */ clib_fifo_add1 (evt->event_index_fifo, ev_idx); evt->recycle_event = 1; clib_spinlock_unlock (&(evt->events_lockp)); clib_spinlock_unlock (&evt->handlers_lockp); pthread_mutex_unlock (&(evt->generator_lock)); } else { handler = pool_elt_at_index (evt->vce_event_handlers, p[0]); handler->ev_idx = ev_idx; clib_spinlock_unlock (&(evt->events_lockp)); clib_spinlock_unlock (&evt->handlers_lockp); pthread_mutex_unlock (&(evt->generator_lock)); (handler->handler_fn)(handler); } pthread_mutex_lock (&(evt->generator_lock)); } while (1); return NULL; } int vce_start_event_thread (vce_event_thread_t *evt, u8 max_events) { clib_fifo_validate (evt->event_index_fifo, max_events); evt->handlers_index_by_event_key = hash_create (0, sizeof (uword)); pthread_cond_init (&(evt->generator_cond), NULL); pthread_mutex_init (&(evt->generator_lock), NULL); clib_spinlock_init (&(evt->events_lockp)); clib_spinlock_init (&(evt->handlers_lockp)); return pthread_create (&(evt->thread), NULL /* attr */ , vce_event_thread_fn, evt); }