Line data Source code
1 : /*
2 : * Copyright (c) 2015 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:
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 : * init.c: mechanism for functions to be called at init/exit.
17 : *
18 : * Copyright (c) 2008 Eliot Dresselhaus
19 : *
20 : * Permission is hereby granted, free of charge, to any person obtaining
21 : * a copy of this software and associated documentation files (the
22 : * "Software"), to deal in the Software without restriction, including
23 : * without limitation the rights to use, copy, modify, merge, publish,
24 : * distribute, sublicense, and/or sell copies of the Software, and to
25 : * permit persons to whom the Software is furnished to do so, subject to
26 : * the following conditions:
27 : *
28 : * The above copyright notice and this permission notice shall be
29 : * included in all copies or substantial portions of the Software.
30 : *
31 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 : * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 : * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 : * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 : * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 : * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 : * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 : */
39 :
40 : #include <vlib/vlib.h>
41 : #include <vppinfra/ptclosure.h>
42 :
43 : /**
44 : * @file
45 : * @brief Init function ordering and execution implementation
46 : * Topological sort for all classes of init functions, and
47 : * a relatively simple API routine to invoke them.
48 : */
49 :
50 : /*? %%clicmd:group_label Init functions %% ?*/
51 :
52 : static int
53 46409 : comma_split (u8 * s, u8 ** a, u8 ** b)
54 : {
55 46409 : *a = s;
56 :
57 686604 : while (*s && *s != ',')
58 640195 : s++;
59 :
60 46409 : if (*s == ',')
61 46409 : *s = 0;
62 : else
63 0 : return 1;
64 :
65 46409 : *b = (u8 *) (s + 1);
66 46409 : return 0;
67 : }
68 :
69 : /**
70 : * @brief Topological sorter for init function chains.
71 : * @param head [in/out] address of the listhead to be sorted
72 : * @returns 0 on success, otherwise a clib_error_t *.
73 : */
74 :
75 3354 : clib_error_t *vlib_sort_init_exit_functions
76 : (_vlib_init_function_list_elt_t ** head)
77 : {
78 : uword *index_by_name;
79 : uword *reg_by_index;
80 3354 : u8 **init_f_names = 0;
81 : u8 *init_f_name;
82 : char **these_constraints;
83 : char *this_constraint_c;
84 3354 : u8 **constraints = 0;
85 : u8 *constraint_tuple;
86 : u8 *this_constraint;
87 : char *prev_name;
88 : u8 **orig, **closure;
89 : uword *p;
90 : int i, j, k;
91 : u8 *a_name, *b_name;
92 : int a_index, b_index;
93 : int n_init_fns;
94 3354 : u32 *result = 0;
95 3354 : _vlib_init_function_list_elt_t *this_reg = 0;
96 : hash_pair_t *hp;
97 3354 : u8 **keys_to_delete = 0;
98 :
99 : /*
100 : * two hash tables: name to index in init_f_names, and
101 : * init function registration pointer by index
102 : */
103 3354 : index_by_name = hash_create_string (0, sizeof (uword));
104 3354 : reg_by_index = hash_create (0, sizeof (uword));
105 :
106 3354 : this_reg = *head;
107 :
108 : /* pass 1, collect init fcn names, construct a before b pairs */
109 235933 : while (this_reg)
110 : {
111 232579 : init_f_name = format (0, "%s%c", this_reg->name, 0);
112 232579 : hash_set (reg_by_index, vec_len (init_f_names), (uword) this_reg);
113 :
114 465158 : hash_set_mem (index_by_name, init_f_name, vec_len (init_f_names));
115 :
116 232579 : vec_add1 (init_f_names, init_f_name);
117 :
118 232579 : these_constraints = this_reg->runs_before;
119 235933 : while (these_constraints && these_constraints[0])
120 : {
121 3354 : this_constraint_c = these_constraints[0];
122 :
123 3354 : constraint_tuple = format (0, "%s,%s%c", init_f_name,
124 : this_constraint_c, 0);
125 3354 : vec_add1 (constraints, constraint_tuple);
126 3354 : these_constraints++;
127 : }
128 :
129 232579 : these_constraints = this_reg->runs_after;
130 262777 : while (these_constraints && these_constraints[0])
131 : {
132 30198 : this_constraint_c = these_constraints[0];
133 :
134 30198 : constraint_tuple = format (0, "%s,%s%c",
135 : this_constraint_c, init_f_name, 0);
136 30198 : vec_add1 (constraints, constraint_tuple);
137 30198 : these_constraints++;
138 : }
139 :
140 232579 : this_reg = this_reg->next_init_function;
141 : }
142 :
143 : /*
144 : * pass 2: collect "a then b then c then d" constraints.
145 : * all init fcns must be known at this point.
146 : */
147 3354 : this_reg = *head;
148 235933 : while (this_reg)
149 : {
150 232579 : these_constraints = this_reg->init_order;
151 :
152 232579 : prev_name = 0;
153 : /* Across the list of constraints */
154 247672 : while (these_constraints && these_constraints[0])
155 : {
156 15093 : this_constraint_c = these_constraints[0];
157 15093 : p = hash_get_mem (index_by_name, this_constraint_c);
158 15093 : if (p == 0)
159 : {
160 0 : clib_warning
161 : ("order constraint fcn '%s' not found", this_constraint_c);
162 0 : these_constraints++;
163 0 : continue;
164 : }
165 :
166 15093 : if (prev_name == 0)
167 : {
168 2236 : prev_name = this_constraint_c;
169 2236 : these_constraints++;
170 2236 : continue;
171 : }
172 :
173 12857 : constraint_tuple = format (0, "%s,%s%c", prev_name,
174 : this_constraint_c, 0);
175 12857 : vec_add1 (constraints, constraint_tuple);
176 12857 : prev_name = this_constraint_c;
177 12857 : these_constraints++;
178 : }
179 232579 : this_reg = this_reg->next_init_function;
180 : }
181 :
182 3354 : n_init_fns = vec_len (init_f_names);
183 3354 : orig = clib_ptclosure_alloc (n_init_fns);
184 :
185 49763 : for (i = 0; i < vec_len (constraints); i++)
186 : {
187 46409 : this_constraint = constraints[i];
188 :
189 46409 : if (comma_split (this_constraint, &a_name, &b_name))
190 0 : return clib_error_return (0, "comma_split failed!");
191 :
192 92818 : p = hash_get_mem (index_by_name, a_name);
193 : /*
194 : * Note: the next two errors mean that something is
195 : * b0rked. As in: if you code "A runs before on B," and you type
196 : * B incorrectly, you lose. Nonexistent init functions are tolerated.
197 : */
198 46409 : if (p == 0)
199 : {
200 0 : clib_warning ("init function '%s' not found (before '%s')",
201 : a_name, b_name);
202 0 : continue;
203 : }
204 46409 : a_index = p[0];
205 :
206 92818 : p = hash_get_mem (index_by_name, b_name);
207 46409 : if (p == 0)
208 : {
209 0 : clib_warning ("init function '%s' not found (after '%s')",
210 : b_name, a_name);
211 0 : continue;
212 : }
213 46409 : b_index = p[0];
214 :
215 : /* add a before b to the original set of constraints */
216 46409 : orig[a_index][b_index] = 1;
217 46409 : vec_free (this_constraint);
218 : }
219 :
220 : /* Compute the positive transitive closure of the original constraints */
221 3354 : closure = clib_ptclosure (orig);
222 :
223 : /* Compute a partial order across feature nodes, if one exists. */
224 235933 : again:
225 33327800 : for (i = 0; i < n_init_fns; i++)
226 : {
227 3720380000 : for (j = 0; j < n_init_fns; j++)
228 : {
229 3720140000 : if (closure[i][j])
230 33091800 : goto item_constrained;
231 : }
232 : /* Item i can be output */
233 232579 : vec_add1 (result, i);
234 : {
235 66183700 : for (k = 0; k < n_init_fns; k++)
236 65951100 : closure[k][i] = 0;
237 : /*
238 : * Add a "Magic" a before a constraint.
239 : * This means we'll never output it again
240 : */
241 232579 : closure[i][i] = 1;
242 232579 : goto again;
243 : }
244 33091800 : item_constrained:
245 : ;
246 : }
247 :
248 : /* see if we got a partial order... */
249 3354 : if (vec_len (result) != n_init_fns)
250 0 : return clib_error_return
251 : (0, "Failed to find a suitable init function order!");
252 :
253 : /*
254 : * We win.
255 : * Bind the index variables, and output the feature node name vector
256 : * using the partial order we just computed. Result is in stack
257 : * order, because the entry with the fewest constraints (e.g. none)
258 : * is output first, etc.
259 : * Reset the listhead, and add items in result (aka reverse) order.
260 : */
261 3354 : *head = 0;
262 235933 : for (i = 0; i < n_init_fns; i++)
263 : {
264 232579 : p = hash_get (reg_by_index, result[i]);
265 232579 : ASSERT (p != 0);
266 232579 : this_reg = (_vlib_init_function_list_elt_t *) p[0];
267 :
268 232579 : this_reg->next_init_function = *head;
269 232579 : *head = this_reg;
270 : }
271 :
272 : /* Finally, clean up all the fine data we allocated */
273 : /* *INDENT-OFF* */
274 736797 : hash_foreach_pair (hp, index_by_name,
275 : ({
276 : vec_add1 (keys_to_delete, (u8 *)hp->key);
277 : }));
278 : /* *INDENT-ON* */
279 3354 : hash_free (index_by_name);
280 235933 : for (i = 0; i < vec_len (keys_to_delete); i++)
281 232579 : vec_free (keys_to_delete[i]);
282 3354 : vec_free (keys_to_delete);
283 3354 : hash_free (reg_by_index);
284 3354 : vec_free (result);
285 3354 : clib_ptclosure_free (orig);
286 3354 : clib_ptclosure_free (closure);
287 3354 : return 0;
288 : }
289 :
290 : /**
291 : * @brief call a set of init / exit / main-loop enter functions
292 : * @param vm vlib_main_t
293 : * @param head address of the listhead to sort and then invoke
294 : * @returns 0 on success, clib_error_t * on error
295 : *
296 : * The "init_functions_called" hash supports a subtle mix of procedural
297 : * and formally-specified ordering constraints. The following schemes
298 : * are *roughly* equivalent:
299 : *
300 : * static clib_error_t *init_runs_first (vlib_main_t *vm)
301 : * {
302 : * clib_error_t *error;
303 : *
304 : * ... do some stuff...
305 : *
306 : * if ((error = vlib_call_init_function (init_runs_next)))
307 : * return error;
308 : * ...
309 : * }
310 : * VLIB_INIT_FUNCTION (init_runs_first);
311 : *
312 : * and
313 : *
314 : * static clib_error_t *init_runs_first (vlib_main_t *vm)
315 : * {
316 : * ... do some stuff...
317 : * }
318 : * VLIB_INIT_FUNCTION (init_runs_first) =
319 : * {
320 : * .runs_before = VLIB_INITS("init_runs_next"),
321 : * };
322 : *
323 : * The first form will [most likely] call "init_runs_next" on the
324 : * spot. The second form means that "init_runs_first" runs before
325 : * "init_runs_next," possibly much earlier in the sequence.
326 : *
327 : * Please DO NOT construct sets of init functions where A before B
328 : * actually means A *right before* B. It's not necessary - simply combine
329 : * A and B - and it leads to hugely annoying debugging exercises.
330 : */
331 :
332 : static inline clib_error_t *
333 2847 : call_init_exit_functions_internal (vlib_main_t *vm,
334 : _vlib_init_function_list_elt_t **headp,
335 : int call_once, int do_sort, int is_global)
336 : {
337 2847 : vlib_global_main_t *vgm = vlib_get_global_main ();
338 2847 : clib_error_t *error = 0;
339 : _vlib_init_function_list_elt_t *i;
340 :
341 2847 : if (do_sort && (error = vlib_sort_init_exit_functions (headp)))
342 0 : return (error);
343 :
344 2847 : i = *headp;
345 232898 : while (i)
346 : {
347 : uword *h;
348 :
349 230049 : if (is_global)
350 229784 : h = hash_get (vgm->init_functions_called, i->f);
351 : else
352 265 : h = hash_get (vm->worker_init_functions_called, i->f);
353 :
354 230043 : if (call_once && !h)
355 : {
356 208801 : if (call_once)
357 : {
358 208801 : if (is_global)
359 208542 : hash_set1 (vgm->init_functions_called, i->f);
360 : else
361 259 : hash_set1 (vm->worker_init_functions_called, i->f);
362 : }
363 208800 : error = i->f (vm);
364 208809 : if (error)
365 0 : return error;
366 : }
367 230051 : i = i->next_init_function;
368 : }
369 2849 : return error;
370 : }
371 :
372 : clib_error_t *
373 2795 : vlib_call_init_exit_functions (vlib_main_t *vm,
374 : _vlib_init_function_list_elt_t **headp,
375 : int call_once, int is_global)
376 : {
377 2795 : return call_init_exit_functions_internal (vm, headp, call_once,
378 : 1 /* do_sort */, is_global);
379 : }
380 :
381 : clib_error_t *
382 52 : vlib_call_init_exit_functions_no_sort (vlib_main_t *vm,
383 : _vlib_init_function_list_elt_t **headp,
384 : int call_once, int is_global)
385 : {
386 52 : return call_init_exit_functions_internal (vm, headp, call_once,
387 : 0 /* do_sort */, is_global);
388 : }
389 :
390 : clib_error_t *
391 559 : vlib_call_all_init_functions (vlib_main_t * vm)
392 : {
393 559 : vlib_global_main_t *vgm = vlib_get_global_main ();
394 : /* Call placeholder functions to make sure purely static modules are
395 : linked in. */
396 : #define _(f) vlib_##f##_reference ();
397 559 : foreach_vlib_module_reference;
398 : #undef _
399 :
400 559 : return vlib_call_init_exit_functions (vm, &vgm->init_function_registrations,
401 : 1 /* call_once */, 1 /* is_global */);
402 : }
403 :
404 : clib_error_t *
405 559 : vlib_call_all_main_loop_enter_functions (vlib_main_t * vm)
406 : {
407 559 : vlib_global_main_t *vgm = vlib_get_global_main ();
408 559 : return vlib_call_init_exit_functions (
409 : vm, &vgm->main_loop_enter_function_registrations, 1 /* call_once */,
410 : 1 /* is_global */);
411 : }
412 :
413 : clib_error_t *
414 559 : vlib_call_all_main_loop_exit_functions (vlib_main_t * vm)
415 : {
416 559 : vlib_global_main_t *vgm = vlib_get_global_main ();
417 559 : return vlib_call_init_exit_functions (
418 : vm, &vgm->main_loop_exit_function_registrations, 1 /* call_once */,
419 : 1 /* is_global */);
420 : }
421 :
422 : clib_error_t *
423 1118 : vlib_call_all_config_functions (vlib_main_t * vm,
424 : unformat_input_t * input, int is_early)
425 : {
426 1118 : vlib_global_main_t *vgm = vlib_get_global_main ();
427 1118 : clib_error_t *error = 0;
428 : vlib_config_function_runtime_t *c, **all;
429 1118 : uword *hash = 0, *p;
430 : uword i;
431 :
432 1118 : hash = hash_create_string (0, sizeof (uword));
433 1118 : all = 0;
434 :
435 1118 : c = vgm->config_function_registrations;
436 :
437 48128 : while (c)
438 : {
439 94020 : hash_set_mem (hash, c->name, vec_len (all));
440 47010 : vec_add1 (all, c);
441 47010 : unformat_init (&c->input, 0, 0);
442 47010 : c = c->next_registration;
443 : }
444 :
445 12472 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
446 : {
447 : u8 *s, *v;
448 :
449 22708 : if (!unformat (input, "%s %v", &s, &v) || !(p = hash_get_mem (hash, s)))
450 : {
451 0 : error = clib_error_create ("unknown input `%s %v'", s, v);
452 0 : goto done;
453 : }
454 :
455 11354 : c = all[p[0]];
456 11354 : if (vec_len (c->input.buffer) > 0)
457 0 : vec_add1 (c->input.buffer, ' ');
458 11354 : vec_add (c->input.buffer, v, vec_len (v));
459 11354 : vec_free (v);
460 11354 : vec_free (s);
461 : }
462 :
463 48128 : for (i = 0; i < vec_len (all); i++)
464 : {
465 47010 : c = all[i];
466 :
467 : /* Is this an early config? Are we doing early configs? */
468 47010 : if (is_early ^ c->is_early)
469 23505 : continue;
470 :
471 : /* Already called? */
472 23505 : if (hash_get (vgm->init_functions_called, c->function))
473 0 : continue;
474 23505 : hash_set1 (vgm->init_functions_called, c->function);
475 :
476 23505 : error = c->function (vm, &c->input);
477 23505 : if (error)
478 0 : goto done;
479 : }
480 :
481 1118 : done:
482 48128 : for (i = 0; i < vec_len (all); i++)
483 : {
484 47010 : c = all[i];
485 47010 : unformat_free (&c->input);
486 : }
487 1118 : vec_free (all);
488 1118 : hash_free (hash);
489 1118 : return error;
490 : }
491 :
492 : void
493 0 : vlib_init_dump (void)
494 : {
495 0 : vlib_global_main_t *vgm = vlib_get_global_main ();
496 0 : int i = 0;
497 :
498 : _vlib_init_function_list_elt_t *head, *this;
499 0 : head = vgm->init_function_registrations;
500 :
501 0 : this = head;
502 0 : while (this)
503 : {
504 0 : fformat (stdout, "[%d]: %s\n", i++, this->name);
505 0 : this = this->next_init_function;
506 : }
507 0 : }
508 :
509 : static clib_error_t *
510 0 : show_init_function_command_fn (vlib_main_t * vm,
511 : unformat_input_t * input,
512 : vlib_cli_command_t * cmd)
513 : {
514 0 : vlib_global_main_t *vgm = vlib_get_global_main ();
515 0 : int which = 1;
516 0 : int verbose = 0;
517 : int i, n_init_fns;
518 : _vlib_init_function_list_elt_t *head, *this;
519 : uword *index_by_name;
520 : uword *reg_by_index;
521 0 : u8 **init_f_names = 0;
522 : u8 *init_f_name;
523 : uword *p;
524 0 : _vlib_init_function_list_elt_t *this_reg = 0;
525 : hash_pair_t *hp;
526 0 : u8 **keys_to_delete = 0;
527 :
528 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
529 : {
530 0 : if (unformat (input, "init"))
531 0 : which = 1;
532 0 : else if (unformat (input, "enter"))
533 0 : which = 2;
534 0 : else if (unformat (input, "exit"))
535 0 : which = 3;
536 0 : else if (unformat (input, "verbose %d", &verbose))
537 : ;
538 0 : else if (unformat (input, "verbose"))
539 0 : verbose = 1;
540 : else
541 0 : break;
542 : }
543 :
544 0 : switch (which)
545 : {
546 0 : case 1:
547 0 : head = vgm->init_function_registrations;
548 0 : break;
549 0 : case 2:
550 0 : head = vgm->main_loop_enter_function_registrations;
551 0 : break;
552 0 : case 3:
553 0 : head = vgm->main_loop_exit_function_registrations;
554 0 : break;
555 0 : default:
556 0 : return clib_error_return (0, "BUG");
557 : }
558 :
559 0 : if (verbose == 0)
560 : {
561 0 : this = head;
562 0 : i = 0;
563 0 : while (this)
564 : {
565 0 : vlib_cli_output (vm, "[%d]: %s", i++, this->name);
566 0 : this = this->next_init_function;
567 : }
568 0 : return 0;
569 : }
570 :
571 0 : index_by_name = hash_create_string (0, sizeof (uword));
572 0 : reg_by_index = hash_create (0, sizeof (uword));
573 :
574 0 : this_reg = head;
575 0 : n_init_fns = 0;
576 : /* collect init fcn names */
577 0 : while (this_reg)
578 : {
579 0 : init_f_name = format (0, "%s%c", this_reg->name, 0);
580 0 : hash_set (reg_by_index, vec_len (init_f_names), (uword) this_reg);
581 :
582 0 : hash_set_mem (index_by_name, init_f_name, vec_len (init_f_names));
583 0 : vec_add1 (init_f_names, init_f_name);
584 0 : n_init_fns++;
585 0 : this_reg = this_reg->next_init_function;
586 : }
587 :
588 0 : for (i = 0; i < n_init_fns; i++)
589 : {
590 0 : p = hash_get (reg_by_index, i);
591 0 : ASSERT (p != 0);
592 0 : this_reg = (_vlib_init_function_list_elt_t *) p[0];
593 0 : vlib_cli_output (vm, "[%d] %s", i, this_reg->name);
594 : {
595 : char **runs_before, **runs_after, **init_order;
596 0 : runs_before = this_reg->runs_before;
597 0 : while (runs_before && runs_before[0])
598 : {
599 : _vlib_init_function_list_elt_t *successor;
600 : uword successor_index;
601 0 : p = hash_get_mem (index_by_name, runs_before[0]);
602 0 : if (p == 0)
603 : {
604 0 : clib_warning ("couldn't find successor '%s'", runs_before[0]);
605 0 : runs_before++;
606 0 : continue;
607 : }
608 0 : successor_index = p[0];
609 0 : p = hash_get (reg_by_index, p[0]);
610 0 : ASSERT (p != 0);
611 0 : successor = (_vlib_init_function_list_elt_t *) p[0];
612 0 : vlib_cli_output (vm, " before '%s' [%lld]",
613 : successor->name, successor_index);
614 0 : runs_before++;
615 : }
616 0 : runs_after = this_reg->runs_after;
617 0 : while (runs_after && runs_after[0])
618 : {
619 : _vlib_init_function_list_elt_t *predecessor;
620 : uword predecessor_index;
621 0 : p = hash_get_mem (index_by_name, runs_after[0]);
622 0 : if (p == 0)
623 : {
624 0 : clib_warning ("couldn't find predecessor '%s'",
625 : runs_after[0]);
626 0 : runs_after++;
627 0 : continue;
628 : }
629 0 : predecessor_index = p[0];
630 0 : p = hash_get (reg_by_index, p[0]);
631 0 : ASSERT (p != 0);
632 0 : predecessor = (_vlib_init_function_list_elt_t *) p[0];
633 0 : vlib_cli_output (vm, " after '%s' [%lld]",
634 : predecessor->name, predecessor_index);
635 0 : runs_after++;
636 : }
637 0 : init_order = this_reg->init_order;
638 0 : while (init_order && init_order[0])
639 : {
640 : _vlib_init_function_list_elt_t *inorder;
641 : uword inorder_index;
642 0 : p = hash_get_mem (index_by_name, init_order[0]);
643 0 : if (p == 0)
644 : {
645 0 : clib_warning ("couldn't find order element'%s'",
646 : init_order[0]);
647 0 : init_order++;
648 0 : continue;
649 : }
650 0 : inorder_index = p[0];
651 0 : p = hash_get (reg_by_index, p[0]);
652 0 : ASSERT (p != 0);
653 0 : inorder = (_vlib_init_function_list_elt_t *) p[0];
654 0 : vlib_cli_output (vm, " in order '%s' [%lld]",
655 : inorder->name, inorder_index);
656 0 : init_order++;
657 : }
658 : }
659 : }
660 : /* *INDENT-OFF* */
661 0 : hash_foreach_pair (hp, index_by_name,
662 : ({
663 : vec_add1 (keys_to_delete, (u8 *)hp->key);
664 : }));
665 : /* *INDENT-ON* */
666 0 : hash_free (index_by_name);
667 0 : for (i = 0; i < vec_len (keys_to_delete); i++)
668 0 : vec_free (keys_to_delete[i]);
669 0 : vec_free (keys_to_delete);
670 0 : hash_free (reg_by_index);
671 :
672 0 : return 0;
673 : }
674 :
675 : /*?
676 : * Show init function order
677 : *
678 : * @cliexpar
679 : * @cliexstart{show init-function [init | enter | exit] [verbose [nn]]}
680 : * @cliexend
681 : ?*/
682 : /* *INDENT-OFF* */
683 272887 : VLIB_CLI_COMMAND (show_init_function, static) = {
684 : .path = "show init-function",
685 : .short_help = "show init-function [init | enter | exit][verbose [nn]]",
686 : .function = show_init_function_command_fn,
687 : };
688 : /* *INDENT-ON* */
689 :
690 :
691 : /*
692 : * fd.io coding-style-patch-verification: ON
693 : *
694 : * Local Variables:
695 : * eval: (c-set-style "gnu")
696 : * End:
697 : */
|