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