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