From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id 9CF585B30 for ; Thu, 15 Mar 2018 15:27:13 +0100 (CET) X-Amp-Result: UNSCANNABLE X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 15 Mar 2018 07:27:12 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.48,311,1517904000"; d="scan'208";a="24941907" Received: from bricha3-mobl.ger.corp.intel.com ([10.237.221.118]) by orsmga007.jf.intel.com with SMTP; 15 Mar 2018 07:27:10 -0700 Received: by (sSMTP sendmail emulation); Thu, 15 Mar 2018 14:27:09 +0000 Date: Thu, 15 Mar 2018 14:27:09 +0000 From: Bruce Richardson To: Medvedkin Vladimir Cc: dev@dpdk.org, thomas@monjalon.net Message-ID: <20180315142702.GA5776@bricha3-MOBL.ger.corp.intel.com> References: <1519339856-683-1-git-send-email-medvedkinv@gmail.com> <1519339856-683-2-git-send-email-medvedkinv@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <1519339856-683-2-git-send-email-medvedkinv@gmail.com> Organization: Intel Research and Development Ireland Ltd. User-Agent: Mutt/1.9.4 (2018-02-28) Subject: Re: [dpdk-dev] [PATCH v3 1/2] Add RIB library X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 15 Mar 2018 14:27:14 -0000 On Thu, Feb 22, 2018 at 10:50:55PM +0000, Medvedkin Vladimir wrote: > RIB is an alternative to current LPM library. > It solves the following problems > - Increases the speed of control plane operations against lpm such as > adding/deleting routes > - Adds abstraction from dataplane algorithms, so it is possible to add > different ip route lookup algorythms such as DXR/poptrie/lpc-trie/etc > in addition to current dir24_8 > - It is possible to keep user defined application specific additional > information in struct rte_rib_node which represents route entry. > It can be next hop/set of next hops (i.e. active and feasible), > pointers to link rte_rib_node based on some criteria (i.e. next_hop), > plenty of additional control plane information. > - For dir24_8 implementation it is possible to remove > rte_lpm_tbl_entry.depth field that helps to save 6 bits. > - Also new dir24_8 implementation supports different next_hop sizes > (1/2/4/8 bytes per next hop) > - Removed RTE_LPM_LOOKUP_SUCCESS to save 1 bit and to eleminate > ternary operator. > Instead it returns special default value if there is no route. > > Signed-off-by: Medvedkin Vladimir More comments inline below. Mostly for rte_rib.c file. /Bruce > --- > config/common_base | 6 + > doc/api/doxy-api.conf | 1 + > lib/Makefile | 2 + > lib/librte_rib/Makefile | 23 ++ Don't forget meson.build file too, to build with meson and ninja. [Strongly recommend it for your day-to-day development work too, incremental builds are much, much faster using ninja]. > lib/librte_rib/rte_dir24_8.c | 482 +++++++++++++++++++++++++++++++++ > lib/librte_rib/rte_dir24_8.h | 115 ++++++++ > lib/librte_rib/rte_rib.c | 526 +++++++++++++++++++++++++++++++++++++ > lib/librte_rib/rte_rib.h | 322 +++++++++++++++++++++++ > lib/librte_rib/rte_rib_version.map | 18 ++ > mk/rte.app.mk | 1 + > 10 files changed, 1496 insertions(+) > create mode 100644 lib/librte_rib/Makefile > create mode 100644 lib/librte_rib/rte_dir24_8.c > create mode 100644 lib/librte_rib/rte_dir24_8.h > create mode 100644 lib/librte_rib/rte_rib.c > create mode 100644 lib/librte_rib/rte_rib.h > create mode 100644 lib/librte_rib/rte_rib_version.map > > diff --git a/config/common_base b/config/common_base > index ad03cf4..aceeff5 100644 > --- a/config/common_base > +++ b/config/common_base > @@ -679,6 +679,12 @@ CONFIG_RTE_LIBRTE_LPM=y > CONFIG_RTE_LIBRTE_LPM_DEBUG=n > > # > +# Compile librte_rib > +# > +CONFIG_RTE_LIBRTE_RIB=y > +CONFIG_RTE_LIBRTE_RIB_DEBUG=n > + > +# > # Compile librte_acl > # > CONFIG_RTE_LIBRTE_ACL=y > diff --git a/doc/api/doxy-api.conf b/doc/api/doxy-api.conf > index cda52fd..8e4f969 100644 > --- a/doc/api/doxy-api.conf > +++ b/doc/api/doxy-api.conf > @@ -60,6 +60,7 @@ INPUT = doc/api/doxy-api-index.md \ > lib/librte_kvargs \ > lib/librte_latencystats \ > lib/librte_lpm \ > + lib/librte_rib \ > lib/librte_mbuf \ > lib/librte_member \ > lib/librte_mempool \ > diff --git a/lib/Makefile b/lib/Makefile > index ec965a6..e4faf10 100644 > --- a/lib/Makefile > +++ b/lib/Makefile > @@ -43,6 +43,8 @@ DIRS-$(CONFIG_RTE_LIBRTE_EFD) += librte_efd > DEPDIRS-librte_efd := librte_eal librte_ring librte_hash > DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm > DEPDIRS-librte_lpm := librte_eal > +DIRS-$(CONFIG_RTE_LIBRTE_RIB) += librte_rib > +DEPDIRS-librte_rib := librte_eal librte_mempool > DIRS-$(CONFIG_RTE_LIBRTE_ACL) += librte_acl > DEPDIRS-librte_acl := librte_eal > DIRS-$(CONFIG_RTE_LIBRTE_MEMBER) += librte_member > diff --git a/lib/librte_rib/Makefile b/lib/librte_rib/Makefile > new file mode 100644 > index 0000000..f6431b6 > --- /dev/null > +++ b/lib/librte_rib/Makefile > @@ -0,0 +1,23 @@ > +# SPDX-License-Identifier: BSD-3-Clause > +# Copyright(c) 2018 Vladimir Medvedkin > + > +include $(RTE_SDK)/mk/rte.vars.mk > + > +# library name > +LIB = librte_rib.a > + > +CFLAGS += -O3 > +CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) > +LDLIBS += -lrte_eal -lrte_mempool > + > +EXPORT_MAP := rte_rib_version.map > + > +LIBABIVER := 1 > + > +# all source are stored in SRCS-y > +SRCS-$(CONFIG_RTE_LIBRTE_RIB) := rte_rib.c rte_dir24_8.c > + > +# install this header file > +SYMLINK-$(CONFIG_RTE_LIBRTE_RIB)-include := rte_rib.h rte_dir24_8.h > + > +include $(RTE_SDK)/mk/rte.lib.mk > diff --git a/lib/librte_rib/rte_dir24_8.c b/lib/librte_rib/rte_dir24_8.c > new file mode 100644 > index 0000000..2fc55fe > --- /dev/null > +++ b/lib/librte_rib/rte_dir24_8.c For future patches, it would be good if you can split the dir24_8 code into a separate patch from the main rib code. The more you can split it into separate patch blocks, the easier it is to review. > @@ -0,0 +1,482 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright(c) 2018 Vladimir Medvedkin > + */ > diff --git a/lib/librte_rib/rte_rib.c b/lib/librte_rib/rte_rib.c > new file mode 100644 > index 0000000..7783b23 > --- /dev/null > +++ b/lib/librte_rib/rte_rib.c > @@ -0,0 +1,526 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright(c) 2018 Vladimir Medvedkin > + */ > + > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +TAILQ_HEAD(rte_rib_list, rte_tailq_entry); > +static struct rte_tailq_elem rte_rib_tailq = { > + .name = "RTE_RIB", > +}; > +EAL_REGISTER_TAILQ(rte_rib_tailq) > + > +static struct rte_rib_node * > +new_node_malloc(struct rte_rib *rib) > +{ > + struct rte_rib_node *ent; > + > + ent = malloc(rib->node_sz); > + if (unlikely(ent == NULL)) > + return NULL; > + ++rib->cur_nodes; > + return ent; > +} > + > +static void > +free_node_malloc(__rte_unused struct rte_rib *rib, struct rte_rib_node *ent) > +{ > + --rib->cur_nodes; > + free(ent); > +} > + > +static struct rte_rib_node * > +new_node_mempool(struct rte_rib *rib) > +{ > + struct rte_rib_node *ent; > + int ret; > + > + ret = rte_mempool_get(rib->node_pool, (void *)&ent); > + if (unlikely(ret != 0)) > + return NULL; > + ++rib->cur_nodes; > + return ent; > +} > + > +static void > +free_node_mempool(struct rte_rib *rib, struct rte_rib_node *ent) > +{ > + --rib->cur_nodes; > + rte_mempool_put(rib->node_pool, ent); > +} > + > +struct rte_rib_node * > +rte_rib_tree_lookup(struct rte_rib *rib, uint32_t key) > +{ > + struct rte_rib_node *cur = rib->trie; > + struct rte_rib_node *prev = NULL; > + > + while ((cur != NULL) && (((cur->key ^ key) & > + (uint32_t)(UINT64_MAX << (32 - cur->depth))) == 0)) { > + if ((cur->flag & RTE_RIB_VALID_NODE) == RTE_RIB_VALID_NODE) > + prev = cur; > + cur = RTE_RIB_GET_NXT_NODE(cur, key); > + } > + return prev; > +} > + > +struct rte_rib_node * > +rte_rib_tree_lookup_parent(struct rte_rib_node *ent) > +{ > + struct rte_rib_node *tmp; > + > + if (ent == NULL) > + return NULL; > + tmp = ent->parent; > + while ((tmp != NULL) && > + (tmp->flag & RTE_RIB_VALID_NODE) != RTE_RIB_VALID_NODE) { > + tmp = tmp->parent; > +} Watch indentation here. The close brace obviously needs an indent, and the line continuation of while should have different indent to body. Coding standards suggest double indent on the continuation, ie. two tabs before "(tmp->flag". > + return tmp; > +} > + > +struct rte_rib_node * > +rte_rib_tree_lookup_exact(struct rte_rib *rib, uint32_t key, uint8_t depth) > +{ > + struct rte_rib_node *cur = rib->trie; > + > + key &= (uint32_t)(UINT64_MAX << (32 - depth)); This mask generation seems a common idiom. Maybe make an inline fn or macro out of it. > + while (cur != NULL) { > + if ((cur->key == key) && (cur->depth == depth) && > + (cur->flag & RTE_RIB_VALID_NODE)) > + return cur; > + if ((cur->depth > depth) || > + (((uint64_t)key >> (32 - cur->depth)) != > + ((uint64_t)cur->key >> (32 - cur->depth)))) > + break; > + cur = RTE_RIB_GET_NXT_NODE(cur, key); > + } > + return NULL; > +} > + > +struct rte_rib_node * > +rte_rib_tree_get_nxt(struct rte_rib *rib, uint32_t key, > + uint8_t depth, struct rte_rib_node *cur, int flag) > +{ > + struct rte_rib_node *tmp, *prev = NULL; > + > + if (cur == NULL) { > + tmp = rib->trie; > + while ((tmp) && (tmp->depth < depth)) > + tmp = RTE_RIB_GET_NXT_NODE(tmp, key); > + } else { > + tmp = cur; > + while ((tmp->parent != NULL) && (RTE_RIB_IS_RIGHT_NODE(tmp) || > + (tmp->parent->right == NULL))) { > + tmp = tmp->parent; > + if ((tmp->flag & RTE_RIB_VALID_NODE) && > + (RTE_RIB_IS_COVERED(tmp->key, tmp->depth, > + key, depth))) > + return tmp; > + } > + tmp = (tmp->parent) ? tmp->parent->right : NULL; > + } > + while (tmp) { > + if ((tmp->flag & RTE_RIB_VALID_NODE) && > + (RTE_RIB_IS_COVERED(tmp->key, tmp->depth, > + key, depth))) { > + prev = tmp; > + if (flag == RTE_RIB_GET_NXT_COVER) > + return prev; > + } > + tmp = (tmp->left) ? tmp->left : tmp->right; > + } > + return prev; > +} I think this function could do with some comments explaining the logic behind it. > + > +void > +rte_rib_tree_remove(struct rte_rib *rib, uint32_t key, uint8_t depth) > +{ > + struct rte_rib_node *cur, *prev, *child; > + > + cur = rte_rib_tree_lookup_exact(rib, key, depth); > + if (cur == NULL) > + return; > + > + --rib->cur_routes; > + cur->flag &= ~RTE_RIB_VALID_NODE; > + while ((cur->flag & RTE_RIB_VALID_NODE) != RTE_RIB_VALID_NODE) { > + if ((cur->left != NULL) && (cur->right != NULL)) > + return; > + child = (cur->left == NULL) ? cur->right : cur->left; > + if (child != NULL) > + child->parent = cur->parent; > + if (cur->parent == NULL) { > + rib->trie = child; > + rib->free_node(rib, cur); > + return; > + } > + if (cur->parent->left == cur) > + cur->parent->left = child; > + else > + cur->parent->right = child; > + prev = cur; > + cur = cur->parent; > + rib->free_node(rib, prev); > + } > +} > + > +struct rte_rib_node * > +rte_rib_tree_insert(struct rte_rib *rib, uint32_t key, uint8_t depth) > +{ > + struct rte_rib_node **tmp = &rib->trie; > + struct rte_rib_node *prev = NULL; > + struct rte_rib_node *new_node = NULL; > + struct rte_rib_node *common_node = NULL; > + int i = 0; > + uint32_t common_prefix; > + uint8_t common_depth; > + > + if (depth > 32) { > + rte_errno = EINVAL; > + return NULL; > + } > + > + key &= (uint32_t)(UINT64_MAX << (32 - depth)); > + new_node = rte_rib_tree_lookup_exact(rib, key, depth); > + if (new_node != NULL) { > + rte_errno = EEXIST; > + return NULL; > + } > + > + new_node = rib->alloc_node(rib); > + if (new_node == NULL) { > + rte_errno = ENOMEM; > + return NULL; > + } > + new_node->left = NULL; > + new_node->right = NULL; > + new_node->parent = NULL; > + new_node->key = key; > + new_node->depth = depth; > + new_node->flag = RTE_RIB_VALID_NODE; > + > + while (1) { > + if (*tmp == NULL) { > + *tmp = new_node; > + new_node->parent = prev; > + } I think it would be clearer to have a return in this block, rather than having fallthrough to the next one. > + if ((key == (*tmp)->key) && (depth == (*tmp)->depth)) { > + if (new_node != *tmp) { > + rib->free_node(rib, new_node); > + (*tmp)->flag |= RTE_RIB_VALID_NODE; > + } > + ++rib->cur_routes; > + return *tmp; > + } Comment in the above block please. My understanding is that the previous search just confirmed that there wasn't already a valid entry present for this new item, but did not report if there was already an invalid entry, which is what this block is matching. > + i = (*tmp)->depth; I think "d" might be a better choice of name here, rather than "i", which you expect to be a loop variable. > + if ((i >= depth) || (((uint64_t)key >> (32 - i)) != > + ((uint64_t)(*tmp)->key >> (32 - i)))) > + break; > + prev = *tmp; > + tmp = (key & (1 << (31 - i))) ? &(*tmp)->right : &(*tmp)->left; > + } > + common_depth = RTE_MIN(depth, (*tmp)->depth); > + common_prefix = key ^ (*tmp)->key; > + i = __builtin_clz(common_prefix); > + > + common_depth = RTE_MIN(i, common_depth); > + common_prefix = key & (uint32_t)(UINT64_MAX << (32 - common_depth)); > + if ((common_prefix == key) && (common_depth == depth)) { > + if ((*tmp)->key & (1 << (31 - depth))) > + new_node->right = *tmp; > + else > + new_node->left = *tmp; > + new_node->parent = (*tmp)->parent; > + (*tmp)->parent = new_node; > + *tmp = new_node; > + } else { > + common_node = rib->alloc_node(rib); > + if (common_node == NULL) { > + rib->free_node(rib, new_node); > + rte_errno = ENOMEM; > + return NULL; > + } > + common_node->key = common_prefix; > + common_node->depth = common_depth; > + common_node->flag = 0; > + common_node->parent = (*tmp)->parent; > + new_node->parent = common_node; > + (*tmp)->parent = common_node; > + if ((new_node->key & (1 << (31 - common_depth))) == 0) { > + common_node->left = new_node; > + common_node->right = *tmp; > + } else { > + common_node->left = *tmp; > + common_node->right = new_node; > + } > + *tmp = common_node; > + } Again some commenting of the logic here would help the reader. > + ++rib->cur_routes; > + return new_node; > +} > + > +struct rte_rib * > +rte_rib_create(const char *name, int socket_id, struct rte_rib_conf *conf) > +{ > + char mem_name[RTE_RIB_NAMESIZE]; > + struct rte_rib *rib = NULL; > + struct rte_tailq_entry *te; > + struct rte_rib_list *rib_list; > + struct rte_mempool *node_pool = NULL; > + enum rte_dir24_8_nh_sz dir24_8_nh_size; > + > + /* Check user arguments. */ > + if ((name == NULL) || (conf == NULL) || (socket_id < -1) || > + (conf->type >= RTE_RIB_TYPE_MAX) || > + (conf->alloc_type >= RTE_RIB_ALLOC_MAX) || > + (conf->max_nodes == 0) || > + (conf->node_sz < sizeof(struct rte_rib_node))) { > + rte_errno = EINVAL; > + return NULL; > + } > + > + if (conf->alloc_type == RTE_RIB_MEMPOOL) { Since you are forcing the user always to specify the max number of nodes, why not always use mempool allocation type? What is the use-case for malloc-based allocation instead? > + snprintf(mem_name, sizeof(mem_name), "MP_%s", name); > + node_pool = rte_mempool_create(mem_name, conf->max_nodes, > + conf->node_sz, 0, 0, NULL, NULL, NULL, NULL, > + socket_id, 0); > + > + if (node_pool == NULL) { > + RTE_LOG(ERR, LPM, > + "Can not allocate mempool for RIB %s\n", name); > + rte_errno = ENOMEM; > + return NULL; > + } > + > + } > + > + snprintf(mem_name, sizeof(mem_name), "RIB_%s", name); > + > + rib_list = RTE_TAILQ_CAST(rte_rib_tailq.head, rte_rib_list); > + > + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); > + /* guarantee there's no existing */ > + TAILQ_FOREACH(te, rib_list, next) { > + rib = (struct rte_rib *)te->data; > + if (strncmp(name, rib->name, RTE_RIB_NAMESIZE) == 0) > + break; > + } > + rib = NULL; > + if (te != NULL) { > + rte_errno = EEXIST; > + goto exit; > + } > + > + /* allocate tailq entry */ > + te = rte_zmalloc("RIB_TAILQ_ENTRY", sizeof(*te), 0); > + if (te == NULL) { > + RTE_LOG(ERR, LPM, > + "Can not allocate tailq entry for RIB %s\n", name); > + rte_errno = ENOMEM; > + goto exit; > + } > + > + /* Allocate memory to store the LPM data structures. */ > + rib = (struct rte_rib *)rte_zmalloc_socket(mem_name, > + sizeof(struct rte_rib), RTE_CACHE_LINE_SIZE, socket_id); > + if (rib == NULL) { > + RTE_LOG(ERR, LPM, "RIB %s memory allocation failed\n", name); > + rte_errno = ENOMEM; > + goto free_te; > + } > + snprintf(rib->name, sizeof(rib->name), "%s", name); > + rib->trie = NULL; > + rib->max_nodes = conf->max_nodes; > + rib->node_sz = conf->node_sz; > + rib->type = conf->type; > + rib->alloc_type = conf->alloc_type; > + > + if (conf->type <= RTE_RIB_DIR24_8_8B) { > + switch (conf->type) { > + case RTE_RIB_DIR24_8_1B: > + dir24_8_nh_size = RTE_DIR24_8_1B; > + rib->lookup = rte_dir24_8_lookup_bulk_1b; > + break; > + case RTE_RIB_DIR24_8_2B: > + dir24_8_nh_size = RTE_DIR24_8_2B; > + rib->lookup = rte_dir24_8_lookup_bulk_2b; > + break; > + case RTE_RIB_DIR24_8_4B: > + dir24_8_nh_size = RTE_DIR24_8_4B; > + rib->lookup = rte_dir24_8_lookup_bulk_4b; > + break; > + case RTE_RIB_DIR24_8_8B: > + dir24_8_nh_size = RTE_DIR24_8_8B; > + rib->lookup = rte_dir24_8_lookup_bulk_8b; > + break; > + case RTE_RIB_TYPE_MAX: > + default: > + RTE_LOG(ERR, LPM, "Bad RIB %s type\n", name); > + rte_errno = EINVAL; > + goto free_rib; > + } > + rib->fib = (void *)rte_dir24_8_create(name, socket_id, > + dir24_8_nh_size, conf->def_nh); > + if (rib->fib == NULL) { > + RTE_LOG(ERR, LPM, "Failed to allocate FIB %s\n", name); > + rte_errno = ENOMEM; > + goto free_rib; > + } > + rib->modify = rte_dir24_8_modify; > + } > + > + switch (conf->alloc_type) { > + case RTE_RIB_MALLOC: > + rib->alloc_node = new_node_malloc; > + rib->free_node = free_node_malloc; > + break; > + case RTE_RIB_MEMPOOL: > + rib->node_pool = node_pool; > + rib->alloc_node = new_node_mempool; > + rib->free_node = free_node_mempool; > + break; > + case RTE_RIB_ALLOC_MAX: > + default: > + RTE_LOG(ERR, LPM, "Bad RIB %s alloc type\n", name); > + rte_errno = EINVAL; > + goto free_fib; > + } > + > + te->data = (void *)rib; > + TAILQ_INSERT_TAIL(rib_list, te, next); > + > + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); > + > + return rib; > + > +free_fib: > + switch (conf->type) { > + case RTE_RIB_DIR24_8_1B: > + case RTE_RIB_DIR24_8_2B: > + case RTE_RIB_DIR24_8_4B: > + case RTE_RIB_DIR24_8_8B: > + rte_dir24_8_free(rib->fib); > + break; > + default: > + break; > + } > +free_rib: > + rte_free(rib); > +free_te: > + rte_free(te); > +exit: > + if (conf->alloc_type == RTE_RIB_MEMPOOL) > + rte_mempool_free(node_pool); > + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); > + > + return NULL; > +} > + > +struct rte_rib * > +rte_rib_find_existing(const char *name) > +{ > + struct rte_rib *rib = NULL; > + struct rte_tailq_entry *te; > + struct rte_rib_list *rib_list; > + > + rib_list = RTE_TAILQ_CAST(rte_rib_tailq.head, rte_rib_list); > + > + rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK); > + TAILQ_FOREACH(te, rib_list, next) { > + rib = (struct rte_rib *) te->data; > + if (strncmp(name, rib->name, RTE_RIB_NAMESIZE) == 0) > + break; > + } > + rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK); > + > + if (te == NULL) { > + rte_errno = ENOENT; > + return NULL; > + } > + > + return rib; > +} > + > +void > +rte_rib_free(struct rte_rib *rib) > +{ > + struct rte_tailq_entry *te; > + struct rte_rib_list *rib_list; > + struct rte_rib_node *tmp = NULL; > + > + if (rib == NULL) > + return; > + > + rib_list = RTE_TAILQ_CAST(rte_rib_tailq.head, rte_rib_list); > + > + rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); > + > + /* find our tailq entry */ > + TAILQ_FOREACH(te, rib_list, next) { > + if (te->data == (void *)rib) > + break; > + } > + if (te != NULL) > + TAILQ_REMOVE(rib_list, te, next); > + > + rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); > + > + while ((tmp = rte_rib_tree_get_nxt(rib, 0, 0, tmp, > + RTE_RIB_GET_NXT_ALL)) != NULL) > + rte_rib_tree_remove(rib, tmp->key, tmp->depth); > + > + if (rib->alloc_type == RTE_RIB_MEMPOOL) > + rte_mempool_free(rib->node_pool); > + > + switch (rib->type) { > + case RTE_RIB_DIR24_8_1B: > + case RTE_RIB_DIR24_8_2B: > + case RTE_RIB_DIR24_8_4B: > + case RTE_RIB_DIR24_8_8B: > + rte_dir24_8_free(rib->fib); > + break; > + default: > + break; > + } > + > + rte_free(rib); > + rte_free(te); > +} > + > +int > +rte_rib_add(struct rte_rib *rib, uint32_t ip, uint8_t depth, uint64_t next_hop) > +{ > + if ((rib == NULL) || (depth > RTE_RIB_MAXDEPTH)) > + return -EINVAL; > + > + return rib->modify(rib, ip, depth, next_hop, RTE_RIB_ADD); > +} > + > +int > +rte_rib_delete(struct rte_rib *rib, uint32_t ip, uint8_t depth) > +{ > + if ((rib == NULL) || (depth > RTE_RIB_MAXDEPTH)) > + return -EINVAL; > + > + return rib->modify(rib, ip, depth, 0, RTE_RIB_DEL); > +}