| /* |
| * Copyright (c) 2019 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. |
| */ |
| |
| #include <vnet/fib/fib_entry_track.h> |
| #include <vnet/fib/fib_table.h> |
| #include <vnet/fib/fib_entry_delegate.h> |
| #include <vnet/fib/fib_walk.h> |
| |
| static fib_entry_delegate_t * |
| fib_entry_track_delegate_add (u32 fib_index, |
| const fib_prefix_t *prefix) |
| { |
| fib_entry_delegate_t *fed; |
| fib_node_index_t fei; |
| |
| fei = fib_table_entry_special_add(fib_index, |
| prefix, |
| FIB_SOURCE_RR, |
| FIB_ENTRY_FLAG_NONE); |
| |
| fed = fib_entry_delegate_find_or_add(fib_entry_get(fei), |
| FIB_ENTRY_DELEGATE_TRACK); |
| |
| fib_node_init(&fed->fd_track.fedt_node, |
| FIB_NODE_TYPE_ENTRY_TRACK); |
| |
| fed->fd_entry_index = fei; |
| fed->fd_track.fedt_sibling = |
| fib_entry_child_add(fei, |
| FIB_NODE_TYPE_ENTRY_TRACK, |
| fib_entry_delegate_get_index(fed)); |
| |
| return (fed); |
| } |
| |
| fib_node_index_t |
| fib_entry_track (u32 fib_index, |
| const fib_prefix_t *prefix, |
| fib_node_type_t child_type, |
| index_t child_index, |
| u32 *sibling) |
| { |
| fib_entry_delegate_t *fed; |
| fib_node_index_t fei; |
| |
| fei = fib_table_lookup_exact_match(fib_index, prefix); |
| |
| if (INDEX_INVALID == fei || |
| NULL == (fed = fib_entry_delegate_find(fib_entry_get(fei), |
| FIB_ENTRY_DELEGATE_TRACK))) |
| { |
| fed = fib_entry_track_delegate_add(fib_index, prefix); |
| } |
| |
| /* |
| * add this child to the entry's delegate |
| */ |
| *sibling = fib_node_child_add(FIB_NODE_TYPE_ENTRY_TRACK, |
| fib_entry_delegate_get_index(fed), |
| child_type, |
| child_index); |
| |
| return (fed->fd_entry_index); |
| } |
| |
| void |
| fib_entry_untrack (fib_node_index_t fei, |
| u32 sibling) |
| { |
| fib_entry_delegate_t *fed; |
| |
| fed = fib_entry_delegate_find(fib_entry_get(fei), |
| FIB_ENTRY_DELEGATE_TRACK); |
| |
| if (NULL != fed) |
| { |
| fib_node_child_remove(FIB_NODE_TYPE_ENTRY_TRACK, |
| fib_entry_delegate_get_index(fed), |
| sibling); |
| /* if this is the last child the delegate will be removed. */ |
| } |
| /* else untracked */ |
| } |
| |
| static fib_node_t * |
| fib_entry_track_get_node (fib_node_index_t index) |
| { |
| fib_entry_delegate_t *fed; |
| |
| fed = fib_entry_delegate_get(index); |
| return (&fed->fd_track.fedt_node); |
| } |
| |
| static fib_entry_delegate_t* |
| fib_entry_delegate_from_fib_node (fib_node_t *node) |
| { |
| ASSERT(FIB_NODE_TYPE_ENTRY_TRACK == node->fn_type); |
| return ((fib_entry_delegate_t *) (((char *) node) - |
| STRUCT_OFFSET_OF (fib_entry_delegate_t, |
| fd_track.fedt_node))); |
| } |
| |
| static void |
| fib_entry_track_last_lock_gone (fib_node_t *node) |
| { |
| fib_entry_delegate_t *fed; |
| fib_node_index_t fei; |
| u32 sibling; |
| |
| fed = fib_entry_delegate_from_fib_node(node); |
| fei = fed->fd_entry_index; |
| sibling = fed->fd_track.fedt_sibling; |
| |
| /* |
| * the tracker has no more children so it can be removed, |
| * and the FIB entry unsourced. |
| * remove the delegate first, then unlock the fib entry, |
| * since the delegate may be holding the last lock |
| */ |
| fib_entry_delegate_remove(fib_entry_get(fei), |
| FIB_ENTRY_DELEGATE_TRACK); |
| /* having removed the deletegate the fed object is now toast */ |
| fib_entry_child_remove(fei, sibling); |
| |
| fib_table_entry_delete_index(fei, FIB_SOURCE_RR); |
| } |
| |
| static fib_node_back_walk_rc_t |
| fib_entry_track_back_walk_notify (fib_node_t *node, |
| fib_node_back_walk_ctx_t *ctx) |
| { |
| fib_entry_delegate_t *fed; |
| |
| fed = fib_entry_delegate_from_fib_node(node); |
| |
| /* |
| * propagate the walk to the delgate's children |
| */ |
| |
| fib_walk_sync(FIB_NODE_TYPE_ENTRY_TRACK, |
| fib_entry_delegate_get_index(fed), |
| ctx); |
| |
| return (FIB_NODE_BACK_WALK_CONTINUE); |
| } |
| |
| static void |
| fib_entry_track_show_memory (void) |
| { |
| } |
| |
| /* |
| * The FIB entry tracker's graph node virtual function table |
| */ |
| static const fib_node_vft_t fib_entry_track_vft = { |
| .fnv_get = fib_entry_track_get_node, |
| .fnv_last_lock = fib_entry_track_last_lock_gone, |
| .fnv_back_walk = fib_entry_track_back_walk_notify, |
| .fnv_mem_show = fib_entry_track_show_memory, |
| }; |
| |
| void |
| fib_entry_track_module_init (void) |
| { |
| fib_node_register_type(FIB_NODE_TYPE_ENTRY_TRACK, &fib_entry_track_vft); |
| } |