blob: 04e3b5a39569aecd998ceef3f4ca5ea25459bea8 [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;
Neale Ranns76b56492018-09-28 15:16:14 +000066} punt_reason_data_t;
67
68/**
69 * data for each punt reason
70 */
71static punt_reason_data_t *punt_reason_data;
72
73typedef 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 */
82typedef 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 */
108static punt_reg_t *punt_reg_pool;
109
110/**
111 * A DB of all the register nodes against punt reason and node index
112 */
113static 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 */
119u16 **punt_dp_db;
120
121/**
122 * A client using the punt serivce and its registrations
123 */
124typedef 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 */
140static punt_client_t *punt_client_pool;
141
142/**
143 * DB of clients key'd by their name
144 */
145static uword *punt_client_db;
146
147u8 *
148format_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
155vlib_punt_hdl_t
156vlib_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
185static int
186punt_validate_client (vlib_punt_hdl_t client)
187{
188 return (!pool_is_free_index (punt_client_pool, client));
189}
190
191static u64
192punt_reg_mk_key (vlib_punt_reason_t reason, u32 node_index)
193{
194 return (((u64) node_index) << 32 | reason);
195}
196
197static u32
198punt_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
210static void
211punt_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
218static void
219punt_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 */
228static void
229punt_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
267int
Neale Ranns8d6d74c2020-02-20 09:45:16 +0000268vlib_punt_register (vlib_punt_hdl_t client,
269 vlib_punt_reason_t reason, const char *node_name)
Neale Ranns76b56492018-09-28 15:16:14 +0000270{
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
Neale Ranns8d6d74c2020-02-20 09:45:16 +0000317 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
Neale Ranns76b56492018-09-28 15:16:14 +0000322 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
336int
337vlib_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 {
Neale Ranns8d6d74c2020-02-20 09:45:16 +0000377 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);
Neale Ranns76b56492018-09-28 15:16:14 +0000381 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
394int
Neale Ranns50f0ac02019-05-15 02:13:37 -0700395vlib_punt_reason_validate (vlib_punt_reason_t reason)
396{
397 if (reason < punt_reason_last)
398 return (0);
399
400 return (-1);
401}
402
403int
Neale Ranns76b56492018-09-28 15:16:14 +0000404vlib_punt_reason_alloc (vlib_punt_hdl_t client,
Neale Ranns8d6d74c2020-02-20 09:45:16 +0000405 const char *reason_name,
406 punt_interested_listener_t fn,
407 void *data, vlib_punt_reason_t * reason)
Neale Ranns76b56492018-09-28 15:16:14 +0000408{
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;
Neale Ranns8d6d74c2020-02-20 09:45:16 +0000418 punt_reason_data[new].pd_fn = fn;
419 punt_reason_data[new].pd_data = data;
Neale Ranns76b56492018-09-28 15:16:14 +0000420 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
Neale Ranns50f0ac02019-05-15 02:13:37 -0700433void
434punt_reason_walk (punt_reason_walk_cb_t cb, void *ctx)
435{
436 punt_reason_data_t *pd;
437
Filip Tehlar4362baa2020-04-02 13:13:39 +0000438 for (pd = punt_reason_data + 1; pd < vec_end (punt_reason_data); pd++)
439 {
440 cb (pd->pd_reason, pd->pd_name, ctx);
441 }
Neale Ranns50f0ac02019-05-15 02:13:37 -0700442}
443
Neale Ranns76b56492018-09-28 15:16:14 +0000444/* Parse node name -> node index. */
445uword
446unformat_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
454u8 *
455format_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
469u8 *
470format_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
487u8 *
488format_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
525static clib_error_t *
526punt_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* */
561VLIB_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
569static clib_error_t *
570punt_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* */
584VLIB_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
592static clib_error_t *
593punt_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* */
627VLIB_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
635static clib_error_t *
636punt_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* */
654VLIB_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
662static clib_error_t *
663punt_init (vlib_main_t * vm)
664{
665 punt_client_db = hash_create_vec (0, sizeof (u8), sizeof (u32));
666
667 return (NULL);
668}
669
670VLIB_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 */