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