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