| /* |
| * Copyright (c) 2015 Cisco and/or its affiliates. |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at: |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| * init.c: mechanism for functions to be called at init/exit. |
| * |
| * Copyright (c) 2008 Eliot Dresselhaus |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sublicense, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #include <vlib/vlib.h> |
| #include <vppinfra/ptclosure.h> |
| |
| /** |
| * @file |
| * @brief Init function ordering and execution implementation |
| * Topological sort for all classes of init functions, and |
| * a relatively simple API routine to invoke them. |
| */ |
| |
| /*? %%clicmd:group_label Init functions %% ?*/ |
| |
| static int |
| comma_split (u8 * s, u8 ** a, u8 ** b) |
| { |
| *a = s; |
| |
| while (*s && *s != ',') |
| s++; |
| |
| if (*s == ',') |
| *s = 0; |
| else |
| return 1; |
| |
| *b = (u8 *) (s + 1); |
| return 0; |
| } |
| |
| /** |
| * @brief Topological sorter for init function chains. |
| * @param head [in/out] address of the listhead to be sorted |
| * @returns 0 on success, otherwise a clib_error_t *. |
| */ |
| |
| clib_error_t *vlib_sort_init_exit_functions |
| (_vlib_init_function_list_elt_t ** head) |
| { |
| uword *index_by_name; |
| uword *reg_by_index; |
| u8 **init_f_names = 0; |
| u8 *init_f_name; |
| char **these_constraints; |
| char *this_constraint_c; |
| u8 **constraints = 0; |
| u8 *constraint_tuple; |
| u8 *this_constraint; |
| char *prev_name; |
| u8 **orig, **closure; |
| uword *p; |
| int i, j, k; |
| u8 *a_name, *b_name; |
| int a_index, b_index; |
| int n_init_fns; |
| u32 *result = 0; |
| _vlib_init_function_list_elt_t *this_reg = 0; |
| hash_pair_t *hp; |
| u8 **keys_to_delete = 0; |
| |
| /* |
| * two hash tables: name to index in init_f_names, and |
| * init function registration pointer by index |
| */ |
| index_by_name = hash_create_string (0, sizeof (uword)); |
| reg_by_index = hash_create (0, sizeof (uword)); |
| |
| this_reg = *head; |
| |
| /* pass 1, collect init fcn names, construct a before b pairs */ |
| while (this_reg) |
| { |
| init_f_name = format (0, "%s%c", this_reg->name, 0); |
| hash_set (reg_by_index, vec_len (init_f_names), (uword) this_reg); |
| |
| hash_set_mem (index_by_name, init_f_name, vec_len (init_f_names)); |
| |
| vec_add1 (init_f_names, init_f_name); |
| |
| these_constraints = this_reg->runs_before; |
| while (these_constraints && these_constraints[0]) |
| { |
| this_constraint_c = these_constraints[0]; |
| |
| constraint_tuple = format (0, "%s,%s%c", init_f_name, |
| this_constraint_c, 0); |
| vec_add1 (constraints, constraint_tuple); |
| these_constraints++; |
| } |
| |
| these_constraints = this_reg->runs_after; |
| while (these_constraints && these_constraints[0]) |
| { |
| this_constraint_c = these_constraints[0]; |
| |
| constraint_tuple = format (0, "%s,%s%c", |
| this_constraint_c, init_f_name, 0); |
| vec_add1 (constraints, constraint_tuple); |
| these_constraints++; |
| } |
| |
| this_reg = this_reg->next_init_function; |
| } |
| |
| /* |
| * pass 2: collect "a then b then c then d" constraints. |
| * all init fcns must be known at this point. |
| */ |
| this_reg = *head; |
| while (this_reg) |
| { |
| these_constraints = this_reg->init_order; |
| |
| prev_name = 0; |
| /* Across the list of constraints */ |
| while (these_constraints && these_constraints[0]) |
| { |
| this_constraint_c = these_constraints[0]; |
| p = hash_get_mem (index_by_name, this_constraint_c); |
| if (p == 0) |
| { |
| clib_warning |
| ("order constraint fcn '%s' not found", this_constraint_c); |
| these_constraints++; |
| continue; |
| } |
| |
| if (prev_name == 0) |
| { |
| prev_name = this_constraint_c; |
| these_constraints++; |
| continue; |
| } |
| |
| constraint_tuple = format (0, "%s,%s%c", prev_name, |
| this_constraint_c, 0); |
| vec_add1 (constraints, constraint_tuple); |
| prev_name = this_constraint_c; |
| these_constraints++; |
| } |
| this_reg = this_reg->next_init_function; |
| } |
| |
| n_init_fns = vec_len (init_f_names); |
| orig = clib_ptclosure_alloc (n_init_fns); |
| |
| for (i = 0; i < vec_len (constraints); i++) |
| { |
| this_constraint = constraints[i]; |
| |
| if (comma_split (this_constraint, &a_name, &b_name)) |
| return clib_error_return (0, "comma_split failed!"); |
| |
| p = hash_get_mem (index_by_name, a_name); |
| /* |
| * Note: the next two errors mean that something is |
| * b0rked. As in: if you code "A runs before on B," and you type |
| * B incorrectly, you lose. Nonexistent init functions are tolerated. |
| */ |
| if (p == 0) |
| { |
| clib_warning ("init function '%s' not found (before '%s')", |
| a_name, b_name); |
| continue; |
| } |
| a_index = p[0]; |
| |
| p = hash_get_mem (index_by_name, b_name); |
| if (p == 0) |
| { |
| clib_warning ("init function '%s' not found (after '%s')", |
| b_name, a_name); |
| continue; |
| } |
| b_index = p[0]; |
| |
| /* add a before b to the original set of constraints */ |
| orig[a_index][b_index] = 1; |
| vec_free (this_constraint); |
| } |
| |
| /* Compute the positive transitive closure of the original constraints */ |
| closure = clib_ptclosure (orig); |
| |
| /* Compute a partial order across feature nodes, if one exists. */ |
| again: |
| for (i = 0; i < n_init_fns; i++) |
| { |
| for (j = 0; j < n_init_fns; j++) |
| { |
| if (closure[i][j]) |
| goto item_constrained; |
| } |
| /* Item i can be output */ |
| vec_add1 (result, i); |
| { |
| for (k = 0; k < n_init_fns; k++) |
| closure[k][i] = 0; |
| /* |
| * Add a "Magic" a before a constraint. |
| * This means we'll never output it again |
| */ |
| closure[i][i] = 1; |
| goto again; |
| } |
| item_constrained: |
| ; |
| } |
| |
| /* see if we got a partial order... */ |
| if (vec_len (result) != n_init_fns) |
| return clib_error_return |
| (0, "Failed to find a suitable init function order!"); |
| |
| /* |
| * We win. |
| * Bind the index variables, and output the feature node name vector |
| * using the partial order we just computed. Result is in stack |
| * order, because the entry with the fewest constraints (e.g. none) |
| * is output first, etc. |
| * Reset the listhead, and add items in result (aka reverse) order. |
| */ |
| *head = 0; |
| for (i = 0; i < n_init_fns; i++) |
| { |
| p = hash_get (reg_by_index, result[i]); |
| ASSERT (p != 0); |
| this_reg = (_vlib_init_function_list_elt_t *) p[0]; |
| |
| this_reg->next_init_function = *head; |
| *head = this_reg; |
| } |
| |
| /* Finally, clean up all the fine data we allocated */ |
| hash_foreach_pair (hp, index_by_name, |
| ({ |
| vec_add1 (keys_to_delete, (u8 *)hp->key); |
| })); |
| hash_free (index_by_name); |
| for (i = 0; i < vec_len (keys_to_delete); i++) |
| vec_free (keys_to_delete[i]); |
| vec_free (keys_to_delete); |
| hash_free (reg_by_index); |
| vec_free (result); |
| clib_ptclosure_free (orig); |
| clib_ptclosure_free (closure); |
| return 0; |
| } |
| |
| /** |
| * @brief call a set of init / exit / main-loop enter functions |
| * @param vm vlib_main_t |
| * @param head address of the listhead to sort and then invoke |
| * @returns 0 on success, clib_error_t * on error |
| * |
| * The "init_functions_called" hash supports a subtle mix of procedural |
| * and formally-specified ordering constraints. The following schemes |
| * are *roughly* equivalent: |
| * |
| * static clib_error_t *init_runs_first (vlib_main_t *vm) |
| * { |
| * clib_error_t *error; |
| * |
| * ... do some stuff... |
| * |
| * if ((error = vlib_call_init_function (init_runs_next))) |
| * return error; |
| * ... |
| * } |
| * VLIB_INIT_FUNCTION (init_runs_first); |
| * |
| * and |
| * |
| * static clib_error_t *init_runs_first (vlib_main_t *vm) |
| * { |
| * ... do some stuff... |
| * } |
| * VLIB_INIT_FUNCTION (init_runs_first) = |
| * { |
| * .runs_before = VLIB_INITS("init_runs_next"), |
| * }; |
| * |
| * The first form will [most likely] call "init_runs_next" on the |
| * spot. The second form means that "init_runs_first" runs before |
| * "init_runs_next," possibly much earlier in the sequence. |
| * |
| * Please DO NOT construct sets of init functions where A before B |
| * actually means A *right before* B. It's not necessary - simply combine |
| * A and B - and it leads to hugely annoying debugging exercises. |
| */ |
| |
| static inline clib_error_t * |
| call_init_exit_functions_internal (vlib_main_t *vm, |
| _vlib_init_function_list_elt_t **headp, |
| int call_once, int do_sort, int is_global) |
| { |
| vlib_global_main_t *vgm = vlib_get_global_main (); |
| clib_error_t *error = 0; |
| _vlib_init_function_list_elt_t *i; |
| |
| if (do_sort && (error = vlib_sort_init_exit_functions (headp))) |
| return (error); |
| |
| i = *headp; |
| while (i) |
| { |
| uword *h; |
| |
| if (is_global) |
| h = hash_get (vgm->init_functions_called, i->f); |
| else |
| h = hash_get (vm->worker_init_functions_called, i->f); |
| |
| if (call_once && !h) |
| { |
| if (call_once) |
| { |
| if (is_global) |
| hash_set1 (vgm->init_functions_called, i->f); |
| else |
| hash_set1 (vm->worker_init_functions_called, i->f); |
| } |
| error = i->f (vm); |
| if (error) |
| return error; |
| } |
| i = i->next_init_function; |
| } |
| return error; |
| } |
| |
| clib_error_t * |
| vlib_call_init_exit_functions (vlib_main_t *vm, |
| _vlib_init_function_list_elt_t **headp, |
| int call_once, int is_global) |
| { |
| return call_init_exit_functions_internal (vm, headp, call_once, |
| 1 /* do_sort */, is_global); |
| } |
| |
| clib_error_t * |
| vlib_call_init_exit_functions_no_sort (vlib_main_t *vm, |
| _vlib_init_function_list_elt_t **headp, |
| int call_once, int is_global) |
| { |
| return call_init_exit_functions_internal (vm, headp, call_once, |
| 0 /* do_sort */, is_global); |
| } |
| |
| clib_error_t * |
| vlib_call_all_init_functions (vlib_main_t * vm) |
| { |
| vlib_global_main_t *vgm = vlib_get_global_main (); |
| /* Call placeholder functions to make sure purely static modules are |
| linked in. */ |
| #define _(f) vlib_##f##_reference (); |
| foreach_vlib_module_reference; |
| #undef _ |
| |
| return vlib_call_init_exit_functions (vm, &vgm->init_function_registrations, |
| 1 /* call_once */, 1 /* is_global */); |
| } |
| |
| clib_error_t * |
| vlib_call_all_main_loop_enter_functions (vlib_main_t * vm) |
| { |
| vlib_global_main_t *vgm = vlib_get_global_main (); |
| return vlib_call_init_exit_functions ( |
| vm, &vgm->main_loop_enter_function_registrations, 1 /* call_once */, |
| 1 /* is_global */); |
| } |
| |
| clib_error_t * |
| vlib_call_all_main_loop_exit_functions (vlib_main_t * vm) |
| { |
| vlib_global_main_t *vgm = vlib_get_global_main (); |
| return vlib_call_init_exit_functions ( |
| vm, &vgm->main_loop_exit_function_registrations, 1 /* call_once */, |
| 1 /* is_global */); |
| } |
| |
| clib_error_t * |
| vlib_call_all_config_functions (vlib_main_t * vm, |
| unformat_input_t * input, int is_early) |
| { |
| vlib_global_main_t *vgm = vlib_get_global_main (); |
| clib_error_t *error = 0; |
| vlib_config_function_runtime_t *c, **all; |
| uword *hash = 0, *p; |
| uword i; |
| |
| hash = hash_create_string (0, sizeof (uword)); |
| all = 0; |
| |
| c = vgm->config_function_registrations; |
| |
| while (c) |
| { |
| hash_set_mem (hash, c->name, vec_len (all)); |
| vec_add1 (all, c); |
| unformat_init (&c->input, 0, 0); |
| c = c->next_registration; |
| } |
| |
| while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) |
| { |
| u8 *s, *v; |
| |
| if (!unformat (input, "%s %v", &s, &v) || !(p = hash_get_mem (hash, s))) |
| { |
| error = clib_error_create ("unknown input `%s %v'", s, v); |
| goto done; |
| } |
| |
| c = all[p[0]]; |
| if (vec_len (c->input.buffer) > 0) |
| vec_add1 (c->input.buffer, ' '); |
| vec_add (c->input.buffer, v, vec_len (v)); |
| vec_free (v); |
| vec_free (s); |
| } |
| |
| for (i = 0; i < vec_len (all); i++) |
| { |
| c = all[i]; |
| |
| /* Is this an early config? Are we doing early configs? */ |
| if (is_early ^ c->is_early) |
| continue; |
| |
| /* Already called? */ |
| if (hash_get (vgm->init_functions_called, c->function)) |
| continue; |
| hash_set1 (vgm->init_functions_called, c->function); |
| |
| error = c->function (vm, &c->input); |
| if (error) |
| goto done; |
| } |
| |
| done: |
| for (i = 0; i < vec_len (all); i++) |
| { |
| c = all[i]; |
| unformat_free (&c->input); |
| } |
| vec_free (all); |
| hash_free (hash); |
| return error; |
| } |
| |
| void |
| vlib_init_dump (void) |
| { |
| vlib_global_main_t *vgm = vlib_get_global_main (); |
| int i = 0; |
| |
| _vlib_init_function_list_elt_t *head, *this; |
| head = vgm->init_function_registrations; |
| |
| this = head; |
| while (this) |
| { |
| fformat (stdout, "[%d]: %s\n", i++, this->name); |
| this = this->next_init_function; |
| } |
| } |
| |
| static clib_error_t * |
| show_init_function_command_fn (vlib_main_t * vm, |
| unformat_input_t * input, |
| vlib_cli_command_t * cmd) |
| { |
| vlib_global_main_t *vgm = vlib_get_global_main (); |
| int which = 1; |
| int verbose = 0; |
| int i, n_init_fns; |
| _vlib_init_function_list_elt_t *head, *this; |
| uword *index_by_name; |
| uword *reg_by_index; |
| u8 **init_f_names = 0; |
| u8 *init_f_name; |
| uword *p; |
| _vlib_init_function_list_elt_t *this_reg = 0; |
| hash_pair_t *hp; |
| u8 **keys_to_delete = 0; |
| |
| while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) |
| { |
| if (unformat (input, "init")) |
| which = 1; |
| else if (unformat (input, "enter")) |
| which = 2; |
| else if (unformat (input, "exit")) |
| which = 3; |
| else if (unformat (input, "verbose %d", &verbose)) |
| ; |
| else if (unformat (input, "verbose")) |
| verbose = 1; |
| else |
| break; |
| } |
| |
| switch (which) |
| { |
| case 1: |
| head = vgm->init_function_registrations; |
| break; |
| case 2: |
| head = vgm->main_loop_enter_function_registrations; |
| break; |
| case 3: |
| head = vgm->main_loop_exit_function_registrations; |
| break; |
| default: |
| return clib_error_return (0, "BUG"); |
| } |
| |
| if (verbose == 0) |
| { |
| this = head; |
| i = 0; |
| while (this) |
| { |
| vlib_cli_output (vm, "[%d]: %s", i++, this->name); |
| this = this->next_init_function; |
| } |
| return 0; |
| } |
| |
| index_by_name = hash_create_string (0, sizeof (uword)); |
| reg_by_index = hash_create (0, sizeof (uword)); |
| |
| this_reg = head; |
| n_init_fns = 0; |
| /* collect init fcn names */ |
| while (this_reg) |
| { |
| init_f_name = format (0, "%s%c", this_reg->name, 0); |
| hash_set (reg_by_index, vec_len (init_f_names), (uword) this_reg); |
| |
| hash_set_mem (index_by_name, init_f_name, vec_len (init_f_names)); |
| vec_add1 (init_f_names, init_f_name); |
| n_init_fns++; |
| this_reg = this_reg->next_init_function; |
| } |
| |
| for (i = 0; i < n_init_fns; i++) |
| { |
| p = hash_get (reg_by_index, i); |
| ASSERT (p != 0); |
| this_reg = (_vlib_init_function_list_elt_t *) p[0]; |
| vlib_cli_output (vm, "[%d] %s", i, this_reg->name); |
| { |
| char **runs_before, **runs_after, **init_order; |
| runs_before = this_reg->runs_before; |
| while (runs_before && runs_before[0]) |
| { |
| _vlib_init_function_list_elt_t *successor; |
| uword successor_index; |
| p = hash_get_mem (index_by_name, runs_before[0]); |
| if (p == 0) |
| { |
| clib_warning ("couldn't find successor '%s'", runs_before[0]); |
| runs_before++; |
| continue; |
| } |
| successor_index = p[0]; |
| p = hash_get (reg_by_index, p[0]); |
| ASSERT (p != 0); |
| successor = (_vlib_init_function_list_elt_t *) p[0]; |
| vlib_cli_output (vm, " before '%s' [%lld]", |
| successor->name, successor_index); |
| runs_before++; |
| } |
| runs_after = this_reg->runs_after; |
| while (runs_after && runs_after[0]) |
| { |
| _vlib_init_function_list_elt_t *predecessor; |
| uword predecessor_index; |
| p = hash_get_mem (index_by_name, runs_after[0]); |
| if (p == 0) |
| { |
| clib_warning ("couldn't find predecessor '%s'", |
| runs_after[0]); |
| runs_after++; |
| continue; |
| } |
| predecessor_index = p[0]; |
| p = hash_get (reg_by_index, p[0]); |
| ASSERT (p != 0); |
| predecessor = (_vlib_init_function_list_elt_t *) p[0]; |
| vlib_cli_output (vm, " after '%s' [%lld]", |
| predecessor->name, predecessor_index); |
| runs_after++; |
| } |
| init_order = this_reg->init_order; |
| while (init_order && init_order[0]) |
| { |
| _vlib_init_function_list_elt_t *inorder; |
| uword inorder_index; |
| p = hash_get_mem (index_by_name, init_order[0]); |
| if (p == 0) |
| { |
| clib_warning ("couldn't find order element'%s'", |
| init_order[0]); |
| init_order++; |
| continue; |
| } |
| inorder_index = p[0]; |
| p = hash_get (reg_by_index, p[0]); |
| ASSERT (p != 0); |
| inorder = (_vlib_init_function_list_elt_t *) p[0]; |
| vlib_cli_output (vm, " in order '%s' [%lld]", |
| inorder->name, inorder_index); |
| init_order++; |
| } |
| } |
| } |
| hash_foreach_pair (hp, index_by_name, |
| ({ |
| vec_add1 (keys_to_delete, (u8 *)hp->key); |
| })); |
| hash_free (index_by_name); |
| for (i = 0; i < vec_len (keys_to_delete); i++) |
| vec_free (keys_to_delete[i]); |
| vec_free (keys_to_delete); |
| hash_free (reg_by_index); |
| |
| return 0; |
| } |
| |
| /*? |
| * Show init function order |
| * |
| * @cliexpar |
| * @cliexstart{show init-function [init | enter | exit] [verbose [nn]]} |
| * @cliexend |
| ?*/ |
| VLIB_CLI_COMMAND (show_init_function, static) = { |
| .path = "show init-function", |
| .short_help = "show init-function [init | enter | exit][verbose [nn]]", |
| .function = show_init_function_command_fn, |
| }; |
| |
| |
| /* |
| * fd.io coding-style-patch-verification: ON |
| * |
| * Local Variables: |
| * eval: (c-set-style "gnu") |
| * End: |
| */ |