LCOV - code coverage report
Current view: top level - plugins/acl - sess_mgmt_node.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 286 348 82.2 %
Date: 2023-10-26 01:39:38 Functions: 20 21 95.2 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016-2018 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : #include <stddef.h>
      16             : #include <netinet/in.h>
      17             : 
      18             : #include <vlib/vlib.h>
      19             : #include <vnet/vnet.h>
      20             : #include <vppinfra/error.h>
      21             : 
      22             : 
      23             : #include <acl/acl.h>
      24             : #include <vnet/ip/icmp46_packet.h>
      25             : 
      26             : #include <plugins/acl/fa_node.h>
      27             : #include <plugins/acl/acl.h>
      28             : #include <plugins/acl/lookup_context.h>
      29             : #include <plugins/acl/public_inlines.h>
      30             : #include <plugins/acl/session_inlines.h>
      31             : 
      32             : 
      33             : 
      34             : static_always_inline u8 *
      35         160 : format_ip46_session_bihash_kv (u8 * s, va_list * args, int is_ip6)
      36             : {
      37             :   fa_5tuple_t a5t;
      38             :   void *paddr0;
      39             :   void *paddr1;
      40             :   void *format_addr_func;
      41             : 
      42         160 :   if (is_ip6)
      43             :     {
      44          80 :       clib_bihash_kv_40_8_t *kv_40_8 =
      45             :         va_arg (*args, clib_bihash_kv_40_8_t *);
      46          80 :       a5t.kv_40_8 = *kv_40_8;
      47          80 :       paddr0 = &a5t.ip6_addr[0];
      48          80 :       paddr1 = &a5t.ip6_addr[1];
      49          80 :       format_addr_func = format_ip6_address;
      50             :     }
      51             :   else
      52             :     {
      53          80 :       clib_bihash_kv_16_8_t *kv_16_8 =
      54             :         va_arg (*args, clib_bihash_kv_16_8_t *);
      55          80 :       a5t.kv_16_8 = *kv_16_8;
      56          80 :       paddr0 = &a5t.ip4_addr[0];
      57          80 :       paddr1 = &a5t.ip4_addr[1];
      58          80 :       format_addr_func = format_ip4_address;
      59             :     }
      60             : 
      61         160 :   fa_full_session_id_t *sess = (fa_full_session_id_t *) & a5t.pkt;
      62             : 
      63         320 :   return (format (s, "l3 %U -> %U %U | sess id %d thread id %d epoch %04x",
      64             :                   format_addr_func, paddr0,
      65             :                   format_addr_func, paddr1,
      66             :                   format_fa_session_l4_key, &a5t.l4,
      67         160 :                   sess->session_index, sess->thread_index,
      68         160 :                   sess->intf_policy_epoch));
      69             : }
      70             : 
      71             : static u8 *
      72          80 : format_ip6_session_bihash_kv (u8 * s, va_list * args)
      73             : {
      74          80 :   return format_ip46_session_bihash_kv (s, args, 1);
      75             : }
      76             : 
      77             : static u8 *
      78          80 : format_ip4_session_bihash_kv (u8 * s, va_list * args)
      79             : {
      80          80 :   return format_ip46_session_bihash_kv (s, args, 0);
      81             : }
      82             : 
      83             : 
      84             : static void
      85         323 : acl_fa_verify_init_sessions (acl_main_t * am)
      86             : {
      87         323 :   if (!am->fa_sessions_hash_is_initialized)
      88             :     {
      89             :       u16 wk;
      90             :       /* Allocate the per-worker sessions pools */
      91          26 :       for (wk = 0; wk < vec_len (am->per_worker_data); wk++)
      92             :         {
      93          15 :           acl_fa_per_worker_data_t *pw = &am->per_worker_data[wk];
      94             : 
      95             :           /*
      96             :            * // In lieu of trying to preallocate the pool and its free bitmap, rather use pool_init_fixed
      97             :            * pool_alloc_aligned(pw->fa_sessions_pool, am->fa_conn_table_max_entries, CLIB_CACHE_LINE_BYTES);
      98             :            * clib_bitmap_validate(pool_header(pw->fa_sessions_pool)->free_bitmap, am->fa_conn_table_max_entries);
      99             :            */
     100          15 :           pool_init_fixed (pw->fa_sessions_pool,
     101             :                            am->fa_conn_table_max_entries);
     102             :         }
     103             : 
     104             :       /* ... and the interface session hash table */
     105          11 :       clib_bihash_init_40_8 (&am->fa_ip6_sessions_hash,
     106             :                              "ACL plugin FA IPv6 session bihash",
     107             :                              am->fa_conn_table_hash_num_buckets,
     108             :                              am->fa_conn_table_hash_memory_size);
     109          11 :       clib_bihash_set_kvp_format_fn_40_8 (&am->fa_ip6_sessions_hash,
     110             :                                           format_ip6_session_bihash_kv);
     111             : 
     112          11 :       clib_bihash_init_16_8 (&am->fa_ip4_sessions_hash,
     113             :                              "ACL plugin FA IPv4 session bihash",
     114             :                              am->fa_conn_table_hash_num_buckets,
     115             :                              am->fa_conn_table_hash_memory_size);
     116          11 :       clib_bihash_set_kvp_format_fn_16_8 (&am->fa_ip4_sessions_hash,
     117             :                                           format_ip4_session_bihash_kv);
     118             : 
     119          11 :       am->fa_sessions_hash_is_initialized = 1;
     120             :     }
     121         323 : }
     122             : 
     123             : 
     124             : /*
     125             :  * Get the timeout of the session in a list since its enqueue time.
     126             :  */
     127             : 
     128             : static u64
     129         328 : fa_session_get_list_timeout (acl_main_t * am, fa_session_t * sess)
     130             : {
     131         328 :   u64 timeout = am->vlib_main->clib_time.clocks_per_second / 1000;
     132         328 :   timeout = fa_session_get_timeout (am, sess);
     133             :   /* for all user lists, check them twice per timeout */
     134         328 :   timeout >>= (sess->link_list_id != ACL_TIMEOUT_PURGATORY);
     135         328 :   return timeout;
     136             : }
     137             : 
     138             : static u64
     139       30675 : acl_fa_get_list_head_expiry_time (acl_main_t * am,
     140             :                                   acl_fa_per_worker_data_t * pw, u64 now,
     141             :                                   u16 thread_index, int timeout_type)
     142             : {
     143       30675 :   return pw->fa_conn_list_head_expiry_time[timeout_type];
     144             : }
     145             : 
     146             : static int
     147       50849 : acl_fa_conn_time_to_check (acl_main_t * am, acl_fa_per_worker_data_t * pw,
     148             :                            u64 now, u16 thread_index, u32 session_index)
     149             : {
     150       50849 :   if (session_index == FA_SESSION_BOGUS_INDEX)
     151       50524 :     return 0;
     152         325 :   fa_session_t *sess = get_session_ptr (am, thread_index, session_index);
     153         328 :   u64 timeout_time =
     154         328 :     sess->link_enqueue_time + fa_session_get_list_timeout (am, sess);
     155             :   return (timeout_time < now)
     156         328 :     || (sess->link_enqueue_time <= pw->swipe_end_time);
     157             : }
     158             : 
     159             : /*
     160             :  * see if there are sessions ready to be checked,
     161             :  * do the maintenance (requeue or delete), and
     162             :  * return the total number of sessions reclaimed.
     163             :  */
     164             : static int
     165       10130 : acl_fa_check_idle_sessions (acl_main_t * am, u16 thread_index, u64 now)
     166             : {
     167       10130 :   acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
     168             :   fa_full_session_id_t fsid;
     169       10130 :   fsid.thread_index = thread_index;
     170       10130 :   int total_expired = 0;
     171             : 
     172             :   /* let the other threads enqueue more requests while we process, if they like */
     173       10130 :   aclp_swap_wip_and_pending_session_change_requests (am, thread_index);
     174       10171 :   u64 *psr = NULL;
     175             : 
     176       10171 :   vec_foreach (psr, pw->wip_session_change_requests)
     177             :   {
     178           0 :     acl_fa_sess_req_t op = *psr >> 32;
     179           0 :     fsid.session_index = *psr & 0xffffffff;
     180           0 :     switch (op)
     181             :       {
     182           0 :       case ACL_FA_REQ_SESS_RESCHEDULE:
     183           0 :         acl_fa_restart_timer_for_session (am, now, fsid);
     184           0 :         break;
     185           0 :       default:
     186             :         /* do nothing */
     187           0 :         break;
     188             :       }
     189             :   }
     190       10171 :   if (pw->wip_session_change_requests)
     191           0 :     vec_set_len (pw->wip_session_change_requests, 0);
     192             : 
     193             :   {
     194       10160 :     u8 tt = 0;
     195       10160 :     int n_pending_swipes = 0;
     196       60797 :     for (tt = 0; tt < ACL_N_TIMEOUTS; tt++)
     197             :       {
     198       50644 :         int n_expired = 0;
     199       50844 :         while (n_expired < am->fa_max_deleted_sessions_per_interval)
     200             :           {
     201       50846 :             fsid.session_index = pw->fa_conn_list_head[tt];
     202       50849 :             if (!acl_fa_conn_time_to_check
     203       50846 :                 (am, pw, now, thread_index, pw->fa_conn_list_head[tt]))
     204             :               {
     205       50639 :                 break;
     206             :               }
     207         210 :             if (am->trace_sessions > 3)
     208             :               {
     209           0 :                 elog_acl_maybe_trace_X3 (am,
     210             :                                          "acl_fa_check_idle_sessions: expire session %d in list %d on thread %d",
     211             :                                          "i4i4i4", (u32) fsid.session_index,
     212             :                                          (u32) tt, (u32) thread_index);
     213             :               }
     214         210 :             vec_add1 (pw->expired, fsid.session_index);
     215         208 :             n_expired++;
     216         208 :             acl_fa_conn_list_delete_session (am, fsid, now);
     217             :           }
     218             :       }
     219       10153 :     for (tt = 0; tt < ACL_N_TIMEOUTS; tt++)
     220             :       {
     221       10121 :         u32 session_index = pw->fa_conn_list_head[tt];
     222       10121 :         if (session_index == FA_SESSION_BOGUS_INDEX)
     223       10121 :           break;
     224             :         fa_session_t *sess =
     225           0 :           get_session_ptr (am, thread_index, session_index);
     226           0 :         n_pending_swipes += sess->link_enqueue_time <= pw->swipe_end_time;
     227             :       }
     228       10153 :     if (n_pending_swipes == 0)
     229             :       {
     230       10121 :         pw->swipe_end_time = 0;
     231             :       }
     232             :   }
     233             : 
     234       10153 :   u32 *psid = NULL;
     235       10361 :   vec_foreach (psid, pw->expired)
     236             :   {
     237         208 :     fsid.session_index = *psid;
     238         208 :     if (!pool_is_free_index (pw->fa_sessions_pool, fsid.session_index))
     239             :       {
     240             :         fa_session_t *sess =
     241         208 :           get_session_ptr (am, thread_index, fsid.session_index);
     242         208 :         u32 sw_if_index = sess->sw_if_index;
     243         208 :         u64 sess_timeout_time =
     244         208 :           sess->last_active_time + fa_session_get_timeout (am, sess);
     245         208 :         int timeout_passed = (now >= sess_timeout_time);
     246         208 :         int clearing_interface =
     247         208 :           clib_bitmap_get (pw->pending_clear_sw_if_index_bitmap, sw_if_index);
     248         208 :         if (am->trace_sessions > 3)
     249             :           {
     250           0 :             elog_acl_maybe_trace_X2 (am,
     251             :                                      "acl_fa_check_idle_sessions: now %lu sess_timeout_time %lu",
     252             :                                      "i8i8", now, sess_timeout_time);
     253           0 :             elog_acl_maybe_trace_X4 (am,
     254             :                                      "acl_fa_check_idle_sessions: session %d sw_if_index %d timeout_passed %d clearing_interface %d",
     255             :                                      "i4i4i4i4", (u32) fsid.session_index,
     256             :                                      (u32) sess->sw_if_index,
     257             :                                      (u32) timeout_passed,
     258             :                                      (u32) clearing_interface);
     259             :           }
     260         208 :         if (timeout_passed || clearing_interface)
     261             :           {
     262         168 :             if (acl_fa_two_stage_delete_session (am, sw_if_index, fsid, now))
     263             :               {
     264          84 :                 if (am->trace_sessions > 3)
     265             :                   {
     266           0 :                     elog_acl_maybe_trace_X2 (am,
     267             :                                              "acl_fa_check_idle_sessions: deleted session %d sw_if_index %d",
     268             :                                              "i4i4", (u32) fsid.session_index,
     269             :                                              (u32) sess->sw_if_index);
     270             :                   }
     271             :                 /* the session has been put */
     272          84 :                 pw->cnt_deleted_sessions++;
     273             :               }
     274             :             else
     275             :               {
     276             :                 /* the connection marked as deleted and put to purgatory */
     277          84 :                 if (am->trace_sessions > 3)
     278             :                   {
     279           0 :                     elog_acl_maybe_trace_X2 (am,
     280             :                                              "acl_fa_check_idle_sessions: session %d sw_if_index %d marked as deleted, put to purgatory",
     281             :                                              "i4i4", (u32) fsid.session_index,
     282             :                                              (u32) sess->sw_if_index);
     283             :                   }
     284             :               }
     285             :           }
     286             :         else
     287             : 
     288             :           {
     289          40 :             if (am->trace_sessions > 3)
     290             :               {
     291           0 :                 elog_acl_maybe_trace_X2 (am,
     292             :                                          "acl_fa_check_idle_sessions: restart timer for session %d sw_if_index %d",
     293             :                                          "i4i4", (u32) fsid.session_index,
     294             :                                          (u32) sess->sw_if_index);
     295             :               }
     296             :             /* There was activity on the session, so the idle timeout
     297             :                has not passed. Enqueue for another time period. */
     298             : 
     299          40 :             acl_fa_conn_list_add_session (am, fsid, now);
     300          40 :             pw->cnt_session_timer_restarted++;
     301             :           }
     302             :       }
     303             :     else
     304             :       {
     305           0 :         pw->cnt_already_deleted_sessions++;
     306             :       }
     307             :   }
     308       10161 :   total_expired = vec_len (pw->expired);
     309             :   /* zero out the vector which we have acted on */
     310       10158 :   if (pw->expired)
     311       10131 :     vec_set_len (pw->expired, 0);
     312             :   /* if we were advancing and reached the end
     313             :    * (no more sessions to recycle), reset the fast-forward timestamp */
     314             : 
     315       10119 :   if (pw->swipe_end_time && 0 == total_expired)
     316           0 :     pw->swipe_end_time = 0;
     317             : 
     318       10119 :   elog_acl_maybe_trace_X1 (am,
     319             :                            "acl_fa_check_idle_sessions: done, total sessions expired: %d",
     320             :                            "i4", (u32) total_expired);
     321       10119 :   return (total_expired);
     322             : }
     323             : 
     324             : /*
     325             :  * This process ensures the connection cleanup happens every so often
     326             :  * even in absence of traffic, as well as provides general orchestration
     327             :  * for requests like connection deletion on a given sw_if_index.
     328             :  */
     329             : 
     330             : 
     331             : /* *INDENT-OFF* */
     332             : #define foreach_acl_fa_cleaner_error \
     333             : _(UNKNOWN_EVENT, "unknown event received")  \
     334             : /* end  of errors */
     335             : 
     336             : typedef enum
     337             : {
     338             : #define _(sym,str) ACL_FA_CLEANER_ERROR_##sym,
     339             :   foreach_acl_fa_cleaner_error
     340             : #undef _
     341             :     ACL_FA_CLEANER_N_ERROR,
     342             : } acl_fa_cleaner_error_t;
     343             : 
     344             : static char *acl_fa_cleaner_error_strings[] = {
     345             : #define _(sym,string) string,
     346             :   foreach_acl_fa_cleaner_error
     347             : #undef _
     348             : };
     349             : 
     350             : /* *INDENT-ON* */
     351             : 
     352             : static vlib_node_registration_t acl_fa_session_cleaner_process_node;
     353             : static vlib_node_registration_t acl_fa_worker_session_cleaner_process_node;
     354             : 
     355             : static void
     356       10521 : send_one_worker_interrupt (vlib_main_t * vm, acl_main_t * am,
     357             :                            int thread_index)
     358             : {
     359       10521 :   acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
     360       10521 :   if (!pw->interrupt_is_pending)
     361             :     {
     362       10204 :       pw->interrupt_is_pending = 1;
     363       10204 :       vlib_node_set_interrupt_pending (
     364             :         vlib_get_main_by_index (thread_index),
     365             :         acl_fa_worker_session_cleaner_process_node.index);
     366       10204 :       elog_acl_maybe_trace_X1 (am,
     367             :                                "send_one_worker_interrupt: send interrupt to worker %u",
     368             :                                "i4", ((u32) thread_index));
     369             :       /* if the interrupt was requested, mark that done. */
     370             :       /* pw->interrupt_is_needed = 0; */
     371       10204 :       CLIB_MEMORY_BARRIER ();
     372             :     }
     373       10521 : }
     374             : 
     375             : void
     376           0 : aclp_post_session_change_request (acl_main_t * am, u32 target_thread,
     377             :                                   u32 target_session, u32 request_type)
     378             : {
     379           0 :   acl_fa_per_worker_data_t *pw_me =
     380           0 :     &am->per_worker_data[os_get_thread_index ()];
     381           0 :   acl_fa_per_worker_data_t *pw = &am->per_worker_data[target_thread];
     382           0 :   clib_spinlock_lock_if_init (&pw->pending_session_change_request_lock);
     383             :   /* vec_add1 might cause a reallocation */
     384           0 :   vec_add1 (pw->pending_session_change_requests,
     385             :             (((u64) request_type) << 32) | target_session);
     386           0 :   pw->rcvd_session_change_requests++;
     387           0 :   pw_me->sent_session_change_requests++;
     388           0 :   if (vec_len (pw->pending_session_change_requests) == 1)
     389             :     {
     390             :       /* ensure the requests get processed */
     391           0 :       send_one_worker_interrupt (am->vlib_main, am, target_thread);
     392             :     }
     393           0 :   clib_spinlock_unlock_if_init (&pw->pending_session_change_request_lock);
     394           0 : }
     395             : 
     396             : void
     397       10128 : aclp_swap_wip_and_pending_session_change_requests (acl_main_t * am,
     398             :                                                    u32 target_thread)
     399             : {
     400       10128 :   acl_fa_per_worker_data_t *pw = &am->per_worker_data[target_thread];
     401             :   u64 *tmp;
     402       10128 :   clib_spinlock_lock_if_init (&pw->pending_session_change_request_lock);
     403       10180 :   tmp = pw->pending_session_change_requests;
     404       10180 :   pw->pending_session_change_requests = pw->wip_session_change_requests;
     405       10180 :   pw->wip_session_change_requests = tmp;
     406       10180 :   clib_spinlock_unlock_if_init (&pw->pending_session_change_request_lock);
     407       10169 : }
     408             : 
     409             : 
     410             : static int
     411       10118 : purgatory_has_connections (vlib_main_t * vm, acl_main_t * am,
     412             :                            int thread_index)
     413             : {
     414       10118 :   acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
     415             : 
     416       20236 :   return (FA_SESSION_BOGUS_INDEX !=
     417       10118 :           pw->fa_conn_list_head[ACL_TIMEOUT_PURGATORY]);
     418             : 
     419             : }
     420             : 
     421             : 
     422             : /*
     423             :  * Per-worker thread interrupt-driven cleaner thread
     424             :  * to clean idle connections if there are no packets
     425             :  */
     426             : static uword
     427       10144 : acl_fa_worker_conn_cleaner_process (vlib_main_t * vm,
     428             :                                     vlib_node_runtime_t * rt,
     429             :                                     vlib_frame_t * f)
     430             : {
     431       10144 :   acl_main_t *am = &acl_main;
     432       10144 :   u64 now = clib_cpu_time_now ();
     433       10141 :   u16 thread_index = os_get_thread_index ();
     434       10145 :   acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
     435             :   int num_expired;
     436       10145 :   elog_acl_maybe_trace_X1 (am,
     437             :                            "acl_fa_worker_conn_cleaner interrupt: now %lu",
     438             :                            "i8", now);
     439             :   /* allow another interrupt to be queued */
     440       10145 :   pw->interrupt_is_pending = 0;
     441       10145 :   if (pw->clear_in_process)
     442             :     {
     443        4944 :       if (0 == pw->swipe_end_time)
     444             :         {
     445             :           /*
     446             :            * Someone has just set the flag to start clearing.
     447             :            * we do this by combing through the connections up to a "time T"
     448             :            * which is now, and requeueing everything except the expired
     449             :            * connections and those matching the interface(s) being cleared.
     450             :            */
     451             : 
     452             :           /*
     453             :            * first filter the sw_if_index bitmap that they want from us, by
     454             :            * a bitmap of sw_if_index for which we actually have connections.
     455             :            */
     456        4944 :           if ((pw->pending_clear_sw_if_index_bitmap == 0)
     457         596 :               || (pw->serviced_sw_if_index_bitmap == 0))
     458             :             {
     459        4630 :               elog_acl_maybe_trace_X1 (am,
     460             :                                        "acl_fa_worker_conn_cleaner: now %lu, someone tried to call clear but one of the bitmaps are empty",
     461             :                                        "i8", now);
     462        4630 :               clib_bitmap_zero (pw->pending_clear_sw_if_index_bitmap);
     463             :             }
     464             :           else
     465             :             {
     466             : #ifdef FA_NODE_VERBOSE_DEBUG
     467             :               clib_warning
     468             :                 ("WORKER-CLEAR: (before and) swiping sw-if-index bitmap: %U, my serviced bitmap %U",
     469             :                  format_bitmap_hex, pw->pending_clear_sw_if_index_bitmap,
     470             :                  format_bitmap_hex, pw->serviced_sw_if_index_bitmap);
     471             : #endif
     472         314 :               pw->pending_clear_sw_if_index_bitmap =
     473         314 :                 clib_bitmap_and (pw->pending_clear_sw_if_index_bitmap,
     474             :                                  pw->serviced_sw_if_index_bitmap);
     475             :             }
     476             : 
     477        4939 :           if (clib_bitmap_is_zero (pw->pending_clear_sw_if_index_bitmap))
     478             :             {
     479             :               /* if the cross-section is a zero vector, no need to do anything. */
     480        4713 :               elog_acl_maybe_trace_X1 (am,
     481             :                                        "acl_fa_worker_conn_cleaner: now %lu, clearing done, nothing to do",
     482             :                                        "i8", now);
     483        4711 :               pw->clear_in_process = 0;
     484        4711 :               pw->swipe_end_time = 0;
     485             :             }
     486             :           else
     487             :             {
     488             : #ifdef FA_NODE_VERBOSE_DEBUG
     489             :               clib_warning
     490             :                 ("WORKER-CLEAR: swiping sw-if-index bitmap: %U, my serviced bitmap %U",
     491             :                  format_bitmap_hex, pw->pending_clear_sw_if_index_bitmap,
     492             :                  format_bitmap_hex, pw->serviced_sw_if_index_bitmap);
     493             : #endif
     494         219 :               elog_acl_maybe_trace_X1 (am,
     495             :                                        "acl_fa_worker_conn_cleaner: swiping until %lu",
     496             :                                        "i8", now);
     497             :               /* swipe through the connection lists until enqueue timestamps become above "now" */
     498         219 :               pw->swipe_end_time = now;
     499             :             }
     500             :         }
     501             :     }
     502       10131 :   num_expired = acl_fa_check_idle_sessions (am, thread_index, now);
     503             :   // clib_warning("WORKER-CLEAR: checked %d sessions (clear_in_progress: %d)", num_expired, pw->clear_in_process);
     504       10118 :   elog_acl_maybe_trace_X2 (am,
     505             :                            "acl_fa_worker_conn_cleaner: checked %d sessions (clear_in_process: %d)",
     506             :                            "i4i4", (u32) num_expired,
     507             :                            (u32) pw->clear_in_process);
     508       10118 :   if (pw->clear_in_process)
     509             :     {
     510         219 :       if (pw->swipe_end_time == 0)
     511             :         {
     512             :           /* we were clearing but we could not process any more connections. time to stop. */
     513         219 :           clib_bitmap_zero (pw->pending_clear_sw_if_index_bitmap);
     514         219 :           pw->clear_in_process = 0;
     515         219 :           elog_acl_maybe_trace_X1 (am,
     516             :                                    "acl_fa_worker_conn_cleaner: now %lu, clearing done - all done",
     517             :                                    "i8", now);
     518             :         }
     519             :       else
     520             :         {
     521           0 :           elog_acl_maybe_trace_X1 (am,
     522             :                                    "acl_fa_worker_conn_cleaner: now %lu, more work to do - requesting interrupt",
     523             :                                    "i8", now);
     524             :           /* should continue clearing.. So could they please sent an interrupt again? */
     525           0 :           send_one_worker_interrupt (vm, am, thread_index);
     526             :           // pw->interrupt_is_needed = 1;
     527             :         }
     528             :     }
     529             :   else
     530             :     {
     531        9899 :       if (num_expired > 0)
     532             :         {
     533             :           /* there was too much work, we should get an interrupt ASAP */
     534             :           // pw->interrupt_is_needed = 1;
     535          25 :           send_one_worker_interrupt (vm, am, thread_index);
     536          25 :           pw->interrupt_is_unwanted = 0;
     537             :         }
     538             :       else
     539             :         {
     540             :           /* the current rate of interrupts is ok */
     541        9874 :           pw->interrupt_is_needed = 0;
     542        9874 :           pw->interrupt_is_unwanted = 0;
     543             :         }
     544        9899 :       elog_acl_maybe_trace_X3 (am,
     545             :                                "acl_fa_worker_conn_cleaner: now %lu, interrupt needed: %u, interrupt unwanted: %u",
     546             :                                "i8i4i4", now, ((u32) pw->interrupt_is_needed),
     547             :                                ((u32) pw->interrupt_is_unwanted));
     548             :     }
     549             :   /* be persistent about quickly deleting the connections from the purgatory */
     550       10118 :   if (purgatory_has_connections (vm, am, thread_index))
     551             :     {
     552          25 :       send_one_worker_interrupt (vm, am, thread_index);
     553             :     }
     554       10119 :   pw->interrupt_generation = am->fa_interrupt_generation;
     555       10119 :   return 0;
     556             : }
     557             : 
     558             : static void
     559       10155 : send_interrupts_to_workers (vlib_main_t * vm, acl_main_t * am)
     560             : {
     561             :   int i;
     562             :   /* Can't use vec_len(am->per_worker_data) since the threads might not have come up yet; */
     563       10155 :   int n_threads = vlib_get_n_threads ();
     564       20626 :   for (i = 0; i < n_threads; i++)
     565             :     {
     566       10471 :       send_one_worker_interrupt (vm, am, i);
     567             :     }
     568       10155 : }
     569             : 
     570             : /* centralized process to drive per-worker cleaners */
     571             : static uword
     572         575 : acl_fa_session_cleaner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
     573             :                                 vlib_frame_t * f)
     574             : {
     575         575 :   acl_main_t *am = &acl_main;
     576             :   u64 now;
     577         575 :   f64 cpu_cps = vm->clib_time.clocks_per_second;
     578             :   u64 next_expire;
     579             :   /* We should check if there are connections to clean up - at least twice a second */
     580         575 :   u64 max_timer_wait_interval = cpu_cps / 2;
     581         575 :   uword event_type, *event_data = 0;
     582             :   acl_fa_per_worker_data_t *pw0;
     583             : 
     584         575 :   am->fa_current_cleaner_timer_wait_interval = max_timer_wait_interval;
     585         575 :   am->fa_cleaner_node_index = acl_fa_session_cleaner_process_node.index;
     586         575 :   am->fa_interrupt_generation = 1;
     587             :   while (1)
     588        5321 :     {
     589        5896 :       now = clib_cpu_time_now ();
     590        5896 :       next_expire = now + am->fa_current_cleaner_timer_wait_interval;
     591        5896 :       int has_pending_conns = 0;
     592             :       u16 ti;
     593             :       u8 tt;
     594             : 
     595             :       /*
     596             :        * walk over all per-thread list heads of different timeouts,
     597             :        * and see if there are any connections pending.
     598             :        * If there aren't - we do not need to wake up until the
     599             :        * worker code signals that it has added a connection.
     600             :        *
     601             :        * Also, while we are at it, calculate the earliest we need to wake up.
     602             :        */
     603       12031 :       for (ti = 0; ti < vlib_get_n_threads (); ti++)
     604             :         {
     605        6135 :           if (ti >= vec_len (am->per_worker_data))
     606             :             {
     607           0 :               continue;
     608             :             }
     609        6135 :           acl_fa_per_worker_data_t *pw = &am->per_worker_data[ti];
     610       36810 :           for (tt = 0; tt < vec_len (pw->fa_conn_list_head); tt++)
     611             :             {
     612             :               u64 head_expiry =
     613       30675 :                 acl_fa_get_list_head_expiry_time (am, pw, now, ti, tt);
     614       30675 :               if ((head_expiry < next_expire) && !pw->interrupt_is_pending)
     615             :                 {
     616           0 :                   elog_acl_maybe_trace_X3 (am,
     617             :                                            "acl_fa_session_cleaner_process: now %lu, worker: %u tt: %u",
     618             :                                            "i8i2i2", now, ti, tt);
     619           0 :                   elog_acl_maybe_trace_X2 (am,
     620             :                                            "acl_fa_session_cleaner_process: head expiry: %lu, is earlier than curr next expire: %lu",
     621             :                                            "i8i8", head_expiry, next_expire);
     622           0 :                   next_expire = head_expiry;
     623             :                 }
     624       30675 :               if (FA_SESSION_BOGUS_INDEX != pw->fa_conn_list_head[tt])
     625             :                 {
     626         119 :                   has_pending_conns = 1;
     627             :                 }
     628             :             }
     629             :         }
     630             : 
     631             :       /* If no pending connections and no ACL applied then no point in timing out */
     632        5896 :       if (!has_pending_conns && (0 == am->fa_total_enabled_count))
     633             :         {
     634        4905 :           am->fa_cleaner_cnt_wait_without_timeout++;
     635        4905 :           elog_acl_maybe_trace_X1 (am,
     636             :                                    "acl_conn_cleaner: now %lu entering wait without timeout",
     637             :                                    "i8", now);
     638        4905 :           (void) vlib_process_wait_for_event (vm);
     639        4331 :           event_type = vlib_process_get_events (vm, &event_data);
     640             :         }
     641             :       else
     642             :         {
     643         991 :           f64 timeout = ((i64) next_expire - (i64) now) / cpu_cps;
     644         991 :           if (timeout <= 0)
     645             :             {
     646             :               /* skip waiting altogether */
     647           0 :               event_type = ~0;
     648             :             }
     649             :           else
     650             :             {
     651         991 :               am->fa_cleaner_cnt_wait_with_timeout++;
     652         991 :               elog_acl_maybe_trace_X2 (am,
     653             :                                        "acl_conn_cleaner: now %lu entering wait with timeout %.6f sec",
     654             :                                        "i8f8", now, timeout);
     655         991 :               (void) vlib_process_wait_for_event_or_clock (vm, timeout);
     656         990 :               event_type = vlib_process_get_events (vm, &event_data);
     657             :             }
     658             :         }
     659             : 
     660        5321 :       switch (event_type)
     661             :         {
     662         236 :         case ~0:
     663             :           /* nothing to do */
     664         236 :           break;
     665         251 :         case ACL_FA_CLEANER_RESCHEDULE:
     666             :           /* Nothing to do. */
     667         251 :           break;
     668        4834 :         case ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX:
     669             :           {
     670        4834 :             uword *clear_sw_if_index_bitmap = 0;
     671             :             uword *sw_if_index0;
     672        4834 :             int clear_all = 0;
     673        4834 :             now = clib_cpu_time_now ();
     674        4834 :             elog_acl_maybe_trace_X1 (am,
     675             :                                      "acl_fa_session_cleaner_process: now %lu, received ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX",
     676             :                                      "i8", now);
     677        9876 :             vec_foreach (sw_if_index0, event_data)
     678             :             {
     679        5042 :               am->fa_cleaner_cnt_delete_by_sw_index++;
     680        5042 :               elog_acl_maybe_trace_X1 (am,
     681             :                                        "acl_fa_session_cleaner_process: ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX %u",
     682             :                                        "i4", *sw_if_index0);
     683        5042 :               if (*sw_if_index0 == ~0)
     684             :                 {
     685           0 :                   clear_all = 1;
     686             :                 }
     687             :               else
     688             :                 {
     689        5042 :                   if (!pool_is_free_index
     690        5042 :                       (am->vnet_main->interface_main.sw_interfaces,
     691             :                        *sw_if_index0))
     692             :                     {
     693         790 :                       clear_sw_if_index_bitmap =
     694         790 :                         clib_bitmap_set (clear_sw_if_index_bitmap,
     695             :                                          *sw_if_index0, 1);
     696             :                     }
     697             :                 }
     698             :             }
     699        4834 :             acl_log_info
     700             :               ("ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX bitmap: %U, clear_all: %u",
     701             :                format_bitmap_hex, clear_sw_if_index_bitmap, clear_all);
     702        9800 :             vec_foreach (pw0, am->per_worker_data)
     703             :             {
     704        4966 :               CLIB_MEMORY_BARRIER ();
     705        4966 :               while (pw0->clear_in_process)
     706             :                 {
     707           0 :                   CLIB_MEMORY_BARRIER ();
     708           0 :                   elog_acl_maybe_trace_X1 (am,
     709             :                                            "ACL_FA_NODE_CLEAN: waiting previous cleaning cycle to finish on %u",
     710             :                                            "i4",
     711             :                                            (u32) (pw0 - am->per_worker_data));
     712           0 :                   vlib_process_suspend (vm, 0.0001);
     713           0 :                   if (pw0->interrupt_is_needed)
     714             :                     {
     715           0 :                       send_one_worker_interrupt (vm, am,
     716           0 :                                                  (pw0 - am->per_worker_data));
     717             :                     }
     718             :                 }
     719        4966 :               if (pw0->clear_in_process)
     720             :                 {
     721           0 :                   acl_log_err
     722             :                     ("ERROR-BUG! Could not initiate cleaning on worker because another cleanup in progress");
     723             :                 }
     724             :               else
     725             :                 {
     726        4966 :                   clib_bitmap_free (pw0->pending_clear_sw_if_index_bitmap);
     727        4966 :                   if (clear_all)
     728             :                     {
     729             :                       /* if we need to clear all, then just clear the interfaces that we are servicing */
     730           0 :                       pw0->pending_clear_sw_if_index_bitmap =
     731           0 :                         clib_bitmap_dup (pw0->serviced_sw_if_index_bitmap);
     732             :                     }
     733             :                   else
     734             :                     {
     735        4966 :                       pw0->pending_clear_sw_if_index_bitmap =
     736        4966 :                         clib_bitmap_dup (clear_sw_if_index_bitmap);
     737             :                     }
     738        4966 :                   acl_log_info
     739             :                     ("ACL_FA_CLEANER: thread %u, pending clear bitmap: %U",
     740             :                      (am->per_worker_data - pw0), format_bitmap_hex,
     741             :                      pw0->pending_clear_sw_if_index_bitmap);
     742        4966 :                   pw0->clear_in_process = 1;
     743             :                 }
     744             :             }
     745             :             /* send some interrupts so they can start working */
     746        4834 :             send_interrupts_to_workers (vm, am);
     747             : 
     748             :             /* now wait till they all complete */
     749        4834 :             acl_log_info ("CLEANER mains len: %u per-worker len: %d",
     750             :                           vlib_get_n_threads (),
     751             :                           vec_len (am->per_worker_data));
     752        9800 :             vec_foreach (pw0, am->per_worker_data)
     753             :             {
     754        4966 :               CLIB_MEMORY_BARRIER ();
     755        9800 :               while (pw0->clear_in_process)
     756             :                 {
     757        4834 :                   CLIB_MEMORY_BARRIER ();
     758        4834 :                   elog_acl_maybe_trace_X1 (am,
     759             :                                            "ACL_FA_NODE_CLEAN: waiting for my cleaning cycle to finish on %u",
     760             :                                            "i4",
     761             :                                            (u32) (pw0 - am->per_worker_data));
     762        4834 :                   vlib_process_suspend (vm, 0.0001);
     763        4834 :                   if (pw0->interrupt_is_needed)
     764             :                     {
     765           0 :                       send_one_worker_interrupt (vm, am,
     766           0 :                                                  (pw0 - am->per_worker_data));
     767             :                     }
     768             :                 }
     769             :             }
     770        4834 :             acl_log_info ("ACL_FA_NODE_CLEAN: cleaning done");
     771        4834 :             clib_bitmap_free (clear_sw_if_index_bitmap);
     772             :           }
     773        4834 :           am->fa_cleaner_cnt_delete_by_sw_index_ok++;
     774        4834 :           break;
     775           0 :         default:
     776             : #ifdef FA_NODE_VERBOSE_DEBUG
     777             :           clib_warning ("ACL plugin connection cleaner: unknown event %u",
     778             :                         event_type);
     779             : #endif
     780           0 :           vlib_node_increment_counter (vm,
     781             :                                        acl_fa_session_cleaner_process_node.
     782             :                                        index,
     783             :                                        ACL_FA_CLEANER_ERROR_UNKNOWN_EVENT, 1);
     784           0 :           am->fa_cleaner_cnt_unknown_event++;
     785           0 :           break;
     786             :         }
     787             : 
     788        5321 :       send_interrupts_to_workers (vm, am);
     789             : 
     790        5321 :       if (event_data)
     791        5321 :         vec_set_len (event_data, 0);
     792             : 
     793             :       /*
     794             :        * If the interrupts were not processed yet, ensure we wait a bit,
     795             :        * but up to a point.
     796             :        */
     797        5321 :       int need_more_wait = 0;
     798        5321 :       int max_wait_cycles = 100;
     799             :       do
     800             :         {
     801        5737 :           need_more_wait = 0;
     802       11742 :           vec_foreach (pw0, am->per_worker_data)
     803             :           {
     804        6005 :             if (pw0->interrupt_generation != am->fa_interrupt_generation)
     805             :               {
     806         479 :                 need_more_wait = 1;
     807             :               }
     808             :           }
     809        5737 :           if (need_more_wait)
     810             :             {
     811         416 :               vlib_process_suspend (vm, 0.0001);
     812             :             }
     813             :         }
     814        5737 :       while (need_more_wait && (--max_wait_cycles > 0));
     815             : 
     816        5321 :       int interrupts_needed = 0;
     817        5321 :       int interrupts_unwanted = 0;
     818             : 
     819       10826 :       vec_foreach (pw0, am->per_worker_data)
     820             :       {
     821        5505 :         if (pw0->interrupt_is_needed)
     822             :           {
     823           0 :             interrupts_needed++;
     824             :             /* the per-worker value is reset when sending the interrupt */
     825             :           }
     826        5505 :         if (pw0->interrupt_is_unwanted)
     827             :           {
     828           0 :             interrupts_unwanted++;
     829           0 :             pw0->interrupt_is_unwanted = 0;
     830             :           }
     831             :       }
     832        5321 :       if (interrupts_needed)
     833             :         {
     834             :           /* they need more interrupts, do less waiting around next time */
     835           0 :           am->fa_current_cleaner_timer_wait_interval /= 2;
     836             :           /* never go into zero-wait either though - we need to give the space to others */
     837           0 :           am->fa_current_cleaner_timer_wait_interval += 1;
     838             :         }
     839        5321 :       else if (interrupts_unwanted)
     840             :         {
     841             :           /* slowly increase the amount of sleep up to a limit */
     842           0 :           if (am->fa_current_cleaner_timer_wait_interval <
     843             :               max_timer_wait_interval)
     844           0 :             am->fa_current_cleaner_timer_wait_interval +=
     845           0 :               cpu_cps * am->fa_cleaner_wait_time_increment;
     846             :         }
     847        5321 :       am->fa_cleaner_cnt_event_cycles++;
     848        5321 :       am->fa_interrupt_generation++;
     849             :     }
     850             :   /* NOT REACHED */
     851             :   return 0;
     852             : }
     853             : 
     854             : 
     855             : void
     856         646 : acl_fa_enable_disable (u32 sw_if_index, int is_input, int enable_disable)
     857             : {
     858         646 :   acl_main_t *am = &acl_main;
     859         646 :   if (enable_disable)
     860             :     {
     861         323 :       acl_fa_verify_init_sessions (am);
     862         323 :       am->fa_total_enabled_count++;
     863         323 :       vlib_process_signal_event (am->vlib_main, am->fa_cleaner_node_index,
     864             :                                  ACL_FA_CLEANER_RESCHEDULE, 0);
     865             :     }
     866             :   else
     867             :     {
     868         323 :       am->fa_total_enabled_count--;
     869             :     }
     870             : 
     871         646 :   if (is_input)
     872             :     {
     873         404 :       ASSERT (clib_bitmap_get (am->fa_in_acl_on_sw_if_index, sw_if_index) !=
     874             :               enable_disable);
     875         404 :       vnet_feature_enable_disable ("ip4-unicast", "acl-plugin-in-ip4-fa",
     876             :                                    sw_if_index, enable_disable, 0, 0);
     877         404 :       vnet_feature_enable_disable ("ip6-unicast", "acl-plugin-in-ip6-fa",
     878             :                                    sw_if_index, enable_disable, 0, 0);
     879         404 :       am->fa_in_acl_on_sw_if_index =
     880         404 :         clib_bitmap_set (am->fa_in_acl_on_sw_if_index, sw_if_index,
     881             :                          enable_disable);
     882             :     }
     883             :   else
     884             :     {
     885         242 :       ASSERT (clib_bitmap_get (am->fa_out_acl_on_sw_if_index, sw_if_index) !=
     886             :               enable_disable);
     887         242 :       vnet_feature_enable_disable ("ip4-output", "acl-plugin-out-ip4-fa",
     888             :                                    sw_if_index, enable_disable, 0, 0);
     889         242 :       vnet_feature_enable_disable ("ip6-output", "acl-plugin-out-ip6-fa",
     890             :                                    sw_if_index, enable_disable, 0, 0);
     891         242 :       am->fa_out_acl_on_sw_if_index =
     892         242 :         clib_bitmap_set (am->fa_out_acl_on_sw_if_index, sw_if_index,
     893             :                          enable_disable);
     894             :     }
     895         646 :   if ((!enable_disable) && (!acl_fa_ifc_has_in_acl (am, sw_if_index))
     896         234 :       && (!acl_fa_ifc_has_out_acl (am, sw_if_index)))
     897             :     {
     898             : #ifdef FA_NODE_VERBOSE_DEBUG
     899             :       clib_warning ("ENABLE-DISABLE: clean the connections on interface %d",
     900             :                     sw_if_index);
     901             : #endif
     902         218 :       vlib_process_signal_event (am->vlib_main, am->fa_cleaner_node_index,
     903             :                                  ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX,
     904             :                                  sw_if_index);
     905             :     }
     906         646 : }
     907             : 
     908             : void
     909          41 : show_fa_sessions_hash (vlib_main_t * vm, u32 verbose)
     910             : {
     911          41 :   acl_main_t *am = &acl_main;
     912          41 :   if (am->fa_sessions_hash_is_initialized)
     913             :     {
     914          40 :       vlib_cli_output (vm, "\nIPv6 Session lookup hash table:\n%U\n\n",
     915             :                        format_bihash_40_8, &am->fa_ip6_sessions_hash,
     916             :                        verbose);
     917             : 
     918          40 :       vlib_cli_output (vm, "\nIPv4 Session lookup hash table:\n%U\n\n",
     919             :                        format_bihash_16_8, &am->fa_ip4_sessions_hash,
     920             :                        verbose);
     921             :     }
     922             :   else
     923             :     {
     924           1 :       vlib_cli_output (vm,
     925             :                        "\nSession lookup hash table is not allocated.\n\n");
     926             :     }
     927          41 : }
     928             : 
     929             : 
     930             : /* *INDENT-OFF* */
     931             : 
     932      175724 : VLIB_REGISTER_NODE (acl_fa_worker_session_cleaner_process_node, static) = {
     933             :   .function = acl_fa_worker_conn_cleaner_process,
     934             :   .name = "acl-plugin-fa-worker-cleaner-process",
     935             :   .type = VLIB_NODE_TYPE_INPUT,
     936             :   .state = VLIB_NODE_STATE_INTERRUPT,
     937             : };
     938             : 
     939      175724 : VLIB_REGISTER_NODE (acl_fa_session_cleaner_process_node, static) = {
     940             :   .function = acl_fa_session_cleaner_process,
     941             :   .type = VLIB_NODE_TYPE_PROCESS,
     942             :   .name = "acl-plugin-fa-cleaner-process",
     943             :   .n_errors = ARRAY_LEN (acl_fa_cleaner_error_strings),
     944             :   .error_strings = acl_fa_cleaner_error_strings,
     945             :   .n_next_nodes = 0,
     946             :   .next_nodes = {},
     947             : };
     948             : 
     949             : 
     950             : /* *INDENT-ON* */
     951             : 
     952             : /*
     953             :  * fd.io coding-style-patch-verification: ON
     954             :  *
     955             :  * Local Variables:
     956             :  * eval: (c-set-style "gnu")
     957             :  * End:
     958             :  */

Generated by: LCOV version 1.14