blob: b59e5d251bec46ff095345b64c6ddaef63fdda33 [file] [log] [blame]
Neale Ranns76b56492018-09-28 15:16:14 +00001/*
Neale Ranns7f6bd242019-04-08 02:37:40 -07002 * Copyright (c) 2019 Cisco and/or its affiliates.
Neale Ranns76b56492018-09-28 15:16:14 +00003 * 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
Filip Tehlar4362baa2020-04-02 13:13:39 +000020 * Value 0 is reserved for invalid index.
Neale Ranns76b56492018-09-28 15:16:14 +000021 */
Filip Tehlar4362baa2020-04-02 13:13:39 +000022static vlib_punt_reason_t punt_reason_last = 1;
Neale Ranns76b56492018-09-28 15:16:14 +000023
24/**
25 * Counters per punt-reason
26 */
27vlib_combined_counter_main_t punt_counters = {
28 .name = "punt",
29 .stat_segment_name = "/net/punt",
30};
31
32/**
33 * A punt reason
34 */
35typedef 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;
Neale Ranns8d6d74c2020-02-20 09:45:16 +000051
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;
Mohammed Hawari45723b82021-02-05 15:40:00 +010066
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;
Neale Ranns76b56492018-09-28 15:16:14 +000076} punt_reason_data_t;
77
78/**
79 * data for each punt reason
80 */
81static punt_reason_data_t *punt_reason_data;
82
83typedef 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 */
92typedef 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 */
118static punt_reg_t *punt_reg_pool;
119
120/**
121 * A DB of all the register nodes against punt reason and node index
122 */
123static 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 */
129u16 **punt_dp_db;
130
131/**
132 * A client using the punt serivce and its registrations
133 */
134typedef 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 */
150static punt_client_t *punt_client_pool;
151
152/**
153 * DB of clients key'd by their name
154 */
155static uword *punt_client_db;
156
157u8 *
158format_vlib_punt_reason (u8 * s, va_list * args)
159{
160 vlib_punt_reason_t pr = va_arg (*args, int);
Mohammed Hawari45723b82021-02-05 15:40:00 +0100161 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));
Neale Ranns76b56492018-09-28 15:16:14 +0000168}
169
170vlib_punt_hdl_t
171vlib_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
200static int
201punt_validate_client (vlib_punt_hdl_t client)
202{
203 return (!pool_is_free_index (punt_client_pool, client));
204}
205
206static u64
207punt_reg_mk_key (vlib_punt_reason_t reason, u32 node_index)
208{
209 return (((u64) node_index) << 32 | reason);
210}
211
212static u32
213punt_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
225static void
226punt_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
233static void
234punt_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 */
243static void
244punt_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
Neale Ranns76b56492018-09-28 15:16:14 +0000257 hash_foreach (key, pri, punt_reg_db,
258 ({
259 vec_add1(pris, pri);
260 }));
Neale Ranns76b56492018-09-28 15:16:14 +0000261
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
280int
Neale Ranns8d6d74c2020-02-20 09:45:16 +0000281vlib_punt_register (vlib_punt_hdl_t client,
282 vlib_punt_reason_t reason, const char *node_name)
Neale Ranns76b56492018-09-28 15:16:14 +0000283{
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
Neale Ranns8d6d74c2020-02-20 09:45:16 +0000330 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
Neale Ranns76b56492018-09-28 15:16:14 +0000335 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
349int
350vlib_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 {
Neale Ranns8d6d74c2020-02-20 09:45:16 +0000390 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);
Neale Ranns76b56492018-09-28 15:16:14 +0000394 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
407int
Neale Ranns50f0ac02019-05-15 02:13:37 -0700408vlib_punt_reason_validate (vlib_punt_reason_t reason)
409{
410 if (reason < punt_reason_last)
411 return (0);
412
413 return (-1);
414}
415
Mohammed Hawari45723b82021-02-05 15:40:00 +0100416u32
417vlib_punt_reason_get_flags (vlib_punt_reason_t pr)
418{
419 return pr < punt_reason_last ? punt_reason_data[pr].flags : 0;
420}
421
Neale Ranns50f0ac02019-05-15 02:13:37 -0700422int
Mohammed Hawari45723b82021-02-05 15:40:00 +0100423vlib_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)
Neale Ranns76b56492018-09-28 15:16:14 +0000427{
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;
Neale Ranns8d6d74c2020-02-20 09:45:16 +0000437 punt_reason_data[new].pd_fn = fn;
438 punt_reason_data[new].pd_data = data;
Mohammed Hawari45723b82021-02-05 15:40:00 +0100439 punt_reason_data[new].flags = flags;
440 punt_reason_data[new].flags_format = flags_format;
Neale Ranns76b56492018-09-28 15:16:14 +0000441 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
Neale Ranns50f0ac02019-05-15 02:13:37 -0700454void
455punt_reason_walk (punt_reason_walk_cb_t cb, void *ctx)
456{
457 punt_reason_data_t *pd;
458
Filip Tehlar4362baa2020-04-02 13:13:39 +0000459 for (pd = punt_reason_data + 1; pd < vec_end (punt_reason_data); pd++)
460 {
461 cb (pd->pd_reason, pd->pd_name, ctx);
462 }
Neale Ranns50f0ac02019-05-15 02:13:37 -0700463}
464
Neale Ranns76b56492018-09-28 15:16:14 +0000465/* Parse node name -> node index. */
466uword
467unformat_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
Mohammed Hawari45723b82021-02-05 15:40:00 +0100475/* Parse punt reason */
476uword
477unformat_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
Neale Ranns76b56492018-09-28 15:16:14 +0000498u8 *
499format_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
513u8 *
514format_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;
Mohammed Hawari45723b82021-02-05 15:40:00 +0100519 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);
Neale Ranns76b56492018-09-28 15:16:14 +0000524 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
534u8 *
535format_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
572static clib_error_t *
573punt_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
Neale Ranns76b56492018-09-28 15:16:14 +0000595 hash_foreach(name, pci, punt_client_db,
596 ({
597 vlib_cli_output (vm, "%U", format_punt_client, pci,
598 PUNT_FORMAT_FLAG_NONE);
599 }));
Neale Ranns76b56492018-09-28 15:16:14 +0000600 }
601
602 return (NULL);
603}
604
Neale Ranns76b56492018-09-28 15:16:14 +0000605VLIB_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};
Neale Ranns76b56492018-09-28 15:16:14 +0000611
612static clib_error_t *
613punt_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
Neale Ranns76b56492018-09-28 15:16:14 +0000626VLIB_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};
Neale Ranns76b56492018-09-28 15:16:14 +0000632
633static clib_error_t *
634punt_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
Neale Ranns76b56492018-09-28 15:16:14 +0000640 hash_foreach (key, pri, punt_reg_db,
641 ({
642 vlib_cli_output (vm, " %U", format_punt_reg, pri);
643 }));
Neale Ranns76b56492018-09-28 15:16:14 +0000644
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
Neale Ranns76b56492018-09-28 15:16:14 +0000665VLIB_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};
Neale Ranns76b56492018-09-28 15:16:14 +0000671
672static clib_error_t *
673punt_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
Neale Ranns76b56492018-09-28 15:16:14 +0000690VLIB_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};
Neale Ranns76b56492018-09-28 15:16:14 +0000696
697static clib_error_t *
698punt_init (vlib_main_t * vm)
699{
700 punt_client_db = hash_create_vec (0, sizeof (u8), sizeof (u32));
701
702 return (NULL);
703}
704
705VLIB_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 */