misc: remove GNU Indent directives
[vpp.git] / src / vlib / punt.c
1 /*
2  * Copyright (c) 2019 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
16 #include <vlib/punt.h>
17
18 /**
19  * The last allocated punt reason
20  * Value 0 is reserved for invalid index.
21  */
22 static vlib_punt_reason_t punt_reason_last = 1;
23
24 /**
25  * Counters per punt-reason
26  */
27 vlib_combined_counter_main_t punt_counters = {
28   .name = "punt",
29   .stat_segment_name = "/net/punt",
30 };
31
32 /**
33  * A punt reason
34  */
35 typedef struct punt_reason_data_t_
36 {
37   /**
38    * The reason name
39    */
40   u8 *pd_name;
41
42   /**
43    * The allocated reason value
44    */
45   vlib_punt_reason_t pd_reason;
46
47   /**
48    * Clients/owners that have registered this reason
49    */
50   u32 *pd_owners;
51
52   /**
53    * clients interested/listening to this reason
54    */
55   u32 pd_users;
56
57   /**
58    * function to invoke if a client becomes interested in the code.
59    */
60   punt_interested_listener_t pd_fn;
61
62   /**
63    * Data to pass to the callback
64    */
65   void *pd_data;
66
67   /**
68    * Flags associated to the reason
69    */
70   u32 flags;
71
72   /**
73    * Formatting function for flags;
74    */
75   format_function_t *flags_format;
76 } punt_reason_data_t;
77
78 /**
79  * data for each punt reason
80  */
81 static punt_reason_data_t *punt_reason_data;
82
83 typedef enum punt_format_flags_t_
84 {
85   PUNT_FORMAT_FLAG_NONE = 0,
86   PUNT_FORMAT_FLAG_DETAIL = (1 << 0),
87 } punt_format_flags_t;
88
89 /**
90  * A registration, by a client, to direct punted traffic to a given node
91  */
92 typedef struct punt_reg_t_
93 {
94   /**
95    * Reason the packets were punted
96    */
97   vlib_punt_reason_t pr_reason;
98
99   /**
100    * number of clients that have made this registration
101    */
102   u16 pr_locks;
103
104   /**
105    * The edge from the punt dispatch node to the requested node
106    */
107   u16 pr_edge;
108
109   /**
110    * node-index to send punted packets to
111    */
112   u32 pr_node_index;
113 } punt_reg_t;
114
115 /**
116  * Pool of registrations
117  */
118 static punt_reg_t *punt_reg_pool;
119
120 /**
121  * A DB of all the register nodes against punt reason and node index
122  */
123 static uword *punt_reg_db;
124
125 /**
126  * A DB used in the DP per-reason to dispatch packets to the requested nodes.
127  * this is a vector of edges per-reason
128  */
129 u16 **punt_dp_db;
130
131 /**
132  * A client using the punt serivce and its registrations
133  */
134 typedef struct punt_client_t_
135 {
136   /**
137    * The name of the client
138    */
139   u8 *pc_name;
140
141   /**
142    * The registrations is has made
143    */
144   u32 *pc_regs;
145 } punt_client_t;
146
147 /**
148  * Pool of clients
149  */
150 static punt_client_t *punt_client_pool;
151
152 /**
153  * DB of clients key'd by their name
154  */
155 static uword *punt_client_db;
156
157 u8 *
158 format_vlib_punt_reason (u8 * s, va_list * args)
159 {
160   vlib_punt_reason_t pr = va_arg (*args, int);
161   format_function_t *flags_format = punt_reason_data[pr].flags_format;
162   u32 flags = punt_reason_data[pr].flags;
163   if (flags_format)
164     return (format (s, "[%d] %v flags: %U", pr, punt_reason_data[pr].pd_name,
165                     flags_format, flags));
166   else
167     return (format (s, "[%d] %v", pr, punt_reason_data[pr].pd_name));
168 }
169
170 vlib_punt_hdl_t
171 vlib_punt_client_register (const char *who)
172 {
173   u8 *pc_name;
174   uword *p;
175   u32 pci;
176
177   pc_name = format (NULL, "%s", who);
178   p = hash_get_mem (punt_client_db, pc_name);
179
180   if (NULL == p)
181     {
182       punt_client_t *pc;
183
184       pool_get (punt_client_pool, pc);
185       pci = pc - punt_client_pool;
186
187       pc->pc_name = pc_name;
188
189       hash_set_mem (punt_client_db, pc->pc_name, pci);
190     }
191   else
192     {
193       pci = p[0];
194       vec_free (pc_name);
195     }
196
197   return (pci);
198 }
199
200 static int
201 punt_validate_client (vlib_punt_hdl_t client)
202 {
203   return (!pool_is_free_index (punt_client_pool, client));
204 }
205
206 static u64
207 punt_reg_mk_key (vlib_punt_reason_t reason, u32 node_index)
208 {
209   return (((u64) node_index) << 32 | reason);
210 }
211
212 static u32
213 punt_reg_find (vlib_punt_reason_t reason, u32 node_index)
214 {
215   uword *p;
216
217   p = hash_get (punt_reg_db, punt_reg_mk_key (reason, node_index));
218
219   if (p)
220     return p[0];
221
222   return ~0;
223 }
224
225 static void
226 punt_reg_add (const punt_reg_t * pr)
227 {
228   hash_set (punt_reg_db, punt_reg_mk_key (pr->pr_reason,
229                                           pr->pr_node_index),
230             pr - punt_reg_pool);
231 }
232
233 static void
234 punt_reg_remove (const punt_reg_t * pr)
235 {
236   hash_unset (punt_reg_db, punt_reg_mk_key (pr->pr_reason,
237                                             pr->pr_node_index));
238 }
239
240 /**
241  * reconstruct the DP per-reason DB
242  */
243 static void
244 punt_reg_mk_dp (vlib_punt_reason_t reason)
245 {
246   u32 pri, *prip, *pris;
247   const punt_reg_t *pr;
248   u16 *edges, *old;
249   u64 key;
250
251   pris = NULL;
252   edges = NULL;
253   vec_validate (punt_dp_db, reason);
254
255   old = punt_dp_db[reason];
256
257   hash_foreach (key, pri, punt_reg_db,
258     ({
259       vec_add1(pris, pri);
260     }));
261
262   /*
263    * A check for an empty vector is done in the DP, so the a zero
264    * length vector here is ok
265    */
266   vec_foreach (prip, pris)
267   {
268     pr = pool_elt_at_index (punt_reg_pool, *prip);
269
270     if (pr->pr_reason == reason)
271       vec_add1 (edges, pr->pr_edge);
272   }
273
274   /* atomic update of the DP */
275   punt_dp_db[reason] = edges;
276
277   vec_free (old);
278 }
279
280 int
281 vlib_punt_register (vlib_punt_hdl_t client,
282                     vlib_punt_reason_t reason, const char *node_name)
283 {
284   vlib_node_t *punt_to, *punt_from;
285   punt_client_t *pc;
286   vlib_main_t *vm;
287   punt_reg_t *pr;
288   u32 pri;
289
290   if (reason >= punt_reason_last)
291     return -1;
292   if (!punt_validate_client (client))
293     return -2;
294
295   vm = vlib_get_main ();
296   pc = pool_elt_at_index (punt_client_pool, client);
297   punt_to = vlib_get_node_by_name (vm, (u8 *) node_name);
298   punt_from = vlib_get_node_by_name (vm, (u8 *) "punt-dispatch");
299
300   /*
301    * find a global matching registration
302    */
303   pri = punt_reg_find (reason, punt_to->index);
304
305   if (~0 != pri)
306     {
307       u32 pos;
308
309       pos = vec_search (pc->pc_regs, pri);
310
311       if (~0 != pos)
312         {
313           /* duplicate registration for this client */
314           return -1;
315         }
316
317       pr = pool_elt_at_index (punt_reg_pool, pri);
318     }
319   else
320     {
321       pool_get (punt_reg_pool, pr);
322
323       pr->pr_reason = reason;
324       pr->pr_node_index = punt_to->index;
325       pr->pr_edge = vlib_node_add_next (vm,
326                                         punt_from->index, pr->pr_node_index);
327
328       pri = pr - punt_reg_pool;
329
330       if (0 == punt_reason_data[reason].pd_users++ &&
331           NULL != punt_reason_data[reason].pd_fn)
332         punt_reason_data[reason].pd_fn (VLIB_ENABLE,
333                                         punt_reason_data[reason].pd_data);
334
335       punt_reg_add (pr);
336     }
337
338   /*
339    * add this reg to the list the client has made
340    */
341   pr->pr_locks++;
342   vec_add1 (pc->pc_regs, pri);
343
344   punt_reg_mk_dp (reason);
345
346   return 0;
347 }
348
349 int
350 vlib_punt_unregister (vlib_punt_hdl_t client,
351                       vlib_punt_reason_t reason, const char *node_name)
352 {
353   vlib_node_t *punt_to;
354   punt_client_t *pc;
355   vlib_main_t *vm;
356   punt_reg_t *pr;
357   u32 pri;
358
359   if (reason >= punt_reason_last)
360     return -1;
361
362   vm = vlib_get_main ();
363   pc = pool_elt_at_index (punt_client_pool, client);
364   punt_to = vlib_get_node_by_name (vm, (u8 *) node_name);
365
366   /*
367    * construct a registration and check if it's one this client already has
368    */
369   pri = punt_reg_find (reason, punt_to->index);
370
371   if (~0 != pri)
372     {
373       u32 pos;
374
375       pos = vec_search (pc->pc_regs, pri);
376
377       if (~0 == pos)
378         {
379           /* not a registration for this client */
380           return -1;
381         }
382       vec_del1 (pc->pc_regs, pos);
383
384       pr = pool_elt_at_index (punt_reg_pool, pri);
385
386       pr->pr_locks--;
387
388       if (0 == pr->pr_locks)
389         {
390           if (0 == --punt_reason_data[reason].pd_users &&
391               NULL != punt_reason_data[reason].pd_fn)
392             punt_reason_data[reason].pd_fn (VLIB_DISABLE,
393                                             punt_reason_data[reason].pd_data);
394           punt_reg_remove (pr);
395           pool_put (punt_reg_pool, pr);
396         }
397     }
398
399   /*
400    * rebuild the DP data-base
401    */
402   punt_reg_mk_dp (reason);
403
404   return (0);
405 }
406
407 int
408 vlib_punt_reason_validate (vlib_punt_reason_t reason)
409 {
410   if (reason < punt_reason_last)
411     return (0);
412
413   return (-1);
414 }
415
416 u32
417 vlib_punt_reason_get_flags (vlib_punt_reason_t pr)
418 {
419   return pr < punt_reason_last ? punt_reason_data[pr].flags : 0;
420 }
421
422 int
423 vlib_punt_reason_alloc (vlib_punt_hdl_t client, const char *reason_name,
424                         punt_interested_listener_t fn, void *data,
425                         vlib_punt_reason_t *reason, u32 flags,
426                         format_function_t *flags_format)
427 {
428   vlib_punt_reason_t new;
429
430   if (!punt_validate_client (client))
431     return -2;
432
433   new = punt_reason_last++;
434   vec_validate (punt_reason_data, new);
435   punt_reason_data[new].pd_name = format (NULL, "%s", reason_name);
436   punt_reason_data[new].pd_reason = new;
437   punt_reason_data[new].pd_fn = fn;
438   punt_reason_data[new].pd_data = data;
439   punt_reason_data[new].flags = flags;
440   punt_reason_data[new].flags_format = flags_format;
441   vec_add1 (punt_reason_data[new].pd_owners, client);
442
443   vlib_validate_combined_counter (&punt_counters, new);
444   vlib_zero_combined_counter (&punt_counters, new);
445
446   *reason = new;
447
448   /* build the DP data-base */
449   punt_reg_mk_dp (*reason);
450
451   return (0);
452 }
453
454 void
455 punt_reason_walk (punt_reason_walk_cb_t cb, void *ctx)
456 {
457   punt_reason_data_t *pd;
458
459   for (pd = punt_reason_data + 1; pd < vec_end (punt_reason_data); pd++)
460     {
461       cb (pd->pd_reason, pd->pd_name, ctx);
462     }
463 }
464
465 /* Parse node name -> node index. */
466 uword
467 unformat_punt_client (unformat_input_t * input, va_list * args)
468 {
469   u32 *result = va_arg (*args, u32 *);
470
471   return unformat_user (input, unformat_hash_vec_string,
472                         punt_client_db, result);
473 }
474
475 /* Parse punt reason */
476 uword
477 unformat_punt_reason (unformat_input_t *input, va_list *args)
478 {
479   u32 *result = va_arg (*args, u32 *);
480   u8 *s = 0;
481   u8 found = 0;
482   for (int i = 0; i < punt_reason_last - 1; i++)
483     {
484       punt_reason_data_t *pd = vec_elt_at_index (punt_reason_data, 1 + i);
485       vec_reset_length (s);
486       s = format (0, "%v%c", pd->pd_name, 0);
487       if (unformat (input, (const char *) s))
488         {
489           *result = pd->pd_reason;
490           found = 1;
491           break;
492         }
493     }
494   vec_free (s);
495   return found;
496 }
497
498 u8 *
499 format_punt_reg (u8 * s, va_list * args)
500 {
501   u32 pri = va_arg (*args, u32);
502   punt_reg_t *pr;
503
504   pr = pool_elt_at_index (punt_reg_pool, pri);
505
506   s = format (s, "%U -> %U",
507               format_vlib_punt_reason, pr->pr_reason,
508               format_vlib_node_name, vlib_get_main (), pr->pr_node_index);
509
510   return (s);
511 }
512
513 u8 *
514 format_punt_reason_data (u8 * s, va_list * args)
515 {
516   punt_reason_data_t *pd = va_arg (*args, punt_reason_data_t *);
517   punt_client_t *pc;
518   u32 *pci;
519   if (pd->flags_format)
520     s = format (s, "[%d] %v flags: %U from:[", pd->pd_reason, pd->pd_name,
521                 pd->flags_format, pd->flags);
522   else
523     s = format (s, "[%d] %v from:[", pd->pd_reason, pd->pd_name);
524   vec_foreach (pci, pd->pd_owners)
525   {
526     pc = pool_elt_at_index (punt_client_pool, *pci);
527     s = format (s, "%v ", pc->pc_name);
528   }
529   s = format (s, "]");
530
531   return (s);
532 }
533
534 u8 *
535 format_punt_client (u8 * s, va_list * args)
536 {
537   u32 pci = va_arg (*args, u32);
538   punt_format_flags_t flags = va_arg (*args, punt_format_flags_t);
539   punt_client_t *pc;
540
541   pc = pool_elt_at_index (punt_client_pool, pci);
542
543   s = format (s, "%v", pc->pc_name);
544
545   if (flags & PUNT_FORMAT_FLAG_DETAIL)
546     {
547       punt_reason_data_t *pd;
548       u32 *pri;
549
550       s = format (s, "\n registrations:");
551       vec_foreach (pri, pc->pc_regs)
552       {
553         s = format (s, "\n  [%U]", format_punt_reg, *pri);
554       }
555
556       s = format (s, "\n reasons:");
557
558       vec_foreach (pd, punt_reason_data)
559       {
560         u32 *tmp;
561
562         vec_foreach (tmp, pd->pd_owners)
563         {
564           if (*tmp == pci)
565             s = format (s, "\n  %U", format_punt_reason_data, pd);
566         }
567       }
568     }
569   return (s);
570 }
571
572 static clib_error_t *
573 punt_client_show (vlib_main_t * vm,
574                   unformat_input_t * input, vlib_cli_command_t * cmd)
575 {
576   u32 pci = ~0;
577
578   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
579     {
580       if (unformat (input, "%U", unformat_punt_client, &pci))
581         ;
582       else
583         break;
584     }
585
586   if (~0 != pci)
587     {
588       vlib_cli_output (vm, "%U", format_punt_client, pci,
589                        PUNT_FORMAT_FLAG_DETAIL);
590     }
591   else
592     {
593       u8 *name;
594
595       hash_foreach(name, pci, punt_client_db,
596         ({
597           vlib_cli_output (vm, "%U", format_punt_client, pci,
598                            PUNT_FORMAT_FLAG_NONE);
599         }));
600     }
601
602   return (NULL);
603 }
604
605 VLIB_CLI_COMMAND (punt_client_show_command, static) =
606 {
607   .path = "show punt client",
608   .short_help = "show client[s] registered with the punt infra",
609   .function = punt_client_show,
610 };
611
612 static clib_error_t *
613 punt_reason_show (vlib_main_t * vm,
614                   unformat_input_t * input, vlib_cli_command_t * cmd)
615 {
616   const punt_reason_data_t *pd;
617
618   vec_foreach (pd, punt_reason_data)
619   {
620     vlib_cli_output (vm, "%U", format_punt_reason_data, pd);
621   }
622
623   return (NULL);
624 }
625
626 VLIB_CLI_COMMAND (punt_reason_show_command, static) =
627 {
628   .path = "show punt reasons",
629   .short_help = "show all punt reasons",
630   .function = punt_reason_show,
631 };
632
633 static clib_error_t *
634 punt_db_show (vlib_main_t * vm,
635               unformat_input_t * input, vlib_cli_command_t * cmd)
636 {
637   u32 pri, ii, jj;
638   u64 key;
639
640   hash_foreach (key, pri, punt_reg_db,
641     ({
642       vlib_cli_output (vm, " %U", format_punt_reg, pri);
643     }));
644
645   vlib_cli_output (vm, "\nDerived data-plane data-base:");
646   vlib_cli_output (vm,
647                    "  (for each punt-reason the edge[s] from punt-dispatch)");
648
649   vec_foreach_index (ii, punt_dp_db)
650   {
651     u8 *s = NULL;
652     vlib_cli_output (vm, " %U", format_vlib_punt_reason, ii);
653
654     vec_foreach_index (jj, punt_dp_db[ii])
655     {
656       s = format (s, "%d ", punt_dp_db[ii][jj]);
657     }
658     vlib_cli_output (vm, "   [%v]", s);
659     vec_free (s);
660   }
661
662   return (NULL);
663 }
664
665 VLIB_CLI_COMMAND (punt_db_show_command, static) =
666 {
667   .path = "show punt db",
668   .short_help = "show the punt DB",
669   .function = punt_db_show,
670 };
671
672 static clib_error_t *
673 punt_stats_show (vlib_main_t * vm,
674                  unformat_input_t * input, vlib_cli_command_t * cmd)
675 {
676   vlib_combined_counter_main_t *cm = &punt_counters;
677   vlib_counter_t c;
678   u32 ii;
679
680   for (ii = 0; ii < vlib_combined_counter_n_counters (cm); ii++)
681     {
682       vlib_get_combined_counter (cm, ii, &c);
683       vlib_cli_output (vm, "%U packets:%lld bytes:%lld",
684                        format_vlib_punt_reason, ii, c.packets, c.bytes);
685     }
686
687   return (NULL);
688 }
689
690 VLIB_CLI_COMMAND (punt_stats_show_command, static) =
691 {
692   .path = "show punt stats",
693   .short_help = "show the punt stats",
694   .function = punt_stats_show,
695 };
696
697 static clib_error_t *
698 punt_init (vlib_main_t * vm)
699 {
700   punt_client_db = hash_create_vec (0, sizeof (u8), sizeof (u32));
701
702   return (NULL);
703 }
704
705 VLIB_INIT_FUNCTION (punt_init);
706
707 /*
708  * fd.io coding-style-patch-verification: ON
709  *
710  * Local Variables:
711  * eval: (c-set-style "gnu")
712  * End:
713  */