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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <vlib/punt.h>
19 * The last allocated punt reason
20 * Value 0 is reserved for invalid index.
22 static vlib_punt_reason_t punt_reason_last = 1;
25 * Counters per punt-reason
27 vlib_combined_counter_main_t punt_counters = {
29 .stat_segment_name = "/net/punt",
35 typedef struct punt_reason_data_t_
43 * The allocated reason value
45 vlib_punt_reason_t pd_reason;
48 * Clients/owners that have registered this reason
53 * clients interested/listening to this reason
58 * function to invoke if a client becomes interested in the code.
60 punt_interested_listener_t pd_fn;
63 * Data to pass to the callback
68 * Flags associated to the reason
73 * Formatting function for flags;
75 format_function_t *flags_format;
79 * data for each punt reason
81 static punt_reason_data_t *punt_reason_data;
83 typedef enum punt_format_flags_t_
85 PUNT_FORMAT_FLAG_NONE = 0,
86 PUNT_FORMAT_FLAG_DETAIL = (1 << 0),
87 } punt_format_flags_t;
90 * A registration, by a client, to direct punted traffic to a given node
92 typedef struct punt_reg_t_
95 * Reason the packets were punted
97 vlib_punt_reason_t pr_reason;
100 * number of clients that have made this registration
105 * The edge from the punt dispatch node to the requested node
110 * node-index to send punted packets to
116 * Pool of registrations
118 static punt_reg_t *punt_reg_pool;
121 * A DB of all the register nodes against punt reason and node index
123 static uword *punt_reg_db;
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
132 * A client using the punt serivce and its registrations
134 typedef struct punt_client_t_
137 * The name of the client
142 * The registrations is has made
150 static punt_client_t *punt_client_pool;
153 * DB of clients key'd by their name
155 static uword *punt_client_db;
158 format_vlib_punt_reason (u8 * s, va_list * args)
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;
164 return (format (s, "[%d] %v flags: %U", pr, punt_reason_data[pr].pd_name,
165 flags_format, flags));
167 return (format (s, "[%d] %v", pr, punt_reason_data[pr].pd_name));
171 vlib_punt_client_register (const char *who)
177 pc_name = format (NULL, "%s", who);
178 p = hash_get_mem (punt_client_db, pc_name);
184 pool_get (punt_client_pool, pc);
185 pci = pc - punt_client_pool;
187 pc->pc_name = pc_name;
189 hash_set_mem (punt_client_db, pc->pc_name, pci);
201 punt_validate_client (vlib_punt_hdl_t client)
203 return (!pool_is_free_index (punt_client_pool, client));
207 punt_reg_mk_key (vlib_punt_reason_t reason, u32 node_index)
209 return (((u64) node_index) << 32 | reason);
213 punt_reg_find (vlib_punt_reason_t reason, u32 node_index)
217 p = hash_get (punt_reg_db, punt_reg_mk_key (reason, node_index));
226 punt_reg_add (const punt_reg_t * pr)
228 hash_set (punt_reg_db, punt_reg_mk_key (pr->pr_reason,
234 punt_reg_remove (const punt_reg_t * pr)
236 hash_unset (punt_reg_db, punt_reg_mk_key (pr->pr_reason,
241 * reconstruct the DP per-reason DB
244 punt_reg_mk_dp (vlib_punt_reason_t reason)
246 u32 pri, *prip, *pris;
247 const punt_reg_t *pr;
253 vec_validate (punt_dp_db, reason);
255 old = punt_dp_db[reason];
258 hash_foreach (key, pri, punt_reg_db,
265 * A check for an empty vector is done in the DP, so the a zero
266 * length vector here is ok
268 vec_foreach (prip, pris)
270 pr = pool_elt_at_index (punt_reg_pool, *prip);
272 if (pr->pr_reason == reason)
273 vec_add1 (edges, pr->pr_edge);
276 /* atomic update of the DP */
277 punt_dp_db[reason] = edges;
283 vlib_punt_register (vlib_punt_hdl_t client,
284 vlib_punt_reason_t reason, const char *node_name)
286 vlib_node_t *punt_to, *punt_from;
292 if (reason >= punt_reason_last)
294 if (!punt_validate_client (client))
297 vm = vlib_get_main ();
298 pc = pool_elt_at_index (punt_client_pool, client);
299 punt_to = vlib_get_node_by_name (vm, (u8 *) node_name);
300 punt_from = vlib_get_node_by_name (vm, (u8 *) "punt-dispatch");
303 * find a global matching registration
305 pri = punt_reg_find (reason, punt_to->index);
311 pos = vec_search (pc->pc_regs, pri);
315 /* duplicate registration for this client */
319 pr = pool_elt_at_index (punt_reg_pool, pri);
323 pool_get (punt_reg_pool, pr);
325 pr->pr_reason = reason;
326 pr->pr_node_index = punt_to->index;
327 pr->pr_edge = vlib_node_add_next (vm,
328 punt_from->index, pr->pr_node_index);
330 pri = pr - punt_reg_pool;
332 if (0 == punt_reason_data[reason].pd_users++ &&
333 NULL != punt_reason_data[reason].pd_fn)
334 punt_reason_data[reason].pd_fn (VLIB_ENABLE,
335 punt_reason_data[reason].pd_data);
341 * add this reg to the list the client has made
344 vec_add1 (pc->pc_regs, pri);
346 punt_reg_mk_dp (reason);
352 vlib_punt_unregister (vlib_punt_hdl_t client,
353 vlib_punt_reason_t reason, const char *node_name)
355 vlib_node_t *punt_to;
361 if (reason >= punt_reason_last)
364 vm = vlib_get_main ();
365 pc = pool_elt_at_index (punt_client_pool, client);
366 punt_to = vlib_get_node_by_name (vm, (u8 *) node_name);
369 * construct a registration and check if it's one this client already has
371 pri = punt_reg_find (reason, punt_to->index);
377 pos = vec_search (pc->pc_regs, pri);
381 /* not a registration for this client */
384 vec_del1 (pc->pc_regs, pos);
386 pr = pool_elt_at_index (punt_reg_pool, pri);
390 if (0 == pr->pr_locks)
392 if (0 == --punt_reason_data[reason].pd_users &&
393 NULL != punt_reason_data[reason].pd_fn)
394 punt_reason_data[reason].pd_fn (VLIB_DISABLE,
395 punt_reason_data[reason].pd_data);
396 punt_reg_remove (pr);
397 pool_put (punt_reg_pool, pr);
402 * rebuild the DP data-base
404 punt_reg_mk_dp (reason);
410 vlib_punt_reason_validate (vlib_punt_reason_t reason)
412 if (reason < punt_reason_last)
419 vlib_punt_reason_get_flags (vlib_punt_reason_t pr)
421 return pr < punt_reason_last ? punt_reason_data[pr].flags : 0;
425 vlib_punt_reason_alloc (vlib_punt_hdl_t client, const char *reason_name,
426 punt_interested_listener_t fn, void *data,
427 vlib_punt_reason_t *reason, u32 flags,
428 format_function_t *flags_format)
430 vlib_punt_reason_t new;
432 if (!punt_validate_client (client))
435 new = punt_reason_last++;
436 vec_validate (punt_reason_data, new);
437 punt_reason_data[new].pd_name = format (NULL, "%s", reason_name);
438 punt_reason_data[new].pd_reason = new;
439 punt_reason_data[new].pd_fn = fn;
440 punt_reason_data[new].pd_data = data;
441 punt_reason_data[new].flags = flags;
442 punt_reason_data[new].flags_format = flags_format;
443 vec_add1 (punt_reason_data[new].pd_owners, client);
445 vlib_validate_combined_counter (&punt_counters, new);
446 vlib_zero_combined_counter (&punt_counters, new);
450 /* build the DP data-base */
451 punt_reg_mk_dp (*reason);
457 punt_reason_walk (punt_reason_walk_cb_t cb, void *ctx)
459 punt_reason_data_t *pd;
461 for (pd = punt_reason_data + 1; pd < vec_end (punt_reason_data); pd++)
463 cb (pd->pd_reason, pd->pd_name, ctx);
467 /* Parse node name -> node index. */
469 unformat_punt_client (unformat_input_t * input, va_list * args)
471 u32 *result = va_arg (*args, u32 *);
473 return unformat_user (input, unformat_hash_vec_string,
474 punt_client_db, result);
477 /* Parse punt reason */
479 unformat_punt_reason (unformat_input_t *input, va_list *args)
481 u32 *result = va_arg (*args, u32 *);
484 for (int i = 0; i < punt_reason_last - 1; i++)
486 punt_reason_data_t *pd = vec_elt_at_index (punt_reason_data, 1 + i);
487 vec_reset_length (s);
488 s = format (0, "%v%c", pd->pd_name, 0);
489 if (unformat (input, (const char *) s))
491 *result = pd->pd_reason;
501 format_punt_reg (u8 * s, va_list * args)
503 u32 pri = va_arg (*args, u32);
506 pr = pool_elt_at_index (punt_reg_pool, pri);
508 s = format (s, "%U -> %U",
509 format_vlib_punt_reason, pr->pr_reason,
510 format_vlib_node_name, vlib_get_main (), pr->pr_node_index);
516 format_punt_reason_data (u8 * s, va_list * args)
518 punt_reason_data_t *pd = va_arg (*args, punt_reason_data_t *);
521 if (pd->flags_format)
522 s = format (s, "[%d] %v flags: %U from:[", pd->pd_reason, pd->pd_name,
523 pd->flags_format, pd->flags);
525 s = format (s, "[%d] %v from:[", pd->pd_reason, pd->pd_name);
526 vec_foreach (pci, pd->pd_owners)
528 pc = pool_elt_at_index (punt_client_pool, *pci);
529 s = format (s, "%v ", pc->pc_name);
537 format_punt_client (u8 * s, va_list * args)
539 u32 pci = va_arg (*args, u32);
540 punt_format_flags_t flags = va_arg (*args, punt_format_flags_t);
543 pc = pool_elt_at_index (punt_client_pool, pci);
545 s = format (s, "%v", pc->pc_name);
547 if (flags & PUNT_FORMAT_FLAG_DETAIL)
549 punt_reason_data_t *pd;
552 s = format (s, "\n registrations:");
553 vec_foreach (pri, pc->pc_regs)
555 s = format (s, "\n [%U]", format_punt_reg, *pri);
558 s = format (s, "\n reasons:");
560 vec_foreach (pd, punt_reason_data)
564 vec_foreach (tmp, pd->pd_owners)
567 s = format (s, "\n %U", format_punt_reason_data, pd);
574 static clib_error_t *
575 punt_client_show (vlib_main_t * vm,
576 unformat_input_t * input, vlib_cli_command_t * cmd)
580 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
582 if (unformat (input, "%U", unformat_punt_client, &pci))
590 vlib_cli_output (vm, "%U", format_punt_client, pci,
591 PUNT_FORMAT_FLAG_DETAIL);
598 hash_foreach(name, pci, punt_client_db,
600 vlib_cli_output (vm, "%U", format_punt_client, pci,
601 PUNT_FORMAT_FLAG_NONE);
610 VLIB_CLI_COMMAND (punt_client_show_command, static) =
612 .path = "show punt client",
613 .short_help = "show client[s] registered with the punt infra",
614 .function = punt_client_show,
618 static clib_error_t *
619 punt_reason_show (vlib_main_t * vm,
620 unformat_input_t * input, vlib_cli_command_t * cmd)
622 const punt_reason_data_t *pd;
624 vec_foreach (pd, punt_reason_data)
626 vlib_cli_output (vm, "%U", format_punt_reason_data, pd);
633 VLIB_CLI_COMMAND (punt_reason_show_command, static) =
635 .path = "show punt reasons",
636 .short_help = "show all punt reasons",
637 .function = punt_reason_show,
641 static clib_error_t *
642 punt_db_show (vlib_main_t * vm,
643 unformat_input_t * input, vlib_cli_command_t * cmd)
649 hash_foreach (key, pri, punt_reg_db,
651 vlib_cli_output (vm, " %U", format_punt_reg, pri);
655 vlib_cli_output (vm, "\nDerived data-plane data-base:");
657 " (for each punt-reason the edge[s] from punt-dispatch)");
659 vec_foreach_index (ii, punt_dp_db)
662 vlib_cli_output (vm, " %U", format_vlib_punt_reason, ii);
664 vec_foreach_index (jj, punt_dp_db[ii])
666 s = format (s, "%d ", punt_dp_db[ii][jj]);
668 vlib_cli_output (vm, " [%v]", s);
676 VLIB_CLI_COMMAND (punt_db_show_command, static) =
678 .path = "show punt db",
679 .short_help = "show the punt DB",
680 .function = punt_db_show,
684 static clib_error_t *
685 punt_stats_show (vlib_main_t * vm,
686 unformat_input_t * input, vlib_cli_command_t * cmd)
688 vlib_combined_counter_main_t *cm = &punt_counters;
692 for (ii = 0; ii < vlib_combined_counter_n_counters (cm); ii++)
694 vlib_get_combined_counter (cm, ii, &c);
695 vlib_cli_output (vm, "%U packets:%lld bytes:%lld",
696 format_vlib_punt_reason, ii, c.packets, c.bytes);
703 VLIB_CLI_COMMAND (punt_stats_show_command, static) =
705 .path = "show punt stats",
706 .short_help = "show the punt stats",
707 .function = punt_stats_show,
711 static clib_error_t *
712 punt_init (vlib_main_t * vm)
714 punt_client_db = hash_create_vec (0, sizeof (u8), sizeof (u32));
719 VLIB_INIT_FUNCTION (punt_init);
722 * fd.io coding-style-patch-verification: ON
725 * eval: (c-set-style "gnu")