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