DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library
@ 2018-04-26 22:03 Medvedkin Vladimir
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 1/4] Add RIB library Medvedkin Vladimir
                   ` (18 more replies)
  0 siblings, 19 replies; 61+ messages in thread
From: Medvedkin Vladimir @ 2018-04-26 22:03 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, thomas, cristian.dumitrescu, Medvedkin Vladimir

This patch series introduces new library librte_rib which potentially could
replace librte_lpm.

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.

v4:
  fix various bugs
  make struct rte_rib opaque
  make inline functions instead of macro
  remove RTE_RIB_MALLOC node allocation type
  add new lookup functions
  remove rte_dir24_8_lookup()
  add rte_dir24_8_get_lookup()
  add meson support
  add fib configuration


Medvedkin Vladimir (4):
  Add RIB library
  Add dir24_8 implementation for rib library
  Add autotests for RIB library
  Add support for lpm and rib bulk lookup

 config/common_base                 |   6 +
 doc/api/doxy-api.conf              |   1 +
 examples/l3fwd/l3fwd_lpm.c         | 132 +++++++
 examples/l3fwd/meson.build         |   2 +-
 lib/Makefile                       |   2 +
 lib/librte_rib/Makefile            |  23 ++
 lib/librte_rib/meson.build         |   6 +
 lib/librte_rib/rte_dir24_8.c       | 725 +++++++++++++++++++++++++++++++++++++
 lib/librte_rib/rte_dir24_8.h       |  47 +++
 lib/librte_rib/rte_rib.c           | 520 ++++++++++++++++++++++++++
 lib/librte_rib/rte_rib.h           | 302 +++++++++++++++
 lib/librte_rib/rte_rib_version.map |  18 +
 lib/meson.build                    |   2 +-
 mk/rte.app.mk                      |   1 +
 test/test/Makefile                 |   5 +
 test/test/meson.build              |   8 +
 test/test/test_rib.c               | 308 ++++++++++++++++
 test/test/test_rib_generate_rt.c   | 297 +++++++++++++++
 test/test/test_rib_generate_rt.h   |  38 ++
 test/test/test_rib_lpm_comp.c      | 189 ++++++++++
 test/test/test_rib_perf.c          | 145 ++++++++
 21 files changed, 2775 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_rib/Makefile
 create mode 100644 lib/librte_rib/meson.build
 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
 create mode 100644 test/test/test_rib.c
 create mode 100644 test/test/test_rib_generate_rt.c
 create mode 100644 test/test/test_rib_generate_rt.h
 create mode 100644 test/test/test_rib_lpm_comp.c
 create mode 100644 test/test/test_rib_perf.c

-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v4 1/4] Add RIB library
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
@ 2018-04-26 22:03 ` Medvedkin Vladimir
  2018-04-26 22:17   ` Stephen Hemminger
                     ` (6 more replies)
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 2/4] Add dir24_8 implementation for rib library Medvedkin Vladimir
                   ` (17 subsequent siblings)
  18 siblings, 7 replies; 61+ messages in thread
From: Medvedkin Vladimir @ 2018-04-26 22:03 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, thomas, cristian.dumitrescu, Medvedkin Vladimir

Signed-off-by: Medvedkin Vladimir <medvedkinv@gmail.com>
---
 config/common_base                 |   6 +
 doc/api/doxy-api.conf              |   1 +
 lib/Makefile                       |   2 +
 lib/librte_rib/Makefile            |  23 ++
 lib/librte_rib/meson.build         |   6 +
 lib/librte_rib/rte_rib.c           | 520 +++++++++++++++++++++++++++++++++++++
 lib/librte_rib/rte_rib.h           | 302 +++++++++++++++++++++
 lib/librte_rib/rte_rib_version.map |  18 ++
 lib/meson.build                    |   2 +-
 mk/rte.app.mk                      |   1 +
 10 files changed, 880 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_rib/Makefile
 create mode 100644 lib/librte_rib/meson.build
 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 7e45412..833442c 100644
--- a/config/common_base
+++ b/config/common_base
@@ -709,6 +709,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 ad8bdcf..8f42564 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 965be6c..cb1d4e0 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 <medvedkinv@gmail.com>
+
+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/meson.build b/lib/librte_rib/meson.build
new file mode 100644
index 0000000..0f4b865
--- /dev/null
+++ b/lib/librte_rib/meson.build
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2017 Intel Corporation
+
+sources = files('rte_rib.c', 'rte_dir24_8.c')
+headers = files('rte_rib.h', 'rte_dir24_8.h')
+deps += ['mempool']
diff --git a/lib/librte_rib/rte_rib.c b/lib/librte_rib/rte_rib.c
new file mode 100644
index 0000000..67a637b
--- /dev/null
+++ b/lib/librte_rib/rte_rib.c
@@ -0,0 +1,520 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_common.h>
+#include <rte_tailq.h>
+#include <rte_errno.h>
+#include <rte_rwlock.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_mempool.h>
+#include <rte_malloc.h>
+#include <rte_log.h>
+
+#include <rte_rib.h>
+#include <rte_dir24_8.h>
+
+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)
+
+struct rte_rib {
+	char		name[RTE_RIB_NAMESIZE];
+	/*pointer to rib trie*/
+	struct rte_rib_node	*trie;
+	/*pointer to dataplane struct*/
+	void			*fib;
+	/*prefix modification*/
+	rte_rib_modify_fn_t	modify;
+	/* Bulk lookup fn*/
+	rte_rib_lookup_fn_t	lookup;
+	struct rte_mempool	*node_pool;
+	uint32_t		cur_nodes;
+	uint32_t		cur_routes;
+	int			max_nodes;
+	int			node_sz;
+	enum rte_rib_type	type;
+};
+
+static inline int __attribute__((pure))
+rte_rib_is_right_node(struct rte_rib_node *node)
+{
+	return (node->parent->right == node);
+}
+
+/**
+ * Check if prefix1 {key1/depth1}
+ * is covered by prefix2 {key2/depth2}
+ */
+static inline int __attribute__((pure))
+rte_rib_is_covered(uint32_t key1, uint8_t depth1, uint32_t key2, uint8_t depth2)
+{
+	return ((((key1 ^ key2) & rte_rib_depth_to_mask(depth2)) == 0)
+		&& (depth1 > depth2));
+}
+
+static inline struct rte_rib_node *__attribute__((pure))
+rte_rib_get_nxt_node(struct rte_rib_node *node, uint32_t key)
+{
+	return ((key & (1 << (31 - node->depth))) ? node->right : node->left);
+}
+
+static struct rte_rib_node *
+rte_rib_node_alloc(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
+rte_rib_node_free(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) &
+			rte_rib_depth_to_mask(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;
+	}
+	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 &= rte_rib_depth_to_mask(depth);
+	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;
+}
+
+/**
+ *  Traverses on subtree and retrieves more specific routes
+ *  for a given in args key/depth prefix
+ *  last = NULL means the first invocation
+ */
+struct rte_rib_node *
+rte_rib_tree_get_nxt(struct rte_rib *rib, uint32_t key,
+	uint8_t depth, struct rte_rib_node *last, int flag)
+{
+	struct rte_rib_node *tmp, *prev = NULL;
+
+	if (last == NULL) {
+		tmp = rib->trie;
+		while ((tmp) && (tmp->depth < depth))
+			tmp = rte_rib_get_nxt_node(tmp, key);
+	} else {
+		tmp = last;
+		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;
+}
+
+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;
+			rte_rib_node_free(rib, cur);
+			return;
+		}
+		if (cur->parent->left == cur)
+			cur->parent->left = child;
+		else
+			cur->parent->right = child;
+		prev = cur;
+		cur = cur->parent;
+		rte_rib_node_free(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 d = 0;
+	uint32_t common_prefix;
+	uint8_t common_depth;
+
+	if (depth > 32) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	key &= rte_rib_depth_to_mask(depth);
+	new_node = rte_rib_tree_lookup_exact(rib, key, depth);
+	if (new_node != NULL) {
+		rte_errno = EEXIST;
+		return NULL;
+	}
+
+	new_node = rte_rib_node_alloc(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;
+
+	/* traverse down the tree to find matching node or closest matching */
+	while (1) {
+		/* insert as the last node in the branch */
+		if (*tmp == NULL) {
+			*tmp = new_node;
+			new_node->parent = prev;
+			++rib->cur_routes;
+			return *tmp;
+		}
+		/**
+		 * Intermediate node found.
+		 * Previous rte_rib_tree_lookup_exact() returned NULL
+		 * but node with proper search criteria is found.
+		 * Validate intermediate node and return.
+		 */
+		if ((key == (*tmp)->key) && (depth == (*tmp)->depth)) {
+			rte_rib_node_free(rib, new_node);
+			(*tmp)->flag |= RTE_RIB_VALID_NODE;
+			++rib->cur_routes;
+			return *tmp;
+		}
+		d = (*tmp)->depth;
+		if ((d >= depth) || (((uint64_t)key >> (32 - d)) !=
+				((uint64_t)(*tmp)->key >> (32 - d))))
+			break;
+		prev = *tmp;
+		tmp = (key & (1 << (31 - d))) ? &(*tmp)->right : &(*tmp)->left;
+	}
+	/* closest node found, new_node should be inserted in the middle */
+	common_depth = RTE_MIN(depth, (*tmp)->depth);
+	common_prefix = key ^ (*tmp)->key;
+	d = __builtin_clz(common_prefix);
+
+	common_depth = RTE_MIN(d, common_depth);
+	common_prefix = key & rte_rib_depth_to_mask(common_depth);
+	if ((common_prefix == key) && (common_depth == depth)) {
+		/* insert as a parent */
+		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 {
+		/* create intermediate node */
+		common_node = rte_rib_node_alloc(rib);
+		if (common_node == NULL) {
+			rte_rib_node_free(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;
+	}
+	++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;
+
+	/* Check user arguments. */
+	if ((name == NULL) || (conf == NULL) || (socket_id < -1) ||
+			(conf->type >= RTE_RIB_TYPE_MAX) ||
+			(conf->max_nodes == 0) ||
+			(conf->node_sz < sizeof(struct rte_rib_node))) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	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->node_pool = node_pool;
+
+	switch (conf->type) {
+	case RTE_RIB_DIR24_8:
+		rib->lookup = rte_dir24_8_get_lookup_fn(conf);
+		rib->modify = rte_dir24_8_modify;
+		rib->fib = (void *)rte_dir24_8_create(name, socket_id, conf);
+		if (rib->fib == NULL)
+			goto free_rib;
+		break;
+	case RTE_RIB_TYPE_MAX:
+	default:
+		RTE_LOG(ERR, LPM, "Bad RIB %s type\n", name);
+		rte_errno = EINVAL;
+		goto free_rib;
+	}
+
+	te->data = (void *)rib;
+	TAILQ_INSERT_TAIL(rib_list, te, next);
+
+	rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK);
+
+	return rib;
+
+free_rib:
+	rte_free(rib);
+free_te:
+	rte_free(te);
+exit:
+	rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK);
+	rte_mempool_free(node_pool);
+
+	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);
+
+	rte_mempool_free(rib->node_pool);
+
+	switch (rib->type) {
+	case RTE_RIB_DIR24_8:
+		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);
+}
+
+int
+rte_rib_fib_lookup_bulk(struct rte_rib *rib, uint32_t *ips,
+	uint64_t *next_hops, int n)
+{
+	return rib->lookup(rib->fib, ips, next_hops, n);
+}
+
+void *
+rte_rib_get_fibp(struct rte_rib *rib)
+{
+	return rib->fib;
+}
diff --git a/lib/librte_rib/rte_rib.h b/lib/librte_rib/rte_rib.h
new file mode 100644
index 0000000..404deb2
--- /dev/null
+++ b/lib/librte_rib/rte_rib.h
@@ -0,0 +1,302 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ */
+
+#ifndef _RTE_RIB_H_
+#define _RTE_RIB_H_
+
+/**
+ * @file
+ * Compressed trie implementation for Longest Prefix Match
+ */
+
+/** @internal Macro to enable/disable run-time checks. */
+#if defined(RTE_LIBRTE_RIB_DEBUG)
+#define RTE_RIB_RETURN_IF_TRUE(cond, retval) do {	\
+	if (cond)					\
+		return retval;				\
+} while (0)
+#else
+#define RTE_RIB_RETURN_IF_TRUE(cond, retval)
+#endif
+
+#define RTE_RIB_VALID_NODE	1
+#define RTE_RIB_GET_NXT_ALL	0
+#define RTE_RIB_GET_NXT_COVER	1
+
+/** Max number of characters in RIB name. */
+#define RTE_RIB_NAMESIZE	64
+
+/** Maximum depth value possible for IPv4 RIB. */
+#define RTE_RIB_MAXDEPTH	32
+
+struct rte_rib;
+
+static inline uint32_t __attribute__((pure))
+rte_rib_depth_to_mask(uint8_t depth)
+{
+	return (uint32_t)(UINT64_MAX << (32 - depth));
+}
+
+struct rte_rib_node {
+	struct rte_rib_node *left;
+	struct rte_rib_node *right;
+	struct rte_rib_node *parent;
+	uint32_t	key;
+	uint8_t		depth;
+	uint8_t		flag;
+	uint64_t	nh;
+	uint64_t	ext[0];
+};
+
+/** Type of FIB struct*/
+enum rte_rib_type {
+	RTE_RIB_DIR24_8,
+	RTE_RIB_TYPE_MAX
+};
+
+enum rte_rib_op {
+	RTE_RIB_ADD,
+	RTE_RIB_DEL
+};
+
+typedef int (*rte_rib_modify_fn_t)(struct rte_rib *rib, uint32_t key,
+	uint8_t depth, uint64_t next_hop, enum rte_rib_op op);
+typedef int (*rte_rib_lookup_fn_t)(void *fib, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n);
+
+/** Size of nexthop (1 << nh_sz) bits */
+enum rte_dir24_8_nh_sz {
+	RTE_DIR24_8_1B,
+	RTE_DIR24_8_2B,
+	RTE_DIR24_8_4B,
+	RTE_DIR24_8_8B
+};
+
+/** DIR24_8 FIB configuration structure */
+struct rte_rib_dir24_8_conf {
+	uint64_t	def_nh;
+	uint32_t	num_tbl8;
+	enum rte_dir24_8_nh_sz	nh_sz;
+};
+
+/** RIB configuration structure */
+struct rte_rib_conf {
+	enum rte_rib_type	type;
+	int	max_nodes;
+	size_t	node_sz;
+	union {
+		struct rte_rib_dir24_8_conf dir24_8;
+	} fib_conf;
+};
+
+/**
+ * Lookup an IP into the RIB structure
+ *
+ * @param rib
+ *  RIB object handle
+ * @param key
+ *  IP to be looked up in the RIB
+ * @return
+ *  pointer to struct rte_rib_node on success,
+ *  NULL otherwise
+ */
+struct rte_rib_node *
+rte_rib_tree_lookup(struct rte_rib *rib, uint32_t key);
+
+/**
+ * Lookup less specific route into the RIB structure
+ *
+ * @param ent
+ *  Pointer to struct rte_rib_node that represents target route
+ * @return
+ *  pointer to struct rte_rib_node that represents
+ *  less specific route on success,
+ *  NULL otherwise
+ */
+struct rte_rib_node *
+rte_rib_tree_lookup_parent(struct rte_rib_node *ent);
+
+/**
+ * Lookup prefix into the RIB structure
+ *
+ * @param rib
+ *  RIB object handle
+ * @param key
+ *  net to be looked up in the RIB
+ * @param depth
+ *  prefix length
+ * @return
+ *  pointer to struct rte_rib_node on success,
+ *  NULL otherwise
+ */
+struct rte_rib_node *
+rte_rib_tree_lookup_exact(struct rte_rib *rib, uint32_t key, uint8_t depth);
+
+/**
+ * Retrieve next more specific prefix from the RIB
+ * that is covered by key/depth supernet
+ *
+ * @param rib
+ *  RIB object handle
+ * @param key
+ *  net address of supernet prefix that covers returned more specific prefixes
+ * @param depth
+ *  supernet prefix length
+ * @param last
+ *   pointer to the last returned prefix to get next prefix
+ *   or
+ *   NULL to get first more specific prefix
+ * @param flag
+ *  -RTE_RIB_GET_NXT_ALL
+ *   get all prefixes from subtrie
+ *  -RTE_RIB_GET_NXT_COVER
+ *   get only first more specific prefix even if it have more specifics
+ * @return
+ *  pointer to the next more specific prefix
+ *  or
+ *  NULL if there is no prefixes left
+ */
+struct rte_rib_node *
+rte_rib_tree_get_nxt(struct rte_rib *rib, uint32_t key, uint8_t depth,
+	struct rte_rib_node *last, int flag);
+
+/**
+ * Remove prefix from the RIB
+ *
+ * @param rib
+ *  RIB object handle
+ * @param key
+ *  net to be removed from the RIB
+ * @param depth
+ *  prefix length
+ */
+void
+rte_rib_tree_remove(struct rte_rib *rib, uint32_t key, uint8_t depth);
+
+/**
+ * Insert prefix into the RIB
+ *
+ * @param rib
+ *  RIB object handle
+ * @param key
+ *  net to be inserted to the RIB
+ * @param depth
+ *  prefix length
+ * @return
+ *  pointer to new rte_rib_node on success
+ *  NULL otherwise
+ */
+struct rte_rib_node *
+rte_rib_tree_insert(struct rte_rib *rib, uint32_t key, uint8_t depth);
+
+/**
+ * Create RIB
+ *
+ * @param name
+ *  RIB name
+ * @param socket_id
+ *  NUMA socket ID for RIB table memory allocation
+ * @param conf
+ *  Structure containing the configuration
+ * @return
+ *  Handle to RIB object on success
+ *  NULL otherwise with rte_errno set to an appropriate values.
+ */
+struct rte_rib *
+rte_rib_create(const char *name, int socket_id, struct rte_rib_conf *conf);
+
+/**
+ * Find an existing RIB object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the rib object as passed to rte_rib_create()
+ * @return
+ *  Pointer to rib object or NULL if object not found with rte_errno
+ *  set appropriately. Possible rte_errno values include:
+ *   - ENOENT - required entry not available to return.
+ */
+struct rte_rib *
+rte_rib_find_existing(const char *name);
+
+/**
+ * Free an RIB object.
+ *
+ * @param rib
+ *   RIB object handle
+ * @return
+ *   None
+ */
+void
+rte_rib_free(struct rte_rib *rib);
+
+/**
+ * Add a rule to the RIB.
+ *
+ * @param rib
+ *   RIB object handle
+ * @param ip
+ *   IP of the rule to be added to the RIB
+ * @param depth
+ *   Depth of the rule to be added to the RIB
+ * @param next_hop
+ *   Next hop of the rule to be added to the RIB
+ * @return
+ *   0 on success, negative value otherwise
+ */
+int
+rte_rib_add(struct rte_rib *rib, uint32_t ip, uint8_t depth, uint64_t next_hop);
+
+/**
+ * Delete a rule from the RIB.
+ *
+ * @param rib
+ *   RIB object handle
+ * @param ip
+ *   IP of the rule to be deleted from the RIB
+ * @param depth
+ *   Depth of the rule to be deleted from the RIB
+ * @return
+ *   0 on success, negative value otherwise
+ */
+int
+rte_rib_delete(struct rte_rib *rib, uint32_t ip, uint8_t depth);
+
+/**
+ * Lookup multiple IP addresses in an FIB. This may be implemented as a
+ * macro, so the address of the function should not be used.
+ *
+ * @param RIB
+ *   RIB object handle
+ * @param ips
+ *   Array of IPs to be looked up in the FIB
+ * @param next_hops
+ *   Next hop of the most specific rule found for IP.
+ *   This is an array of eight byte values.
+ *   If the lookup for the given IP failed, then corresponding element would
+ *   contain default value, see description of then next parameter.
+ * @param n
+ *   Number of elements in ips (and next_hops) array to lookup. This should be a
+ *   compile time constant, and divisible by 8 for best performance.
+ * @param defv
+ *   Default value to populate into corresponding element of hop[] array,
+ *   if lookup would fail.
+ *  @return
+ *   -EINVAL for incorrect arguments, otherwise 0
+ */
+int
+rte_rib_fib_lookup_bulk(struct rte_rib *rib, uint32_t *ips,
+		uint64_t *next_hops, int n);
+
+/**
+ * Function to retrieve pointer to fib struct
+ *
+ * @param RIB
+ *   RIB object handle
+ * @return
+ *   pointer to fib
+ */
+void *
+rte_rib_get_fibp(struct rte_rib *rib);
+
+#endif /* _RTE_RIB_H_ */
diff --git a/lib/librte_rib/rte_rib_version.map b/lib/librte_rib/rte_rib_version.map
new file mode 100644
index 0000000..b193d6c
--- /dev/null
+++ b/lib/librte_rib/rte_rib_version.map
@@ -0,0 +1,18 @@
+DPDK_17.08 {
+	global:
+
+	rte_rib_create;
+	rte_rib_free;
+	rte_rib_tree_lookup;
+	rte_rib_tree_lookup_parent;
+	rte_rib_tree_lookup_exact;
+	rte_rib_tree_get_nxt;
+	rte_rib_tree_remove;
+	rte_rib_tree_insert;
+	rte_rib_find_existing;
+	rte_rib_add;
+	rte_rib_delete;
+	rte_rib_delete_all;
+
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index 73d6f25..6d06304 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -20,7 +20,7 @@ libraries = [ 'compat', # just a header, used for versioning
 	'gro', 'gso', 'ip_frag', 'jobstats',
 	'kni', 'latencystats', 'lpm', 'member',
 	'meter', 'power', 'pdump', 'rawdev',
-	'reorder', 'sched', 'security', 'vhost',
+	'reorder', 'rib', 'sched', 'security', 'vhost',
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index a145791..b7c5cce 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -70,6 +70,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_GRO)            += -lrte_gro
 _LDLIBS-$(CONFIG_RTE_LIBRTE_GSO)            += -lrte_gso
 _LDLIBS-$(CONFIG_RTE_LIBRTE_METER)          += -lrte_meter
 _LDLIBS-$(CONFIG_RTE_LIBRTE_LPM)            += -lrte_lpm
+_LDLIBS-$(CONFIG_RTE_LIBRTE_RIB)            += -lrte_rib
 # librte_acl needs --whole-archive because of weak functions
 _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL)            += --whole-archive
 _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL)            += -lrte_acl
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v4 2/4] Add dir24_8 implementation for rib library
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 1/4] Add RIB library Medvedkin Vladimir
@ 2018-04-26 22:03 ` Medvedkin Vladimir
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 3/4] Add autotests for RIB library Medvedkin Vladimir
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Medvedkin Vladimir @ 2018-04-26 22:03 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, thomas, cristian.dumitrescu, Medvedkin Vladimir

- Fast table modifcations (add/del routes)
- Variable length next hops (1/2/4/8 bytes)
- 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 <medvedkinv@gmail.com>
---
 lib/librte_rib/rte_dir24_8.c | 725 +++++++++++++++++++++++++++++++++++++++++++
 lib/librte_rib/rte_dir24_8.h |  47 +++
 2 files changed, 772 insertions(+)
 create mode 100644 lib/librte_rib/rte_dir24_8.c
 create mode 100644 lib/librte_rib/rte_dir24_8.h

diff --git a/lib/librte_rib/rte_dir24_8.c b/lib/librte_rib/rte_dir24_8.c
new file mode 100644
index 0000000..b89b1dc
--- /dev/null
+++ b/lib/librte_rib/rte_dir24_8.c
@@ -0,0 +1,725 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <rte_debug.h>
+#include <rte_malloc.h>
+#include <rte_prefetch.h>
+#include <rte_errno.h>
+#include <rte_memory.h>
+#include <rte_branch_prediction.h>
+
+#include <rte_rib.h>
+#include <rte_dir24_8.h>
+
+#define BITMAP_SLAB_BIT_SIZE_LOG2	6
+#define BITMAP_SLAB_BIT_SIZE		(1 << BITMAP_SLAB_BIT_SIZE_LOG2)
+#define BITMAP_SLAB_BITMASK		(BITMAP_SLAB_BIT_SIZE - 1)
+
+struct rte_dir24_8_tbl {
+	uint32_t	number_tbl8s;	/**< Total number of tbl8s */
+	uint32_t	cur_tbl8s;	/**< Current cumber of tbl8s */
+	uint64_t	def_nh;		/**< Default next hop */
+	enum rte_dir24_8_nh_sz	nh_sz;	/**< Size of nexthop entry */
+	uint64_t	*tbl8;		/**< LPM tbl8 table. */
+	uint64_t	*tbl8_idxes;	/**< bitmap containing free tbl8 idxes*/
+	uint64_t	tbl24[0] __rte_cache_aligned;	/**< LPM tbl24 table. */
+};
+
+#define ROUNDUP(x, y)	 RTE_ALIGN_CEIL(x, (1 << (32 - y)))
+
+enum lookup_type {
+	MACRO,
+	INLINE,
+	UNI
+};
+enum lookup_type test_lookup = MACRO;
+
+static __rte_always_inline __attribute__((pure)) void *
+get_tbl24_p(struct rte_dir24_8_tbl *fib, uint32_t ip, uint8_t nh_sz)
+{
+	return (void *)&((uint8_t *)fib->tbl24)[(ip &
+		RTE_DIR24_8_TBL24_MASK) >> (8 - nh_sz)];
+}
+
+static __rte_always_inline __attribute__((pure)) uint8_t
+bits_in_nh(uint8_t nh_sz)
+{
+	return 8 * (1 << nh_sz);
+}
+
+static  __rte_always_inline __attribute__((pure)) uint64_t
+get_max_nh(uint8_t nh_sz)
+{
+	return ((1ULL << (bits_in_nh(nh_sz) - 1)) - 1);
+}
+
+static  __rte_always_inline __attribute__((pure)) uint32_t
+get_tbl24_idx(uint32_t ip)
+{
+	return ip >> 8;
+}
+
+static  __rte_always_inline __attribute__((pure)) uint32_t
+get_tbl8_idx(uint32_t res, uint32_t ip)
+{
+	return (res >> 1) * RTE_DIR24_8_TBL8_GRP_NUM_ENT + (uint8_t)ip;
+}
+
+static __rte_always_inline __attribute__((pure)) uint64_t
+lookup_msk(uint8_t nh_sz)
+{
+	return ((1ULL << ((1 << (nh_sz + 3)) - 1)) << 1) - 1;
+}
+
+static __rte_always_inline __attribute__((pure)) uint8_t
+get_psd_idx(uint32_t val, uint8_t nh_sz)
+{
+	return val & ((1 << (3 - nh_sz)) - 1);
+}
+
+static  __rte_always_inline __attribute__((pure)) uint32_t
+get_tbl_idx(uint32_t val, uint8_t nh_sz)
+{
+	return val >> (3 - nh_sz);
+}
+
+static  __rte_always_inline __attribute__((pure)) uint64_t
+get_tbl24(struct rte_dir24_8_tbl *fib, uint32_t ip, uint8_t nh_sz)
+{
+	return ((fib->tbl24[get_tbl_idx(get_tbl24_idx(ip), nh_sz)] >>
+		(get_psd_idx(get_tbl24_idx(ip), nh_sz) *
+		bits_in_nh(nh_sz))) & lookup_msk(nh_sz));
+}
+
+static  __rte_always_inline __attribute__((pure)) uint64_t
+get_tbl8(struct rte_dir24_8_tbl *fib, uint32_t res, uint32_t ip, uint8_t nh_sz)
+{
+	return ((fib->tbl8[get_tbl_idx(get_tbl8_idx(res, ip), nh_sz)] >>
+		(get_psd_idx(get_tbl8_idx(res, ip), nh_sz) *
+		bits_in_nh(nh_sz))) & lookup_msk(nh_sz));
+}
+
+#define LOOKUP_FUNC(suffix, type, bulk_prefetch, nh_sz)			\
+static int rte_dir24_8_lookup_bulk_##suffix(void *fib_p, const uint32_t *ips, \
+	uint64_t *next_hops, const unsigned int n)			\
+{									\
+	struct rte_dir24_8_tbl *fib = (struct rte_dir24_8_tbl *)fib_p;	\
+	uint64_t tmp;							\
+	uint32_t i;							\
+	uint32_t prefetch_offset =					\
+		RTE_MIN((unsigned int)bulk_prefetch, n);		\
+									\
+	RTE_RIB_RETURN_IF_TRUE(((fib == NULL) || (ips == NULL) ||	\
+		(next_hops == NULL)), -EINVAL);				\
+									\
+	for (i = 0; i < prefetch_offset; i++)				\
+		rte_prefetch0(get_tbl24_p(fib, ips[i], nh_sz));		\
+	for (i = 0; i < (n - prefetch_offset); i++) {			\
+		rte_prefetch0(get_tbl24_p(fib,				\
+			ips[i + prefetch_offset], nh_sz));		\
+		tmp = ((type *)fib->tbl24)[ips[i] >> 8];		\
+		if (unlikely((tmp & RTE_DIR24_8_VALID_EXT_ENT) ==	\
+				RTE_DIR24_8_VALID_EXT_ENT)) {		\
+			tmp = ((type *)fib->tbl8)[(uint8_t)ips[i] +	\
+				((tmp >> 1) * RTE_DIR24_8_TBL8_GRP_NUM_ENT)]; \
+		}							\
+		next_hops[i] = tmp >> 1;				\
+	}								\
+	for (; i < n; i++) {						\
+		tmp = ((type *)fib->tbl24)[ips[i] >> 8];		\
+		if (unlikely((tmp & RTE_DIR24_8_VALID_EXT_ENT) ==	\
+				RTE_DIR24_8_VALID_EXT_ENT)) {		\
+			tmp = ((type *)fib->tbl8)[(uint8_t)ips[i] +	\
+				((tmp >> 1) * RTE_DIR24_8_TBL8_GRP_NUM_ENT)]; \
+		}							\
+		next_hops[i] = tmp >> 1;				\
+	}								\
+	return 0;							\
+}									\
+
+LOOKUP_FUNC(1b, uint8_t, 5, 0)
+LOOKUP_FUNC(2b, uint16_t, 6, 1)
+LOOKUP_FUNC(4b, uint32_t, 15, 2)
+LOOKUP_FUNC(8b, uint64_t, 12, 3)
+
+static inline int
+rte_dir24_8_lookup_bulk(struct rte_dir24_8_tbl *fib, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n, uint8_t nh_sz)
+{
+	uint64_t tmp;
+	uint32_t i;
+	uint32_t prefetch_offset = RTE_MIN(15U, n);
+
+	RTE_RIB_RETURN_IF_TRUE(((fib == NULL) || (ips == NULL) ||
+		(next_hops == NULL)), -EINVAL);
+
+	for (i = 0; i < prefetch_offset; i++)
+		rte_prefetch0(get_tbl24_p(fib, ips[i], nh_sz));
+	for (i = 0; i < (n - prefetch_offset); i++) {
+		rte_prefetch0(get_tbl24_p(fib, ips[i + prefetch_offset],
+			nh_sz));
+		tmp = get_tbl24(fib, ips[i], nh_sz);
+		if (unlikely((tmp & RTE_DIR24_8_VALID_EXT_ENT) ==
+				RTE_DIR24_8_VALID_EXT_ENT)) {
+			tmp = get_tbl8(fib, tmp, ips[i], nh_sz);
+		}
+		next_hops[i] = tmp >> 1;
+	}
+	for (; i < n; i++) {
+		tmp = get_tbl24(fib, ips[i], nh_sz);
+		if (unlikely((tmp & RTE_DIR24_8_VALID_EXT_ENT) ==
+				RTE_DIR24_8_VALID_EXT_ENT)) {
+			tmp = get_tbl8(fib, tmp, ips[i], nh_sz);
+		}
+		next_hops[i] = tmp >> 1;
+	}
+	return 0;
+}
+
+static int
+rte_dir24_8_lookup_bulk_0(void *fib_p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct rte_dir24_8_tbl *fib = (struct rte_dir24_8_tbl *)fib_p;
+
+	return rte_dir24_8_lookup_bulk(fib, ips, next_hops, n, 0);
+}
+
+static int
+rte_dir24_8_lookup_bulk_1(void *fib_p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct rte_dir24_8_tbl *fib = (struct rte_dir24_8_tbl *)fib_p;
+
+	return rte_dir24_8_lookup_bulk(fib, ips, next_hops, n, 1);
+}
+
+static int
+rte_dir24_8_lookup_bulk_2(void *fib_p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct rte_dir24_8_tbl *fib = (struct rte_dir24_8_tbl *)fib_p;
+
+	return rte_dir24_8_lookup_bulk(fib, ips, next_hops, n, 2);
+}
+
+static int
+rte_dir24_8_lookup_bulk_3(void *fib_p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct rte_dir24_8_tbl *fib = (struct rte_dir24_8_tbl *)fib_p;
+
+	return rte_dir24_8_lookup_bulk(fib, ips, next_hops, n, 3);
+}
+
+static int
+rte_dir24_8_lookup_bulk_uni(void *fib_p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct rte_dir24_8_tbl *fib = (struct rte_dir24_8_tbl *)fib_p;
+	uint64_t tmp;
+	uint32_t i;
+	uint32_t prefetch_offset = RTE_MIN(15U, n);
+	uint8_t nh_sz = fib->nh_sz
+
+	RTE_RIB_RETURN_IF_TRUE(((fib == NULL) || (ips == NULL) ||
+		(next_hops == NULL)), -EINVAL);
+
+	for (i = 0; i < prefetch_offset; i++)
+		rte_prefetch0(get_tbl24_p(fib, ips[i], nh_sz));
+	for (i = 0; i < (n - prefetch_offset); i++) {
+		rte_prefetch0(get_tbl24_p(fib, ips[i + prefetch_offset],
+			nh_sz));
+		tmp = get_tbl24(fib, ips[i], nh_sz);
+		if (unlikely((tmp & RTE_DIR24_8_VALID_EXT_ENT) ==
+				RTE_DIR24_8_VALID_EXT_ENT)) {
+			tmp = get_tbl8(fib, tmp, ips[i], nh_sz);
+		}
+		next_hops[i] = tmp >> 1;
+	}
+	for (; i < n; i++) {
+		tmp = get_tbl24(fib, ips[i], nh_sz);
+		if (unlikely((tmp & RTE_DIR24_8_VALID_EXT_ENT) ==
+				RTE_DIR24_8_VALID_EXT_ENT)) {
+			tmp = get_tbl8(fib, tmp, ips[i], nh_sz);
+		}
+		next_hops[i] = tmp >> 1;
+	}
+	return 0;
+}
+
+rte_rib_lookup_fn_t
+rte_dir24_8_get_lookup_fn(struct rte_rib_conf *rib_conf)
+{
+	enum rte_dir24_8_nh_sz nh_sz = rib_conf->fib_conf.dir24_8.nh_sz;
+
+	if (test_lookup == MACRO) {
+		switch (nh_sz) {
+		case(RTE_DIR24_8_1B):
+			return rte_dir24_8_lookup_bulk_1b;
+		case(RTE_DIR24_8_2B):
+			return rte_dir24_8_lookup_bulk_2b;
+		case(RTE_DIR24_8_4B):
+			return rte_dir24_8_lookup_bulk_4b;
+		case(RTE_DIR24_8_8B):
+			return rte_dir24_8_lookup_bulk_8b;
+		}
+	} else if (test_lookup == INLINE) {
+		switch (nh_sz) {
+		case(RTE_DIR24_8_1B):
+			return rte_dir24_8_lookup_bulk_0;
+		case(RTE_DIR24_8_2B):
+			return rte_dir24_8_lookup_bulk_1;
+		case(RTE_DIR24_8_4B):
+			return rte_dir24_8_lookup_bulk_2;
+		case(RTE_DIR24_8_8B):
+			return rte_dir24_8_lookup_bulk_3;
+		}
+	} else
+		return rte_dir24_8_lookup_bulk_uni;
+	return NULL;
+}
+
+static void
+write_to_fib(void *ptr, uint64_t val, enum rte_dir24_8_nh_sz size, int n)
+{
+	int i;
+	uint8_t *ptr8 = (uint8_t *)ptr;
+	uint16_t *ptr16 = (uint16_t *)ptr;
+	uint32_t *ptr32 = (uint32_t *)ptr;
+	uint64_t *ptr64 = (uint64_t *)ptr;
+
+	switch (size) {
+	case RTE_DIR24_8_1B:
+		for (i = 0; i < n; i++)
+			ptr8[i] = (uint8_t)val;
+		break;
+	case RTE_DIR24_8_2B:
+		for (i = 0; i < n; i++)
+			ptr16[i] = (uint16_t)val;
+		break;
+	case RTE_DIR24_8_4B:
+		for (i = 0; i < n; i++)
+			ptr32[i] = (uint32_t)val;
+		break;
+	case RTE_DIR24_8_8B:
+		for (i = 0; i < n; i++)
+			ptr64[i] = (uint64_t)val;
+		break;
+	}
+}
+
+static int
+tbl8_get_idx(struct rte_dir24_8_tbl *fib)
+{
+	uint32_t i;
+	int bit_idx;
+
+	for (i = 0; (i <= (fib->number_tbl8s >> BITMAP_SLAB_BIT_SIZE_LOG2)) &&
+			(fib->tbl8_idxes[i] == UINT64_MAX); i++)
+		;
+	if (i <= (fib->number_tbl8s >> BITMAP_SLAB_BIT_SIZE_LOG2)) {
+		bit_idx = __builtin_ctzll(~fib->tbl8_idxes[i]);
+		fib->tbl8_idxes[i] |= (1ULL << bit_idx);
+		return (i << BITMAP_SLAB_BIT_SIZE_LOG2) + bit_idx;
+	}
+	return -ENOSPC;
+}
+
+static inline void
+tbl8_free_idx(struct rte_dir24_8_tbl *fib, int idx)
+{
+	fib->tbl8_idxes[idx >> BITMAP_SLAB_BIT_SIZE_LOG2] &=
+		~(1ULL << (idx & BITMAP_SLAB_BITMASK));
+}
+
+static int
+tbl8_alloc(struct rte_dir24_8_tbl *fib, uint64_t nh)
+{
+	int	tbl8_idx;
+	uint8_t	*tbl8_ptr;
+
+	tbl8_idx = tbl8_get_idx(fib);
+	if (tbl8_idx < 0)
+		return tbl8_idx;
+	tbl8_ptr = (uint8_t *)fib->tbl8 +
+		((tbl8_idx * RTE_DIR24_8_TBL8_GRP_NUM_ENT) <<
+		fib->nh_sz);
+	/*Init tbl8 entries with nexthop from tbl24*/
+	write_to_fib((void *)tbl8_ptr, nh|
+		RTE_DIR24_8_VALID_EXT_ENT, fib->nh_sz,
+		RTE_DIR24_8_TBL8_GRP_NUM_ENT);
+	return tbl8_idx;
+}
+
+static void
+tbl8_recycle(struct rte_dir24_8_tbl *fib, uint32_t ip, uint64_t tbl8_idx)
+{
+	int i;
+	uint64_t nh;
+	uint8_t *ptr8;
+	uint16_t *ptr16;
+	uint32_t *ptr32;
+	uint64_t *ptr64;
+
+	switch (fib->nh_sz) {
+	case RTE_DIR24_8_1B:
+		ptr8 = &((uint8_t *)fib->tbl8)[tbl8_idx *
+				RTE_DIR24_8_TBL8_GRP_NUM_ENT];
+		nh = *ptr8;
+		for (i = 1; i < RTE_DIR24_8_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr8[i])
+				return;
+		}
+		((uint8_t *)fib->tbl24)[ip >> 8] =
+			nh & ~RTE_DIR24_8_VALID_EXT_ENT;
+		for (i = 0; i < RTE_DIR24_8_TBL8_GRP_NUM_ENT; i++)
+			ptr8[i] = 0;
+		break;
+	case RTE_DIR24_8_2B:
+		ptr16 = &((uint16_t *)fib->tbl8)[tbl8_idx *
+				RTE_DIR24_8_TBL8_GRP_NUM_ENT];
+		nh = *ptr16;
+		for (i = 1; i < RTE_DIR24_8_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr16[i])
+				return;
+		}
+		((uint16_t *)fib->tbl24)[ip >> 8] =
+			nh & ~RTE_DIR24_8_VALID_EXT_ENT;
+		for (i = 0; i < RTE_DIR24_8_TBL8_GRP_NUM_ENT; i++)
+			ptr16[i] = 0;
+		break;
+	case RTE_DIR24_8_4B:
+		ptr32 = &((uint32_t *)fib->tbl8)[tbl8_idx *
+				RTE_DIR24_8_TBL8_GRP_NUM_ENT];
+		nh = *ptr32;
+		for (i = 1; i < RTE_DIR24_8_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr32[i])
+				return;
+		}
+		((uint32_t *)fib->tbl24)[ip >> 8] =
+			nh & ~RTE_DIR24_8_VALID_EXT_ENT;
+		for (i = 0; i < RTE_DIR24_8_TBL8_GRP_NUM_ENT; i++)
+			ptr32[i] = 0;
+		break;
+	case RTE_DIR24_8_8B:
+		ptr64 = &((uint64_t *)fib->tbl8)[tbl8_idx *
+				RTE_DIR24_8_TBL8_GRP_NUM_ENT];
+		nh = *ptr64;
+		for (i = 1; i < RTE_DIR24_8_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr64[i])
+				return;
+		}
+		((uint64_t *)fib->tbl24)[ip >> 8] =
+			nh & ~RTE_DIR24_8_VALID_EXT_ENT;
+		for (i = 0; i < RTE_DIR24_8_TBL8_GRP_NUM_ENT; i++)
+			ptr64[i] = 0;
+		break;
+	}
+	tbl8_free_idx(fib, tbl8_idx);
+}
+
+static int
+install_to_fib(struct rte_dir24_8_tbl *fib, uint32_t ledge, uint32_t redge,
+	uint64_t next_hop)
+{
+	uint64_t	tbl24_tmp;
+	int	tbl8_idx;
+	int tmp_tbl8_idx;
+	uint8_t	*tbl8_ptr;
+	uint32_t len;
+
+	len = (unlikely((ledge == 0) && (redge == 0))) ? 1 << 24 :
+		((redge & RTE_DIR24_8_TBL24_MASK) - ROUNDUP(ledge, 24)) >> 8;
+
+	if (((ledge >> 8) != (redge >> 8)) || (len == 1 << 24)) {
+		if ((ROUNDUP(ledge, 24) - ledge) != 0) {
+			tbl24_tmp = get_tbl24(fib, ledge, fib->nh_sz);
+			if ((tbl24_tmp & RTE_DIR24_8_VALID_EXT_ENT) !=
+					RTE_DIR24_8_VALID_EXT_ENT) {
+				/**
+				 * Make sure there is space for two TBL8.
+				 * This is necessary when installing range that
+				 * needs tbl8 for ledge and redge.
+				 */
+				tbl8_idx = tbl8_alloc(fib, tbl24_tmp);
+				tmp_tbl8_idx = tbl8_get_idx(fib);
+				if (tbl8_idx < 0)
+					return -ENOSPC;
+				else if (tmp_tbl8_idx < 0) {
+					tbl8_free_idx(fib, tbl8_idx);
+					return -ENOSPC;
+				}
+				tbl8_free_idx(fib, tmp_tbl8_idx);
+				/*update dir24 entry with tbl8 index*/
+				write_to_fib(get_tbl24_p(fib, ledge,
+					fib->nh_sz), (tbl8_idx << 1)|
+					RTE_DIR24_8_VALID_EXT_ENT,
+					fib->nh_sz, 1);
+			} else
+				tbl8_idx = tbl24_tmp >> 1;
+			tbl8_ptr = (uint8_t *)fib->tbl8 +
+				(((tbl8_idx * RTE_DIR24_8_TBL8_GRP_NUM_ENT) +
+				(ledge & ~RTE_DIR24_8_TBL24_MASK)) <<
+				fib->nh_sz);
+			/*update tbl8 with new next hop*/
+			write_to_fib((void *)tbl8_ptr, (next_hop << 1)|
+				RTE_DIR24_8_VALID_EXT_ENT,
+				fib->nh_sz, ROUNDUP(ledge, 24) - ledge);
+			tbl8_recycle(fib, ledge, tbl8_idx);
+		}
+		write_to_fib(get_tbl24_p(fib, ROUNDUP(ledge, 24), fib->nh_sz),
+			next_hop << 1, fib->nh_sz, len);
+		if (redge & ~RTE_DIR24_8_TBL24_MASK) {
+			tbl24_tmp = get_tbl24(fib, redge, fib->nh_sz);
+			if ((tbl24_tmp & RTE_DIR24_8_VALID_EXT_ENT) !=
+					RTE_DIR24_8_VALID_EXT_ENT) {
+				tbl8_idx = tbl8_alloc(fib, tbl24_tmp);
+				if (tbl8_idx < 0)
+					return -ENOSPC;
+				/*update dir24 entry with tbl8 index*/
+				write_to_fib(get_tbl24_p(fib, redge,
+					fib->nh_sz), (tbl8_idx << 1)|
+					RTE_DIR24_8_VALID_EXT_ENT,
+					fib->nh_sz, 1);
+			} else
+				tbl8_idx = tbl24_tmp >> 1;
+			tbl8_ptr = (uint8_t *)fib->tbl8 +
+				((tbl8_idx * RTE_DIR24_8_TBL8_GRP_NUM_ENT) <<
+				fib->nh_sz);
+			/*update tbl8 with new next hop*/
+			write_to_fib((void *)tbl8_ptr, (next_hop << 1)|
+				RTE_DIR24_8_VALID_EXT_ENT,
+				fib->nh_sz, redge & ~RTE_DIR24_8_TBL24_MASK);
+			tbl8_recycle(fib, redge, tbl8_idx);
+		}
+	} else if ((redge - ledge) != 0) {
+		tbl24_tmp = get_tbl24(fib, ledge, fib->nh_sz);
+		if ((tbl24_tmp & RTE_DIR24_8_VALID_EXT_ENT) !=
+				RTE_DIR24_8_VALID_EXT_ENT) {
+			tbl8_idx = tbl8_alloc(fib, tbl24_tmp);
+			if (tbl8_idx < 0)
+				return -ENOSPC;
+			/*update dir24 entry with tbl8 index*/
+			write_to_fib(get_tbl24_p(fib, ledge, fib->nh_sz),
+				(tbl8_idx << 1)|
+				RTE_DIR24_8_VALID_EXT_ENT,
+				fib->nh_sz, 1);
+		} else
+			tbl8_idx = tbl24_tmp >> 1;
+		tbl8_ptr = (uint8_t *)fib->tbl8 +
+			(((tbl8_idx * RTE_DIR24_8_TBL8_GRP_NUM_ENT) +
+			(ledge & ~RTE_DIR24_8_TBL24_MASK)) <<
+			fib->nh_sz);
+		/*update tbl8 with new next hop*/
+		write_to_fib((void *)tbl8_ptr, (next_hop << 1)|
+			RTE_DIR24_8_VALID_EXT_ENT,
+			fib->nh_sz, redge - ledge);
+		tbl8_recycle(fib, ledge, tbl8_idx);
+	}
+	return 0;
+}
+
+static int
+modify_fib(struct rte_rib *rib, uint32_t ip, uint8_t depth,
+	uint64_t next_hop)
+{
+	struct rte_rib_node *tmp = NULL;
+	struct rte_dir24_8_tbl *fib;
+	uint32_t ledge, redge;
+	int ret;
+
+	fib = rte_rib_get_fibp(rib);
+
+	if (next_hop > get_max_nh(fib->nh_sz))
+		return -EINVAL;
+
+	ip &= rte_rib_depth_to_mask(depth);
+	ledge = ip;
+	do {
+		tmp = rte_rib_tree_get_nxt(rib, ip, depth, tmp,
+			RTE_RIB_GET_NXT_COVER);
+		if (tmp != NULL) {
+			if (tmp->depth == depth)
+				continue;
+			redge = tmp->key & rte_rib_depth_to_mask(tmp->depth);
+			if (ledge == redge) {
+				ledge = redge +
+					(uint32_t)(1ULL << (32 - tmp->depth));
+				continue;
+			}
+			ret = install_to_fib(fib, ledge, redge,
+				next_hop);
+			if (ret != 0)
+				return ret;
+			ledge = redge +
+				(uint32_t)(1ULL << (32 - tmp->depth));
+		} else {
+			redge = ip + (uint32_t)(1ULL << (32 - depth));
+			ret = install_to_fib(fib, ledge, redge,
+				next_hop);
+			if (ret != 0)
+				return ret;
+		}
+	} while (tmp);
+
+	return 0;
+}
+
+int
+rte_dir24_8_modify(struct rte_rib *rib, uint32_t ip, uint8_t depth,
+	uint64_t next_hop, enum rte_rib_op op)
+{
+	struct rte_dir24_8_tbl *fib;
+	struct rte_rib_node *tmp = NULL;
+	struct rte_rib_node *node;
+	struct rte_rib_node *parent;
+	int ret = 0;
+
+	if ((rib == NULL) || (depth > RTE_RIB_MAXDEPTH))
+		return -EINVAL;
+
+	fib = rte_rib_get_fibp(rib);
+	RTE_ASSERT(fib);
+
+	ip &= rte_rib_depth_to_mask(depth);
+
+	node = rte_rib_tree_lookup_exact(rib, ip, depth);
+	switch (op) {
+	case RTE_RIB_ADD:
+		if (node != NULL) {
+			if (node->nh == next_hop)
+				return 0;
+			ret = modify_fib(rib, ip, depth, next_hop);
+			if (ret == 0)
+				node->nh = next_hop;
+			return 0;
+		}
+		if (depth > 24) {
+			tmp = rte_rib_tree_get_nxt(rib, ip, 24, NULL,
+				RTE_RIB_GET_NXT_COVER);
+			if ((tmp == NULL) &&
+				(fib->cur_tbl8s >= fib->number_tbl8s))
+				return -ENOSPC;
+
+		}
+		node = rte_rib_tree_insert(rib, ip, depth);
+		if (node == NULL)
+			return -rte_errno;
+		node->nh = next_hop;
+		parent = rte_rib_tree_lookup_parent(node);
+		if ((parent != NULL) && (parent->nh == next_hop))
+			return 0;
+		ret = modify_fib(rib, ip, depth, next_hop);
+		if (ret != 0) {
+			rte_rib_tree_remove(rib, ip, depth);
+			return ret;
+		}
+		if ((depth > 24) && (tmp == NULL))
+			fib->cur_tbl8s++;
+		return 0;
+	case RTE_RIB_DEL:
+		if (node == NULL)
+			return -ENOENT;
+
+		parent = rte_rib_tree_lookup_parent(node);
+		if (parent != NULL) {
+			if (parent->nh != node->nh)
+				ret = modify_fib(rib, ip, depth, parent->nh);
+		} else
+			ret = modify_fib(rib, ip, depth, fib->def_nh);
+		if (ret == 0) {
+			rte_rib_tree_remove(rib, ip, depth);
+			if (depth > 24) {
+				tmp = rte_rib_tree_get_nxt(rib, ip, 24, NULL,
+					RTE_RIB_GET_NXT_COVER);
+				if (tmp == NULL)
+					fib->cur_tbl8s--;
+			}
+		}
+		return ret;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+struct rte_dir24_8_tbl *rte_dir24_8_create(const char *name, int socket_id,
+	struct rte_rib_conf *rib_conf)
+{
+	char mem_name[RTE_RIB_NAMESIZE];
+	struct rte_dir24_8_tbl *fib;
+	uint64_t	def_nh;
+	uint32_t	num_tbl8;
+	enum rte_dir24_8_nh_sz	nh_sz;
+
+	if ((name == NULL) || (socket_id < -1) || (rib_conf == NULL) ||
+			(rib_conf->fib_conf.dir24_8.nh_sz < RTE_DIR24_8_1B) ||
+			(rib_conf->fib_conf.dir24_8.nh_sz > RTE_DIR24_8_8B) ||
+			(rib_conf->fib_conf.dir24_8.num_tbl8 >
+			get_max_nh(rib_conf->fib_conf.dir24_8.nh_sz)) ||
+			(rib_conf->fib_conf.dir24_8.num_tbl8 == 0) ||
+			(rib_conf->fib_conf.dir24_8.def_nh >
+			get_max_nh(rib_conf->fib_conf.dir24_8.nh_sz))) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	def_nh = rib_conf->fib_conf.dir24_8.def_nh;
+	nh_sz = rib_conf->fib_conf.dir24_8.nh_sz;
+	num_tbl8 = rib_conf->fib_conf.dir24_8.num_tbl8;
+
+	snprintf(mem_name, sizeof(mem_name), "FIB_%s", name);
+	fib = rte_zmalloc_socket(name, sizeof(struct rte_dir24_8_tbl) +
+		RTE_DIR24_8_TBL24_NUM_ENT * (1 << nh_sz), RTE_CACHE_LINE_SIZE,
+		socket_id);
+	if (fib == NULL) {
+		rte_errno = ENOMEM;
+		return fib;
+	}
+
+	write_to_fib(&fib->tbl24, (def_nh << 1), nh_sz, 1 << 24);
+
+	snprintf(mem_name, sizeof(mem_name), "TBL8_%s", name);
+	fib->tbl8 = rte_zmalloc_socket(mem_name, RTE_DIR24_8_TBL8_GRP_NUM_ENT *
+			(1 << nh_sz) * (num_tbl8 + 1),
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (fib->tbl8 == NULL) {
+		rte_errno = ENOMEM;
+		rte_free(fib);
+		return NULL;
+	}
+	fib->def_nh = def_nh;
+	fib->nh_sz = nh_sz;
+	fib->number_tbl8s = num_tbl8;
+
+	snprintf(mem_name, sizeof(mem_name), "TBL8_idxes_%s", name);
+	fib->tbl8_idxes = rte_zmalloc_socket(mem_name,
+			RTE_ALIGN_CEIL(fib->number_tbl8s, 64) >> 3,
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (fib->tbl8_idxes == NULL) {
+		rte_errno = ENOMEM;
+		rte_free(fib->tbl8);
+		rte_free(fib);
+		return NULL;
+	}
+
+	return fib;
+}
+
+void
+rte_dir24_8_free(void *fib_p)
+{
+	struct rte_dir24_8_tbl *fib = (struct rte_dir24_8_tbl *)fib_p;
+
+	rte_free(fib->tbl8_idxes);
+	rte_free(fib->tbl8);
+	rte_free(fib);
+}
diff --git a/lib/librte_rib/rte_dir24_8.h b/lib/librte_rib/rte_dir24_8.h
new file mode 100644
index 0000000..84c2941
--- /dev/null
+++ b/lib/librte_rib/rte_dir24_8.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ */
+
+#ifndef _RTE_DIR24_8_H_
+#define _RTE_DIR24_8_H_
+
+/**
+ * @file
+ * RTE Longest Prefix Match (LPM)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @internal Total number of tbl24 entries. */
+#define RTE_DIR24_8_TBL24_NUM_ENT	(1 << 24)
+
+/** Maximum depth value possible for IPv4 LPM. */
+#define RTE_DIR24_8_MAX_DEPTH		32
+
+/** @internal Number of entries in a tbl8 group. */
+#define RTE_DIR24_8_TBL8_GRP_NUM_ENT	256
+
+/** @internal Total number of tbl8 groups in the tbl8. */
+#define RTE_DIR24_8_TBL8_NUM_GROUPS	65536
+
+/** @internal bitmask with valid and valid_group fields set */
+#define RTE_DIR24_8_VALID_EXT_ENT	0x01
+
+#define RTE_DIR24_8_TBL24_MASK		0xffffff00
+
+struct rte_dir24_8_tbl *rte_dir24_8_create(const char *name, int socket_id,
+	struct rte_rib_conf *rib_conf);
+void rte_dir24_8_free(void *fib_p);
+int rte_dir24_8_modify(struct rte_rib *rib, uint32_t key,
+	uint8_t depth, uint64_t next_hop, enum rte_rib_op op);
+
+rte_rib_lookup_fn_t rte_dir24_8_get_lookup_fn(struct rte_rib_conf *rib_conf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_DIR24_8_H_ */
+
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v4 3/4] Add autotests for RIB library
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 1/4] Add RIB library Medvedkin Vladimir
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 2/4] Add dir24_8 implementation for rib library Medvedkin Vladimir
@ 2018-04-26 22:03 ` Medvedkin Vladimir
  2018-06-29 14:13   ` Bruce Richardson
                     ` (2 more replies)
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 4/4] Add support for lpm and rib bulk lookup Medvedkin Vladimir
                   ` (15 subsequent siblings)
  18 siblings, 3 replies; 61+ messages in thread
From: Medvedkin Vladimir @ 2018-04-26 22:03 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, thomas, cristian.dumitrescu, Medvedkin Vladimir

Signed-off-by: Medvedkin Vladimir <medvedkinv@gmail.com>
---
 test/test/Makefile               |   5 +
 test/test/meson.build            |   8 +
 test/test/test_rib.c             | 308 +++++++++++++++++++++++++++++++++++++++
 test/test/test_rib_generate_rt.c | 297 +++++++++++++++++++++++++++++++++++++
 test/test/test_rib_generate_rt.h |  38 +++++
 test/test/test_rib_lpm_comp.c    | 189 ++++++++++++++++++++++++
 test/test/test_rib_perf.c        | 145 ++++++++++++++++++
 7 files changed, 990 insertions(+)
 create mode 100644 test/test/test_rib.c
 create mode 100644 test/test/test_rib_generate_rt.c
 create mode 100644 test/test/test_rib_generate_rt.h
 create mode 100644 test/test/test_rib_lpm_comp.c
 create mode 100644 test/test/test_rib_perf.c

diff --git a/test/test/Makefile b/test/test/Makefile
index 2630ab4..b5f4fb3 100644
--- a/test/test/Makefile
+++ b/test/test/Makefile
@@ -119,6 +119,11 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6_perf.c
 
+SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib.c
+SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_rib_generate_rt.c
+SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_rib_perf.c
+SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_rib_lpm_comp.c
+
 SRCS-y += test_debug.c
 SRCS-y += test_errno.c
 SRCS-y += test_tailq.c
diff --git a/test/test/meson.build b/test/test/meson.build
index ad0a650..f9abc3d 100644
--- a/test/test/meson.build
+++ b/test/test/meson.build
@@ -74,6 +74,10 @@ test_sources = files('commands.c',
 	'test_reciprocal_division_perf.c',
 	'test_red.c',
 	'test_reorder.c',
+	'test_rib.c',
+	'test_rib_generate_rt.c',
+	'test_rib_perf.c',
+	'test_rib_lpm_comp.c',
 	'test_ring.c',
 	'test_ring_perf.c',
 	'test_rwlock.c',
@@ -111,6 +115,7 @@ test_deps = ['acl',
 	'pipeline',
 	'port',
 	'reorder',
+	'rib',
 	'ring',
 	'timer'
 ]
@@ -192,6 +197,9 @@ test_names = [
 	'red_autotest',
 	'red_perf',
 	'reorder_autotest',
+	'rib_autotest',
+	'rib_perf_autotest',
+	'rib_lpm_comp_autotest',
 	'ring_autotest',
 	'ring_perf_autotest',
 	'ring_pmd_autotest',
diff --git a/test/test/test_rib.c b/test/test/test_rib.c
new file mode 100644
index 0000000..c5d7509
--- /dev/null
+++ b/test/test/test_rib.c
@@ -0,0 +1,308 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <rte_ip.h>
+#include <rte_rib.h>
+
+#include "test.h"
+#include "test_xmmt_ops.h"
+#include <rte_dir24_8.h>
+
+
+#define TEST_RIB_ASSERT(cond) do {				\
+	if (!(cond)) {						\
+		printf("Error at line %d:\n", __LINE__);	\
+		return -1;					\
+	}							\
+} while (0)
+
+typedef int32_t (*rte_rib_test)(void);
+
+static int32_t test0(void);
+static int32_t test1(void);
+static int32_t test2(void);
+static int32_t test3(void);
+static int32_t test4(void);
+static int32_t test5(void);
+
+static rte_rib_test tests[] = {
+/* Test Cases */
+	test0,
+	test1,
+	test2,
+	test3,
+	test4,
+	test5
+};
+
+#define NUM_RIB_TESTS (sizeof(tests)/sizeof(tests[0]))
+#define MAX_DEPTH 32
+#define MAX_RULES (1 << 22)
+#define NUMBER_TBL8S 4096
+#define PASS 0
+
+/*
+ * Check that rte_rib_create fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test0(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf config;
+
+	config.type = RTE_RIB_DIR24_8;
+	config.max_nodes = MAX_RULES;
+	config.node_sz = sizeof(struct rte_rib_node);
+	config.fib_conf.dir24_8.nh_sz = RTE_DIR24_8_4B;
+	config.fib_conf.dir24_8.num_tbl8 = NUMBER_TBL8S;
+	config.fib_conf.dir24_8.def_nh = 0;
+
+	/* rte_rib_create: rib name == NULL */
+	rib = rte_rib_create(NULL, SOCKET_ID_ANY, &config);
+	TEST_RIB_ASSERT(rib == NULL);
+
+	/* rte_rib_create: config == NULL */
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, NULL);
+	TEST_RIB_ASSERT(rib == NULL);
+
+	/* socket_id < -1 is invalid */
+	rib = rte_rib_create(__func__, -2, &config);
+	TEST_RIB_ASSERT(rib == NULL);
+
+	/* rte_rib_create: max_nodes = 0 */
+	config.max_nodes = 0;
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_RIB_ASSERT(rib == NULL);
+	config.max_nodes = MAX_RULES;
+
+	/* rte_rib_create: node_sz = 0 */
+	config.node_sz = 0;
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_RIB_ASSERT(rib == NULL);
+	config.node_sz = sizeof(struct rte_rib_node);
+
+	/* rte_rib_create: invalid type */
+	config.type = RTE_RIB_TYPE_MAX;
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_RIB_ASSERT(rib == NULL);
+	config.type = RTE_RIB_DIR24_8;
+
+	/* rte_rib_create: invalid fib type */
+	config.fib_conf.dir24_8.nh_sz = 10;
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_RIB_ASSERT(rib == NULL);
+	config.fib_conf.dir24_8.nh_sz = RTE_DIR24_8_4B;
+
+	/** rte_rib_create: invalid default next hop */
+	config.fib_conf.dir24_8.def_nh = UINT32_MAX;
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_RIB_ASSERT(rib == NULL);
+	config.fib_conf.dir24_8.def_nh = 0;
+
+	config.fib_conf.dir24_8.num_tbl8 = UINT32_MAX;
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_RIB_ASSERT(rib == NULL);
+
+	return PASS;
+}
+
+/*
+ * Create rib table then delete rib table 10 times
+ * Use a slightly different rules size each time
+ */
+int32_t
+test1(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf config;
+	int32_t i, j;
+
+	config.node_sz = sizeof(struct rte_rib_node);
+	config.type = RTE_RIB_DIR24_8;
+	config.fib_conf.dir24_8.def_nh = 0;
+	config.fib_conf.dir24_8.num_tbl8 = 127;
+
+	for (j = 0; j < 4; j++) {
+		config.fib_conf.dir24_8.nh_sz = j;
+		/* rte_rib_free: Free NULL */
+		for (i = 0; i < 2; i++) {
+			config.max_nodes = MAX_RULES - i;
+			rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+			TEST_RIB_ASSERT(rib != NULL);
+			rte_rib_free(rib);
+		}
+	}
+	/* Can not test free so return success */
+	return PASS;
+}
+
+/*
+ * Call rte_rib_free for NULL pointer user input. Note: free has no return and
+ * therefore it is impossible to check for failure but this test is added to
+ * increase function coverage metrics and to validate that freeing null does
+ * not crash.
+ */
+int32_t
+test2(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf config;
+
+	config.type = RTE_RIB_DIR24_8;
+	config.max_nodes = MAX_RULES;
+	config.node_sz = sizeof(struct rte_rib_node);
+	config.fib_conf.dir24_8.nh_sz = RTE_DIR24_8_4B;
+	config.fib_conf.dir24_8.num_tbl8 = NUMBER_TBL8S;
+	config.fib_conf.dir24_8.def_nh = 0;
+
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_RIB_ASSERT(rib != NULL);
+
+	rte_rib_free(rib);
+	rte_rib_free(NULL);
+	return PASS;
+}
+
+/*
+ * Check that rte_rib_add fails gracefully for incorrect user input arguments
+ */
+int32_t
+test3(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf config;
+	uint32_t ip = IPv4(0, 0, 0, 0);
+	uint64_t next_hop = 100;
+	uint8_t depth = 24;
+	int32_t status = 0;
+
+	config.type = RTE_RIB_DIR24_8;
+	config.max_nodes = MAX_RULES;
+	config.node_sz = sizeof(struct rte_rib_node);
+	config.fib_conf.dir24_8.nh_sz = RTE_DIR24_8_4B;
+	config.fib_conf.dir24_8.num_tbl8 = NUMBER_TBL8S;
+	config.fib_conf.dir24_8.def_nh = 0;
+
+	/* rte_rib_add: rib == NULL */
+	status = rte_rib_add(NULL, ip, depth, next_hop);
+	TEST_RIB_ASSERT(status < 0);
+
+	/*Create valid rib to use in rest of test. */
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_RIB_ASSERT(rib != NULL);
+
+	/* rte_rib_add: depth > MAX_DEPTH */
+	status = rte_rib_add(rib, ip, (MAX_DEPTH + 1), next_hop);
+	TEST_RIB_ASSERT(status < 0);
+
+	rte_rib_free(rib);
+
+	return PASS;
+}
+
+/*
+ * Check that rte_rib_delete fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test4(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf config;
+	uint32_t ip = IPv4(0, 0, 0, 0);
+	uint8_t depth = 24;
+	int32_t status = 0;
+
+	config.type = RTE_RIB_DIR24_8;
+	config.max_nodes = MAX_RULES;
+	config.node_sz = sizeof(struct rte_rib_node);
+	config.fib_conf.dir24_8.nh_sz = RTE_DIR24_8_4B;
+	config.fib_conf.dir24_8.num_tbl8 = NUMBER_TBL8S;
+	config.fib_conf.dir24_8.def_nh = 0;
+
+	/* rte_rib_delete: rib == NULL */
+	status = rte_rib_delete(NULL, ip, depth);
+	TEST_RIB_ASSERT(status < 0);
+
+	/*Create valid rib to use in rest of test. */
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_RIB_ASSERT(rib != NULL);
+
+	/* rte_rib_delete: depth > MAX_DEPTH */
+	status = rte_rib_delete(rib, ip, (MAX_DEPTH + 1));
+	TEST_RIB_ASSERT(status < 0);
+
+	rte_rib_free(rib);
+
+	return PASS;
+}
+
+/*
+ * Call add, lookup and delete for a single rule with depth <= 24
+ */
+int32_t
+test5(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf config;
+
+	uint32_t ip = IPv4(190, 2, 0, 0);
+	uint64_t next_hop_add = 10;
+	uint64_t next_hop_return = 20;
+	uint64_t next_hop_default = 14;
+	uint8_t depth = 24;
+	uint32_t status = 0;
+
+	config.type = RTE_RIB_DIR24_8;
+	config.max_nodes = MAX_RULES;
+	config.node_sz = sizeof(struct rte_rib_node);
+	config.fib_conf.dir24_8.nh_sz = RTE_DIR24_8_4B;
+	config.fib_conf.dir24_8.num_tbl8 = NUMBER_TBL8S;
+	config.fib_conf.dir24_8.def_nh = next_hop_default;
+
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_RIB_ASSERT(rib != NULL);
+
+	status = rte_rib_add(rib, ip, depth, next_hop_add);
+	TEST_RIB_ASSERT(status == 0);
+
+	status = rte_rib_fib_lookup_bulk(rib, &ip, &next_hop_return, 1);
+	TEST_RIB_ASSERT((status == 0) && (next_hop_return == next_hop_add));
+
+	status = rte_rib_delete(rib, ip, depth);
+	TEST_RIB_ASSERT(status == 0);
+	status = rte_rib_fib_lookup_bulk(rib, &ip, &next_hop_return, 1);
+	TEST_RIB_ASSERT(next_hop_return == next_hop_default);
+
+	rte_rib_free(rib);
+
+	return PASS;
+}
+
+/*
+ * Do all unit tests.
+ */
+static int
+test_rib(void)
+{
+	unsigned int i;
+	int status, global_status = 0;
+
+	for (i = 0; i < NUM_RIB_TESTS; i++) {
+		status = tests[i]();
+		if (status < 0) {
+			printf("ERROR: RIB Test %u: FAIL\n", i);
+			global_status = status;
+		}
+	}
+
+	return global_status;
+}
+
+REGISTER_TEST_COMMAND(rib_autotest, test_rib);
diff --git a/test/test/test_rib_generate_rt.c b/test/test/test_rib_generate_rt.c
new file mode 100644
index 0000000..36834ed
--- /dev/null
+++ b/test/test/test_rib_generate_rt.c
@@ -0,0 +1,297 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <rte_random.h>
+#include <rte_cycles.h>
+#include <rte_ip.h>
+
+#include "test_rib_generate_rt.h"
+
+static uint32_t max_route_entries;
+static uint32_t num_route_entries;
+
+/* All following numbers of each depth of each common IP class are just
+ * got from previous large constant table in app/test/test_rib_routes.h .
+ * In order to match similar performance, they keep same depth and IP
+ * address coverage as previous constant table. These numbers don't
+ * include any private local IP address. As previous large const rule
+ * table was just dumped from a real router, there are no any IP address
+ * in class C or D.
+ */
+static struct route_rule_count rule_count = {
+	.a = { /* IP class A in which the most significant bit is 0 */
+		    0, /* depth =  1 */
+		    0, /* depth =  2 */
+		    1, /* depth =  3 */
+		    0, /* depth =  4 */
+		    2, /* depth =  5 */
+		    1, /* depth =  6 */
+		    3, /* depth =  7 */
+		  185, /* depth =  8 */
+		   26, /* depth =  9 */
+		   16, /* depth = 10 */
+		   39, /* depth = 11 */
+		  144, /* depth = 12 */
+		  233, /* depth = 13 */
+		  528, /* depth = 14 */
+		  866, /* depth = 15 */
+		 3856, /* depth = 16 */
+		 3268, /* depth = 17 */
+		 5662, /* depth = 18 */
+		17301, /* depth = 19 */
+		22226, /* depth = 20 */
+		11147, /* depth = 21 */
+		16746, /* depth = 22 */
+		17120, /* depth = 23 */
+		77578, /* depth = 24 */
+		  401, /* depth = 25 */
+		  656, /* depth = 26 */
+		 1107, /* depth = 27 */
+		 1121, /* depth = 28 */
+		 2316, /* depth = 29 */
+		  717, /* depth = 30 */
+		   10, /* depth = 31 */
+		   66  /* depth = 32 */
+	},
+	.b = { /* IP class A in which the most 2 significant bits are 10 */
+		    0, /* depth =  1 */
+		    0, /* depth =  2 */
+		    0, /* depth =  3 */
+		    0, /* depth =  4 */
+		    1, /* depth =  5 */
+		    1, /* depth =  6 */
+		    1, /* depth =  7 */
+		    3, /* depth =  8 */
+		    3, /* depth =  9 */
+		   30, /* depth = 10 */
+		   25, /* depth = 11 */
+		  168, /* depth = 12 */
+		  305, /* depth = 13 */
+		  569, /* depth = 14 */
+		 1129, /* depth = 15 */
+		50800, /* depth = 16 */
+		 1645, /* depth = 17 */
+		 1820, /* depth = 18 */
+		 3506, /* depth = 19 */
+		 3258, /* depth = 20 */
+		 3424, /* depth = 21 */
+		 4971, /* depth = 22 */
+		 6885, /* depth = 23 */
+		39771, /* depth = 24 */
+		  424, /* depth = 25 */
+		  170, /* depth = 26 */
+		  443, /* depth = 27 */
+		   92, /* depth = 28 */
+		  366, /* depth = 29 */
+		  377, /* depth = 30 */
+		    2, /* depth = 31 */
+		  200  /* depth = 32 */
+	},
+	.c = { /* IP class A in which the most 3 significant bits are 110 */
+		     0, /* depth =  1 */
+		     0, /* depth =  2 */
+		     0, /* depth =  3 */
+		     0, /* depth =  4 */
+		     0, /* depth =  5 */
+		     0, /* depth =  6 */
+		     0, /* depth =  7 */
+		    12, /* depth =  8 */
+		     8, /* depth =  9 */
+		     9, /* depth = 10 */
+		    33, /* depth = 11 */
+		    69, /* depth = 12 */
+		   237, /* depth = 13 */
+		  1007, /* depth = 14 */
+		  1717, /* depth = 15 */
+		 14663, /* depth = 16 */
+		  8070, /* depth = 17 */
+		 16185, /* depth = 18 */
+		 48261, /* depth = 19 */
+		 36870, /* depth = 20 */
+		 33960, /* depth = 21 */
+		 50638, /* depth = 22 */
+		 61422, /* depth = 23 */
+		466549, /* depth = 24 */
+		  1829, /* depth = 25 */
+		  4824, /* depth = 26 */
+		  4927, /* depth = 27 */
+		  5914, /* depth = 28 */
+		 10254, /* depth = 29 */
+		  4905, /* depth = 30 */
+		     1, /* depth = 31 */
+		   716  /* depth = 32 */
+	}
+};
+
+static void generate_random_rule_prefix(struct route_rule *rt,
+	uint32_t ip_class, uint8_t depth)
+{
+/* IP address class A, the most significant bit is 0 */
+#define IP_HEAD_MASK_A			0x00000000
+#define IP_HEAD_BIT_NUM_A		1
+
+/* IP address class B, the most significant 2 bits are 10 */
+#define IP_HEAD_MASK_B			0x80000000
+#define IP_HEAD_BIT_NUM_B		2
+
+/* IP address class C, the most significant 3 bits are 110 */
+#define IP_HEAD_MASK_C			0xC0000000
+#define IP_HEAD_BIT_NUM_C		3
+
+	uint32_t class_depth;
+	uint32_t range;
+	uint32_t mask;
+	uint32_t step;
+	uint32_t start;
+	uint32_t fixed_bit_num;
+	uint32_t ip_head_mask;
+	uint32_t rule_num;
+	uint32_t k;
+	struct route_rule *ptr_rule;
+
+	if (ip_class == IP_CLASS_A) {        /* IP Address class A */
+		fixed_bit_num = IP_HEAD_BIT_NUM_A;
+		ip_head_mask = IP_HEAD_MASK_A;
+		rule_num = rule_count.a[depth - 1];
+	} else if (ip_class == IP_CLASS_B) { /* IP Address class B */
+		fixed_bit_num = IP_HEAD_BIT_NUM_B;
+		ip_head_mask = IP_HEAD_MASK_B;
+		rule_num = rule_count.b[depth - 1];
+	} else {                             /* IP Address class C */
+		fixed_bit_num = IP_HEAD_BIT_NUM_C;
+		ip_head_mask = IP_HEAD_MASK_C;
+		rule_num = rule_count.c[depth - 1];
+	}
+
+	if ((rule_num == 0) || ((num_route_entries + rule_num) >=
+		max_route_entries))
+		return;
+
+	/* the number of rest bits which don't include the most significant
+	 * fixed bits for this IP address class
+	 */
+	class_depth = depth - fixed_bit_num;
+
+	/* range is the maximum number of rules for this depth and
+	 * this IP address class
+	 */
+	range = 1 << class_depth;
+
+	/* only mask the most depth significant generated bits
+	 * except fixed bits for IP address class
+	 */
+	mask = range - 1;
+
+	/* Widen coverage of IP address in generated rules */
+	if (range <= rule_num)
+		step = 1;
+	else
+		step = round((double)range / rule_num);
+
+	/* Only generate rest bits except the most significant
+	 * fixed bits for IP address class
+	 */
+	start = rte_rand() & mask;
+	ptr_rule = &rt[num_route_entries];
+	for (k = 0; k < rule_num; k++) {
+		ptr_rule->ip = (start << (RTE_RIB_MAXDEPTH - depth))
+			| ip_head_mask;
+		ptr_rule->depth = depth;
+		ptr_rule++;
+		start = (start + step) & mask;
+	}
+	num_route_entries += rule_num;
+}
+
+static void insert_rule_in_random_pos(struct route_rule *rt,
+	uint32_t ip, uint8_t depth)
+{
+	uint32_t pos;
+	int try_count = 0;
+	struct route_rule tmp;
+
+	do {
+		pos = rte_rand();
+		try_count++;
+	} while ((try_count < 10) && (pos > num_route_entries));
+
+	if ((pos > num_route_entries) || (pos > max_route_entries))
+		pos = num_route_entries >> 1;
+
+	tmp = rt[pos];
+	rt[pos].ip = ip;
+	rt[pos].depth = depth;
+	if (num_route_entries < max_route_entries)
+		rt[num_route_entries++] = tmp;
+}
+
+uint32_t
+generate_large_route_rule_table(uint32_t num_routes, struct route_rule *rt)
+{
+	uint32_t ip_class;
+	uint8_t  depth;
+
+	rte_srand(rte_rdtsc());
+	num_route_entries = 0;
+	max_route_entries = num_routes;
+	for (ip_class = IP_CLASS_A; ip_class <= IP_CLASS_C; ip_class++) {
+		for (depth = 1; depth <= RTE_RIB_MAXDEPTH; depth++)
+			generate_random_rule_prefix(rt, ip_class, depth);
+	}
+	/* Add following rules to keep same as previous large constant table,
+	 * they are 4 rules with private local IP address and 1 all-zeros prefix
+	 * with depth = 8.
+	 */
+	insert_rule_in_random_pos(rt, IPv4(0, 0, 0, 0), 8);
+	insert_rule_in_random_pos(rt, IPv4(10, 2, 23, 147), 32);
+	insert_rule_in_random_pos(rt, IPv4(192, 168, 100, 10), 24);
+	insert_rule_in_random_pos(rt, IPv4(192, 168, 25, 100), 24);
+	insert_rule_in_random_pos(rt, IPv4(192, 168, 129, 124), 32);
+
+	return num_route_entries;
+}
+
+void
+print_route_distribution(const struct route_rule *table, uint32_t n)
+{
+	unsigned int i, j;
+
+	printf("Route distribution per prefix width:\n");
+	printf("DEPTH    QUANTITY (PERCENT)\n");
+	printf("---------------------------\n");
+
+	/* Count depths. */
+	for (i = 1; i <= 32; i++) {
+		unsigned int depth_counter = 0;
+		double percent_hits;
+
+		for (j = 0; j < n; j++)
+			if (table[j].depth == (uint8_t) i)
+				depth_counter++;
+
+		percent_hits = ((double)depth_counter)/((double)n) * 100;
+		printf("%.2u%15u (%.2f)\n", i, depth_counter, percent_hits);
+	}
+	printf("\n");
+}
+
+void
+shuffle_rt(struct route_rule *rt, uint32_t n)
+{
+	uint32_t pos;
+	struct route_rule tmp;
+	uint32_t i;
+
+	for (i = 0; i < n; i++) {
+		pos = rte_rand() % n;
+		tmp = rt[pos];
+		rt[pos] = rt[i];
+		rt[i] = tmp;
+	}
+}
diff --git a/test/test/test_rib_generate_rt.h b/test/test/test_rib_generate_rt.h
new file mode 100644
index 0000000..90573c7
--- /dev/null
+++ b/test/test/test_rib_generate_rt.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ */
+
+#ifndef _TEST_RIB_GENERATE_RT_H_
+#define _TEST_RIB_GENERATE_RT_H_
+
+#define RTE_RIB_MAXDEPTH	32
+
+struct route_rule {
+	uint64_t nh;
+	uint32_t ip;
+	uint8_t depth;
+};
+
+enum {
+	IP_CLASS_A,
+	IP_CLASS_B,
+	IP_CLASS_C
+};
+
+/* struct route_rule_count defines the total number of rules in following a/b/c
+ * each item in a[]/b[]/c[] is the number of common IP address class A/B/C, not
+ * including the ones for private local network.
+ */
+struct route_rule_count {
+	uint32_t a[RTE_RIB_MAXDEPTH];
+	uint32_t b[RTE_RIB_MAXDEPTH];
+	uint32_t c[RTE_RIB_MAXDEPTH];
+};
+
+
+uint32_t generate_large_route_rule_table(uint32_t num_routes,
+	struct route_rule *rt);
+void print_route_distribution(const struct route_rule *table, uint32_t n);
+void shuffle_rt(struct route_rule *rt, uint32_t n);
+
+#endif /* _TEST_RIB_GENERATE_RT_H_ */
diff --git a/test/test/test_rib_lpm_comp.c b/test/test/test_rib_lpm_comp.c
new file mode 100644
index 0000000..ef48c8c
--- /dev/null
+++ b/test/test/test_rib_lpm_comp.c
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <rte_random.h>
+#include <rte_cycles.h>
+#include <rte_branch_prediction.h>
+#include <rte_ip.h>
+#include <rte_malloc.h>
+#include <rte_lpm.h>
+#include <rte_rib.h>
+
+#include "test.h"
+#include "test_xmmt_ops.h"
+#include "test_rib_generate_rt.h"
+
+#define TEST_RIB_ASSERT(cond) do {				\
+	if (!(cond)) {						\
+		printf("Error at line %d:\n", __LINE__);	\
+		return -1;					\
+	}							\
+} while (0)
+
+#define ITERATIONS (1 << 25)
+#define BATCH_SIZE (1 << 7)
+#define BULK_SIZE 32
+#define LPM_NH_MASK	((1 << 24) - 1)
+
+static uint64_t default_nh;
+
+static int
+test_lookup(struct rte_rib *rib, struct rte_lpm *lpm)
+{
+	static uint32_t ip_batch[BATCH_SIZE];
+	uint64_t rib_next_hops[BULK_SIZE];
+	uint32_t lpm_next_hops[BULK_SIZE];
+	int i, j, k;
+
+	for (i = 0; i < ITERATIONS; i++) {
+		for (j = 0; j < BATCH_SIZE; j++)
+			ip_batch[j] = (i << 7) + j;
+
+		for (j = 0; j < BATCH_SIZE; j += BULK_SIZE) {
+			rte_rib_fib_lookup_bulk(rib, &ip_batch[j],
+				rib_next_hops, BULK_SIZE);
+			rte_lpm_lookup_bulk(lpm, &ip_batch[j],
+				lpm_next_hops, BULK_SIZE);
+			for (k = 0; k < BULK_SIZE; k++) {
+				if (likely(lpm_next_hops[k] &
+					RTE_LPM_LOOKUP_SUCCESS))
+					lpm_next_hops[k] &= LPM_NH_MASK;
+				else
+					lpm_next_hops[k] = default_nh;
+			}
+			for (k = 0; k < BULK_SIZE; k++)
+				TEST_RIB_ASSERT(rib_next_hops[k] ==
+						lpm_next_hops[k]);
+		}
+	}
+	return 0;
+}
+
+static int
+test_rib_lpm_comp(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_lpm *lpm = NULL;
+	struct route_rule *rt = NULL;
+	unsigned int i;
+	int rib_add = 0, lpm_add = 0;
+	int ret, nh_bits, nr_tbl8;
+	uint32_t num_routes;
+	struct rte_rib_conf conf;
+	struct rte_lpm_config config;
+
+	rte_srand(rte_rdtsc());
+	default_nh = 17;
+
+	conf.max_nodes = 3000000;
+	conf.node_sz = sizeof(struct rte_rib_node);
+	conf.type = RTE_RIB_DIR24_8;
+	conf.fib_conf.dir24_8.def_nh = default_nh;
+	conf.fib_conf.dir24_8.nh_sz = RTE_DIR24_8_8B;
+
+	nh_bits = RTE_MIN(((1 << (3 + conf.fib_conf.dir24_8.nh_sz)) - 1), 24);
+	nr_tbl8 = RTE_MIN(((1 << nh_bits) - 1), 65535);
+	config.number_tbl8s = nr_tbl8;
+	conf.fib_conf.dir24_8.num_tbl8 = nr_tbl8;
+	config.max_rules = 2000000;
+	config.flags = 0;
+
+	num_routes = 1200000;
+
+	rt = rte_zmalloc("struct route_rule *", sizeof(struct route_rule) *
+		num_routes + 5, 0);
+	TEST_RIB_ASSERT(rt != NULL);
+
+	num_routes = generate_large_route_rule_table(num_routes, rt);
+	TEST_RIB_ASSERT(num_routes != 0);
+	printf("No. routes = %u\n", (unsigned int) num_routes);
+
+	shuffle_rt(rt, num_routes);
+
+	print_route_distribution(rt, (uint32_t) num_routes);
+
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &conf);
+	TEST_RIB_ASSERT(rib != NULL);
+
+	lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_RIB_ASSERT(lpm != NULL);
+
+	for (i = 0; i < num_routes; i++)
+		rt[i].nh = rte_rand() & ((1ULL << nh_bits) - 1);
+
+	for (i = 0; i < num_routes; i++) {
+		ret = rte_rib_add(rib, rt[i].ip, rt[i].depth, rt[i].nh);
+		if (ret == 0)
+			rib_add++;
+		else
+			continue;
+
+		ret = rte_lpm_add(lpm, rt[i].ip, rt[i].depth, rt[i].nh);
+		if (ret == 0)
+			lpm_add++;
+		else {
+			rte_rib_delete(rib, rt[i].ip, rt[i].depth);
+			rib_add--;
+		}
+	}
+	TEST_RIB_ASSERT(rib_add == lpm_add);
+
+	ret = test_lookup(rib, lpm);
+	if (ret != 0)
+		return ret;
+
+	for (i = 0; i < num_routes; i++) {
+		if ((i % 3) == 0) {
+			ret = rte_rib_delete(rib, rt[i].ip, rt[i].depth);
+			if (ret == 0)
+				rib_add--;
+			else
+				continue;
+
+			ret = rte_lpm_delete(lpm, rt[i].ip, rt[i].depth);
+			if (ret == 0)
+				lpm_add--;
+		}
+	}
+	TEST_RIB_ASSERT(rib_add == lpm_add);
+
+	ret = test_lookup(rib, lpm);
+	if (ret != 0)
+		return ret;
+
+	for (i = 0; i < num_routes; i++) {
+		if ((i % 6) == 0) {
+			ret = rte_rib_add(rib, rt[i].ip, rt[i].depth, rt[i].nh);
+			if (ret == 0)
+				rib_add++;
+			else
+				continue;
+
+			ret = rte_lpm_add(lpm, rt[i].ip, rt[i].depth, rt[i].nh);
+			if (ret == 0)
+				lpm_add++;
+			else {
+				rte_rib_delete(rib, rt[i].ip, rt[i].depth);
+				rib_add--;
+			}
+		}
+	}
+	TEST_RIB_ASSERT(rib_add == lpm_add);
+
+	ret = test_lookup(rib, lpm);
+	if (ret != 0)
+		return ret;
+
+	rte_rib_free(rib);
+	rte_lpm_free(lpm);
+	rte_free(rt);
+
+	return 0;
+}
+
+REGISTER_TEST_COMMAND(rib_lpm_comp_autotest, test_rib_lpm_comp);
diff --git a/test/test/test_rib_perf.c b/test/test/test_rib_perf.c
new file mode 100644
index 0000000..42fbd1e
--- /dev/null
+++ b/test/test/test_rib_perf.c
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <rte_cycles.h>
+#include <rte_random.h>
+#include <rte_branch_prediction.h>
+#include <rte_ip.h>
+#include <rte_malloc.h>
+#include <rte_rib.h>
+#include <rte_dir24_8.h>
+
+#include "test.h"
+#include "test_xmmt_ops.h"
+#include "test_rib_generate_rt.h"
+
+#define TEST_RIB_ASSERT(cond) do {				\
+	if (!(cond)) {						\
+		printf("Error at line %d:\n", __LINE__);	\
+		return -1;					\
+	}							\
+} while (0)
+
+#define ITERATIONS (1 << 15)
+#define BATCH_SIZE (1 << 12)
+#define BULK_SIZE 32
+
+#define NH_MSK(nh_sz)	((1ULL << ((1 << (3 + nh_sz)) - 1)) - 1)
+
+static int
+test_rib_perf(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf conf;
+	struct route_rule *rt;
+	uint64_t begin, total_time;
+	uint64_t next_hop_add;
+	uint64_t default_nh = 0;
+	int64_t count = 0;
+	unsigned int i, j;
+	int status = 0;
+	int ret, nh_bits, nr_tbl8;
+	uint32_t num_routes;
+
+	conf.max_nodes = 3000000;
+	conf.node_sz = sizeof(struct rte_rib_node);
+	conf.type = RTE_RIB_DIR24_8;
+	conf.fib_conf.dir24_8.def_nh = default_nh;
+	conf.fib_conf.dir24_8.nh_sz = RTE_DIR24_8_8B;
+
+	rte_srand(rte_rdtsc());
+
+	nh_bits = RTE_MIN(((1 << (3 + conf.fib_conf.dir24_8.nh_sz)) - 1), 24);
+	nr_tbl8 = RTE_MIN(((1 << nh_bits) - 1), 131071);
+	conf.fib_conf.dir24_8.num_tbl8 = nr_tbl8;
+	num_routes = 1200000;
+
+	rt = rte_zmalloc("struct route_rule *", sizeof(struct route_rule) *
+		num_routes, 0);
+	TEST_RIB_ASSERT(rt != NULL);
+
+	num_routes = generate_large_route_rule_table(num_routes, rt);
+	TEST_RIB_ASSERT(num_routes != 0);
+
+	printf("No. routes = %u\n", (unsigned int) num_routes);
+
+	shuffle_rt(rt, num_routes);
+
+	print_route_distribution(rt, (uint32_t) num_routes);
+
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &conf);
+	TEST_RIB_ASSERT(rib != NULL);
+
+	/* Measue add. */
+	begin = rte_rdtsc();
+
+	for (i = 0; i < num_routes; i++) {
+		do {
+			next_hop_add = rte_rand() & NH_MSK(conf.fib_conf.dir24_8.nh_sz);
+		} while (next_hop_add == default_nh);
+
+		ret = rte_rib_add(rib, rt[i].ip, rt[i].depth, next_hop_add);
+		if ((ret == 0))
+			status++;
+	}
+
+	total_time = rte_rdtsc() - begin;
+
+	printf("Unique added entries = %d\n", status);
+	printf("Average RIB Add: %g cycles\n",
+			(double)total_time / num_routes);
+
+	/* Measure bulk Lookup */
+	total_time = 0;
+	count = 0;
+	for (i = 0; i < ITERATIONS; i++) {
+		static uint32_t ip_batch[BATCH_SIZE];
+		uint64_t next_hops[BULK_SIZE];
+
+		/* Create array of random IP addresses */
+		for (j = 0; j < BATCH_SIZE; j++)
+			ip_batch[j] = rte_rand();
+
+		/* Lookup per batch */
+		begin = rte_rdtsc();
+		for (j = 0; j < BATCH_SIZE; j += BULK_SIZE)
+			rte_rib_fib_lookup_bulk(rib, &ip_batch[j], next_hops,
+				BULK_SIZE);
+
+		total_time += rte_rdtsc() - begin;
+		for (j = 0; j < BULK_SIZE; j++) {
+			if (next_hops[j] == default_nh)
+				count++;
+		}
+	}
+	printf("BULK RIB Lookup: %.1f cycles (fails = %.1f%%)\n",
+			(double)total_time / ((double)ITERATIONS * BATCH_SIZE),
+			(count * 100.0) / (double)(ITERATIONS * BATCH_SIZE));
+
+	/* Delete */
+	status = 0;
+	begin = rte_rdtsc();
+
+	for (i = 0; i < num_routes; i++) {
+		ret = rte_rib_delete(rib, rt[i].ip, rt[i].depth);
+		if (ret == 0)
+			status++;
+	}
+
+	total_time = rte_rdtsc() - begin;
+
+	printf("Average RIB Delete: %g cycles\n",
+			(double)total_time / num_routes);
+
+	rte_rib_free(rib);
+	rte_free(rt);
+
+	return 0;
+}
+
+REGISTER_TEST_COMMAND(rib_perf_autotest, test_rib_perf);
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v4 4/4] Add support for lpm and rib bulk lookup
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (2 preceding siblings ...)
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 3/4] Add autotests for RIB library Medvedkin Vladimir
@ 2018-04-26 22:03 ` Medvedkin Vladimir
  2018-04-26 22:24 ` [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Stephen Hemminger
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Medvedkin Vladimir @ 2018-04-26 22:03 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, thomas, cristian.dumitrescu, Medvedkin Vladimir

Signed-off-by: Medvedkin Vladimir <medvedkinv@gmail.com>
---
 examples/l3fwd/l3fwd_lpm.c | 132 +++++++++++++++++++++++++++++++++++++++++++++
 examples/l3fwd/meson.build |   2 +-
 2 files changed, 133 insertions(+), 1 deletion(-)

diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index a747126..9ad1ddc 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -24,9 +24,12 @@
 #include <rte_tcp.h>
 #include <rte_udp.h>
 #include <rte_lpm.h>
+#include <rte_rib.h>
 #include <rte_lpm6.h>
 
 #include "l3fwd.h"
+#define BULK_LOOKUP
+#define USE_RIB
 
 struct ipv4_l3fwd_lpm_route {
 	uint32_t ip;
@@ -72,7 +75,11 @@ struct ipv6_l3fwd_lpm_route {
 #define IPV6_L3FWD_LPM_MAX_RULES         1024
 #define IPV6_L3FWD_LPM_NUMBER_TBL8S (1 << 16)
 
+#if  defined(BULK_LOOKUP) && defined(USE_RIB)
+struct rte_rib *ipv4_l3fwd_lpm_lookup_struct[NB_SOCKETS];
+#else
 struct rte_lpm *ipv4_l3fwd_lpm_lookup_struct[NB_SOCKETS];
+#endif
 struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS];
 
 static inline uint16_t
@@ -168,6 +175,104 @@ struct ipv6_l3fwd_lpm_route {
 #include "l3fwd_lpm.h"
 #endif
 
+static __rte_always_inline int
+l3fwd_rib_handle_pkt(struct rte_mbuf *m, uint32_t *dst_ip)
+{
+	struct ipv4_hdr *ipv4_hdr;
+
+	if (RTE_ETH_IS_IPV4_HDR(m->packet_type)) {
+		/* Handle IPv4 headers.*/
+		ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct ipv4_hdr *,
+				sizeof(struct ether_hdr));
+#ifdef DO_RFC_1812_CHECKS
+		/* Check to make sure the packet is valid (RFC1812) */
+		if (is_valid_ipv4_pkt(ipv4_hdr, m->pkt_len) < 0)
+			return -1;
+		/* Update time to live and header checksum */
+		--(ipv4_hdr->time_to_live);
+		++(ipv4_hdr->hdr_checksum);
+#endif
+		*dst_ip = rte_be_to_cpu_32(ipv4_hdr->dst_addr);
+	} else
+		return -1;
+
+	return 0;
+}
+
+static inline void
+l3fwd_rib_send_packets(int nb_rx, struct rte_mbuf **pkts_burst,
+			uint16_t portid, struct lcore_conf *qconf)
+{
+	struct rte_mbuf *send_pkts[MAX_PKT_BURST];
+#ifdef USE_RIB
+	uint64_t next_hops[MAX_PKT_BURST];
+#else
+	uint32_t next_hops[MAX_PKT_BURST];
+#endif
+	uint32_t dst_ips[MAX_PKT_BURST];
+#ifdef USE_RIB
+	struct rte_rib *rib = qconf->ipv4_lookup_struct;
+#else
+	struct rte_lpm *rib = qconf->ipv4_lookup_struct;
+#endif
+	struct ether_hdr *eth_hdr;
+        int32_t j, k = 0;
+	int ret;
+	uint16_t dst_port;
+
+	for (j = 0; j < PREFETCH_OFFSET && j < nb_rx; j++)
+		rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[j], void *));
+
+	for (j = 0; j < (nb_rx - PREFETCH_OFFSET); j++) {
+		rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[
+			j + PREFETCH_OFFSET], void *));
+
+		ret = l3fwd_rib_handle_pkt(pkts_burst[j], &dst_ips[k]);
+		if (ret != 0) {
+			rte_pktmbuf_free(pkts_burst[j]);
+			continue;
+		}
+		send_pkts[k++] = pkts_burst[j];
+        }
+
+        for (; j < nb_rx; j++) {
+		ret = l3fwd_rib_handle_pkt(pkts_burst[j], &dst_ips[k]);
+		if (ret != 0) {
+			rte_pktmbuf_free(pkts_burst[j]);
+			continue;
+		}
+		send_pkts[k++] = pkts_burst[j];
+	}
+
+#ifdef USE_RIB
+	ret = rte_rib_fib_lookup_bulk(rib, dst_ips, next_hops, k);
+#else
+	ret = rte_lpm_lookup_bulk(rib, dst_ips, next_hops, k);
+#endif
+	if (ret != 0) {
+		for (j = 0; j < k; j++)
+			rte_pktmbuf_free(send_pkts[j]);
+	} else {
+		for (j = 0; j < k; j++) {
+#ifdef USE_RIB
+			dst_port = next_hops[j];
+#else
+			dst_port = next_hops[j] & 0x00FFFFFF;
+#endif
+			if (dst_port >= RTE_MAX_ETHPORTS ||
+				(enabled_port_mask & 1 << dst_port) == 0) {
+				dst_port = portid;
+			}
+			eth_hdr = rte_pktmbuf_mtod(send_pkts[j], struct ether_hdr *);
+			/* dst addr */
+			*(uint64_t *)&eth_hdr->d_addr = dest_eth_addr[dst_port];
+			/* src addr */
+			ether_addr_copy(&ports_eth_addr[dst_port], &eth_hdr->s_addr);
+			send_single_packet(qconf, send_pkts[j], dst_port);
+		}
+	}
+}
+
 /* main processing loop */
 int
 lpm_main_loop(__attribute__((unused)) void *dummy)
@@ -237,6 +342,10 @@ struct ipv6_l3fwd_lpm_route {
 			if (nb_rx == 0)
 				continue;
 
+#if defined(BULK_LOOKUP)
+			l3fwd_rib_send_packets(nb_rx, pkts_burst,
+						portid, qconf);
+#else
 #if defined RTE_ARCH_X86 || defined RTE_MACHINE_CPUFLAG_NEON \
 			 || defined RTE_ARCH_PPC_64
 			l3fwd_lpm_send_packets(nb_rx, pkts_burst,
@@ -245,6 +354,7 @@ struct ipv6_l3fwd_lpm_route {
 			l3fwd_lpm_no_opt_send_packets(nb_rx, pkts_burst,
 							portid, qconf);
 #endif /* X86 */
+#endif /* BULK_LOOKUP*/
 		}
 	}
 
@@ -255,18 +365,36 @@ struct ipv6_l3fwd_lpm_route {
 setup_lpm(const int socketid)
 {
 	struct rte_lpm6_config config;
+#if defined(BULK_LOOKUP) && defined(USE_RIB)
+	struct rte_rib_conf config_ipv4;
+#else
 	struct rte_lpm_config config_ipv4;
+#endif
 	unsigned i;
 	int ret;
 	char s[64];
 
 	/* create the LPM table */
+#if defined(BULK_LOOKUP) && defined(USE_RIB)
+	config_ipv4.type = RTE_RIB_DIR24_8;
+	config_ipv4.max_nodes = IPV4_L3FWD_LPM_MAX_RULES;
+	config_ipv4.node_sz = sizeof(struct rte_rib_node);
+	config_ipv4.fib_conf.dir24_8.def_nh = 0;
+	config_ipv4.fib_conf.dir24_8.nh_sz = RTE_DIR24_8_4B;
+	config_ipv4.fib_conf.dir24_8.num_tbl8 = IPV4_L3FWD_LPM_NUMBER_TBL8S;
+#else
 	config_ipv4.max_rules = IPV4_L3FWD_LPM_MAX_RULES;
 	config_ipv4.number_tbl8s = IPV4_L3FWD_LPM_NUMBER_TBL8S;
 	config_ipv4.flags = 0;
+#endif
 	snprintf(s, sizeof(s), "IPV4_L3FWD_LPM_%d", socketid);
+
 	ipv4_l3fwd_lpm_lookup_struct[socketid] =
+#if defined(BULK_LOOKUP) && defined(USE_RIB)
+			rte_rib_create(s, socketid, &config_ipv4);
+#else
 			rte_lpm_create(s, socketid, &config_ipv4);
+#endif
 	if (ipv4_l3fwd_lpm_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
 			"Unable to create the l3fwd LPM table on socket %d\n",
@@ -280,7 +408,11 @@ struct ipv6_l3fwd_lpm_route {
 				enabled_port_mask) == 0)
 			continue;
 
+#if defined(BULK_LOOKUP) && defined(USE_RIB)
+		ret = rte_rib_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
+#else
 		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
+#endif
 			ipv4_l3fwd_lpm_route_array[i].ip,
 			ipv4_l3fwd_lpm_route_array[i].depth,
 			ipv4_l3fwd_lpm_route_array[i].if_out);
diff --git a/examples/l3fwd/meson.build b/examples/l3fwd/meson.build
index cbef07f..f742fb1 100644
--- a/examples/l3fwd/meson.build
+++ b/examples/l3fwd/meson.build
@@ -6,7 +6,7 @@
 # To build this example as a standalone application with an already-installed
 # DPDK instance, use 'make'
 
-deps += ['hash', 'lpm']
+deps += ['hash', 'lpm', 'rib']
 allow_experimental_apis = true
 sources = files(
 	'l3fwd_em.c', 'l3fwd_lpm.c', 'main.c'
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 1/4] Add RIB library
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 1/4] Add RIB library Medvedkin Vladimir
@ 2018-04-26 22:17   ` Stephen Hemminger
  2018-04-26 22:18   ` Stephen Hemminger
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 61+ messages in thread
From: Stephen Hemminger @ 2018-04-26 22:17 UTC (permalink / raw)
  To: Medvedkin Vladimir; +Cc: dev, bruce.richardson, thomas, cristian.dumitrescu

On Fri, 27 Apr 2018 01:03:31 +0300
Medvedkin Vladimir <medvedkinv@gmail.com> wrote:

> +static inline int __attribute__((pure))
> +rte_rib_is_right_node(struct rte_rib_node *node)
> +{
> +	return (node->parent->right == node);
> +}
> +

Minor nit (repeated in several places).
DPDK uses Linux (not BSD) style where extra paranthesis are not needed on return statement.

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 1/4] Add RIB library
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 1/4] Add RIB library Medvedkin Vladimir
  2018-04-26 22:17   ` Stephen Hemminger
@ 2018-04-26 22:18   ` Stephen Hemminger
  2018-04-26 22:19   ` Stephen Hemminger
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 61+ messages in thread
From: Stephen Hemminger @ 2018-04-26 22:18 UTC (permalink / raw)
  To: Medvedkin Vladimir; +Cc: dev, bruce.richardson, thomas, cristian.dumitrescu

On Fri, 27 Apr 2018 01:03:31 +0300
Medvedkin Vladimir <medvedkinv@gmail.com> wrote:

> +/**
> + * Check if prefix1 {key1/depth1}
> + * is covered by prefix2 {key2/depth2}
> + */
> +static inline int __attribute__((pure))
> +rte_rib_is_covered(uint32_t key1, uint8_t depth1, uint32_t key2, uint8_t depth2)
> +{
> +	return ((((key1 ^ key2) & rte_rib_depth_to_mask(depth2)) == 0)
> +		&& (depth1 > depth2));
> +}

Use standard boolean type (bool) for these kind of functions.

Plus you really don't need the  pure attribute for static (or inline) functions
since compiler determines that itself.

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 1/4] Add RIB library
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 1/4] Add RIB library Medvedkin Vladimir
  2018-04-26 22:17   ` Stephen Hemminger
  2018-04-26 22:18   ` Stephen Hemminger
@ 2018-04-26 22:19   ` Stephen Hemminger
  2018-04-26 22:19   ` Stephen Hemminger
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 61+ messages in thread
From: Stephen Hemminger @ 2018-04-26 22:19 UTC (permalink / raw)
  To: Medvedkin Vladimir; +Cc: dev, bruce.richardson, thomas, cristian.dumitrescu

On Fri, 27 Apr 2018 01:03:31 +0300
Medvedkin Vladimir <medvedkinv@gmail.com> wrote:

> +	snprintf(rib->name, sizeof(rib->name), "%s", name);

Use strlcpy

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 1/4] Add RIB library
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 1/4] Add RIB library Medvedkin Vladimir
                     ` (2 preceding siblings ...)
  2018-04-26 22:19   ` Stephen Hemminger
@ 2018-04-26 22:19   ` Stephen Hemminger
  2018-04-26 22:20   ` Stephen Hemminger
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 61+ messages in thread
From: Stephen Hemminger @ 2018-04-26 22:19 UTC (permalink / raw)
  To: Medvedkin Vladimir; +Cc: dev, bruce.richardson, thomas, cristian.dumitrescu

On Fri, 27 Apr 2018 01:03:31 +0300
Medvedkin Vladimir <medvedkinv@gmail.com> wrote:

> rib = (struct rte_rib *)rte_zmalloc_socket(mem_name,
> +		sizeof(struct rte_rib),	RTE_CACHE_LINE_SIZE, socket_id);

Cast of void * pointer is not required in C.

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 1/4] Add RIB library
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 1/4] Add RIB library Medvedkin Vladimir
                     ` (3 preceding siblings ...)
  2018-04-26 22:19   ` Stephen Hemminger
@ 2018-04-26 22:20   ` Stephen Hemminger
  2018-04-27  6:45     ` Vladimir Medvedkin
  2018-06-29 13:54   ` Bruce Richardson
  2018-06-29 14:02   ` Bruce Richardson
  6 siblings, 1 reply; 61+ messages in thread
From: Stephen Hemminger @ 2018-04-26 22:20 UTC (permalink / raw)
  To: Medvedkin Vladimir; +Cc: dev, bruce.richardson, thomas, cristian.dumitrescu

On Fri, 27 Apr 2018 01:03:31 +0300
Medvedkin Vladimir <medvedkinv@gmail.com> wrote:

> +		/**
> +		 * Intermediate node found.
> +		 * Previous rte_rib_tree_lookup_exact() returned NULL
> +		 * but node with proper search criteria is found.
> +		 * Validate intermediate node and return.
> +		 */
> +		if ((key == (*tmp)->key) && (depth == (*tmp)->depth)) {

Comments with /** are intended to be docbook comments.
This is not a docbook comment for a function.

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (3 preceding siblings ...)
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 4/4] Add support for lpm and rib bulk lookup Medvedkin Vladimir
@ 2018-04-26 22:24 ` Stephen Hemminger
  2018-04-26 22:27   ` Thomas Monjalon
  2018-04-27  8:27   ` Vladimir Medvedkin
  2018-06-29 15:48 ` Bruce Richardson
                   ` (13 subsequent siblings)
  18 siblings, 2 replies; 61+ messages in thread
From: Stephen Hemminger @ 2018-04-26 22:24 UTC (permalink / raw)
  To: Medvedkin Vladimir; +Cc: dev, bruce.richardson, thomas, cristian.dumitrescu

On Fri, 27 Apr 2018 01:03:30 +0300
Medvedkin Vladimir <medvedkinv@gmail.com> wrote:

> This patch series introduces new library librte_rib which potentially could
> replace librte_lpm.
> 
> 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.
> 
> v4:
>   fix various bugs
>   make struct rte_rib opaque
>   make inline functions instead of macro
>   remove RTE_RIB_MALLOC node allocation type
>   add new lookup functions
>   remove rte_dir24_8_lookup()
>   add rte_dir24_8_get_lookup()
>   add meson support
>   add fib configuration
> 
> 
> Medvedkin Vladimir (4):
>   Add RIB library
>   Add dir24_8 implementation for rib library
>   Add autotests for RIB library

The existing DPDK LPM code does need more work it does trade space for time.

It does have some advantages though:
	* LPM lookup table is independent of number of routes.
        * LPM lookup table can be lock free reader safe using RCU.

The existing slowness of add and delete was fixed at Vyatta/Brocade by
replacing list with red-black tree. Patches were submitted but never merged.

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library
  2018-04-26 22:24 ` [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Stephen Hemminger
@ 2018-04-26 22:27   ` Thomas Monjalon
  2018-04-26 22:42     ` Stephen Hemminger
  2018-04-26 22:49     ` Stephen Hemminger
  2018-04-27  8:27   ` Vladimir Medvedkin
  1 sibling, 2 replies; 61+ messages in thread
From: Thomas Monjalon @ 2018-04-26 22:27 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Medvedkin Vladimir, dev, bruce.richardson, cristian.dumitrescu

27/04/2018 00:24, Stephen Hemminger:
> The existing slowness of add and delete was fixed at Vyatta/Brocade by
> replacing list with red-black tree. Patches were submitted but never merged.

I don't remember these patches.
Why they were not merged?

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library
  2018-04-26 22:27   ` Thomas Monjalon
@ 2018-04-26 22:42     ` Stephen Hemminger
  2018-04-26 22:49     ` Stephen Hemminger
  1 sibling, 0 replies; 61+ messages in thread
From: Stephen Hemminger @ 2018-04-26 22:42 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Medvedkin Vladimir, dev, bruce.richardson, cristian.dumitrescu

On Fri, 27 Apr 2018 00:27:03 +0200
Thomas Monjalon <thomas@monjalon.net> wrote:

> 27/04/2018 00:24, Stephen Hemminger:
> > The existing slowness of add and delete was fixed at Vyatta/Brocade by
> > replacing list with red-black tree. Patches were submitted but never merged.  
> 
> I don't remember these patches.
> Why they were not merged?
> 
> 

Probably because they caused dependency on -libbsd on Linux?

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library
  2018-04-26 22:27   ` Thomas Monjalon
  2018-04-26 22:42     ` Stephen Hemminger
@ 2018-04-26 22:49     ` Stephen Hemminger
  1 sibling, 0 replies; 61+ messages in thread
From: Stephen Hemminger @ 2018-04-26 22:49 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Medvedkin Vladimir, dev, bruce.richardson, cristian.dumitrescu

On Fri, 27 Apr 2018 00:27:03 +0200
Thomas Monjalon <thomas@monjalon.net> wrote:

> 27/04/2018 00:24, Stephen Hemminger:
> > The existing slowness of add and delete was fixed at Vyatta/Brocade by
> > replacing list with red-black tree. Patches were submitted but never merged.  
> 
> I don't remember these patches.
> Why they were not merged?
> 
> 

http://dpdk.org/dev/patchwork/patch/7981/

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 1/4] Add RIB library
  2018-04-26 22:20   ` Stephen Hemminger
@ 2018-04-27  6:45     ` Vladimir Medvedkin
  0 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2018-04-27  6:45 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: dev, Bruce Richardson, thomas, cristian.dumitrescu

Hi Stephen,

Thanks for comments! Will fix in next version.

2018-04-27 1:20 GMT+03:00 Stephen Hemminger <stephen@networkplumber.org>:

> On Fri, 27 Apr 2018 01:03:31 +0300
> Medvedkin Vladimir <medvedkinv@gmail.com> wrote:
>
> > +             /**
> > +              * Intermediate node found.
> > +              * Previous rte_rib_tree_lookup_exact() returned NULL
> > +              * but node with proper search criteria is found.
> > +              * Validate intermediate node and return.
> > +              */
> > +             if ((key == (*tmp)->key) && (depth == (*tmp)->depth)) {
>
> Comments with /** are intended to be docbook comments.
> This is not a docbook comment for a function.
>



-- 
Regards,
Vladimir

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library
  2018-04-26 22:24 ` [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Stephen Hemminger
  2018-04-26 22:27   ` Thomas Monjalon
@ 2018-04-27  8:27   ` Vladimir Medvedkin
  1 sibling, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2018-04-27  8:27 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: dev, Bruce Richardson, thomas, cristian.dumitrescu

Hi Stephen,


2018-04-27 1:24 GMT+03:00 Stephen Hemminger <stephen@networkplumber.org>:

> On Fri, 27 Apr 2018 01:03:30 +0300
> Medvedkin Vladimir <medvedkinv@gmail.com> wrote:
>
> > This patch series introduces new library librte_rib which potentially
> could
> > replace librte_lpm.
> >
> > 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.
> >
> > v4:
> >   fix various bugs
> >   make struct rte_rib opaque
> >   make inline functions instead of macro
> >   remove RTE_RIB_MALLOC node allocation type
> >   add new lookup functions
> >   remove rte_dir24_8_lookup()
> >   add rte_dir24_8_get_lookup()
> >   add meson support
> >   add fib configuration
> >
> >
> > Medvedkin Vladimir (4):
> >   Add RIB library
> >   Add dir24_8 implementation for rib library
> >   Add autotests for RIB library
>
> The existing DPDK LPM code does need more work it does trade space for
> time.
>
> It does have some advantages though:
>         * LPM lookup table is independent of number of routes.
>
To be honest it depends on number of prefixes longer 24. But despite this
my implementation is independent of number of routes too.

>         * LPM lookup table can be lock free reader safe using RCU.
>
As I see you are using rcu only for tbl8's growing. This feature was out of
scope and could be added if needed. But I'm afraid about license
constraints.

>
> The existing slowness of add and delete was fixed at Vyatta/Brocade by
> replacing list with red-black tree. Patches were submitted but never
> merged.
>
Could you please share lpm_perf_autotest results for your version?
Here my results for lpm_perf_autotest and rib_perf_autotest (sizeof key ==
sizeof(struct rte_lpm_tbl_entry) so memory footprint is the same)
LPM:
Unique added entries = 1037026
Used table 24 entries = 14680064 (87.5%)
64 byte Cache entries used = 458753 (29360192 bytes)
Average LPM Add: 306566 cycles
Average LPM Lookup: 29.7 cycles (fails = 0.0%)
BULK LPM Lookup: 29.4 cycles (fails = 0.0%)
LPM LookupX4: 27.1 cycles (fails = 0.0%)
Average LPM Delete: 201360 cycles
Test OK
RIB:
Unique added entries = 1076816
Average RIB Add: 3149.62 cycles
BULK RIB Lookup: 23.8 cycles (fails = 0.0%)
Average RIB Delete: 2949.62 cycles
Test OK

As you can see for 1M+ routes I have 100 times faster add and 70 times
faster delete. Lookup speed is faster too.
It was achieved due to:
1. routes are kept in compressed binary tree so rule insertion/deletion are
cheap comparing to flat array in LPM. And yes, rb_tree implementation fixes
that problem too.
2. routes are kept in compressed binary tree so you can get less specific
prefix (parent node) for a given prefix (you want add/del to) and compare
it's next hop to a given next hop. There is no need to touch fib table if
they are equal.
3. routes are kept in compressed binary tree so you can traverse on some
part of that tree to get subprefixes for a given prefix (you want add/del
to). Hence you can get gaps between retrieved more specific routes and
based on this gaps unconditionally rewrite part of fib table. Comparing to
LPM where every write to tbl24 or tbl8 is accompanied by a comparison of
depth. In addition there is no need to keep depth in fib anymore.
4. LPM tbl8_alloc is expensive due to memory scan through tbl8 to find a
free tbl8 group. More effective to keep free tbl8 indexes in bitmap.

-- 
Regards,
Vladimir

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 1/4] Add RIB library
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 1/4] Add RIB library Medvedkin Vladimir
                     ` (4 preceding siblings ...)
  2018-04-26 22:20   ` Stephen Hemminger
@ 2018-06-29 13:54   ` Bruce Richardson
  2018-06-29 14:02   ` Bruce Richardson
  6 siblings, 0 replies; 61+ messages in thread
From: Bruce Richardson @ 2018-06-29 13:54 UTC (permalink / raw)
  To: Medvedkin Vladimir; +Cc: dev, thomas, cristian.dumitrescu

On Fri, Apr 27, 2018 at 01:03:31AM +0300, Medvedkin Vladimir wrote:
> Signed-off-by: Medvedkin Vladimir <medvedkinv@gmail.com>
> ---
>  config/common_base                 |   6 +
>  doc/api/doxy-api.conf              |   1 +
>  lib/Makefile                       |   2 +
>  lib/librte_rib/Makefile            |  23 ++
>  lib/librte_rib/meson.build         |   6 +
>  lib/librte_rib/rte_rib.c           | 520 +++++++++++++++++++++++++++++++++++++
>  lib/librte_rib/rte_rib.h           | 302 +++++++++++++++++++++
>  lib/librte_rib/rte_rib_version.map |  18 ++
>  lib/meson.build                    |   2 +-
>  mk/rte.app.mk                      |   1 +
>  10 files changed, 880 insertions(+), 1 deletion(-)
>  create mode 100644 lib/librte_rib/Makefile
>  create mode 100644 lib/librte_rib/meson.build
>  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
> 
<snip>
> --- /dev/null
> +++ b/lib/librte_rib/meson.build
> @@ -0,0 +1,6 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2017 Intel Corporation
> +
> +sources = files('rte_rib.c', 'rte_dir24_8.c')
> +headers = files('rte_rib.h', 'rte_dir24_8.h')
> +deps += ['mempool']

The dir24_8 files don't exist yet - to avoid breaking the build they should
be added in a subsequent patch.

/Bruce

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 1/4] Add RIB library
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 1/4] Add RIB library Medvedkin Vladimir
                     ` (5 preceding siblings ...)
  2018-06-29 13:54   ` Bruce Richardson
@ 2018-06-29 14:02   ` Bruce Richardson
  6 siblings, 0 replies; 61+ messages in thread
From: Bruce Richardson @ 2018-06-29 14:02 UTC (permalink / raw)
  To: Medvedkin Vladimir; +Cc: dev, thomas, cristian.dumitrescu

On Fri, Apr 27, 2018 at 01:03:31AM +0300, Medvedkin Vladimir wrote:
> Signed-off-by: Medvedkin Vladimir <medvedkinv@gmail.com>
> ---
>  config/common_base                 |   6 +
>  doc/api/doxy-api.conf              |   1 +
>  lib/Makefile                       |   2 +
>  lib/librte_rib/Makefile            |  23 ++
>  lib/librte_rib/meson.build         |   6 +
>  lib/librte_rib/rte_rib.c           | 520 +++++++++++++++++++++++++++++++++++++
>  lib/librte_rib/rte_rib.h           | 302 +++++++++++++++++++++
>  lib/librte_rib/rte_rib_version.map |  18 ++
>  lib/meson.build                    |   2 +-
>  mk/rte.app.mk                      |   1 +
>  10 files changed, 880 insertions(+), 1 deletion(-)
>  create mode 100644 lib/librte_rib/Makefile
>  create mode 100644 lib/librte_rib/meson.build
>  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
> 
<snip>
> +}
> +
> +int
> +rte_rib_fib_lookup_bulk(struct rte_rib *rib, uint32_t *ips,
> +	uint64_t *next_hops, int n)
> +{
> +	return rib->lookup(rib->fib, ips, next_hops, n);
> +}

This function is missing from the map file, causing shared library build
failures [seen with meson when linking the unit tests after applying patch
3]

/Bruce

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 3/4] Add autotests for RIB library
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 3/4] Add autotests for RIB library Medvedkin Vladimir
@ 2018-06-29 14:13   ` Bruce Richardson
  2018-06-29 15:07   ` Bruce Richardson
  2018-06-29 15:31   ` Bruce Richardson
  2 siblings, 0 replies; 61+ messages in thread
From: Bruce Richardson @ 2018-06-29 14:13 UTC (permalink / raw)
  To: Medvedkin Vladimir; +Cc: dev, thomas, cristian.dumitrescu

On Fri, Apr 27, 2018 at 01:03:33AM +0300, Medvedkin Vladimir wrote:
> Signed-off-by: Medvedkin Vladimir <medvedkinv@gmail.com>
> ---
>  test/test/Makefile               |   5 +
>  test/test/meson.build            |   8 +
>  test/test/test_rib.c             | 308 +++++++++++++++++++++++++++++++++++++++
>  test/test/test_rib_generate_rt.c | 297 +++++++++++++++++++++++++++++++++++++
>  test/test/test_rib_generate_rt.h |  38 +++++
>  test/test/test_rib_lpm_comp.c    | 189 ++++++++++++++++++++++++
>  test/test/test_rib_perf.c        | 145 ++++++++++++++++++
>  7 files changed, 990 insertions(+)
>  create mode 100644 test/test/test_rib.c
>  create mode 100644 test/test/test_rib_generate_rt.c
>  create mode 100644 test/test/test_rib_generate_rt.h
>  create mode 100644 test/test/test_rib_lpm_comp.c
>  create mode 100644 test/test/test_rib_perf.c
> 
Clang is giving some errors after this patch, nothing to serious, but they
need to be fixed in the next version:

../test/test/test_rib_perf.c:87:12: error: equality comparison with extraneous parentheses [-Werror,-Wparentheses-equality]
                if ((ret == 0))
                     ~~~~^~~~
../test/test/test_rib_perf.c:87:12: note: remove extraneous parentheses around the comparison to silence this warning
                if ((ret == 0))
                    ~    ^   ~
../test/test/test_rib_perf.c:87:12: note: use '=' to turn this equality comparison into an assignment
                if ((ret == 0))
                         ^~
                         =
1 error generated.

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 3/4] Add autotests for RIB library
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 3/4] Add autotests for RIB library Medvedkin Vladimir
  2018-06-29 14:13   ` Bruce Richardson
@ 2018-06-29 15:07   ` Bruce Richardson
  2018-06-29 15:31   ` Bruce Richardson
  2 siblings, 0 replies; 61+ messages in thread
From: Bruce Richardson @ 2018-06-29 15:07 UTC (permalink / raw)
  To: Medvedkin Vladimir; +Cc: dev, thomas, cristian.dumitrescu

On Fri, Apr 27, 2018 at 01:03:33AM +0300, Medvedkin Vladimir wrote:
> Signed-off-by: Medvedkin Vladimir <medvedkinv@gmail.com>
> ---
>  test/test/Makefile               |   5 +
>  test/test/meson.build            |   8 +
>  test/test/test_rib.c             | 308 +++++++++++++++++++++++++++++++++++++++
>  test/test/test_rib_generate_rt.c | 297 +++++++++++++++++++++++++++++++++++++
>  test/test/test_rib_generate_rt.h |  38 +++++
>  test/test/test_rib_lpm_comp.c    | 189 ++++++++++++++++++++++++
>  test/test/test_rib_perf.c        | 145 ++++++++++++++++++
>  7 files changed, 990 insertions(+)
>  create mode 100644 test/test/test_rib.c
>  create mode 100644 test/test/test_rib_generate_rt.c
>  create mode 100644 test/test/test_rib_generate_rt.h
>  create mode 100644 test/test/test_rib_lpm_comp.c
>  create mode 100644 test/test/test_rib_perf.c
> 

<snip>

> diff --git a/test/test/test_rib_lpm_comp.c b/test/test/test_rib_lpm_comp.c
> new file mode 100644
> index 0000000..ef48c8c
> --- /dev/null
> +++ b/test/test/test_rib_lpm_comp.c
> @@ -0,0 +1,189 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
> + */
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +
> +#include <rte_random.h>
> +#include <rte_cycles.h>
> +#include <rte_branch_prediction.h>
> +#include <rte_ip.h>
> +#include <rte_malloc.h>
> +#include <rte_lpm.h>
> +#include <rte_rib.h>
> +
> +#include "test.h"
> +#include "test_xmmt_ops.h"
> +#include "test_rib_generate_rt.h"
> +
> +#define TEST_RIB_ASSERT(cond) do {				\
> +	if (!(cond)) {						\
> +		printf("Error at line %d:\n", __LINE__);	\
> +		return -1;					\
> +	}							\
> +} while (0)
> +
> +#define ITERATIONS (1 << 25)
> +#define BATCH_SIZE (1 << 7)
> +#define BULK_SIZE 32
> +#define LPM_NH_MASK	((1 << 24) - 1)
> +
> +static uint64_t default_nh;
> +
> +static int
> +test_lookup(struct rte_rib *rib, struct rte_lpm *lpm)

It should be fairly obvious, but put in a comment explaining the function
and what it does, and how.

> +{
> +	static uint32_t ip_batch[BATCH_SIZE];
> +	uint64_t rib_next_hops[BULK_SIZE];
> +	uint32_t lpm_next_hops[BULK_SIZE];
> +	int i, j, k;
> +
> +	for (i = 0; i < ITERATIONS; i++) {
> +		for (j = 0; j < BATCH_SIZE; j++)
> +			ip_batch[j] = (i << 7) + j;
> +
> +		for (j = 0; j < BATCH_SIZE; j += BULK_SIZE) {
> +			rte_rib_fib_lookup_bulk(rib, &ip_batch[j],
> +				rib_next_hops, BULK_SIZE);
> +			rte_lpm_lookup_bulk(lpm, &ip_batch[j],
> +				lpm_next_hops, BULK_SIZE);
> +			for (k = 0; k < BULK_SIZE; k++) {
> +				if (likely(lpm_next_hops[k] &
> +					RTE_LPM_LOOKUP_SUCCESS))
> +					lpm_next_hops[k] &= LPM_NH_MASK;
> +				else
> +					lpm_next_hops[k] = default_nh;
> +			}
> +			for (k = 0; k < BULK_SIZE; k++)
> +				TEST_RIB_ASSERT(rib_next_hops[k] ==
> +						lpm_next_hops[k]);
> +		}
> +	}
> +	return 0;
> +}

This looks a good unit test for comparisons. Although it's scanning
linearly, I wonder if it may be worthwhile to rework the loops so you do
all lookups for a batch for lpm first then for fib, and track the cycles
for each. Then at the end you can print out the lookup perf comparison.
Alternatively, an additional batch at the end with random lookups could be
done. [Yes, I know the info can be got by running the perf tests for lpm
and rib separately, but it would be nice to have it as part of a comparison
autotest]

> +
> +static int
> +test_rib_lpm_comp(void)
> +{
> +	struct rte_rib *rib = NULL;
> +	struct rte_lpm *lpm = NULL;
> +	struct route_rule *rt = NULL;
> +	unsigned int i;
> +	int rib_add = 0, lpm_add = 0;
> +	int ret, nh_bits, nr_tbl8;
> +	uint32_t num_routes;
> +	struct rte_rib_conf conf;
> +	struct rte_lpm_config config;
> +
> +	rte_srand(rte_rdtsc());
> +	default_nh = 17;
> +
> +	conf.max_nodes = 3000000;
> +	conf.node_sz = sizeof(struct rte_rib_node);
> +	conf.type = RTE_RIB_DIR24_8;
> +	conf.fib_conf.dir24_8.def_nh = default_nh;
> +	conf.fib_conf.dir24_8.nh_sz = RTE_DIR24_8_8B;
> +
> +	nh_bits = RTE_MIN(((1 << (3 + conf.fib_conf.dir24_8.nh_sz)) - 1), 24);
> +	nr_tbl8 = RTE_MIN(((1 << nh_bits) - 1), 65535);

These two lines need a comment explaining them - especially the former one.

> +	config.number_tbl8s = nr_tbl8;
> +	conf.fib_conf.dir24_8.num_tbl8 = nr_tbl8;
> +	config.max_rules = 2000000;
> +	config.flags = 0;
> +
> +	num_routes = 1200000;
> +
> +	rt = rte_zmalloc("struct route_rule *", sizeof(struct route_rule) *
> +		num_routes + 5, 0);
> +	TEST_RIB_ASSERT(rt != NULL);
> +
> +	num_routes = generate_large_route_rule_table(num_routes, rt);
> +	TEST_RIB_ASSERT(num_routes != 0);
> +	printf("No. routes = %u\n", (unsigned int) num_routes);
> +
> +	shuffle_rt(rt, num_routes);
> +
> +	print_route_distribution(rt, (uint32_t) num_routes);
> +
> +	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &conf);
> +	TEST_RIB_ASSERT(rib != NULL);
> +
> +	lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
> +	TEST_RIB_ASSERT(lpm != NULL);
> +
> +	for (i = 0; i < num_routes; i++)
> +		rt[i].nh = rte_rand() & ((1ULL << nh_bits) - 1);
> +

Put comment here explaining this next block.
> +	for (i = 0; i < num_routes; i++) {
> +		ret = rte_rib_add(rib, rt[i].ip, rt[i].depth, rt[i].nh);
> +		if (ret == 0)
> +			rib_add++;
> +		else
> +			continue;
> +
> +		ret = rte_lpm_add(lpm, rt[i].ip, rt[i].depth, rt[i].nh);
> +		if (ret == 0)
> +			lpm_add++;
> +		else {
> +			rte_rib_delete(rib, rt[i].ip, rt[i].depth);
> +			rib_add--;
> +		}
> +	}
> +	TEST_RIB_ASSERT(rib_add == lpm_add);
> +
> +	ret = test_lookup(rib, lpm);
> +	if (ret != 0)
> +		return ret;
> +
> +	for (i = 0; i < num_routes; i++) {
> +		if ((i % 3) == 0) {
I assume the intention here is after filling the table and doing the lookup
tests, we drop 1/3 of the entries and retest. Put in a comment explaining
the why.
Rather than putting in the if statement, why not just change the loop to
be:

	for (i = 0; i < num_routes; i += 3)

> +			ret = rte_rib_delete(rib, rt[i].ip, rt[i].depth);
> +			if (ret == 0)
> +				rib_add--;
> +			else
> +				continue;
> +
> +			ret = rte_lpm_delete(lpm, rt[i].ip, rt[i].depth);
> +			if (ret == 0)
> +				lpm_add--;
> +		}
> +	}
> +	TEST_RIB_ASSERT(rib_add == lpm_add);
> +
> +	ret = test_lookup(rib, lpm);
> +	if (ret != 0)
> +		return ret;
> +
> +	for (i = 0; i < num_routes; i++) {
> +		if ((i % 6) == 0) {

As above, put in a comment, and consider removing the if statment. It helps
having the reduced indentation.

> +			ret = rte_rib_add(rib, rt[i].ip, rt[i].depth, rt[i].nh);
> +			if (ret == 0)
> +				rib_add++;
> +			else
> +				continue;
> +
> +			ret = rte_lpm_add(lpm, rt[i].ip, rt[i].depth, rt[i].nh);
> +			if (ret == 0)
> +				lpm_add++;
> +			else {
> +				rte_rib_delete(rib, rt[i].ip, rt[i].depth);
> +				rib_add--;
> +			}
> +		}
> +	}
> +	TEST_RIB_ASSERT(rib_add == lpm_add);
> +
> +	ret = test_lookup(rib, lpm);
> +	if (ret != 0)
> +		return ret;
> +
> +	rte_rib_free(rib);
> +	rte_lpm_free(lpm);
> +	rte_free(rt);
> +
> +	return 0;
> +}

Looks a really good test. Only issue I have is that it sits for a long time
doing the checks in the background without any output. I would therefore
suggest:
* At end of each stage, add, lookup, delete, etc print out a message
  stating what was done with how many entries, e.g X adds, Y lookups etc.
* For any of those individual items that takes a long time on its own,
  consider printing out every e.g. 100,000 items, and/or printing a dot
  every 10,000 items, so the user can see progress.

> +
> +REGISTER_TEST_COMMAND(rib_lpm_comp_autotest, test_rib_lpm_comp);

<snip>

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 3/4] Add autotests for RIB library
  2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 3/4] Add autotests for RIB library Medvedkin Vladimir
  2018-06-29 14:13   ` Bruce Richardson
  2018-06-29 15:07   ` Bruce Richardson
@ 2018-06-29 15:31   ` Bruce Richardson
  2 siblings, 0 replies; 61+ messages in thread
From: Bruce Richardson @ 2018-06-29 15:31 UTC (permalink / raw)
  To: Medvedkin Vladimir; +Cc: dev, thomas, cristian.dumitrescu

On Fri, Apr 27, 2018 at 01:03:33AM +0300, Medvedkin Vladimir wrote:
> Signed-off-by: Medvedkin Vladimir <medvedkinv@gmail.com>
> ---
>  test/test/Makefile               |   5 +
>  test/test/meson.build            |   8 +
>  test/test/test_rib.c             | 308 +++++++++++++++++++++++++++++++++++++++
>  test/test/test_rib_generate_rt.c | 297 +++++++++++++++++++++++++++++++++++++
>  test/test/test_rib_generate_rt.h |  38 +++++
>  test/test/test_rib_lpm_comp.c    | 189 ++++++++++++++++++++++++
>  test/test/test_rib_perf.c        | 145 ++++++++++++++++++
>  7 files changed, 990 insertions(+)
>  create mode 100644 test/test/test_rib.c
>  create mode 100644 test/test/test_rib_generate_rt.c
>  create mode 100644 test/test/test_rib_generate_rt.h
>  create mode 100644 test/test/test_rib_lpm_comp.c
>  create mode 100644 test/test/test_rib_perf.c
> 

<snip>

> diff --git a/test/test/test_rib_perf.c b/test/test/test_rib_perf.c
> new file mode 100644
> index 0000000..42fbd1e
> --- /dev/null
> +++ b/test/test/test_rib_perf.c
> @@ -0,0 +1,145 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
> + */
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +
> +#include <rte_cycles.h>
> +#include <rte_random.h>
> +#include <rte_branch_prediction.h>
> +#include <rte_ip.h>
> +#include <rte_malloc.h>
> +#include <rte_rib.h>
> +#include <rte_dir24_8.h>
> +
> +#include "test.h"
> +#include "test_xmmt_ops.h"
> +#include "test_rib_generate_rt.h"
> +
> +#define TEST_RIB_ASSERT(cond) do {				\
> +	if (!(cond)) {						\
> +		printf("Error at line %d:\n", __LINE__);	\
> +		return -1;					\
> +	}							\
> +} while (0)
> +
> +#define ITERATIONS (1 << 15)
> +#define BATCH_SIZE (1 << 12)
> +#define BULK_SIZE 32
> +
> +#define NH_MSK(nh_sz)	((1ULL << ((1 << (3 + nh_sz)) - 1)) - 1)
> +
> +static int
> +test_rib_perf(void)
> +{
> +	struct rte_rib *rib = NULL;
> +	struct rte_rib_conf conf;
> +	struct route_rule *rt;
> +	uint64_t begin, total_time;
> +	uint64_t next_hop_add;
> +	uint64_t default_nh = 0;
> +	int64_t count = 0;
> +	unsigned int i, j;
> +	int status = 0;
> +	int ret, nh_bits, nr_tbl8;
> +	uint32_t num_routes;
> +
> +	conf.max_nodes = 3000000;
> +	conf.node_sz = sizeof(struct rte_rib_node);
> +	conf.type = RTE_RIB_DIR24_8;
> +	conf.fib_conf.dir24_8.def_nh = default_nh;
> +	conf.fib_conf.dir24_8.nh_sz = RTE_DIR24_8_8B;

It's good that you are taking the worst-case to show the perf, but it means
that the library comes out a bit slower than LPM in the autotest.
How about running the same test cases for multiple data sizes, 8, 4, 2?

> +
> +	rte_srand(rte_rdtsc());
> +
> +	nh_bits = RTE_MIN(((1 << (3 + conf.fib_conf.dir24_8.nh_sz)) - 1), 24);
> +	nr_tbl8 = RTE_MIN(((1 << nh_bits) - 1), 131071);
> +	conf.fib_conf.dir24_8.num_tbl8 = nr_tbl8;
> +	num_routes = 1200000;
> +
> +	rt = rte_zmalloc("struct route_rule *", sizeof(struct route_rule) *
> +		num_routes, 0);
> +	TEST_RIB_ASSERT(rt != NULL);
> +
> +	num_routes = generate_large_route_rule_table(num_routes, rt);
> +	TEST_RIB_ASSERT(num_routes != 0);
> +
> +	printf("No. routes = %u\n", (unsigned int) num_routes);
> +
> +	shuffle_rt(rt, num_routes);
> +
> +	print_route_distribution(rt, (uint32_t) num_routes);
> +
> +	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &conf);
> +	TEST_RIB_ASSERT(rib != NULL);
> +
> +	/* Measue add. */
> +	begin = rte_rdtsc();
> +
> +	for (i = 0; i < num_routes; i++) {
> +		do {
> +			next_hop_add = rte_rand() & NH_MSK(conf.fib_conf.dir24_8.nh_sz);
> +		} while (next_hop_add == default_nh);
> +
> +		ret = rte_rib_add(rib, rt[i].ip, rt[i].depth, next_hop_add);
> +		if ((ret == 0))
> +			status++;
> +	}
> +
> +	total_time = rte_rdtsc() - begin;
> +
> +	printf("Unique added entries = %d\n", status);
> +	printf("Average RIB Add: %g cycles\n",
> +			(double)total_time / num_routes);
> +
> +	/* Measure bulk Lookup */
> +	total_time = 0;
> +	count = 0;
> +	for (i = 0; i < ITERATIONS; i++) {
> +		static uint32_t ip_batch[BATCH_SIZE];
> +		uint64_t next_hops[BULK_SIZE];
> +
> +		/* Create array of random IP addresses */
> +		for (j = 0; j < BATCH_SIZE; j++)
> +			ip_batch[j] = rte_rand();
> +
> +		/* Lookup per batch */
> +		begin = rte_rdtsc();
> +		for (j = 0; j < BATCH_SIZE; j += BULK_SIZE)
> +			rte_rib_fib_lookup_bulk(rib, &ip_batch[j], next_hops,
> +				BULK_SIZE);
> +
> +		total_time += rte_rdtsc() - begin;
> +		for (j = 0; j < BULK_SIZE; j++) {
> +			if (next_hops[j] == default_nh)
> +				count++;
> +		}
> +	}
> +	printf("BULK RIB Lookup: %.1f cycles (fails = %.1f%%)\n",
> +			(double)total_time / ((double)ITERATIONS * BATCH_SIZE),
> +			(count * 100.0) / (double)(ITERATIONS * BATCH_SIZE));
> +
> +	/* Delete */
> +	status = 0;
> +	begin = rte_rdtsc();
> +
> +	for (i = 0; i < num_routes; i++) {
> +		ret = rte_rib_delete(rib, rt[i].ip, rt[i].depth);
> +		if (ret == 0)
> +			status++;
> +	}
> +
> +	total_time = rte_rdtsc() - begin;
> +
> +	printf("Average RIB Delete: %g cycles\n",
> +			(double)total_time / num_routes);
> +
> +	rte_rib_free(rib);
> +	rte_free(rt);
> +
> +	return 0;
> +}
> +
> +REGISTER_TEST_COMMAND(rib_perf_autotest, test_rib_perf);
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (4 preceding siblings ...)
  2018-04-26 22:24 ` [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Stephen Hemminger
@ 2018-06-29 15:48 ` Bruce Richardson
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Bruce Richardson @ 2018-06-29 15:48 UTC (permalink / raw)
  To: Medvedkin Vladimir; +Cc: dev, thomas, cristian.dumitrescu

On Fri, Apr 27, 2018 at 01:03:30AM +0300, Medvedkin Vladimir wrote:
> This patch series introduces new library librte_rib which potentially could
> replace librte_lpm.
> 
> 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.
> 
> v4:
>   fix various bugs
>   make struct rte_rib opaque
>   make inline functions instead of macro
>   remove RTE_RIB_MALLOC node allocation type
>   add new lookup functions
>   remove rte_dir24_8_lookup()
>   add rte_dir24_8_get_lookup()
>   add meson support
>   add fib configuration
> 
> 
> Medvedkin Vladimir (4):
>   Add RIB library
>   Add dir24_8 implementation for rib library
>   Add autotests for RIB library
>   Add support for lpm and rib bulk lookup
> 
Some general comments on this set:

* run checkpatches and check-git-log from devtools on the set, a number of
  warnings are thrown up by both [e.g. no initial-caps for patch titles]
* before merge to DPDK this probably needs to be split up into smaller
  patches with more limited set of functionality in each one. For example,
  the create/destroy, add/delete and lookup functions can probably go in
  separate patches to make reviews easier.

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (5 preceding siblings ...)
  2018-06-29 15:48 ` Bruce Richardson
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  2019-09-12  7:37   ` Morten Brørup
                     ` (13 more replies)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 01/12] rib: add RIB library Vladimir Medvedkin
                   ` (11 subsequent siblings)
  18 siblings, 14 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

This is heavily reworked version of previous RIB library series:
https://mails.dpdk.org/archives/dev/2018-April/099492.html

Current lpm implementation while provides really good lookup
performance has number of problems.
One of them is very low speed for control plane operations
such as add or delete a route.
Another disadvantage is fixed number of bits for userdata
(24 for v4 and 21 for v6)
Also it is hard to introduce changes in existing LPM code or add new
algorithms without breaking ABI.

This patch series tries to solve this problems by:
Introduce two new libraries - RIB and FIB.
RIB that is Routing Information Base.
It implements a control plane struct containing routes in a tree and
provides fast add/del operations for routes. Also it allows to perform
fast subtree traversals (i.e. retrieve existing subroutes for a given
prefix). This structure will be used as a control plane helper structure
for FIB implementation.
Also it might be used standalone in other different places such as
bitmaps for example.

Second library is FIB that is Forwarding Information Base. It represents
dataplane related struct and algorithms for longest prefix match.
Internally it consists of two parts - RIB (control plane ops) and
implementation for the dataplane tasks.
Initial version provides two implementations for both ipv4 and ipv6:
dummy (uses RIB as a dataplane) and DIR24_8 (same as current LPM)
Due to proposed design it allows to extend FIB with new algorithms in future
(for example DXR, poptrie, etc).

From our measurements we saw 10x speedup for control plane operations
comparing with current LPM library (depending on prefix length distribution)

ToDo:
 - introduce new performance measurement app.
 - add documentation.
 - add support into existing examples (l3fwd)


Vladimir Medvedkin (12):
  rib: add RIB library
  test/rib: add RIB library autotests
  rib: add ipv6 support for RIB
  test/rib: add ipv6 support for RIB autotests
  fib: add FIB library
  fib: add FIB ipv6 support
  fib: add DIR24-8 dataplane algorithm
  fib: add dataplane algorithm for ipv6
  test/fib: add FIB library autotests
  test/fib: add ipv6 support for FIB autotests
  test/fib: add FIB library performance autotests
  test/fib: add FIB library ipv6 performance autotests

 app/test/Makefile                  |   7 +
 app/test/autotest_data.py          |  36 ++
 app/test/meson.build               |  14 +
 app/test/test_fib.c                | 397 +++++++++++++++++++
 app/test/test_fib6.c               | 405 ++++++++++++++++++++
 app/test/test_fib6_perf.c          | 157 ++++++++
 app/test/test_fib_perf.c           | 411 ++++++++++++++++++++
 app/test/test_rib.c                | 351 +++++++++++++++++
 app/test/test_rib6.c               | 357 +++++++++++++++++
 config/common_base                 |  11 +
 doc/api/doxy-api.conf.in           |   2 +
 lib/Makefile                       |   4 +
 lib/librte_fib/Makefile            |  25 ++
 lib/librte_fib/dir24_8.c           | 737 +++++++++++++++++++++++++++++++++++
 lib/librte_fib/dir24_8.h           |  36 ++
 lib/librte_fib/meson.build         |   8 +
 lib/librte_fib/rte_fib.c           | 319 ++++++++++++++++
 lib/librte_fib/rte_fib.h           | 188 +++++++++
 lib/librte_fib/rte_fib6.c          | 322 ++++++++++++++++
 lib/librte_fib/rte_fib6.h          | 193 ++++++++++
 lib/librte_fib/rte_fib_version.map |  23 ++
 lib/librte_fib/trie.c              | 760 +++++++++++++++++++++++++++++++++++++
 lib/librte_fib/trie.h              |  37 ++
 lib/librte_rib/Makefile            |  25 ++
 lib/librte_rib/meson.build         |   8 +
 lib/librte_rib/rte_rib.c           | 532 ++++++++++++++++++++++++++
 lib/librte_rib/rte_rib.h           | 277 ++++++++++++++
 lib/librte_rib/rte_rib6.c          | 598 +++++++++++++++++++++++++++++
 lib/librte_rib/rte_rib6.h          | 334 ++++++++++++++++
 lib/librte_rib/rte_rib_version.map |  35 ++
 lib/meson.build                    |   4 +-
 mk/rte.app.mk                      |   2 +
 32 files changed, 6614 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_fib.c
 create mode 100644 app/test/test_fib6.c
 create mode 100644 app/test/test_fib6_perf.c
 create mode 100644 app/test/test_fib_perf.c
 create mode 100644 app/test/test_rib.c
 create mode 100644 app/test/test_rib6.c
 create mode 100644 lib/librte_fib/Makefile
 create mode 100644 lib/librte_fib/dir24_8.c
 create mode 100644 lib/librte_fib/dir24_8.h
 create mode 100644 lib/librte_fib/meson.build
 create mode 100644 lib/librte_fib/rte_fib.c
 create mode 100644 lib/librte_fib/rte_fib.h
 create mode 100644 lib/librte_fib/rte_fib6.c
 create mode 100644 lib/librte_fib/rte_fib6.h
 create mode 100644 lib/librte_fib/rte_fib_version.map
 create mode 100644 lib/librte_fib/trie.c
 create mode 100644 lib/librte_fib/trie.h
 create mode 100644 lib/librte_rib/Makefile
 create mode 100644 lib/librte_rib/meson.build
 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_rib6.c
 create mode 100644 lib/librte_rib/rte_rib6.h
 create mode 100644 lib/librte_rib/rte_rib_version.map

-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 01/12] rib: add RIB library
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (6 preceding siblings ...)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 02/12] test/rib: add RIB library autotests Vladimir Medvedkin
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

Add RIB (Routing Information Base) library. This library
implements an IPv4 routing table optimized for control plane
operations. It implements a control plane struct containing routes
in a tree and provides fast add/del operations for routes.
Also it allows to perform fast subtree traversals
(i.e. retrieve existing subroutes for a giver prefix).
This structure will be used as a control plane helper structure
for FIB implementation. Also it might be used standalone in other
different places such as bitmaps for example.
Internal implementation is level compressed binary trie.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 config/common_base                 |   5 +
 doc/api/doxy-api.conf.in           |   1 +
 lib/Makefile                       |   2 +
 lib/librte_rib/Makefile            |  25 ++
 lib/librte_rib/meson.build         |   8 +
 lib/librte_rib/rte_rib.c           | 532 +++++++++++++++++++++++++++++++++++++
 lib/librte_rib/rte_rib.h           | 277 +++++++++++++++++++
 lib/librte_rib/rte_rib_version.map |  20 ++
 lib/meson.build                    |   2 +-
 mk/rte.app.mk                      |   1 +
 10 files changed, 872 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_rib/Makefile
 create mode 100644 lib/librte_rib/meson.build
 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 8ef75c2..5a9d4c3 100644
--- a/config/common_base
+++ b/config/common_base
@@ -894,6 +894,11 @@ CONFIG_RTE_LIBRTE_RCU=y
 CONFIG_RTE_LIBRTE_RCU_DEBUG=n
 
 #
+# Compile librte_rib
+#
+CONFIG_RTE_LIBRTE_RIB=y
+
+#
 # Compile librte_lpm
 #
 CONFIG_RTE_LIBRTE_LPM=y
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 908cee8..3ec012d 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -54,6 +54,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_rawdev \
                           @TOPDIR@/lib/librte_rcu \
                           @TOPDIR@/lib/librte_reorder \
+                          @TOPDIR@/lib/librte_rib \
                           @TOPDIR@/lib/librte_ring \
                           @TOPDIR@/lib/librte_sched \
                           @TOPDIR@/lib/librte_security \
diff --git a/lib/Makefile b/lib/Makefile
index 41c463d..aa5ee1e 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -51,6 +51,8 @@ DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash
 DEPDIRS-librte_hash := librte_eal librte_ring
 DIRS-$(CONFIG_RTE_LIBRTE_EFD) += librte_efd
 DEPDIRS-librte_efd := librte_eal librte_ring librte_hash
+DIRS-$(CONFIG_RTE_LIBRTE_RIB) += librte_rib
+DEPDIRS-librte_rib := librte_eal librte_mempool
 DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm
 DEPDIRS-librte_lpm := librte_eal librte_hash
 DIRS-$(CONFIG_RTE_LIBRTE_ACL) += librte_acl
diff --git a/lib/librte_rib/Makefile b/lib/librte_rib/Makefile
new file mode 100644
index 0000000..79f259a
--- /dev/null
+++ b/lib/librte_rib/Makefile
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+# Copyright(c) 2019 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_rib.a
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+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
+
+# install this header file
+SYMLINK-$(CONFIG_RTE_LIBRTE_RIB)-include := rte_rib.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_rib/meson.build b/lib/librte_rib/meson.build
new file mode 100644
index 0000000..e7b8920
--- /dev/null
+++ b/lib/librte_rib/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+# Copyright(c) 2019 Intel Corporation
+
+allow_experimental_apis = true
+sources = files('rte_rib.c')
+headers = files('rte_rib.h')
+deps += ['mempool']
diff --git a/lib/librte_rib/rte_rib.c b/lib/librte_rib/rte_rib.c
new file mode 100644
index 0000000..55d612d
--- /dev/null
+++ b/lib/librte_rib/rte_rib.c
@@ -0,0 +1,532 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_mempool.h>
+#include <rte_rwlock.h>
+#include <rte_string_fns.h>
+#include <rte_tailq.h>
+
+#include <rte_rib.h>
+
+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)
+
+#define RTE_RIB_VALID_NODE	1
+/* Maximum depth value possible for IPv4 RIB. */
+#define RIB_MAXDEPTH		32
+/* Maximum length of a RIB name. */
+#define RTE_RIB_NAMESIZE	64
+
+struct rte_rib_node {
+	struct rte_rib_node	*left;
+	struct rte_rib_node	*right;
+	struct rte_rib_node	*parent;
+	uint32_t	ip;
+	uint8_t		depth;
+	uint8_t		flag;
+	uint64_t	nh;
+	__extension__ uint64_t	ext[0];
+};
+
+struct rte_rib {
+	char		name[RTE_RIB_NAMESIZE];
+	struct rte_rib_node	*tree;
+	struct rte_mempool	*node_pool;
+	uint32_t		cur_nodes;
+	uint32_t		cur_routes;
+	uint32_t		max_nodes;
+};
+
+static inline bool
+is_valid_node(struct rte_rib_node *node)
+{
+	return (node->flag & RTE_RIB_VALID_NODE) == RTE_RIB_VALID_NODE;
+}
+
+static inline bool
+is_right_node(struct rte_rib_node *node)
+{
+	return node->parent->right == node;
+}
+
+/*
+ * Check if ip1 is covered by ip2/depth prefix
+ */
+static inline bool
+is_covered(uint32_t ip1, uint32_t ip2, uint8_t depth)
+{
+	return ((ip1 ^ ip2) & rte_rib_depth_to_mask(depth)) == 0;
+}
+
+static inline struct rte_rib_node *
+get_nxt_node(struct rte_rib_node *node, uint32_t ip)
+{
+	return (ip & (1 << (31 - node->depth))) ? node->right : node->left;
+}
+
+static struct rte_rib_node *
+node_alloc(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
+node_free(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_lookup(struct rte_rib *rib, uint32_t ip)
+{
+	struct rte_rib_node *cur, *prev = NULL;
+
+	if (rib == NULL) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	cur = rib->tree;
+	while ((cur != NULL) && is_covered(ip, cur->ip, cur->depth)) {
+		if (is_valid_node(cur))
+			prev = cur;
+		cur = get_nxt_node(cur, ip);
+	}
+	return prev;
+}
+
+struct rte_rib_node *
+rte_rib_lookup_parent(struct rte_rib_node *ent)
+{
+	struct rte_rib_node *tmp;
+
+	if (ent == NULL)
+		return NULL;
+	tmp = ent->parent;
+	while ((tmp != NULL) &&	!is_valid_node(tmp))
+		tmp = tmp->parent;
+	return tmp;
+}
+
+static struct rte_rib_node *
+__rib_lookup_exact(struct rte_rib *rib, uint32_t ip, uint8_t depth)
+{
+	struct rte_rib_node *cur;
+
+	cur = rib->tree;
+	while (cur != NULL) {
+		if ((cur->ip == ip) && (cur->depth == depth) &&
+				is_valid_node(cur))
+			return cur;
+		if ((cur->depth > depth) ||
+				!is_covered(ip, cur->ip, cur->depth))
+			break;
+		cur = get_nxt_node(cur, ip);
+	}
+	return NULL;
+}
+
+struct rte_rib_node *
+rte_rib_lookup_exact(struct rte_rib *rib, uint32_t ip, uint8_t depth)
+{
+	if ((rib == NULL) || (depth > RIB_MAXDEPTH)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+	ip &= rte_rib_depth_to_mask(depth);
+
+	return __rib_lookup_exact(rib, ip, depth);
+}
+
+/*
+ *  Traverses on subtree and retrieves more specific routes
+ *  for a given in args ip/depth prefix
+ *  last = NULL means the first invocation
+ */
+struct rte_rib_node *
+rte_rib_get_nxt(struct rte_rib *rib, uint32_t ip,
+	uint8_t depth, struct rte_rib_node *last, int flag)
+{
+	struct rte_rib_node *tmp, *prev = NULL;
+
+	if ((rib == NULL) || (depth > RIB_MAXDEPTH)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	if (last == NULL) {
+		tmp = rib->tree;
+		while ((tmp) && (tmp->depth < depth))
+			tmp = get_nxt_node(tmp, ip);
+	} else {
+		tmp = last;
+		while ((tmp->parent != NULL) && (is_right_node(tmp) ||
+				(tmp->parent->right == NULL))) {
+			tmp = tmp->parent;
+			if (is_valid_node(tmp) &&
+					(is_covered(tmp->ip, ip, depth) &&
+					(tmp->depth > depth)))
+				return tmp;
+		}
+		tmp = (tmp->parent) ? tmp->parent->right : NULL;
+	}
+	while (tmp) {
+		if (is_valid_node(tmp) &&
+				(is_covered(tmp->ip, ip, depth) &&
+				(tmp->depth > depth))) {
+			prev = tmp;
+			if (flag == RTE_RIB_GET_NXT_COVER)
+				return prev;
+		}
+		tmp = (tmp->left) ? tmp->left : tmp->right;
+	}
+	return prev;
+}
+
+void
+rte_rib_remove(struct rte_rib *rib, uint32_t ip, uint8_t depth)
+{
+	struct rte_rib_node *cur, *prev, *child;
+
+	cur = rte_rib_lookup_exact(rib, ip, depth);
+	if (cur == NULL)
+		return;
+
+	--rib->cur_routes;
+	cur->flag &= ~RTE_RIB_VALID_NODE;
+	while (!is_valid_node(cur)) {
+		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->tree = child;
+			node_free(rib, cur);
+			return;
+		}
+		if (cur->parent->left == cur)
+			cur->parent->left = child;
+		else
+			cur->parent->right = child;
+		prev = cur;
+		cur = cur->parent;
+		node_free(rib, prev);
+	}
+}
+
+struct rte_rib_node *
+rte_rib_insert(struct rte_rib *rib, uint32_t ip, uint8_t depth)
+{
+	struct rte_rib_node **tmp;
+	struct rte_rib_node *prev = NULL;
+	struct rte_rib_node *new_node = NULL;
+	struct rte_rib_node *common_node = NULL;
+	int d = 0;
+	uint32_t common_prefix;
+	uint8_t common_depth;
+
+	if ((rib == NULL) || (depth > RIB_MAXDEPTH)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	tmp = &rib->tree;
+	ip &= rte_rib_depth_to_mask(depth);
+	new_node = __rib_lookup_exact(rib, ip, depth);
+	if (new_node != NULL) {
+		rte_errno = EEXIST;
+		return NULL;
+	}
+
+	new_node = node_alloc(rib);
+	if (new_node == NULL) {
+		rte_errno = ENOMEM;
+		return NULL;
+	}
+	new_node->left = NULL;
+	new_node->right = NULL;
+	new_node->parent = NULL;
+	new_node->ip = ip;
+	new_node->depth = depth;
+	new_node->flag = RTE_RIB_VALID_NODE;
+
+	/* traverse down the tree to find matching node or closest matching */
+	while (1) {
+		/* insert as the last node in the branch */
+		if (*tmp == NULL) {
+			*tmp = new_node;
+			new_node->parent = prev;
+			++rib->cur_routes;
+			return *tmp;
+		}
+		/*
+		 * Intermediate node found.
+		 * Previous rte_rib_lookup_exact() returned NULL
+		 * but node with proper search criteria is found.
+		 * Validate intermediate node and return.
+		 */
+		if ((ip == (*tmp)->ip) && (depth == (*tmp)->depth)) {
+			node_free(rib, new_node);
+			(*tmp)->flag |= RTE_RIB_VALID_NODE;
+			++rib->cur_routes;
+			return *tmp;
+		}
+		d = (*tmp)->depth;
+		if ((d >= depth) || !is_covered(ip, (*tmp)->ip, d))
+			break;
+		prev = *tmp;
+		tmp = (ip & (1 << (31 - d))) ? &(*tmp)->right : &(*tmp)->left;
+	}
+	/* closest node found, new_node should be inserted in the middle */
+	common_depth = RTE_MIN(depth, (*tmp)->depth);
+	common_prefix = ip ^ (*tmp)->ip;
+	d = __builtin_clz(common_prefix);
+
+	common_depth = RTE_MIN(d, common_depth);
+	common_prefix = ip & rte_rib_depth_to_mask(common_depth);
+	if ((common_prefix == ip) && (common_depth == depth)) {
+		/* insert as a parent */
+		if ((*tmp)->ip & (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 {
+		/* create intermediate node */
+		common_node = node_alloc(rib);
+		if (common_node == NULL) {
+			node_free(rib, new_node);
+			rte_errno = ENOMEM;
+			return NULL;
+		}
+		common_node->ip = 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->ip & (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;
+	}
+	++rib->cur_routes;
+	return new_node;
+}
+
+int
+rte_rib_get_ip(struct rte_rib_node *node, uint32_t *ip)
+{
+	if ((node == NULL) || (ip == NULL)) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	*ip = node->ip;
+	return 0;
+}
+
+int
+rte_rib_get_depth(struct rte_rib_node *node, uint8_t *depth)
+{
+	if ((node == NULL) || (depth == NULL)) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	*depth = node->depth;
+	return 0;
+}
+
+void *
+rte_rib_get_ext(struct rte_rib_node *node)
+{
+	return (node == NULL) ? NULL : &node->ext[0];
+}
+
+int
+rte_rib_get_nh(struct rte_rib_node *node, uint64_t *nh)
+{
+	if ((node == NULL) || (nh == NULL)) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	*nh = node->nh;
+	return 0;
+}
+
+int
+rte_rib_set_nh(struct rte_rib_node *node, uint64_t nh)
+{
+	if (node == NULL) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	node->nh = nh;
+	return 0;
+}
+
+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;
+
+	/* Check user arguments. */
+	if ((name == NULL) || (conf == NULL) ||
+			(conf->max_nodes == 0)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	snprintf(mem_name, sizeof(mem_name), "MP_%s", name);
+	node_pool = rte_mempool_create(mem_name, conf->max_nodes,
+		sizeof(struct rte_rib_node) + conf->ext_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);
+		return NULL;
+	}
+
+	snprintf(mem_name, sizeof(mem_name), "RIB_%s", name);
+	rib_list = RTE_TAILQ_CAST(rte_rib_tailq.head, rte_rib_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* 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 RIB data structures. */
+	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;
+	}
+
+	rte_strlcpy(rib->name, name, sizeof(rib->name));
+	rib->tree = NULL;
+	rib->max_nodes = conf->max_nodes;
+	rib->node_pool = node_pool;
+	te->data = (void *)rib;
+	TAILQ_INSERT_TAIL(rib_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	return rib;
+
+free_te:
+	rte_free(te);
+exit:
+	rte_mcfg_tailq_write_unlock();
+	rte_mempool_free(node_pool);
+
+	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_mcfg_tailq_read_lock();
+	TAILQ_FOREACH(te, rib_list, next) {
+		rib = (struct rte_rib *) te->data;
+		if (strncmp(name, rib->name, RTE_RIB_NAMESIZE) == 0)
+			break;
+	}
+	rte_mcfg_tailq_read_unlock();
+
+	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_mcfg_tailq_write_lock();
+
+	/* 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_mcfg_tailq_write_unlock();
+
+	while ((tmp = rte_rib_get_nxt(rib, 0, 0, tmp,
+			RTE_RIB_GET_NXT_ALL)) != NULL)
+		rte_rib_remove(rib, tmp->ip, tmp->depth);
+
+	rte_mempool_free(rib->node_pool);
+	rte_free(rib);
+	rte_free(te);
+}
diff --git a/lib/librte_rib/rte_rib.h b/lib/librte_rib/rte_rib.h
new file mode 100644
index 0000000..6b70de9
--- /dev/null
+++ b/lib/librte_rib/rte_rib.h
@@ -0,0 +1,277 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _RTE_RIB_H_
+#define _RTE_RIB_H_
+
+/**
+ * @file
+ * Level compressed tree implementation for IPv4 Longest Prefix Match
+ */
+
+#include <rte_compat.h>
+
+/**
+ * rte_rib_get_nxt() flags
+ */
+enum {
+	/** flag to get all subroutes in a RIB tree */
+	RTE_RIB_GET_NXT_ALL,
+	/** flag to get first matched subroutes in a RIB tree */
+	RTE_RIB_GET_NXT_COVER
+};
+
+struct rte_rib;
+struct rte_rib_node;
+
+/** RIB configuration structure */
+struct rte_rib_conf {
+	/**
+	 * Size of extension block inside rte_rib_node.
+	 * This space could be used to store additional user
+	 * defined data.
+	 */
+	size_t	ext_sz;
+	/* size of rte_rib_node's pool */
+	int	max_nodes;
+};
+
+/**
+ * Get an IPv4 mask from prefix length
+ * It is caller responsibility to make sure depth is not bigger than 32
+ *
+ * @param depth
+ *   prefix length
+ * @return
+ *  IPv4 mask
+ */
+static inline uint32_t
+rte_rib_depth_to_mask(uint8_t depth)
+{
+	return (uint32_t)(UINT64_MAX << (32 - depth));
+}
+
+/**
+ * Lookup an IP into the RIB structure
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  IP to be looked up in the RIB
+ * @return
+ *  pointer to struct rte_rib_node on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib_node *
+rte_rib_lookup(struct rte_rib *rib, uint32_t ip);
+
+/**
+ * Lookup less specific route into the RIB structure
+ *
+ * @param ent
+ *  Pointer to struct rte_rib_node that represents target route
+ * @return
+ *  pointer to struct rte_rib_node that represents
+ *   less specific route on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib_node *
+rte_rib_lookup_parent(struct rte_rib_node *ent);
+
+/**
+ * Lookup prefix into the RIB structure
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net to be looked up in the RIB
+ * @param depth
+ *  prefix length
+ * @return
+ *  pointer to struct rte_rib_node on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib_node *
+rte_rib_lookup_exact(struct rte_rib *rib, uint32_t ip, uint8_t depth);
+
+/**
+ * Retrieve next more specific prefix from the RIB
+ * that is covered by ip/depth supernet in an ascending order
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net address of supernet prefix that covers returned more specific prefixes
+ * @param depth
+ *  supernet prefix length
+ * @param last
+ *   pointer to the last returned prefix to get next prefix
+ *   or
+ *   NULL to get first more specific prefix
+ * @param flag
+ *  -RTE_RIB_GET_NXT_ALL
+ *   get all prefixes from subtrie
+ *  -RTE_RIB_GET_NXT_COVER
+ *   get only first more specific prefix even if it have more specifics
+ * @return
+ *  pointer to the next more specific prefix
+ *  NULL if there is no prefixes left
+ */
+__rte_experimental
+struct rte_rib_node *
+rte_rib_get_nxt(struct rte_rib *rib, uint32_t ip, uint8_t depth,
+	struct rte_rib_node *last, int flag);
+
+/**
+ * Remove prefix from the RIB
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net to be removed from the RIB
+ * @param depth
+ *  prefix length
+ */
+__rte_experimental
+void
+rte_rib_remove(struct rte_rib *rib, uint32_t ip, uint8_t depth);
+
+/**
+ * Insert prefix into the RIB
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net to be inserted to the RIB
+ * @param depth
+ *  prefix length
+ * @return
+ *  pointer to new rte_rib_node on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib_node *
+rte_rib_insert(struct rte_rib *rib, uint32_t ip, uint8_t depth);
+
+/**
+ * Get an ip from rte_rib_node
+ *
+ * @param node
+ *  pointer to the rib node
+ * @param ip
+ *  pointer to the ip to save
+ * @return
+ *  0 on success.
+ *  -1 on failure with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib_get_ip(struct rte_rib_node *node, uint32_t *ip);
+
+/**
+ * Get a depth from rte_rib_node
+ *
+ * @param node
+ *  pointer to the rib node
+ * @param depth
+ *  pointer to the depth to save
+ * @return
+ *  0 on success.
+ *  -1 on failure with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib_get_depth(struct rte_rib_node *node, uint8_t *depth);
+
+/**
+ * Get ext field from the rib node
+ * It is caller responsibility to make sure there are necessary space
+ * for the ext field inside rib node.
+ *
+ * @param node
+ *  pointer to the rib node
+ * @return
+ *  pointer to the ext
+ */
+__rte_experimental
+void *
+rte_rib_get_ext(struct rte_rib_node *node);
+
+/**
+ * Get nexthop from the rib node
+ *
+ * @param node
+ *  pointer to the rib node
+ * @param nh
+ *  pointer to the nexthop to save
+ * @return
+ *  0 on success.
+ *  -1 on failure with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib_get_nh(struct rte_rib_node *node, uint64_t *nh);
+
+/**
+ * Set nexthop into the rib node
+ *
+ * @param node
+ *  pointer to the rib node
+ * @param nh
+ *  nexthop value to set to the rib node
+ * @return
+ *  0 on success.
+ *  -1 on failure with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib_set_nh(struct rte_rib_node *node, uint64_t nh);
+
+/**
+ * Create RIB
+ *
+ * @param name
+ *  RIB name
+ * @param socket_id
+ *  NUMA socket ID for RIB table memory allocation
+ * @param conf
+ *  Structure containing the configuration
+ * @return
+ *  Handle to RIB object on success
+ *  NULL otherwise with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+struct rte_rib *
+rte_rib_create(const char *name, int socket_id, struct rte_rib_conf *conf);
+
+/**
+ * Find an existing RIB object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the rib object as passed to rte_rib_create()
+ * @return
+ *  Pointer to RIB object on success
+ *  NULL otherwise with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+struct rte_rib *
+rte_rib_find_existing(const char *name);
+
+/**
+ * Free an RIB object.
+ *
+ * @param rib
+ *   RIB object handle
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_rib_free(struct rte_rib *rib);
+
+#endif /* _RTE_RIB_H_ */
diff --git a/lib/librte_rib/rte_rib_version.map b/lib/librte_rib/rte_rib_version.map
new file mode 100644
index 0000000..1432a22
--- /dev/null
+++ b/lib/librte_rib/rte_rib_version.map
@@ -0,0 +1,20 @@
+EXPERIMENTAL {
+	global:
+
+	rte_rib_create;
+	rte_rib_find_existing;
+	rte_rib_free;
+	rte_rib_get_depth;
+	rte_rib_get_ext;
+	rte_rib_get_ip;
+	rte_rib_get_nh;
+	rte_rib_get_nxt;
+	rte_rib_insert;
+	rte_rib_lookup;
+	rte_rib_lookup_parent;
+	rte_rib_lookup_exact;
+	rte_rib_set_nh;
+	rte_rib_remove;
+
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index e5ff838..d7f2a04 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -22,7 +22,7 @@ libraries = [
 	'gro', 'gso', 'ip_frag', 'jobstats',
 	'kni', 'latencystats', 'lpm', 'member',
 	'power', 'pdump', 'rawdev',
-	'rcu', 'reorder', 'sched', 'security', 'stack', 'vhost',
+	'rcu', 'rib', 'reorder', 'sched', 'security', 'stack', 'vhost',
 	# ipsec lib depends on net, crypto and security
 	'ipsec',
 	# add pkt framework libs which use other libs from above
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index ba5c39e..4517874 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -45,6 +45,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_PDUMP)          += -lrte_pdump
 _LDLIBS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR)    += -lrte_distributor
 _LDLIBS-$(CONFIG_RTE_LIBRTE_IP_FRAG)        += -lrte_ip_frag
 _LDLIBS-$(CONFIG_RTE_LIBRTE_METER)          += -lrte_meter
+_LDLIBS-$(CONFIG_RTE_LIBRTE_RIB)            += -lrte_rib
 _LDLIBS-$(CONFIG_RTE_LIBRTE_LPM)            += -lrte_lpm
 _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL)            += -lrte_acl
 _LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY)      += --no-as-needed
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 02/12] test/rib: add RIB library autotests
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (7 preceding siblings ...)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 01/12] rib: add RIB library Vladimir Medvedkin
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 03/12] rib: add ipv6 support for RIB Vladimir Medvedkin
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

Functional tests for the new RIB library.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   2 +
 app/test/autotest_data.py |   6 +
 app/test/meson.build      |   3 +
 app/test/test_rib.c       | 351 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 362 insertions(+)
 create mode 100644 app/test/test_rib.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 26ba6fe..5bd8487 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -124,6 +124,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_multiwriter.c
 SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite.c
 SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite_lf.c
 
+SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib.c
+
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 7405149..3c3b806 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -111,6 +111,12 @@
         "Report":  None,
     },
     {
+        "Name":    "RIB autotest",
+        "Command": "rib_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
         "Name":    "Memcpy autotest",
         "Command": "memcpy_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index ec40943..98524e1 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -99,6 +99,7 @@ test_sources = files('commands.c',
 	'test_reciprocal_division_perf.c',
 	'test_red.c',
 	'test_reorder.c',
+	'test_rib.c',
 	'test_ring.c',
 	'test_ring_perf.c',
 	'test_rwlock.c',
@@ -147,6 +148,7 @@ test_deps = ['acl',
 	'rawdev',
 	'rcu',
 	'reorder',
+	'rib',
 	'ring',
 	'stack',
 	'timer'
@@ -197,6 +199,7 @@ fast_test_names = [
         'prefetch_autotest',
         'rcu_qsbr_autotest',
         'red_autotest',
+        'rib_autotest',
         'ring_autotest',
         'ring_pmd_autotest',
         'rwlock_test1_autotest',
diff --git a/app/test/test_rib.c b/app/test/test_rib.c
new file mode 100644
index 0000000..c95957a
--- /dev/null
+++ b/app/test/test_rib.c
@@ -0,0 +1,351 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <rte_ip.h>
+#include <rte_rib.h>
+
+#include "test.h"
+
+typedef int32_t (*rte_rib_test)(void);
+
+static int32_t test_create_invalid(void);
+static int32_t test_multiple_create(void);
+static int32_t test_free_null(void);
+static int32_t test_insert_invalid(void);
+static int32_t test_get_fn(void);
+static int32_t test_basic(void);
+static int32_t test_tree_traversal(void);
+
+#define MAX_DEPTH 32
+#define MAX_RULES (1 << 22)
+
+/*
+ * Check that rte_rib_create fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test_create_invalid(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf config;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	/* rte_rib_create: rib name == NULL */
+	rib = rte_rib_create(NULL, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_rib_create: config == NULL */
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, NULL);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* socket_id < -1 is invalid */
+	rib = rte_rib_create(__func__, -2, &config);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_rib_create: max_nodes = 0 */
+	config.max_nodes = 0;
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.max_nodes = MAX_RULES;
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Create rib table then delete rib table 10 times
+ * Use a slightly different rules size each time
+ */
+int32_t
+test_multiple_create(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf config;
+	int32_t i;
+
+	config.ext_sz = 0;
+
+	for (i = 0; i < 10; i++) {
+		config.max_nodes = MAX_RULES - i;
+		rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+		RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+		rte_rib_free(rib);
+	}
+	/* Can not test free so return success */
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call rte_rib_free for NULL pointer user input. Note: free has no return and
+ * therefore it is impossible to check for failure but this test is added to
+ * increase function coverage metrics and to validate that freeing null does
+ * not crash.
+ */
+int32_t
+test_free_null(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf config;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	rte_rib_free(rib);
+	rte_rib_free(NULL);
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_rib_insert fails gracefully for incorrect user input arguments
+ */
+int32_t
+test_insert_invalid(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_node *node, *node1;
+	struct rte_rib_conf config;
+	uint32_t ip = RTE_IPV4(0, 0, 0, 0);
+	uint8_t depth = 24;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	/* rte_rib_insert: rib == NULL */
+	node = rte_rib_insert(NULL, ip, depth);
+	RTE_TEST_ASSERT(node == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/*Create valid rib to use in rest of test. */
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	/* rte_rib_insert: depth > MAX_DEPTH */
+	node = rte_rib_insert(rib, ip, MAX_DEPTH + 1);
+	RTE_TEST_ASSERT(node == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* insert the same ip/depth twice*/
+	node = rte_rib_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+	node1 = rte_rib_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node1 == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	rte_rib_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call rte_rib_node access functions with incorrect input.
+ * After call rte_rib_node access functions with correct args
+ * and check the return values for correctness
+ */
+int32_t
+test_get_fn(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_node *node;
+	struct rte_rib_conf config;
+	void *ext;
+	uint32_t ip = RTE_IPV4(192, 0, 2, 0);
+	uint32_t ip_ret;
+	uint64_t nh_set = 10;
+	uint64_t nh_ret;
+	uint8_t depth = 24;
+	uint8_t depth_ret;
+	int ret;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	node = rte_rib_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	/* test rte_rib_get_ip() with incorrect args */
+	ret = rte_rib_get_ip(NULL, &ip_ret);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+	ret = rte_rib_get_ip(node, NULL);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib_get_depth() with incorrect args */
+	ret = rte_rib_get_depth(NULL, &depth_ret);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+	ret = rte_rib_get_depth(node, NULL);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib_set_nh() with incorrect args */
+	ret = rte_rib_set_nh(NULL, nh_set);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib_get_nh() with incorrect args */
+	ret = rte_rib_get_nh(NULL, &nh_ret);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+	ret = rte_rib_get_nh(node, NULL);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib_get_ext() with incorrect args */
+	ext = rte_rib_get_ext(NULL);
+	RTE_TEST_ASSERT(ext == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* check the return values */
+	ret = rte_rib_get_ip(node, &ip_ret);
+	RTE_TEST_ASSERT((ret == 0) && (ip_ret == ip),
+		"Failed to get proper node ip\n");
+	ret = rte_rib_get_depth(node, &depth_ret);
+	RTE_TEST_ASSERT((ret == 0) && (depth_ret == depth),
+		"Failed to get proper node depth\n");
+	ret = rte_rib_set_nh(node, nh_set);
+	RTE_TEST_ASSERT(ret == 0,
+		"Failed to set rte_rib_node nexthop\n");
+	ret = rte_rib_get_nh(node, &nh_ret);
+	RTE_TEST_ASSERT((ret == 0) && (nh_ret == nh_set),
+		"Failed to get proper nexthop\n");
+
+	rte_rib_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call insert, lookup/lookup_exact and delete for a single rule
+ */
+int32_t
+test_basic(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_node *node;
+	struct rte_rib_conf config;
+
+	uint32_t ip = RTE_IPV4(192, 0, 2, 0);
+	uint64_t next_hop_add = 10;
+	uint64_t next_hop_return;
+	uint8_t depth = 24;
+	int ret;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	node = rte_rib_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	ret = rte_rib_set_nh(node, next_hop_add);
+	RTE_TEST_ASSERT(ret == 0,
+		"Failed to set rte_rib_node field\n");
+
+	node = rte_rib_lookup(rib, ip);
+	RTE_TEST_ASSERT(node != NULL, "Failed to lookup\n");
+
+	ret = rte_rib_get_nh(node, &next_hop_return);
+	RTE_TEST_ASSERT((ret == 0) && (next_hop_add == next_hop_return),
+		"Failed to get proper nexthop\n");
+
+	node = rte_rib_lookup_exact(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL,
+		"Failed to lookup\n");
+
+	ret = rte_rib_get_nh(node, &next_hop_return);
+	RTE_TEST_ASSERT((ret == 0) && (next_hop_add == next_hop_return),
+		"Failed to get proper nexthop\n");
+
+	rte_rib_remove(rib, ip, depth);
+
+	node = rte_rib_lookup(rib, ip);
+	RTE_TEST_ASSERT(node == NULL,
+		"Lookup returns non existent rule\n");
+	node = rte_rib_lookup_exact(rib, ip, depth);
+	RTE_TEST_ASSERT(node == NULL,
+		"Lookup returns non existent rule\n");
+
+	rte_rib_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+int32_t
+test_tree_traversal(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_node *node;
+	struct rte_rib_conf config;
+
+	uint32_t ip1 = RTE_IPV4(10, 10, 10, 0);
+	uint32_t ip2 = RTE_IPV4(10, 10, 130, 80);
+	uint8_t depth = 30;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	node = rte_rib_insert(rib, ip1, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	node = rte_rib_insert(rib, ip2, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	node = NULL;
+	node = rte_rib_get_nxt(rib, RTE_IPV4(10, 10, 130, 0), 24, node,
+			RTE_RIB_GET_NXT_ALL);
+	RTE_TEST_ASSERT(node != NULL, "Failed to get rib_node\n");
+
+	rte_rib_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite rib_tests = {
+	.suite_name = "rib autotest",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+		TEST_CASE(test_create_invalid),
+		TEST_CASE(test_multiple_create),
+		TEST_CASE(test_free_null),
+		TEST_CASE(test_insert_invalid),
+		TEST_CASE(test_get_fn),
+		TEST_CASE(test_basic),
+		TEST_CASE(test_tree_traversal),
+		TEST_CASES_END()
+	}
+};
+
+/*
+ * Do all unit tests.
+ */
+static int
+test_rib(void)
+{
+	return unit_test_suite_runner(&rib_tests);
+}
+
+REGISTER_TEST_COMMAND(rib_autotest, test_rib);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 03/12] rib: add ipv6 support for RIB
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (8 preceding siblings ...)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 02/12] test/rib: add RIB library autotests Vladimir Medvedkin
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 04/12] test/rib: add ipv6 support for RIB autotests Vladimir Medvedkin
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

Extend RIB library with ipv6 support.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 lib/librte_rib/Makefile            |   4 +-
 lib/librte_rib/meson.build         |   4 +-
 lib/librte_rib/rte_rib6.c          | 598 +++++++++++++++++++++++++++++++++++++
 lib/librte_rib/rte_rib6.h          | 334 +++++++++++++++++++++
 lib/librte_rib/rte_rib_version.map |  15 +
 5 files changed, 951 insertions(+), 4 deletions(-)
 create mode 100644 lib/librte_rib/rte_rib6.c
 create mode 100644 lib/librte_rib/rte_rib6.h

diff --git a/lib/librte_rib/Makefile b/lib/librte_rib/Makefile
index 79f259a..6d861ad 100644
--- a/lib/librte_rib/Makefile
+++ b/lib/librte_rib/Makefile
@@ -17,9 +17,9 @@ EXPORT_MAP := rte_rib_version.map
 LIBABIVER := 1
 
 # all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_RIB) := rte_rib.c
+SRCS-$(CONFIG_RTE_LIBRTE_RIB) := rte_rib.c rte_rib6.c
 
 # install this header file
-SYMLINK-$(CONFIG_RTE_LIBRTE_RIB)-include := rte_rib.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_RIB)-include := rte_rib.h rte_rib6.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_rib/meson.build b/lib/librte_rib/meson.build
index e7b8920..46a1c0c 100644
--- a/lib/librte_rib/meson.build
+++ b/lib/librte_rib/meson.build
@@ -3,6 +3,6 @@
 # Copyright(c) 2019 Intel Corporation
 
 allow_experimental_apis = true
-sources = files('rte_rib.c')
-headers = files('rte_rib.h')
+sources = files('rte_rib.c', 'rte_rib6.c')
+headers = files('rte_rib.h', 'rte_rib6.h')
 deps += ['mempool']
diff --git a/lib/librte_rib/rte_rib6.c b/lib/librte_rib/rte_rib6.c
new file mode 100644
index 0000000..78b8dcf
--- /dev/null
+++ b/lib/librte_rib/rte_rib6.c
@@ -0,0 +1,598 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_mempool.h>
+#include <rte_rwlock.h>
+#include <rte_string_fns.h>
+#include <rte_tailq.h>
+
+#include <rte_rib6.h>
+
+#define RTE_RIB_VALID_NODE	1
+#define RIB6_MAXDEPTH		128
+/* Maximum length of a RIB6 name. */
+#define RTE_RIB6_NAMESIZE	64
+
+TAILQ_HEAD(rte_rib6_list, rte_tailq_entry);
+static struct rte_tailq_elem rte_rib6_tailq = {
+	.name = "RTE_RIB6",
+};
+EAL_REGISTER_TAILQ(rte_rib6_tailq)
+
+struct rte_rib6_node {
+	struct rte_rib6_node	*left;
+	struct rte_rib6_node	*right;
+	struct rte_rib6_node	*parent;
+	uint64_t		nh;
+	uint8_t			ip[RTE_RIB6_IPV6_ADDR_SIZE];
+	uint8_t			depth;
+	uint8_t			flag;
+	__extension__ uint64_t		ext[0];
+};
+
+struct rte_rib6 {
+	char		name[RTE_RIB6_NAMESIZE];
+	struct rte_rib6_node	*tree;
+	struct rte_mempool	*node_pool;
+	uint32_t		cur_nodes;
+	uint32_t		cur_routes;
+	int			max_nodes;
+};
+
+static inline bool
+is_valid_node(struct rte_rib6_node *node)
+{
+	return (node->flag & RTE_RIB_VALID_NODE) == RTE_RIB_VALID_NODE;
+}
+
+static inline bool
+is_right_node(struct rte_rib6_node *node)
+{
+	return node->parent->right == node;
+}
+
+/*
+ * Check if ip1 is covered by ip2/depth prefix
+ */
+static inline bool
+is_covered(const uint8_t ip1[RTE_RIB6_IPV6_ADDR_SIZE],
+		const uint8_t ip2[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)
+{
+	int i;
+
+	for (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)
+		if ((ip1[i] ^ ip2[i]) & get_msk_part(depth, i))
+			return false;
+
+	return true;
+}
+
+static inline int
+get_dir(const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)
+{
+	int i = 0;
+	uint8_t p_depth, msk;
+
+	for (p_depth = depth; p_depth >= 8; p_depth -= 8)
+		i++;
+
+	msk = 1 << (7 - p_depth);
+	return (ip[i] & msk) != 0;
+}
+
+static inline struct rte_rib6_node *
+get_nxt_node(struct rte_rib6_node *node,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE])
+{
+	return (get_dir(ip, node->depth)) ? node->right : node->left;
+}
+
+static struct rte_rib6_node *
+node_alloc(struct rte_rib6 *rib)
+{
+	struct rte_rib6_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
+node_free(struct rte_rib6 *rib, struct rte_rib6_node *ent)
+{
+	--rib->cur_nodes;
+	rte_mempool_put(rib->node_pool, ent);
+}
+
+struct rte_rib6_node *
+rte_rib6_lookup(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE])
+{
+	struct rte_rib6_node *cur;
+	struct rte_rib6_node *prev = NULL;
+
+	if (unlikely(rib == NULL)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+	cur = rib->tree;
+
+	while ((cur != NULL) && is_covered(ip, cur->ip, cur->depth)) {
+		if (is_valid_node(cur))
+			prev = cur;
+		cur = get_nxt_node(cur, ip);
+	}
+	return prev;
+}
+
+struct rte_rib6_node *
+rte_rib6_lookup_parent(struct rte_rib6_node *ent)
+{
+	struct rte_rib6_node *tmp;
+
+	if (ent == NULL)
+		return NULL;
+
+	tmp = ent->parent;
+	while ((tmp != NULL) && (!is_valid_node(tmp)))
+		tmp = tmp->parent;
+
+	return tmp;
+}
+
+struct rte_rib6_node *
+rte_rib6_lookup_exact(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)
+{
+	struct rte_rib6_node *cur;
+	uint8_t tmp_ip[RTE_RIB6_IPV6_ADDR_SIZE];
+	int i;
+
+	if ((rib == NULL) || (ip == NULL) || (depth > RIB6_MAXDEPTH)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+	cur = rib->tree;
+
+	for (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)
+		tmp_ip[i] = ip[i] & get_msk_part(depth, i);
+
+	while (cur != NULL) {
+		if (rte_rib6_is_equal(cur->ip, tmp_ip) &&
+				(cur->depth == depth) &&
+				is_valid_node(cur))
+			return cur;
+
+		if (!(is_covered(tmp_ip, cur->ip, cur->depth)) ||
+				(cur->depth >= depth))
+			break;
+
+		cur = get_nxt_node(cur, tmp_ip);
+	}
+
+	return NULL;
+}
+
+/*
+ *  Traverses on subtree and retreeves more specific routes
+ *  for a given in args ip/depth prefix
+ *  last = NULL means the first invocation
+ */
+struct rte_rib6_node *
+rte_rib6_get_nxt(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, struct rte_rib6_node *last, int flag)
+{
+	struct rte_rib6_node *tmp, *prev = NULL;
+	uint8_t tmp_ip[RTE_RIB6_IPV6_ADDR_SIZE];
+	int i;
+
+	if ((rib == NULL) || (ip == NULL) || (depth > RIB6_MAXDEPTH)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	for (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)
+		tmp_ip[i] = ip[i] & get_msk_part(depth, i);
+
+	if (last == NULL) {
+		tmp = rib->tree;
+		while ((tmp) && (tmp->depth < depth))
+			tmp = get_nxt_node(tmp, tmp_ip);
+	} else {
+		tmp = last;
+		while ((tmp->parent != NULL) && (is_right_node(tmp) ||
+				(tmp->parent->right == NULL))) {
+			tmp = tmp->parent;
+			if (is_valid_node(tmp) &&
+					(is_covered(tmp->ip, tmp_ip, depth) &&
+					(tmp->depth > depth)))
+				return tmp;
+		}
+		tmp = (tmp->parent != NULL) ? tmp->parent->right : NULL;
+	}
+	while (tmp) {
+		if (is_valid_node(tmp) &&
+				(is_covered(tmp->ip, tmp_ip, depth) &&
+				(tmp->depth > depth))) {
+			prev = tmp;
+			if (flag == RTE_RIB6_GET_NXT_COVER)
+				return prev;
+		}
+		tmp = (tmp->left != NULL) ? tmp->left : tmp->right;
+	}
+	return prev;
+}
+
+void
+rte_rib6_remove(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)
+{
+	struct rte_rib6_node *cur, *prev, *child;
+
+	cur = rte_rib6_lookup_exact(rib, ip, depth);
+	if (cur == NULL)
+		return;
+
+	--rib->cur_routes;
+	cur->flag &= ~RTE_RIB_VALID_NODE;
+	while (!is_valid_node(cur)) {
+		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->tree = child;
+			node_free(rib, cur);
+			return;
+		}
+		if (cur->parent->left == cur)
+			cur->parent->left = child;
+		else
+			cur->parent->right = child;
+		prev = cur;
+		cur = cur->parent;
+		node_free(rib, prev);
+	}
+}
+
+struct rte_rib6_node *
+rte_rib6_insert(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)
+{
+	struct rte_rib6_node **tmp;
+	struct rte_rib6_node *prev = NULL;
+	struct rte_rib6_node *new_node = NULL;
+	struct rte_rib6_node *common_node = NULL;
+	uint8_t common_prefix[RTE_RIB6_IPV6_ADDR_SIZE];
+	uint8_t tmp_ip[RTE_RIB6_IPV6_ADDR_SIZE];
+	int i, d;
+	uint8_t common_depth, ip_xor;
+
+	if (unlikely((rib == NULL) || (ip == NULL) ||
+			(depth > RIB6_MAXDEPTH))) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	tmp = &rib->tree;
+
+	for (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)
+		tmp_ip[i] = ip[i] & get_msk_part(depth, i);
+
+	new_node = rte_rib6_lookup_exact(rib, tmp_ip, depth);
+	if (new_node != NULL) {
+		rte_errno = EEXIST;
+		return NULL;
+	}
+
+	new_node = node_alloc(rib);
+	if (new_node == NULL) {
+		rte_errno = ENOMEM;
+		return NULL;
+	}
+	new_node->left = NULL;
+	new_node->right = NULL;
+	new_node->parent = NULL;
+	rte_rib6_copy_addr(new_node->ip, tmp_ip);
+	new_node->depth = depth;
+	new_node->flag = RTE_RIB_VALID_NODE;
+
+	/* traverse down the tree to find matching node or closest matching */
+	while (1) {
+		/* insert as the last node in the branch */
+		if (*tmp == NULL) {
+			*tmp = new_node;
+			new_node->parent = prev;
+			++rib->cur_routes;
+			return *tmp;
+		}
+		/*
+		 * Intermediate node found.
+		 * Previous rte_rib6_lookup_exact() returned NULL
+		 * but node with proper search criteria is found.
+		 * Validate intermediate node and return.
+		 */
+		if (rte_rib6_is_equal(tmp_ip, (*tmp)->ip) &&
+				(depth == (*tmp)->depth)) {
+			node_free(rib, new_node);
+			(*tmp)->flag |= RTE_RIB_VALID_NODE;
+			++rib->cur_routes;
+			return *tmp;
+		}
+
+		if (!is_covered(tmp_ip, (*tmp)->ip, (*tmp)->depth) ||
+				((*tmp)->depth >= depth)) {
+			break;
+		}
+		prev = *tmp;
+
+		tmp = (get_dir(tmp_ip, (*tmp)->depth)) ? &(*tmp)->right :
+				&(*tmp)->left;
+	}
+
+	/* closest node found, new_node should be inserted in the middle */
+	common_depth = RTE_MIN(depth, (*tmp)->depth);
+	for (i = 0, d = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++) {
+		ip_xor = tmp_ip[i] ^ (*tmp)->ip[i];
+		if (ip_xor == 0)
+			d += 8;
+		else {
+			d += __builtin_clz(ip_xor << 24);
+			break;
+		}
+	}
+
+	common_depth = RTE_MIN(d, common_depth);
+
+	for (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)
+		common_prefix[i] = tmp_ip[i] & get_msk_part(common_depth, i);
+
+	if (rte_rib6_is_equal(common_prefix, tmp_ip) &&
+			(common_depth == depth)) {
+		/* insert as a parent */
+		if (get_dir((*tmp)->ip, depth))
+			new_node->right = *tmp;
+		else
+			new_node->left = *tmp;
+		new_node->parent = (*tmp)->parent;
+		(*tmp)->parent = new_node;
+		*tmp = new_node;
+	} else {
+		/* create intermediate node */
+		common_node = node_alloc(rib);
+		if (common_node == NULL) {
+			node_free(rib, new_node);
+			rte_errno = ENOMEM;
+			return NULL;
+		}
+		rte_rib6_copy_addr(common_node->ip, 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 (get_dir((*tmp)->ip, common_depth) == 1) {
+			common_node->left = new_node;
+			common_node->right = *tmp;
+		} else {
+			common_node->left = *tmp;
+			common_node->right = new_node;
+		}
+		*tmp = common_node;
+	}
+	++rib->cur_routes;
+	return new_node;
+}
+
+int
+rte_rib6_get_ip(struct rte_rib6_node *node, uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE])
+{
+	if ((node == NULL) || (ip == NULL)) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	rte_rib6_copy_addr(ip, node->ip);
+	return 0;
+}
+
+int
+rte_rib6_get_depth(struct rte_rib6_node *node, uint8_t *depth)
+{
+	if ((node == NULL) || (depth == NULL)) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	*depth = node->depth;
+	return 0;
+}
+
+void *
+rte_rib6_get_ext(struct rte_rib6_node *node)
+{
+	return (node == NULL) ? NULL : &node->ext[0];
+}
+
+int
+rte_rib6_get_nh(struct rte_rib6_node *node, uint64_t *nh)
+{
+	if ((node == NULL) || (nh == NULL)) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	*nh = node->nh;
+	return 0;
+}
+
+int
+rte_rib6_set_nh(struct rte_rib6_node *node, uint64_t nh)
+{
+	if (node == NULL) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	node->nh = nh;
+	return 0;
+}
+
+struct rte_rib6 *
+rte_rib6_create(const char *name, int socket_id, struct rte_rib6_conf *conf)
+{
+	char mem_name[RTE_RIB6_NAMESIZE];
+	struct rte_rib6 *rib = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_rib6_list *rib6_list;
+	struct rte_mempool *node_pool;
+
+	/* Check user arguments. */
+	if ((name == NULL) || (conf == NULL) ||
+			(conf->max_nodes == 0)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	snprintf(mem_name, sizeof(mem_name), "MP_%s", name);
+	node_pool = rte_mempool_create(mem_name, conf->max_nodes,
+		sizeof(struct rte_rib6_node) + conf->ext_sz, 0, 0,
+		NULL, NULL, NULL, NULL, socket_id, 0);
+
+	if (node_pool == NULL) {
+		RTE_LOG(ERR, LPM,
+			"Can not allocate mempool for RIB6 %s\n", name);
+		return NULL;
+	}
+
+	snprintf(mem_name, sizeof(mem_name), "RIB6_%s", name);
+	rib6_list = RTE_TAILQ_CAST(rte_rib6_tailq.head, rte_rib6_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* guarantee there's no existing */
+	TAILQ_FOREACH(te, rib6_list, next) {
+		rib = (struct rte_rib6 *)te->data;
+		if (strncmp(name, rib->name, RTE_RIB6_NAMESIZE) == 0)
+			break;
+	}
+	rib = NULL;
+	if (te != NULL) {
+		rte_errno = EEXIST;
+		goto exit;
+	}
+
+	/* allocate tailq entry */
+	te = rte_zmalloc("RIB6_TAILQ_ENTRY", sizeof(*te), 0);
+	if (te == NULL) {
+		RTE_LOG(ERR, LPM,
+			"Can not allocate tailq entry for RIB6 %s\n", name);
+		rte_errno = ENOMEM;
+		goto exit;
+	}
+
+	/* Allocate memory to store the RIB6 data structures. */
+	rib = rte_zmalloc_socket(mem_name,
+		sizeof(struct rte_rib6), RTE_CACHE_LINE_SIZE, socket_id);
+	if (rib == NULL) {
+		RTE_LOG(ERR, LPM, "RIB6 %s memory allocation failed\n", name);
+		rte_errno = ENOMEM;
+		goto free_te;
+	}
+
+	rte_strlcpy(rib->name, name, sizeof(rib->name));
+	rib->tree = NULL;
+	rib->max_nodes = conf->max_nodes;
+	rib->node_pool = node_pool;
+
+	te->data = (void *)rib;
+	TAILQ_INSERT_TAIL(rib6_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	return rib;
+
+free_te:
+	rte_free(te);
+exit:
+	rte_mcfg_tailq_write_unlock();
+	rte_mempool_free(node_pool);
+
+	return NULL;
+}
+
+struct rte_rib6 *
+rte_rib6_find_existing(const char *name)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_rib6_list *rib6_list;
+
+	if (unlikely(name == NULL)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	rib6_list = RTE_TAILQ_CAST(rte_rib6_tailq.head, rte_rib6_list);
+
+	rte_mcfg_tailq_read_lock();
+	TAILQ_FOREACH(te, rib6_list, next) {
+		rib = (struct rte_rib6 *) te->data;
+		if (strncmp(name, rib->name, RTE_RIB6_NAMESIZE) == 0)
+			break;
+	}
+	rte_mcfg_tailq_read_unlock();
+
+	if (te == NULL) {
+		rte_errno = ENOENT;
+		return NULL;
+	}
+
+	return rib;
+}
+
+void
+rte_rib6_free(struct rte_rib6 *rib)
+{
+	struct rte_tailq_entry *te;
+	struct rte_rib6_list *rib6_list;
+	struct rte_rib6_node *tmp = NULL;
+
+	if (unlikely(rib == NULL)) {
+		rte_errno = EINVAL;
+		return;
+	}
+
+	rib6_list = RTE_TAILQ_CAST(rte_rib6_tailq.head, rte_rib6_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* find our tailq entry */
+	TAILQ_FOREACH(te, rib6_list, next) {
+		if (te->data == (void *)rib)
+			break;
+	}
+	if (te != NULL)
+		TAILQ_REMOVE(rib6_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	while ((tmp = rte_rib6_get_nxt(rib, 0, 0, tmp,
+			RTE_RIB6_GET_NXT_ALL)) != NULL)
+		rte_rib6_remove(rib, tmp->ip, tmp->depth);
+
+	rte_mempool_free(rib->node_pool);
+
+	rte_free(rib);
+	rte_free(te);
+}
diff --git a/lib/librte_rib/rte_rib6.h b/lib/librte_rib/rte_rib6.h
new file mode 100644
index 0000000..8714571
--- /dev/null
+++ b/lib/librte_rib/rte_rib6.h
@@ -0,0 +1,334 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _RTE_RIB6_H_
+#define _RTE_RIB6_H_
+
+/**
+ * @file
+ * Level compressed tree implementation for IPv6 Longest Prefix Match
+ */
+
+#include <rte_memcpy.h>
+#include <rte_compat.h>
+
+#define RTE_RIB6_IPV6_ADDR_SIZE	16
+
+/**
+ * rte_rib6_get_nxt() flags
+ */
+enum {
+	/** flag to get all subroutes in a RIB tree */
+	RTE_RIB6_GET_NXT_ALL,
+	/** flag to get first matched subroutes in a RIB tree */
+	RTE_RIB6_GET_NXT_COVER
+};
+
+struct rte_rib6;
+struct rte_rib6_node;
+
+/** RIB configuration structure */
+struct rte_rib6_conf {
+	/**
+	 * Size of extension block inside rte_rib_node.
+	 * This space could be used to store additional user
+	 * defined data.
+	 */
+	size_t	ext_sz;
+	/* size of rte_rib_node's pool */
+	int	max_nodes;
+};
+
+/**
+ * Copy IPv6 address from one location to another
+ *
+ * @param dst
+ *  pointer to the place to copy
+ * @param src
+ *  pointer from where to copy
+ */
+static inline void
+rte_rib6_copy_addr(uint8_t *dst, const uint8_t *src)
+{
+	if ((dst == NULL) || (src == NULL))
+		return;
+	rte_memcpy(dst, src, RTE_RIB6_IPV6_ADDR_SIZE);
+}
+
+/**
+ * Compare two IPv6 addresses
+ *
+ * @param ip1
+ *  pointer to the first ipv6 address
+ * @param ip2
+ *  pointer to the second ipv6 address
+ *
+ * @return
+ *  1 if equal
+ *  0 otherwise
+ */
+static inline int
+rte_rib6_is_equal(uint8_t *ip1, uint8_t *ip2) {
+	int i;
+
+	if ((ip1 == NULL) || (ip2 == NULL))
+		return 0;
+	for (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++) {
+		if (ip1[i] != ip2[i])
+			return 0;
+	}
+	return 1;
+}
+
+/**
+ * Get 8-bit part of 128-bit IPv6 mask
+ *
+ * @param depth
+ *  ipv6 prefix length
+ * @param byte
+ *  position of a 8-bit chunk in the 128-bit mask
+ *
+ * @return
+ *  8-bit chunk of the 128-bit IPv6 mask
+ */
+static inline uint8_t
+get_msk_part(uint8_t depth, int byte) {
+	uint8_t part;
+
+	byte &= 0xf;
+	depth = RTE_MIN(depth, 128);
+	part = RTE_MAX((int16_t)depth - (byte * 8), 0);
+	part = (part > 8) ? 8 : part;
+	return (uint16_t)(~UINT8_MAX) >> part;
+}
+
+/**
+ * Lookup an IP into the RIB structure
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  IP to be looked up in the RIB
+ * @return
+ *  pointer to struct rte_rib6_node on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib6_node *
+rte_rib6_lookup(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE]);
+
+/**
+ * Lookup less specific route into the RIB structure
+ *
+ * @param ent
+ *  Pointer to struct rte_rib6_node that represents target route
+ * @return
+ *  pointer to struct rte_rib6_node that represents
+ *   less specific route on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib6_node *
+rte_rib6_lookup_parent(struct rte_rib6_node *ent);
+
+/**
+ * Provides exact mach lookup of the prefix into the RIB structure
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net to be looked up in the RIB
+ * @param depth
+ *  prefix length
+ * @return
+ *  pointer to struct rte_rib6_node on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib6_node *
+rte_rib6_lookup_exact(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth);
+
+/**
+ * Retrieve next more specific prefix from the RIB
+ * that is covered by ip/depth supernet in an ascending order
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net address of supernet prefix that covers returned more specific prefixes
+ * @param depth
+ *  supernet prefix length
+ * @param last
+ *   pointer to the last returned prefix to get next prefix
+ *   or
+ *   NULL to get first more specific prefix
+ * @param flag
+ *  -RTE_RIB6_GET_NXT_ALL
+ *   get all prefixes from subtrie
+ *  -RTE_RIB6_GET_NXT_COVER
+ *   get only first more specific prefix even if it have more specifics
+ * @return
+ *  pointer to the next more specific prefix
+ *  NULL if there is no prefixes left
+ */
+__rte_experimental
+struct rte_rib6_node *
+rte_rib6_get_nxt(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, struct rte_rib6_node *last, int flag);
+
+/**
+ * Remove prefix from the RIB
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net to be removed from the RIB
+ * @param depth
+ *  prefix length
+ */
+__rte_experimental
+void
+rte_rib6_remove(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth);
+
+/**
+ * Insert prefix into the RIB
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net to be inserted to the RIB
+ * @param depth
+ *  prefix length
+ * @return
+ *  pointer to new rte_rib6_node on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib6_node *
+rte_rib6_insert(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth);
+
+/**
+ * Get an ip from rte_rib6_node
+ *
+ * @param node
+ *  pointer to the rib6 node
+ * @param ip
+ *  pointer to the ipv6 to save
+ * @return
+ *  0 on success
+ *  -1 on failure with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib6_get_ip(struct rte_rib6_node *node,
+	uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE]);
+
+/**
+ * Get a depth from rte_rib6_node
+ *
+ * @param node
+ *  pointer to the rib6 node
+ * @param depth
+ *  pointer to the depth to save
+ * @return
+ *  0 on success
+ *  -1 on failure with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib6_get_depth(struct rte_rib6_node *node, uint8_t *depth);
+
+/**
+ * Get ext field from the rte_rib6_node
+ * It is caller responsibility to make sure there are necessary space
+ * for the ext field inside rib6 node.
+ *
+ * @param node
+ *  pointer to the rte_rib6_node
+ * @return
+ *  pointer to the ext
+ */
+__rte_experimental
+void *
+rte_rib6_get_ext(struct rte_rib6_node *node);
+
+/**
+ * Get nexthop from the rte_rib6_node
+ *
+ * @param node
+ *  pointer to the rib6 node
+ * @param nh
+ *  pointer to the nexthop to save
+ * @return
+ *  0 on success
+ *  -1 on failure, with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib6_get_nh(struct rte_rib6_node *node, uint64_t *nh);
+
+/**
+ * Set nexthop into the rte_rib6_node
+ *
+ * @param node
+ *  pointer to the rib6 node
+ * @param nh
+ *  nexthop value to set to the rib6 node
+ * @return
+ *  0 on success
+ *  -1 on failure, with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib6_set_nh(struct rte_rib6_node *node, uint64_t nh);
+
+/**
+ * Create RIB
+ *
+ * @param name
+ *  RIB name
+ * @param socket_id
+ *  NUMA socket ID for RIB table memory allocation
+ * @param conf
+ *  Structure containing the configuration
+ * @return
+ *  Pointer to RIB object on success
+ *  NULL otherwise with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+struct rte_rib6 *
+rte_rib6_create(const char *name, int socket_id, struct rte_rib6_conf *conf);
+
+/**
+ * Find an existing RIB object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the rib object as passed to rte_rib_create()
+ * @return
+ *  Pointer to RIB object on success
+ *  NULL otherwise with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+struct rte_rib6 *
+rte_rib6_find_existing(const char *name);
+
+/**
+ * Free an RIB object.
+ *
+ * @param rib
+ *   RIB object handle
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_rib6_free(struct rte_rib6 *rib);
+
+#endif /* _RTE_RIB_H_ */
diff --git a/lib/librte_rib/rte_rib_version.map b/lib/librte_rib/rte_rib_version.map
index 1432a22..9b6161a 100644
--- a/lib/librte_rib/rte_rib_version.map
+++ b/lib/librte_rib/rte_rib_version.map
@@ -16,5 +16,20 @@ EXPERIMENTAL {
 	rte_rib_set_nh;
 	rte_rib_remove;
 
+	rte_rib6_create;
+	rte_rib6_find_existing;
+	rte_rib6_free;
+	rte_rib6_get_depth;
+	rte_rib6_get_ext;
+	rte_rib6_get_ip;
+	rte_rib6_get_nh;
+	rte_rib6_get_nxt;
+	rte_rib6_insert;
+	rte_rib6_lookup;
+	rte_rib6_lookup_parent;
+	rte_rib6_lookup_exact;
+	rte_rib6_set_nh;
+	rte_rib6_remove;
+
 	local: *;
 };
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 04/12] test/rib: add ipv6 support for RIB autotests
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (9 preceding siblings ...)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 03/12] rib: add ipv6 support for RIB Vladimir Medvedkin
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 05/12] fib: add FIB library Vladimir Medvedkin
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

Functional tests for RIB6.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |   6 +
 app/test/meson.build      |   2 +
 app/test/test_rib6.c      | 357 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 366 insertions(+)
 create mode 100644 app/test/test_rib6.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 5bd8487..4638426 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -125,6 +125,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite.c
 SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite_lf.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib.c
+SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib6.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 3c3b806..f35b3cc 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -117,6 +117,12 @@
         "Report":  None,
     },
     {
+        "Name":    "RIB6 autotest",
+        "Command": "rib6_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
         "Name":    "Memcpy autotest",
         "Command": "memcpy_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 98524e1..49c1de0 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -100,6 +100,7 @@ test_sources = files('commands.c',
 	'test_red.c',
 	'test_reorder.c',
 	'test_rib.c',
+	'test_rib6.c',
 	'test_ring.c',
 	'test_ring_perf.c',
 	'test_rwlock.c',
@@ -200,6 +201,7 @@ fast_test_names = [
         'rcu_qsbr_autotest',
         'red_autotest',
         'rib_autotest',
+        'rib6_autotest',
         'ring_autotest',
         'ring_pmd_autotest',
         'rwlock_test1_autotest',
diff --git a/app/test/test_rib6.c b/app/test/test_rib6.c
new file mode 100644
index 0000000..638ba68
--- /dev/null
+++ b/app/test/test_rib6.c
@@ -0,0 +1,357 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <rte_ip.h>
+#include <rte_rib6.h>
+
+#include "test.h"
+
+typedef int32_t (*rte_rib6_test)(void);
+
+static int32_t test_create_invalid(void);
+static int32_t test_multiple_create(void);
+static int32_t test_free_null(void);
+static int32_t test_insert_invalid(void);
+static int32_t test_get_fn(void);
+static int32_t test_basic(void);
+static int32_t test_tree_traversal(void);
+
+#define MAX_DEPTH 128
+#define MAX_RULES (1 << 22)
+
+/*
+ * Check that rte_rib6_create fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test_create_invalid(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_conf config;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	/* rte_rib6_create: rib name == NULL */
+	rib = rte_rib6_create(NULL, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_rib6_create: config == NULL */
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, NULL);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* socket_id < -1 is invalid */
+	rib = rte_rib6_create(__func__, -2, &config);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_rib6_create: max_nodes = 0 */
+	config.max_nodes = 0;
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.max_nodes = MAX_RULES;
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Create rib table then delete rib table 10 times
+ * Use a slightly different rules size each time
+ */
+int32_t
+test_multiple_create(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_conf config;
+	int32_t i;
+
+	config.ext_sz = 0;
+
+	for (i = 0; i < 10; i++) {
+		config.max_nodes = MAX_RULES - i;
+		rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+		RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+		rte_rib6_free(rib);
+	}
+	/* Can not test free so return success */
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call rte_rib6_free for NULL pointer user input. Note: free has no return and
+ * therefore it is impossible to check for failure but this test is added to
+ * increase function coverage metrics and to validate that freeing null does
+ * not crash.
+ */
+int32_t
+test_free_null(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_conf config;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	rte_rib6_free(rib);
+	rte_rib6_free(NULL);
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_rib6_insert fails gracefully
+ * for incorrect user input arguments
+ */
+int32_t
+test_insert_invalid(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_node *node, *node1;
+	struct rte_rib6_conf config;
+	uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE] = {0};
+	uint8_t depth = 24;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	/* rte_rib6_insert: rib == NULL */
+	node = rte_rib6_insert(NULL, ip, depth);
+	RTE_TEST_ASSERT(node == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/*Create valid rib to use in rest of test. */
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	/* rte_rib6_insert: depth > MAX_DEPTH */
+	node = rte_rib6_insert(rib, ip, MAX_DEPTH + 1);
+	RTE_TEST_ASSERT(node == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* insert the same ip/depth twice*/
+	node = rte_rib6_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+	node1 = rte_rib6_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node1 == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	rte_rib6_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call rte_rib6_node access functions with incorrect input.
+ * After call rte_rib6_node access functions with correct args
+ * and check the return values for correctness
+ */
+int32_t
+test_get_fn(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_node *node;
+	struct rte_rib6_conf config;
+	void *ext;
+	uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE] = {192, 0, 2, 0, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 0};
+	uint8_t ip_ret[RTE_RIB6_IPV6_ADDR_SIZE];
+	uint64_t nh_set = 10;
+	uint64_t nh_ret;
+	uint8_t depth = 24;
+	uint8_t depth_ret;
+	int ret;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	node = rte_rib6_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	/* test rte_rib6_get_ip() with incorrect args */
+	ret = rte_rib6_get_ip(NULL, ip_ret);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+	ret = rte_rib6_get_ip(node, NULL);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib6_get_depth() with incorrect args */
+	ret = rte_rib6_get_depth(NULL, &depth_ret);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+	ret = rte_rib6_get_depth(node, NULL);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib6_set_nh() with incorrect args */
+	ret = rte_rib6_set_nh(NULL, nh_set);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib6_get_nh() with incorrect args */
+	ret = rte_rib6_get_nh(NULL, &nh_ret);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+	ret = rte_rib6_get_nh(node, NULL);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib6_get_ext() with incorrect args */
+	ext = rte_rib6_get_ext(NULL);
+	RTE_TEST_ASSERT(ext == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* check the return values */
+	ret = rte_rib6_get_ip(node, ip_ret);
+	RTE_TEST_ASSERT((ret == 0) && (rte_rib6_is_equal(ip_ret, ip)),
+		"Failed to get proper node ip\n");
+	ret = rte_rib6_get_depth(node, &depth_ret);
+	RTE_TEST_ASSERT((ret == 0) && (depth_ret == depth),
+		"Failed to get proper node depth\n");
+	ret = rte_rib6_set_nh(node, nh_set);
+	RTE_TEST_ASSERT(ret == 0,
+		"Failed to set rte_rib_node nexthop\n");
+	ret = rte_rib6_get_nh(node, &nh_ret);
+	RTE_TEST_ASSERT((ret == 0) && (nh_ret == nh_set),
+		"Failed to get proper nexthop\n");
+
+	rte_rib6_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call insert, lookup/lookup_exact and delete for a single rule
+ */
+int32_t
+test_basic(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_node *node;
+	struct rte_rib6_conf config;
+
+	uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE] = {192, 0, 2, 0, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 0};
+	uint64_t next_hop_add = 10;
+	uint64_t next_hop_return;
+	uint8_t depth = 24;
+	uint32_t status = 0;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	node = rte_rib6_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	status = rte_rib6_set_nh(node, next_hop_add);
+	RTE_TEST_ASSERT(status == 0,
+		"Failed to set rte_rib_node field\n");
+
+	node = rte_rib6_lookup(rib, ip);
+	RTE_TEST_ASSERT(node != NULL, "Failed to lookup\n");
+
+	status = rte_rib6_get_nh(node, &next_hop_return);
+	RTE_TEST_ASSERT((status == 0) && (next_hop_add == next_hop_return),
+		"Failed to get proper nexthop\n");
+
+	node = rte_rib6_lookup_exact(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL,
+		"Failed to lookup\n");
+
+	status = rte_rib6_get_nh(node, &next_hop_return);
+	RTE_TEST_ASSERT((status == 0) && (next_hop_add == next_hop_return),
+		"Failed to get proper nexthop\n");
+
+	rte_rib6_remove(rib, ip, depth);
+
+	node = rte_rib6_lookup(rib, ip);
+	RTE_TEST_ASSERT(node == NULL,
+		"Lookup returns non existent rule\n");
+	node = rte_rib6_lookup_exact(rib, ip, depth);
+	RTE_TEST_ASSERT(node == NULL,
+		"Lookup returns non existent rule\n");
+
+	rte_rib6_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+int32_t
+test_tree_traversal(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_node *node;
+	struct rte_rib6_conf config;
+
+	uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE] = {10, 0, 2, 130, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 0};
+	uint8_t ip1[RTE_RIB6_IPV6_ADDR_SIZE] = {10, 0, 2, 0, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 0};
+	uint8_t ip2[RTE_RIB6_IPV6_ADDR_SIZE] = {10, 0, 2, 130, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 80};
+	uint8_t depth = 126;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	node = rte_rib6_insert(rib, ip1, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+	node = rte_rib6_insert(rib, ip2, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	node = NULL;
+	node = rte_rib6_get_nxt(rib, ip, 32, node, RTE_RIB6_GET_NXT_ALL);
+	RTE_TEST_ASSERT(node != NULL, "Failed to get rib_node\n");
+
+	rte_rib6_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite rib6_tests = {
+	.suite_name = "rib6 autotest",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+		TEST_CASE(test_create_invalid),
+		TEST_CASE(test_multiple_create),
+		TEST_CASE(test_free_null),
+		TEST_CASE(test_insert_invalid),
+		TEST_CASE(test_get_fn),
+		TEST_CASE(test_basic),
+		TEST_CASE(test_tree_traversal),
+		TEST_CASES_END()
+	}
+};
+
+/*
+ * Do all unit tests.
+ */
+static int
+test_rib6(void)
+{
+	return unit_test_suite_runner(&rib6_tests);
+}
+
+REGISTER_TEST_COMMAND(rib6_autotest, test_rib6);
+
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 05/12] fib: add FIB library
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (10 preceding siblings ...)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 04/12] test/rib: add ipv6 support for RIB autotests Vladimir Medvedkin
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 06/12] fib: add FIB ipv6 support Vladimir Medvedkin
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

Add FIB (Forwarding Information Base) library. This library
implements a dataplane structures and algorithms designed for
fast longest prefix match.
Internally it consists of two parts - RIB (control plane ops) and
implementation for the dataplane tasks.
Initial version provides two implementations for both ipv4 and ipv6:
dummy (uses RIB as a dataplane) and DIR24_8 (same as current LPM)
Due to proposed design it allows to extend FIB with new algorithms
in future (for example DXR, poptrie, etc).

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 config/common_base                 |   6 +
 doc/api/doxy-api.conf.in           |   1 +
 lib/Makefile                       |   2 +
 lib/librte_fib/Makefile            |  25 +++
 lib/librte_fib/meson.build         |   8 +
 lib/librte_fib/rte_fib.c           | 305 +++++++++++++++++++++++++++++++++++++
 lib/librte_fib/rte_fib.h           | 173 +++++++++++++++++++++
 lib/librte_fib/rte_fib_version.map |  14 ++
 lib/meson.build                    |   2 +
 mk/rte.app.mk                      |   1 +
 10 files changed, 537 insertions(+)
 create mode 100644 lib/librte_fib/Makefile
 create mode 100644 lib/librte_fib/meson.build
 create mode 100644 lib/librte_fib/rte_fib.c
 create mode 100644 lib/librte_fib/rte_fib.h
 create mode 100644 lib/librte_fib/rte_fib_version.map

diff --git a/config/common_base b/config/common_base
index 5a9d4c3..0f32ad7 100644
--- a/config/common_base
+++ b/config/common_base
@@ -899,6 +899,12 @@ CONFIG_RTE_LIBRTE_RCU_DEBUG=n
 CONFIG_RTE_LIBRTE_RIB=y
 
 #
+# Compile librte_fib
+#
+CONFIG_RTE_LIBRTE_FIB=y
+CONFIG_RTE_LIBRTE_FIB_DEBUG=n
+
+#
 # Compile librte_lpm
 #
 CONFIG_RTE_LIBRTE_LPM=y
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 3ec012d..6fd3b51 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -30,6 +30,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_efd \
                           @TOPDIR@/lib/librte_ethdev \
                           @TOPDIR@/lib/librte_eventdev \
+                          @TOPDIR@/lib/librte_fib \
                           @TOPDIR@/lib/librte_flow_classify \
                           @TOPDIR@/lib/librte_gro \
                           @TOPDIR@/lib/librte_gso \
diff --git a/lib/Makefile b/lib/Makefile
index aa5ee1e..5d04ab9 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -53,6 +53,8 @@ DIRS-$(CONFIG_RTE_LIBRTE_EFD) += librte_efd
 DEPDIRS-librte_efd := librte_eal librte_ring librte_hash
 DIRS-$(CONFIG_RTE_LIBRTE_RIB) += librte_rib
 DEPDIRS-librte_rib := librte_eal librte_mempool
+DIRS-$(CONFIG_RTE_LIBRTE_FIB) += librte_fib
+DEPDIRS-librte_fib := librte_eal librte_rib
 DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm
 DEPDIRS-librte_lpm := librte_eal librte_hash
 DIRS-$(CONFIG_RTE_LIBRTE_ACL) += librte_acl
diff --git a/lib/librte_fib/Makefile b/lib/librte_fib/Makefile
new file mode 100644
index 0000000..7362f68
--- /dev/null
+++ b/lib/librte_fib/Makefile
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+# Copyright(c) 2019 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_fib.a
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+LDLIBS += -lrte_eal -lrte_rib
+
+EXPORT_MAP := rte_fib_version.map
+
+LIBABIVER := 1
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c
+
+# install this header file
+SYMLINK-$(CONFIG_RTE_LIBRTE_FIB)-include := rte_fib.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_fib/meson.build b/lib/librte_fib/meson.build
new file mode 100644
index 0000000..6b72360
--- /dev/null
+++ b/lib/librte_fib/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+# Copyright(c) 2019 Intel Corporation
+
+allow_experimental_apis = true
+sources = files('rte_fib.c')
+headers = files('rte_fib.h')
+deps += ['rib']
diff --git a/lib/librte_fib/rte_fib.c b/lib/librte_fib/rte_fib.c
new file mode 100644
index 0000000..4d8a771
--- /dev/null
+++ b/lib/librte_fib/rte_fib.c
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_rwlock.h>
+#include <rte_string_fns.h>
+#include <rte_tailq.h>
+
+#include <rte_rib.h>
+#include <rte_fib.h>
+
+TAILQ_HEAD(rte_fib_list, rte_tailq_entry);
+static struct rte_tailq_elem rte_fib_tailq = {
+	.name = "RTE_FIB",
+};
+EAL_REGISTER_TAILQ(rte_fib_tailq)
+
+/* Maximum length of a FIB name. */
+#define RTE_FIB_NAMESIZE	64
+
+#if defined(RTE_LIBRTE_FIB_DEBUG)
+#define FIB_RETURN_IF_TRUE(cond, retval) do {		\
+	if (cond)					\
+		return retval;				\
+} while (0)
+#else
+#define FIB_RETURN_IF_TRUE(cond, retval)
+#endif
+
+struct rte_fib {
+	char			name[RTE_FIB_NAMESIZE];
+	enum rte_fib_type	type;	/**< Type of FIB struct */
+	struct rte_rib		*rib;	/**< RIB helper datastruct */
+	void			*dp;	/**< pointer to the dataplane struct*/
+	rte_fib_lookup_fn_t	lookup;	/**< fib lookup function */
+	rte_fib_modify_fn_t	modify; /**< modify fib datastruct */
+	uint64_t		def_nh;
+};
+
+static void
+dummy_lookup(void *fib_p, const uint32_t *ips, uint64_t *next_hops,
+	const unsigned int n)
+{
+	unsigned int i;
+	struct rte_fib *fib = fib_p;
+	struct rte_rib_node *node;
+
+	for (i = 0; i < n; i++) {
+		node = rte_rib_lookup(fib->rib, ips[i]);
+		if (node != NULL)
+			rte_rib_get_nh(node, &next_hops[i]);
+		else
+			next_hops[i] = fib->def_nh;
+	}
+}
+
+static int
+dummy_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
+	uint64_t next_hop, int op)
+{
+	struct rte_rib_node *node;
+	if ((fib == NULL) || (depth > RTE_FIB_MAXDEPTH))
+		return -EINVAL;
+
+	node = rte_rib_lookup_exact(fib->rib, ip, depth);
+
+	switch (op) {
+	case RTE_FIB_ADD:
+		if (node == NULL)
+			node = rte_rib_insert(fib->rib, ip, depth);
+		if (node == NULL)
+			return -rte_errno;
+		return rte_rib_set_nh(node, next_hop);
+	case RTE_FIB_DEL:
+		if (node == NULL)
+			return -ENOENT;
+		rte_rib_remove(fib->rib, ip, depth);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int
+init_dataplane(struct rte_fib *fib, __rte_unused int socket_id,
+	struct rte_fib_conf *conf)
+{
+	switch (conf->type) {
+	case RTE_FIB_DUMMY:
+		fib->dp = fib;
+		fib->lookup = dummy_lookup;
+		fib->modify = dummy_modify;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int
+rte_fib_add(struct rte_fib *fib, uint32_t ip, uint8_t depth, uint64_t next_hop)
+{
+	if ((fib == NULL) || (fib->modify == NULL) ||
+			(depth > RTE_FIB_MAXDEPTH))
+		return -EINVAL;
+	return fib->modify(fib, ip, depth, next_hop, RTE_FIB_ADD);
+}
+
+int
+rte_fib_delete(struct rte_fib *fib, uint32_t ip, uint8_t depth)
+{
+	if ((fib == NULL) || (fib->modify == NULL) ||
+			(depth > RTE_FIB_MAXDEPTH))
+		return -EINVAL;
+	return fib->modify(fib, ip, depth, 0, RTE_FIB_DEL);
+}
+
+int
+rte_fib_lookup_bulk(struct rte_fib *fib, uint32_t *ips,
+	uint64_t *next_hops, int n)
+{
+	FIB_RETURN_IF_TRUE(((fib == NULL) || (ips == NULL) ||
+		(next_hops == NULL) || (fib->lookup == NULL)), -EINVAL);
+
+	fib->lookup(fib->dp, ips, next_hops, n);
+	return 0;
+}
+
+struct rte_fib *
+rte_fib_create(const char *name, int socket_id, struct rte_fib_conf *conf)
+{
+	char mem_name[RTE_FIB_NAMESIZE];
+	int ret;
+	struct rte_fib *fib = NULL;
+	struct rte_rib *rib = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_fib_list *fib_list;
+	struct rte_rib_conf rib_conf;
+
+	/* Check user arguments. */
+	if ((name == NULL) || (conf == NULL) ||	(conf->max_routes < 0) ||
+			(conf->type >= RTE_FIB_TYPE_MAX)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	rib_conf.ext_sz = 0;
+	rib_conf.max_nodes = conf->max_routes * 2;
+
+	rib = rte_rib_create(name, socket_id, &rib_conf);
+	if (rib == NULL) {
+		RTE_LOG(ERR, LPM,
+			"Can not allocate RIB %s\n", name);
+		return NULL;
+	}
+
+	snprintf(mem_name, sizeof(mem_name), "FIB_%s", name);
+	fib_list = RTE_TAILQ_CAST(rte_fib_tailq.head, rte_fib_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* guarantee there's no existing */
+	TAILQ_FOREACH(te, fib_list, next) {
+		fib = (struct rte_fib *)te->data;
+		if (strncmp(name, fib->name, RTE_FIB_NAMESIZE) == 0)
+			break;
+	}
+	fib = NULL;
+	if (te != NULL) {
+		rte_errno = EEXIST;
+		goto exit;
+	}
+
+	/* allocate tailq entry */
+	te = rte_zmalloc("FIB_TAILQ_ENTRY", sizeof(*te), 0);
+	if (te == NULL) {
+		RTE_LOG(ERR, LPM,
+			"Can not allocate tailq entry for FIB %s\n", name);
+		rte_errno = ENOMEM;
+		goto exit;
+	}
+
+	/* Allocate memory to store the FIB data structures. */
+	fib = rte_zmalloc_socket(mem_name,
+		sizeof(struct rte_fib),	RTE_CACHE_LINE_SIZE, socket_id);
+	if (fib == NULL) {
+		RTE_LOG(ERR, LPM, "FIB %s memory allocation failed\n", name);
+		rte_errno = ENOMEM;
+		goto free_te;
+	}
+
+	rte_strlcpy(fib->name, name, sizeof(fib->name));
+	fib->rib = rib;
+	fib->type = conf->type;
+	fib->def_nh = conf->default_nh;
+	ret = init_dataplane(fib, socket_id, conf);
+	if (ret < 0) {
+		RTE_LOG(ERR, LPM,
+			"FIB dataplane struct %s memory allocation failed "
+			"with err %d\n", name, ret);
+		rte_errno = -ret;
+		goto free_fib;
+	}
+
+	te->data = (void *)fib;
+	TAILQ_INSERT_TAIL(fib_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	return fib;
+
+free_fib:
+	rte_free(fib);
+free_te:
+	rte_free(te);
+exit:
+	rte_mcfg_tailq_write_unlock();
+	rte_rib_free(rib);
+
+	return NULL;
+}
+
+struct rte_fib *
+rte_fib_find_existing(const char *name)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_fib_list *fib_list;
+
+	fib_list = RTE_TAILQ_CAST(rte_fib_tailq.head, rte_fib_list);
+
+	rte_mcfg_tailq_read_lock();
+	TAILQ_FOREACH(te, fib_list, next) {
+		fib = (struct rte_fib *) te->data;
+		if (strncmp(name, fib->name, RTE_FIB_NAMESIZE) == 0)
+			break;
+	}
+	rte_mcfg_tailq_read_unlock();
+
+	if (te == NULL) {
+		rte_errno = ENOENT;
+		return NULL;
+	}
+
+	return fib;
+}
+
+static void
+free_dataplane(struct rte_fib *fib)
+{
+	switch (fib->type) {
+	case RTE_FIB_DUMMY:
+		return;
+	default:
+		return;
+	}
+}
+
+void
+rte_fib_free(struct rte_fib *fib)
+{
+	struct rte_tailq_entry *te;
+	struct rte_fib_list *fib_list;
+
+	if (fib == NULL)
+		return;
+
+	fib_list = RTE_TAILQ_CAST(rte_fib_tailq.head, rte_fib_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* find our tailq entry */
+	TAILQ_FOREACH(te, fib_list, next) {
+		if (te->data == (void *)fib)
+			break;
+	}
+	if (te != NULL)
+		TAILQ_REMOVE(fib_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	free_dataplane(fib);
+	rte_rib_free(fib->rib);
+	rte_free(fib);
+	rte_free(te);
+}
+
+void *
+rte_fib_get_dp(struct rte_fib *fib)
+{
+	return (fib == NULL) ? NULL : fib->dp;
+}
+
+struct rte_rib *
+rte_fib_get_rib(struct rte_fib *fib)
+{
+	return (fib == NULL) ? NULL : fib->rib;
+}
diff --git a/lib/librte_fib/rte_fib.h b/lib/librte_fib/rte_fib.h
new file mode 100644
index 0000000..096cc92
--- /dev/null
+++ b/lib/librte_fib/rte_fib.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _RTE_FIB_H_
+#define _RTE_FIB_H_
+
+/**
+ * @file
+ * FIB (Forwarding information base) implementation
+ * for IPv4 Longest Prefix Match
+ */
+
+#include <rte_compat.h>
+
+struct rte_fib;
+
+/** Maximum depth value possible for IPv4 FIB. */
+#define RTE_FIB_MAXDEPTH	32
+
+/** Type of FIB struct */
+enum rte_fib_type {
+	RTE_FIB_DUMMY,		/**< RIB tree based FIB */
+	RTE_FIB_TYPE_MAX
+};
+
+/** Modify FIB function */
+typedef int (*rte_fib_modify_fn_t)(struct rte_fib *fib, uint32_t ip,
+	uint8_t depth, uint64_t next_hop, int op);
+/** FIB bulk lookup function */
+typedef void (*rte_fib_lookup_fn_t)(void *fib, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n);
+
+enum rte_fib_op {
+	RTE_FIB_ADD,
+	RTE_FIB_DEL,
+};
+
+/** FIB configuration structure */
+struct rte_fib_conf {
+	enum rte_fib_type type; /**< Type of FIB struct */
+	/** Default value returned on lookup if there is no route */
+	uint64_t default_nh;
+	int	max_routes;
+};
+
+/**
+ * Create FIB
+ *
+ * @param name
+ *  FIB name
+ * @param socket_id
+ *  NUMA socket ID for FIB table memory allocation
+ * @param conf
+ *  Structure containing the configuration
+ * @return
+ *  Handle to the FIB object on success
+ *  NULL otherwise with rte_errno set to an appropriate values.
+ */
+__rte_experimental
+struct rte_fib *
+rte_fib_create(const char *name, int socket_id, struct rte_fib_conf *conf);
+
+/**
+ * Find an existing FIB object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the fib object as passed to rte_fib_create()
+ * @return
+ *  Pointer to fib object or NULL if object not found with rte_errno
+ *  set appropriately. Possible rte_errno values include:
+ *   - ENOENT - required entry not available to return.
+ */
+__rte_experimental
+struct rte_fib *
+rte_fib_find_existing(const char *name);
+
+/**
+ * Free an FIB object.
+ *
+ * @param fib
+ *   FIB object handle
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_fib_free(struct rte_fib *fib);
+
+/**
+ * Add a route to the FIB.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param ip
+ *   IPv4 prefix address to be added to the FIB
+ * @param depth
+ *   Prefix length
+ * @param next_hop
+ *   Next hop to be added to the FIB
+ * @return
+ *   0 on success, negative value otherwise
+ */
+__rte_experimental
+int
+rte_fib_add(struct rte_fib *fib, uint32_t ip, uint8_t depth, uint64_t next_hop);
+
+/**
+ * Delete a rule from the FIB.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param ip
+ *   IPv4 prefix address to be deleted from the FIB
+ * @param depth
+ *   Prefix length
+ * @return
+ *   0 on success, negative value otherwise
+ */
+__rte_experimental
+int
+rte_fib_delete(struct rte_fib *fib, uint32_t ip, uint8_t depth);
+
+/**
+ * Lookup multiple IP addresses in the FIB.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param ips
+ *   Array of IPs to be looked up in the FIB
+ * @param next_hops
+ *   Next hop of the most specific rule found for IP.
+ *   This is an array of eight byte values.
+ *   If the lookup for the given IP failed, then corresponding element would
+ *   contain default nexthop value configured for a FIB.
+ * @param n
+ *   Number of elements in ips (and next_hops) array to lookup.
+ *  @return
+ *   -EINVAL for incorrect arguments, otherwise 0
+ */
+__rte_experimental
+int
+rte_fib_lookup_bulk(struct rte_fib *fib, uint32_t *ips,
+		uint64_t *next_hops, int n);
+
+/**
+ * Get pointer to the dataplane specific struct
+ *
+ * @param fib
+ *   FIB object handle
+ * @return
+ *   Pointer on the dataplane struct on success
+ *   NULL othervise
+ */
+__rte_experimental
+void *
+rte_fib_get_dp(struct rte_fib *fib);
+
+/**
+ * Get pointer to the RIB
+ *
+ * @param fib
+ *   FIB object handle
+ * @return
+ *   Pointer on the RIB on success
+ *   NULL othervise
+ */
+__rte_experimental
+struct rte_rib *
+rte_fib_get_rib(struct rte_fib *fib);
+
+#endif /* _RTE_FIB_H_ */
diff --git a/lib/librte_fib/rte_fib_version.map b/lib/librte_fib/rte_fib_version.map
new file mode 100644
index 0000000..776195f
--- /dev/null
+++ b/lib/librte_fib/rte_fib_version.map
@@ -0,0 +1,14 @@
+EXPERIMENTAL {
+	global:
+
+	rte_fib_add;
+	rte_fib_create;
+	rte_fib_delete;
+	rte_fib_find_existing;
+	rte_fib_free;
+	rte_fib_lookup_bulk;
+	rte_fib_get_dp;
+	rte_fib_get_rib;
+
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index d7f2a04..9700433 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -25,6 +25,8 @@ libraries = [
 	'rcu', 'rib', 'reorder', 'sched', 'security', 'stack', 'vhost',
 	# ipsec lib depends on net, crypto and security
 	'ipsec',
+	#fib lib depends on rib
+	'fib',
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 4517874..7ce87e7 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -45,6 +45,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_PDUMP)          += -lrte_pdump
 _LDLIBS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR)    += -lrte_distributor
 _LDLIBS-$(CONFIG_RTE_LIBRTE_IP_FRAG)        += -lrte_ip_frag
 _LDLIBS-$(CONFIG_RTE_LIBRTE_METER)          += -lrte_meter
+_LDLIBS-$(CONFIG_RTE_LIBRTE_FIB)            += -lrte_fib
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RIB)            += -lrte_rib
 _LDLIBS-$(CONFIG_RTE_LIBRTE_LPM)            += -lrte_lpm
 _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL)            += -lrte_acl
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 06/12] fib: add FIB ipv6 support
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (11 preceding siblings ...)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 05/12] fib: add FIB library Vladimir Medvedkin
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 07/12] fib: add DIR24-8 dataplane algorithm Vladimir Medvedkin
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

Add FIB library support for IPv6.
It implements a dataplane structures and algorithms designed for
fast IPv6 longest prefix match.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 lib/librte_fib/Makefile            |   4 +-
 lib/librte_fib/meson.build         |   4 +-
 lib/librte_fib/rte_fib6.c          | 308 +++++++++++++++++++++++++++++++++++++
 lib/librte_fib/rte_fib6.h          | 179 +++++++++++++++++++++
 lib/librte_fib/rte_fib_version.map |   9 ++
 5 files changed, 500 insertions(+), 4 deletions(-)
 create mode 100644 lib/librte_fib/rte_fib6.c
 create mode 100644 lib/librte_fib/rte_fib6.h

diff --git a/lib/librte_fib/Makefile b/lib/librte_fib/Makefile
index 7362f68..0bdf55b 100644
--- a/lib/librte_fib/Makefile
+++ b/lib/librte_fib/Makefile
@@ -17,9 +17,9 @@ EXPORT_MAP := rte_fib_version.map
 LIBABIVER := 1
 
 # all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c rte_fib6.c
 
 # install this header file
-SYMLINK-$(CONFIG_RTE_LIBRTE_FIB)-include := rte_fib.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_FIB)-include := rte_fib.h rte_fib6.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_fib/meson.build b/lib/librte_fib/meson.build
index 6b72360..32dfdec 100644
--- a/lib/librte_fib/meson.build
+++ b/lib/librte_fib/meson.build
@@ -3,6 +3,6 @@
 # Copyright(c) 2019 Intel Corporation
 
 allow_experimental_apis = true
-sources = files('rte_fib.c')
-headers = files('rte_fib.h')
+sources = files('rte_fib.c', 'rte_fib6.c')
+headers = files('rte_fib.h', 'rte_fib6.h')
 deps += ['rib']
diff --git a/lib/librte_fib/rte_fib6.c b/lib/librte_fib/rte_fib6.c
new file mode 100644
index 0000000..9f00a80
--- /dev/null
+++ b/lib/librte_fib/rte_fib6.c
@@ -0,0 +1,308 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_tailq.h>
+#include <rte_errno.h>
+#include <rte_rwlock.h>
+#include <rte_malloc.h>
+#include <rte_string_fns.h>
+
+#include <rte_rib6.h>
+#include <rte_fib6.h>
+
+TAILQ_HEAD(rte_fib6_list, rte_tailq_entry);
+static struct rte_tailq_elem rte_fib6_tailq = {
+	.name = "RTE_FIB6",
+};
+EAL_REGISTER_TAILQ(rte_fib6_tailq)
+
+/* Maximum length of a FIB name. */
+#define FIB6_NAMESIZE	64
+
+#if defined(RTE_LIBRTE_FIB_DEBUG)
+#define FIB6_RETURN_IF_TRUE(cond, retval) do {		\
+	if (cond)					\
+		return retval;				\
+} while (0)
+#else
+#define FIB6_RETURN_IF_TRUE(cond, retval)
+#endif
+
+struct rte_fib6 {
+	char			name[FIB6_NAMESIZE];
+	enum rte_fib6_type	type;	/**< Type of FIB struct */
+	struct rte_rib6		*rib;	/**< RIB helper datastruct */
+	void			*dp;	/**< pointer to the dataplane struct*/
+	rte_fib6_lookup_fn_t	lookup;	/**< fib lookup function */
+	rte_fib6_modify_fn_t	modify; /**< modify fib datastruct */
+	uint64_t		def_nh;
+};
+
+static void
+dummy_lookup(void *fib_p, uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],
+	uint64_t *next_hops, const unsigned int n)
+{
+	unsigned int i;
+	struct rte_fib6 *fib = fib_p;
+	struct rte_rib6_node *node;
+
+	for (i = 0; i < n; i++) {
+		node = rte_rib6_lookup(fib->rib, ips[i]);
+		if (node != NULL)
+			rte_rib6_get_nh(node, &next_hops[i]);
+		else
+			next_hops[i] = fib->def_nh;
+	}
+}
+
+static int
+dummy_modify(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, uint64_t next_hop, int op)
+{
+	struct rte_rib6_node *node;
+	if ((fib == NULL) || (depth > RTE_FIB6_MAXDEPTH))
+		return -EINVAL;
+
+	node = rte_rib6_lookup_exact(fib->rib, ip, depth);
+
+	switch (op) {
+	case RTE_FIB6_ADD:
+		if (node == NULL)
+			node = rte_rib6_insert(fib->rib, ip, depth);
+		if (node == NULL)
+			return -rte_errno;
+		return rte_rib6_set_nh(node, next_hop);
+	case RTE_FIB6_DEL:
+		if (node == NULL)
+			return -ENOENT;
+		rte_rib6_remove(fib->rib, ip, depth);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int
+init_dataplane(struct rte_fib6 *fib, __rte_unused int socket_id,
+	struct rte_fib6_conf *conf)
+{
+	switch (conf->type) {
+	case RTE_FIB6_DUMMY:
+		fib->dp = fib;
+		fib->lookup = dummy_lookup;
+		fib->modify = dummy_modify;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int
+rte_fib6_add(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, uint64_t next_hop)
+{
+	if ((fib == NULL) || (ip == NULL) || (fib->modify == NULL) ||
+			(depth > RTE_FIB6_MAXDEPTH))
+		return -EINVAL;
+	return fib->modify(fib, ip, depth, next_hop, RTE_FIB6_ADD);
+}
+
+int
+rte_fib6_delete(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth)
+{
+	if ((fib == NULL) || (ip == NULL) || (fib->modify == NULL) ||
+			(depth > RTE_FIB6_MAXDEPTH))
+		return -EINVAL;
+	return fib->modify(fib, ip, depth, 0, RTE_FIB6_DEL);
+}
+
+int
+rte_fib6_lookup_bulk(struct rte_fib6 *fib,
+	uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],
+	uint64_t *next_hops, int n)
+{
+	FIB6_RETURN_IF_TRUE((fib == NULL) || (ips == NULL) ||
+		(next_hops == NULL) || (fib->lookup == NULL), -EINVAL);
+	fib->lookup(fib->dp, ips, next_hops, n);
+	return 0;
+}
+
+struct rte_fib6 *
+rte_fib6_create(const char *name, int socket_id, struct rte_fib6_conf *conf)
+{
+	char mem_name[FIB6_NAMESIZE];
+	int ret;
+	struct rte_fib6 *fib = NULL;
+	struct rte_rib6 *rib = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_fib6_list *fib_list;
+	struct rte_rib6_conf rib_conf;
+
+	/* Check user arguments. */
+	if ((name == NULL) || (conf == NULL) || (conf->max_routes < 0) ||
+			(conf->type >= RTE_FIB6_TYPE_MAX)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	rib_conf.ext_sz = 0;
+	rib_conf.max_nodes = conf->max_routes * 2;
+
+	rib = rte_rib6_create(name, socket_id, &rib_conf);
+	if (rib == NULL) {
+		RTE_LOG(ERR, LPM,
+			"Can not allocate RIB %s\n", name);
+		return NULL;
+	}
+
+	snprintf(mem_name, sizeof(mem_name), "FIB6_%s", name);
+	fib_list = RTE_TAILQ_CAST(rte_fib6_tailq.head, rte_fib6_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* guarantee there's no existing */
+	TAILQ_FOREACH(te, fib_list, next) {
+		fib = (struct rte_fib6 *)te->data;
+		if (strncmp(name, fib->name, FIB6_NAMESIZE) == 0)
+			break;
+	}
+	fib = NULL;
+	if (te != NULL) {
+		rte_errno = EEXIST;
+		goto exit;
+	}
+
+	/* allocate tailq entry */
+	te = rte_zmalloc("FIB_TAILQ_ENTRY", sizeof(*te), 0);
+	if (te == NULL) {
+		RTE_LOG(ERR, LPM,
+			"Can not allocate tailq entry for FIB %s\n", name);
+		rte_errno = ENOMEM;
+		goto exit;
+	}
+
+	/* Allocate memory to store the FIB data structures. */
+	fib = rte_zmalloc_socket(mem_name,
+		sizeof(struct rte_fib6), RTE_CACHE_LINE_SIZE, socket_id);
+	if (fib == NULL) {
+		RTE_LOG(ERR, LPM, "FIB %s memory allocation failed\n", name);
+		rte_errno = ENOMEM;
+		goto free_te;
+	}
+
+	rte_strlcpy(fib->name, name, sizeof(fib->name));
+	fib->rib = rib;
+	fib->type = conf->type;
+	fib->def_nh = conf->default_nh;
+	ret = init_dataplane(fib, socket_id, conf);
+	if (ret < 0) {
+		RTE_LOG(ERR, LPM,
+			"FIB dataplane struct %s memory allocation failed\n",
+			name);
+		rte_errno = -ret;
+		goto free_fib;
+	}
+
+	te->data = (void *)fib;
+	TAILQ_INSERT_TAIL(fib_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	return fib;
+
+free_fib:
+	rte_free(fib);
+free_te:
+	rte_free(te);
+exit:
+	rte_mcfg_tailq_write_unlock();
+	rte_rib6_free(rib);
+
+	return NULL;
+}
+
+struct rte_fib6 *
+rte_fib6_find_existing(const char *name)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_fib6_list *fib_list;
+
+	fib_list = RTE_TAILQ_CAST(rte_fib6_tailq.head, rte_fib6_list);
+
+	rte_mcfg_tailq_read_lock();
+	TAILQ_FOREACH(te, fib_list, next) {
+		fib = (struct rte_fib6 *) te->data;
+		if (strncmp(name, fib->name, FIB6_NAMESIZE) == 0)
+			break;
+	}
+	rte_mcfg_tailq_read_unlock();
+
+	if (te == NULL) {
+		rte_errno = ENOENT;
+		return NULL;
+	}
+
+	return fib;
+}
+
+static void
+free_dataplane(struct rte_fib6 *fib)
+{
+	switch (fib->type) {
+	case RTE_FIB6_DUMMY:
+		return;
+	default:
+		return;
+	}
+}
+
+void
+rte_fib6_free(struct rte_fib6 *fib)
+{
+	struct rte_tailq_entry *te;
+	struct rte_fib6_list *fib_list;
+
+	if (fib == NULL)
+		return;
+
+	fib_list = RTE_TAILQ_CAST(rte_fib6_tailq.head, rte_fib6_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* find our tailq entry */
+	TAILQ_FOREACH(te, fib_list, next) {
+		if (te->data == (void *)fib)
+			break;
+	}
+	if (te != NULL)
+		TAILQ_REMOVE(fib_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	free_dataplane(fib);
+	rte_rib6_free(fib->rib);
+	rte_free(fib);
+	rte_free(te);
+}
+
+void *
+rte_fib6_get_dp(struct rte_fib6 *fib)
+{
+	return (fib == NULL) ? NULL : fib->dp;
+}
+
+struct rte_rib6 *
+rte_fib6_get_rib(struct rte_fib6 *fib)
+{
+	return (fib == NULL) ? NULL : fib->rib;
+}
+
diff --git a/lib/librte_fib/rte_fib6.h b/lib/librte_fib/rte_fib6.h
new file mode 100644
index 0000000..3322123
--- /dev/null
+++ b/lib/librte_fib/rte_fib6.h
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _RTE_FIB6_H_
+#define _RTE_FIB6_H_
+
+/**
+ * @file
+ * FIB (Forwarding information base) implementation
+ * for IPv6 Longest Prefix Match
+ */
+
+#include <rte_compat.h>
+
+#define RTE_FIB6_IPV6_ADDR_SIZE		16
+/** Maximum depth value possible for IPv6 FIB. */
+#define RTE_FIB6_MAXDEPTH       128
+
+struct rte_fib6;
+
+/** Type of FIB struct */
+enum rte_fib6_type {
+	RTE_FIB6_DUMMY,		/**< RIB6 tree based FIB */
+	RTE_FIB6_TYPE_MAX
+};
+
+/** Modify FIB function */
+typedef int (*rte_fib6_modify_fn_t)(struct rte_fib6 *fib,
+	const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE], uint8_t depth,
+	uint64_t next_hop, int op);
+/** FIB bulk lookup function */
+typedef void (*rte_fib6_lookup_fn_t)(void *fib,
+	uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],
+	uint64_t *next_hops, const unsigned int n);
+
+enum rte_fib6_op {
+	RTE_FIB6_ADD,
+	RTE_FIB6_DEL,
+};
+
+/** FIB configuration structure */
+struct rte_fib6_conf {
+	enum rte_fib6_type type; /**< Type of FIB struct */
+	/** Default value returned on lookup if there is no route */
+	uint64_t default_nh;
+	int	max_routes;
+};
+
+/**
+ * Create FIB
+ *
+ * @param name
+ *  FIB name
+ * @param socket_id
+ *  NUMA socket ID for FIB table memory allocation
+ * @param conf
+ *  Structure containing the configuration
+ * @return
+ *  Handle to FIB object on success
+ *  NULL otherwise with rte_errno set to an appropriate values.
+ */
+__rte_experimental
+struct rte_fib6 *
+rte_fib6_create(const char *name, int socket_id, struct rte_fib6_conf *conf);
+
+/**
+ * Find an existing FIB object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the fib object as passed to rte_fib6_create()
+ * @return
+ *  Pointer to fib object or NULL if object not found with rte_errno
+ *  set appropriately. Possible rte_errno values include:
+ *   - ENOENT - required entry not available to return.
+ */
+__rte_experimental
+struct rte_fib6 *
+rte_fib6_find_existing(const char *name);
+
+/**
+ * Free an FIB object.
+ *
+ * @param fib
+ *   FIB object handle
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_fib6_free(struct rte_fib6 *fib);
+
+/**
+ * Add a route to the FIB.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param ip
+ *   IPv6 prefix address to be added to the FIB
+ * @param depth
+ *   Prefix length
+ * @param next_hop
+ *   Next hop to be added to the FIB
+ * @return
+ *   0 on success, negative value otherwise
+ */
+__rte_experimental
+int
+rte_fib6_add(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, uint64_t next_hop);
+
+/**
+ * Delete a rule from the FIB.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param ip
+ *   IPv6 prefix address to be deleted from the FIB
+ * @param depth
+ *   Prefix length
+ * @return
+ *   0 on success, negative value otherwise
+ */
+__rte_experimental
+int
+rte_fib6_delete(struct rte_fib6 *fib,
+	const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE], uint8_t depth);
+
+/**
+ * Lookup multiple IP addresses in the FIB.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param ips
+ *   Array of IPv6s to be looked up in the FIB
+ * @param next_hops
+ *   Next hop of the most specific rule found for IP.
+ *   This is an array of eight byte values.
+ *   If the lookup for the given IP failed, then corresponding element would
+ *   contain default nexthop value configured for a FIB.
+ * @param n
+ *   Number of elements in ips (and next_hops) array to lookup.
+ *  @return
+ *   -EINVAL for incorrect arguments, otherwise 0
+ */
+__rte_experimental
+int
+rte_fib6_lookup_bulk(struct rte_fib6 *fib,
+	uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],
+	uint64_t *next_hops, int n);
+
+/**
+ * Get pointer to the dataplane specific struct
+ *
+ * @param fib
+ *   FIB6 object handle
+ * @return
+ *   Pointer on the dataplane struct on success
+ *   NULL othervise
+ */
+__rte_experimental
+void *
+rte_fib6_get_dp(struct rte_fib6 *fib);
+
+/**
+ * Get pointer to the RIB6
+ *
+ * @param fib
+ *   FIB object handle
+ * @return
+ *   Pointer on the RIB6 on success
+ *   NULL othervise
+ */
+__rte_experimental
+struct rte_rib6 *
+rte_fib6_get_rib(struct rte_fib6 *fib);
+
+#endif /* _RTE_FIB6_H_ */
diff --git a/lib/librte_fib/rte_fib_version.map b/lib/librte_fib/rte_fib_version.map
index 776195f..9527417 100644
--- a/lib/librte_fib/rte_fib_version.map
+++ b/lib/librte_fib/rte_fib_version.map
@@ -10,5 +10,14 @@ EXPERIMENTAL {
 	rte_fib_get_dp;
 	rte_fib_get_rib;
 
+	rte_fib6_add;
+	rte_fib6_create;
+	rte_fib6_delete;
+	rte_fib6_find_existing;
+	rte_fib6_free;
+	rte_fib6_lookup_bulk;
+	rte_fib6_get_dp;
+	rte_fib6_get_rib;
+
 	local: *;
 };
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 07/12] fib: add DIR24-8 dataplane algorithm
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (12 preceding siblings ...)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 06/12] fib: add FIB ipv6 support Vladimir Medvedkin
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 08/12] fib: add dataplane algorithm for ipv6 Vladimir Medvedkin
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

Add fib implementation for DIR24_8 algorithm for ipv4.
Implementation is similar to current LPM implementation but has
few enhancements:
faster control plabe operations
more bits for userdata in table entries
configurable userdata size

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 lib/librte_fib/Makefile    |   2 +-
 lib/librte_fib/dir24_8.c   | 737 +++++++++++++++++++++++++++++++++++++++++++++
 lib/librte_fib/dir24_8.h   |  36 +++
 lib/librte_fib/meson.build |   2 +-
 lib/librte_fib/rte_fib.c   |  14 +
 lib/librte_fib/rte_fib.h   |  17 +-
 6 files changed, 805 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_fib/dir24_8.c
 create mode 100644 lib/librte_fib/dir24_8.h

diff --git a/lib/librte_fib/Makefile b/lib/librte_fib/Makefile
index 0bdf55b..67fde9a 100644
--- a/lib/librte_fib/Makefile
+++ b/lib/librte_fib/Makefile
@@ -17,7 +17,7 @@ EXPORT_MAP := rte_fib_version.map
 LIBABIVER := 1
 
 # all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c rte_fib6.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c rte_fib6.c dir24_8.c
 
 # install this header file
 SYMLINK-$(CONFIG_RTE_LIBRTE_FIB)-include := rte_fib.h rte_fib6.h
diff --git a/lib/librte_fib/dir24_8.c b/lib/librte_fib/dir24_8.c
new file mode 100644
index 0000000..c9dce3c
--- /dev/null
+++ b/lib/librte_fib/dir24_8.c
@@ -0,0 +1,737 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <rte_debug.h>
+#include <rte_malloc.h>
+#include <rte_prefetch.h>
+#include <rte_errno.h>
+#include <rte_memory.h>
+#include <rte_branch_prediction.h>
+
+#include <rte_fib.h>
+#include <rte_rib.h>
+#include "dir24_8.h"
+
+#define DIR24_8_NAMESIZE	64
+
+#define DIR24_8_TBL24_NUM_ENT		(1 << 24)
+#define DIR24_8_TBL8_GRP_NUM_ENT	256U
+#define DIR24_8_EXT_ENT			1
+#define DIR24_8_TBL24_MASK		0xffffff00
+
+#define BITMAP_SLAB_BIT_SIZE_LOG2	6
+#define BITMAP_SLAB_BIT_SIZE		(1 << BITMAP_SLAB_BIT_SIZE_LOG2)
+#define BITMAP_SLAB_BITMASK		(BITMAP_SLAB_BIT_SIZE - 1)
+
+struct dir24_8_tbl {
+	uint32_t	number_tbl8s;	/**< Total number of tbl8s */
+	uint32_t	rsvd_tbl8s;	/**< Number of reserved tbl8s */
+	uint32_t	cur_tbl8s;	/**< Current number of tbl8s */
+	enum rte_fib_dir24_8_nh_sz	nh_sz;	/**< Size of nexthop entry */
+	uint64_t	def_nh;		/**< Default next hop */
+	uint64_t	*tbl8;		/**< tbl8 table. */
+	uint64_t	*tbl8_idxes;	/**< bitmap containing free tbl8 idxes*/
+	/* tbl24 table. */
+	__extension__ uint64_t	tbl24[0] __rte_cache_aligned;
+};
+
+#define ROUNDUP(x, y)	 RTE_ALIGN_CEIL(x, (1 << (32 - y)))
+
+enum lookup_type {
+	MACRO,
+	INLINE,
+	UNI
+};
+enum lookup_type test_lookup = MACRO;
+
+static inline void *
+get_tbl24_p(struct dir24_8_tbl *dp, uint32_t ip, uint8_t nh_sz)
+{
+	return (void *)&((uint8_t *)dp->tbl24)[(ip &
+		DIR24_8_TBL24_MASK) >> (8 - nh_sz)];
+}
+
+static inline  uint8_t
+bits_in_nh(uint8_t nh_sz)
+{
+	return 8 * (1 << nh_sz);
+}
+
+static inline uint64_t
+get_max_nh(uint8_t nh_sz)
+{
+	return ((1ULL << (bits_in_nh(nh_sz) - 1)) - 1);
+}
+
+static  inline uint32_t
+get_tbl24_idx(uint32_t ip)
+{
+	return ip >> 8;
+}
+
+static  inline uint32_t
+get_tbl8_idx(uint32_t res, uint32_t ip)
+{
+	return (res >> 1) * DIR24_8_TBL8_GRP_NUM_ENT + (uint8_t)ip;
+}
+
+static inline uint64_t
+lookup_msk(uint8_t nh_sz)
+{
+	return ((1ULL << ((1 << (nh_sz + 3)) - 1)) << 1) - 1;
+}
+
+static inline uint8_t
+get_psd_idx(uint32_t val, uint8_t nh_sz)
+{
+	return val & ((1 << (3 - nh_sz)) - 1);
+}
+
+static inline uint32_t
+get_tbl_idx(uint32_t val, uint8_t nh_sz)
+{
+	return val >> (3 - nh_sz);
+}
+
+static inline uint64_t
+get_tbl24(struct dir24_8_tbl *dp, uint32_t ip, uint8_t nh_sz)
+{
+	return ((dp->tbl24[get_tbl_idx(get_tbl24_idx(ip), nh_sz)] >>
+		(get_psd_idx(get_tbl24_idx(ip), nh_sz) *
+		bits_in_nh(nh_sz))) & lookup_msk(nh_sz));
+}
+
+static inline uint64_t
+get_tbl8(struct dir24_8_tbl *dp, uint32_t res, uint32_t ip, uint8_t nh_sz)
+{
+	return ((dp->tbl8[get_tbl_idx(get_tbl8_idx(res, ip), nh_sz)] >>
+		(get_psd_idx(get_tbl8_idx(res, ip), nh_sz) *
+		bits_in_nh(nh_sz))) & lookup_msk(nh_sz));
+}
+
+static inline int
+is_entry_extended(uint64_t ent)
+{
+	return (ent & DIR24_8_EXT_ENT) == DIR24_8_EXT_ENT;
+}
+
+#define LOOKUP_FUNC(suffix, type, bulk_prefetch, nh_sz)			\
+static void dir24_8_lookup_bulk_##suffix(void *p, const uint32_t *ips,	\
+	uint64_t *next_hops, const unsigned int n)			\
+{									\
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;		\
+	uint64_t tmp;							\
+	uint32_t i;							\
+	uint32_t prefetch_offset =					\
+		RTE_MIN((unsigned int)bulk_prefetch, n);		\
+									\
+	for (i = 0; i < prefetch_offset; i++)				\
+		rte_prefetch0(get_tbl24_p(dp, ips[i], nh_sz));		\
+	for (i = 0; i < (n - prefetch_offset); i++) {			\
+		rte_prefetch0(get_tbl24_p(dp,				\
+			ips[i + prefetch_offset], nh_sz));		\
+		tmp = ((type *)dp->tbl24)[ips[i] >> 8];			\
+		if (unlikely(is_entry_extended(tmp)))			\
+			tmp = ((type *)dp->tbl8)[(uint8_t)ips[i] +	\
+				((tmp >> 1) * DIR24_8_TBL8_GRP_NUM_ENT)]; \
+		next_hops[i] = tmp >> 1;				\
+	}								\
+	for (; i < n; i++) {						\
+		tmp = ((type *)dp->tbl24)[ips[i] >> 8];			\
+		if (unlikely(is_entry_extended(tmp)))			\
+			tmp = ((type *)dp->tbl8)[(uint8_t)ips[i] +	\
+				((tmp >> 1) * DIR24_8_TBL8_GRP_NUM_ENT)]; \
+		next_hops[i] = tmp >> 1;				\
+	}								\
+}									\
+
+LOOKUP_FUNC(1b, uint8_t, 5, 0)
+LOOKUP_FUNC(2b, uint16_t, 6, 1)
+LOOKUP_FUNC(4b, uint32_t, 15, 2)
+LOOKUP_FUNC(8b, uint64_t, 12, 3)
+
+static inline void
+dir24_8_lookup_bulk(struct dir24_8_tbl *dp, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n, uint8_t nh_sz)
+{
+	uint64_t tmp;
+	uint32_t i;
+	uint32_t prefetch_offset = RTE_MIN(15U, n);
+
+	for (i = 0; i < prefetch_offset; i++)
+		rte_prefetch0(get_tbl24_p(dp, ips[i], nh_sz));
+	for (i = 0; i < (n - prefetch_offset); i++) {
+		rte_prefetch0(get_tbl24_p(dp, ips[i + prefetch_offset],
+			nh_sz));
+		tmp = get_tbl24(dp, ips[i], nh_sz);
+		if (unlikely(is_entry_extended(tmp)))
+			tmp = get_tbl8(dp, tmp, ips[i], nh_sz);
+
+		next_hops[i] = tmp >> 1;
+	}
+	for (; i < n; i++) {
+		tmp = get_tbl24(dp, ips[i], nh_sz);
+		if (unlikely(is_entry_extended(tmp)))
+			tmp = get_tbl8(dp, tmp, ips[i], nh_sz);
+
+		next_hops[i] = tmp >> 1;
+	}
+}
+
+static void
+dir24_8_lookup_bulk_0(void *p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	dir24_8_lookup_bulk(dp, ips, next_hops, n, 0);
+}
+
+static void
+dir24_8_lookup_bulk_1(void *p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	dir24_8_lookup_bulk(dp, ips, next_hops, n, 1);
+}
+
+static void
+dir24_8_lookup_bulk_2(void *p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	dir24_8_lookup_bulk(dp, ips, next_hops, n, 2);
+}
+
+static void
+dir24_8_lookup_bulk_3(void *p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	dir24_8_lookup_bulk(dp, ips, next_hops, n, 3);
+}
+
+static void
+dir24_8_lookup_bulk_uni(void *p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+	uint64_t tmp;
+	uint32_t i;
+	uint32_t prefetch_offset = RTE_MIN(15U, n);
+	uint8_t nh_sz = dp->nh_sz;
+
+	for (i = 0; i < prefetch_offset; i++)
+		rte_prefetch0(get_tbl24_p(dp, ips[i], nh_sz));
+	for (i = 0; i < (n - prefetch_offset); i++) {
+		rte_prefetch0(get_tbl24_p(dp, ips[i + prefetch_offset],
+			nh_sz));
+		tmp = get_tbl24(dp, ips[i], nh_sz);
+		if (unlikely(is_entry_extended(tmp)))
+			tmp = get_tbl8(dp, tmp, ips[i], nh_sz);
+
+		next_hops[i] = tmp >> 1;
+	}
+	for (; i < n; i++) {
+		tmp = get_tbl24(dp, ips[i], nh_sz);
+		if (unlikely(is_entry_extended(tmp)))
+			tmp = get_tbl8(dp, tmp, ips[i], nh_sz);
+
+		next_hops[i] = tmp >> 1;
+	}
+}
+
+rte_fib_lookup_fn_t
+dir24_8_get_lookup_fn(struct rte_fib_conf *fib_conf)
+{
+	enum rte_fib_dir24_8_nh_sz nh_sz = fib_conf->dir24_8.nh_sz;
+
+	if (test_lookup == MACRO) {
+		switch (nh_sz) {
+		case RTE_FIB_DIR24_8_1B:
+			return dir24_8_lookup_bulk_1b;
+		case RTE_FIB_DIR24_8_2B:
+			return dir24_8_lookup_bulk_2b;
+		case RTE_FIB_DIR24_8_4B:
+			return dir24_8_lookup_bulk_4b;
+		case RTE_FIB_DIR24_8_8B:
+			return dir24_8_lookup_bulk_8b;
+		}
+	} else if (test_lookup == INLINE) {
+		switch (nh_sz) {
+		case RTE_FIB_DIR24_8_1B:
+			return dir24_8_lookup_bulk_0;
+		case RTE_FIB_DIR24_8_2B:
+			return dir24_8_lookup_bulk_1;
+		case RTE_FIB_DIR24_8_4B:
+			return dir24_8_lookup_bulk_2;
+		case RTE_FIB_DIR24_8_8B:
+			return dir24_8_lookup_bulk_3;
+		}
+	} else
+		return dir24_8_lookup_bulk_uni;
+	return NULL;
+}
+
+static void
+write_to_fib(void *ptr, uint64_t val, enum rte_fib_dir24_8_nh_sz size, int n)
+{
+	int i;
+	uint8_t *ptr8 = (uint8_t *)ptr;
+	uint16_t *ptr16 = (uint16_t *)ptr;
+	uint32_t *ptr32 = (uint32_t *)ptr;
+	uint64_t *ptr64 = (uint64_t *)ptr;
+
+	switch (size) {
+	case RTE_FIB_DIR24_8_1B:
+		for (i = 0; i < n; i++)
+			ptr8[i] = (uint8_t)val;
+		break;
+	case RTE_FIB_DIR24_8_2B:
+		for (i = 0; i < n; i++)
+			ptr16[i] = (uint16_t)val;
+		break;
+	case RTE_FIB_DIR24_8_4B:
+		for (i = 0; i < n; i++)
+			ptr32[i] = (uint32_t)val;
+		break;
+	case RTE_FIB_DIR24_8_8B:
+		for (i = 0; i < n; i++)
+			ptr64[i] = (uint64_t)val;
+		break;
+	}
+}
+
+static int
+tbl8_get_idx(struct dir24_8_tbl *dp)
+{
+	uint32_t i;
+	int bit_idx;
+
+	for (i = 0; (i < (dp->number_tbl8s >> BITMAP_SLAB_BIT_SIZE_LOG2)) &&
+			(dp->tbl8_idxes[i] == UINT64_MAX); i++)
+		;
+	if (i < (dp->number_tbl8s >> BITMAP_SLAB_BIT_SIZE_LOG2)) {
+		bit_idx = __builtin_ctzll(~dp->tbl8_idxes[i]);
+		dp->tbl8_idxes[i] |= (1ULL << bit_idx);
+		return (i << BITMAP_SLAB_BIT_SIZE_LOG2) + bit_idx;
+	}
+	return -ENOSPC;
+}
+
+static inline void
+tbl8_free_idx(struct dir24_8_tbl *dp, int idx)
+{
+	dp->tbl8_idxes[idx >> BITMAP_SLAB_BIT_SIZE_LOG2] &=
+		~(1ULL << (idx & BITMAP_SLAB_BITMASK));
+}
+
+static int
+tbl8_alloc(struct dir24_8_tbl *dp, uint64_t nh)
+{
+	int64_t	tbl8_idx;
+	uint8_t	*tbl8_ptr;
+
+	tbl8_idx = tbl8_get_idx(dp);
+	if (tbl8_idx < 0)
+		return tbl8_idx;
+	tbl8_ptr = (uint8_t *)dp->tbl8 +
+		((tbl8_idx * DIR24_8_TBL8_GRP_NUM_ENT) <<
+		dp->nh_sz);
+	/*Init tbl8 entries with nexthop from tbl24*/
+	write_to_fib((void *)tbl8_ptr, nh|
+		DIR24_8_EXT_ENT, dp->nh_sz,
+		DIR24_8_TBL8_GRP_NUM_ENT);
+	dp->cur_tbl8s++;
+	return tbl8_idx;
+}
+
+static void
+tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx)
+{
+	uint32_t i;
+	uint64_t nh;
+	uint8_t *ptr8;
+	uint16_t *ptr16;
+	uint32_t *ptr32;
+	uint64_t *ptr64;
+
+	switch (dp->nh_sz) {
+	case RTE_FIB_DIR24_8_1B:
+		ptr8 = &((uint8_t *)dp->tbl8)[tbl8_idx *
+				DIR24_8_TBL8_GRP_NUM_ENT];
+		nh = *ptr8;
+		for (i = 1; i < DIR24_8_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr8[i])
+				return;
+		}
+		((uint8_t *)dp->tbl24)[ip >> 8] =
+			nh & ~DIR24_8_EXT_ENT;
+		for (i = 0; i < DIR24_8_TBL8_GRP_NUM_ENT; i++)
+			ptr8[i] = 0;
+		break;
+	case RTE_FIB_DIR24_8_2B:
+		ptr16 = &((uint16_t *)dp->tbl8)[tbl8_idx *
+				DIR24_8_TBL8_GRP_NUM_ENT];
+		nh = *ptr16;
+		for (i = 1; i < DIR24_8_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr16[i])
+				return;
+		}
+		((uint16_t *)dp->tbl24)[ip >> 8] =
+			nh & ~DIR24_8_EXT_ENT;
+		for (i = 0; i < DIR24_8_TBL8_GRP_NUM_ENT; i++)
+			ptr16[i] = 0;
+		break;
+	case RTE_FIB_DIR24_8_4B:
+		ptr32 = &((uint32_t *)dp->tbl8)[tbl8_idx *
+				DIR24_8_TBL8_GRP_NUM_ENT];
+		nh = *ptr32;
+		for (i = 1; i < DIR24_8_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr32[i])
+				return;
+		}
+		((uint32_t *)dp->tbl24)[ip >> 8] =
+			nh & ~DIR24_8_EXT_ENT;
+		for (i = 0; i < DIR24_8_TBL8_GRP_NUM_ENT; i++)
+			ptr32[i] = 0;
+		break;
+	case RTE_FIB_DIR24_8_8B:
+		ptr64 = &((uint64_t *)dp->tbl8)[tbl8_idx *
+				DIR24_8_TBL8_GRP_NUM_ENT];
+		nh = *ptr64;
+		for (i = 1; i < DIR24_8_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr64[i])
+				return;
+		}
+		((uint64_t *)dp->tbl24)[ip >> 8] =
+			nh & ~DIR24_8_EXT_ENT;
+		for (i = 0; i < DIR24_8_TBL8_GRP_NUM_ENT; i++)
+			ptr64[i] = 0;
+		break;
+	}
+	tbl8_free_idx(dp, tbl8_idx);
+	dp->cur_tbl8s--;
+}
+
+static int
+install_to_fib(struct dir24_8_tbl *dp, uint32_t ledge, uint32_t redge,
+	uint64_t next_hop)
+{
+	uint64_t	tbl24_tmp;
+	int	tbl8_idx;
+	int tmp_tbl8_idx;
+	uint8_t	*tbl8_ptr;
+	uint32_t len;
+
+	len = ((ledge == 0) && (redge == 0)) ? 1 << 24 :
+		((redge & DIR24_8_TBL24_MASK) - ROUNDUP(ledge, 24)) >> 8;
+
+	if (((ledge >> 8) != (redge >> 8)) || (len == 1 << 24)) {
+		if ((ROUNDUP(ledge, 24) - ledge) != 0) {
+			tbl24_tmp = get_tbl24(dp, ledge, dp->nh_sz);
+			if ((tbl24_tmp & DIR24_8_EXT_ENT) !=
+					DIR24_8_EXT_ENT) {
+				/**
+				 * Make sure there is space for two TBL8.
+				 * This is necessary when installing range that
+				 * needs tbl8 for ledge and redge.
+				 */
+				tbl8_idx = tbl8_alloc(dp, tbl24_tmp);
+				tmp_tbl8_idx = tbl8_get_idx(dp);
+				if (tbl8_idx < 0)
+					return -ENOSPC;
+				else if (tmp_tbl8_idx < 0) {
+					tbl8_free_idx(dp, tbl8_idx);
+					return -ENOSPC;
+				}
+				tbl8_free_idx(dp, tmp_tbl8_idx);
+				/*update dir24 entry with tbl8 index*/
+				write_to_fib(get_tbl24_p(dp, ledge,
+					dp->nh_sz), (tbl8_idx << 1)|
+					DIR24_8_EXT_ENT,
+					dp->nh_sz, 1);
+			} else
+				tbl8_idx = tbl24_tmp >> 1;
+			tbl8_ptr = (uint8_t *)dp->tbl8 +
+				(((tbl8_idx * DIR24_8_TBL8_GRP_NUM_ENT) +
+				(ledge & ~DIR24_8_TBL24_MASK)) <<
+				dp->nh_sz);
+			/*update tbl8 with new next hop*/
+			write_to_fib((void *)tbl8_ptr, (next_hop << 1)|
+				DIR24_8_EXT_ENT,
+				dp->nh_sz, ROUNDUP(ledge, 24) - ledge);
+			tbl8_recycle(dp, ledge, tbl8_idx);
+		}
+		write_to_fib(get_tbl24_p(dp, ROUNDUP(ledge, 24), dp->nh_sz),
+			next_hop << 1, dp->nh_sz, len);
+		if (redge & ~DIR24_8_TBL24_MASK) {
+			tbl24_tmp = get_tbl24(dp, redge, dp->nh_sz);
+			if ((tbl24_tmp & DIR24_8_EXT_ENT) !=
+					DIR24_8_EXT_ENT) {
+				tbl8_idx = tbl8_alloc(dp, tbl24_tmp);
+				if (tbl8_idx < 0)
+					return -ENOSPC;
+				/*update dir24 entry with tbl8 index*/
+				write_to_fib(get_tbl24_p(dp, redge,
+					dp->nh_sz), (tbl8_idx << 1)|
+					DIR24_8_EXT_ENT,
+					dp->nh_sz, 1);
+			} else
+				tbl8_idx = tbl24_tmp >> 1;
+			tbl8_ptr = (uint8_t *)dp->tbl8 +
+				((tbl8_idx * DIR24_8_TBL8_GRP_NUM_ENT) <<
+				dp->nh_sz);
+			/*update tbl8 with new next hop*/
+			write_to_fib((void *)tbl8_ptr, (next_hop << 1)|
+				DIR24_8_EXT_ENT,
+				dp->nh_sz, redge & ~DIR24_8_TBL24_MASK);
+			tbl8_recycle(dp, redge, tbl8_idx);
+		}
+	} else if ((redge - ledge) != 0) {
+		tbl24_tmp = get_tbl24(dp, ledge, dp->nh_sz);
+		if ((tbl24_tmp & DIR24_8_EXT_ENT) !=
+				DIR24_8_EXT_ENT) {
+			tbl8_idx = tbl8_alloc(dp, tbl24_tmp);
+			if (tbl8_idx < 0)
+				return -ENOSPC;
+			/*update dir24 entry with tbl8 index*/
+			write_to_fib(get_tbl24_p(dp, ledge, dp->nh_sz),
+				(tbl8_idx << 1)|
+				DIR24_8_EXT_ENT,
+				dp->nh_sz, 1);
+		} else
+			tbl8_idx = tbl24_tmp >> 1;
+		tbl8_ptr = (uint8_t *)dp->tbl8 +
+			(((tbl8_idx * DIR24_8_TBL8_GRP_NUM_ENT) +
+			(ledge & ~DIR24_8_TBL24_MASK)) <<
+			dp->nh_sz);
+		/*update tbl8 with new next hop*/
+		write_to_fib((void *)tbl8_ptr, (next_hop << 1)|
+			DIR24_8_EXT_ENT,
+			dp->nh_sz, redge - ledge);
+		tbl8_recycle(dp, ledge, tbl8_idx);
+	}
+	return 0;
+}
+
+static int
+modify_fib(struct dir24_8_tbl *dp, struct rte_rib *rib, uint32_t ip,
+	uint8_t depth, uint64_t next_hop)
+{
+	struct rte_rib_node *tmp = NULL;
+	uint32_t ledge, redge, tmp_ip;
+	int ret;
+	uint8_t tmp_depth;
+
+	ledge = ip;
+	do {
+		tmp = rte_rib_get_nxt(rib, ip, depth, tmp,
+			RTE_RIB_GET_NXT_COVER);
+		if (tmp != NULL) {
+			rte_rib_get_depth(tmp, &tmp_depth);
+			if (tmp_depth == depth)
+				continue;
+			rte_rib_get_ip(tmp, &tmp_ip);
+			redge = tmp_ip & rte_rib_depth_to_mask(tmp_depth);
+			if (ledge == redge) {
+				ledge = redge +
+					(uint32_t)(1ULL << (32 - tmp_depth));
+				continue;
+			}
+			ret = install_to_fib(dp, ledge, redge,
+				next_hop);
+			if (ret != 0)
+				return ret;
+			ledge = redge +
+				(uint32_t)(1ULL << (32 - tmp_depth));
+		} else {
+			redge = ip + (uint32_t)(1ULL << (32 - depth));
+			if (ledge == redge)
+				break;
+			ret = install_to_fib(dp, ledge, redge,
+				next_hop);
+			if (ret != 0)
+				return ret;
+		}
+	} while (tmp);
+
+	return 0;
+}
+
+int
+dir24_8_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
+	uint64_t next_hop, int op)
+{
+	struct dir24_8_tbl *dp;
+	struct rte_rib *rib;
+	struct rte_rib_node *tmp = NULL;
+	struct rte_rib_node *node;
+	struct rte_rib_node *parent;
+	int ret = 0;
+	uint64_t par_nh, node_nh;
+
+	if ((fib == NULL) || (depth > RTE_FIB_MAXDEPTH))
+		return -EINVAL;
+
+	dp = rte_fib_get_dp(fib);
+	rib = rte_fib_get_rib(fib);
+	RTE_ASSERT((dp != NULL) && (rib != NULL));
+
+	if (next_hop > get_max_nh(dp->nh_sz))
+		return -EINVAL;
+
+	ip &= rte_rib_depth_to_mask(depth);
+
+	node = rte_rib_lookup_exact(rib, ip, depth);
+	switch (op) {
+	case RTE_FIB_ADD:
+		if (node != NULL) {
+			rte_rib_get_nh(node, &node_nh);
+			if (node_nh == next_hop)
+				return 0;
+			ret = modify_fib(dp, rib, ip, depth, next_hop);
+			if (ret == 0)
+				rte_rib_set_nh(node, next_hop);
+			return 0;
+		}
+		if (depth > 24) {
+			tmp = rte_rib_get_nxt(rib, ip, 24, NULL,
+				RTE_RIB_GET_NXT_COVER);
+			if ((tmp == NULL) &&
+				(dp->rsvd_tbl8s >= dp->number_tbl8s))
+				return -ENOSPC;
+
+		}
+		node = rte_rib_insert(rib, ip, depth);
+		if (node == NULL)
+			return -rte_errno;
+		rte_rib_set_nh(node, next_hop);
+		parent = rte_rib_lookup_parent(node);
+		if (parent != NULL) {
+			rte_rib_get_nh(parent, &par_nh);
+			if (par_nh == next_hop)
+				return 0;
+		}
+		ret = modify_fib(dp, rib, ip, depth, next_hop);
+		if (ret != 0) {
+			rte_rib_remove(rib, ip, depth);
+			return ret;
+		}
+		if ((depth > 24) && (tmp == NULL))
+			dp->rsvd_tbl8s++;
+		return 0;
+	case RTE_FIB_DEL:
+		if (node == NULL)
+			return -ENOENT;
+
+		parent = rte_rib_lookup_parent(node);
+		if (parent != NULL) {
+			rte_rib_get_nh(parent, &par_nh);
+			rte_rib_get_nh(node, &node_nh);
+			if (par_nh != node_nh)
+				ret = modify_fib(dp, rib, ip, depth, par_nh);
+		} else
+			ret = modify_fib(dp, rib, ip, depth, dp->def_nh);
+		if (ret == 0) {
+			rte_rib_remove(rib, ip, depth);
+			if (depth > 24) {
+				tmp = rte_rib_get_nxt(rib, ip, 24, NULL,
+					RTE_RIB_GET_NXT_COVER);
+				if (tmp == NULL)
+					dp->rsvd_tbl8s--;
+			}
+		}
+		return ret;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+void *
+dir24_8_create(const char *name, int socket_id, struct rte_fib_conf *fib_conf)
+{
+	char mem_name[DIR24_8_NAMESIZE];
+	struct dir24_8_tbl *dp;
+	uint64_t	def_nh;
+	uint32_t	num_tbl8;
+	enum rte_fib_dir24_8_nh_sz	nh_sz;
+
+	if ((name == NULL) || (fib_conf == NULL) ||
+			(fib_conf->dir24_8.nh_sz < RTE_FIB_DIR24_8_1B) ||
+			(fib_conf->dir24_8.nh_sz > RTE_FIB_DIR24_8_8B) ||
+			(fib_conf->dir24_8.num_tbl8 >
+			get_max_nh(fib_conf->dir24_8.nh_sz)) ||
+			(fib_conf->dir24_8.num_tbl8 == 0) ||
+			(fib_conf->default_nh >
+			get_max_nh(fib_conf->dir24_8.nh_sz))) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	def_nh = fib_conf->default_nh;
+	nh_sz = fib_conf->dir24_8.nh_sz;
+	num_tbl8 = RTE_ALIGN_CEIL(fib_conf->dir24_8.num_tbl8,
+			BITMAP_SLAB_BIT_SIZE);
+
+	snprintf(mem_name, sizeof(mem_name), "DP_%s", name);
+	dp = rte_zmalloc_socket(name, sizeof(struct dir24_8_tbl) +
+		DIR24_8_TBL24_NUM_ENT * (1 << nh_sz), RTE_CACHE_LINE_SIZE,
+		socket_id);
+	if (dp == NULL) {
+		rte_errno = ENOMEM;
+		return NULL;
+	}
+
+	/* Init table with default value */
+	write_to_fib(dp->tbl24, (def_nh << 1), nh_sz, 1 << 24);
+
+	snprintf(mem_name, sizeof(mem_name), "TBL8_%p", dp);
+	uint64_t tbl8_sz = DIR24_8_TBL8_GRP_NUM_ENT * (1ULL << nh_sz) *
+			(num_tbl8 + 1);
+	dp->tbl8 = rte_zmalloc_socket(mem_name, tbl8_sz,
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (dp->tbl8 == NULL) {
+		rte_errno = ENOMEM;
+		rte_free(dp);
+		return NULL;
+	}
+	dp->def_nh = def_nh;
+	dp->nh_sz = nh_sz;
+	dp->number_tbl8s = num_tbl8;
+
+	snprintf(mem_name, sizeof(mem_name), "TBL8_idxes_%p", dp);
+	dp->tbl8_idxes = rte_zmalloc_socket(mem_name,
+			RTE_ALIGN_CEIL(dp->number_tbl8s, 64) >> 3,
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (dp->tbl8_idxes == NULL) {
+		rte_errno = ENOMEM;
+		rte_free(dp->tbl8);
+		rte_free(dp);
+		return NULL;
+	}
+
+	return dp;
+}
+
+void
+dir24_8_free(void *p)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	rte_free(dp->tbl8_idxes);
+	rte_free(dp->tbl8);
+	rte_free(dp);
+}
diff --git a/lib/librte_fib/dir24_8.h b/lib/librte_fib/dir24_8.h
new file mode 100644
index 0000000..34ddb91
--- /dev/null
+++ b/lib/librte_fib/dir24_8.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _DIR24_8_H_
+#define _DIR24_8_H_
+
+/**
+ * @file
+ * DIR24_8 algorithm
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *
+dir24_8_create(const char *name, int socket_id, struct rte_fib_conf *conf);
+
+void
+dir24_8_free(void *p);
+
+rte_fib_lookup_fn_t
+dir24_8_get_lookup_fn(struct rte_fib_conf *conf);
+
+int
+dir24_8_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
+	uint64_t next_hop, int op);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DIR24_8_H_ */
+
diff --git a/lib/librte_fib/meson.build b/lib/librte_fib/meson.build
index 32dfdec..c63cac8 100644
--- a/lib/librte_fib/meson.build
+++ b/lib/librte_fib/meson.build
@@ -3,6 +3,6 @@
 # Copyright(c) 2019 Intel Corporation
 
 allow_experimental_apis = true
-sources = files('rte_fib.c', 'rte_fib6.c')
+sources = files('rte_fib.c', 'rte_fib6.c', 'dir24_8.c')
 headers = files('rte_fib.h', 'rte_fib6.h')
 deps += ['rib']
diff --git a/lib/librte_fib/rte_fib.c b/lib/librte_fib/rte_fib.c
index 4d8a771..e090808 100644
--- a/lib/librte_fib/rte_fib.c
+++ b/lib/librte_fib/rte_fib.c
@@ -17,6 +17,8 @@
 #include <rte_rib.h>
 #include <rte_fib.h>
 
+#include "dir24_8.h"
+
 TAILQ_HEAD(rte_fib_list, rte_tailq_entry);
 static struct rte_tailq_elem rte_fib_tailq = {
 	.name = "RTE_FIB",
@@ -92,12 +94,22 @@ static int
 init_dataplane(struct rte_fib *fib, __rte_unused int socket_id,
 	struct rte_fib_conf *conf)
 {
+	char dp_name[sizeof(void *)];
+
+	snprintf(dp_name, sizeof(dp_name), "%p", fib);
 	switch (conf->type) {
 	case RTE_FIB_DUMMY:
 		fib->dp = fib;
 		fib->lookup = dummy_lookup;
 		fib->modify = dummy_modify;
 		return 0;
+	case RTE_FIB_DIR24_8:
+		fib->dp = dir24_8_create(dp_name, socket_id, conf);
+		if (fib->dp == NULL)
+			return -rte_errno;
+		fib->lookup = dir24_8_get_lookup_fn(conf);
+		fib->modify = dir24_8_modify;
+		return 0;
 	default:
 		return -EINVAL;
 	}
@@ -258,6 +270,8 @@ free_dataplane(struct rte_fib *fib)
 	switch (fib->type) {
 	case RTE_FIB_DUMMY:
 		return;
+	case RTE_FIB_DIR24_8:
+		dir24_8_free(fib->dp);
 	default:
 		return;
 	}
diff --git a/lib/librte_fib/rte_fib.h b/lib/librte_fib/rte_fib.h
index 096cc92..d06c5ef 100644
--- a/lib/librte_fib/rte_fib.h
+++ b/lib/librte_fib/rte_fib.h
@@ -15,6 +15,7 @@
 #include <rte_compat.h>
 
 struct rte_fib;
+struct rte_rib;
 
 /** Maximum depth value possible for IPv4 FIB. */
 #define RTE_FIB_MAXDEPTH	32
@@ -22,6 +23,7 @@ struct rte_fib;
 /** Type of FIB struct */
 enum rte_fib_type {
 	RTE_FIB_DUMMY,		/**< RIB tree based FIB */
+	RTE_FIB_DIR24_8,	/**< DIR24_8 based FIB */
 	RTE_FIB_TYPE_MAX
 };
 
@@ -37,12 +39,26 @@ enum rte_fib_op {
 	RTE_FIB_DEL,
 };
 
+/** Size of nexthop (1 << nh_sz) bits for DIR24_8 based FIB */
+enum rte_fib_dir24_8_nh_sz {
+	RTE_FIB_DIR24_8_1B,
+	RTE_FIB_DIR24_8_2B,
+	RTE_FIB_DIR24_8_4B,
+	RTE_FIB_DIR24_8_8B
+};
+
 /** FIB configuration structure */
 struct rte_fib_conf {
 	enum rte_fib_type type; /**< Type of FIB struct */
 	/** Default value returned on lookup if there is no route */
 	uint64_t default_nh;
 	int	max_routes;
+	union {
+		struct {
+			enum rte_fib_dir24_8_nh_sz nh_sz;
+			uint32_t	num_tbl8;
+		} dir24_8;
+	};
 };
 
 /**
@@ -143,7 +159,6 @@ __rte_experimental
 int
 rte_fib_lookup_bulk(struct rte_fib *fib, uint32_t *ips,
 		uint64_t *next_hops, int n);
-
 /**
  * Get pointer to the dataplane specific struct
  *
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 08/12] fib: add dataplane algorithm for ipv6
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (13 preceding siblings ...)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 07/12] fib: add DIR24-8 dataplane algorithm Vladimir Medvedkin
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 09/12] test/fib: add FIB library autotests Vladimir Medvedkin
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

Add fib implementation for ipv6 using modified DIR24_8 algorithm.
Implementation is similar to current LPM6 implementation but has
few enhancements:
faster control plabe operations
more bits for userdata in table entries
configurable userdata size

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 lib/librte_fib/Makefile    |   2 +-
 lib/librte_fib/meson.build |   2 +-
 lib/librte_fib/rte_fib6.c  |  14 +
 lib/librte_fib/rte_fib6.h  |  14 +
 lib/librte_fib/trie.c      | 760 +++++++++++++++++++++++++++++++++++++++++++++
 lib/librte_fib/trie.h      |  37 +++
 6 files changed, 827 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_fib/trie.c
 create mode 100644 lib/librte_fib/trie.h

diff --git a/lib/librte_fib/Makefile b/lib/librte_fib/Makefile
index 67fde9a..4abd80b 100644
--- a/lib/librte_fib/Makefile
+++ b/lib/librte_fib/Makefile
@@ -17,7 +17,7 @@ EXPORT_MAP := rte_fib_version.map
 LIBABIVER := 1
 
 # all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c rte_fib6.c dir24_8.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c rte_fib6.c dir24_8.c trie.c
 
 # install this header file
 SYMLINK-$(CONFIG_RTE_LIBRTE_FIB)-include := rte_fib.h rte_fib6.h
diff --git a/lib/librte_fib/meson.build b/lib/librte_fib/meson.build
index c63cac8..e2c6f44 100644
--- a/lib/librte_fib/meson.build
+++ b/lib/librte_fib/meson.build
@@ -3,6 +3,6 @@
 # Copyright(c) 2019 Intel Corporation
 
 allow_experimental_apis = true
-sources = files('rte_fib.c', 'rte_fib6.c', 'dir24_8.c')
+sources = files('rte_fib.c', 'rte_fib6.c', 'dir24_8.c', 'trie.c')
 headers = files('rte_fib.h', 'rte_fib6.h')
 deps += ['rib']
diff --git a/lib/librte_fib/rte_fib6.c b/lib/librte_fib/rte_fib6.c
index 9f00a80..354227d 100644
--- a/lib/librte_fib/rte_fib6.c
+++ b/lib/librte_fib/rte_fib6.c
@@ -17,6 +17,8 @@
 #include <rte_rib6.h>
 #include <rte_fib6.h>
 
+#include "trie.h"
+
 TAILQ_HEAD(rte_fib6_list, rte_tailq_entry);
 static struct rte_tailq_elem rte_fib6_tailq = {
 	.name = "RTE_FIB6",
@@ -92,12 +94,22 @@ static int
 init_dataplane(struct rte_fib6 *fib, __rte_unused int socket_id,
 	struct rte_fib6_conf *conf)
 {
+	char dp_name[sizeof(void *)];
+
+	snprintf(dp_name, sizeof(dp_name), "%p", fib);
 	switch (conf->type) {
 	case RTE_FIB6_DUMMY:
 		fib->dp = fib;
 		fib->lookup = dummy_lookup;
 		fib->modify = dummy_modify;
 		return 0;
+	case RTE_FIB6_TRIE:
+		fib->dp = trie_create(dp_name, socket_id, conf);
+		if (fib->dp == NULL)
+			return -rte_errno;
+		fib->lookup = rte_trie_get_lookup_fn(conf);
+		fib->modify = trie_modify;
+		return 0;
 	default:
 		return -EINVAL;
 	}
@@ -260,6 +272,8 @@ free_dataplane(struct rte_fib6 *fib)
 	switch (fib->type) {
 	case RTE_FIB6_DUMMY:
 		return;
+	case RTE_FIB6_TRIE:
+		trie_free(fib->dp);
 	default:
 		return;
 	}
diff --git a/lib/librte_fib/rte_fib6.h b/lib/librte_fib/rte_fib6.h
index 3322123..4268704 100644
--- a/lib/librte_fib/rte_fib6.h
+++ b/lib/librte_fib/rte_fib6.h
@@ -19,10 +19,12 @@
 #define RTE_FIB6_MAXDEPTH       128
 
 struct rte_fib6;
+struct rte_rib6;
 
 /** Type of FIB struct */
 enum rte_fib6_type {
 	RTE_FIB6_DUMMY,		/**< RIB6 tree based FIB */
+	RTE_FIB6_TRIE,		/**< TRIE based fib  */
 	RTE_FIB6_TYPE_MAX
 };
 
@@ -40,12 +42,24 @@ enum rte_fib6_op {
 	RTE_FIB6_DEL,
 };
 
+enum rte_fib_trie_nh_sz {
+	RTE_FIB6_TRIE_2B = 1,
+	RTE_FIB6_TRIE_4B,
+	RTE_FIB6_TRIE_8B
+};
+
 /** FIB configuration structure */
 struct rte_fib6_conf {
 	enum rte_fib6_type type; /**< Type of FIB struct */
 	/** Default value returned on lookup if there is no route */
 	uint64_t default_nh;
 	int	max_routes;
+	union {
+		struct {
+			enum rte_fib_trie_nh_sz nh_sz;
+			uint32_t	num_tbl8;
+		} trie;
+	};
 };
 
 /**
diff --git a/lib/librte_fib/trie.c b/lib/librte_fib/trie.c
new file mode 100644
index 0000000..198e815
--- /dev/null
+++ b/lib/librte_fib/trie.c
@@ -0,0 +1,760 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <rte_debug.h>
+#include <rte_malloc.h>
+#include <rte_prefetch.h>
+#include <rte_errno.h>
+#include <rte_memory.h>
+#include <rte_branch_prediction.h>
+
+#include <rte_rib6.h>
+#include <rte_fib6.h>
+#include "trie.h"
+
+/* @internal Total number of tbl24 entries. */
+#define TRIE_TBL24_NUM_ENT	(1 << 24)
+
+/* Maximum depth value possible for IPv6 LPM. */
+#define TRIE_MAX_DEPTH		128
+
+/* @internal Number of entries in a tbl8 group. */
+#define TRIE_TBL8_GRP_NUM_ENT	256ULL
+
+/* @internal Total number of tbl8 groups in the tbl8. */
+#define TRIE_TBL8_NUM_GROUPS	65536
+
+/* @internal bitmask with valid and valid_group fields set */
+#define TRIE_EXT_ENT		1
+
+#define TRIE_NAMESIZE		64
+
+#define BITMAP_SLAB_BIT_SIZE_LOG2	6
+#define BITMAP_SLAB_BIT_SIZE		(1ULL << BITMAP_SLAB_BIT_SIZE_LOG2)
+#define BITMAP_SLAB_BITMASK		(BITMAP_SLAB_BIT_SIZE - 1)
+
+struct rte_trie_tbl {
+	uint32_t	number_tbl8s;	/**< Total number of tbl8s */
+	uint32_t	rsvd_tbl8s;	/**< Number of reserved tbl8s */
+	uint32_t	cur_tbl8s;	/**< Current cumber of tbl8s */
+	uint64_t	def_nh;		/**< Default next hop */
+	enum rte_fib_trie_nh_sz	nh_sz;	/**< Size of nexthop entry */
+	uint64_t	*tbl8;		/**< tbl8 table. */
+	uint32_t	*tbl8_pool;	/**< bitmap containing free tbl8 idxes*/
+	uint32_t	tbl8_pool_pos;
+	/* tbl24 table. */
+	__extension__ uint64_t	tbl24[0] __rte_cache_aligned;
+};
+
+enum edge {
+	LEDGE,
+	REDGE
+};
+
+enum lookup_type {
+	MACRO,
+	INLINE,
+	UNI
+};
+static enum lookup_type test_lookup = MACRO;
+
+static inline uint32_t
+get_tbl24_idx(const uint8_t *ip)
+{
+	return ip[0] << 16|ip[1] << 8|ip[2];
+}
+
+static inline void *
+get_tbl24_p(struct rte_trie_tbl *dp, const uint8_t *ip, uint8_t nh_sz)
+{
+	uint32_t tbl24_idx;
+
+	tbl24_idx = get_tbl24_idx(ip);
+	return (void *)&((uint8_t *)dp->tbl24)[tbl24_idx << nh_sz];
+}
+
+static inline uint8_t
+bits_in_nh(uint8_t nh_sz)
+{
+	return 8 * (1 << nh_sz);
+}
+
+static inline uint64_t
+get_max_nh(uint8_t nh_sz)
+{
+	return ((1ULL << (bits_in_nh(nh_sz) - 1)) - 1);
+}
+
+static inline uint64_t
+lookup_msk(uint8_t nh_sz)
+{
+	return ((1ULL << ((1 << (nh_sz + 3)) - 1)) << 1) - 1;
+}
+
+static inline uint8_t
+get_psd_idx(uint32_t val, uint8_t nh_sz)
+{
+	return val & ((1 << (3 - nh_sz)) - 1);
+}
+
+static inline uint32_t
+get_tbl_pos(uint32_t val, uint8_t nh_sz)
+{
+	return val >> (3 - nh_sz);
+}
+
+static inline uint64_t
+get_tbl_val_by_idx(uint64_t *tbl, uint32_t idx, uint8_t nh_sz)
+{
+	return ((tbl[get_tbl_pos(idx, nh_sz)] >> (get_psd_idx(idx, nh_sz) *
+		bits_in_nh(nh_sz))) & lookup_msk(nh_sz));
+}
+
+static inline void *
+get_tbl_p_by_idx(uint64_t *tbl, uint64_t idx, uint8_t nh_sz)
+{
+	return (uint8_t *)tbl + (idx << nh_sz);
+}
+
+static inline int
+is_entry_extended(uint64_t ent)
+{
+	return (ent & TRIE_EXT_ENT) == TRIE_EXT_ENT;
+}
+
+#define LOOKUP_FUNC(suffix, type, nh_sz)				\
+static void rte_trie_lookup_bulk_##suffix(void *p,			\
+	uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],			\
+	uint64_t *next_hops, const unsigned int n)			\
+{									\
+	struct rte_trie_tbl *dp = (struct rte_trie_tbl *)p;		\
+	uint64_t tmp;							\
+	uint32_t i, j;							\
+									\
+	for (i = 0; i < n; i++) {					\
+		tmp = ((type *)dp->tbl24)[get_tbl24_idx(&ips[i][0])];	\
+		j = 3;							\
+		while (is_entry_extended(tmp)) {			\
+			tmp = ((type *)dp->tbl8)[ips[i][j++] +		\
+				((tmp >> 1) * TRIE_TBL8_GRP_NUM_ENT)];	\
+		}							\
+		next_hops[i] = tmp >> 1;				\
+	}								\
+}
+LOOKUP_FUNC(2b, uint16_t, 1)
+LOOKUP_FUNC(4b, uint32_t, 2)
+LOOKUP_FUNC(8b, uint64_t, 3)
+
+rte_fib6_lookup_fn_t
+rte_trie_get_lookup_fn(struct rte_fib6_conf *conf)
+{
+	enum rte_fib_trie_nh_sz nh_sz = conf->trie.nh_sz;
+
+	if (test_lookup == MACRO) {
+		switch (nh_sz) {
+		case RTE_FIB6_TRIE_2B:
+			return rte_trie_lookup_bulk_2b;
+		case RTE_FIB6_TRIE_4B:
+			return rte_trie_lookup_bulk_4b;
+		case RTE_FIB6_TRIE_8B:
+			return rte_trie_lookup_bulk_8b;
+		}
+	}
+
+	return NULL;
+}
+
+static void
+write_to_dp(void *ptr, uint64_t val, enum rte_fib_trie_nh_sz size, int n)
+{
+	int i;
+	uint16_t *ptr16 = (uint16_t *)ptr;
+	uint32_t *ptr32 = (uint32_t *)ptr;
+	uint64_t *ptr64 = (uint64_t *)ptr;
+
+	switch (size) {
+	case RTE_FIB6_TRIE_2B:
+		for (i = 0; i < n; i++)
+			ptr16[i] = (uint16_t)val;
+		break;
+	case RTE_FIB6_TRIE_4B:
+		for (i = 0; i < n; i++)
+			ptr32[i] = (uint32_t)val;
+		break;
+	case RTE_FIB6_TRIE_8B:
+		for (i = 0; i < n; i++)
+			ptr64[i] = (uint64_t)val;
+		break;
+	}
+}
+
+static void
+tbl8_pool_init(struct rte_trie_tbl *dp)
+{
+	uint32_t i;
+
+	/* put entire range of indexes to the tbl8 pool */
+	for (i = 0; i < dp->number_tbl8s; i++)
+		dp->tbl8_pool[i] = i;
+
+	dp->tbl8_pool_pos = 0;
+}
+
+/*
+ * Get an index of a free tbl8 from the pool
+ */
+static inline int32_t
+tbl8_get(struct rte_trie_tbl *dp)
+{
+	if (dp->tbl8_pool_pos == dp->number_tbl8s)
+		/* no more free tbl8 */
+		return -ENOSPC;
+
+	/* next index */
+	return dp->tbl8_pool[dp->tbl8_pool_pos++];
+}
+
+/*
+ * Put an index of a free tbl8 back to the pool
+ */
+static inline void
+tbl8_put(struct rte_trie_tbl *dp, uint32_t tbl8_ind)
+{
+	dp->tbl8_pool[--dp->tbl8_pool_pos] = tbl8_ind;
+}
+
+static int
+tbl8_alloc(struct rte_trie_tbl *dp, uint64_t nh)
+{
+	int64_t		tbl8_idx;
+	uint8_t		*tbl8_ptr;
+
+	tbl8_idx = tbl8_get(dp);
+	if (tbl8_idx < 0)
+		return tbl8_idx;
+	tbl8_ptr = (uint8_t *)dp->tbl8 +
+		((tbl8_idx * TRIE_TBL8_GRP_NUM_ENT) <<
+		dp->nh_sz);
+	/*Init tbl8 entries with nexthop from tbl24*/
+	write_to_dp((void *)tbl8_ptr, nh, dp->nh_sz,
+		TRIE_TBL8_GRP_NUM_ENT);
+	return tbl8_idx;
+}
+
+static void
+tbl8_recycle(struct rte_trie_tbl *dp, void *par, uint64_t tbl8_idx)
+{
+	uint32_t i;
+	uint64_t nh;
+	uint16_t *ptr16;
+	uint32_t *ptr32;
+	uint64_t *ptr64;
+
+	switch (dp->nh_sz) {
+	case RTE_FIB6_TRIE_2B:
+		ptr16 = &((uint16_t *)dp->tbl8)[tbl8_idx *
+				TRIE_TBL8_GRP_NUM_ENT];
+		nh = *ptr16;
+		if (nh & TRIE_EXT_ENT)
+			return;
+		for (i = 1; i < TRIE_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr16[i])
+				return;
+		}
+		write_to_dp(par, nh, dp->nh_sz, 1);
+		for (i = 0; i < TRIE_TBL8_GRP_NUM_ENT; i++)
+			ptr16[i] = 0;
+		break;
+	case RTE_FIB6_TRIE_4B:
+		ptr32 = &((uint32_t *)dp->tbl8)[tbl8_idx *
+				TRIE_TBL8_GRP_NUM_ENT];
+		nh = *ptr32;
+		if (nh & TRIE_EXT_ENT)
+			return;
+		for (i = 1; i < TRIE_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr32[i])
+				return;
+		}
+		write_to_dp(par, nh, dp->nh_sz, 1);
+		for (i = 0; i < TRIE_TBL8_GRP_NUM_ENT; i++)
+			ptr32[i] = 0;
+		break;
+	case RTE_FIB6_TRIE_8B:
+		ptr64 = &((uint64_t *)dp->tbl8)[tbl8_idx *
+				TRIE_TBL8_GRP_NUM_ENT];
+		nh = *ptr64;
+		if (nh & TRIE_EXT_ENT)
+			return;
+		for (i = 1; i < TRIE_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr64[i])
+				return;
+		}
+		write_to_dp(par, nh, dp->nh_sz, 1);
+		for (i = 0; i < TRIE_TBL8_GRP_NUM_ENT; i++)
+			ptr64[i] = 0;
+		break;
+	}
+	tbl8_put(dp, tbl8_idx);
+}
+
+#define BYTE_SIZE	8
+static inline uint32_t
+get_idx(const uint8_t *ip, uint32_t prev_idx, int bytes, int first_byte)
+{
+	int i;
+	uint32_t idx = 0;
+	uint8_t bitshift;
+
+	for (i = first_byte; i < (first_byte + bytes); i++) {
+		bitshift = (int8_t)(((first_byte + bytes - 1) - i)*BYTE_SIZE);
+		idx |= ip[i] <<  bitshift;
+	}
+	return (prev_idx * 256) + idx;
+}
+
+static inline uint64_t
+get_val_by_p(void *p, uint8_t nh_sz)
+{
+	uint64_t val = 0;
+
+	switch (nh_sz) {
+	case RTE_FIB6_TRIE_2B:
+		val = *(uint16_t *)p;
+		break;
+	case RTE_FIB6_TRIE_4B:
+		val = *(uint32_t *)p;
+		break;
+	case RTE_FIB6_TRIE_8B:
+		val = *(uint64_t *)p;
+		break;
+	}
+	return val;
+}
+
+/*
+ * recursively recycle tbl8's
+ */
+static void
+recycle_root_path(struct rte_trie_tbl *dp, const uint8_t *ip_part,
+	uint8_t common_tbl8, void *prev)
+{
+	void *p;
+	uint64_t val;
+
+	val = get_val_by_p(prev, dp->nh_sz);
+	if (unlikely((val & TRIE_EXT_ENT) != TRIE_EXT_ENT))
+		return;
+
+	if (common_tbl8 != 0) {
+		p = get_tbl_p_by_idx(dp->tbl8, (val >> 1) * 256 + *ip_part,
+			dp->nh_sz);
+		recycle_root_path(dp, ip_part + 1, common_tbl8 - 1, p);
+	}
+	tbl8_recycle(dp, prev, val >> 1);
+}
+
+static inline int
+build_common_root(struct rte_trie_tbl *dp, const uint8_t *ip,
+	int common_bytes, void **tbl)
+{
+	void *tbl_ptr = NULL;
+	uint64_t *cur_tbl;
+	uint64_t val;
+	int i, j, idx, prev_idx = 0;
+
+	cur_tbl = dp->tbl24;
+	for (i = 3, j = 0; i <= common_bytes; i++) {
+		idx = get_idx(ip, prev_idx, i - j, j);
+		val = get_tbl_val_by_idx(cur_tbl, idx, dp->nh_sz);
+		tbl_ptr = get_tbl_p_by_idx(cur_tbl, idx, dp->nh_sz);
+		if ((val & TRIE_EXT_ENT) != TRIE_EXT_ENT) {
+			idx = tbl8_alloc(dp, val);
+			if (unlikely(idx < 0))
+				return idx;
+			write_to_dp(tbl_ptr, (idx << 1) |
+				TRIE_EXT_ENT, dp->nh_sz, 1);
+			prev_idx = idx;
+		} else
+			prev_idx = val >> 1;
+
+		j = i;
+		cur_tbl = dp->tbl8;
+	}
+	*tbl = get_tbl_p_by_idx(cur_tbl, prev_idx * 256, dp->nh_sz);
+	return 0;
+}
+
+static int
+write_edge(struct rte_trie_tbl *dp, const uint8_t *ip_part, uint64_t next_hop,
+	int len, enum edge edge, void *ent)
+{
+	uint64_t val = next_hop << 1;
+	int tbl8_idx;
+	int ret = 0;
+	void *p;
+
+	if (len != 0) {
+		val = get_val_by_p(ent, dp->nh_sz);
+		if ((val & TRIE_EXT_ENT) == TRIE_EXT_ENT)
+			tbl8_idx = val >> 1;
+		else {
+			tbl8_idx = tbl8_alloc(dp, val);
+			if (tbl8_idx < 0)
+				return tbl8_idx;
+			val = (tbl8_idx << 1)|TRIE_EXT_ENT;
+		}
+		p = get_tbl_p_by_idx(dp->tbl8, (tbl8_idx * 256) + *ip_part,
+			dp->nh_sz);
+		ret = write_edge(dp, ip_part + 1, next_hop, len - 1, edge, p);
+		if (ret < 0)
+			return ret;
+		if (edge == LEDGE) {
+			write_to_dp((uint8_t *)p + (1 << dp->nh_sz),
+				next_hop << 1, dp->nh_sz, UINT8_MAX - *ip_part);
+		} else {
+			write_to_dp(get_tbl_p_by_idx(dp->tbl8, tbl8_idx * 256,
+				dp->nh_sz),
+				next_hop << 1, dp->nh_sz, *ip_part);
+		}
+		tbl8_recycle(dp, &val, tbl8_idx);
+	}
+
+	write_to_dp(ent, val, dp->nh_sz, 1);
+	return ret;
+}
+
+#define IPV6_MAX_IDX	(RTE_FIB6_IPV6_ADDR_SIZE - 1)
+#define TBL24_BYTES	3
+#define TBL8_LEN	(RTE_FIB6_IPV6_ADDR_SIZE - TBL24_BYTES)
+
+static int
+install_to_dp(struct rte_trie_tbl *dp, const uint8_t *ledge, const uint8_t *r,
+	uint64_t next_hop)
+{
+	void *common_root_tbl;
+	void *ent;
+	int ret;
+	int i;
+	int common_bytes;
+	int llen, rlen;
+	uint8_t redge[16];
+
+	/* decrement redge by 1*/
+	rte_rib6_copy_addr(redge, r);
+	for (i = 15; i >= 0; i--) {
+		redge[i]--;
+		if (redge[i] != 0xff)
+			break;
+	}
+
+	for (common_bytes = 0; common_bytes < 15; common_bytes++) {
+		if (ledge[common_bytes] != redge[common_bytes])
+			break;
+	}
+
+	ret = build_common_root(dp, ledge, common_bytes, &common_root_tbl);
+	if (unlikely(ret != 0))
+		return ret;
+	/*first uncommon tbl8 byte idx*/
+	uint8_t first_tbl8_byte = RTE_MAX(common_bytes, TBL24_BYTES);
+
+	for (i = IPV6_MAX_IDX; i > first_tbl8_byte; i--) {
+		if (ledge[i] != 0)
+			break;
+	}
+
+	llen = i - first_tbl8_byte + (common_bytes < 3);
+
+	for (i = IPV6_MAX_IDX; i > first_tbl8_byte; i--) {
+		if (redge[i] != UINT8_MAX)
+			break;
+	}
+	rlen = i - first_tbl8_byte + (common_bytes < 3);
+
+	/*first noncommon byte*/
+	uint8_t first_byte_idx = (common_bytes < 3) ? 0 : common_bytes;
+	uint8_t first_idx_len = (common_bytes < 3) ? 3 : 1;
+
+	uint32_t left_idx = get_idx(ledge, 0, first_idx_len, first_byte_idx);
+	uint32_t right_idx = get_idx(redge, 0, first_idx_len, first_byte_idx);
+
+	ent = get_tbl_p_by_idx(common_root_tbl, left_idx, dp->nh_sz);
+	ret = write_edge(dp, &ledge[first_tbl8_byte + !(common_bytes < 3)],
+		next_hop, llen, LEDGE, ent);
+	if (ret < 0)
+		return ret;
+
+	if (right_idx > left_idx + 1) {
+		ent = get_tbl_p_by_idx(common_root_tbl, left_idx + 1,
+			dp->nh_sz);
+		write_to_dp(ent, next_hop << 1, dp->nh_sz,
+			right_idx - (left_idx + 1));
+	}
+	ent = get_tbl_p_by_idx(common_root_tbl, right_idx, dp->nh_sz);
+	ret = write_edge(dp, &redge[first_tbl8_byte + !((common_bytes < 3))],
+		next_hop, rlen, REDGE, ent);
+	if (ret < 0)
+		return ret;
+
+	uint8_t	common_tbl8 = (common_bytes < TBL24_BYTES) ?
+			0 : common_bytes - (TBL24_BYTES - 1);
+	ent = get_tbl24_p(dp, ledge, dp->nh_sz);
+	recycle_root_path(dp, ledge + TBL24_BYTES, common_tbl8, ent);
+	return 0;
+}
+
+static void
+get_nxt_net(uint8_t *ip, uint8_t depth)
+{
+	int i;
+	uint8_t part_depth;
+	uint8_t prev_byte;
+
+	for (i = 0, part_depth = depth; part_depth > 8; part_depth -= 8, i++)
+		;
+
+	prev_byte = ip[i];
+	ip[i] += 1 << (8 - part_depth);
+	if (ip[i] < prev_byte) {
+		while (i > 0) {
+			ip[--i] += 1;
+			if (ip[i] != 0)
+				break;
+		}
+	}
+}
+
+static int
+modify_dp(struct rte_trie_tbl *dp, struct rte_rib6 *rib,
+	const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, uint64_t next_hop)
+{
+	struct rte_rib6_node *tmp = NULL;
+	uint8_t ledge[RTE_FIB6_IPV6_ADDR_SIZE];
+	uint8_t redge[RTE_FIB6_IPV6_ADDR_SIZE];
+	int ret;
+	uint8_t tmp_depth;
+
+	if (next_hop > get_max_nh(dp->nh_sz))
+		return -EINVAL;
+
+	rte_rib6_copy_addr(ledge, ip);
+	do {
+		tmp = rte_rib6_get_nxt(rib, ip, depth, tmp,
+			RTE_RIB6_GET_NXT_COVER);
+		if (tmp != NULL) {
+			rte_rib6_get_depth(tmp, &tmp_depth);
+			if (tmp_depth == depth)
+				continue;
+			rte_rib6_get_ip(tmp, redge);
+			if (rte_rib6_is_equal(ledge, redge)) {
+				get_nxt_net(ledge, tmp_depth);
+				continue;
+			}
+			ret = install_to_dp(dp, ledge, redge,
+				next_hop);
+			if (ret != 0)
+				return ret;
+			get_nxt_net(redge, tmp_depth);
+			rte_rib6_copy_addr(ledge, redge);
+		} else {
+			rte_rib6_copy_addr(redge, ip);
+			get_nxt_net(redge, depth);
+			if (rte_rib6_is_equal(ledge, redge))
+				break;
+			ret = install_to_dp(dp, ledge, redge,
+				next_hop);
+			if (ret != 0)
+				return ret;
+		}
+	} while (tmp);
+
+	return 0;
+}
+
+int
+trie_modify(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, uint64_t next_hop, int op)
+{
+	struct rte_trie_tbl *dp;
+	struct rte_rib6 *rib;
+	struct rte_rib6_node *tmp = NULL;
+	struct rte_rib6_node *node;
+	struct rte_rib6_node *parent;
+	uint8_t	ip_masked[RTE_FIB6_IPV6_ADDR_SIZE];
+	int i, ret = 0;
+	uint64_t par_nh, node_nh;
+	uint8_t tmp_depth, depth_diff = 0, parent_depth = 24;
+
+	if ((fib == NULL) || (ip == NULL) || (depth > RTE_FIB6_MAXDEPTH))
+		return -EINVAL;
+
+	dp = rte_fib6_get_dp(fib);
+	RTE_ASSERT(dp);
+	rib = rte_fib6_get_rib(fib);
+	RTE_ASSERT(rib);
+
+	for (i = 0; i < RTE_FIB6_IPV6_ADDR_SIZE; i++)
+		ip_masked[i] = ip[i] & get_msk_part(depth, i);
+
+	if (depth > 24) {
+		tmp = rte_rib6_get_nxt(rib, ip_masked,
+			RTE_ALIGN_FLOOR(depth, 8), NULL,
+			RTE_RIB6_GET_NXT_COVER);
+		if (tmp == NULL) {
+			tmp = rte_rib6_lookup(rib, ip);
+			if (tmp != NULL) {
+				rte_rib6_get_depth(tmp, &tmp_depth);
+				parent_depth = RTE_MAX(tmp_depth, 24);
+			}
+			depth_diff = RTE_ALIGN_CEIL(depth, 8) -
+				RTE_ALIGN_CEIL(parent_depth, 8);
+			depth_diff = depth_diff >> 3;
+		}
+	}
+	node = rte_rib6_lookup_exact(rib, ip_masked, depth);
+	switch (op) {
+	case RTE_FIB6_ADD:
+		if (node != NULL) {
+			rte_rib6_get_nh(node, &node_nh);
+			if (node_nh == next_hop)
+				return 0;
+			ret = modify_dp(dp, rib, ip_masked, depth, next_hop);
+			if (ret == 0)
+				rte_rib6_set_nh(node, next_hop);
+			return 0;
+		}
+
+		if ((depth > 24) && (dp->rsvd_tbl8s >=
+				dp->number_tbl8s - depth_diff))
+			return -ENOSPC;
+
+		node = rte_rib6_insert(rib, ip_masked, depth);
+		if (node == NULL)
+			return -rte_errno;
+		rte_rib6_set_nh(node, next_hop);
+		parent = rte_rib6_lookup_parent(node);
+		if (parent != NULL) {
+			rte_rib6_get_nh(parent, &par_nh);
+			if (par_nh == next_hop)
+				return 0;
+		}
+		ret = modify_dp(dp, rib, ip_masked, depth, next_hop);
+		if (ret != 0) {
+			rte_rib6_remove(rib, ip_masked, depth);
+			return ret;
+		}
+
+		dp->rsvd_tbl8s += depth_diff;
+		return 0;
+	case RTE_FIB6_DEL:
+		if (node == NULL)
+			return -ENOENT;
+
+		parent = rte_rib6_lookup_parent(node);
+		if (parent != NULL) {
+			rte_rib6_get_nh(parent, &par_nh);
+			rte_rib6_get_nh(node, &node_nh);
+			if (par_nh != node_nh)
+				ret = modify_dp(dp, rib, ip_masked, depth,
+					par_nh);
+		} else
+			ret = modify_dp(dp, rib, ip_masked, depth, dp->def_nh);
+
+		if (ret != 0)
+			return ret;
+		rte_rib6_remove(rib, ip, depth);
+
+		dp->rsvd_tbl8s -= depth_diff;
+		return 0;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+void *
+trie_create(const char *name, int socket_id,
+	struct rte_fib6_conf *conf)
+{
+	char mem_name[TRIE_NAMESIZE];
+	struct rte_trie_tbl *dp = NULL;
+	uint64_t	def_nh;
+	uint32_t	num_tbl8;
+	enum rte_fib_trie_nh_sz	nh_sz;
+
+	if ((name == NULL) || (conf == NULL) ||
+			(conf->trie.nh_sz < RTE_FIB6_TRIE_2B) ||
+			(conf->trie.nh_sz > RTE_FIB6_TRIE_8B) ||
+			(conf->trie.num_tbl8 >
+			get_max_nh(conf->trie.nh_sz)) ||
+			(conf->trie.num_tbl8 == 0) ||
+			(conf->default_nh >
+			get_max_nh(conf->trie.nh_sz))) {
+
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	def_nh = conf->default_nh;
+	nh_sz = conf->trie.nh_sz;
+	num_tbl8 = conf->trie.num_tbl8;
+
+	snprintf(mem_name, sizeof(mem_name), "DP_%s", name);
+	dp = rte_zmalloc_socket(name, sizeof(struct rte_trie_tbl) +
+		TRIE_TBL24_NUM_ENT * (1 << nh_sz), RTE_CACHE_LINE_SIZE,
+		socket_id);
+	if (dp == NULL) {
+		rte_errno = ENOMEM;
+		return dp;
+	}
+
+	write_to_dp(&dp->tbl24, (def_nh << 1), nh_sz, 1 << 24);
+
+	snprintf(mem_name, sizeof(mem_name), "TBL8_%p", dp);
+	dp->tbl8 = rte_zmalloc_socket(mem_name, TRIE_TBL8_GRP_NUM_ENT *
+			(1ll << nh_sz) * (num_tbl8 + 1),
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (dp->tbl8 == NULL) {
+		rte_errno = ENOMEM;
+		rte_free(dp);
+		return NULL;
+	}
+	dp->def_nh = def_nh;
+	dp->nh_sz = nh_sz;
+	dp->number_tbl8s = num_tbl8;
+
+	snprintf(mem_name, sizeof(mem_name), "TBL8_idxes_%p", dp);
+	dp->tbl8_pool = rte_zmalloc_socket(mem_name,
+			sizeof(uint32_t) * dp->number_tbl8s,
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (dp->tbl8_pool == NULL) {
+		rte_errno = ENOMEM;
+		rte_free(dp->tbl8);
+		rte_free(dp);
+		return NULL;
+	}
+
+	tbl8_pool_init(dp);
+
+	return dp;
+}
+
+void
+trie_free(void *p)
+{
+	struct rte_trie_tbl *dp = (struct rte_trie_tbl *)p;
+
+	rte_free(dp->tbl8_pool);
+	rte_free(dp->tbl8);
+	rte_free(dp);
+}
+
diff --git a/lib/librte_fib/trie.h b/lib/librte_fib/trie.h
new file mode 100644
index 0000000..7762fb9
--- /dev/null
+++ b/lib/librte_fib/trie.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _TRIE_H_
+#define _TRIE_H_
+
+/**
+ * @file
+ * RTE IPv6 Longest Prefix Match (LPM)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *
+trie_create(const char *name, int socket_id, struct rte_fib6_conf *conf);
+
+void
+trie_free(void *p);
+
+rte_fib6_lookup_fn_t
+rte_trie_get_lookup_fn(struct rte_fib6_conf *fib_conf);
+
+int
+trie_modify(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, uint64_t next_hop, int op);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TRIE_H_ */
+
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 09/12] test/fib: add FIB library autotests
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (14 preceding siblings ...)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 08/12] fib: add dataplane algorithm for ipv6 Vladimir Medvedkin
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  2019-09-12 14:07   ` Aaron Conole
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 10/12] test/fib: add ipv6 support for FIB autotests Vladimir Medvedkin
                   ` (2 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

Functional tests for the new FIB library.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |   6 +
 app/test/meson.build      |   3 +
 app/test/test_fib.c       | 397 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 407 insertions(+)
 create mode 100644 app/test/test_fib.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 4638426..b67ca16 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -126,6 +126,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite_lf.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib.c
 SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib6.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index f35b3cc..e736c03 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -123,6 +123,12 @@
         "Report":  None,
     },
     {
+        "Name":    "FIB autotest",
+        "Command": "fib_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
         "Name":    "Memcpy autotest",
         "Command": "memcpy_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 49c1de0..9f1895d 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -47,6 +47,7 @@ test_sources = files('commands.c',
 	'test_eventdev.c',
 	'test_external_mem.c',
 	'test_fbarray.c',
+	'test_fib.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
 	'test_hash.c',
@@ -137,6 +138,7 @@ test_deps = ['acl',
 	'efd',
 	'ethdev',
 	'eventdev',
+	'fib',
 	'flow_classify',
 	'hash',
 	'ipsec',
@@ -180,6 +182,7 @@ fast_test_names = [
         'eal_fs_autotest',
         'errno_autotest',
         'event_ring_autotest',
+        'fib_autotest',
         'func_reentrancy_autotest',
         'flow_classify_autotest',
         'hash_autotest',
diff --git a/app/test/test_fib.c b/app/test/test_fib.c
new file mode 100644
index 0000000..c676a68
--- /dev/null
+++ b/app/test/test_fib.c
@@ -0,0 +1,397 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <rte_ip.h>
+#include <rte_log.h>
+#include <rte_fib.h>
+
+#include "test.h"
+
+typedef int32_t (*rte_fib_test)(void);
+
+static int32_t test_create_invalid(void);
+static int32_t test_multiple_create(void);
+static int32_t test_free_null(void);
+static int32_t test_add_del_invalid(void);
+static int32_t test_get_invalid(void);
+static int32_t test_lookup(void);
+
+#define MAX_ROUTES (1 << 22)
+
+/*
+ * Check that rte_fib_create fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test_create_invalid(void)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_fib_conf config;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = 0;
+	config.type = RTE_FIB_DUMMY;
+
+	/* rte_fib_create: fib name == NULL */
+	fib = rte_fib_create(NULL, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib_create: config == NULL */
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, NULL);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* socket_id < -1 is invalid */
+	fib = rte_fib_create(__func__, -2, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib_create: max_routes = 0 */
+	config.max_routes = 0;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.max_routes = MAX_ROUTES;
+
+	config.type = RTE_FIB_TYPE_MAX;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	config.type = RTE_FIB_DIR24_8;
+	config.dir24_8.num_tbl8 = 100000;
+
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_8B + 1;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_8B;
+
+	config.dir24_8.num_tbl8 = 0;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Create fib table then delete fib table 10 times
+ * Use a slightly different rules size each time
+ */
+int32_t
+test_multiple_create(void)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_fib_conf config;
+	int32_t i;
+
+	config.default_nh = 0;
+	config.type = RTE_FIB_DUMMY;
+
+	for (i = 0; i < 5; i++) {
+		config.max_routes = MAX_ROUTES - i;
+		fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+		RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+		rte_fib_free(fib);
+	}
+	/* Can not test free so return success */
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call rte_fib_free for NULL pointer user input. Note: free has no return and
+ * therefore it is impossible to check for failure but this test is added to
+ * increase function coverage metrics and to validate that freeing null does
+ * not crash.
+ */
+int32_t
+test_free_null(void)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_fib_conf config;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = 0;
+	config.type = RTE_FIB_DUMMY;
+
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+
+	rte_fib_free(fib);
+	rte_fib_free(NULL);
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_fib_add and rte_fib_delete fails gracefully
+ * for incorrect user input arguments
+ */
+int32_t
+test_add_del_invalid(void)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_fib_conf config;
+	uint64_t nh = 100;
+	uint32_t ip = RTE_IPV4(0, 0, 0, 0);
+	int ret;
+	uint8_t depth = 24;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = 0;
+	config.type = RTE_FIB_DUMMY;
+
+	/* rte_fib_add: fib == NULL */
+	ret = rte_fib_add(NULL, ip, depth, nh);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib_delete: fib == NULL */
+	ret = rte_fib_delete(NULL, ip, depth);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/*Create valid fib to use in rest of test. */
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+
+	/* rte_fib_add: depth > RTE_FIB_MAXDEPTH */
+	ret = rte_fib_add(fib, ip, RTE_FIB_MAXDEPTH + 1, nh);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib_delete: depth > RTE_FIB_MAXDEPTH */
+	ret = rte_fib_delete(fib, ip, RTE_FIB_MAXDEPTH + 1);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	rte_fib_free(fib);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_fib_get_dp and rte_fib_get_rib fails gracefully
+ * for incorrect user input arguments
+ */
+int32_t
+test_get_invalid(void)
+{
+	void *p;
+
+	p = rte_fib_get_dp(NULL);
+	RTE_TEST_ASSERT(p == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	p = rte_fib_get_rib(NULL);
+	RTE_TEST_ASSERT(p == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Add routes for one supernet with all possible depths and do lookup
+ * on each step
+ * After delete routes with doing lookup on each step
+ */
+static int
+lookup_and_check_asc(struct rte_fib *fib, uint32_t ip_arr[RTE_FIB_MAXDEPTH],
+	uint32_t ip_missing, uint64_t def_nh, uint32_t n)
+{
+	uint64_t nh_arr[RTE_FIB_MAXDEPTH];
+	int ret;
+	uint32_t i = 0;
+
+	ret = rte_fib_lookup_bulk(fib, ip_arr, nh_arr, RTE_FIB_MAXDEPTH);
+	RTE_TEST_ASSERT(ret == 0, "Failed to lookup\n");
+
+	for (; i <= RTE_FIB_MAXDEPTH - n; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == n,
+			"Failed to get proper nexthop\n");
+
+	for (; i < RTE_FIB_MAXDEPTH; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == --n,
+			"Failed to get proper nexthop\n");
+
+	ret = rte_fib_lookup_bulk(fib, &ip_missing, nh_arr, 1);
+	RTE_TEST_ASSERT((ret == 0) && (nh_arr[0] == def_nh),
+		"Failed to get proper nexthop\n");
+
+	return TEST_SUCCESS;
+}
+
+static int
+lookup_and_check_desc(struct rte_fib *fib, uint32_t ip_arr[RTE_FIB_MAXDEPTH],
+	uint32_t ip_missing, uint64_t def_nh, uint32_t n)
+{
+	uint64_t nh_arr[RTE_FIB_MAXDEPTH];
+	int ret;
+	uint32_t i = 0;
+
+	ret = rte_fib_lookup_bulk(fib, ip_arr, nh_arr, RTE_FIB_MAXDEPTH);
+	RTE_TEST_ASSERT(ret == 0, "Failed to lookup\n");
+
+	for (; i < n; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == RTE_FIB_MAXDEPTH - i,
+			"Failed to get proper nexthop\n");
+
+	for (; i < RTE_FIB_MAXDEPTH; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == def_nh,
+			"Failed to get proper nexthop\n");
+
+	ret = rte_fib_lookup_bulk(fib, &ip_missing, nh_arr, 1);
+	RTE_TEST_ASSERT((ret == 0) && (nh_arr[0] == def_nh),
+		"Failed to get proper nexthop\n");
+
+	return TEST_SUCCESS;
+}
+
+static int
+check_fib(struct rte_fib *fib)
+{
+	uint64_t def_nh = 100;
+	uint32_t ip_arr[RTE_FIB_MAXDEPTH];
+	uint32_t ip_add = RTE_IPV4(128, 0, 0, 0);
+	uint32_t i, ip_missing = RTE_IPV4(127, 255, 255, 255);
+	int ret;
+
+	for (i = 0; i < RTE_FIB_MAXDEPTH; i++)
+		ip_arr[i] = ip_add + (1ULL << i) - 1;
+
+	ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh, 0);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+
+	for (i = 1; i <= RTE_FIB_MAXDEPTH; i++) {
+		ret = rte_fib_add(fib, ip_add, i, i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to add a route\n");
+		ret = lookup_and_check_asc(fib, ip_arr, ip_missing,
+				def_nh, i);
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+	}
+
+	for (i = RTE_FIB_MAXDEPTH; i > 1; i--) {
+		ret = rte_fib_delete(fib, ip_add, i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
+		ret = lookup_and_check_asc(fib, ip_arr, ip_missing,
+			def_nh, i - 1);
+
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+	}
+	ret = rte_fib_delete(fib, ip_add, i);
+	RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
+	ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh, 0);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+
+	for (i = 0; i < RTE_FIB_MAXDEPTH; i++) {
+		ret = rte_fib_add(fib, ip_add, RTE_FIB_MAXDEPTH - i,
+			RTE_FIB_MAXDEPTH - i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to add a route\n");
+		ret = lookup_and_check_desc(fib, ip_arr, ip_missing,
+			def_nh, i + 1);
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+	}
+
+	for (i = 1; i <= RTE_FIB_MAXDEPTH; i++) {
+		ret = rte_fib_delete(fib, ip_add, i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
+		ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh,
+			RTE_FIB_MAXDEPTH - i);
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+	}
+
+	return TEST_SUCCESS;
+}
+
+int32_t
+test_lookup(void)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_fib_conf config;
+	uint64_t def_nh = 100;
+	int ret;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = def_nh;
+	config.type = RTE_FIB_DUMMY;
+
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for DUMMY type\n");
+	rte_fib_free(fib);
+
+	config.type = RTE_FIB_DIR24_8;
+
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_1B;
+	config.dir24_8.num_tbl8 = 127;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for DIR24_8_1B type\n");
+	rte_fib_free(fib);
+
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_2B;
+	config.dir24_8.num_tbl8 = 32767;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for DIR24_8_2B type\n");
+	rte_fib_free(fib);
+
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_4B;
+	config.dir24_8.num_tbl8 = 100000;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for DIR24_8_4B type\n");
+	rte_fib_free(fib);
+
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_8B;
+	config.dir24_8.num_tbl8 = 100000;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for DIR24_8_8B type\n");
+	rte_fib_free(fib);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite fib_tests = {
+	.suite_name = "fib autotest",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+	TEST_CASE(test_create_invalid),
+	TEST_CASE(test_multiple_create),
+	TEST_CASE(test_free_null),
+	TEST_CASE(test_add_del_invalid),
+	TEST_CASE(test_get_invalid),
+	TEST_CASE(test_lookup),
+	TEST_CASES_END()
+	}
+};
+
+/*
+ * Do all unit tests.
+ */
+static int
+test_fib(void)
+{
+	return unit_test_suite_runner(&fib_tests);
+}
+
+REGISTER_TEST_COMMAND(fib_autotest, test_fib);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 10/12] test/fib: add ipv6 support for FIB autotests
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (15 preceding siblings ...)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 09/12] test/fib: add FIB library autotests Vladimir Medvedkin
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 11/12] test/fib: add FIB library performance autotests Vladimir Medvedkin
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 12/12] test/fib: add FIB library ipv6 " Vladimir Medvedkin
  18 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

Functional ipv6 tests for the FIB library.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |   6 +
 app/test/meson.build      |   2 +
 app/test/test_fib6.c      | 405 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 414 insertions(+)
 create mode 100644 app/test/test_fib6.c

diff --git a/app/test/Makefile b/app/test/Makefile
index b67ca16..c230dbf 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -127,6 +127,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite_lf.c
 SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib.c
 SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib6.c
 SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib6.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index e736c03..1244ec4 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -129,6 +129,12 @@
         "Report":  None,
     },
     {
+        "Name":    "FIB6 autotest",
+        "Command": "fib6_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
         "Name":    "Memcpy autotest",
         "Command": "memcpy_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 9f1895d..fb3595b 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -48,6 +48,7 @@ test_sources = files('commands.c',
 	'test_external_mem.c',
 	'test_fbarray.c',
 	'test_fib.c',
+	'test_fib6.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
 	'test_hash.c',
@@ -183,6 +184,7 @@ fast_test_names = [
         'errno_autotest',
         'event_ring_autotest',
         'fib_autotest',
+        'fib6_autotest',
         'func_reentrancy_autotest',
         'flow_classify_autotest',
         'hash_autotest',
diff --git a/app/test/test_fib6.c b/app/test/test_fib6.c
new file mode 100644
index 0000000..0d476ba
--- /dev/null
+++ b/app/test/test_fib6.c
@@ -0,0 +1,405 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <rte_memory.h>
+#include <rte_log.h>
+#include <rte_rib6.h>
+#include <rte_fib6.h>
+
+#include "test.h"
+
+typedef int32_t (*rte_fib6_test)(void);
+
+static int32_t test_create_invalid(void);
+static int32_t test_multiple_create(void);
+static int32_t test_free_null(void);
+static int32_t test_add_del_invalid(void);
+static int32_t test_get_invalid(void);
+static int32_t test_lookup(void);
+
+#define MAX_ROUTES (1 << 22)
+
+/*
+ * Check that rte_fib6_create fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test_create_invalid(void)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_fib6_conf config;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = 0;
+	config.type = RTE_FIB6_DUMMY;
+
+	/* rte_fib6_create: fib name == NULL */
+	fib = rte_fib6_create(NULL, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib6_create: config == NULL */
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, NULL);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* socket_id < -1 is invalid */
+	fib = rte_fib6_create(__func__, -2, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib6_create: max_routes = 0 */
+	config.max_routes = 0;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.max_routes = MAX_ROUTES;
+
+	config.type = RTE_FIB6_TYPE_MAX;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	config.type = RTE_FIB6_TRIE;
+	config.trie.num_tbl8 = 100000;
+
+	config.trie.nh_sz = RTE_FIB6_TRIE_8B + 1;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.trie.nh_sz = RTE_FIB6_TRIE_8B;
+
+	config.trie.num_tbl8 = 0;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Create fib table then delete fib table 10 times
+ * Use a slightly different rules size each time
+ */
+int32_t
+test_multiple_create(void)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_fib6_conf config;
+	int32_t i;
+
+	config.default_nh = 0;
+	config.type = RTE_FIB6_DUMMY;
+
+	for (i = 0; i < 5; i++) {
+		config.max_routes = MAX_ROUTES - i;
+		fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+		RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+		rte_fib6_free(fib);
+	}
+	/* Can not test free so return success */
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call rte_fib6_free for NULL pointer user input. Note: free has no return and
+ * therefore it is impossible to check for failure but this test is added to
+ * increase function coverage metrics and to validate that freeing null does
+ * not crash.
+ */
+int32_t
+test_free_null(void)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_fib6_conf config;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = 0;
+	config.type = RTE_FIB6_DUMMY;
+
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+
+	rte_fib6_free(fib);
+	rte_fib6_free(NULL);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_fib6_add and rte_fib6_delete fails gracefully
+ * for incorrect user input arguments
+ */
+int32_t
+test_add_del_invalid(void)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_fib6_conf config;
+	uint64_t nh = 100;
+	uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE] = {0};
+	int ret;
+	uint8_t depth = 24;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = 0;
+	config.type = RTE_FIB6_DUMMY;
+
+	/* rte_fib6_add: fib == NULL */
+	ret = rte_fib6_add(NULL, ip, depth, nh);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib6_delete: fib == NULL */
+	ret = rte_fib6_delete(NULL, ip, depth);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/*Create valid fib to use in rest of test. */
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+
+	/* rte_fib6_add: depth > RTE_FIB6_MAXDEPTH */
+	ret = rte_fib6_add(fib, ip, RTE_FIB6_MAXDEPTH + 1, nh);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib6_delete: depth > RTE_FIB6_MAXDEPTH */
+	ret = rte_fib6_delete(fib, ip, RTE_FIB6_MAXDEPTH + 1);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	rte_fib6_free(fib);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_fib6_get_dp and rte_fib6_get_rib fails gracefully
+ * for incorrect user input arguments
+ */
+int32_t
+test_get_invalid(void)
+{
+	void *p;
+
+	p = rte_fib6_get_dp(NULL);
+	RTE_TEST_ASSERT(p == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	p = rte_fib6_get_rib(NULL);
+	RTE_TEST_ASSERT(p == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Add routes for one supernet with all possible depths and do lookup
+ * on each step
+ * After delete routes with doing lookup on each step
+ */
+static int
+lookup_and_check_asc(struct rte_fib6 *fib,
+	uint8_t ip_arr[RTE_FIB6_MAXDEPTH][RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t ip_missing[][RTE_FIB6_IPV6_ADDR_SIZE], uint64_t def_nh,
+	uint32_t n)
+{
+	uint64_t nh_arr[RTE_FIB6_MAXDEPTH];
+	int ret;
+	uint32_t i = 0;
+
+	ret = rte_fib6_lookup_bulk(fib, ip_arr, nh_arr, RTE_FIB6_MAXDEPTH);
+	RTE_TEST_ASSERT(ret == 0, "Failed to lookup\n");
+
+	for (; i <= RTE_FIB6_MAXDEPTH - n; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == n,
+			"Failed to get proper nexthop\n");
+
+	for (; i < RTE_FIB6_MAXDEPTH; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == --n,
+			"Failed to get proper nexthop\n");
+
+	ret = rte_fib6_lookup_bulk(fib, ip_missing, nh_arr, 1);
+	RTE_TEST_ASSERT((ret == 0) && (nh_arr[0] == def_nh),
+		"Failed to get proper nexthop\n");
+
+	return TEST_SUCCESS;
+}
+
+static int
+lookup_and_check_desc(struct rte_fib6 *fib,
+	uint8_t ip_arr[RTE_FIB6_MAXDEPTH][RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t ip_missing[][RTE_FIB6_IPV6_ADDR_SIZE], uint64_t def_nh,
+	uint32_t n)
+{
+	uint64_t nh_arr[RTE_FIB6_MAXDEPTH];
+	int ret;
+	uint32_t i = 0;
+
+	ret = rte_fib6_lookup_bulk(fib, ip_arr, nh_arr, RTE_FIB6_MAXDEPTH);
+	RTE_TEST_ASSERT(ret == 0, "Failed to lookup\n");
+
+	for (; i < n; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == RTE_FIB6_MAXDEPTH - i,
+			"Failed to get proper nexthop\n");
+
+	for (; i < RTE_FIB6_MAXDEPTH; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == def_nh,
+			"Failed to get proper nexthop\n");
+
+	ret = rte_fib6_lookup_bulk(fib, ip_missing, nh_arr, 1);
+	RTE_TEST_ASSERT((ret == 0) && (nh_arr[0] == def_nh),
+		"Failed to get proper nexthop\n");
+
+	return TEST_SUCCESS;
+}
+
+static int
+check_fib(struct rte_fib6 *fib)
+{
+	uint64_t def_nh = 100;
+	uint8_t ip_arr[RTE_FIB6_MAXDEPTH][RTE_FIB6_IPV6_ADDR_SIZE];
+	uint8_t ip_add[RTE_FIB6_IPV6_ADDR_SIZE] = {0};
+	uint8_t ip_missing[1][RTE_FIB6_IPV6_ADDR_SIZE] = { {255} };
+	uint32_t i, j;
+	int ret;
+
+	ip_add[0] = 128;
+	ip_missing[0][0] = 127;
+	for (i = 0; i < RTE_FIB6_MAXDEPTH; i++) {
+		for (j = 0; j < RTE_FIB6_IPV6_ADDR_SIZE; j++) {
+			ip_arr[i][j] = ip_add[j] |
+				~get_msk_part(RTE_FIB6_MAXDEPTH - i, j);
+		}
+	}
+
+	ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh, 0);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+
+	for (i = 1; i <= RTE_FIB6_MAXDEPTH; i++) {
+		ret = rte_fib6_add(fib, ip_add, i, i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to add a route\n");
+		ret = lookup_and_check_asc(fib, ip_arr, ip_missing, def_nh, i);
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+			"Lookup and check fails\n");
+	}
+
+	for (i = RTE_FIB6_MAXDEPTH; i > 1; i--) {
+		ret = rte_fib6_delete(fib, ip_add, i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
+		ret = lookup_and_check_asc(fib, ip_arr, ip_missing,
+			def_nh, i - 1);
+
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+			"Lookup and check fails\n");
+	}
+	ret = rte_fib6_delete(fib, ip_add, i);
+	RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
+	ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh, 0);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Lookup and check fails\n");
+
+	for (i = 0; i < RTE_FIB6_MAXDEPTH; i++) {
+		ret = rte_fib6_add(fib, ip_add, RTE_FIB6_MAXDEPTH - i,
+			RTE_FIB6_MAXDEPTH - i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to add a route\n");
+		ret = lookup_and_check_desc(fib, ip_arr, ip_missing,
+			def_nh, i + 1);
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+			"Lookup and check fails\n");
+	}
+
+	for (i = 1; i <= RTE_FIB6_MAXDEPTH; i++) {
+		ret = rte_fib6_delete(fib, ip_add, i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
+		ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh,
+			RTE_FIB6_MAXDEPTH - i);
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+			"Lookup and check fails\n");
+	}
+
+	return TEST_SUCCESS;
+}
+
+int32_t
+test_lookup(void)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_fib6_conf config;
+	uint64_t def_nh = 100;
+	int ret;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = def_nh;
+	config.type = RTE_FIB6_DUMMY;
+
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for DUMMY type\n");
+	rte_fib6_free(fib);
+
+	config.type = RTE_FIB6_TRIE;
+
+	config.trie.nh_sz = RTE_FIB6_TRIE_2B;
+	config.trie.num_tbl8 = 32767;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for TRIE_2B type\n");
+	rte_fib6_free(fib);
+
+	config.trie.nh_sz = RTE_FIB6_TRIE_4B;
+	config.trie.num_tbl8 = 100000;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for TRIE_4B type\n");
+	rte_fib6_free(fib);
+
+	config.trie.nh_sz = RTE_FIB6_TRIE_8B;
+	config.trie.num_tbl8 = 100000;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for TRIE_8B type\n");
+	rte_fib6_free(fib);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite fib6_tests = {
+	.suite_name = "fib6 autotest",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+	TEST_CASE(test_create_invalid),
+	TEST_CASE(test_multiple_create),
+	TEST_CASE(test_free_null),
+	TEST_CASE(test_add_del_invalid),
+	TEST_CASE(test_get_invalid),
+	TEST_CASE(test_lookup),
+	TEST_CASES_END()
+	}
+};
+
+/*
+ * Do all unit tests.
+ */
+static int
+test_fib6(void)
+{
+	return unit_test_suite_runner(&fib6_tests);
+}
+
+REGISTER_TEST_COMMAND(fib6_autotest, test_fib6);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 11/12] test/fib: add FIB library performance autotests
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (16 preceding siblings ...)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 10/12] test/fib: add ipv6 support for FIB autotests Vladimir Medvedkin
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 12/12] test/fib: add FIB library ipv6 " Vladimir Medvedkin
  18 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

Performance tests for the new FIB library.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |   6 +
 app/test/meson.build      |   2 +
 app/test/test_fib_perf.c  | 411 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 420 insertions(+)
 create mode 100644 app/test/test_fib_perf.c

diff --git a/app/test/Makefile b/app/test/Makefile
index c230dbf..3a3f920 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -128,6 +128,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib.c
 SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib6.c
 SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib.c
 SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib6.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib_perf.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 1244ec4..7668203 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -707,6 +707,12 @@
         "Report":  None,
     },
     {
+        "Name":    "FIB perf autotest",
+        "Command": "fib_perf_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
          "Name":    "Efd perf autotest",
          "Command": "efd_perf_autotest",
          "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index fb3595b..e95e7cb 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -48,6 +48,7 @@ test_sources = files('commands.c',
 	'test_external_mem.c',
 	'test_fbarray.c',
 	'test_fib.c',
+	'test_fib_perf.c',
 	'test_fib6.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
@@ -255,6 +256,7 @@ perf_test_names = [
         'reciprocal_division',
         'reciprocal_division_perf',
         'lpm_perf_autotest',
+        'fib_perf_autotest',
         'red_all',
         'barrier_autotest',
         'hash_multiwriter_autotest',
diff --git a/app/test/test_fib_perf.c b/app/test/test_fib_perf.c
new file mode 100644
index 0000000..573087c
--- /dev/null
+++ b/app/test/test_fib_perf.c
@@ -0,0 +1,411 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <rte_cycles.h>
+#include <rte_random.h>
+#include <rte_branch_prediction.h>
+#include <rte_ip.h>
+#include <rte_fib.h>
+
+#include "test.h"
+#include "test_xmmt_ops.h"
+
+#define TEST_FIB_ASSERT(cond) do {				\
+	if (!(cond)) {						\
+		printf("Error at line %d:\n", __LINE__);	\
+		return -1;					\
+	}                                                       \
+} while (0)
+
+#define ITERATIONS (1 << 10)
+#define BATCH_SIZE (1 << 12)
+#define BULK_SIZE 32
+
+#define MAX_RULE_NUM (1200000)
+
+struct route_rule {
+	uint32_t ip;
+	uint8_t depth;
+};
+
+struct route_rule large_route_table[MAX_RULE_NUM];
+
+static uint32_t num_route_entries;
+#define NUM_ROUTE_ENTRIES num_route_entries
+
+enum {
+	IP_CLASS_A,
+	IP_CLASS_B,
+	IP_CLASS_C
+};
+#define RTE_FIB_MAX_DEPTH	32
+/* struct route_rule_count defines the total number of rules in following a/b/c
+ * each item in a[]/b[]/c[] is the number of common IP address class A/B/C, not
+ * including the ones for private local network.
+ */
+struct route_rule_count {
+	uint32_t a[RTE_FIB_MAX_DEPTH];
+	uint32_t b[RTE_FIB_MAX_DEPTH];
+	uint32_t c[RTE_FIB_MAX_DEPTH];
+};
+
+/* All following numbers of each depth of each common IP class are just
+ * got from previous large constant table in app/test/test_lpm_routes.h .
+ * In order to match similar performance, they keep same depth and IP
+ * address coverage as previous constant table. These numbers don't
+ * include any private local IP address. As previous large const rule
+ * table was just dumped from a real router, there are no any IP address
+ * in class C or D.
+ */
+static struct route_rule_count rule_count = {
+	.a = { /* IP class A in which the most significant bit is 0 */
+		    0, /* depth =  1 */
+		    0, /* depth =  2 */
+		    1, /* depth =  3 */
+		    0, /* depth =  4 */
+		    2, /* depth =  5 */
+		    1, /* depth =  6 */
+		    3, /* depth =  7 */
+		  185, /* depth =  8 */
+		   26, /* depth =  9 */
+		   16, /* depth = 10 */
+		   39, /* depth = 11 */
+		  144, /* depth = 12 */
+		  233, /* depth = 13 */
+		  528, /* depth = 14 */
+		  866, /* depth = 15 */
+		 3856, /* depth = 16 */
+		 3268, /* depth = 17 */
+		 5662, /* depth = 18 */
+		17301, /* depth = 19 */
+		22226, /* depth = 20 */
+		11147, /* depth = 21 */
+		16746, /* depth = 22 */
+		17120, /* depth = 23 */
+		77578, /* depth = 24 */
+		  401, /* depth = 25 */
+		  656, /* depth = 26 */
+		 1107, /* depth = 27 */
+		 1121, /* depth = 28 */
+		 2316, /* depth = 29 */
+		  717, /* depth = 30 */
+		   10, /* depth = 31 */
+		   66  /* depth = 32 */
+	},
+	.b = { /* IP class A in which the most 2 significant bits are 10 */
+		    0, /* depth =  1 */
+		    0, /* depth =  2 */
+		    0, /* depth =  3 */
+		    0, /* depth =  4 */
+		    1, /* depth =  5 */
+		    1, /* depth =  6 */
+		    1, /* depth =  7 */
+		    3, /* depth =  8 */
+		    3, /* depth =  9 */
+		   30, /* depth = 10 */
+		   25, /* depth = 11 */
+		  168, /* depth = 12 */
+		  305, /* depth = 13 */
+		  569, /* depth = 14 */
+		 1129, /* depth = 15 */
+		50800, /* depth = 16 */
+		 1645, /* depth = 17 */
+		 1820, /* depth = 18 */
+		 3506, /* depth = 19 */
+		 3258, /* depth = 20 */
+		 3424, /* depth = 21 */
+		 4971, /* depth = 22 */
+		 6885, /* depth = 23 */
+		39771, /* depth = 24 */
+		  424, /* depth = 25 */
+		  170, /* depth = 26 */
+		  433, /* depth = 27 */
+		   92, /* depth = 28 */
+		  366, /* depth = 29 */
+		  377, /* depth = 30 */
+		    2, /* depth = 31 */
+		  200  /* depth = 32 */
+	},
+	.c = { /* IP class A in which the most 3 significant bits are 110 */
+		     0, /* depth =  1 */
+		     0, /* depth =  2 */
+		     0, /* depth =  3 */
+		     0, /* depth =  4 */
+		     0, /* depth =  5 */
+		     0, /* depth =  6 */
+		     0, /* depth =  7 */
+		    12, /* depth =  8 */
+		     8, /* depth =  9 */
+		     9, /* depth = 10 */
+		    33, /* depth = 11 */
+		    69, /* depth = 12 */
+		   237, /* depth = 13 */
+		  1007, /* depth = 14 */
+		  1717, /* depth = 15 */
+		 14663, /* depth = 16 */
+		  8070, /* depth = 17 */
+		 16185, /* depth = 18 */
+		 48261, /* depth = 19 */
+		 36870, /* depth = 20 */
+		 33960, /* depth = 21 */
+		 50638, /* depth = 22 */
+		 61422, /* depth = 23 */
+		466549, /* depth = 24 */
+		  1829, /* depth = 25 */
+		  4824, /* depth = 26 */
+		  4927, /* depth = 27 */
+		  5914, /* depth = 28 */
+		 10254, /* depth = 29 */
+		  4905, /* depth = 30 */
+		     1, /* depth = 31 */
+		   716  /* depth = 32 */
+	}
+};
+
+static void generate_random_rule_prefix(uint32_t ip_class, uint8_t depth)
+{
+/* IP address class A, the most significant bit is 0 */
+#define IP_HEAD_MASK_A			0x00000000
+#define IP_HEAD_BIT_NUM_A		1
+
+/* IP address class B, the most significant 2 bits are 10 */
+#define IP_HEAD_MASK_B			0x80000000
+#define IP_HEAD_BIT_NUM_B		2
+
+/* IP address class C, the most significant 3 bits are 110 */
+#define IP_HEAD_MASK_C			0xC0000000
+#define IP_HEAD_BIT_NUM_C		3
+
+	uint32_t class_depth;
+	uint32_t range;
+	uint32_t mask;
+	uint32_t step;
+	uint32_t start;
+	uint32_t fixed_bit_num;
+	uint32_t ip_head_mask;
+	uint32_t rule_num;
+	uint32_t k;
+	struct route_rule *ptr_rule;
+
+	if (ip_class == IP_CLASS_A) {        /* IP Address class A */
+		fixed_bit_num = IP_HEAD_BIT_NUM_A;
+		ip_head_mask = IP_HEAD_MASK_A;
+		rule_num = rule_count.a[depth - 1];
+	} else if (ip_class == IP_CLASS_B) { /* IP Address class B */
+		fixed_bit_num = IP_HEAD_BIT_NUM_B;
+		ip_head_mask = IP_HEAD_MASK_B;
+		rule_num = rule_count.b[depth - 1];
+	} else {                             /* IP Address class C */
+		fixed_bit_num = IP_HEAD_BIT_NUM_C;
+		ip_head_mask = IP_HEAD_MASK_C;
+		rule_num = rule_count.c[depth - 1];
+	}
+
+	if (rule_num == 0)
+		return;
+
+	/* the number of rest bits which don't include the most significant
+	 * fixed bits for this IP address class
+	 */
+	class_depth = depth - fixed_bit_num;
+
+	/* range is the maximum number of rules for this depth and
+	 * this IP address class
+	 */
+	range = 1 << class_depth;
+
+	/* only mask the most depth significant generated bits
+	 * except fixed bits for IP address class
+	 */
+	mask = range - 1;
+
+	/* Widen coverage of IP address in generated rules */
+	if (range <= rule_num)
+		step = 1;
+	else
+		step = round((double)range / rule_num);
+
+	/* Only generate rest bits except the most significant
+	 * fixed bits for IP address class
+	 */
+	start = lrand48() & mask;
+	ptr_rule = &large_route_table[num_route_entries];
+	for (k = 0; k < rule_num; k++) {
+		ptr_rule->ip = (start << (RTE_FIB_MAX_DEPTH - depth))
+			| ip_head_mask;
+		ptr_rule->depth = depth;
+		ptr_rule++;
+		start = (start + step) & mask;
+	}
+	num_route_entries += rule_num;
+}
+
+static void insert_rule_in_random_pos(uint32_t ip, uint8_t depth)
+{
+	uint32_t pos;
+	int try_count = 0;
+	struct route_rule tmp;
+
+	do {
+		pos = lrand48();
+		try_count++;
+	} while ((try_count < 10) && (pos > num_route_entries));
+
+	if ((pos > num_route_entries) || (pos >= MAX_RULE_NUM))
+		pos = num_route_entries >> 1;
+
+	tmp = large_route_table[pos];
+	large_route_table[pos].ip = ip;
+	large_route_table[pos].depth = depth;
+	if (num_route_entries < MAX_RULE_NUM)
+		large_route_table[num_route_entries++] = tmp;
+}
+
+static void generate_large_route_rule_table(void)
+{
+	uint32_t ip_class;
+	uint8_t  depth;
+
+	num_route_entries = 0;
+	memset(large_route_table, 0, sizeof(large_route_table));
+
+	for (ip_class = IP_CLASS_A; ip_class <= IP_CLASS_C; ip_class++) {
+		for (depth = 1; depth <= RTE_FIB_MAX_DEPTH; depth++)
+			generate_random_rule_prefix(ip_class, depth);
+	}
+
+	/* Add following rules to keep same as previous large constant table,
+	 * they are 4 rules with private local IP address and 1 all-zeros prefix
+	 * with depth = 8.
+	 */
+	insert_rule_in_random_pos(RTE_IPV4(0, 0, 0, 0), 8);
+	insert_rule_in_random_pos(RTE_IPV4(10, 2, 23, 147), 32);
+	insert_rule_in_random_pos(RTE_IPV4(192, 168, 100, 10), 24);
+	insert_rule_in_random_pos(RTE_IPV4(192, 168, 25, 100), 24);
+	insert_rule_in_random_pos(RTE_IPV4(192, 168, 129, 124), 32);
+}
+
+static void
+print_route_distribution(const struct route_rule *table, uint32_t n)
+{
+	unsigned int i, j;
+
+	printf("Route distribution per prefix width:\n");
+	printf("DEPTH    QUANTITY (PERCENT)\n");
+	printf("---------------------------\n");
+
+	/* Count depths. */
+	for (i = 1; i <= 32; i++) {
+		unsigned int depth_counter = 0;
+		double percent_hits;
+
+		for (j = 0; j < n; j++)
+			if (table[j].depth == (uint8_t) i)
+				depth_counter++;
+
+		percent_hits = ((double)depth_counter)/((double)n) * 100;
+		printf("%.2u%15u (%.2f)\n", i, depth_counter, percent_hits);
+	}
+	printf("\n");
+}
+
+static int
+test_fib_perf(void)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_fib_conf config;
+
+	config.max_routes = 2000000;
+	config.type = RTE_FIB_DIR24_8;
+	config.default_nh = 0;
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_4B;
+	config.dir24_8.num_tbl8 = 65535;
+	uint64_t begin, total_time;
+	unsigned int i, j;
+	uint32_t next_hop_add = 0xAA;
+	int status = 0;
+	int64_t count = 0;
+
+	rte_srand(rte_rdtsc());
+
+	generate_large_route_rule_table();
+
+	printf("No. routes = %u\n", (unsigned int) NUM_ROUTE_ENTRIES);
+
+	print_route_distribution(large_route_table,
+		(uint32_t) NUM_ROUTE_ENTRIES);
+
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_FIB_ASSERT(fib != NULL);
+
+	/* Measue add. */
+	begin = rte_rdtsc();
+
+	for (i = 0; i < NUM_ROUTE_ENTRIES; i++) {
+		if (rte_fib_add(fib, large_route_table[i].ip,
+				large_route_table[i].depth, next_hop_add) == 0)
+			status++;
+	}
+	/* End Timer. */
+	total_time = rte_rdtsc() - begin;
+
+	printf("Unique added entries = %d\n", status);
+
+	printf("Average FIB Add: %g cycles\n",
+			(double)total_time / NUM_ROUTE_ENTRIES);
+
+	/* Measure bulk Lookup */
+	total_time = 0;
+	count = 0;
+	for (i = 0; i < ITERATIONS; i++) {
+		static uint32_t ip_batch[BATCH_SIZE];
+		uint64_t next_hops[BULK_SIZE];
+
+		/* Create array of random IP addresses */
+		for (j = 0; j < BATCH_SIZE; j++)
+			ip_batch[j] = rte_rand();
+
+		/* Lookup per batch */
+		begin = rte_rdtsc();
+		for (j = 0; j < BATCH_SIZE; j += BULK_SIZE) {
+			uint32_t k;
+			rte_fib_lookup_bulk(fib, &ip_batch[j], next_hops,
+				BULK_SIZE);
+			for (k = 0; k < BULK_SIZE; k++)
+				if (unlikely(!(next_hops[k] != 0)))
+					count++;
+		}
+
+		total_time += rte_rdtsc() - begin;
+	}
+	printf("BULK FIB Lookup: %.1f cycles (fails = %.1f%%)\n",
+			(double)total_time / ((double)ITERATIONS * BATCH_SIZE),
+			(count * 100.0) / (double)(ITERATIONS * BATCH_SIZE));
+
+	/* Delete */
+	status = 0;
+	begin = rte_rdtsc();
+	for (i = 0; i < NUM_ROUTE_ENTRIES; i++) {
+		/* rte_lpm_delete(lpm, ip, depth) */
+		status += rte_fib_delete(fib, large_route_table[i].ip,
+				large_route_table[i].depth);
+	}
+
+	total_time += rte_rdtsc() - begin;
+
+	printf("Average FIB Delete: %g cycles\n",
+			(double)total_time / NUM_ROUTE_ENTRIES);
+
+	rte_fib_free(fib);
+
+	return 0;
+}
+
+REGISTER_TEST_COMMAND(fib_perf_autotest, test_fib_perf);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v5 12/12] test/fib: add FIB library ipv6 performance autotests
  2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
                   ` (17 preceding siblings ...)
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 11/12] test/fib: add FIB library performance autotests Vladimir Medvedkin
@ 2019-09-11 17:09 ` Vladimir Medvedkin
  18 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-09-11 17:09 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson

Performance ipv6 tests for the FIB library.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |   6 ++
 app/test/meson.build      |   2 +
 app/test/test_fib6_perf.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 166 insertions(+)
 create mode 100644 app/test/test_fib6_perf.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 3a3f920..f792858 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -129,6 +129,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib6.c
 SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib.c
 SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib6.c
 SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib_perf.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib6_perf.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 7668203..692b55a 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -713,6 +713,12 @@
         "Report":  None,
     },
     {
+        "Name":    "FIB6 perf autotest",
+        "Command": "fib6_perf_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
          "Name":    "Efd perf autotest",
          "Command": "efd_perf_autotest",
          "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index e95e7cb..5942997 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -50,6 +50,7 @@ test_sources = files('commands.c',
 	'test_fib.c',
 	'test_fib_perf.c',
 	'test_fib6.c',
+	'test_fib6_perf.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
 	'test_hash.c',
@@ -266,6 +267,7 @@ perf_test_names = [
         'member_perf_autotest',
         'efd_perf_autotest',
         'lpm6_perf_autotest',
+        'fib6_perf_autotest',
         'rcu_qsbr_perf_autotest',
         'red_perf',
         'distributor_perf_autotest',
diff --git a/app/test/test_fib6_perf.c b/app/test/test_fib6_perf.c
new file mode 100644
index 0000000..56c799b
--- /dev/null
+++ b/app/test/test_fib6_perf.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2014 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rte_cycles.h>
+#include <rte_random.h>
+#include <rte_memory.h>
+#include <rte_fib6.h>
+
+#include "test.h"
+#include "test_lpm6_data.h"
+
+#define TEST_FIB_ASSERT(cond) do {				\
+	if (!(cond)) {						\
+		printf("Error at line %d:\n", __LINE__);	\
+		return -1;					\
+	}							\
+} while (0)
+
+#define ITERATIONS (1 << 10)
+#define BATCH_SIZE 100000
+#define NUMBER_TBL8S                                           (1 << 16)
+
+static void
+print_route_distribution(const struct rules_tbl_entry *table, uint32_t n)
+{
+	unsigned int i, j;
+
+	printf("Route distribution per prefix width:\n");
+	printf("DEPTH    QUANTITY (PERCENT)\n");
+	printf("---------------------------\n");
+
+	/* Count depths. */
+	for (i = 1; i <= 128; i++) {
+		unsigned int depth_counter = 0;
+		double percent_hits;
+
+		for (j = 0; j < n; j++)
+			if (table[j].depth == (uint8_t) i)
+				depth_counter++;
+
+		percent_hits = ((double)depth_counter)/((double)n) * 100;
+		printf("%.2u%15u (%.2f)\n", i, depth_counter, percent_hits);
+	}
+	printf("\n");
+}
+
+static inline uint8_t
+bits_in_nh(uint8_t nh_sz)
+{
+	return 8 * (1 << nh_sz);
+}
+
+static inline uint64_t
+get_max_nh(uint8_t nh_sz)
+{
+	return ((1ULL << (bits_in_nh(nh_sz) - 1)) - 1);
+}
+
+static int
+test_fib6_perf(void)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_fib6_conf conf;
+	uint64_t begin, total_time;
+	unsigned int i, j;
+	uint64_t next_hop_add;
+	int status = 0;
+	int64_t count = 0;
+	uint8_t ip_batch[NUM_IPS_ENTRIES][16];
+	uint64_t next_hops[NUM_IPS_ENTRIES];
+
+	conf.type = RTE_FIB6_TRIE;
+	conf.default_nh = 0;
+	conf.max_routes = 1000000;
+	conf.trie.nh_sz = RTE_FIB6_TRIE_4B;
+	conf.trie.num_tbl8 = RTE_MIN(get_max_nh(conf.trie.nh_sz), 1000000U);
+
+	rte_srand(rte_rdtsc());
+
+	printf("No. routes = %u\n", (unsigned int) NUM_ROUTE_ENTRIES);
+
+	print_route_distribution(large_route_table,
+		(uint32_t)NUM_ROUTE_ENTRIES);
+
+	/* Only generate IPv6 address of each item in large IPS table,
+	 * here next_hop is not needed.
+	 */
+	generate_large_ips_table(0);
+
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &conf);
+	TEST_FIB_ASSERT(fib != NULL);
+
+	/* Measure add. */
+	begin = rte_rdtsc();
+
+	for (i = 0; i < NUM_ROUTE_ENTRIES; i++) {
+		next_hop_add = (i & ((1 << 14) - 1)) + 1;
+		if (rte_fib6_add(fib, large_route_table[i].ip,
+				large_route_table[i].depth, next_hop_add) == 0)
+			status++;
+	}
+	/* End Timer. */
+	total_time = rte_rdtsc() - begin;
+
+	printf("Unique added entries = %d\n", status);
+	printf("Average FIB Add: %g cycles\n",
+			(double)total_time / NUM_ROUTE_ENTRIES);
+
+	/* Measure bulk Lookup */
+	total_time = 0;
+	count = 0;
+
+	for (i = 0; i < NUM_IPS_ENTRIES; i++)
+		memcpy(ip_batch[i], large_ips_table[i].ip, 16);
+
+	for (i = 0; i < ITERATIONS; i++) {
+
+		/* Lookup per batch */
+		begin = rte_rdtsc();
+		rte_fib6_lookup_bulk(fib, ip_batch, next_hops, NUM_IPS_ENTRIES);
+		total_time += rte_rdtsc() - begin;
+
+		for (j = 0; j < NUM_IPS_ENTRIES; j++)
+			if (next_hops[j] == 0)
+				count++;
+	}
+	printf("BULK FIB Lookup: %.1f cycles (fails = %.1f%%)\n",
+			(double)total_time / ((double)ITERATIONS * BATCH_SIZE),
+			(count * 100.0) / (double)(ITERATIONS * BATCH_SIZE));
+
+	/* Delete */
+	status = 0;
+	begin = rte_rdtsc();
+
+	for (i = 0; i < NUM_ROUTE_ENTRIES; i++) {
+		/* rte_fib_delete(fib, ip, depth) */
+		status += rte_fib6_delete(fib, large_route_table[i].ip,
+				large_route_table[i].depth);
+	}
+
+	total_time = rte_rdtsc() - begin;
+
+	printf("Average FIB Delete: %g cycles\n",
+			(double)total_time / NUM_ROUTE_ENTRIES);
+
+	rte_fib6_free(fib);
+
+	return 0;
+}
+
+REGISTER_TEST_COMMAND(fib6_perf_autotest, test_fib6_perf);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
@ 2019-09-12  7:37   ` Morten Brørup
  2019-09-12  9:47     ` Medvedkin, Vladimir
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 " Vladimir Medvedkin
                     ` (12 subsequent siblings)
  13 siblings, 1 reply; 61+ messages in thread
From: Morten Brørup @ 2019-09-12  7:37 UTC (permalink / raw)
  To: Vladimir Medvedkin; +Cc: bruce.richardson, dev

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Vladimir Medvedkin
> 
> This is heavily reworked version of previous RIB library series:
> https://mails.dpdk.org/archives/dev/2018-April/099492.html
> 
> Current lpm implementation while provides really good lookup
> performance has number of problems.
> One of them is very low speed for control plane operations
> such as add or delete a route.
> Another disadvantage is fixed number of bits for userdata
> (24 for v4 and 21 for v6)
> Also it is hard to introduce changes in existing LPM code or add new
> algorithms without breaking ABI.
> 
> This patch series tries to solve this problems by:
> Introduce two new libraries - RIB and FIB.
> RIB that is Routing Information Base.
> It implements a control plane struct containing routes in a tree and
> provides fast add/del operations for routes. Also it allows to perform
> fast subtree traversals (i.e. retrieve existing subroutes for a given
> prefix). This structure will be used as a control plane helper
> structure
> for FIB implementation.
> Also it might be used standalone in other different places such as
> bitmaps for example.
> 

Great!


> Second library is FIB that is Forwarding Information Base. It
> represents
> dataplane related struct and algorithms for longest prefix match.
> Internally it consists of two parts - RIB (control plane ops) and
> implementation for the dataplane tasks.
> Initial version provides two implementations for both ipv4 and ipv6:
> dummy (uses RIB as a dataplane) and DIR24_8 (same as current LPM)
> Due to proposed design it allows to extend FIB with new algorithms in
> future
> (for example DXR, poptrie, etc).

The feedback following here is meant as a comment, not an objection. Feel free to ignore!

This FIB library is designed for IP based forwarding only.

How about forwarding based on other criteria?
E.g. the FIB in a standard Ethernet switch is based on VLAN+MAC.

Such a FIB would probably require a different library, based on a hash structure, and would also require a compare-and-set function callable from the data plane in order to provide wire speed learning.

So I suggest that the documentation highlights that this FIB library is for IP based forwarding. Optionally also reconsider the name of the library and its functions, structures etc..

> 
> From our measurements we saw 10x speedup for control plane operations
> comparing with current LPM library (depending on prefix length
> distribution)
> 
> ToDo:
>  - introduce new performance measurement app.
>  - add documentation.
>  - add support into existing examples (l3fwd)
> 


Med venlig hilsen / kind regards
- Morten Brørup

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries
  2019-09-12  7:37   ` Morten Brørup
@ 2019-09-12  9:47     ` Medvedkin, Vladimir
  2019-09-12 12:00       ` Morten Brørup
  0 siblings, 1 reply; 61+ messages in thread
From: Medvedkin, Vladimir @ 2019-09-12  9:47 UTC (permalink / raw)
  To: Morten Brørup; +Cc: bruce.richardson, dev

Hi Brørup,

On 12/09/2019 08:37, Morten Brørup wrote:
>> -----Original Message-----
>> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Vladimir Medvedkin
>>
>> This is heavily reworked version of previous RIB library series:
>> https://mails.dpdk.org/archives/dev/2018-April/099492.html
>>
>> Current lpm implementation while provides really good lookup
>> performance has number of problems.
>> One of them is very low speed for control plane operations
>> such as add or delete a route.
>> Another disadvantage is fixed number of bits for userdata
>> (24 for v4 and 21 for v6)
>> Also it is hard to introduce changes in existing LPM code or add new
>> algorithms without breaking ABI.
>>
>> This patch series tries to solve this problems by:
>> Introduce two new libraries - RIB and FIB.
>> RIB that is Routing Information Base.
>> It implements a control plane struct containing routes in a tree and
>> provides fast add/del operations for routes. Also it allows to perform
>> fast subtree traversals (i.e. retrieve existing subroutes for a given
>> prefix). This structure will be used as a control plane helper
>> structure
>> for FIB implementation.
>> Also it might be used standalone in other different places such as
>> bitmaps for example.
>>
> Great!
>
>
>> Second library is FIB that is Forwarding Information Base. It
>> represents
>> dataplane related struct and algorithms for longest prefix match.
>> Internally it consists of two parts - RIB (control plane ops) and
>> implementation for the dataplane tasks.
>> Initial version provides two implementations for both ipv4 and ipv6:
>> dummy (uses RIB as a dataplane) and DIR24_8 (same as current LPM)
>> Due to proposed design it allows to extend FIB with new algorithms in
>> future
>> (for example DXR, poptrie, etc).
> The feedback following here is meant as a comment, not an objection. Feel free to ignore!
>
> This FIB library is designed for IP based forwarding only.
>
> How about forwarding based on other criteria?
> E.g. the FIB in a standard Ethernet switch is based on VLAN+MAC.
>
> Such a FIB would probably require a different library, based on a hash structure, and would also require a compare-and-set function callable from the data plane in order to provide wire speed learning.
>
> So I suggest that the documentation highlights that this FIB library is for IP based forwarding. Optionally also reconsider the name of the library and its functions, structures etc..

Thanks for the feedback.

Yes, at the moment FIB has only longest prefix match algorithms. 
However, it is possible to add different exact match algorithms for 
VLAN+MAC/MPLS/etc processing.

It is always hard to find proper name for library/function/variable, so 
if you think that fib name is not relevant feel free to suggest better :)

>
>>  From our measurements we saw 10x speedup for control plane operations
>> comparing with current LPM library (depending on prefix length
>> distribution)
>>
>> ToDo:
>>   - introduce new performance measurement app.
>>   - add documentation.
>>   - add support into existing examples (l3fwd)
>>
>
> Med venlig hilsen / kind regards
> - Morten Brørup

-- 
Regards,
Vladimir


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries
  2019-09-12  9:47     ` Medvedkin, Vladimir
@ 2019-09-12 12:00       ` Morten Brørup
  0 siblings, 0 replies; 61+ messages in thread
From: Morten Brørup @ 2019-09-12 12:00 UTC (permalink / raw)
  To: Medvedkin, Vladimir; +Cc: bruce.richardson, dev

Hi Vladimir,

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Medvedkin,
> Vladimir
> 
> Hi Brørup,
> 
> On 12/09/2019 08:37, Morten Brørup wrote:
> >> -----Original Message-----
> >> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Vladimir
> Medvedkin
> >>
> >> This is heavily reworked version of previous RIB library series:
> >> https://mails.dpdk.org/archives/dev/2018-April/099492.html
> >>

[snip]

> >> Second library is FIB that is Forwarding Information Base. It
> >> represents
> >> dataplane related struct and algorithms for longest prefix match.
> >> Internally it consists of two parts - RIB (control plane ops) and
> >> implementation for the dataplane tasks.
> >> Initial version provides two implementations for both ipv4 and ipv6:
> >> dummy (uses RIB as a dataplane) and DIR24_8 (same as current LPM)
> >> Due to proposed design it allows to extend FIB with new algorithms
> in
> >> future
> >> (for example DXR, poptrie, etc).
> > The feedback following here is meant as a comment, not an objection.
> Feel free to ignore!
> >
> > This FIB library is designed for IP based forwarding only.
> >
> > How about forwarding based on other criteria?
> > E.g. the FIB in a standard Ethernet switch is based on VLAN+MAC.
> >
> > Such a FIB would probably require a different library, based on a
> hash structure, and would also require a compare-and-set function
> callable from the data plane in order to provide wire speed learning.
> >
> > So I suggest that the documentation highlights that this FIB library
> is for IP based forwarding. Optionally also reconsider the name of the
> library and its functions, structures etc..
> 
> Thanks for the feedback.
> 
> Yes, at the moment FIB has only longest prefix match algorithms.
> However, it is possible to add different exact match algorithms for
> VLAN+MAC/MPLS/etc processing.
> 
> It is always hard to find proper name for library/function/variable, so
> if you think that fib name is not relevant feel free to suggest better
> :)
> 

You can use ip_/ipv4_/ipv6_ somewhere in the common prefix, like the ip_frag library, and unlike many other libraries. :-)

And again: Feel free to ignore!

> 
> --
> Regards,
> Vladimir
> 

Med venlig hilsen / kind regards
- Morten Brørup

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v5 09/12] test/fib: add FIB library autotests
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 09/12] test/fib: add FIB library autotests Vladimir Medvedkin
@ 2019-09-12 14:07   ` Aaron Conole
  2019-10-01 17:12     ` Medvedkin, Vladimir
  0 siblings, 1 reply; 61+ messages in thread
From: Aaron Conole @ 2019-09-12 14:07 UTC (permalink / raw)
  To: Vladimir Medvedkin; +Cc: dev, bruce.richardson

Vladimir Medvedkin <vladimir.medvedkin@intel.com> writes:

> Functional tests for the new FIB library.
>
> Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
> ---
>  app/test/Makefile         |   1 +
>  app/test/autotest_data.py |   6 +
>  app/test/meson.build      |   3 +
>  app/test/test_fib.c       | 397 ++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 407 insertions(+)
>  create mode 100644 app/test/test_fib.c
>

Thanks so much for adding tests with your library.

Please note that even after 30s, these tests don't complete:

  https://travis-ci.com/ovsrobot/dpdk/jobs/234132379

Can you restrict some of your lpm test vectors rather than looping
through all of combinations?  Or perhaps take the long poll tests and
move them into a perf test?

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v5 09/12] test/fib: add FIB library autotests
  2019-09-12 14:07   ` Aaron Conole
@ 2019-10-01 17:12     ` Medvedkin, Vladimir
  2019-10-24 15:55       ` Thomas Monjalon
  0 siblings, 1 reply; 61+ messages in thread
From: Medvedkin, Vladimir @ 2019-10-01 17:12 UTC (permalink / raw)
  To: Aaron Conole; +Cc: dev, bruce.richardson

Hi Aaron,

On 12/09/2019 15:07, Aaron Conole wrote:
> Vladimir Medvedkin <vladimir.medvedkin@intel.com> writes:
>
>> Functional tests for the new FIB library.
>>
>> Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
>> ---
>>   app/test/Makefile         |   1 +
>>   app/test/autotest_data.py |   6 +
>>   app/test/meson.build      |   3 +
>>   app/test/test_fib.c       | 397 ++++++++++++++++++++++++++++++++++++++++++++++
>>   4 files changed, 407 insertions(+)
>>   create mode 100644 app/test/test_fib.c
>>
> Thanks so much for adding tests with your library.
>
> Please note that even after 30s, these tests don't complete:
>
>    https://travis-ci.com/ovsrobot/dpdk/jobs/234132379
>
> Can you restrict some of your lpm test vectors rather than looping
> through all of combinations?  Or perhaps take the long poll tests and
> move them into a perf test?

Sorry for the late reply. I'll try to make it work faster :)

-- 
Regards,
Vladimir


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v5 09/12] test/fib: add FIB library autotests
  2019-10-01 17:12     ` Medvedkin, Vladimir
@ 2019-10-24 15:55       ` Thomas Monjalon
  0 siblings, 0 replies; 61+ messages in thread
From: Thomas Monjalon @ 2019-10-24 15:55 UTC (permalink / raw)
  To: Medvedkin, Vladimir; +Cc: dev, Aaron Conole, bruce.richardson

01/10/2019 19:12, Medvedkin, Vladimir:
> On 12/09/2019 15:07, Aaron Conole wrote:
> > Vladimir Medvedkin <vladimir.medvedkin@intel.com> writes:
> >
> >> Functional tests for the new FIB library.
> >>
> >> Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
> >> ---
> >>   app/test/Makefile         |   1 +
> >>   app/test/autotest_data.py |   6 +
> >>   app/test/meson.build      |   3 +
> >>   app/test/test_fib.c       | 397 ++++++++++++++++++++++++++++++++++++++++++++++
> >>   4 files changed, 407 insertions(+)
> >>   create mode 100644 app/test/test_fib.c
> >>
> > Thanks so much for adding tests with your library.
> >
> > Please note that even after 30s, these tests don't complete:
> >
> >    https://travis-ci.com/ovsrobot/dpdk/jobs/234132379
> >
> > Can you restrict some of your lpm test vectors rather than looping
> > through all of combinations?  Or perhaps take the long poll tests and
> > move them into a perf test?
> 
> Sorry for the late reply. I'll try to make it work faster :)

Because of the test duration, this series is waiting for a v6.
I hope we could merge it in 19.11-rc2.



^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 00/12] lib: add RIB and FIB liraries
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
  2019-09-12  7:37   ` Morten Brørup
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  2019-11-05 23:14     ` Thomas Monjalon
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 01/12] rib: add RIB library Vladimir Medvedkin
                     ` (11 subsequent siblings)
  13 siblings, 1 reply; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

This is heavily reworked version of previous RIB library series:
https://mails.dpdk.org/archives/dev/2018-April/099492.html

Current lpm implementation while provides really good lookup
performance has number of problems.
One of them is very low speed for control plane operations
such as add or delete a route.
Another disadvantage is fixed number of bits for userdata
(24 for v4 and 21 for v6)
Also it is hard to introduce changes in existing LPM code or add new
algorithms without breaking ABI.

This patch series tries to solve this problems by:
Introduce two new libraries - RIB and FIB.
RIB that is Routing Information Base.
It implements a control plane struct containing routes in a tree and
provides fast add/del operations for routes. Also it allows to perform
fast subtree traversals (i.e. retrieve existing subroutes for a given
prefix). This structure will be used as a control plane helper structure
for FIB implementation.
Also it might be used standalone in other different places such as
bitmaps for example.

Second library is FIB that is Forwarding Information Base. It represents
dataplane related struct and algorithms for longest prefix match.
Internally it consists of two parts - RIB (control plane ops) and
implementation for the dataplane tasks.
Initial version provides two implementations for both ipv4 and ipv6:
dummy (uses RIB as a dataplane) and DIR24_8 (same as current LPM)
Due to proposed design it allows to extend FIB with new algorithms in future
(for example DXR, poptrie, etc).

From our measurements we saw 10x speedup for control plane operations
comparing with current LPM library (depending on prefix length distribution)

ToDo:
 - introduce new performance measurement app.
 - add documentation.
 - add support into existing examples (l3fwd)

v6:
Move long-running tests into a pref test section

Vladimir Medvedkin (12):
  rib: add RIB library
  test/rib: add RIB library autotests
  rib: add ipv6 support for RIB
  test/rib: add ipv6 support for RIB autotests
  fib: add FIB library
  fib: add FIB ipv6 support
  fib: add DIR24-8 dataplane algorithm
  fib: add dataplane algorithm for ipv6
  test/fib: add FIB library autotests
  test/fib: add ipv6 support for FIB autotests
  test/fib: add FIB library performance autotests
  test/fib: add FIB library ipv6 performance autotests

 app/test/Makefile                  |   7 +
 app/test/autotest_data.py          |  48 +++
 app/test/meson.build               |  16 +
 app/test/test_fib.c                | 414 ++++++++++++++++++++
 app/test/test_fib6.c               | 423 +++++++++++++++++++++
 app/test/test_fib6_perf.c          | 157 ++++++++
 app/test/test_fib_perf.c           | 411 ++++++++++++++++++++
 app/test/test_rib.c                | 351 +++++++++++++++++
 app/test/test_rib6.c               | 357 +++++++++++++++++
 config/common_base                 |  11 +
 doc/api/doxy-api.conf.in           |   2 +
 lib/Makefile                       |   4 +
 lib/librte_fib/Makefile            |  25 ++
 lib/librte_fib/dir24_8.c           | 737 +++++++++++++++++++++++++++++++++++
 lib/librte_fib/dir24_8.h           |  36 ++
 lib/librte_fib/meson.build         |   8 +
 lib/librte_fib/rte_fib.c           | 319 ++++++++++++++++
 lib/librte_fib/rte_fib.h           | 188 +++++++++
 lib/librte_fib/rte_fib6.c          | 322 ++++++++++++++++
 lib/librte_fib/rte_fib6.h          | 193 ++++++++++
 lib/librte_fib/rte_fib_version.map |  23 ++
 lib/librte_fib/trie.c              | 760 +++++++++++++++++++++++++++++++++++++
 lib/librte_fib/trie.h              |  37 ++
 lib/librte_rib/Makefile            |  25 ++
 lib/librte_rib/meson.build         |   8 +
 lib/librte_rib/rte_rib.c           | 532 ++++++++++++++++++++++++++
 lib/librte_rib/rte_rib.h           | 277 ++++++++++++++
 lib/librte_rib/rte_rib6.c          | 598 +++++++++++++++++++++++++++++
 lib/librte_rib/rte_rib6.h          | 334 ++++++++++++++++
 lib/librte_rib/rte_rib_version.map |  35 ++
 lib/meson.build                    |   4 +-
 mk/rte.app.mk                      |   2 +
 32 files changed, 6663 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_fib.c
 create mode 100644 app/test/test_fib6.c
 create mode 100644 app/test/test_fib6_perf.c
 create mode 100644 app/test/test_fib_perf.c
 create mode 100644 app/test/test_rib.c
 create mode 100644 app/test/test_rib6.c
 create mode 100644 lib/librte_fib/Makefile
 create mode 100644 lib/librte_fib/dir24_8.c
 create mode 100644 lib/librte_fib/dir24_8.h
 create mode 100644 lib/librte_fib/meson.build
 create mode 100644 lib/librte_fib/rte_fib.c
 create mode 100644 lib/librte_fib/rte_fib.h
 create mode 100644 lib/librte_fib/rte_fib6.c
 create mode 100644 lib/librte_fib/rte_fib6.h
 create mode 100644 lib/librte_fib/rte_fib_version.map
 create mode 100644 lib/librte_fib/trie.c
 create mode 100644 lib/librte_fib/trie.h
 create mode 100644 lib/librte_rib/Makefile
 create mode 100644 lib/librte_rib/meson.build
 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_rib6.c
 create mode 100644 lib/librte_rib/rte_rib6.h
 create mode 100644 lib/librte_rib/rte_rib_version.map

-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 01/12] rib: add RIB library
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
  2019-09-12  7:37   ` Morten Brørup
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 " Vladimir Medvedkin
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 02/12] test/rib: add RIB library autotests Vladimir Medvedkin
                     ` (10 subsequent siblings)
  13 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

Add RIB (Routing Information Base) library. This library
implements an IPv4 routing table optimized for control plane
operations. It implements a control plane struct containing routes
in a tree and provides fast add/del operations for routes.
Also it allows to perform fast subtree traversals
(i.e. retrieve existing subroutes for a giver prefix).
This structure will be used as a control plane helper structure
for FIB implementation. Also it might be used standalone in other
different places such as bitmaps for example.
Internal implementation is level compressed binary trie.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 config/common_base                 |   5 +
 doc/api/doxy-api.conf.in           |   1 +
 lib/Makefile                       |   2 +
 lib/librte_rib/Makefile            |  25 ++
 lib/librte_rib/meson.build         |   8 +
 lib/librte_rib/rte_rib.c           | 532 +++++++++++++++++++++++++++++++++++++
 lib/librte_rib/rte_rib.h           | 277 +++++++++++++++++++
 lib/librte_rib/rte_rib_version.map |  20 ++
 lib/meson.build                    |   2 +-
 mk/rte.app.mk                      |   1 +
 10 files changed, 872 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_rib/Makefile
 create mode 100644 lib/librte_rib/meson.build
 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 8ef75c2..5a9d4c3 100644
--- a/config/common_base
+++ b/config/common_base
@@ -894,6 +894,11 @@ CONFIG_RTE_LIBRTE_RCU=y
 CONFIG_RTE_LIBRTE_RCU_DEBUG=n
 
 #
+# Compile librte_rib
+#
+CONFIG_RTE_LIBRTE_RIB=y
+
+#
 # Compile librte_lpm
 #
 CONFIG_RTE_LIBRTE_LPM=y
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 908cee8..3ec012d 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -54,6 +54,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_rawdev \
                           @TOPDIR@/lib/librte_rcu \
                           @TOPDIR@/lib/librte_reorder \
+                          @TOPDIR@/lib/librte_rib \
                           @TOPDIR@/lib/librte_ring \
                           @TOPDIR@/lib/librte_sched \
                           @TOPDIR@/lib/librte_security \
diff --git a/lib/Makefile b/lib/Makefile
index 41c463d..aa5ee1e 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -51,6 +51,8 @@ DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash
 DEPDIRS-librte_hash := librte_eal librte_ring
 DIRS-$(CONFIG_RTE_LIBRTE_EFD) += librte_efd
 DEPDIRS-librte_efd := librte_eal librte_ring librte_hash
+DIRS-$(CONFIG_RTE_LIBRTE_RIB) += librte_rib
+DEPDIRS-librte_rib := librte_eal librte_mempool
 DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm
 DEPDIRS-librte_lpm := librte_eal librte_hash
 DIRS-$(CONFIG_RTE_LIBRTE_ACL) += librte_acl
diff --git a/lib/librte_rib/Makefile b/lib/librte_rib/Makefile
new file mode 100644
index 0000000..79f259a
--- /dev/null
+++ b/lib/librte_rib/Makefile
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+# Copyright(c) 2019 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_rib.a
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+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
+
+# install this header file
+SYMLINK-$(CONFIG_RTE_LIBRTE_RIB)-include := rte_rib.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_rib/meson.build b/lib/librte_rib/meson.build
new file mode 100644
index 0000000..e7b8920
--- /dev/null
+++ b/lib/librte_rib/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+# Copyright(c) 2019 Intel Corporation
+
+allow_experimental_apis = true
+sources = files('rte_rib.c')
+headers = files('rte_rib.h')
+deps += ['mempool']
diff --git a/lib/librte_rib/rte_rib.c b/lib/librte_rib/rte_rib.c
new file mode 100644
index 0000000..55d612d
--- /dev/null
+++ b/lib/librte_rib/rte_rib.c
@@ -0,0 +1,532 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_mempool.h>
+#include <rte_rwlock.h>
+#include <rte_string_fns.h>
+#include <rte_tailq.h>
+
+#include <rte_rib.h>
+
+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)
+
+#define RTE_RIB_VALID_NODE	1
+/* Maximum depth value possible for IPv4 RIB. */
+#define RIB_MAXDEPTH		32
+/* Maximum length of a RIB name. */
+#define RTE_RIB_NAMESIZE	64
+
+struct rte_rib_node {
+	struct rte_rib_node	*left;
+	struct rte_rib_node	*right;
+	struct rte_rib_node	*parent;
+	uint32_t	ip;
+	uint8_t		depth;
+	uint8_t		flag;
+	uint64_t	nh;
+	__extension__ uint64_t	ext[0];
+};
+
+struct rte_rib {
+	char		name[RTE_RIB_NAMESIZE];
+	struct rte_rib_node	*tree;
+	struct rte_mempool	*node_pool;
+	uint32_t		cur_nodes;
+	uint32_t		cur_routes;
+	uint32_t		max_nodes;
+};
+
+static inline bool
+is_valid_node(struct rte_rib_node *node)
+{
+	return (node->flag & RTE_RIB_VALID_NODE) == RTE_RIB_VALID_NODE;
+}
+
+static inline bool
+is_right_node(struct rte_rib_node *node)
+{
+	return node->parent->right == node;
+}
+
+/*
+ * Check if ip1 is covered by ip2/depth prefix
+ */
+static inline bool
+is_covered(uint32_t ip1, uint32_t ip2, uint8_t depth)
+{
+	return ((ip1 ^ ip2) & rte_rib_depth_to_mask(depth)) == 0;
+}
+
+static inline struct rte_rib_node *
+get_nxt_node(struct rte_rib_node *node, uint32_t ip)
+{
+	return (ip & (1 << (31 - node->depth))) ? node->right : node->left;
+}
+
+static struct rte_rib_node *
+node_alloc(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
+node_free(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_lookup(struct rte_rib *rib, uint32_t ip)
+{
+	struct rte_rib_node *cur, *prev = NULL;
+
+	if (rib == NULL) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	cur = rib->tree;
+	while ((cur != NULL) && is_covered(ip, cur->ip, cur->depth)) {
+		if (is_valid_node(cur))
+			prev = cur;
+		cur = get_nxt_node(cur, ip);
+	}
+	return prev;
+}
+
+struct rte_rib_node *
+rte_rib_lookup_parent(struct rte_rib_node *ent)
+{
+	struct rte_rib_node *tmp;
+
+	if (ent == NULL)
+		return NULL;
+	tmp = ent->parent;
+	while ((tmp != NULL) &&	!is_valid_node(tmp))
+		tmp = tmp->parent;
+	return tmp;
+}
+
+static struct rte_rib_node *
+__rib_lookup_exact(struct rte_rib *rib, uint32_t ip, uint8_t depth)
+{
+	struct rte_rib_node *cur;
+
+	cur = rib->tree;
+	while (cur != NULL) {
+		if ((cur->ip == ip) && (cur->depth == depth) &&
+				is_valid_node(cur))
+			return cur;
+		if ((cur->depth > depth) ||
+				!is_covered(ip, cur->ip, cur->depth))
+			break;
+		cur = get_nxt_node(cur, ip);
+	}
+	return NULL;
+}
+
+struct rte_rib_node *
+rte_rib_lookup_exact(struct rte_rib *rib, uint32_t ip, uint8_t depth)
+{
+	if ((rib == NULL) || (depth > RIB_MAXDEPTH)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+	ip &= rte_rib_depth_to_mask(depth);
+
+	return __rib_lookup_exact(rib, ip, depth);
+}
+
+/*
+ *  Traverses on subtree and retrieves more specific routes
+ *  for a given in args ip/depth prefix
+ *  last = NULL means the first invocation
+ */
+struct rte_rib_node *
+rte_rib_get_nxt(struct rte_rib *rib, uint32_t ip,
+	uint8_t depth, struct rte_rib_node *last, int flag)
+{
+	struct rte_rib_node *tmp, *prev = NULL;
+
+	if ((rib == NULL) || (depth > RIB_MAXDEPTH)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	if (last == NULL) {
+		tmp = rib->tree;
+		while ((tmp) && (tmp->depth < depth))
+			tmp = get_nxt_node(tmp, ip);
+	} else {
+		tmp = last;
+		while ((tmp->parent != NULL) && (is_right_node(tmp) ||
+				(tmp->parent->right == NULL))) {
+			tmp = tmp->parent;
+			if (is_valid_node(tmp) &&
+					(is_covered(tmp->ip, ip, depth) &&
+					(tmp->depth > depth)))
+				return tmp;
+		}
+		tmp = (tmp->parent) ? tmp->parent->right : NULL;
+	}
+	while (tmp) {
+		if (is_valid_node(tmp) &&
+				(is_covered(tmp->ip, ip, depth) &&
+				(tmp->depth > depth))) {
+			prev = tmp;
+			if (flag == RTE_RIB_GET_NXT_COVER)
+				return prev;
+		}
+		tmp = (tmp->left) ? tmp->left : tmp->right;
+	}
+	return prev;
+}
+
+void
+rte_rib_remove(struct rte_rib *rib, uint32_t ip, uint8_t depth)
+{
+	struct rte_rib_node *cur, *prev, *child;
+
+	cur = rte_rib_lookup_exact(rib, ip, depth);
+	if (cur == NULL)
+		return;
+
+	--rib->cur_routes;
+	cur->flag &= ~RTE_RIB_VALID_NODE;
+	while (!is_valid_node(cur)) {
+		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->tree = child;
+			node_free(rib, cur);
+			return;
+		}
+		if (cur->parent->left == cur)
+			cur->parent->left = child;
+		else
+			cur->parent->right = child;
+		prev = cur;
+		cur = cur->parent;
+		node_free(rib, prev);
+	}
+}
+
+struct rte_rib_node *
+rte_rib_insert(struct rte_rib *rib, uint32_t ip, uint8_t depth)
+{
+	struct rte_rib_node **tmp;
+	struct rte_rib_node *prev = NULL;
+	struct rte_rib_node *new_node = NULL;
+	struct rte_rib_node *common_node = NULL;
+	int d = 0;
+	uint32_t common_prefix;
+	uint8_t common_depth;
+
+	if ((rib == NULL) || (depth > RIB_MAXDEPTH)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	tmp = &rib->tree;
+	ip &= rte_rib_depth_to_mask(depth);
+	new_node = __rib_lookup_exact(rib, ip, depth);
+	if (new_node != NULL) {
+		rte_errno = EEXIST;
+		return NULL;
+	}
+
+	new_node = node_alloc(rib);
+	if (new_node == NULL) {
+		rte_errno = ENOMEM;
+		return NULL;
+	}
+	new_node->left = NULL;
+	new_node->right = NULL;
+	new_node->parent = NULL;
+	new_node->ip = ip;
+	new_node->depth = depth;
+	new_node->flag = RTE_RIB_VALID_NODE;
+
+	/* traverse down the tree to find matching node or closest matching */
+	while (1) {
+		/* insert as the last node in the branch */
+		if (*tmp == NULL) {
+			*tmp = new_node;
+			new_node->parent = prev;
+			++rib->cur_routes;
+			return *tmp;
+		}
+		/*
+		 * Intermediate node found.
+		 * Previous rte_rib_lookup_exact() returned NULL
+		 * but node with proper search criteria is found.
+		 * Validate intermediate node and return.
+		 */
+		if ((ip == (*tmp)->ip) && (depth == (*tmp)->depth)) {
+			node_free(rib, new_node);
+			(*tmp)->flag |= RTE_RIB_VALID_NODE;
+			++rib->cur_routes;
+			return *tmp;
+		}
+		d = (*tmp)->depth;
+		if ((d >= depth) || !is_covered(ip, (*tmp)->ip, d))
+			break;
+		prev = *tmp;
+		tmp = (ip & (1 << (31 - d))) ? &(*tmp)->right : &(*tmp)->left;
+	}
+	/* closest node found, new_node should be inserted in the middle */
+	common_depth = RTE_MIN(depth, (*tmp)->depth);
+	common_prefix = ip ^ (*tmp)->ip;
+	d = __builtin_clz(common_prefix);
+
+	common_depth = RTE_MIN(d, common_depth);
+	common_prefix = ip & rte_rib_depth_to_mask(common_depth);
+	if ((common_prefix == ip) && (common_depth == depth)) {
+		/* insert as a parent */
+		if ((*tmp)->ip & (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 {
+		/* create intermediate node */
+		common_node = node_alloc(rib);
+		if (common_node == NULL) {
+			node_free(rib, new_node);
+			rte_errno = ENOMEM;
+			return NULL;
+		}
+		common_node->ip = 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->ip & (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;
+	}
+	++rib->cur_routes;
+	return new_node;
+}
+
+int
+rte_rib_get_ip(struct rte_rib_node *node, uint32_t *ip)
+{
+	if ((node == NULL) || (ip == NULL)) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	*ip = node->ip;
+	return 0;
+}
+
+int
+rte_rib_get_depth(struct rte_rib_node *node, uint8_t *depth)
+{
+	if ((node == NULL) || (depth == NULL)) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	*depth = node->depth;
+	return 0;
+}
+
+void *
+rte_rib_get_ext(struct rte_rib_node *node)
+{
+	return (node == NULL) ? NULL : &node->ext[0];
+}
+
+int
+rte_rib_get_nh(struct rte_rib_node *node, uint64_t *nh)
+{
+	if ((node == NULL) || (nh == NULL)) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	*nh = node->nh;
+	return 0;
+}
+
+int
+rte_rib_set_nh(struct rte_rib_node *node, uint64_t nh)
+{
+	if (node == NULL) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	node->nh = nh;
+	return 0;
+}
+
+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;
+
+	/* Check user arguments. */
+	if ((name == NULL) || (conf == NULL) ||
+			(conf->max_nodes == 0)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	snprintf(mem_name, sizeof(mem_name), "MP_%s", name);
+	node_pool = rte_mempool_create(mem_name, conf->max_nodes,
+		sizeof(struct rte_rib_node) + conf->ext_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);
+		return NULL;
+	}
+
+	snprintf(mem_name, sizeof(mem_name), "RIB_%s", name);
+	rib_list = RTE_TAILQ_CAST(rte_rib_tailq.head, rte_rib_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* 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 RIB data structures. */
+	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;
+	}
+
+	rte_strlcpy(rib->name, name, sizeof(rib->name));
+	rib->tree = NULL;
+	rib->max_nodes = conf->max_nodes;
+	rib->node_pool = node_pool;
+	te->data = (void *)rib;
+	TAILQ_INSERT_TAIL(rib_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	return rib;
+
+free_te:
+	rte_free(te);
+exit:
+	rte_mcfg_tailq_write_unlock();
+	rte_mempool_free(node_pool);
+
+	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_mcfg_tailq_read_lock();
+	TAILQ_FOREACH(te, rib_list, next) {
+		rib = (struct rte_rib *) te->data;
+		if (strncmp(name, rib->name, RTE_RIB_NAMESIZE) == 0)
+			break;
+	}
+	rte_mcfg_tailq_read_unlock();
+
+	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_mcfg_tailq_write_lock();
+
+	/* 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_mcfg_tailq_write_unlock();
+
+	while ((tmp = rte_rib_get_nxt(rib, 0, 0, tmp,
+			RTE_RIB_GET_NXT_ALL)) != NULL)
+		rte_rib_remove(rib, tmp->ip, tmp->depth);
+
+	rte_mempool_free(rib->node_pool);
+	rte_free(rib);
+	rte_free(te);
+}
diff --git a/lib/librte_rib/rte_rib.h b/lib/librte_rib/rte_rib.h
new file mode 100644
index 0000000..6b70de9
--- /dev/null
+++ b/lib/librte_rib/rte_rib.h
@@ -0,0 +1,277 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _RTE_RIB_H_
+#define _RTE_RIB_H_
+
+/**
+ * @file
+ * Level compressed tree implementation for IPv4 Longest Prefix Match
+ */
+
+#include <rte_compat.h>
+
+/**
+ * rte_rib_get_nxt() flags
+ */
+enum {
+	/** flag to get all subroutes in a RIB tree */
+	RTE_RIB_GET_NXT_ALL,
+	/** flag to get first matched subroutes in a RIB tree */
+	RTE_RIB_GET_NXT_COVER
+};
+
+struct rte_rib;
+struct rte_rib_node;
+
+/** RIB configuration structure */
+struct rte_rib_conf {
+	/**
+	 * Size of extension block inside rte_rib_node.
+	 * This space could be used to store additional user
+	 * defined data.
+	 */
+	size_t	ext_sz;
+	/* size of rte_rib_node's pool */
+	int	max_nodes;
+};
+
+/**
+ * Get an IPv4 mask from prefix length
+ * It is caller responsibility to make sure depth is not bigger than 32
+ *
+ * @param depth
+ *   prefix length
+ * @return
+ *  IPv4 mask
+ */
+static inline uint32_t
+rte_rib_depth_to_mask(uint8_t depth)
+{
+	return (uint32_t)(UINT64_MAX << (32 - depth));
+}
+
+/**
+ * Lookup an IP into the RIB structure
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  IP to be looked up in the RIB
+ * @return
+ *  pointer to struct rte_rib_node on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib_node *
+rte_rib_lookup(struct rte_rib *rib, uint32_t ip);
+
+/**
+ * Lookup less specific route into the RIB structure
+ *
+ * @param ent
+ *  Pointer to struct rte_rib_node that represents target route
+ * @return
+ *  pointer to struct rte_rib_node that represents
+ *   less specific route on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib_node *
+rte_rib_lookup_parent(struct rte_rib_node *ent);
+
+/**
+ * Lookup prefix into the RIB structure
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net to be looked up in the RIB
+ * @param depth
+ *  prefix length
+ * @return
+ *  pointer to struct rte_rib_node on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib_node *
+rte_rib_lookup_exact(struct rte_rib *rib, uint32_t ip, uint8_t depth);
+
+/**
+ * Retrieve next more specific prefix from the RIB
+ * that is covered by ip/depth supernet in an ascending order
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net address of supernet prefix that covers returned more specific prefixes
+ * @param depth
+ *  supernet prefix length
+ * @param last
+ *   pointer to the last returned prefix to get next prefix
+ *   or
+ *   NULL to get first more specific prefix
+ * @param flag
+ *  -RTE_RIB_GET_NXT_ALL
+ *   get all prefixes from subtrie
+ *  -RTE_RIB_GET_NXT_COVER
+ *   get only first more specific prefix even if it have more specifics
+ * @return
+ *  pointer to the next more specific prefix
+ *  NULL if there is no prefixes left
+ */
+__rte_experimental
+struct rte_rib_node *
+rte_rib_get_nxt(struct rte_rib *rib, uint32_t ip, uint8_t depth,
+	struct rte_rib_node *last, int flag);
+
+/**
+ * Remove prefix from the RIB
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net to be removed from the RIB
+ * @param depth
+ *  prefix length
+ */
+__rte_experimental
+void
+rte_rib_remove(struct rte_rib *rib, uint32_t ip, uint8_t depth);
+
+/**
+ * Insert prefix into the RIB
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net to be inserted to the RIB
+ * @param depth
+ *  prefix length
+ * @return
+ *  pointer to new rte_rib_node on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib_node *
+rte_rib_insert(struct rte_rib *rib, uint32_t ip, uint8_t depth);
+
+/**
+ * Get an ip from rte_rib_node
+ *
+ * @param node
+ *  pointer to the rib node
+ * @param ip
+ *  pointer to the ip to save
+ * @return
+ *  0 on success.
+ *  -1 on failure with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib_get_ip(struct rte_rib_node *node, uint32_t *ip);
+
+/**
+ * Get a depth from rte_rib_node
+ *
+ * @param node
+ *  pointer to the rib node
+ * @param depth
+ *  pointer to the depth to save
+ * @return
+ *  0 on success.
+ *  -1 on failure with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib_get_depth(struct rte_rib_node *node, uint8_t *depth);
+
+/**
+ * Get ext field from the rib node
+ * It is caller responsibility to make sure there are necessary space
+ * for the ext field inside rib node.
+ *
+ * @param node
+ *  pointer to the rib node
+ * @return
+ *  pointer to the ext
+ */
+__rte_experimental
+void *
+rte_rib_get_ext(struct rte_rib_node *node);
+
+/**
+ * Get nexthop from the rib node
+ *
+ * @param node
+ *  pointer to the rib node
+ * @param nh
+ *  pointer to the nexthop to save
+ * @return
+ *  0 on success.
+ *  -1 on failure with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib_get_nh(struct rte_rib_node *node, uint64_t *nh);
+
+/**
+ * Set nexthop into the rib node
+ *
+ * @param node
+ *  pointer to the rib node
+ * @param nh
+ *  nexthop value to set to the rib node
+ * @return
+ *  0 on success.
+ *  -1 on failure with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib_set_nh(struct rte_rib_node *node, uint64_t nh);
+
+/**
+ * Create RIB
+ *
+ * @param name
+ *  RIB name
+ * @param socket_id
+ *  NUMA socket ID for RIB table memory allocation
+ * @param conf
+ *  Structure containing the configuration
+ * @return
+ *  Handle to RIB object on success
+ *  NULL otherwise with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+struct rte_rib *
+rte_rib_create(const char *name, int socket_id, struct rte_rib_conf *conf);
+
+/**
+ * Find an existing RIB object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the rib object as passed to rte_rib_create()
+ * @return
+ *  Pointer to RIB object on success
+ *  NULL otherwise with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+struct rte_rib *
+rte_rib_find_existing(const char *name);
+
+/**
+ * Free an RIB object.
+ *
+ * @param rib
+ *   RIB object handle
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_rib_free(struct rte_rib *rib);
+
+#endif /* _RTE_RIB_H_ */
diff --git a/lib/librte_rib/rte_rib_version.map b/lib/librte_rib/rte_rib_version.map
new file mode 100644
index 0000000..1432a22
--- /dev/null
+++ b/lib/librte_rib/rte_rib_version.map
@@ -0,0 +1,20 @@
+EXPERIMENTAL {
+	global:
+
+	rte_rib_create;
+	rte_rib_find_existing;
+	rte_rib_free;
+	rte_rib_get_depth;
+	rte_rib_get_ext;
+	rte_rib_get_ip;
+	rte_rib_get_nh;
+	rte_rib_get_nxt;
+	rte_rib_insert;
+	rte_rib_lookup;
+	rte_rib_lookup_parent;
+	rte_rib_lookup_exact;
+	rte_rib_set_nh;
+	rte_rib_remove;
+
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index e5ff838..d7f2a04 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -22,7 +22,7 @@ libraries = [
 	'gro', 'gso', 'ip_frag', 'jobstats',
 	'kni', 'latencystats', 'lpm', 'member',
 	'power', 'pdump', 'rawdev',
-	'rcu', 'reorder', 'sched', 'security', 'stack', 'vhost',
+	'rcu', 'rib', 'reorder', 'sched', 'security', 'stack', 'vhost',
 	# ipsec lib depends on net, crypto and security
 	'ipsec',
 	# add pkt framework libs which use other libs from above
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index ba5c39e..4517874 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -45,6 +45,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_PDUMP)          += -lrte_pdump
 _LDLIBS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR)    += -lrte_distributor
 _LDLIBS-$(CONFIG_RTE_LIBRTE_IP_FRAG)        += -lrte_ip_frag
 _LDLIBS-$(CONFIG_RTE_LIBRTE_METER)          += -lrte_meter
+_LDLIBS-$(CONFIG_RTE_LIBRTE_RIB)            += -lrte_rib
 _LDLIBS-$(CONFIG_RTE_LIBRTE_LPM)            += -lrte_lpm
 _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL)            += -lrte_acl
 _LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY)      += --no-as-needed
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 02/12] test/rib: add RIB library autotests
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
                     ` (2 preceding siblings ...)
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 01/12] rib: add RIB library Vladimir Medvedkin
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 03/12] rib: add ipv6 support for RIB Vladimir Medvedkin
                     ` (9 subsequent siblings)
  13 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

Functional tests for the new RIB library.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   2 +
 app/test/autotest_data.py |   6 +
 app/test/meson.build      |   3 +
 app/test/test_rib.c       | 351 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 362 insertions(+)
 create mode 100644 app/test/test_rib.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 26ba6fe..5bd8487 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -124,6 +124,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_multiwriter.c
 SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite.c
 SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite_lf.c
 
+SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib.c
+
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 7405149..3c3b806 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -111,6 +111,12 @@
         "Report":  None,
     },
     {
+        "Name":    "RIB autotest",
+        "Command": "rib_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
         "Name":    "Memcpy autotest",
         "Command": "memcpy_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index ec40943..98524e1 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -99,6 +99,7 @@ test_sources = files('commands.c',
 	'test_reciprocal_division_perf.c',
 	'test_red.c',
 	'test_reorder.c',
+	'test_rib.c',
 	'test_ring.c',
 	'test_ring_perf.c',
 	'test_rwlock.c',
@@ -147,6 +148,7 @@ test_deps = ['acl',
 	'rawdev',
 	'rcu',
 	'reorder',
+	'rib',
 	'ring',
 	'stack',
 	'timer'
@@ -197,6 +199,7 @@ fast_test_names = [
         'prefetch_autotest',
         'rcu_qsbr_autotest',
         'red_autotest',
+        'rib_autotest',
         'ring_autotest',
         'ring_pmd_autotest',
         'rwlock_test1_autotest',
diff --git a/app/test/test_rib.c b/app/test/test_rib.c
new file mode 100644
index 0000000..c95957a
--- /dev/null
+++ b/app/test/test_rib.c
@@ -0,0 +1,351 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <rte_ip.h>
+#include <rte_rib.h>
+
+#include "test.h"
+
+typedef int32_t (*rte_rib_test)(void);
+
+static int32_t test_create_invalid(void);
+static int32_t test_multiple_create(void);
+static int32_t test_free_null(void);
+static int32_t test_insert_invalid(void);
+static int32_t test_get_fn(void);
+static int32_t test_basic(void);
+static int32_t test_tree_traversal(void);
+
+#define MAX_DEPTH 32
+#define MAX_RULES (1 << 22)
+
+/*
+ * Check that rte_rib_create fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test_create_invalid(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf config;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	/* rte_rib_create: rib name == NULL */
+	rib = rte_rib_create(NULL, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_rib_create: config == NULL */
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, NULL);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* socket_id < -1 is invalid */
+	rib = rte_rib_create(__func__, -2, &config);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_rib_create: max_nodes = 0 */
+	config.max_nodes = 0;
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.max_nodes = MAX_RULES;
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Create rib table then delete rib table 10 times
+ * Use a slightly different rules size each time
+ */
+int32_t
+test_multiple_create(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf config;
+	int32_t i;
+
+	config.ext_sz = 0;
+
+	for (i = 0; i < 10; i++) {
+		config.max_nodes = MAX_RULES - i;
+		rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+		RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+		rte_rib_free(rib);
+	}
+	/* Can not test free so return success */
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call rte_rib_free for NULL pointer user input. Note: free has no return and
+ * therefore it is impossible to check for failure but this test is added to
+ * increase function coverage metrics and to validate that freeing null does
+ * not crash.
+ */
+int32_t
+test_free_null(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_conf config;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	rte_rib_free(rib);
+	rte_rib_free(NULL);
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_rib_insert fails gracefully for incorrect user input arguments
+ */
+int32_t
+test_insert_invalid(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_node *node, *node1;
+	struct rte_rib_conf config;
+	uint32_t ip = RTE_IPV4(0, 0, 0, 0);
+	uint8_t depth = 24;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	/* rte_rib_insert: rib == NULL */
+	node = rte_rib_insert(NULL, ip, depth);
+	RTE_TEST_ASSERT(node == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/*Create valid rib to use in rest of test. */
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	/* rte_rib_insert: depth > MAX_DEPTH */
+	node = rte_rib_insert(rib, ip, MAX_DEPTH + 1);
+	RTE_TEST_ASSERT(node == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* insert the same ip/depth twice*/
+	node = rte_rib_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+	node1 = rte_rib_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node1 == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	rte_rib_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call rte_rib_node access functions with incorrect input.
+ * After call rte_rib_node access functions with correct args
+ * and check the return values for correctness
+ */
+int32_t
+test_get_fn(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_node *node;
+	struct rte_rib_conf config;
+	void *ext;
+	uint32_t ip = RTE_IPV4(192, 0, 2, 0);
+	uint32_t ip_ret;
+	uint64_t nh_set = 10;
+	uint64_t nh_ret;
+	uint8_t depth = 24;
+	uint8_t depth_ret;
+	int ret;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	node = rte_rib_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	/* test rte_rib_get_ip() with incorrect args */
+	ret = rte_rib_get_ip(NULL, &ip_ret);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+	ret = rte_rib_get_ip(node, NULL);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib_get_depth() with incorrect args */
+	ret = rte_rib_get_depth(NULL, &depth_ret);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+	ret = rte_rib_get_depth(node, NULL);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib_set_nh() with incorrect args */
+	ret = rte_rib_set_nh(NULL, nh_set);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib_get_nh() with incorrect args */
+	ret = rte_rib_get_nh(NULL, &nh_ret);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+	ret = rte_rib_get_nh(node, NULL);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib_get_ext() with incorrect args */
+	ext = rte_rib_get_ext(NULL);
+	RTE_TEST_ASSERT(ext == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* check the return values */
+	ret = rte_rib_get_ip(node, &ip_ret);
+	RTE_TEST_ASSERT((ret == 0) && (ip_ret == ip),
+		"Failed to get proper node ip\n");
+	ret = rte_rib_get_depth(node, &depth_ret);
+	RTE_TEST_ASSERT((ret == 0) && (depth_ret == depth),
+		"Failed to get proper node depth\n");
+	ret = rte_rib_set_nh(node, nh_set);
+	RTE_TEST_ASSERT(ret == 0,
+		"Failed to set rte_rib_node nexthop\n");
+	ret = rte_rib_get_nh(node, &nh_ret);
+	RTE_TEST_ASSERT((ret == 0) && (nh_ret == nh_set),
+		"Failed to get proper nexthop\n");
+
+	rte_rib_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call insert, lookup/lookup_exact and delete for a single rule
+ */
+int32_t
+test_basic(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_node *node;
+	struct rte_rib_conf config;
+
+	uint32_t ip = RTE_IPV4(192, 0, 2, 0);
+	uint64_t next_hop_add = 10;
+	uint64_t next_hop_return;
+	uint8_t depth = 24;
+	int ret;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	node = rte_rib_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	ret = rte_rib_set_nh(node, next_hop_add);
+	RTE_TEST_ASSERT(ret == 0,
+		"Failed to set rte_rib_node field\n");
+
+	node = rte_rib_lookup(rib, ip);
+	RTE_TEST_ASSERT(node != NULL, "Failed to lookup\n");
+
+	ret = rte_rib_get_nh(node, &next_hop_return);
+	RTE_TEST_ASSERT((ret == 0) && (next_hop_add == next_hop_return),
+		"Failed to get proper nexthop\n");
+
+	node = rte_rib_lookup_exact(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL,
+		"Failed to lookup\n");
+
+	ret = rte_rib_get_nh(node, &next_hop_return);
+	RTE_TEST_ASSERT((ret == 0) && (next_hop_add == next_hop_return),
+		"Failed to get proper nexthop\n");
+
+	rte_rib_remove(rib, ip, depth);
+
+	node = rte_rib_lookup(rib, ip);
+	RTE_TEST_ASSERT(node == NULL,
+		"Lookup returns non existent rule\n");
+	node = rte_rib_lookup_exact(rib, ip, depth);
+	RTE_TEST_ASSERT(node == NULL,
+		"Lookup returns non existent rule\n");
+
+	rte_rib_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+int32_t
+test_tree_traversal(void)
+{
+	struct rte_rib *rib = NULL;
+	struct rte_rib_node *node;
+	struct rte_rib_conf config;
+
+	uint32_t ip1 = RTE_IPV4(10, 10, 10, 0);
+	uint32_t ip2 = RTE_IPV4(10, 10, 130, 80);
+	uint8_t depth = 30;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	node = rte_rib_insert(rib, ip1, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	node = rte_rib_insert(rib, ip2, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	node = NULL;
+	node = rte_rib_get_nxt(rib, RTE_IPV4(10, 10, 130, 0), 24, node,
+			RTE_RIB_GET_NXT_ALL);
+	RTE_TEST_ASSERT(node != NULL, "Failed to get rib_node\n");
+
+	rte_rib_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite rib_tests = {
+	.suite_name = "rib autotest",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+		TEST_CASE(test_create_invalid),
+		TEST_CASE(test_multiple_create),
+		TEST_CASE(test_free_null),
+		TEST_CASE(test_insert_invalid),
+		TEST_CASE(test_get_fn),
+		TEST_CASE(test_basic),
+		TEST_CASE(test_tree_traversal),
+		TEST_CASES_END()
+	}
+};
+
+/*
+ * Do all unit tests.
+ */
+static int
+test_rib(void)
+{
+	return unit_test_suite_runner(&rib_tests);
+}
+
+REGISTER_TEST_COMMAND(rib_autotest, test_rib);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 03/12] rib: add ipv6 support for RIB
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
                     ` (3 preceding siblings ...)
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 02/12] test/rib: add RIB library autotests Vladimir Medvedkin
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 04/12] test/rib: add ipv6 support for RIB autotests Vladimir Medvedkin
                     ` (8 subsequent siblings)
  13 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

Extend RIB library with ipv6 support.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 lib/librte_rib/Makefile            |   4 +-
 lib/librte_rib/meson.build         |   4 +-
 lib/librte_rib/rte_rib6.c          | 598 +++++++++++++++++++++++++++++++++++++
 lib/librte_rib/rte_rib6.h          | 334 +++++++++++++++++++++
 lib/librte_rib/rte_rib_version.map |  15 +
 5 files changed, 951 insertions(+), 4 deletions(-)
 create mode 100644 lib/librte_rib/rte_rib6.c
 create mode 100644 lib/librte_rib/rte_rib6.h

diff --git a/lib/librte_rib/Makefile b/lib/librte_rib/Makefile
index 79f259a..6d861ad 100644
--- a/lib/librte_rib/Makefile
+++ b/lib/librte_rib/Makefile
@@ -17,9 +17,9 @@ EXPORT_MAP := rte_rib_version.map
 LIBABIVER := 1
 
 # all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_RIB) := rte_rib.c
+SRCS-$(CONFIG_RTE_LIBRTE_RIB) := rte_rib.c rte_rib6.c
 
 # install this header file
-SYMLINK-$(CONFIG_RTE_LIBRTE_RIB)-include := rte_rib.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_RIB)-include := rte_rib.h rte_rib6.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_rib/meson.build b/lib/librte_rib/meson.build
index e7b8920..46a1c0c 100644
--- a/lib/librte_rib/meson.build
+++ b/lib/librte_rib/meson.build
@@ -3,6 +3,6 @@
 # Copyright(c) 2019 Intel Corporation
 
 allow_experimental_apis = true
-sources = files('rte_rib.c')
-headers = files('rte_rib.h')
+sources = files('rte_rib.c', 'rte_rib6.c')
+headers = files('rte_rib.h', 'rte_rib6.h')
 deps += ['mempool']
diff --git a/lib/librte_rib/rte_rib6.c b/lib/librte_rib/rte_rib6.c
new file mode 100644
index 0000000..78b8dcf
--- /dev/null
+++ b/lib/librte_rib/rte_rib6.c
@@ -0,0 +1,598 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_mempool.h>
+#include <rte_rwlock.h>
+#include <rte_string_fns.h>
+#include <rte_tailq.h>
+
+#include <rte_rib6.h>
+
+#define RTE_RIB_VALID_NODE	1
+#define RIB6_MAXDEPTH		128
+/* Maximum length of a RIB6 name. */
+#define RTE_RIB6_NAMESIZE	64
+
+TAILQ_HEAD(rte_rib6_list, rte_tailq_entry);
+static struct rte_tailq_elem rte_rib6_tailq = {
+	.name = "RTE_RIB6",
+};
+EAL_REGISTER_TAILQ(rte_rib6_tailq)
+
+struct rte_rib6_node {
+	struct rte_rib6_node	*left;
+	struct rte_rib6_node	*right;
+	struct rte_rib6_node	*parent;
+	uint64_t		nh;
+	uint8_t			ip[RTE_RIB6_IPV6_ADDR_SIZE];
+	uint8_t			depth;
+	uint8_t			flag;
+	__extension__ uint64_t		ext[0];
+};
+
+struct rte_rib6 {
+	char		name[RTE_RIB6_NAMESIZE];
+	struct rte_rib6_node	*tree;
+	struct rte_mempool	*node_pool;
+	uint32_t		cur_nodes;
+	uint32_t		cur_routes;
+	int			max_nodes;
+};
+
+static inline bool
+is_valid_node(struct rte_rib6_node *node)
+{
+	return (node->flag & RTE_RIB_VALID_NODE) == RTE_RIB_VALID_NODE;
+}
+
+static inline bool
+is_right_node(struct rte_rib6_node *node)
+{
+	return node->parent->right == node;
+}
+
+/*
+ * Check if ip1 is covered by ip2/depth prefix
+ */
+static inline bool
+is_covered(const uint8_t ip1[RTE_RIB6_IPV6_ADDR_SIZE],
+		const uint8_t ip2[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)
+{
+	int i;
+
+	for (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)
+		if ((ip1[i] ^ ip2[i]) & get_msk_part(depth, i))
+			return false;
+
+	return true;
+}
+
+static inline int
+get_dir(const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)
+{
+	int i = 0;
+	uint8_t p_depth, msk;
+
+	for (p_depth = depth; p_depth >= 8; p_depth -= 8)
+		i++;
+
+	msk = 1 << (7 - p_depth);
+	return (ip[i] & msk) != 0;
+}
+
+static inline struct rte_rib6_node *
+get_nxt_node(struct rte_rib6_node *node,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE])
+{
+	return (get_dir(ip, node->depth)) ? node->right : node->left;
+}
+
+static struct rte_rib6_node *
+node_alloc(struct rte_rib6 *rib)
+{
+	struct rte_rib6_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
+node_free(struct rte_rib6 *rib, struct rte_rib6_node *ent)
+{
+	--rib->cur_nodes;
+	rte_mempool_put(rib->node_pool, ent);
+}
+
+struct rte_rib6_node *
+rte_rib6_lookup(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE])
+{
+	struct rte_rib6_node *cur;
+	struct rte_rib6_node *prev = NULL;
+
+	if (unlikely(rib == NULL)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+	cur = rib->tree;
+
+	while ((cur != NULL) && is_covered(ip, cur->ip, cur->depth)) {
+		if (is_valid_node(cur))
+			prev = cur;
+		cur = get_nxt_node(cur, ip);
+	}
+	return prev;
+}
+
+struct rte_rib6_node *
+rte_rib6_lookup_parent(struct rte_rib6_node *ent)
+{
+	struct rte_rib6_node *tmp;
+
+	if (ent == NULL)
+		return NULL;
+
+	tmp = ent->parent;
+	while ((tmp != NULL) && (!is_valid_node(tmp)))
+		tmp = tmp->parent;
+
+	return tmp;
+}
+
+struct rte_rib6_node *
+rte_rib6_lookup_exact(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)
+{
+	struct rte_rib6_node *cur;
+	uint8_t tmp_ip[RTE_RIB6_IPV6_ADDR_SIZE];
+	int i;
+
+	if ((rib == NULL) || (ip == NULL) || (depth > RIB6_MAXDEPTH)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+	cur = rib->tree;
+
+	for (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)
+		tmp_ip[i] = ip[i] & get_msk_part(depth, i);
+
+	while (cur != NULL) {
+		if (rte_rib6_is_equal(cur->ip, tmp_ip) &&
+				(cur->depth == depth) &&
+				is_valid_node(cur))
+			return cur;
+
+		if (!(is_covered(tmp_ip, cur->ip, cur->depth)) ||
+				(cur->depth >= depth))
+			break;
+
+		cur = get_nxt_node(cur, tmp_ip);
+	}
+
+	return NULL;
+}
+
+/*
+ *  Traverses on subtree and retreeves more specific routes
+ *  for a given in args ip/depth prefix
+ *  last = NULL means the first invocation
+ */
+struct rte_rib6_node *
+rte_rib6_get_nxt(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, struct rte_rib6_node *last, int flag)
+{
+	struct rte_rib6_node *tmp, *prev = NULL;
+	uint8_t tmp_ip[RTE_RIB6_IPV6_ADDR_SIZE];
+	int i;
+
+	if ((rib == NULL) || (ip == NULL) || (depth > RIB6_MAXDEPTH)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	for (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)
+		tmp_ip[i] = ip[i] & get_msk_part(depth, i);
+
+	if (last == NULL) {
+		tmp = rib->tree;
+		while ((tmp) && (tmp->depth < depth))
+			tmp = get_nxt_node(tmp, tmp_ip);
+	} else {
+		tmp = last;
+		while ((tmp->parent != NULL) && (is_right_node(tmp) ||
+				(tmp->parent->right == NULL))) {
+			tmp = tmp->parent;
+			if (is_valid_node(tmp) &&
+					(is_covered(tmp->ip, tmp_ip, depth) &&
+					(tmp->depth > depth)))
+				return tmp;
+		}
+		tmp = (tmp->parent != NULL) ? tmp->parent->right : NULL;
+	}
+	while (tmp) {
+		if (is_valid_node(tmp) &&
+				(is_covered(tmp->ip, tmp_ip, depth) &&
+				(tmp->depth > depth))) {
+			prev = tmp;
+			if (flag == RTE_RIB6_GET_NXT_COVER)
+				return prev;
+		}
+		tmp = (tmp->left != NULL) ? tmp->left : tmp->right;
+	}
+	return prev;
+}
+
+void
+rte_rib6_remove(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)
+{
+	struct rte_rib6_node *cur, *prev, *child;
+
+	cur = rte_rib6_lookup_exact(rib, ip, depth);
+	if (cur == NULL)
+		return;
+
+	--rib->cur_routes;
+	cur->flag &= ~RTE_RIB_VALID_NODE;
+	while (!is_valid_node(cur)) {
+		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->tree = child;
+			node_free(rib, cur);
+			return;
+		}
+		if (cur->parent->left == cur)
+			cur->parent->left = child;
+		else
+			cur->parent->right = child;
+		prev = cur;
+		cur = cur->parent;
+		node_free(rib, prev);
+	}
+}
+
+struct rte_rib6_node *
+rte_rib6_insert(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)
+{
+	struct rte_rib6_node **tmp;
+	struct rte_rib6_node *prev = NULL;
+	struct rte_rib6_node *new_node = NULL;
+	struct rte_rib6_node *common_node = NULL;
+	uint8_t common_prefix[RTE_RIB6_IPV6_ADDR_SIZE];
+	uint8_t tmp_ip[RTE_RIB6_IPV6_ADDR_SIZE];
+	int i, d;
+	uint8_t common_depth, ip_xor;
+
+	if (unlikely((rib == NULL) || (ip == NULL) ||
+			(depth > RIB6_MAXDEPTH))) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	tmp = &rib->tree;
+
+	for (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)
+		tmp_ip[i] = ip[i] & get_msk_part(depth, i);
+
+	new_node = rte_rib6_lookup_exact(rib, tmp_ip, depth);
+	if (new_node != NULL) {
+		rte_errno = EEXIST;
+		return NULL;
+	}
+
+	new_node = node_alloc(rib);
+	if (new_node == NULL) {
+		rte_errno = ENOMEM;
+		return NULL;
+	}
+	new_node->left = NULL;
+	new_node->right = NULL;
+	new_node->parent = NULL;
+	rte_rib6_copy_addr(new_node->ip, tmp_ip);
+	new_node->depth = depth;
+	new_node->flag = RTE_RIB_VALID_NODE;
+
+	/* traverse down the tree to find matching node or closest matching */
+	while (1) {
+		/* insert as the last node in the branch */
+		if (*tmp == NULL) {
+			*tmp = new_node;
+			new_node->parent = prev;
+			++rib->cur_routes;
+			return *tmp;
+		}
+		/*
+		 * Intermediate node found.
+		 * Previous rte_rib6_lookup_exact() returned NULL
+		 * but node with proper search criteria is found.
+		 * Validate intermediate node and return.
+		 */
+		if (rte_rib6_is_equal(tmp_ip, (*tmp)->ip) &&
+				(depth == (*tmp)->depth)) {
+			node_free(rib, new_node);
+			(*tmp)->flag |= RTE_RIB_VALID_NODE;
+			++rib->cur_routes;
+			return *tmp;
+		}
+
+		if (!is_covered(tmp_ip, (*tmp)->ip, (*tmp)->depth) ||
+				((*tmp)->depth >= depth)) {
+			break;
+		}
+		prev = *tmp;
+
+		tmp = (get_dir(tmp_ip, (*tmp)->depth)) ? &(*tmp)->right :
+				&(*tmp)->left;
+	}
+
+	/* closest node found, new_node should be inserted in the middle */
+	common_depth = RTE_MIN(depth, (*tmp)->depth);
+	for (i = 0, d = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++) {
+		ip_xor = tmp_ip[i] ^ (*tmp)->ip[i];
+		if (ip_xor == 0)
+			d += 8;
+		else {
+			d += __builtin_clz(ip_xor << 24);
+			break;
+		}
+	}
+
+	common_depth = RTE_MIN(d, common_depth);
+
+	for (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)
+		common_prefix[i] = tmp_ip[i] & get_msk_part(common_depth, i);
+
+	if (rte_rib6_is_equal(common_prefix, tmp_ip) &&
+			(common_depth == depth)) {
+		/* insert as a parent */
+		if (get_dir((*tmp)->ip, depth))
+			new_node->right = *tmp;
+		else
+			new_node->left = *tmp;
+		new_node->parent = (*tmp)->parent;
+		(*tmp)->parent = new_node;
+		*tmp = new_node;
+	} else {
+		/* create intermediate node */
+		common_node = node_alloc(rib);
+		if (common_node == NULL) {
+			node_free(rib, new_node);
+			rte_errno = ENOMEM;
+			return NULL;
+		}
+		rte_rib6_copy_addr(common_node->ip, 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 (get_dir((*tmp)->ip, common_depth) == 1) {
+			common_node->left = new_node;
+			common_node->right = *tmp;
+		} else {
+			common_node->left = *tmp;
+			common_node->right = new_node;
+		}
+		*tmp = common_node;
+	}
+	++rib->cur_routes;
+	return new_node;
+}
+
+int
+rte_rib6_get_ip(struct rte_rib6_node *node, uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE])
+{
+	if ((node == NULL) || (ip == NULL)) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	rte_rib6_copy_addr(ip, node->ip);
+	return 0;
+}
+
+int
+rte_rib6_get_depth(struct rte_rib6_node *node, uint8_t *depth)
+{
+	if ((node == NULL) || (depth == NULL)) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	*depth = node->depth;
+	return 0;
+}
+
+void *
+rte_rib6_get_ext(struct rte_rib6_node *node)
+{
+	return (node == NULL) ? NULL : &node->ext[0];
+}
+
+int
+rte_rib6_get_nh(struct rte_rib6_node *node, uint64_t *nh)
+{
+	if ((node == NULL) || (nh == NULL)) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	*nh = node->nh;
+	return 0;
+}
+
+int
+rte_rib6_set_nh(struct rte_rib6_node *node, uint64_t nh)
+{
+	if (node == NULL) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+	node->nh = nh;
+	return 0;
+}
+
+struct rte_rib6 *
+rte_rib6_create(const char *name, int socket_id, struct rte_rib6_conf *conf)
+{
+	char mem_name[RTE_RIB6_NAMESIZE];
+	struct rte_rib6 *rib = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_rib6_list *rib6_list;
+	struct rte_mempool *node_pool;
+
+	/* Check user arguments. */
+	if ((name == NULL) || (conf == NULL) ||
+			(conf->max_nodes == 0)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	snprintf(mem_name, sizeof(mem_name), "MP_%s", name);
+	node_pool = rte_mempool_create(mem_name, conf->max_nodes,
+		sizeof(struct rte_rib6_node) + conf->ext_sz, 0, 0,
+		NULL, NULL, NULL, NULL, socket_id, 0);
+
+	if (node_pool == NULL) {
+		RTE_LOG(ERR, LPM,
+			"Can not allocate mempool for RIB6 %s\n", name);
+		return NULL;
+	}
+
+	snprintf(mem_name, sizeof(mem_name), "RIB6_%s", name);
+	rib6_list = RTE_TAILQ_CAST(rte_rib6_tailq.head, rte_rib6_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* guarantee there's no existing */
+	TAILQ_FOREACH(te, rib6_list, next) {
+		rib = (struct rte_rib6 *)te->data;
+		if (strncmp(name, rib->name, RTE_RIB6_NAMESIZE) == 0)
+			break;
+	}
+	rib = NULL;
+	if (te != NULL) {
+		rte_errno = EEXIST;
+		goto exit;
+	}
+
+	/* allocate tailq entry */
+	te = rte_zmalloc("RIB6_TAILQ_ENTRY", sizeof(*te), 0);
+	if (te == NULL) {
+		RTE_LOG(ERR, LPM,
+			"Can not allocate tailq entry for RIB6 %s\n", name);
+		rte_errno = ENOMEM;
+		goto exit;
+	}
+
+	/* Allocate memory to store the RIB6 data structures. */
+	rib = rte_zmalloc_socket(mem_name,
+		sizeof(struct rte_rib6), RTE_CACHE_LINE_SIZE, socket_id);
+	if (rib == NULL) {
+		RTE_LOG(ERR, LPM, "RIB6 %s memory allocation failed\n", name);
+		rte_errno = ENOMEM;
+		goto free_te;
+	}
+
+	rte_strlcpy(rib->name, name, sizeof(rib->name));
+	rib->tree = NULL;
+	rib->max_nodes = conf->max_nodes;
+	rib->node_pool = node_pool;
+
+	te->data = (void *)rib;
+	TAILQ_INSERT_TAIL(rib6_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	return rib;
+
+free_te:
+	rte_free(te);
+exit:
+	rte_mcfg_tailq_write_unlock();
+	rte_mempool_free(node_pool);
+
+	return NULL;
+}
+
+struct rte_rib6 *
+rte_rib6_find_existing(const char *name)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_rib6_list *rib6_list;
+
+	if (unlikely(name == NULL)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	rib6_list = RTE_TAILQ_CAST(rte_rib6_tailq.head, rte_rib6_list);
+
+	rte_mcfg_tailq_read_lock();
+	TAILQ_FOREACH(te, rib6_list, next) {
+		rib = (struct rte_rib6 *) te->data;
+		if (strncmp(name, rib->name, RTE_RIB6_NAMESIZE) == 0)
+			break;
+	}
+	rte_mcfg_tailq_read_unlock();
+
+	if (te == NULL) {
+		rte_errno = ENOENT;
+		return NULL;
+	}
+
+	return rib;
+}
+
+void
+rte_rib6_free(struct rte_rib6 *rib)
+{
+	struct rte_tailq_entry *te;
+	struct rte_rib6_list *rib6_list;
+	struct rte_rib6_node *tmp = NULL;
+
+	if (unlikely(rib == NULL)) {
+		rte_errno = EINVAL;
+		return;
+	}
+
+	rib6_list = RTE_TAILQ_CAST(rte_rib6_tailq.head, rte_rib6_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* find our tailq entry */
+	TAILQ_FOREACH(te, rib6_list, next) {
+		if (te->data == (void *)rib)
+			break;
+	}
+	if (te != NULL)
+		TAILQ_REMOVE(rib6_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	while ((tmp = rte_rib6_get_nxt(rib, 0, 0, tmp,
+			RTE_RIB6_GET_NXT_ALL)) != NULL)
+		rte_rib6_remove(rib, tmp->ip, tmp->depth);
+
+	rte_mempool_free(rib->node_pool);
+
+	rte_free(rib);
+	rte_free(te);
+}
diff --git a/lib/librte_rib/rte_rib6.h b/lib/librte_rib/rte_rib6.h
new file mode 100644
index 0000000..8714571
--- /dev/null
+++ b/lib/librte_rib/rte_rib6.h
@@ -0,0 +1,334 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _RTE_RIB6_H_
+#define _RTE_RIB6_H_
+
+/**
+ * @file
+ * Level compressed tree implementation for IPv6 Longest Prefix Match
+ */
+
+#include <rte_memcpy.h>
+#include <rte_compat.h>
+
+#define RTE_RIB6_IPV6_ADDR_SIZE	16
+
+/**
+ * rte_rib6_get_nxt() flags
+ */
+enum {
+	/** flag to get all subroutes in a RIB tree */
+	RTE_RIB6_GET_NXT_ALL,
+	/** flag to get first matched subroutes in a RIB tree */
+	RTE_RIB6_GET_NXT_COVER
+};
+
+struct rte_rib6;
+struct rte_rib6_node;
+
+/** RIB configuration structure */
+struct rte_rib6_conf {
+	/**
+	 * Size of extension block inside rte_rib_node.
+	 * This space could be used to store additional user
+	 * defined data.
+	 */
+	size_t	ext_sz;
+	/* size of rte_rib_node's pool */
+	int	max_nodes;
+};
+
+/**
+ * Copy IPv6 address from one location to another
+ *
+ * @param dst
+ *  pointer to the place to copy
+ * @param src
+ *  pointer from where to copy
+ */
+static inline void
+rte_rib6_copy_addr(uint8_t *dst, const uint8_t *src)
+{
+	if ((dst == NULL) || (src == NULL))
+		return;
+	rte_memcpy(dst, src, RTE_RIB6_IPV6_ADDR_SIZE);
+}
+
+/**
+ * Compare two IPv6 addresses
+ *
+ * @param ip1
+ *  pointer to the first ipv6 address
+ * @param ip2
+ *  pointer to the second ipv6 address
+ *
+ * @return
+ *  1 if equal
+ *  0 otherwise
+ */
+static inline int
+rte_rib6_is_equal(uint8_t *ip1, uint8_t *ip2) {
+	int i;
+
+	if ((ip1 == NULL) || (ip2 == NULL))
+		return 0;
+	for (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++) {
+		if (ip1[i] != ip2[i])
+			return 0;
+	}
+	return 1;
+}
+
+/**
+ * Get 8-bit part of 128-bit IPv6 mask
+ *
+ * @param depth
+ *  ipv6 prefix length
+ * @param byte
+ *  position of a 8-bit chunk in the 128-bit mask
+ *
+ * @return
+ *  8-bit chunk of the 128-bit IPv6 mask
+ */
+static inline uint8_t
+get_msk_part(uint8_t depth, int byte) {
+	uint8_t part;
+
+	byte &= 0xf;
+	depth = RTE_MIN(depth, 128);
+	part = RTE_MAX((int16_t)depth - (byte * 8), 0);
+	part = (part > 8) ? 8 : part;
+	return (uint16_t)(~UINT8_MAX) >> part;
+}
+
+/**
+ * Lookup an IP into the RIB structure
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  IP to be looked up in the RIB
+ * @return
+ *  pointer to struct rte_rib6_node on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib6_node *
+rte_rib6_lookup(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE]);
+
+/**
+ * Lookup less specific route into the RIB structure
+ *
+ * @param ent
+ *  Pointer to struct rte_rib6_node that represents target route
+ * @return
+ *  pointer to struct rte_rib6_node that represents
+ *   less specific route on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib6_node *
+rte_rib6_lookup_parent(struct rte_rib6_node *ent);
+
+/**
+ * Provides exact mach lookup of the prefix into the RIB structure
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net to be looked up in the RIB
+ * @param depth
+ *  prefix length
+ * @return
+ *  pointer to struct rte_rib6_node on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib6_node *
+rte_rib6_lookup_exact(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth);
+
+/**
+ * Retrieve next more specific prefix from the RIB
+ * that is covered by ip/depth supernet in an ascending order
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net address of supernet prefix that covers returned more specific prefixes
+ * @param depth
+ *  supernet prefix length
+ * @param last
+ *   pointer to the last returned prefix to get next prefix
+ *   or
+ *   NULL to get first more specific prefix
+ * @param flag
+ *  -RTE_RIB6_GET_NXT_ALL
+ *   get all prefixes from subtrie
+ *  -RTE_RIB6_GET_NXT_COVER
+ *   get only first more specific prefix even if it have more specifics
+ * @return
+ *  pointer to the next more specific prefix
+ *  NULL if there is no prefixes left
+ */
+__rte_experimental
+struct rte_rib6_node *
+rte_rib6_get_nxt(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, struct rte_rib6_node *last, int flag);
+
+/**
+ * Remove prefix from the RIB
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net to be removed from the RIB
+ * @param depth
+ *  prefix length
+ */
+__rte_experimental
+void
+rte_rib6_remove(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth);
+
+/**
+ * Insert prefix into the RIB
+ *
+ * @param rib
+ *  RIB object handle
+ * @param ip
+ *  net to be inserted to the RIB
+ * @param depth
+ *  prefix length
+ * @return
+ *  pointer to new rte_rib6_node on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_rib6_node *
+rte_rib6_insert(struct rte_rib6 *rib,
+	const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth);
+
+/**
+ * Get an ip from rte_rib6_node
+ *
+ * @param node
+ *  pointer to the rib6 node
+ * @param ip
+ *  pointer to the ipv6 to save
+ * @return
+ *  0 on success
+ *  -1 on failure with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib6_get_ip(struct rte_rib6_node *node,
+	uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE]);
+
+/**
+ * Get a depth from rte_rib6_node
+ *
+ * @param node
+ *  pointer to the rib6 node
+ * @param depth
+ *  pointer to the depth to save
+ * @return
+ *  0 on success
+ *  -1 on failure with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib6_get_depth(struct rte_rib6_node *node, uint8_t *depth);
+
+/**
+ * Get ext field from the rte_rib6_node
+ * It is caller responsibility to make sure there are necessary space
+ * for the ext field inside rib6 node.
+ *
+ * @param node
+ *  pointer to the rte_rib6_node
+ * @return
+ *  pointer to the ext
+ */
+__rte_experimental
+void *
+rte_rib6_get_ext(struct rte_rib6_node *node);
+
+/**
+ * Get nexthop from the rte_rib6_node
+ *
+ * @param node
+ *  pointer to the rib6 node
+ * @param nh
+ *  pointer to the nexthop to save
+ * @return
+ *  0 on success
+ *  -1 on failure, with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib6_get_nh(struct rte_rib6_node *node, uint64_t *nh);
+
+/**
+ * Set nexthop into the rte_rib6_node
+ *
+ * @param node
+ *  pointer to the rib6 node
+ * @param nh
+ *  nexthop value to set to the rib6 node
+ * @return
+ *  0 on success
+ *  -1 on failure, with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+int
+rte_rib6_set_nh(struct rte_rib6_node *node, uint64_t nh);
+
+/**
+ * Create RIB
+ *
+ * @param name
+ *  RIB name
+ * @param socket_id
+ *  NUMA socket ID for RIB table memory allocation
+ * @param conf
+ *  Structure containing the configuration
+ * @return
+ *  Pointer to RIB object on success
+ *  NULL otherwise with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+struct rte_rib6 *
+rte_rib6_create(const char *name, int socket_id, struct rte_rib6_conf *conf);
+
+/**
+ * Find an existing RIB object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the rib object as passed to rte_rib_create()
+ * @return
+ *  Pointer to RIB object on success
+ *  NULL otherwise with rte_errno indicating reason for failure.
+ */
+__rte_experimental
+struct rte_rib6 *
+rte_rib6_find_existing(const char *name);
+
+/**
+ * Free an RIB object.
+ *
+ * @param rib
+ *   RIB object handle
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_rib6_free(struct rte_rib6 *rib);
+
+#endif /* _RTE_RIB_H_ */
diff --git a/lib/librte_rib/rte_rib_version.map b/lib/librte_rib/rte_rib_version.map
index 1432a22..9b6161a 100644
--- a/lib/librte_rib/rte_rib_version.map
+++ b/lib/librte_rib/rte_rib_version.map
@@ -16,5 +16,20 @@ EXPERIMENTAL {
 	rte_rib_set_nh;
 	rte_rib_remove;
 
+	rte_rib6_create;
+	rte_rib6_find_existing;
+	rte_rib6_free;
+	rte_rib6_get_depth;
+	rte_rib6_get_ext;
+	rte_rib6_get_ip;
+	rte_rib6_get_nh;
+	rte_rib6_get_nxt;
+	rte_rib6_insert;
+	rte_rib6_lookup;
+	rte_rib6_lookup_parent;
+	rte_rib6_lookup_exact;
+	rte_rib6_set_nh;
+	rte_rib6_remove;
+
 	local: *;
 };
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 04/12] test/rib: add ipv6 support for RIB autotests
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
                     ` (4 preceding siblings ...)
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 03/12] rib: add ipv6 support for RIB Vladimir Medvedkin
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 05/12] fib: add FIB library Vladimir Medvedkin
                     ` (7 subsequent siblings)
  13 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

Functional tests for RIB6.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |   6 +
 app/test/meson.build      |   2 +
 app/test/test_rib6.c      | 357 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 366 insertions(+)
 create mode 100644 app/test/test_rib6.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 5bd8487..4638426 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -125,6 +125,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite.c
 SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite_lf.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib.c
+SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib6.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 3c3b806..f35b3cc 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -117,6 +117,12 @@
         "Report":  None,
     },
     {
+        "Name":    "RIB6 autotest",
+        "Command": "rib6_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
         "Name":    "Memcpy autotest",
         "Command": "memcpy_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 98524e1..49c1de0 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -100,6 +100,7 @@ test_sources = files('commands.c',
 	'test_red.c',
 	'test_reorder.c',
 	'test_rib.c',
+	'test_rib6.c',
 	'test_ring.c',
 	'test_ring_perf.c',
 	'test_rwlock.c',
@@ -200,6 +201,7 @@ fast_test_names = [
         'rcu_qsbr_autotest',
         'red_autotest',
         'rib_autotest',
+        'rib6_autotest',
         'ring_autotest',
         'ring_pmd_autotest',
         'rwlock_test1_autotest',
diff --git a/app/test/test_rib6.c b/app/test/test_rib6.c
new file mode 100644
index 0000000..638ba68
--- /dev/null
+++ b/app/test/test_rib6.c
@@ -0,0 +1,357 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <rte_ip.h>
+#include <rte_rib6.h>
+
+#include "test.h"
+
+typedef int32_t (*rte_rib6_test)(void);
+
+static int32_t test_create_invalid(void);
+static int32_t test_multiple_create(void);
+static int32_t test_free_null(void);
+static int32_t test_insert_invalid(void);
+static int32_t test_get_fn(void);
+static int32_t test_basic(void);
+static int32_t test_tree_traversal(void);
+
+#define MAX_DEPTH 128
+#define MAX_RULES (1 << 22)
+
+/*
+ * Check that rte_rib6_create fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test_create_invalid(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_conf config;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	/* rte_rib6_create: rib name == NULL */
+	rib = rte_rib6_create(NULL, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_rib6_create: config == NULL */
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, NULL);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* socket_id < -1 is invalid */
+	rib = rte_rib6_create(__func__, -2, &config);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_rib6_create: max_nodes = 0 */
+	config.max_nodes = 0;
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.max_nodes = MAX_RULES;
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Create rib table then delete rib table 10 times
+ * Use a slightly different rules size each time
+ */
+int32_t
+test_multiple_create(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_conf config;
+	int32_t i;
+
+	config.ext_sz = 0;
+
+	for (i = 0; i < 10; i++) {
+		config.max_nodes = MAX_RULES - i;
+		rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+		RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+		rte_rib6_free(rib);
+	}
+	/* Can not test free so return success */
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call rte_rib6_free for NULL pointer user input. Note: free has no return and
+ * therefore it is impossible to check for failure but this test is added to
+ * increase function coverage metrics and to validate that freeing null does
+ * not crash.
+ */
+int32_t
+test_free_null(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_conf config;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	rte_rib6_free(rib);
+	rte_rib6_free(NULL);
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_rib6_insert fails gracefully
+ * for incorrect user input arguments
+ */
+int32_t
+test_insert_invalid(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_node *node, *node1;
+	struct rte_rib6_conf config;
+	uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE] = {0};
+	uint8_t depth = 24;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	/* rte_rib6_insert: rib == NULL */
+	node = rte_rib6_insert(NULL, ip, depth);
+	RTE_TEST_ASSERT(node == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/*Create valid rib to use in rest of test. */
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	/* rte_rib6_insert: depth > MAX_DEPTH */
+	node = rte_rib6_insert(rib, ip, MAX_DEPTH + 1);
+	RTE_TEST_ASSERT(node == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* insert the same ip/depth twice*/
+	node = rte_rib6_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+	node1 = rte_rib6_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node1 == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	rte_rib6_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call rte_rib6_node access functions with incorrect input.
+ * After call rte_rib6_node access functions with correct args
+ * and check the return values for correctness
+ */
+int32_t
+test_get_fn(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_node *node;
+	struct rte_rib6_conf config;
+	void *ext;
+	uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE] = {192, 0, 2, 0, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 0};
+	uint8_t ip_ret[RTE_RIB6_IPV6_ADDR_SIZE];
+	uint64_t nh_set = 10;
+	uint64_t nh_ret;
+	uint8_t depth = 24;
+	uint8_t depth_ret;
+	int ret;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	node = rte_rib6_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	/* test rte_rib6_get_ip() with incorrect args */
+	ret = rte_rib6_get_ip(NULL, ip_ret);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+	ret = rte_rib6_get_ip(node, NULL);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib6_get_depth() with incorrect args */
+	ret = rte_rib6_get_depth(NULL, &depth_ret);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+	ret = rte_rib6_get_depth(node, NULL);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib6_set_nh() with incorrect args */
+	ret = rte_rib6_set_nh(NULL, nh_set);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib6_get_nh() with incorrect args */
+	ret = rte_rib6_get_nh(NULL, &nh_ret);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+	ret = rte_rib6_get_nh(node, NULL);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* test rte_rib6_get_ext() with incorrect args */
+	ext = rte_rib6_get_ext(NULL);
+	RTE_TEST_ASSERT(ext == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* check the return values */
+	ret = rte_rib6_get_ip(node, ip_ret);
+	RTE_TEST_ASSERT((ret == 0) && (rte_rib6_is_equal(ip_ret, ip)),
+		"Failed to get proper node ip\n");
+	ret = rte_rib6_get_depth(node, &depth_ret);
+	RTE_TEST_ASSERT((ret == 0) && (depth_ret == depth),
+		"Failed to get proper node depth\n");
+	ret = rte_rib6_set_nh(node, nh_set);
+	RTE_TEST_ASSERT(ret == 0,
+		"Failed to set rte_rib_node nexthop\n");
+	ret = rte_rib6_get_nh(node, &nh_ret);
+	RTE_TEST_ASSERT((ret == 0) && (nh_ret == nh_set),
+		"Failed to get proper nexthop\n");
+
+	rte_rib6_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call insert, lookup/lookup_exact and delete for a single rule
+ */
+int32_t
+test_basic(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_node *node;
+	struct rte_rib6_conf config;
+
+	uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE] = {192, 0, 2, 0, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 0};
+	uint64_t next_hop_add = 10;
+	uint64_t next_hop_return;
+	uint8_t depth = 24;
+	uint32_t status = 0;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	node = rte_rib6_insert(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	status = rte_rib6_set_nh(node, next_hop_add);
+	RTE_TEST_ASSERT(status == 0,
+		"Failed to set rte_rib_node field\n");
+
+	node = rte_rib6_lookup(rib, ip);
+	RTE_TEST_ASSERT(node != NULL, "Failed to lookup\n");
+
+	status = rte_rib6_get_nh(node, &next_hop_return);
+	RTE_TEST_ASSERT((status == 0) && (next_hop_add == next_hop_return),
+		"Failed to get proper nexthop\n");
+
+	node = rte_rib6_lookup_exact(rib, ip, depth);
+	RTE_TEST_ASSERT(node != NULL,
+		"Failed to lookup\n");
+
+	status = rte_rib6_get_nh(node, &next_hop_return);
+	RTE_TEST_ASSERT((status == 0) && (next_hop_add == next_hop_return),
+		"Failed to get proper nexthop\n");
+
+	rte_rib6_remove(rib, ip, depth);
+
+	node = rte_rib6_lookup(rib, ip);
+	RTE_TEST_ASSERT(node == NULL,
+		"Lookup returns non existent rule\n");
+	node = rte_rib6_lookup_exact(rib, ip, depth);
+	RTE_TEST_ASSERT(node == NULL,
+		"Lookup returns non existent rule\n");
+
+	rte_rib6_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+int32_t
+test_tree_traversal(void)
+{
+	struct rte_rib6 *rib = NULL;
+	struct rte_rib6_node *node;
+	struct rte_rib6_conf config;
+
+	uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE] = {10, 0, 2, 130, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 0};
+	uint8_t ip1[RTE_RIB6_IPV6_ADDR_SIZE] = {10, 0, 2, 0, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 0};
+	uint8_t ip2[RTE_RIB6_IPV6_ADDR_SIZE] = {10, 0, 2, 130, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 80};
+	uint8_t depth = 126;
+
+	config.max_nodes = MAX_RULES;
+	config.ext_sz = 0;
+
+	rib = rte_rib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(rib != NULL, "Failed to create RIB\n");
+
+	node = rte_rib6_insert(rib, ip1, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+	node = rte_rib6_insert(rib, ip2, depth);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+
+	node = NULL;
+	node = rte_rib6_get_nxt(rib, ip, 32, node, RTE_RIB6_GET_NXT_ALL);
+	RTE_TEST_ASSERT(node != NULL, "Failed to get rib_node\n");
+
+	rte_rib6_free(rib);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite rib6_tests = {
+	.suite_name = "rib6 autotest",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+		TEST_CASE(test_create_invalid),
+		TEST_CASE(test_multiple_create),
+		TEST_CASE(test_free_null),
+		TEST_CASE(test_insert_invalid),
+		TEST_CASE(test_get_fn),
+		TEST_CASE(test_basic),
+		TEST_CASE(test_tree_traversal),
+		TEST_CASES_END()
+	}
+};
+
+/*
+ * Do all unit tests.
+ */
+static int
+test_rib6(void)
+{
+	return unit_test_suite_runner(&rib6_tests);
+}
+
+REGISTER_TEST_COMMAND(rib6_autotest, test_rib6);
+
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 05/12] fib: add FIB library
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
                     ` (5 preceding siblings ...)
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 04/12] test/rib: add ipv6 support for RIB autotests Vladimir Medvedkin
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 06/12] fib: add FIB ipv6 support Vladimir Medvedkin
                     ` (6 subsequent siblings)
  13 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

Add FIB (Forwarding Information Base) library. This library
implements a dataplane structures and algorithms designed for
fast longest prefix match.
Internally it consists of two parts - RIB (control plane ops) and
implementation for the dataplane tasks.
Initial version provides two implementations for both ipv4 and ipv6:
dummy (uses RIB as a dataplane) and DIR24_8 (same as current LPM)
Due to proposed design it allows to extend FIB with new algorithms
in future (for example DXR, poptrie, etc).

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 config/common_base                 |   6 +
 doc/api/doxy-api.conf.in           |   1 +
 lib/Makefile                       |   2 +
 lib/librte_fib/Makefile            |  25 +++
 lib/librte_fib/meson.build         |   8 +
 lib/librte_fib/rte_fib.c           | 305 +++++++++++++++++++++++++++++++++++++
 lib/librte_fib/rte_fib.h           | 173 +++++++++++++++++++++
 lib/librte_fib/rte_fib_version.map |  14 ++
 lib/meson.build                    |   2 +
 mk/rte.app.mk                      |   1 +
 10 files changed, 537 insertions(+)
 create mode 100644 lib/librte_fib/Makefile
 create mode 100644 lib/librte_fib/meson.build
 create mode 100644 lib/librte_fib/rte_fib.c
 create mode 100644 lib/librte_fib/rte_fib.h
 create mode 100644 lib/librte_fib/rte_fib_version.map

diff --git a/config/common_base b/config/common_base
index 5a9d4c3..0f32ad7 100644
--- a/config/common_base
+++ b/config/common_base
@@ -899,6 +899,12 @@ CONFIG_RTE_LIBRTE_RCU_DEBUG=n
 CONFIG_RTE_LIBRTE_RIB=y
 
 #
+# Compile librte_fib
+#
+CONFIG_RTE_LIBRTE_FIB=y
+CONFIG_RTE_LIBRTE_FIB_DEBUG=n
+
+#
 # Compile librte_lpm
 #
 CONFIG_RTE_LIBRTE_LPM=y
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 3ec012d..6fd3b51 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -30,6 +30,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_efd \
                           @TOPDIR@/lib/librte_ethdev \
                           @TOPDIR@/lib/librte_eventdev \
+                          @TOPDIR@/lib/librte_fib \
                           @TOPDIR@/lib/librte_flow_classify \
                           @TOPDIR@/lib/librte_gro \
                           @TOPDIR@/lib/librte_gso \
diff --git a/lib/Makefile b/lib/Makefile
index aa5ee1e..5d04ab9 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -53,6 +53,8 @@ DIRS-$(CONFIG_RTE_LIBRTE_EFD) += librte_efd
 DEPDIRS-librte_efd := librte_eal librte_ring librte_hash
 DIRS-$(CONFIG_RTE_LIBRTE_RIB) += librte_rib
 DEPDIRS-librte_rib := librte_eal librte_mempool
+DIRS-$(CONFIG_RTE_LIBRTE_FIB) += librte_fib
+DEPDIRS-librte_fib := librte_eal librte_rib
 DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm
 DEPDIRS-librte_lpm := librte_eal librte_hash
 DIRS-$(CONFIG_RTE_LIBRTE_ACL) += librte_acl
diff --git a/lib/librte_fib/Makefile b/lib/librte_fib/Makefile
new file mode 100644
index 0000000..7362f68
--- /dev/null
+++ b/lib/librte_fib/Makefile
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+# Copyright(c) 2019 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_fib.a
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+LDLIBS += -lrte_eal -lrte_rib
+
+EXPORT_MAP := rte_fib_version.map
+
+LIBABIVER := 1
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c
+
+# install this header file
+SYMLINK-$(CONFIG_RTE_LIBRTE_FIB)-include := rte_fib.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_fib/meson.build b/lib/librte_fib/meson.build
new file mode 100644
index 0000000..6b72360
--- /dev/null
+++ b/lib/librte_fib/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+# Copyright(c) 2019 Intel Corporation
+
+allow_experimental_apis = true
+sources = files('rte_fib.c')
+headers = files('rte_fib.h')
+deps += ['rib']
diff --git a/lib/librte_fib/rte_fib.c b/lib/librte_fib/rte_fib.c
new file mode 100644
index 0000000..4d8a771
--- /dev/null
+++ b/lib/librte_fib/rte_fib.c
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_rwlock.h>
+#include <rte_string_fns.h>
+#include <rte_tailq.h>
+
+#include <rte_rib.h>
+#include <rte_fib.h>
+
+TAILQ_HEAD(rte_fib_list, rte_tailq_entry);
+static struct rte_tailq_elem rte_fib_tailq = {
+	.name = "RTE_FIB",
+};
+EAL_REGISTER_TAILQ(rte_fib_tailq)
+
+/* Maximum length of a FIB name. */
+#define RTE_FIB_NAMESIZE	64
+
+#if defined(RTE_LIBRTE_FIB_DEBUG)
+#define FIB_RETURN_IF_TRUE(cond, retval) do {		\
+	if (cond)					\
+		return retval;				\
+} while (0)
+#else
+#define FIB_RETURN_IF_TRUE(cond, retval)
+#endif
+
+struct rte_fib {
+	char			name[RTE_FIB_NAMESIZE];
+	enum rte_fib_type	type;	/**< Type of FIB struct */
+	struct rte_rib		*rib;	/**< RIB helper datastruct */
+	void			*dp;	/**< pointer to the dataplane struct*/
+	rte_fib_lookup_fn_t	lookup;	/**< fib lookup function */
+	rte_fib_modify_fn_t	modify; /**< modify fib datastruct */
+	uint64_t		def_nh;
+};
+
+static void
+dummy_lookup(void *fib_p, const uint32_t *ips, uint64_t *next_hops,
+	const unsigned int n)
+{
+	unsigned int i;
+	struct rte_fib *fib = fib_p;
+	struct rte_rib_node *node;
+
+	for (i = 0; i < n; i++) {
+		node = rte_rib_lookup(fib->rib, ips[i]);
+		if (node != NULL)
+			rte_rib_get_nh(node, &next_hops[i]);
+		else
+			next_hops[i] = fib->def_nh;
+	}
+}
+
+static int
+dummy_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
+	uint64_t next_hop, int op)
+{
+	struct rte_rib_node *node;
+	if ((fib == NULL) || (depth > RTE_FIB_MAXDEPTH))
+		return -EINVAL;
+
+	node = rte_rib_lookup_exact(fib->rib, ip, depth);
+
+	switch (op) {
+	case RTE_FIB_ADD:
+		if (node == NULL)
+			node = rte_rib_insert(fib->rib, ip, depth);
+		if (node == NULL)
+			return -rte_errno;
+		return rte_rib_set_nh(node, next_hop);
+	case RTE_FIB_DEL:
+		if (node == NULL)
+			return -ENOENT;
+		rte_rib_remove(fib->rib, ip, depth);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int
+init_dataplane(struct rte_fib *fib, __rte_unused int socket_id,
+	struct rte_fib_conf *conf)
+{
+	switch (conf->type) {
+	case RTE_FIB_DUMMY:
+		fib->dp = fib;
+		fib->lookup = dummy_lookup;
+		fib->modify = dummy_modify;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int
+rte_fib_add(struct rte_fib *fib, uint32_t ip, uint8_t depth, uint64_t next_hop)
+{
+	if ((fib == NULL) || (fib->modify == NULL) ||
+			(depth > RTE_FIB_MAXDEPTH))
+		return -EINVAL;
+	return fib->modify(fib, ip, depth, next_hop, RTE_FIB_ADD);
+}
+
+int
+rte_fib_delete(struct rte_fib *fib, uint32_t ip, uint8_t depth)
+{
+	if ((fib == NULL) || (fib->modify == NULL) ||
+			(depth > RTE_FIB_MAXDEPTH))
+		return -EINVAL;
+	return fib->modify(fib, ip, depth, 0, RTE_FIB_DEL);
+}
+
+int
+rte_fib_lookup_bulk(struct rte_fib *fib, uint32_t *ips,
+	uint64_t *next_hops, int n)
+{
+	FIB_RETURN_IF_TRUE(((fib == NULL) || (ips == NULL) ||
+		(next_hops == NULL) || (fib->lookup == NULL)), -EINVAL);
+
+	fib->lookup(fib->dp, ips, next_hops, n);
+	return 0;
+}
+
+struct rte_fib *
+rte_fib_create(const char *name, int socket_id, struct rte_fib_conf *conf)
+{
+	char mem_name[RTE_FIB_NAMESIZE];
+	int ret;
+	struct rte_fib *fib = NULL;
+	struct rte_rib *rib = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_fib_list *fib_list;
+	struct rte_rib_conf rib_conf;
+
+	/* Check user arguments. */
+	if ((name == NULL) || (conf == NULL) ||	(conf->max_routes < 0) ||
+			(conf->type >= RTE_FIB_TYPE_MAX)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	rib_conf.ext_sz = 0;
+	rib_conf.max_nodes = conf->max_routes * 2;
+
+	rib = rte_rib_create(name, socket_id, &rib_conf);
+	if (rib == NULL) {
+		RTE_LOG(ERR, LPM,
+			"Can not allocate RIB %s\n", name);
+		return NULL;
+	}
+
+	snprintf(mem_name, sizeof(mem_name), "FIB_%s", name);
+	fib_list = RTE_TAILQ_CAST(rte_fib_tailq.head, rte_fib_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* guarantee there's no existing */
+	TAILQ_FOREACH(te, fib_list, next) {
+		fib = (struct rte_fib *)te->data;
+		if (strncmp(name, fib->name, RTE_FIB_NAMESIZE) == 0)
+			break;
+	}
+	fib = NULL;
+	if (te != NULL) {
+		rte_errno = EEXIST;
+		goto exit;
+	}
+
+	/* allocate tailq entry */
+	te = rte_zmalloc("FIB_TAILQ_ENTRY", sizeof(*te), 0);
+	if (te == NULL) {
+		RTE_LOG(ERR, LPM,
+			"Can not allocate tailq entry for FIB %s\n", name);
+		rte_errno = ENOMEM;
+		goto exit;
+	}
+
+	/* Allocate memory to store the FIB data structures. */
+	fib = rte_zmalloc_socket(mem_name,
+		sizeof(struct rte_fib),	RTE_CACHE_LINE_SIZE, socket_id);
+	if (fib == NULL) {
+		RTE_LOG(ERR, LPM, "FIB %s memory allocation failed\n", name);
+		rte_errno = ENOMEM;
+		goto free_te;
+	}
+
+	rte_strlcpy(fib->name, name, sizeof(fib->name));
+	fib->rib = rib;
+	fib->type = conf->type;
+	fib->def_nh = conf->default_nh;
+	ret = init_dataplane(fib, socket_id, conf);
+	if (ret < 0) {
+		RTE_LOG(ERR, LPM,
+			"FIB dataplane struct %s memory allocation failed "
+			"with err %d\n", name, ret);
+		rte_errno = -ret;
+		goto free_fib;
+	}
+
+	te->data = (void *)fib;
+	TAILQ_INSERT_TAIL(fib_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	return fib;
+
+free_fib:
+	rte_free(fib);
+free_te:
+	rte_free(te);
+exit:
+	rte_mcfg_tailq_write_unlock();
+	rte_rib_free(rib);
+
+	return NULL;
+}
+
+struct rte_fib *
+rte_fib_find_existing(const char *name)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_fib_list *fib_list;
+
+	fib_list = RTE_TAILQ_CAST(rte_fib_tailq.head, rte_fib_list);
+
+	rte_mcfg_tailq_read_lock();
+	TAILQ_FOREACH(te, fib_list, next) {
+		fib = (struct rte_fib *) te->data;
+		if (strncmp(name, fib->name, RTE_FIB_NAMESIZE) == 0)
+			break;
+	}
+	rte_mcfg_tailq_read_unlock();
+
+	if (te == NULL) {
+		rte_errno = ENOENT;
+		return NULL;
+	}
+
+	return fib;
+}
+
+static void
+free_dataplane(struct rte_fib *fib)
+{
+	switch (fib->type) {
+	case RTE_FIB_DUMMY:
+		return;
+	default:
+		return;
+	}
+}
+
+void
+rte_fib_free(struct rte_fib *fib)
+{
+	struct rte_tailq_entry *te;
+	struct rte_fib_list *fib_list;
+
+	if (fib == NULL)
+		return;
+
+	fib_list = RTE_TAILQ_CAST(rte_fib_tailq.head, rte_fib_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* find our tailq entry */
+	TAILQ_FOREACH(te, fib_list, next) {
+		if (te->data == (void *)fib)
+			break;
+	}
+	if (te != NULL)
+		TAILQ_REMOVE(fib_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	free_dataplane(fib);
+	rte_rib_free(fib->rib);
+	rte_free(fib);
+	rte_free(te);
+}
+
+void *
+rte_fib_get_dp(struct rte_fib *fib)
+{
+	return (fib == NULL) ? NULL : fib->dp;
+}
+
+struct rte_rib *
+rte_fib_get_rib(struct rte_fib *fib)
+{
+	return (fib == NULL) ? NULL : fib->rib;
+}
diff --git a/lib/librte_fib/rte_fib.h b/lib/librte_fib/rte_fib.h
new file mode 100644
index 0000000..096cc92
--- /dev/null
+++ b/lib/librte_fib/rte_fib.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _RTE_FIB_H_
+#define _RTE_FIB_H_
+
+/**
+ * @file
+ * FIB (Forwarding information base) implementation
+ * for IPv4 Longest Prefix Match
+ */
+
+#include <rte_compat.h>
+
+struct rte_fib;
+
+/** Maximum depth value possible for IPv4 FIB. */
+#define RTE_FIB_MAXDEPTH	32
+
+/** Type of FIB struct */
+enum rte_fib_type {
+	RTE_FIB_DUMMY,		/**< RIB tree based FIB */
+	RTE_FIB_TYPE_MAX
+};
+
+/** Modify FIB function */
+typedef int (*rte_fib_modify_fn_t)(struct rte_fib *fib, uint32_t ip,
+	uint8_t depth, uint64_t next_hop, int op);
+/** FIB bulk lookup function */
+typedef void (*rte_fib_lookup_fn_t)(void *fib, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n);
+
+enum rte_fib_op {
+	RTE_FIB_ADD,
+	RTE_FIB_DEL,
+};
+
+/** FIB configuration structure */
+struct rte_fib_conf {
+	enum rte_fib_type type; /**< Type of FIB struct */
+	/** Default value returned on lookup if there is no route */
+	uint64_t default_nh;
+	int	max_routes;
+};
+
+/**
+ * Create FIB
+ *
+ * @param name
+ *  FIB name
+ * @param socket_id
+ *  NUMA socket ID for FIB table memory allocation
+ * @param conf
+ *  Structure containing the configuration
+ * @return
+ *  Handle to the FIB object on success
+ *  NULL otherwise with rte_errno set to an appropriate values.
+ */
+__rte_experimental
+struct rte_fib *
+rte_fib_create(const char *name, int socket_id, struct rte_fib_conf *conf);
+
+/**
+ * Find an existing FIB object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the fib object as passed to rte_fib_create()
+ * @return
+ *  Pointer to fib object or NULL if object not found with rte_errno
+ *  set appropriately. Possible rte_errno values include:
+ *   - ENOENT - required entry not available to return.
+ */
+__rte_experimental
+struct rte_fib *
+rte_fib_find_existing(const char *name);
+
+/**
+ * Free an FIB object.
+ *
+ * @param fib
+ *   FIB object handle
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_fib_free(struct rte_fib *fib);
+
+/**
+ * Add a route to the FIB.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param ip
+ *   IPv4 prefix address to be added to the FIB
+ * @param depth
+ *   Prefix length
+ * @param next_hop
+ *   Next hop to be added to the FIB
+ * @return
+ *   0 on success, negative value otherwise
+ */
+__rte_experimental
+int
+rte_fib_add(struct rte_fib *fib, uint32_t ip, uint8_t depth, uint64_t next_hop);
+
+/**
+ * Delete a rule from the FIB.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param ip
+ *   IPv4 prefix address to be deleted from the FIB
+ * @param depth
+ *   Prefix length
+ * @return
+ *   0 on success, negative value otherwise
+ */
+__rte_experimental
+int
+rte_fib_delete(struct rte_fib *fib, uint32_t ip, uint8_t depth);
+
+/**
+ * Lookup multiple IP addresses in the FIB.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param ips
+ *   Array of IPs to be looked up in the FIB
+ * @param next_hops
+ *   Next hop of the most specific rule found for IP.
+ *   This is an array of eight byte values.
+ *   If the lookup for the given IP failed, then corresponding element would
+ *   contain default nexthop value configured for a FIB.
+ * @param n
+ *   Number of elements in ips (and next_hops) array to lookup.
+ *  @return
+ *   -EINVAL for incorrect arguments, otherwise 0
+ */
+__rte_experimental
+int
+rte_fib_lookup_bulk(struct rte_fib *fib, uint32_t *ips,
+		uint64_t *next_hops, int n);
+
+/**
+ * Get pointer to the dataplane specific struct
+ *
+ * @param fib
+ *   FIB object handle
+ * @return
+ *   Pointer on the dataplane struct on success
+ *   NULL othervise
+ */
+__rte_experimental
+void *
+rte_fib_get_dp(struct rte_fib *fib);
+
+/**
+ * Get pointer to the RIB
+ *
+ * @param fib
+ *   FIB object handle
+ * @return
+ *   Pointer on the RIB on success
+ *   NULL othervise
+ */
+__rte_experimental
+struct rte_rib *
+rte_fib_get_rib(struct rte_fib *fib);
+
+#endif /* _RTE_FIB_H_ */
diff --git a/lib/librte_fib/rte_fib_version.map b/lib/librte_fib/rte_fib_version.map
new file mode 100644
index 0000000..776195f
--- /dev/null
+++ b/lib/librte_fib/rte_fib_version.map
@@ -0,0 +1,14 @@
+EXPERIMENTAL {
+	global:
+
+	rte_fib_add;
+	rte_fib_create;
+	rte_fib_delete;
+	rte_fib_find_existing;
+	rte_fib_free;
+	rte_fib_lookup_bulk;
+	rte_fib_get_dp;
+	rte_fib_get_rib;
+
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index d7f2a04..9700433 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -25,6 +25,8 @@ libraries = [
 	'rcu', 'rib', 'reorder', 'sched', 'security', 'stack', 'vhost',
 	# ipsec lib depends on net, crypto and security
 	'ipsec',
+	#fib lib depends on rib
+	'fib',
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 4517874..7ce87e7 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -45,6 +45,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_PDUMP)          += -lrte_pdump
 _LDLIBS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR)    += -lrte_distributor
 _LDLIBS-$(CONFIG_RTE_LIBRTE_IP_FRAG)        += -lrte_ip_frag
 _LDLIBS-$(CONFIG_RTE_LIBRTE_METER)          += -lrte_meter
+_LDLIBS-$(CONFIG_RTE_LIBRTE_FIB)            += -lrte_fib
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RIB)            += -lrte_rib
 _LDLIBS-$(CONFIG_RTE_LIBRTE_LPM)            += -lrte_lpm
 _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL)            += -lrte_acl
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 06/12] fib: add FIB ipv6 support
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
                     ` (6 preceding siblings ...)
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 05/12] fib: add FIB library Vladimir Medvedkin
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 07/12] fib: add DIR24-8 dataplane algorithm Vladimir Medvedkin
                     ` (5 subsequent siblings)
  13 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

Add FIB library support for IPv6.
It implements a dataplane structures and algorithms designed for
fast IPv6 longest prefix match.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 lib/librte_fib/Makefile            |   4 +-
 lib/librte_fib/meson.build         |   4 +-
 lib/librte_fib/rte_fib6.c          | 308 +++++++++++++++++++++++++++++++++++++
 lib/librte_fib/rte_fib6.h          | 179 +++++++++++++++++++++
 lib/librte_fib/rte_fib_version.map |   9 ++
 5 files changed, 500 insertions(+), 4 deletions(-)
 create mode 100644 lib/librte_fib/rte_fib6.c
 create mode 100644 lib/librte_fib/rte_fib6.h

diff --git a/lib/librte_fib/Makefile b/lib/librte_fib/Makefile
index 7362f68..0bdf55b 100644
--- a/lib/librte_fib/Makefile
+++ b/lib/librte_fib/Makefile
@@ -17,9 +17,9 @@ EXPORT_MAP := rte_fib_version.map
 LIBABIVER := 1
 
 # all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c rte_fib6.c
 
 # install this header file
-SYMLINK-$(CONFIG_RTE_LIBRTE_FIB)-include := rte_fib.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_FIB)-include := rte_fib.h rte_fib6.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_fib/meson.build b/lib/librte_fib/meson.build
index 6b72360..32dfdec 100644
--- a/lib/librte_fib/meson.build
+++ b/lib/librte_fib/meson.build
@@ -3,6 +3,6 @@
 # Copyright(c) 2019 Intel Corporation
 
 allow_experimental_apis = true
-sources = files('rte_fib.c')
-headers = files('rte_fib.h')
+sources = files('rte_fib.c', 'rte_fib6.c')
+headers = files('rte_fib.h', 'rte_fib6.h')
 deps += ['rib']
diff --git a/lib/librte_fib/rte_fib6.c b/lib/librte_fib/rte_fib6.c
new file mode 100644
index 0000000..9f00a80
--- /dev/null
+++ b/lib/librte_fib/rte_fib6.c
@@ -0,0 +1,308 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_tailq.h>
+#include <rte_errno.h>
+#include <rte_rwlock.h>
+#include <rte_malloc.h>
+#include <rte_string_fns.h>
+
+#include <rte_rib6.h>
+#include <rte_fib6.h>
+
+TAILQ_HEAD(rte_fib6_list, rte_tailq_entry);
+static struct rte_tailq_elem rte_fib6_tailq = {
+	.name = "RTE_FIB6",
+};
+EAL_REGISTER_TAILQ(rte_fib6_tailq)
+
+/* Maximum length of a FIB name. */
+#define FIB6_NAMESIZE	64
+
+#if defined(RTE_LIBRTE_FIB_DEBUG)
+#define FIB6_RETURN_IF_TRUE(cond, retval) do {		\
+	if (cond)					\
+		return retval;				\
+} while (0)
+#else
+#define FIB6_RETURN_IF_TRUE(cond, retval)
+#endif
+
+struct rte_fib6 {
+	char			name[FIB6_NAMESIZE];
+	enum rte_fib6_type	type;	/**< Type of FIB struct */
+	struct rte_rib6		*rib;	/**< RIB helper datastruct */
+	void			*dp;	/**< pointer to the dataplane struct*/
+	rte_fib6_lookup_fn_t	lookup;	/**< fib lookup function */
+	rte_fib6_modify_fn_t	modify; /**< modify fib datastruct */
+	uint64_t		def_nh;
+};
+
+static void
+dummy_lookup(void *fib_p, uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],
+	uint64_t *next_hops, const unsigned int n)
+{
+	unsigned int i;
+	struct rte_fib6 *fib = fib_p;
+	struct rte_rib6_node *node;
+
+	for (i = 0; i < n; i++) {
+		node = rte_rib6_lookup(fib->rib, ips[i]);
+		if (node != NULL)
+			rte_rib6_get_nh(node, &next_hops[i]);
+		else
+			next_hops[i] = fib->def_nh;
+	}
+}
+
+static int
+dummy_modify(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, uint64_t next_hop, int op)
+{
+	struct rte_rib6_node *node;
+	if ((fib == NULL) || (depth > RTE_FIB6_MAXDEPTH))
+		return -EINVAL;
+
+	node = rte_rib6_lookup_exact(fib->rib, ip, depth);
+
+	switch (op) {
+	case RTE_FIB6_ADD:
+		if (node == NULL)
+			node = rte_rib6_insert(fib->rib, ip, depth);
+		if (node == NULL)
+			return -rte_errno;
+		return rte_rib6_set_nh(node, next_hop);
+	case RTE_FIB6_DEL:
+		if (node == NULL)
+			return -ENOENT;
+		rte_rib6_remove(fib->rib, ip, depth);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int
+init_dataplane(struct rte_fib6 *fib, __rte_unused int socket_id,
+	struct rte_fib6_conf *conf)
+{
+	switch (conf->type) {
+	case RTE_FIB6_DUMMY:
+		fib->dp = fib;
+		fib->lookup = dummy_lookup;
+		fib->modify = dummy_modify;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int
+rte_fib6_add(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, uint64_t next_hop)
+{
+	if ((fib == NULL) || (ip == NULL) || (fib->modify == NULL) ||
+			(depth > RTE_FIB6_MAXDEPTH))
+		return -EINVAL;
+	return fib->modify(fib, ip, depth, next_hop, RTE_FIB6_ADD);
+}
+
+int
+rte_fib6_delete(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth)
+{
+	if ((fib == NULL) || (ip == NULL) || (fib->modify == NULL) ||
+			(depth > RTE_FIB6_MAXDEPTH))
+		return -EINVAL;
+	return fib->modify(fib, ip, depth, 0, RTE_FIB6_DEL);
+}
+
+int
+rte_fib6_lookup_bulk(struct rte_fib6 *fib,
+	uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],
+	uint64_t *next_hops, int n)
+{
+	FIB6_RETURN_IF_TRUE((fib == NULL) || (ips == NULL) ||
+		(next_hops == NULL) || (fib->lookup == NULL), -EINVAL);
+	fib->lookup(fib->dp, ips, next_hops, n);
+	return 0;
+}
+
+struct rte_fib6 *
+rte_fib6_create(const char *name, int socket_id, struct rte_fib6_conf *conf)
+{
+	char mem_name[FIB6_NAMESIZE];
+	int ret;
+	struct rte_fib6 *fib = NULL;
+	struct rte_rib6 *rib = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_fib6_list *fib_list;
+	struct rte_rib6_conf rib_conf;
+
+	/* Check user arguments. */
+	if ((name == NULL) || (conf == NULL) || (conf->max_routes < 0) ||
+			(conf->type >= RTE_FIB6_TYPE_MAX)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	rib_conf.ext_sz = 0;
+	rib_conf.max_nodes = conf->max_routes * 2;
+
+	rib = rte_rib6_create(name, socket_id, &rib_conf);
+	if (rib == NULL) {
+		RTE_LOG(ERR, LPM,
+			"Can not allocate RIB %s\n", name);
+		return NULL;
+	}
+
+	snprintf(mem_name, sizeof(mem_name), "FIB6_%s", name);
+	fib_list = RTE_TAILQ_CAST(rte_fib6_tailq.head, rte_fib6_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* guarantee there's no existing */
+	TAILQ_FOREACH(te, fib_list, next) {
+		fib = (struct rte_fib6 *)te->data;
+		if (strncmp(name, fib->name, FIB6_NAMESIZE) == 0)
+			break;
+	}
+	fib = NULL;
+	if (te != NULL) {
+		rte_errno = EEXIST;
+		goto exit;
+	}
+
+	/* allocate tailq entry */
+	te = rte_zmalloc("FIB_TAILQ_ENTRY", sizeof(*te), 0);
+	if (te == NULL) {
+		RTE_LOG(ERR, LPM,
+			"Can not allocate tailq entry for FIB %s\n", name);
+		rte_errno = ENOMEM;
+		goto exit;
+	}
+
+	/* Allocate memory to store the FIB data structures. */
+	fib = rte_zmalloc_socket(mem_name,
+		sizeof(struct rte_fib6), RTE_CACHE_LINE_SIZE, socket_id);
+	if (fib == NULL) {
+		RTE_LOG(ERR, LPM, "FIB %s memory allocation failed\n", name);
+		rte_errno = ENOMEM;
+		goto free_te;
+	}
+
+	rte_strlcpy(fib->name, name, sizeof(fib->name));
+	fib->rib = rib;
+	fib->type = conf->type;
+	fib->def_nh = conf->default_nh;
+	ret = init_dataplane(fib, socket_id, conf);
+	if (ret < 0) {
+		RTE_LOG(ERR, LPM,
+			"FIB dataplane struct %s memory allocation failed\n",
+			name);
+		rte_errno = -ret;
+		goto free_fib;
+	}
+
+	te->data = (void *)fib;
+	TAILQ_INSERT_TAIL(fib_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	return fib;
+
+free_fib:
+	rte_free(fib);
+free_te:
+	rte_free(te);
+exit:
+	rte_mcfg_tailq_write_unlock();
+	rte_rib6_free(rib);
+
+	return NULL;
+}
+
+struct rte_fib6 *
+rte_fib6_find_existing(const char *name)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_fib6_list *fib_list;
+
+	fib_list = RTE_TAILQ_CAST(rte_fib6_tailq.head, rte_fib6_list);
+
+	rte_mcfg_tailq_read_lock();
+	TAILQ_FOREACH(te, fib_list, next) {
+		fib = (struct rte_fib6 *) te->data;
+		if (strncmp(name, fib->name, FIB6_NAMESIZE) == 0)
+			break;
+	}
+	rte_mcfg_tailq_read_unlock();
+
+	if (te == NULL) {
+		rte_errno = ENOENT;
+		return NULL;
+	}
+
+	return fib;
+}
+
+static void
+free_dataplane(struct rte_fib6 *fib)
+{
+	switch (fib->type) {
+	case RTE_FIB6_DUMMY:
+		return;
+	default:
+		return;
+	}
+}
+
+void
+rte_fib6_free(struct rte_fib6 *fib)
+{
+	struct rte_tailq_entry *te;
+	struct rte_fib6_list *fib_list;
+
+	if (fib == NULL)
+		return;
+
+	fib_list = RTE_TAILQ_CAST(rte_fib6_tailq.head, rte_fib6_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	/* find our tailq entry */
+	TAILQ_FOREACH(te, fib_list, next) {
+		if (te->data == (void *)fib)
+			break;
+	}
+	if (te != NULL)
+		TAILQ_REMOVE(fib_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	free_dataplane(fib);
+	rte_rib6_free(fib->rib);
+	rte_free(fib);
+	rte_free(te);
+}
+
+void *
+rte_fib6_get_dp(struct rte_fib6 *fib)
+{
+	return (fib == NULL) ? NULL : fib->dp;
+}
+
+struct rte_rib6 *
+rte_fib6_get_rib(struct rte_fib6 *fib)
+{
+	return (fib == NULL) ? NULL : fib->rib;
+}
+
diff --git a/lib/librte_fib/rte_fib6.h b/lib/librte_fib/rte_fib6.h
new file mode 100644
index 0000000..3322123
--- /dev/null
+++ b/lib/librte_fib/rte_fib6.h
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _RTE_FIB6_H_
+#define _RTE_FIB6_H_
+
+/**
+ * @file
+ * FIB (Forwarding information base) implementation
+ * for IPv6 Longest Prefix Match
+ */
+
+#include <rte_compat.h>
+
+#define RTE_FIB6_IPV6_ADDR_SIZE		16
+/** Maximum depth value possible for IPv6 FIB. */
+#define RTE_FIB6_MAXDEPTH       128
+
+struct rte_fib6;
+
+/** Type of FIB struct */
+enum rte_fib6_type {
+	RTE_FIB6_DUMMY,		/**< RIB6 tree based FIB */
+	RTE_FIB6_TYPE_MAX
+};
+
+/** Modify FIB function */
+typedef int (*rte_fib6_modify_fn_t)(struct rte_fib6 *fib,
+	const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE], uint8_t depth,
+	uint64_t next_hop, int op);
+/** FIB bulk lookup function */
+typedef void (*rte_fib6_lookup_fn_t)(void *fib,
+	uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],
+	uint64_t *next_hops, const unsigned int n);
+
+enum rte_fib6_op {
+	RTE_FIB6_ADD,
+	RTE_FIB6_DEL,
+};
+
+/** FIB configuration structure */
+struct rte_fib6_conf {
+	enum rte_fib6_type type; /**< Type of FIB struct */
+	/** Default value returned on lookup if there is no route */
+	uint64_t default_nh;
+	int	max_routes;
+};
+
+/**
+ * Create FIB
+ *
+ * @param name
+ *  FIB name
+ * @param socket_id
+ *  NUMA socket ID for FIB table memory allocation
+ * @param conf
+ *  Structure containing the configuration
+ * @return
+ *  Handle to FIB object on success
+ *  NULL otherwise with rte_errno set to an appropriate values.
+ */
+__rte_experimental
+struct rte_fib6 *
+rte_fib6_create(const char *name, int socket_id, struct rte_fib6_conf *conf);
+
+/**
+ * Find an existing FIB object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the fib object as passed to rte_fib6_create()
+ * @return
+ *  Pointer to fib object or NULL if object not found with rte_errno
+ *  set appropriately. Possible rte_errno values include:
+ *   - ENOENT - required entry not available to return.
+ */
+__rte_experimental
+struct rte_fib6 *
+rte_fib6_find_existing(const char *name);
+
+/**
+ * Free an FIB object.
+ *
+ * @param fib
+ *   FIB object handle
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_fib6_free(struct rte_fib6 *fib);
+
+/**
+ * Add a route to the FIB.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param ip
+ *   IPv6 prefix address to be added to the FIB
+ * @param depth
+ *   Prefix length
+ * @param next_hop
+ *   Next hop to be added to the FIB
+ * @return
+ *   0 on success, negative value otherwise
+ */
+__rte_experimental
+int
+rte_fib6_add(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, uint64_t next_hop);
+
+/**
+ * Delete a rule from the FIB.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param ip
+ *   IPv6 prefix address to be deleted from the FIB
+ * @param depth
+ *   Prefix length
+ * @return
+ *   0 on success, negative value otherwise
+ */
+__rte_experimental
+int
+rte_fib6_delete(struct rte_fib6 *fib,
+	const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE], uint8_t depth);
+
+/**
+ * Lookup multiple IP addresses in the FIB.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param ips
+ *   Array of IPv6s to be looked up in the FIB
+ * @param next_hops
+ *   Next hop of the most specific rule found for IP.
+ *   This is an array of eight byte values.
+ *   If the lookup for the given IP failed, then corresponding element would
+ *   contain default nexthop value configured for a FIB.
+ * @param n
+ *   Number of elements in ips (and next_hops) array to lookup.
+ *  @return
+ *   -EINVAL for incorrect arguments, otherwise 0
+ */
+__rte_experimental
+int
+rte_fib6_lookup_bulk(struct rte_fib6 *fib,
+	uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],
+	uint64_t *next_hops, int n);
+
+/**
+ * Get pointer to the dataplane specific struct
+ *
+ * @param fib
+ *   FIB6 object handle
+ * @return
+ *   Pointer on the dataplane struct on success
+ *   NULL othervise
+ */
+__rte_experimental
+void *
+rte_fib6_get_dp(struct rte_fib6 *fib);
+
+/**
+ * Get pointer to the RIB6
+ *
+ * @param fib
+ *   FIB object handle
+ * @return
+ *   Pointer on the RIB6 on success
+ *   NULL othervise
+ */
+__rte_experimental
+struct rte_rib6 *
+rte_fib6_get_rib(struct rte_fib6 *fib);
+
+#endif /* _RTE_FIB6_H_ */
diff --git a/lib/librte_fib/rte_fib_version.map b/lib/librte_fib/rte_fib_version.map
index 776195f..9527417 100644
--- a/lib/librte_fib/rte_fib_version.map
+++ b/lib/librte_fib/rte_fib_version.map
@@ -10,5 +10,14 @@ EXPERIMENTAL {
 	rte_fib_get_dp;
 	rte_fib_get_rib;
 
+	rte_fib6_add;
+	rte_fib6_create;
+	rte_fib6_delete;
+	rte_fib6_find_existing;
+	rte_fib6_free;
+	rte_fib6_lookup_bulk;
+	rte_fib6_get_dp;
+	rte_fib6_get_rib;
+
 	local: *;
 };
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 07/12] fib: add DIR24-8 dataplane algorithm
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
                     ` (7 preceding siblings ...)
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 06/12] fib: add FIB ipv6 support Vladimir Medvedkin
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 08/12] fib: add dataplane algorithm for ipv6 Vladimir Medvedkin
                     ` (4 subsequent siblings)
  13 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

Add fib implementation for DIR24_8 algorithm for ipv4.
Implementation is similar to current LPM implementation but has
few enhancements:
faster control plabe operations
more bits for userdata in table entries
configurable userdata size

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 lib/librte_fib/Makefile    |   2 +-
 lib/librte_fib/dir24_8.c   | 737 +++++++++++++++++++++++++++++++++++++++++++++
 lib/librte_fib/dir24_8.h   |  36 +++
 lib/librte_fib/meson.build |   2 +-
 lib/librte_fib/rte_fib.c   |  14 +
 lib/librte_fib/rte_fib.h   |  17 +-
 6 files changed, 805 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_fib/dir24_8.c
 create mode 100644 lib/librte_fib/dir24_8.h

diff --git a/lib/librte_fib/Makefile b/lib/librte_fib/Makefile
index 0bdf55b..67fde9a 100644
--- a/lib/librte_fib/Makefile
+++ b/lib/librte_fib/Makefile
@@ -17,7 +17,7 @@ EXPORT_MAP := rte_fib_version.map
 LIBABIVER := 1
 
 # all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c rte_fib6.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c rte_fib6.c dir24_8.c
 
 # install this header file
 SYMLINK-$(CONFIG_RTE_LIBRTE_FIB)-include := rte_fib.h rte_fib6.h
diff --git a/lib/librte_fib/dir24_8.c b/lib/librte_fib/dir24_8.c
new file mode 100644
index 0000000..c9dce3c
--- /dev/null
+++ b/lib/librte_fib/dir24_8.c
@@ -0,0 +1,737 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <rte_debug.h>
+#include <rte_malloc.h>
+#include <rte_prefetch.h>
+#include <rte_errno.h>
+#include <rte_memory.h>
+#include <rte_branch_prediction.h>
+
+#include <rte_fib.h>
+#include <rte_rib.h>
+#include "dir24_8.h"
+
+#define DIR24_8_NAMESIZE	64
+
+#define DIR24_8_TBL24_NUM_ENT		(1 << 24)
+#define DIR24_8_TBL8_GRP_NUM_ENT	256U
+#define DIR24_8_EXT_ENT			1
+#define DIR24_8_TBL24_MASK		0xffffff00
+
+#define BITMAP_SLAB_BIT_SIZE_LOG2	6
+#define BITMAP_SLAB_BIT_SIZE		(1 << BITMAP_SLAB_BIT_SIZE_LOG2)
+#define BITMAP_SLAB_BITMASK		(BITMAP_SLAB_BIT_SIZE - 1)
+
+struct dir24_8_tbl {
+	uint32_t	number_tbl8s;	/**< Total number of tbl8s */
+	uint32_t	rsvd_tbl8s;	/**< Number of reserved tbl8s */
+	uint32_t	cur_tbl8s;	/**< Current number of tbl8s */
+	enum rte_fib_dir24_8_nh_sz	nh_sz;	/**< Size of nexthop entry */
+	uint64_t	def_nh;		/**< Default next hop */
+	uint64_t	*tbl8;		/**< tbl8 table. */
+	uint64_t	*tbl8_idxes;	/**< bitmap containing free tbl8 idxes*/
+	/* tbl24 table. */
+	__extension__ uint64_t	tbl24[0] __rte_cache_aligned;
+};
+
+#define ROUNDUP(x, y)	 RTE_ALIGN_CEIL(x, (1 << (32 - y)))
+
+enum lookup_type {
+	MACRO,
+	INLINE,
+	UNI
+};
+enum lookup_type test_lookup = MACRO;
+
+static inline void *
+get_tbl24_p(struct dir24_8_tbl *dp, uint32_t ip, uint8_t nh_sz)
+{
+	return (void *)&((uint8_t *)dp->tbl24)[(ip &
+		DIR24_8_TBL24_MASK) >> (8 - nh_sz)];
+}
+
+static inline  uint8_t
+bits_in_nh(uint8_t nh_sz)
+{
+	return 8 * (1 << nh_sz);
+}
+
+static inline uint64_t
+get_max_nh(uint8_t nh_sz)
+{
+	return ((1ULL << (bits_in_nh(nh_sz) - 1)) - 1);
+}
+
+static  inline uint32_t
+get_tbl24_idx(uint32_t ip)
+{
+	return ip >> 8;
+}
+
+static  inline uint32_t
+get_tbl8_idx(uint32_t res, uint32_t ip)
+{
+	return (res >> 1) * DIR24_8_TBL8_GRP_NUM_ENT + (uint8_t)ip;
+}
+
+static inline uint64_t
+lookup_msk(uint8_t nh_sz)
+{
+	return ((1ULL << ((1 << (nh_sz + 3)) - 1)) << 1) - 1;
+}
+
+static inline uint8_t
+get_psd_idx(uint32_t val, uint8_t nh_sz)
+{
+	return val & ((1 << (3 - nh_sz)) - 1);
+}
+
+static inline uint32_t
+get_tbl_idx(uint32_t val, uint8_t nh_sz)
+{
+	return val >> (3 - nh_sz);
+}
+
+static inline uint64_t
+get_tbl24(struct dir24_8_tbl *dp, uint32_t ip, uint8_t nh_sz)
+{
+	return ((dp->tbl24[get_tbl_idx(get_tbl24_idx(ip), nh_sz)] >>
+		(get_psd_idx(get_tbl24_idx(ip), nh_sz) *
+		bits_in_nh(nh_sz))) & lookup_msk(nh_sz));
+}
+
+static inline uint64_t
+get_tbl8(struct dir24_8_tbl *dp, uint32_t res, uint32_t ip, uint8_t nh_sz)
+{
+	return ((dp->tbl8[get_tbl_idx(get_tbl8_idx(res, ip), nh_sz)] >>
+		(get_psd_idx(get_tbl8_idx(res, ip), nh_sz) *
+		bits_in_nh(nh_sz))) & lookup_msk(nh_sz));
+}
+
+static inline int
+is_entry_extended(uint64_t ent)
+{
+	return (ent & DIR24_8_EXT_ENT) == DIR24_8_EXT_ENT;
+}
+
+#define LOOKUP_FUNC(suffix, type, bulk_prefetch, nh_sz)			\
+static void dir24_8_lookup_bulk_##suffix(void *p, const uint32_t *ips,	\
+	uint64_t *next_hops, const unsigned int n)			\
+{									\
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;		\
+	uint64_t tmp;							\
+	uint32_t i;							\
+	uint32_t prefetch_offset =					\
+		RTE_MIN((unsigned int)bulk_prefetch, n);		\
+									\
+	for (i = 0; i < prefetch_offset; i++)				\
+		rte_prefetch0(get_tbl24_p(dp, ips[i], nh_sz));		\
+	for (i = 0; i < (n - prefetch_offset); i++) {			\
+		rte_prefetch0(get_tbl24_p(dp,				\
+			ips[i + prefetch_offset], nh_sz));		\
+		tmp = ((type *)dp->tbl24)[ips[i] >> 8];			\
+		if (unlikely(is_entry_extended(tmp)))			\
+			tmp = ((type *)dp->tbl8)[(uint8_t)ips[i] +	\
+				((tmp >> 1) * DIR24_8_TBL8_GRP_NUM_ENT)]; \
+		next_hops[i] = tmp >> 1;				\
+	}								\
+	for (; i < n; i++) {						\
+		tmp = ((type *)dp->tbl24)[ips[i] >> 8];			\
+		if (unlikely(is_entry_extended(tmp)))			\
+			tmp = ((type *)dp->tbl8)[(uint8_t)ips[i] +	\
+				((tmp >> 1) * DIR24_8_TBL8_GRP_NUM_ENT)]; \
+		next_hops[i] = tmp >> 1;				\
+	}								\
+}									\
+
+LOOKUP_FUNC(1b, uint8_t, 5, 0)
+LOOKUP_FUNC(2b, uint16_t, 6, 1)
+LOOKUP_FUNC(4b, uint32_t, 15, 2)
+LOOKUP_FUNC(8b, uint64_t, 12, 3)
+
+static inline void
+dir24_8_lookup_bulk(struct dir24_8_tbl *dp, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n, uint8_t nh_sz)
+{
+	uint64_t tmp;
+	uint32_t i;
+	uint32_t prefetch_offset = RTE_MIN(15U, n);
+
+	for (i = 0; i < prefetch_offset; i++)
+		rte_prefetch0(get_tbl24_p(dp, ips[i], nh_sz));
+	for (i = 0; i < (n - prefetch_offset); i++) {
+		rte_prefetch0(get_tbl24_p(dp, ips[i + prefetch_offset],
+			nh_sz));
+		tmp = get_tbl24(dp, ips[i], nh_sz);
+		if (unlikely(is_entry_extended(tmp)))
+			tmp = get_tbl8(dp, tmp, ips[i], nh_sz);
+
+		next_hops[i] = tmp >> 1;
+	}
+	for (; i < n; i++) {
+		tmp = get_tbl24(dp, ips[i], nh_sz);
+		if (unlikely(is_entry_extended(tmp)))
+			tmp = get_tbl8(dp, tmp, ips[i], nh_sz);
+
+		next_hops[i] = tmp >> 1;
+	}
+}
+
+static void
+dir24_8_lookup_bulk_0(void *p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	dir24_8_lookup_bulk(dp, ips, next_hops, n, 0);
+}
+
+static void
+dir24_8_lookup_bulk_1(void *p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	dir24_8_lookup_bulk(dp, ips, next_hops, n, 1);
+}
+
+static void
+dir24_8_lookup_bulk_2(void *p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	dir24_8_lookup_bulk(dp, ips, next_hops, n, 2);
+}
+
+static void
+dir24_8_lookup_bulk_3(void *p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	dir24_8_lookup_bulk(dp, ips, next_hops, n, 3);
+}
+
+static void
+dir24_8_lookup_bulk_uni(void *p, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+	uint64_t tmp;
+	uint32_t i;
+	uint32_t prefetch_offset = RTE_MIN(15U, n);
+	uint8_t nh_sz = dp->nh_sz;
+
+	for (i = 0; i < prefetch_offset; i++)
+		rte_prefetch0(get_tbl24_p(dp, ips[i], nh_sz));
+	for (i = 0; i < (n - prefetch_offset); i++) {
+		rte_prefetch0(get_tbl24_p(dp, ips[i + prefetch_offset],
+			nh_sz));
+		tmp = get_tbl24(dp, ips[i], nh_sz);
+		if (unlikely(is_entry_extended(tmp)))
+			tmp = get_tbl8(dp, tmp, ips[i], nh_sz);
+
+		next_hops[i] = tmp >> 1;
+	}
+	for (; i < n; i++) {
+		tmp = get_tbl24(dp, ips[i], nh_sz);
+		if (unlikely(is_entry_extended(tmp)))
+			tmp = get_tbl8(dp, tmp, ips[i], nh_sz);
+
+		next_hops[i] = tmp >> 1;
+	}
+}
+
+rte_fib_lookup_fn_t
+dir24_8_get_lookup_fn(struct rte_fib_conf *fib_conf)
+{
+	enum rte_fib_dir24_8_nh_sz nh_sz = fib_conf->dir24_8.nh_sz;
+
+	if (test_lookup == MACRO) {
+		switch (nh_sz) {
+		case RTE_FIB_DIR24_8_1B:
+			return dir24_8_lookup_bulk_1b;
+		case RTE_FIB_DIR24_8_2B:
+			return dir24_8_lookup_bulk_2b;
+		case RTE_FIB_DIR24_8_4B:
+			return dir24_8_lookup_bulk_4b;
+		case RTE_FIB_DIR24_8_8B:
+			return dir24_8_lookup_bulk_8b;
+		}
+	} else if (test_lookup == INLINE) {
+		switch (nh_sz) {
+		case RTE_FIB_DIR24_8_1B:
+			return dir24_8_lookup_bulk_0;
+		case RTE_FIB_DIR24_8_2B:
+			return dir24_8_lookup_bulk_1;
+		case RTE_FIB_DIR24_8_4B:
+			return dir24_8_lookup_bulk_2;
+		case RTE_FIB_DIR24_8_8B:
+			return dir24_8_lookup_bulk_3;
+		}
+	} else
+		return dir24_8_lookup_bulk_uni;
+	return NULL;
+}
+
+static void
+write_to_fib(void *ptr, uint64_t val, enum rte_fib_dir24_8_nh_sz size, int n)
+{
+	int i;
+	uint8_t *ptr8 = (uint8_t *)ptr;
+	uint16_t *ptr16 = (uint16_t *)ptr;
+	uint32_t *ptr32 = (uint32_t *)ptr;
+	uint64_t *ptr64 = (uint64_t *)ptr;
+
+	switch (size) {
+	case RTE_FIB_DIR24_8_1B:
+		for (i = 0; i < n; i++)
+			ptr8[i] = (uint8_t)val;
+		break;
+	case RTE_FIB_DIR24_8_2B:
+		for (i = 0; i < n; i++)
+			ptr16[i] = (uint16_t)val;
+		break;
+	case RTE_FIB_DIR24_8_4B:
+		for (i = 0; i < n; i++)
+			ptr32[i] = (uint32_t)val;
+		break;
+	case RTE_FIB_DIR24_8_8B:
+		for (i = 0; i < n; i++)
+			ptr64[i] = (uint64_t)val;
+		break;
+	}
+}
+
+static int
+tbl8_get_idx(struct dir24_8_tbl *dp)
+{
+	uint32_t i;
+	int bit_idx;
+
+	for (i = 0; (i < (dp->number_tbl8s >> BITMAP_SLAB_BIT_SIZE_LOG2)) &&
+			(dp->tbl8_idxes[i] == UINT64_MAX); i++)
+		;
+	if (i < (dp->number_tbl8s >> BITMAP_SLAB_BIT_SIZE_LOG2)) {
+		bit_idx = __builtin_ctzll(~dp->tbl8_idxes[i]);
+		dp->tbl8_idxes[i] |= (1ULL << bit_idx);
+		return (i << BITMAP_SLAB_BIT_SIZE_LOG2) + bit_idx;
+	}
+	return -ENOSPC;
+}
+
+static inline void
+tbl8_free_idx(struct dir24_8_tbl *dp, int idx)
+{
+	dp->tbl8_idxes[idx >> BITMAP_SLAB_BIT_SIZE_LOG2] &=
+		~(1ULL << (idx & BITMAP_SLAB_BITMASK));
+}
+
+static int
+tbl8_alloc(struct dir24_8_tbl *dp, uint64_t nh)
+{
+	int64_t	tbl8_idx;
+	uint8_t	*tbl8_ptr;
+
+	tbl8_idx = tbl8_get_idx(dp);
+	if (tbl8_idx < 0)
+		return tbl8_idx;
+	tbl8_ptr = (uint8_t *)dp->tbl8 +
+		((tbl8_idx * DIR24_8_TBL8_GRP_NUM_ENT) <<
+		dp->nh_sz);
+	/*Init tbl8 entries with nexthop from tbl24*/
+	write_to_fib((void *)tbl8_ptr, nh|
+		DIR24_8_EXT_ENT, dp->nh_sz,
+		DIR24_8_TBL8_GRP_NUM_ENT);
+	dp->cur_tbl8s++;
+	return tbl8_idx;
+}
+
+static void
+tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx)
+{
+	uint32_t i;
+	uint64_t nh;
+	uint8_t *ptr8;
+	uint16_t *ptr16;
+	uint32_t *ptr32;
+	uint64_t *ptr64;
+
+	switch (dp->nh_sz) {
+	case RTE_FIB_DIR24_8_1B:
+		ptr8 = &((uint8_t *)dp->tbl8)[tbl8_idx *
+				DIR24_8_TBL8_GRP_NUM_ENT];
+		nh = *ptr8;
+		for (i = 1; i < DIR24_8_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr8[i])
+				return;
+		}
+		((uint8_t *)dp->tbl24)[ip >> 8] =
+			nh & ~DIR24_8_EXT_ENT;
+		for (i = 0; i < DIR24_8_TBL8_GRP_NUM_ENT; i++)
+			ptr8[i] = 0;
+		break;
+	case RTE_FIB_DIR24_8_2B:
+		ptr16 = &((uint16_t *)dp->tbl8)[tbl8_idx *
+				DIR24_8_TBL8_GRP_NUM_ENT];
+		nh = *ptr16;
+		for (i = 1; i < DIR24_8_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr16[i])
+				return;
+		}
+		((uint16_t *)dp->tbl24)[ip >> 8] =
+			nh & ~DIR24_8_EXT_ENT;
+		for (i = 0; i < DIR24_8_TBL8_GRP_NUM_ENT; i++)
+			ptr16[i] = 0;
+		break;
+	case RTE_FIB_DIR24_8_4B:
+		ptr32 = &((uint32_t *)dp->tbl8)[tbl8_idx *
+				DIR24_8_TBL8_GRP_NUM_ENT];
+		nh = *ptr32;
+		for (i = 1; i < DIR24_8_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr32[i])
+				return;
+		}
+		((uint32_t *)dp->tbl24)[ip >> 8] =
+			nh & ~DIR24_8_EXT_ENT;
+		for (i = 0; i < DIR24_8_TBL8_GRP_NUM_ENT; i++)
+			ptr32[i] = 0;
+		break;
+	case RTE_FIB_DIR24_8_8B:
+		ptr64 = &((uint64_t *)dp->tbl8)[tbl8_idx *
+				DIR24_8_TBL8_GRP_NUM_ENT];
+		nh = *ptr64;
+		for (i = 1; i < DIR24_8_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr64[i])
+				return;
+		}
+		((uint64_t *)dp->tbl24)[ip >> 8] =
+			nh & ~DIR24_8_EXT_ENT;
+		for (i = 0; i < DIR24_8_TBL8_GRP_NUM_ENT; i++)
+			ptr64[i] = 0;
+		break;
+	}
+	tbl8_free_idx(dp, tbl8_idx);
+	dp->cur_tbl8s--;
+}
+
+static int
+install_to_fib(struct dir24_8_tbl *dp, uint32_t ledge, uint32_t redge,
+	uint64_t next_hop)
+{
+	uint64_t	tbl24_tmp;
+	int	tbl8_idx;
+	int tmp_tbl8_idx;
+	uint8_t	*tbl8_ptr;
+	uint32_t len;
+
+	len = ((ledge == 0) && (redge == 0)) ? 1 << 24 :
+		((redge & DIR24_8_TBL24_MASK) - ROUNDUP(ledge, 24)) >> 8;
+
+	if (((ledge >> 8) != (redge >> 8)) || (len == 1 << 24)) {
+		if ((ROUNDUP(ledge, 24) - ledge) != 0) {
+			tbl24_tmp = get_tbl24(dp, ledge, dp->nh_sz);
+			if ((tbl24_tmp & DIR24_8_EXT_ENT) !=
+					DIR24_8_EXT_ENT) {
+				/**
+				 * Make sure there is space for two TBL8.
+				 * This is necessary when installing range that
+				 * needs tbl8 for ledge and redge.
+				 */
+				tbl8_idx = tbl8_alloc(dp, tbl24_tmp);
+				tmp_tbl8_idx = tbl8_get_idx(dp);
+				if (tbl8_idx < 0)
+					return -ENOSPC;
+				else if (tmp_tbl8_idx < 0) {
+					tbl8_free_idx(dp, tbl8_idx);
+					return -ENOSPC;
+				}
+				tbl8_free_idx(dp, tmp_tbl8_idx);
+				/*update dir24 entry with tbl8 index*/
+				write_to_fib(get_tbl24_p(dp, ledge,
+					dp->nh_sz), (tbl8_idx << 1)|
+					DIR24_8_EXT_ENT,
+					dp->nh_sz, 1);
+			} else
+				tbl8_idx = tbl24_tmp >> 1;
+			tbl8_ptr = (uint8_t *)dp->tbl8 +
+				(((tbl8_idx * DIR24_8_TBL8_GRP_NUM_ENT) +
+				(ledge & ~DIR24_8_TBL24_MASK)) <<
+				dp->nh_sz);
+			/*update tbl8 with new next hop*/
+			write_to_fib((void *)tbl8_ptr, (next_hop << 1)|
+				DIR24_8_EXT_ENT,
+				dp->nh_sz, ROUNDUP(ledge, 24) - ledge);
+			tbl8_recycle(dp, ledge, tbl8_idx);
+		}
+		write_to_fib(get_tbl24_p(dp, ROUNDUP(ledge, 24), dp->nh_sz),
+			next_hop << 1, dp->nh_sz, len);
+		if (redge & ~DIR24_8_TBL24_MASK) {
+			tbl24_tmp = get_tbl24(dp, redge, dp->nh_sz);
+			if ((tbl24_tmp & DIR24_8_EXT_ENT) !=
+					DIR24_8_EXT_ENT) {
+				tbl8_idx = tbl8_alloc(dp, tbl24_tmp);
+				if (tbl8_idx < 0)
+					return -ENOSPC;
+				/*update dir24 entry with tbl8 index*/
+				write_to_fib(get_tbl24_p(dp, redge,
+					dp->nh_sz), (tbl8_idx << 1)|
+					DIR24_8_EXT_ENT,
+					dp->nh_sz, 1);
+			} else
+				tbl8_idx = tbl24_tmp >> 1;
+			tbl8_ptr = (uint8_t *)dp->tbl8 +
+				((tbl8_idx * DIR24_8_TBL8_GRP_NUM_ENT) <<
+				dp->nh_sz);
+			/*update tbl8 with new next hop*/
+			write_to_fib((void *)tbl8_ptr, (next_hop << 1)|
+				DIR24_8_EXT_ENT,
+				dp->nh_sz, redge & ~DIR24_8_TBL24_MASK);
+			tbl8_recycle(dp, redge, tbl8_idx);
+		}
+	} else if ((redge - ledge) != 0) {
+		tbl24_tmp = get_tbl24(dp, ledge, dp->nh_sz);
+		if ((tbl24_tmp & DIR24_8_EXT_ENT) !=
+				DIR24_8_EXT_ENT) {
+			tbl8_idx = tbl8_alloc(dp, tbl24_tmp);
+			if (tbl8_idx < 0)
+				return -ENOSPC;
+			/*update dir24 entry with tbl8 index*/
+			write_to_fib(get_tbl24_p(dp, ledge, dp->nh_sz),
+				(tbl8_idx << 1)|
+				DIR24_8_EXT_ENT,
+				dp->nh_sz, 1);
+		} else
+			tbl8_idx = tbl24_tmp >> 1;
+		tbl8_ptr = (uint8_t *)dp->tbl8 +
+			(((tbl8_idx * DIR24_8_TBL8_GRP_NUM_ENT) +
+			(ledge & ~DIR24_8_TBL24_MASK)) <<
+			dp->nh_sz);
+		/*update tbl8 with new next hop*/
+		write_to_fib((void *)tbl8_ptr, (next_hop << 1)|
+			DIR24_8_EXT_ENT,
+			dp->nh_sz, redge - ledge);
+		tbl8_recycle(dp, ledge, tbl8_idx);
+	}
+	return 0;
+}
+
+static int
+modify_fib(struct dir24_8_tbl *dp, struct rte_rib *rib, uint32_t ip,
+	uint8_t depth, uint64_t next_hop)
+{
+	struct rte_rib_node *tmp = NULL;
+	uint32_t ledge, redge, tmp_ip;
+	int ret;
+	uint8_t tmp_depth;
+
+	ledge = ip;
+	do {
+		tmp = rte_rib_get_nxt(rib, ip, depth, tmp,
+			RTE_RIB_GET_NXT_COVER);
+		if (tmp != NULL) {
+			rte_rib_get_depth(tmp, &tmp_depth);
+			if (tmp_depth == depth)
+				continue;
+			rte_rib_get_ip(tmp, &tmp_ip);
+			redge = tmp_ip & rte_rib_depth_to_mask(tmp_depth);
+			if (ledge == redge) {
+				ledge = redge +
+					(uint32_t)(1ULL << (32 - tmp_depth));
+				continue;
+			}
+			ret = install_to_fib(dp, ledge, redge,
+				next_hop);
+			if (ret != 0)
+				return ret;
+			ledge = redge +
+				(uint32_t)(1ULL << (32 - tmp_depth));
+		} else {
+			redge = ip + (uint32_t)(1ULL << (32 - depth));
+			if (ledge == redge)
+				break;
+			ret = install_to_fib(dp, ledge, redge,
+				next_hop);
+			if (ret != 0)
+				return ret;
+		}
+	} while (tmp);
+
+	return 0;
+}
+
+int
+dir24_8_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
+	uint64_t next_hop, int op)
+{
+	struct dir24_8_tbl *dp;
+	struct rte_rib *rib;
+	struct rte_rib_node *tmp = NULL;
+	struct rte_rib_node *node;
+	struct rte_rib_node *parent;
+	int ret = 0;
+	uint64_t par_nh, node_nh;
+
+	if ((fib == NULL) || (depth > RTE_FIB_MAXDEPTH))
+		return -EINVAL;
+
+	dp = rte_fib_get_dp(fib);
+	rib = rte_fib_get_rib(fib);
+	RTE_ASSERT((dp != NULL) && (rib != NULL));
+
+	if (next_hop > get_max_nh(dp->nh_sz))
+		return -EINVAL;
+
+	ip &= rte_rib_depth_to_mask(depth);
+
+	node = rte_rib_lookup_exact(rib, ip, depth);
+	switch (op) {
+	case RTE_FIB_ADD:
+		if (node != NULL) {
+			rte_rib_get_nh(node, &node_nh);
+			if (node_nh == next_hop)
+				return 0;
+			ret = modify_fib(dp, rib, ip, depth, next_hop);
+			if (ret == 0)
+				rte_rib_set_nh(node, next_hop);
+			return 0;
+		}
+		if (depth > 24) {
+			tmp = rte_rib_get_nxt(rib, ip, 24, NULL,
+				RTE_RIB_GET_NXT_COVER);
+			if ((tmp == NULL) &&
+				(dp->rsvd_tbl8s >= dp->number_tbl8s))
+				return -ENOSPC;
+
+		}
+		node = rte_rib_insert(rib, ip, depth);
+		if (node == NULL)
+			return -rte_errno;
+		rte_rib_set_nh(node, next_hop);
+		parent = rte_rib_lookup_parent(node);
+		if (parent != NULL) {
+			rte_rib_get_nh(parent, &par_nh);
+			if (par_nh == next_hop)
+				return 0;
+		}
+		ret = modify_fib(dp, rib, ip, depth, next_hop);
+		if (ret != 0) {
+			rte_rib_remove(rib, ip, depth);
+			return ret;
+		}
+		if ((depth > 24) && (tmp == NULL))
+			dp->rsvd_tbl8s++;
+		return 0;
+	case RTE_FIB_DEL:
+		if (node == NULL)
+			return -ENOENT;
+
+		parent = rte_rib_lookup_parent(node);
+		if (parent != NULL) {
+			rte_rib_get_nh(parent, &par_nh);
+			rte_rib_get_nh(node, &node_nh);
+			if (par_nh != node_nh)
+				ret = modify_fib(dp, rib, ip, depth, par_nh);
+		} else
+			ret = modify_fib(dp, rib, ip, depth, dp->def_nh);
+		if (ret == 0) {
+			rte_rib_remove(rib, ip, depth);
+			if (depth > 24) {
+				tmp = rte_rib_get_nxt(rib, ip, 24, NULL,
+					RTE_RIB_GET_NXT_COVER);
+				if (tmp == NULL)
+					dp->rsvd_tbl8s--;
+			}
+		}
+		return ret;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+void *
+dir24_8_create(const char *name, int socket_id, struct rte_fib_conf *fib_conf)
+{
+	char mem_name[DIR24_8_NAMESIZE];
+	struct dir24_8_tbl *dp;
+	uint64_t	def_nh;
+	uint32_t	num_tbl8;
+	enum rte_fib_dir24_8_nh_sz	nh_sz;
+
+	if ((name == NULL) || (fib_conf == NULL) ||
+			(fib_conf->dir24_8.nh_sz < RTE_FIB_DIR24_8_1B) ||
+			(fib_conf->dir24_8.nh_sz > RTE_FIB_DIR24_8_8B) ||
+			(fib_conf->dir24_8.num_tbl8 >
+			get_max_nh(fib_conf->dir24_8.nh_sz)) ||
+			(fib_conf->dir24_8.num_tbl8 == 0) ||
+			(fib_conf->default_nh >
+			get_max_nh(fib_conf->dir24_8.nh_sz))) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	def_nh = fib_conf->default_nh;
+	nh_sz = fib_conf->dir24_8.nh_sz;
+	num_tbl8 = RTE_ALIGN_CEIL(fib_conf->dir24_8.num_tbl8,
+			BITMAP_SLAB_BIT_SIZE);
+
+	snprintf(mem_name, sizeof(mem_name), "DP_%s", name);
+	dp = rte_zmalloc_socket(name, sizeof(struct dir24_8_tbl) +
+		DIR24_8_TBL24_NUM_ENT * (1 << nh_sz), RTE_CACHE_LINE_SIZE,
+		socket_id);
+	if (dp == NULL) {
+		rte_errno = ENOMEM;
+		return NULL;
+	}
+
+	/* Init table with default value */
+	write_to_fib(dp->tbl24, (def_nh << 1), nh_sz, 1 << 24);
+
+	snprintf(mem_name, sizeof(mem_name), "TBL8_%p", dp);
+	uint64_t tbl8_sz = DIR24_8_TBL8_GRP_NUM_ENT * (1ULL << nh_sz) *
+			(num_tbl8 + 1);
+	dp->tbl8 = rte_zmalloc_socket(mem_name, tbl8_sz,
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (dp->tbl8 == NULL) {
+		rte_errno = ENOMEM;
+		rte_free(dp);
+		return NULL;
+	}
+	dp->def_nh = def_nh;
+	dp->nh_sz = nh_sz;
+	dp->number_tbl8s = num_tbl8;
+
+	snprintf(mem_name, sizeof(mem_name), "TBL8_idxes_%p", dp);
+	dp->tbl8_idxes = rte_zmalloc_socket(mem_name,
+			RTE_ALIGN_CEIL(dp->number_tbl8s, 64) >> 3,
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (dp->tbl8_idxes == NULL) {
+		rte_errno = ENOMEM;
+		rte_free(dp->tbl8);
+		rte_free(dp);
+		return NULL;
+	}
+
+	return dp;
+}
+
+void
+dir24_8_free(void *p)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	rte_free(dp->tbl8_idxes);
+	rte_free(dp->tbl8);
+	rte_free(dp);
+}
diff --git a/lib/librte_fib/dir24_8.h b/lib/librte_fib/dir24_8.h
new file mode 100644
index 0000000..34ddb91
--- /dev/null
+++ b/lib/librte_fib/dir24_8.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _DIR24_8_H_
+#define _DIR24_8_H_
+
+/**
+ * @file
+ * DIR24_8 algorithm
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *
+dir24_8_create(const char *name, int socket_id, struct rte_fib_conf *conf);
+
+void
+dir24_8_free(void *p);
+
+rte_fib_lookup_fn_t
+dir24_8_get_lookup_fn(struct rte_fib_conf *conf);
+
+int
+dir24_8_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
+	uint64_t next_hop, int op);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DIR24_8_H_ */
+
diff --git a/lib/librte_fib/meson.build b/lib/librte_fib/meson.build
index 32dfdec..c63cac8 100644
--- a/lib/librte_fib/meson.build
+++ b/lib/librte_fib/meson.build
@@ -3,6 +3,6 @@
 # Copyright(c) 2019 Intel Corporation
 
 allow_experimental_apis = true
-sources = files('rte_fib.c', 'rte_fib6.c')
+sources = files('rte_fib.c', 'rte_fib6.c', 'dir24_8.c')
 headers = files('rte_fib.h', 'rte_fib6.h')
 deps += ['rib']
diff --git a/lib/librte_fib/rte_fib.c b/lib/librte_fib/rte_fib.c
index 4d8a771..e090808 100644
--- a/lib/librte_fib/rte_fib.c
+++ b/lib/librte_fib/rte_fib.c
@@ -17,6 +17,8 @@
 #include <rte_rib.h>
 #include <rte_fib.h>
 
+#include "dir24_8.h"
+
 TAILQ_HEAD(rte_fib_list, rte_tailq_entry);
 static struct rte_tailq_elem rte_fib_tailq = {
 	.name = "RTE_FIB",
@@ -92,12 +94,22 @@ static int
 init_dataplane(struct rte_fib *fib, __rte_unused int socket_id,
 	struct rte_fib_conf *conf)
 {
+	char dp_name[sizeof(void *)];
+
+	snprintf(dp_name, sizeof(dp_name), "%p", fib);
 	switch (conf->type) {
 	case RTE_FIB_DUMMY:
 		fib->dp = fib;
 		fib->lookup = dummy_lookup;
 		fib->modify = dummy_modify;
 		return 0;
+	case RTE_FIB_DIR24_8:
+		fib->dp = dir24_8_create(dp_name, socket_id, conf);
+		if (fib->dp == NULL)
+			return -rte_errno;
+		fib->lookup = dir24_8_get_lookup_fn(conf);
+		fib->modify = dir24_8_modify;
+		return 0;
 	default:
 		return -EINVAL;
 	}
@@ -258,6 +270,8 @@ free_dataplane(struct rte_fib *fib)
 	switch (fib->type) {
 	case RTE_FIB_DUMMY:
 		return;
+	case RTE_FIB_DIR24_8:
+		dir24_8_free(fib->dp);
 	default:
 		return;
 	}
diff --git a/lib/librte_fib/rte_fib.h b/lib/librte_fib/rte_fib.h
index 096cc92..d06c5ef 100644
--- a/lib/librte_fib/rte_fib.h
+++ b/lib/librte_fib/rte_fib.h
@@ -15,6 +15,7 @@
 #include <rte_compat.h>
 
 struct rte_fib;
+struct rte_rib;
 
 /** Maximum depth value possible for IPv4 FIB. */
 #define RTE_FIB_MAXDEPTH	32
@@ -22,6 +23,7 @@ struct rte_fib;
 /** Type of FIB struct */
 enum rte_fib_type {
 	RTE_FIB_DUMMY,		/**< RIB tree based FIB */
+	RTE_FIB_DIR24_8,	/**< DIR24_8 based FIB */
 	RTE_FIB_TYPE_MAX
 };
 
@@ -37,12 +39,26 @@ enum rte_fib_op {
 	RTE_FIB_DEL,
 };
 
+/** Size of nexthop (1 << nh_sz) bits for DIR24_8 based FIB */
+enum rte_fib_dir24_8_nh_sz {
+	RTE_FIB_DIR24_8_1B,
+	RTE_FIB_DIR24_8_2B,
+	RTE_FIB_DIR24_8_4B,
+	RTE_FIB_DIR24_8_8B
+};
+
 /** FIB configuration structure */
 struct rte_fib_conf {
 	enum rte_fib_type type; /**< Type of FIB struct */
 	/** Default value returned on lookup if there is no route */
 	uint64_t default_nh;
 	int	max_routes;
+	union {
+		struct {
+			enum rte_fib_dir24_8_nh_sz nh_sz;
+			uint32_t	num_tbl8;
+		} dir24_8;
+	};
 };
 
 /**
@@ -143,7 +159,6 @@ __rte_experimental
 int
 rte_fib_lookup_bulk(struct rte_fib *fib, uint32_t *ips,
 		uint64_t *next_hops, int n);
-
 /**
  * Get pointer to the dataplane specific struct
  *
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 08/12] fib: add dataplane algorithm for ipv6
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
                     ` (8 preceding siblings ...)
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 07/12] fib: add DIR24-8 dataplane algorithm Vladimir Medvedkin
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 09/12] test/fib: add FIB library autotests Vladimir Medvedkin
                     ` (3 subsequent siblings)
  13 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

Add fib implementation for ipv6 using modified DIR24_8 algorithm.
Implementation is similar to current LPM6 implementation but has
few enhancements:
faster control plabe operations
more bits for userdata in table entries
configurable userdata size

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 lib/librte_fib/Makefile    |   2 +-
 lib/librte_fib/meson.build |   2 +-
 lib/librte_fib/rte_fib6.c  |  14 +
 lib/librte_fib/rte_fib6.h  |  14 +
 lib/librte_fib/trie.c      | 760 +++++++++++++++++++++++++++++++++++++++++++++
 lib/librte_fib/trie.h      |  37 +++
 6 files changed, 827 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_fib/trie.c
 create mode 100644 lib/librte_fib/trie.h

diff --git a/lib/librte_fib/Makefile b/lib/librte_fib/Makefile
index 67fde9a..4abd80b 100644
--- a/lib/librte_fib/Makefile
+++ b/lib/librte_fib/Makefile
@@ -17,7 +17,7 @@ EXPORT_MAP := rte_fib_version.map
 LIBABIVER := 1
 
 # all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c rte_fib6.c dir24_8.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) := rte_fib.c rte_fib6.c dir24_8.c trie.c
 
 # install this header file
 SYMLINK-$(CONFIG_RTE_LIBRTE_FIB)-include := rte_fib.h rte_fib6.h
diff --git a/lib/librte_fib/meson.build b/lib/librte_fib/meson.build
index c63cac8..e2c6f44 100644
--- a/lib/librte_fib/meson.build
+++ b/lib/librte_fib/meson.build
@@ -3,6 +3,6 @@
 # Copyright(c) 2019 Intel Corporation
 
 allow_experimental_apis = true
-sources = files('rte_fib.c', 'rte_fib6.c', 'dir24_8.c')
+sources = files('rte_fib.c', 'rte_fib6.c', 'dir24_8.c', 'trie.c')
 headers = files('rte_fib.h', 'rte_fib6.h')
 deps += ['rib']
diff --git a/lib/librte_fib/rte_fib6.c b/lib/librte_fib/rte_fib6.c
index 9f00a80..354227d 100644
--- a/lib/librte_fib/rte_fib6.c
+++ b/lib/librte_fib/rte_fib6.c
@@ -17,6 +17,8 @@
 #include <rte_rib6.h>
 #include <rte_fib6.h>
 
+#include "trie.h"
+
 TAILQ_HEAD(rte_fib6_list, rte_tailq_entry);
 static struct rte_tailq_elem rte_fib6_tailq = {
 	.name = "RTE_FIB6",
@@ -92,12 +94,22 @@ static int
 init_dataplane(struct rte_fib6 *fib, __rte_unused int socket_id,
 	struct rte_fib6_conf *conf)
 {
+	char dp_name[sizeof(void *)];
+
+	snprintf(dp_name, sizeof(dp_name), "%p", fib);
 	switch (conf->type) {
 	case RTE_FIB6_DUMMY:
 		fib->dp = fib;
 		fib->lookup = dummy_lookup;
 		fib->modify = dummy_modify;
 		return 0;
+	case RTE_FIB6_TRIE:
+		fib->dp = trie_create(dp_name, socket_id, conf);
+		if (fib->dp == NULL)
+			return -rte_errno;
+		fib->lookup = rte_trie_get_lookup_fn(conf);
+		fib->modify = trie_modify;
+		return 0;
 	default:
 		return -EINVAL;
 	}
@@ -260,6 +272,8 @@ free_dataplane(struct rte_fib6 *fib)
 	switch (fib->type) {
 	case RTE_FIB6_DUMMY:
 		return;
+	case RTE_FIB6_TRIE:
+		trie_free(fib->dp);
 	default:
 		return;
 	}
diff --git a/lib/librte_fib/rte_fib6.h b/lib/librte_fib/rte_fib6.h
index 3322123..4268704 100644
--- a/lib/librte_fib/rte_fib6.h
+++ b/lib/librte_fib/rte_fib6.h
@@ -19,10 +19,12 @@
 #define RTE_FIB6_MAXDEPTH       128
 
 struct rte_fib6;
+struct rte_rib6;
 
 /** Type of FIB struct */
 enum rte_fib6_type {
 	RTE_FIB6_DUMMY,		/**< RIB6 tree based FIB */
+	RTE_FIB6_TRIE,		/**< TRIE based fib  */
 	RTE_FIB6_TYPE_MAX
 };
 
@@ -40,12 +42,24 @@ enum rte_fib6_op {
 	RTE_FIB6_DEL,
 };
 
+enum rte_fib_trie_nh_sz {
+	RTE_FIB6_TRIE_2B = 1,
+	RTE_FIB6_TRIE_4B,
+	RTE_FIB6_TRIE_8B
+};
+
 /** FIB configuration structure */
 struct rte_fib6_conf {
 	enum rte_fib6_type type; /**< Type of FIB struct */
 	/** Default value returned on lookup if there is no route */
 	uint64_t default_nh;
 	int	max_routes;
+	union {
+		struct {
+			enum rte_fib_trie_nh_sz nh_sz;
+			uint32_t	num_tbl8;
+		} trie;
+	};
 };
 
 /**
diff --git a/lib/librte_fib/trie.c b/lib/librte_fib/trie.c
new file mode 100644
index 0000000..198e815
--- /dev/null
+++ b/lib/librte_fib/trie.c
@@ -0,0 +1,760 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <rte_debug.h>
+#include <rte_malloc.h>
+#include <rte_prefetch.h>
+#include <rte_errno.h>
+#include <rte_memory.h>
+#include <rte_branch_prediction.h>
+
+#include <rte_rib6.h>
+#include <rte_fib6.h>
+#include "trie.h"
+
+/* @internal Total number of tbl24 entries. */
+#define TRIE_TBL24_NUM_ENT	(1 << 24)
+
+/* Maximum depth value possible for IPv6 LPM. */
+#define TRIE_MAX_DEPTH		128
+
+/* @internal Number of entries in a tbl8 group. */
+#define TRIE_TBL8_GRP_NUM_ENT	256ULL
+
+/* @internal Total number of tbl8 groups in the tbl8. */
+#define TRIE_TBL8_NUM_GROUPS	65536
+
+/* @internal bitmask with valid and valid_group fields set */
+#define TRIE_EXT_ENT		1
+
+#define TRIE_NAMESIZE		64
+
+#define BITMAP_SLAB_BIT_SIZE_LOG2	6
+#define BITMAP_SLAB_BIT_SIZE		(1ULL << BITMAP_SLAB_BIT_SIZE_LOG2)
+#define BITMAP_SLAB_BITMASK		(BITMAP_SLAB_BIT_SIZE - 1)
+
+struct rte_trie_tbl {
+	uint32_t	number_tbl8s;	/**< Total number of tbl8s */
+	uint32_t	rsvd_tbl8s;	/**< Number of reserved tbl8s */
+	uint32_t	cur_tbl8s;	/**< Current cumber of tbl8s */
+	uint64_t	def_nh;		/**< Default next hop */
+	enum rte_fib_trie_nh_sz	nh_sz;	/**< Size of nexthop entry */
+	uint64_t	*tbl8;		/**< tbl8 table. */
+	uint32_t	*tbl8_pool;	/**< bitmap containing free tbl8 idxes*/
+	uint32_t	tbl8_pool_pos;
+	/* tbl24 table. */
+	__extension__ uint64_t	tbl24[0] __rte_cache_aligned;
+};
+
+enum edge {
+	LEDGE,
+	REDGE
+};
+
+enum lookup_type {
+	MACRO,
+	INLINE,
+	UNI
+};
+static enum lookup_type test_lookup = MACRO;
+
+static inline uint32_t
+get_tbl24_idx(const uint8_t *ip)
+{
+	return ip[0] << 16|ip[1] << 8|ip[2];
+}
+
+static inline void *
+get_tbl24_p(struct rte_trie_tbl *dp, const uint8_t *ip, uint8_t nh_sz)
+{
+	uint32_t tbl24_idx;
+
+	tbl24_idx = get_tbl24_idx(ip);
+	return (void *)&((uint8_t *)dp->tbl24)[tbl24_idx << nh_sz];
+}
+
+static inline uint8_t
+bits_in_nh(uint8_t nh_sz)
+{
+	return 8 * (1 << nh_sz);
+}
+
+static inline uint64_t
+get_max_nh(uint8_t nh_sz)
+{
+	return ((1ULL << (bits_in_nh(nh_sz) - 1)) - 1);
+}
+
+static inline uint64_t
+lookup_msk(uint8_t nh_sz)
+{
+	return ((1ULL << ((1 << (nh_sz + 3)) - 1)) << 1) - 1;
+}
+
+static inline uint8_t
+get_psd_idx(uint32_t val, uint8_t nh_sz)
+{
+	return val & ((1 << (3 - nh_sz)) - 1);
+}
+
+static inline uint32_t
+get_tbl_pos(uint32_t val, uint8_t nh_sz)
+{
+	return val >> (3 - nh_sz);
+}
+
+static inline uint64_t
+get_tbl_val_by_idx(uint64_t *tbl, uint32_t idx, uint8_t nh_sz)
+{
+	return ((tbl[get_tbl_pos(idx, nh_sz)] >> (get_psd_idx(idx, nh_sz) *
+		bits_in_nh(nh_sz))) & lookup_msk(nh_sz));
+}
+
+static inline void *
+get_tbl_p_by_idx(uint64_t *tbl, uint64_t idx, uint8_t nh_sz)
+{
+	return (uint8_t *)tbl + (idx << nh_sz);
+}
+
+static inline int
+is_entry_extended(uint64_t ent)
+{
+	return (ent & TRIE_EXT_ENT) == TRIE_EXT_ENT;
+}
+
+#define LOOKUP_FUNC(suffix, type, nh_sz)				\
+static void rte_trie_lookup_bulk_##suffix(void *p,			\
+	uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],			\
+	uint64_t *next_hops, const unsigned int n)			\
+{									\
+	struct rte_trie_tbl *dp = (struct rte_trie_tbl *)p;		\
+	uint64_t tmp;							\
+	uint32_t i, j;							\
+									\
+	for (i = 0; i < n; i++) {					\
+		tmp = ((type *)dp->tbl24)[get_tbl24_idx(&ips[i][0])];	\
+		j = 3;							\
+		while (is_entry_extended(tmp)) {			\
+			tmp = ((type *)dp->tbl8)[ips[i][j++] +		\
+				((tmp >> 1) * TRIE_TBL8_GRP_NUM_ENT)];	\
+		}							\
+		next_hops[i] = tmp >> 1;				\
+	}								\
+}
+LOOKUP_FUNC(2b, uint16_t, 1)
+LOOKUP_FUNC(4b, uint32_t, 2)
+LOOKUP_FUNC(8b, uint64_t, 3)
+
+rte_fib6_lookup_fn_t
+rte_trie_get_lookup_fn(struct rte_fib6_conf *conf)
+{
+	enum rte_fib_trie_nh_sz nh_sz = conf->trie.nh_sz;
+
+	if (test_lookup == MACRO) {
+		switch (nh_sz) {
+		case RTE_FIB6_TRIE_2B:
+			return rte_trie_lookup_bulk_2b;
+		case RTE_FIB6_TRIE_4B:
+			return rte_trie_lookup_bulk_4b;
+		case RTE_FIB6_TRIE_8B:
+			return rte_trie_lookup_bulk_8b;
+		}
+	}
+
+	return NULL;
+}
+
+static void
+write_to_dp(void *ptr, uint64_t val, enum rte_fib_trie_nh_sz size, int n)
+{
+	int i;
+	uint16_t *ptr16 = (uint16_t *)ptr;
+	uint32_t *ptr32 = (uint32_t *)ptr;
+	uint64_t *ptr64 = (uint64_t *)ptr;
+
+	switch (size) {
+	case RTE_FIB6_TRIE_2B:
+		for (i = 0; i < n; i++)
+			ptr16[i] = (uint16_t)val;
+		break;
+	case RTE_FIB6_TRIE_4B:
+		for (i = 0; i < n; i++)
+			ptr32[i] = (uint32_t)val;
+		break;
+	case RTE_FIB6_TRIE_8B:
+		for (i = 0; i < n; i++)
+			ptr64[i] = (uint64_t)val;
+		break;
+	}
+}
+
+static void
+tbl8_pool_init(struct rte_trie_tbl *dp)
+{
+	uint32_t i;
+
+	/* put entire range of indexes to the tbl8 pool */
+	for (i = 0; i < dp->number_tbl8s; i++)
+		dp->tbl8_pool[i] = i;
+
+	dp->tbl8_pool_pos = 0;
+}
+
+/*
+ * Get an index of a free tbl8 from the pool
+ */
+static inline int32_t
+tbl8_get(struct rte_trie_tbl *dp)
+{
+	if (dp->tbl8_pool_pos == dp->number_tbl8s)
+		/* no more free tbl8 */
+		return -ENOSPC;
+
+	/* next index */
+	return dp->tbl8_pool[dp->tbl8_pool_pos++];
+}
+
+/*
+ * Put an index of a free tbl8 back to the pool
+ */
+static inline void
+tbl8_put(struct rte_trie_tbl *dp, uint32_t tbl8_ind)
+{
+	dp->tbl8_pool[--dp->tbl8_pool_pos] = tbl8_ind;
+}
+
+static int
+tbl8_alloc(struct rte_trie_tbl *dp, uint64_t nh)
+{
+	int64_t		tbl8_idx;
+	uint8_t		*tbl8_ptr;
+
+	tbl8_idx = tbl8_get(dp);
+	if (tbl8_idx < 0)
+		return tbl8_idx;
+	tbl8_ptr = (uint8_t *)dp->tbl8 +
+		((tbl8_idx * TRIE_TBL8_GRP_NUM_ENT) <<
+		dp->nh_sz);
+	/*Init tbl8 entries with nexthop from tbl24*/
+	write_to_dp((void *)tbl8_ptr, nh, dp->nh_sz,
+		TRIE_TBL8_GRP_NUM_ENT);
+	return tbl8_idx;
+}
+
+static void
+tbl8_recycle(struct rte_trie_tbl *dp, void *par, uint64_t tbl8_idx)
+{
+	uint32_t i;
+	uint64_t nh;
+	uint16_t *ptr16;
+	uint32_t *ptr32;
+	uint64_t *ptr64;
+
+	switch (dp->nh_sz) {
+	case RTE_FIB6_TRIE_2B:
+		ptr16 = &((uint16_t *)dp->tbl8)[tbl8_idx *
+				TRIE_TBL8_GRP_NUM_ENT];
+		nh = *ptr16;
+		if (nh & TRIE_EXT_ENT)
+			return;
+		for (i = 1; i < TRIE_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr16[i])
+				return;
+		}
+		write_to_dp(par, nh, dp->nh_sz, 1);
+		for (i = 0; i < TRIE_TBL8_GRP_NUM_ENT; i++)
+			ptr16[i] = 0;
+		break;
+	case RTE_FIB6_TRIE_4B:
+		ptr32 = &((uint32_t *)dp->tbl8)[tbl8_idx *
+				TRIE_TBL8_GRP_NUM_ENT];
+		nh = *ptr32;
+		if (nh & TRIE_EXT_ENT)
+			return;
+		for (i = 1; i < TRIE_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr32[i])
+				return;
+		}
+		write_to_dp(par, nh, dp->nh_sz, 1);
+		for (i = 0; i < TRIE_TBL8_GRP_NUM_ENT; i++)
+			ptr32[i] = 0;
+		break;
+	case RTE_FIB6_TRIE_8B:
+		ptr64 = &((uint64_t *)dp->tbl8)[tbl8_idx *
+				TRIE_TBL8_GRP_NUM_ENT];
+		nh = *ptr64;
+		if (nh & TRIE_EXT_ENT)
+			return;
+		for (i = 1; i < TRIE_TBL8_GRP_NUM_ENT; i++) {
+			if (nh != ptr64[i])
+				return;
+		}
+		write_to_dp(par, nh, dp->nh_sz, 1);
+		for (i = 0; i < TRIE_TBL8_GRP_NUM_ENT; i++)
+			ptr64[i] = 0;
+		break;
+	}
+	tbl8_put(dp, tbl8_idx);
+}
+
+#define BYTE_SIZE	8
+static inline uint32_t
+get_idx(const uint8_t *ip, uint32_t prev_idx, int bytes, int first_byte)
+{
+	int i;
+	uint32_t idx = 0;
+	uint8_t bitshift;
+
+	for (i = first_byte; i < (first_byte + bytes); i++) {
+		bitshift = (int8_t)(((first_byte + bytes - 1) - i)*BYTE_SIZE);
+		idx |= ip[i] <<  bitshift;
+	}
+	return (prev_idx * 256) + idx;
+}
+
+static inline uint64_t
+get_val_by_p(void *p, uint8_t nh_sz)
+{
+	uint64_t val = 0;
+
+	switch (nh_sz) {
+	case RTE_FIB6_TRIE_2B:
+		val = *(uint16_t *)p;
+		break;
+	case RTE_FIB6_TRIE_4B:
+		val = *(uint32_t *)p;
+		break;
+	case RTE_FIB6_TRIE_8B:
+		val = *(uint64_t *)p;
+		break;
+	}
+	return val;
+}
+
+/*
+ * recursively recycle tbl8's
+ */
+static void
+recycle_root_path(struct rte_trie_tbl *dp, const uint8_t *ip_part,
+	uint8_t common_tbl8, void *prev)
+{
+	void *p;
+	uint64_t val;
+
+	val = get_val_by_p(prev, dp->nh_sz);
+	if (unlikely((val & TRIE_EXT_ENT) != TRIE_EXT_ENT))
+		return;
+
+	if (common_tbl8 != 0) {
+		p = get_tbl_p_by_idx(dp->tbl8, (val >> 1) * 256 + *ip_part,
+			dp->nh_sz);
+		recycle_root_path(dp, ip_part + 1, common_tbl8 - 1, p);
+	}
+	tbl8_recycle(dp, prev, val >> 1);
+}
+
+static inline int
+build_common_root(struct rte_trie_tbl *dp, const uint8_t *ip,
+	int common_bytes, void **tbl)
+{
+	void *tbl_ptr = NULL;
+	uint64_t *cur_tbl;
+	uint64_t val;
+	int i, j, idx, prev_idx = 0;
+
+	cur_tbl = dp->tbl24;
+	for (i = 3, j = 0; i <= common_bytes; i++) {
+		idx = get_idx(ip, prev_idx, i - j, j);
+		val = get_tbl_val_by_idx(cur_tbl, idx, dp->nh_sz);
+		tbl_ptr = get_tbl_p_by_idx(cur_tbl, idx, dp->nh_sz);
+		if ((val & TRIE_EXT_ENT) != TRIE_EXT_ENT) {
+			idx = tbl8_alloc(dp, val);
+			if (unlikely(idx < 0))
+				return idx;
+			write_to_dp(tbl_ptr, (idx << 1) |
+				TRIE_EXT_ENT, dp->nh_sz, 1);
+			prev_idx = idx;
+		} else
+			prev_idx = val >> 1;
+
+		j = i;
+		cur_tbl = dp->tbl8;
+	}
+	*tbl = get_tbl_p_by_idx(cur_tbl, prev_idx * 256, dp->nh_sz);
+	return 0;
+}
+
+static int
+write_edge(struct rte_trie_tbl *dp, const uint8_t *ip_part, uint64_t next_hop,
+	int len, enum edge edge, void *ent)
+{
+	uint64_t val = next_hop << 1;
+	int tbl8_idx;
+	int ret = 0;
+	void *p;
+
+	if (len != 0) {
+		val = get_val_by_p(ent, dp->nh_sz);
+		if ((val & TRIE_EXT_ENT) == TRIE_EXT_ENT)
+			tbl8_idx = val >> 1;
+		else {
+			tbl8_idx = tbl8_alloc(dp, val);
+			if (tbl8_idx < 0)
+				return tbl8_idx;
+			val = (tbl8_idx << 1)|TRIE_EXT_ENT;
+		}
+		p = get_tbl_p_by_idx(dp->tbl8, (tbl8_idx * 256) + *ip_part,
+			dp->nh_sz);
+		ret = write_edge(dp, ip_part + 1, next_hop, len - 1, edge, p);
+		if (ret < 0)
+			return ret;
+		if (edge == LEDGE) {
+			write_to_dp((uint8_t *)p + (1 << dp->nh_sz),
+				next_hop << 1, dp->nh_sz, UINT8_MAX - *ip_part);
+		} else {
+			write_to_dp(get_tbl_p_by_idx(dp->tbl8, tbl8_idx * 256,
+				dp->nh_sz),
+				next_hop << 1, dp->nh_sz, *ip_part);
+		}
+		tbl8_recycle(dp, &val, tbl8_idx);
+	}
+
+	write_to_dp(ent, val, dp->nh_sz, 1);
+	return ret;
+}
+
+#define IPV6_MAX_IDX	(RTE_FIB6_IPV6_ADDR_SIZE - 1)
+#define TBL24_BYTES	3
+#define TBL8_LEN	(RTE_FIB6_IPV6_ADDR_SIZE - TBL24_BYTES)
+
+static int
+install_to_dp(struct rte_trie_tbl *dp, const uint8_t *ledge, const uint8_t *r,
+	uint64_t next_hop)
+{
+	void *common_root_tbl;
+	void *ent;
+	int ret;
+	int i;
+	int common_bytes;
+	int llen, rlen;
+	uint8_t redge[16];
+
+	/* decrement redge by 1*/
+	rte_rib6_copy_addr(redge, r);
+	for (i = 15; i >= 0; i--) {
+		redge[i]--;
+		if (redge[i] != 0xff)
+			break;
+	}
+
+	for (common_bytes = 0; common_bytes < 15; common_bytes++) {
+		if (ledge[common_bytes] != redge[common_bytes])
+			break;
+	}
+
+	ret = build_common_root(dp, ledge, common_bytes, &common_root_tbl);
+	if (unlikely(ret != 0))
+		return ret;
+	/*first uncommon tbl8 byte idx*/
+	uint8_t first_tbl8_byte = RTE_MAX(common_bytes, TBL24_BYTES);
+
+	for (i = IPV6_MAX_IDX; i > first_tbl8_byte; i--) {
+		if (ledge[i] != 0)
+			break;
+	}
+
+	llen = i - first_tbl8_byte + (common_bytes < 3);
+
+	for (i = IPV6_MAX_IDX; i > first_tbl8_byte; i--) {
+		if (redge[i] != UINT8_MAX)
+			break;
+	}
+	rlen = i - first_tbl8_byte + (common_bytes < 3);
+
+	/*first noncommon byte*/
+	uint8_t first_byte_idx = (common_bytes < 3) ? 0 : common_bytes;
+	uint8_t first_idx_len = (common_bytes < 3) ? 3 : 1;
+
+	uint32_t left_idx = get_idx(ledge, 0, first_idx_len, first_byte_idx);
+	uint32_t right_idx = get_idx(redge, 0, first_idx_len, first_byte_idx);
+
+	ent = get_tbl_p_by_idx(common_root_tbl, left_idx, dp->nh_sz);
+	ret = write_edge(dp, &ledge[first_tbl8_byte + !(common_bytes < 3)],
+		next_hop, llen, LEDGE, ent);
+	if (ret < 0)
+		return ret;
+
+	if (right_idx > left_idx + 1) {
+		ent = get_tbl_p_by_idx(common_root_tbl, left_idx + 1,
+			dp->nh_sz);
+		write_to_dp(ent, next_hop << 1, dp->nh_sz,
+			right_idx - (left_idx + 1));
+	}
+	ent = get_tbl_p_by_idx(common_root_tbl, right_idx, dp->nh_sz);
+	ret = write_edge(dp, &redge[first_tbl8_byte + !((common_bytes < 3))],
+		next_hop, rlen, REDGE, ent);
+	if (ret < 0)
+		return ret;
+
+	uint8_t	common_tbl8 = (common_bytes < TBL24_BYTES) ?
+			0 : common_bytes - (TBL24_BYTES - 1);
+	ent = get_tbl24_p(dp, ledge, dp->nh_sz);
+	recycle_root_path(dp, ledge + TBL24_BYTES, common_tbl8, ent);
+	return 0;
+}
+
+static void
+get_nxt_net(uint8_t *ip, uint8_t depth)
+{
+	int i;
+	uint8_t part_depth;
+	uint8_t prev_byte;
+
+	for (i = 0, part_depth = depth; part_depth > 8; part_depth -= 8, i++)
+		;
+
+	prev_byte = ip[i];
+	ip[i] += 1 << (8 - part_depth);
+	if (ip[i] < prev_byte) {
+		while (i > 0) {
+			ip[--i] += 1;
+			if (ip[i] != 0)
+				break;
+		}
+	}
+}
+
+static int
+modify_dp(struct rte_trie_tbl *dp, struct rte_rib6 *rib,
+	const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, uint64_t next_hop)
+{
+	struct rte_rib6_node *tmp = NULL;
+	uint8_t ledge[RTE_FIB6_IPV6_ADDR_SIZE];
+	uint8_t redge[RTE_FIB6_IPV6_ADDR_SIZE];
+	int ret;
+	uint8_t tmp_depth;
+
+	if (next_hop > get_max_nh(dp->nh_sz))
+		return -EINVAL;
+
+	rte_rib6_copy_addr(ledge, ip);
+	do {
+		tmp = rte_rib6_get_nxt(rib, ip, depth, tmp,
+			RTE_RIB6_GET_NXT_COVER);
+		if (tmp != NULL) {
+			rte_rib6_get_depth(tmp, &tmp_depth);
+			if (tmp_depth == depth)
+				continue;
+			rte_rib6_get_ip(tmp, redge);
+			if (rte_rib6_is_equal(ledge, redge)) {
+				get_nxt_net(ledge, tmp_depth);
+				continue;
+			}
+			ret = install_to_dp(dp, ledge, redge,
+				next_hop);
+			if (ret != 0)
+				return ret;
+			get_nxt_net(redge, tmp_depth);
+			rte_rib6_copy_addr(ledge, redge);
+		} else {
+			rte_rib6_copy_addr(redge, ip);
+			get_nxt_net(redge, depth);
+			if (rte_rib6_is_equal(ledge, redge))
+				break;
+			ret = install_to_dp(dp, ledge, redge,
+				next_hop);
+			if (ret != 0)
+				return ret;
+		}
+	} while (tmp);
+
+	return 0;
+}
+
+int
+trie_modify(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, uint64_t next_hop, int op)
+{
+	struct rte_trie_tbl *dp;
+	struct rte_rib6 *rib;
+	struct rte_rib6_node *tmp = NULL;
+	struct rte_rib6_node *node;
+	struct rte_rib6_node *parent;
+	uint8_t	ip_masked[RTE_FIB6_IPV6_ADDR_SIZE];
+	int i, ret = 0;
+	uint64_t par_nh, node_nh;
+	uint8_t tmp_depth, depth_diff = 0, parent_depth = 24;
+
+	if ((fib == NULL) || (ip == NULL) || (depth > RTE_FIB6_MAXDEPTH))
+		return -EINVAL;
+
+	dp = rte_fib6_get_dp(fib);
+	RTE_ASSERT(dp);
+	rib = rte_fib6_get_rib(fib);
+	RTE_ASSERT(rib);
+
+	for (i = 0; i < RTE_FIB6_IPV6_ADDR_SIZE; i++)
+		ip_masked[i] = ip[i] & get_msk_part(depth, i);
+
+	if (depth > 24) {
+		tmp = rte_rib6_get_nxt(rib, ip_masked,
+			RTE_ALIGN_FLOOR(depth, 8), NULL,
+			RTE_RIB6_GET_NXT_COVER);
+		if (tmp == NULL) {
+			tmp = rte_rib6_lookup(rib, ip);
+			if (tmp != NULL) {
+				rte_rib6_get_depth(tmp, &tmp_depth);
+				parent_depth = RTE_MAX(tmp_depth, 24);
+			}
+			depth_diff = RTE_ALIGN_CEIL(depth, 8) -
+				RTE_ALIGN_CEIL(parent_depth, 8);
+			depth_diff = depth_diff >> 3;
+		}
+	}
+	node = rte_rib6_lookup_exact(rib, ip_masked, depth);
+	switch (op) {
+	case RTE_FIB6_ADD:
+		if (node != NULL) {
+			rte_rib6_get_nh(node, &node_nh);
+			if (node_nh == next_hop)
+				return 0;
+			ret = modify_dp(dp, rib, ip_masked, depth, next_hop);
+			if (ret == 0)
+				rte_rib6_set_nh(node, next_hop);
+			return 0;
+		}
+
+		if ((depth > 24) && (dp->rsvd_tbl8s >=
+				dp->number_tbl8s - depth_diff))
+			return -ENOSPC;
+
+		node = rte_rib6_insert(rib, ip_masked, depth);
+		if (node == NULL)
+			return -rte_errno;
+		rte_rib6_set_nh(node, next_hop);
+		parent = rte_rib6_lookup_parent(node);
+		if (parent != NULL) {
+			rte_rib6_get_nh(parent, &par_nh);
+			if (par_nh == next_hop)
+				return 0;
+		}
+		ret = modify_dp(dp, rib, ip_masked, depth, next_hop);
+		if (ret != 0) {
+			rte_rib6_remove(rib, ip_masked, depth);
+			return ret;
+		}
+
+		dp->rsvd_tbl8s += depth_diff;
+		return 0;
+	case RTE_FIB6_DEL:
+		if (node == NULL)
+			return -ENOENT;
+
+		parent = rte_rib6_lookup_parent(node);
+		if (parent != NULL) {
+			rte_rib6_get_nh(parent, &par_nh);
+			rte_rib6_get_nh(node, &node_nh);
+			if (par_nh != node_nh)
+				ret = modify_dp(dp, rib, ip_masked, depth,
+					par_nh);
+		} else
+			ret = modify_dp(dp, rib, ip_masked, depth, dp->def_nh);
+
+		if (ret != 0)
+			return ret;
+		rte_rib6_remove(rib, ip, depth);
+
+		dp->rsvd_tbl8s -= depth_diff;
+		return 0;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+void *
+trie_create(const char *name, int socket_id,
+	struct rte_fib6_conf *conf)
+{
+	char mem_name[TRIE_NAMESIZE];
+	struct rte_trie_tbl *dp = NULL;
+	uint64_t	def_nh;
+	uint32_t	num_tbl8;
+	enum rte_fib_trie_nh_sz	nh_sz;
+
+	if ((name == NULL) || (conf == NULL) ||
+			(conf->trie.nh_sz < RTE_FIB6_TRIE_2B) ||
+			(conf->trie.nh_sz > RTE_FIB6_TRIE_8B) ||
+			(conf->trie.num_tbl8 >
+			get_max_nh(conf->trie.nh_sz)) ||
+			(conf->trie.num_tbl8 == 0) ||
+			(conf->default_nh >
+			get_max_nh(conf->trie.nh_sz))) {
+
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	def_nh = conf->default_nh;
+	nh_sz = conf->trie.nh_sz;
+	num_tbl8 = conf->trie.num_tbl8;
+
+	snprintf(mem_name, sizeof(mem_name), "DP_%s", name);
+	dp = rte_zmalloc_socket(name, sizeof(struct rte_trie_tbl) +
+		TRIE_TBL24_NUM_ENT * (1 << nh_sz), RTE_CACHE_LINE_SIZE,
+		socket_id);
+	if (dp == NULL) {
+		rte_errno = ENOMEM;
+		return dp;
+	}
+
+	write_to_dp(&dp->tbl24, (def_nh << 1), nh_sz, 1 << 24);
+
+	snprintf(mem_name, sizeof(mem_name), "TBL8_%p", dp);
+	dp->tbl8 = rte_zmalloc_socket(mem_name, TRIE_TBL8_GRP_NUM_ENT *
+			(1ll << nh_sz) * (num_tbl8 + 1),
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (dp->tbl8 == NULL) {
+		rte_errno = ENOMEM;
+		rte_free(dp);
+		return NULL;
+	}
+	dp->def_nh = def_nh;
+	dp->nh_sz = nh_sz;
+	dp->number_tbl8s = num_tbl8;
+
+	snprintf(mem_name, sizeof(mem_name), "TBL8_idxes_%p", dp);
+	dp->tbl8_pool = rte_zmalloc_socket(mem_name,
+			sizeof(uint32_t) * dp->number_tbl8s,
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (dp->tbl8_pool == NULL) {
+		rte_errno = ENOMEM;
+		rte_free(dp->tbl8);
+		rte_free(dp);
+		return NULL;
+	}
+
+	tbl8_pool_init(dp);
+
+	return dp;
+}
+
+void
+trie_free(void *p)
+{
+	struct rte_trie_tbl *dp = (struct rte_trie_tbl *)p;
+
+	rte_free(dp->tbl8_pool);
+	rte_free(dp->tbl8);
+	rte_free(dp);
+}
+
diff --git a/lib/librte_fib/trie.h b/lib/librte_fib/trie.h
new file mode 100644
index 0000000..7762fb9
--- /dev/null
+++ b/lib/librte_fib/trie.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _TRIE_H_
+#define _TRIE_H_
+
+/**
+ * @file
+ * RTE IPv6 Longest Prefix Match (LPM)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *
+trie_create(const char *name, int socket_id, struct rte_fib6_conf *conf);
+
+void
+trie_free(void *p);
+
+rte_fib6_lookup_fn_t
+rte_trie_get_lookup_fn(struct rte_fib6_conf *fib_conf);
+
+int
+trie_modify(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t depth, uint64_t next_hop, int op);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TRIE_H_ */
+
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 09/12] test/fib: add FIB library autotests
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
                     ` (9 preceding siblings ...)
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 08/12] fib: add dataplane algorithm for ipv6 Vladimir Medvedkin
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 10/12] test/fib: add ipv6 support for FIB autotests Vladimir Medvedkin
                     ` (2 subsequent siblings)
  13 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

Functional tests for the new FIB library.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |  12 ++
 app/test/meson.build      |   4 +
 app/test/test_fib.c       | 414 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 431 insertions(+)
 create mode 100644 app/test/test_fib.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 4638426..b67ca16 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -126,6 +126,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite_lf.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib.c
 SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib6.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index f35b3cc..18a8595 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -123,6 +123,18 @@
         "Report":  None,
     },
     {
+        "Name":    "FIB autotest",
+        "Command": "fib_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
+        "Name":    "FIB slow autotest",
+        "Command": "fib_slow_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
         "Name":    "Memcpy autotest",
         "Command": "memcpy_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 49c1de0..c5d62de 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -47,6 +47,7 @@ test_sources = files('commands.c',
 	'test_eventdev.c',
 	'test_external_mem.c',
 	'test_fbarray.c',
+	'test_fib.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
 	'test_hash.c',
@@ -137,6 +138,7 @@ test_deps = ['acl',
 	'efd',
 	'ethdev',
 	'eventdev',
+	'fib',
 	'flow_classify',
 	'hash',
 	'ipsec',
@@ -180,6 +182,7 @@ fast_test_names = [
         'eal_fs_autotest',
         'errno_autotest',
         'event_ring_autotest',
+        'fib_autotest',
         'func_reentrancy_autotest',
         'flow_classify_autotest',
         'hash_autotest',
@@ -250,6 +253,7 @@ perf_test_names = [
         'reciprocal_division',
         'reciprocal_division_perf',
         'lpm_perf_autotest',
+        'fib_slow_autotest',
         'red_all',
         'barrier_autotest',
         'hash_multiwriter_autotest',
diff --git a/app/test/test_fib.c b/app/test/test_fib.c
new file mode 100644
index 0000000..ca80a5d
--- /dev/null
+++ b/app/test/test_fib.c
@@ -0,0 +1,414 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <rte_ip.h>
+#include <rte_log.h>
+#include <rte_fib.h>
+
+#include "test.h"
+
+typedef int32_t (*rte_fib_test)(void);
+
+static int32_t test_create_invalid(void);
+static int32_t test_multiple_create(void);
+static int32_t test_free_null(void);
+static int32_t test_add_del_invalid(void);
+static int32_t test_get_invalid(void);
+static int32_t test_lookup(void);
+
+#define MAX_ROUTES	(1 << 16)
+#define MAX_TBL8	(1 << 15)
+
+/*
+ * Check that rte_fib_create fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test_create_invalid(void)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_fib_conf config;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = 0;
+	config.type = RTE_FIB_DUMMY;
+
+	/* rte_fib_create: fib name == NULL */
+	fib = rte_fib_create(NULL, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib_create: config == NULL */
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, NULL);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* socket_id < -1 is invalid */
+	fib = rte_fib_create(__func__, -2, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib_create: max_routes = 0 */
+	config.max_routes = 0;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.max_routes = MAX_ROUTES;
+
+	config.type = RTE_FIB_TYPE_MAX;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	config.type = RTE_FIB_DIR24_8;
+	config.dir24_8.num_tbl8 = MAX_TBL8;
+
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_8B + 1;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_8B;
+
+	config.dir24_8.num_tbl8 = 0;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Create fib table then delete fib table 10 times
+ * Use a slightly different rules size each time
+ */
+int32_t
+test_multiple_create(void)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_fib_conf config;
+	int32_t i;
+
+	config.default_nh = 0;
+	config.type = RTE_FIB_DUMMY;
+
+	for (i = 0; i < 100; i++) {
+		config.max_routes = MAX_ROUTES - i;
+		fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+		RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+		rte_fib_free(fib);
+	}
+	/* Can not test free so return success */
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call rte_fib_free for NULL pointer user input. Note: free has no return and
+ * therefore it is impossible to check for failure but this test is added to
+ * increase function coverage metrics and to validate that freeing null does
+ * not crash.
+ */
+int32_t
+test_free_null(void)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_fib_conf config;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = 0;
+	config.type = RTE_FIB_DUMMY;
+
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+
+	rte_fib_free(fib);
+	rte_fib_free(NULL);
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_fib_add and rte_fib_delete fails gracefully
+ * for incorrect user input arguments
+ */
+int32_t
+test_add_del_invalid(void)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_fib_conf config;
+	uint64_t nh = 100;
+	uint32_t ip = RTE_IPV4(0, 0, 0, 0);
+	int ret;
+	uint8_t depth = 24;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = 0;
+	config.type = RTE_FIB_DUMMY;
+
+	/* rte_fib_add: fib == NULL */
+	ret = rte_fib_add(NULL, ip, depth, nh);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib_delete: fib == NULL */
+	ret = rte_fib_delete(NULL, ip, depth);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/*Create valid fib to use in rest of test. */
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+
+	/* rte_fib_add: depth > RTE_FIB_MAXDEPTH */
+	ret = rte_fib_add(fib, ip, RTE_FIB_MAXDEPTH + 1, nh);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib_delete: depth > RTE_FIB_MAXDEPTH */
+	ret = rte_fib_delete(fib, ip, RTE_FIB_MAXDEPTH + 1);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	rte_fib_free(fib);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_fib_get_dp and rte_fib_get_rib fails gracefully
+ * for incorrect user input arguments
+ */
+int32_t
+test_get_invalid(void)
+{
+	void *p;
+
+	p = rte_fib_get_dp(NULL);
+	RTE_TEST_ASSERT(p == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	p = rte_fib_get_rib(NULL);
+	RTE_TEST_ASSERT(p == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Add routes for one supernet with all possible depths and do lookup
+ * on each step
+ * After delete routes with doing lookup on each step
+ */
+static int
+lookup_and_check_asc(struct rte_fib *fib, uint32_t ip_arr[RTE_FIB_MAXDEPTH],
+	uint32_t ip_missing, uint64_t def_nh, uint32_t n)
+{
+	uint64_t nh_arr[RTE_FIB_MAXDEPTH];
+	int ret;
+	uint32_t i = 0;
+
+	ret = rte_fib_lookup_bulk(fib, ip_arr, nh_arr, RTE_FIB_MAXDEPTH);
+	RTE_TEST_ASSERT(ret == 0, "Failed to lookup\n");
+
+	for (; i <= RTE_FIB_MAXDEPTH - n; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == n,
+			"Failed to get proper nexthop\n");
+
+	for (; i < RTE_FIB_MAXDEPTH; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == --n,
+			"Failed to get proper nexthop\n");
+
+	ret = rte_fib_lookup_bulk(fib, &ip_missing, nh_arr, 1);
+	RTE_TEST_ASSERT((ret == 0) && (nh_arr[0] == def_nh),
+		"Failed to get proper nexthop\n");
+
+	return TEST_SUCCESS;
+}
+
+static int
+lookup_and_check_desc(struct rte_fib *fib, uint32_t ip_arr[RTE_FIB_MAXDEPTH],
+	uint32_t ip_missing, uint64_t def_nh, uint32_t n)
+{
+	uint64_t nh_arr[RTE_FIB_MAXDEPTH];
+	int ret;
+	uint32_t i = 0;
+
+	ret = rte_fib_lookup_bulk(fib, ip_arr, nh_arr, RTE_FIB_MAXDEPTH);
+	RTE_TEST_ASSERT(ret == 0, "Failed to lookup\n");
+
+	for (; i < n; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == RTE_FIB_MAXDEPTH - i,
+			"Failed to get proper nexthop\n");
+
+	for (; i < RTE_FIB_MAXDEPTH; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == def_nh,
+			"Failed to get proper nexthop\n");
+
+	ret = rte_fib_lookup_bulk(fib, &ip_missing, nh_arr, 1);
+	RTE_TEST_ASSERT((ret == 0) && (nh_arr[0] == def_nh),
+		"Failed to get proper nexthop\n");
+
+	return TEST_SUCCESS;
+}
+
+static int
+check_fib(struct rte_fib *fib)
+{
+	uint64_t def_nh = 100;
+	uint32_t ip_arr[RTE_FIB_MAXDEPTH];
+	uint32_t ip_add = RTE_IPV4(128, 0, 0, 0);
+	uint32_t i, ip_missing = RTE_IPV4(127, 255, 255, 255);
+	int ret;
+
+	for (i = 0; i < RTE_FIB_MAXDEPTH; i++)
+		ip_arr[i] = ip_add + (1ULL << i) - 1;
+
+	ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh, 0);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+
+	for (i = 1; i <= RTE_FIB_MAXDEPTH; i++) {
+		ret = rte_fib_add(fib, ip_add, i, i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to add a route\n");
+		ret = lookup_and_check_asc(fib, ip_arr, ip_missing,
+				def_nh, i);
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+	}
+
+	for (i = RTE_FIB_MAXDEPTH; i > 1; i--) {
+		ret = rte_fib_delete(fib, ip_add, i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
+		ret = lookup_and_check_asc(fib, ip_arr, ip_missing,
+			def_nh, i - 1);
+
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+	}
+	ret = rte_fib_delete(fib, ip_add, i);
+	RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
+	ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh, 0);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+
+	for (i = 0; i < RTE_FIB_MAXDEPTH; i++) {
+		ret = rte_fib_add(fib, ip_add, RTE_FIB_MAXDEPTH - i,
+			RTE_FIB_MAXDEPTH - i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to add a route\n");
+		ret = lookup_and_check_desc(fib, ip_arr, ip_missing,
+			def_nh, i + 1);
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+	}
+
+	for (i = 1; i <= RTE_FIB_MAXDEPTH; i++) {
+		ret = rte_fib_delete(fib, ip_add, i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
+		ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh,
+			RTE_FIB_MAXDEPTH - i);
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+	}
+
+	return TEST_SUCCESS;
+}
+
+int32_t
+test_lookup(void)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_fib_conf config;
+	uint64_t def_nh = 100;
+	int ret;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = def_nh;
+	config.type = RTE_FIB_DUMMY;
+
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for DUMMY type\n");
+	rte_fib_free(fib);
+
+	config.type = RTE_FIB_DIR24_8;
+
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_1B;
+	config.dir24_8.num_tbl8 = 127;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for DIR24_8_1B type\n");
+	rte_fib_free(fib);
+
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_2B;
+	config.dir24_8.num_tbl8 = MAX_TBL8 - 1;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for DIR24_8_2B type\n");
+	rte_fib_free(fib);
+
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_4B;
+	config.dir24_8.num_tbl8 = MAX_TBL8;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for DIR24_8_4B type\n");
+	rte_fib_free(fib);
+
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_8B;
+	config.dir24_8.num_tbl8 = MAX_TBL8;
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for DIR24_8_8B type\n");
+	rte_fib_free(fib);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite fib_fast_tests = {
+	.suite_name = "fib autotest",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+	TEST_CASE(test_create_invalid),
+	TEST_CASE(test_free_null),
+	TEST_CASE(test_add_del_invalid),
+	TEST_CASE(test_get_invalid),
+	TEST_CASE(test_lookup),
+	TEST_CASES_END()
+	}
+};
+
+static struct unit_test_suite fib_slow_tests = {
+	.suite_name = "fib slow autotest",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+	TEST_CASE(test_multiple_create),
+	TEST_CASES_END()
+	}
+};
+
+/*
+ * Do all unit tests.
+ */
+static int
+test_fib(void)
+{
+	return unit_test_suite_runner(&fib_fast_tests);
+}
+
+static int
+test_slow_fib(void)
+{
+	return unit_test_suite_runner(&fib_slow_tests);
+}
+
+REGISTER_TEST_COMMAND(fib_autotest, test_fib);
+REGISTER_TEST_COMMAND(fib_slow_autotest, test_slow_fib);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 10/12] test/fib: add ipv6 support for FIB autotests
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
                     ` (10 preceding siblings ...)
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 09/12] test/fib: add FIB library autotests Vladimir Medvedkin
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 11/12] test/fib: add FIB library performance autotests Vladimir Medvedkin
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 12/12] test/fib: add FIB library ipv6 " Vladimir Medvedkin
  13 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

Functional ipv6 tests for the FIB library.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |  12 ++
 app/test/meson.build      |   3 +
 app/test/test_fib6.c      | 423 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 439 insertions(+)
 create mode 100644 app/test/test_fib6.c

diff --git a/app/test/Makefile b/app/test/Makefile
index b67ca16..c230dbf 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -127,6 +127,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite_lf.c
 SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib.c
 SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib6.c
 SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib6.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 18a8595..5c220db 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -135,6 +135,18 @@
         "Report":  None,
     },
     {
+        "Name":    "FIB6 autotest",
+        "Command": "fib6_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
+        "Name":    "FIB6 slow autotest",
+        "Command": "fib6_slow_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
         "Name":    "Memcpy autotest",
         "Command": "memcpy_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index c5d62de..0b83ada 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -48,6 +48,7 @@ test_sources = files('commands.c',
 	'test_external_mem.c',
 	'test_fbarray.c',
 	'test_fib.c',
+	'test_fib6.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
 	'test_hash.c',
@@ -183,6 +184,7 @@ fast_test_names = [
         'errno_autotest',
         'event_ring_autotest',
         'fib_autotest',
+        'fib6_autotest',
         'func_reentrancy_autotest',
         'flow_classify_autotest',
         'hash_autotest',
@@ -263,6 +265,7 @@ perf_test_names = [
         'member_perf_autotest',
         'efd_perf_autotest',
         'lpm6_perf_autotest',
+        'fib6_slow_autotest',
         'rcu_qsbr_perf_autotest',
         'red_perf',
         'distributor_perf_autotest',
diff --git a/app/test/test_fib6.c b/app/test/test_fib6.c
new file mode 100644
index 0000000..af589fe
--- /dev/null
+++ b/app/test/test_fib6.c
@@ -0,0 +1,423 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <rte_memory.h>
+#include <rte_log.h>
+#include <rte_rib6.h>
+#include <rte_fib6.h>
+
+#include "test.h"
+
+typedef int32_t (*rte_fib6_test)(void);
+
+static int32_t test_create_invalid(void);
+static int32_t test_multiple_create(void);
+static int32_t test_free_null(void);
+static int32_t test_add_del_invalid(void);
+static int32_t test_get_invalid(void);
+static int32_t test_lookup(void);
+
+#define MAX_ROUTES	(1 << 16)
+/** Maximum number of tbl8 for 2-byte entries */
+#define MAX_TBL8	(1 << 15)
+
+/*
+ * Check that rte_fib6_create fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test_create_invalid(void)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_fib6_conf config;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = 0;
+	config.type = RTE_FIB6_DUMMY;
+
+	/* rte_fib6_create: fib name == NULL */
+	fib = rte_fib6_create(NULL, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib6_create: config == NULL */
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, NULL);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* socket_id < -1 is invalid */
+	fib = rte_fib6_create(__func__, -2, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib6_create: max_routes = 0 */
+	config.max_routes = 0;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.max_routes = MAX_ROUTES;
+
+	config.type = RTE_FIB6_TYPE_MAX;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	config.type = RTE_FIB6_TRIE;
+	config.trie.num_tbl8 = MAX_TBL8;
+
+	config.trie.nh_sz = RTE_FIB6_TRIE_8B + 1;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.trie.nh_sz = RTE_FIB6_TRIE_8B;
+
+	config.trie.num_tbl8 = 0;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Create fib table then delete fib table 10 times
+ * Use a slightly different rules size each time
+ */
+int32_t
+test_multiple_create(void)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_fib6_conf config;
+	int32_t i;
+
+	config.default_nh = 0;
+	config.type = RTE_FIB6_DUMMY;
+
+	for (i = 0; i < 100; i++) {
+		config.max_routes = MAX_ROUTES - i;
+		fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+		RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+		rte_fib6_free(fib);
+	}
+	/* Can not test free so return success */
+	return TEST_SUCCESS;
+}
+
+/*
+ * Call rte_fib6_free for NULL pointer user input. Note: free has no return and
+ * therefore it is impossible to check for failure but this test is added to
+ * increase function coverage metrics and to validate that freeing null does
+ * not crash.
+ */
+int32_t
+test_free_null(void)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_fib6_conf config;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = 0;
+	config.type = RTE_FIB6_DUMMY;
+
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+
+	rte_fib6_free(fib);
+	rte_fib6_free(NULL);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_fib6_add and rte_fib6_delete fails gracefully
+ * for incorrect user input arguments
+ */
+int32_t
+test_add_del_invalid(void)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_fib6_conf config;
+	uint64_t nh = 100;
+	uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE] = {0};
+	int ret;
+	uint8_t depth = 24;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = 0;
+	config.type = RTE_FIB6_DUMMY;
+
+	/* rte_fib6_add: fib == NULL */
+	ret = rte_fib6_add(NULL, ip, depth, nh);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib6_delete: fib == NULL */
+	ret = rte_fib6_delete(NULL, ip, depth);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/*Create valid fib to use in rest of test. */
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+
+	/* rte_fib6_add: depth > RTE_FIB6_MAXDEPTH */
+	ret = rte_fib6_add(fib, ip, RTE_FIB6_MAXDEPTH + 1, nh);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* rte_fib6_delete: depth > RTE_FIB6_MAXDEPTH */
+	ret = rte_fib6_delete(fib, ip, RTE_FIB6_MAXDEPTH + 1);
+	RTE_TEST_ASSERT(ret < 0,
+		"Call succeeded with invalid parameters\n");
+
+	rte_fib6_free(fib);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_fib6_get_dp and rte_fib6_get_rib fails gracefully
+ * for incorrect user input arguments
+ */
+int32_t
+test_get_invalid(void)
+{
+	void *p;
+
+	p = rte_fib6_get_dp(NULL);
+	RTE_TEST_ASSERT(p == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	p = rte_fib6_get_rib(NULL);
+	RTE_TEST_ASSERT(p == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Add routes for one supernet with all possible depths and do lookup
+ * on each step
+ * After delete routes with doing lookup on each step
+ */
+static int
+lookup_and_check_asc(struct rte_fib6 *fib,
+	uint8_t ip_arr[RTE_FIB6_MAXDEPTH][RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t ip_missing[][RTE_FIB6_IPV6_ADDR_SIZE], uint64_t def_nh,
+	uint32_t n)
+{
+	uint64_t nh_arr[RTE_FIB6_MAXDEPTH];
+	int ret;
+	uint32_t i = 0;
+
+	ret = rte_fib6_lookup_bulk(fib, ip_arr, nh_arr, RTE_FIB6_MAXDEPTH);
+	RTE_TEST_ASSERT(ret == 0, "Failed to lookup\n");
+
+	for (; i <= RTE_FIB6_MAXDEPTH - n; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == n,
+			"Failed to get proper nexthop\n");
+
+	for (; i < RTE_FIB6_MAXDEPTH; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == --n,
+			"Failed to get proper nexthop\n");
+
+	ret = rte_fib6_lookup_bulk(fib, ip_missing, nh_arr, 1);
+	RTE_TEST_ASSERT((ret == 0) && (nh_arr[0] == def_nh),
+		"Failed to get proper nexthop\n");
+
+	return TEST_SUCCESS;
+}
+
+static int
+lookup_and_check_desc(struct rte_fib6 *fib,
+	uint8_t ip_arr[RTE_FIB6_MAXDEPTH][RTE_FIB6_IPV6_ADDR_SIZE],
+	uint8_t ip_missing[][RTE_FIB6_IPV6_ADDR_SIZE], uint64_t def_nh,
+	uint32_t n)
+{
+	uint64_t nh_arr[RTE_FIB6_MAXDEPTH];
+	int ret;
+	uint32_t i = 0;
+
+	ret = rte_fib6_lookup_bulk(fib, ip_arr, nh_arr, RTE_FIB6_MAXDEPTH);
+	RTE_TEST_ASSERT(ret == 0, "Failed to lookup\n");
+
+	for (; i < n; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == RTE_FIB6_MAXDEPTH - i,
+			"Failed to get proper nexthop\n");
+
+	for (; i < RTE_FIB6_MAXDEPTH; i++)
+		RTE_TEST_ASSERT(nh_arr[i] == def_nh,
+			"Failed to get proper nexthop\n");
+
+	ret = rte_fib6_lookup_bulk(fib, ip_missing, nh_arr, 1);
+	RTE_TEST_ASSERT((ret == 0) && (nh_arr[0] == def_nh),
+		"Failed to get proper nexthop\n");
+
+	return TEST_SUCCESS;
+}
+
+static int
+check_fib(struct rte_fib6 *fib)
+{
+	uint64_t def_nh = 100;
+	uint8_t ip_arr[RTE_FIB6_MAXDEPTH][RTE_FIB6_IPV6_ADDR_SIZE];
+	uint8_t ip_add[RTE_FIB6_IPV6_ADDR_SIZE] = {0};
+	uint8_t ip_missing[1][RTE_FIB6_IPV6_ADDR_SIZE] = { {255} };
+	uint32_t i, j;
+	int ret;
+
+	ip_add[0] = 128;
+	ip_missing[0][0] = 127;
+	for (i = 0; i < RTE_FIB6_MAXDEPTH; i++) {
+		for (j = 0; j < RTE_FIB6_IPV6_ADDR_SIZE; j++) {
+			ip_arr[i][j] = ip_add[j] |
+				~get_msk_part(RTE_FIB6_MAXDEPTH - i, j);
+		}
+	}
+
+	ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh, 0);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
+
+	for (i = 1; i <= RTE_FIB6_MAXDEPTH; i++) {
+		ret = rte_fib6_add(fib, ip_add, i, i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to add a route\n");
+		ret = lookup_and_check_asc(fib, ip_arr, ip_missing, def_nh, i);
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+			"Lookup and check fails\n");
+	}
+
+	for (i = RTE_FIB6_MAXDEPTH; i > 1; i--) {
+		ret = rte_fib6_delete(fib, ip_add, i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
+		ret = lookup_and_check_asc(fib, ip_arr, ip_missing,
+			def_nh, i - 1);
+
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+			"Lookup and check fails\n");
+	}
+	ret = rte_fib6_delete(fib, ip_add, i);
+	RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
+	ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh, 0);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Lookup and check fails\n");
+
+	for (i = 0; i < RTE_FIB6_MAXDEPTH; i++) {
+		ret = rte_fib6_add(fib, ip_add, RTE_FIB6_MAXDEPTH - i,
+			RTE_FIB6_MAXDEPTH - i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to add a route\n");
+		ret = lookup_and_check_desc(fib, ip_arr, ip_missing,
+			def_nh, i + 1);
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+			"Lookup and check fails\n");
+	}
+
+	for (i = 1; i <= RTE_FIB6_MAXDEPTH; i++) {
+		ret = rte_fib6_delete(fib, ip_add, i);
+		RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
+		ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh,
+			RTE_FIB6_MAXDEPTH - i);
+		RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+			"Lookup and check fails\n");
+	}
+
+	return TEST_SUCCESS;
+}
+
+int32_t
+test_lookup(void)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_fib6_conf config;
+	uint64_t def_nh = 100;
+	int ret;
+
+	config.max_routes = MAX_ROUTES;
+	config.default_nh = def_nh;
+	config.type = RTE_FIB6_DUMMY;
+
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for DUMMY type\n");
+	rte_fib6_free(fib);
+
+	config.type = RTE_FIB6_TRIE;
+
+	config.trie.nh_sz = RTE_FIB6_TRIE_2B;
+	config.trie.num_tbl8 = MAX_TBL8 - 1;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for TRIE_2B type\n");
+	rte_fib6_free(fib);
+
+	config.trie.nh_sz = RTE_FIB6_TRIE_4B;
+	config.trie.num_tbl8 = MAX_TBL8;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for TRIE_4B type\n");
+	rte_fib6_free(fib);
+
+	config.trie.nh_sz = RTE_FIB6_TRIE_8B;
+	config.trie.num_tbl8 = MAX_TBL8;
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
+	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
+	ret = check_fib(fib);
+	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
+		"Check_fib fails for TRIE_8B type\n");
+	rte_fib6_free(fib);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite fib6_fast_tests = {
+	.suite_name = "fib6 autotest",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+	TEST_CASE(test_create_invalid),
+	TEST_CASE(test_free_null),
+	TEST_CASE(test_add_del_invalid),
+	TEST_CASE(test_get_invalid),
+	TEST_CASE(test_lookup),
+	TEST_CASES_END()
+	}
+};
+
+static struct unit_test_suite fib6_slow_tests = {
+	.suite_name = "fib6 slow autotest",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+	TEST_CASE(test_multiple_create),
+	TEST_CASES_END()
+	}
+};
+
+/*
+ * Do all unit tests.
+ */
+static int
+test_fib6(void)
+{
+	return unit_test_suite_runner(&fib6_fast_tests);
+}
+
+static int
+test_slow_fib6(void)
+{
+	return unit_test_suite_runner(&fib6_slow_tests);
+}
+
+REGISTER_TEST_COMMAND(fib6_autotest, test_fib6);
+REGISTER_TEST_COMMAND(fib6_slow_autotest, test_slow_fib6);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 11/12] test/fib: add FIB library performance autotests
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
                     ` (11 preceding siblings ...)
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 10/12] test/fib: add ipv6 support for FIB autotests Vladimir Medvedkin
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 12/12] test/fib: add FIB library ipv6 " Vladimir Medvedkin
  13 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

Performance tests for the new FIB library.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |   6 +
 app/test/meson.build      |   2 +
 app/test/test_fib_perf.c  | 411 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 420 insertions(+)
 create mode 100644 app/test/test_fib_perf.c

diff --git a/app/test/Makefile b/app/test/Makefile
index c230dbf..3a3f920 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -128,6 +128,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib.c
 SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib6.c
 SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib.c
 SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib6.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib_perf.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 5c220db..0783979 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -719,6 +719,12 @@
         "Report":  None,
     },
     {
+        "Name":    "FIB perf autotest",
+        "Command": "fib_perf_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
          "Name":    "Efd perf autotest",
          "Command": "efd_perf_autotest",
          "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 0b83ada..929883a 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -48,6 +48,7 @@ test_sources = files('commands.c',
 	'test_external_mem.c',
 	'test_fbarray.c',
 	'test_fib.c',
+	'test_fib_perf.c',
 	'test_fib6.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
@@ -256,6 +257,7 @@ perf_test_names = [
         'reciprocal_division_perf',
         'lpm_perf_autotest',
         'fib_slow_autotest',
+        'fib_perf_autotest',
         'red_all',
         'barrier_autotest',
         'hash_multiwriter_autotest',
diff --git a/app/test/test_fib_perf.c b/app/test/test_fib_perf.c
new file mode 100644
index 0000000..573087c
--- /dev/null
+++ b/app/test/test_fib_perf.c
@@ -0,0 +1,411 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <rte_cycles.h>
+#include <rte_random.h>
+#include <rte_branch_prediction.h>
+#include <rte_ip.h>
+#include <rte_fib.h>
+
+#include "test.h"
+#include "test_xmmt_ops.h"
+
+#define TEST_FIB_ASSERT(cond) do {				\
+	if (!(cond)) {						\
+		printf("Error at line %d:\n", __LINE__);	\
+		return -1;					\
+	}                                                       \
+} while (0)
+
+#define ITERATIONS (1 << 10)
+#define BATCH_SIZE (1 << 12)
+#define BULK_SIZE 32
+
+#define MAX_RULE_NUM (1200000)
+
+struct route_rule {
+	uint32_t ip;
+	uint8_t depth;
+};
+
+struct route_rule large_route_table[MAX_RULE_NUM];
+
+static uint32_t num_route_entries;
+#define NUM_ROUTE_ENTRIES num_route_entries
+
+enum {
+	IP_CLASS_A,
+	IP_CLASS_B,
+	IP_CLASS_C
+};
+#define RTE_FIB_MAX_DEPTH	32
+/* struct route_rule_count defines the total number of rules in following a/b/c
+ * each item in a[]/b[]/c[] is the number of common IP address class A/B/C, not
+ * including the ones for private local network.
+ */
+struct route_rule_count {
+	uint32_t a[RTE_FIB_MAX_DEPTH];
+	uint32_t b[RTE_FIB_MAX_DEPTH];
+	uint32_t c[RTE_FIB_MAX_DEPTH];
+};
+
+/* All following numbers of each depth of each common IP class are just
+ * got from previous large constant table in app/test/test_lpm_routes.h .
+ * In order to match similar performance, they keep same depth and IP
+ * address coverage as previous constant table. These numbers don't
+ * include any private local IP address. As previous large const rule
+ * table was just dumped from a real router, there are no any IP address
+ * in class C or D.
+ */
+static struct route_rule_count rule_count = {
+	.a = { /* IP class A in which the most significant bit is 0 */
+		    0, /* depth =  1 */
+		    0, /* depth =  2 */
+		    1, /* depth =  3 */
+		    0, /* depth =  4 */
+		    2, /* depth =  5 */
+		    1, /* depth =  6 */
+		    3, /* depth =  7 */
+		  185, /* depth =  8 */
+		   26, /* depth =  9 */
+		   16, /* depth = 10 */
+		   39, /* depth = 11 */
+		  144, /* depth = 12 */
+		  233, /* depth = 13 */
+		  528, /* depth = 14 */
+		  866, /* depth = 15 */
+		 3856, /* depth = 16 */
+		 3268, /* depth = 17 */
+		 5662, /* depth = 18 */
+		17301, /* depth = 19 */
+		22226, /* depth = 20 */
+		11147, /* depth = 21 */
+		16746, /* depth = 22 */
+		17120, /* depth = 23 */
+		77578, /* depth = 24 */
+		  401, /* depth = 25 */
+		  656, /* depth = 26 */
+		 1107, /* depth = 27 */
+		 1121, /* depth = 28 */
+		 2316, /* depth = 29 */
+		  717, /* depth = 30 */
+		   10, /* depth = 31 */
+		   66  /* depth = 32 */
+	},
+	.b = { /* IP class A in which the most 2 significant bits are 10 */
+		    0, /* depth =  1 */
+		    0, /* depth =  2 */
+		    0, /* depth =  3 */
+		    0, /* depth =  4 */
+		    1, /* depth =  5 */
+		    1, /* depth =  6 */
+		    1, /* depth =  7 */
+		    3, /* depth =  8 */
+		    3, /* depth =  9 */
+		   30, /* depth = 10 */
+		   25, /* depth = 11 */
+		  168, /* depth = 12 */
+		  305, /* depth = 13 */
+		  569, /* depth = 14 */
+		 1129, /* depth = 15 */
+		50800, /* depth = 16 */
+		 1645, /* depth = 17 */
+		 1820, /* depth = 18 */
+		 3506, /* depth = 19 */
+		 3258, /* depth = 20 */
+		 3424, /* depth = 21 */
+		 4971, /* depth = 22 */
+		 6885, /* depth = 23 */
+		39771, /* depth = 24 */
+		  424, /* depth = 25 */
+		  170, /* depth = 26 */
+		  433, /* depth = 27 */
+		   92, /* depth = 28 */
+		  366, /* depth = 29 */
+		  377, /* depth = 30 */
+		    2, /* depth = 31 */
+		  200  /* depth = 32 */
+	},
+	.c = { /* IP class A in which the most 3 significant bits are 110 */
+		     0, /* depth =  1 */
+		     0, /* depth =  2 */
+		     0, /* depth =  3 */
+		     0, /* depth =  4 */
+		     0, /* depth =  5 */
+		     0, /* depth =  6 */
+		     0, /* depth =  7 */
+		    12, /* depth =  8 */
+		     8, /* depth =  9 */
+		     9, /* depth = 10 */
+		    33, /* depth = 11 */
+		    69, /* depth = 12 */
+		   237, /* depth = 13 */
+		  1007, /* depth = 14 */
+		  1717, /* depth = 15 */
+		 14663, /* depth = 16 */
+		  8070, /* depth = 17 */
+		 16185, /* depth = 18 */
+		 48261, /* depth = 19 */
+		 36870, /* depth = 20 */
+		 33960, /* depth = 21 */
+		 50638, /* depth = 22 */
+		 61422, /* depth = 23 */
+		466549, /* depth = 24 */
+		  1829, /* depth = 25 */
+		  4824, /* depth = 26 */
+		  4927, /* depth = 27 */
+		  5914, /* depth = 28 */
+		 10254, /* depth = 29 */
+		  4905, /* depth = 30 */
+		     1, /* depth = 31 */
+		   716  /* depth = 32 */
+	}
+};
+
+static void generate_random_rule_prefix(uint32_t ip_class, uint8_t depth)
+{
+/* IP address class A, the most significant bit is 0 */
+#define IP_HEAD_MASK_A			0x00000000
+#define IP_HEAD_BIT_NUM_A		1
+
+/* IP address class B, the most significant 2 bits are 10 */
+#define IP_HEAD_MASK_B			0x80000000
+#define IP_HEAD_BIT_NUM_B		2
+
+/* IP address class C, the most significant 3 bits are 110 */
+#define IP_HEAD_MASK_C			0xC0000000
+#define IP_HEAD_BIT_NUM_C		3
+
+	uint32_t class_depth;
+	uint32_t range;
+	uint32_t mask;
+	uint32_t step;
+	uint32_t start;
+	uint32_t fixed_bit_num;
+	uint32_t ip_head_mask;
+	uint32_t rule_num;
+	uint32_t k;
+	struct route_rule *ptr_rule;
+
+	if (ip_class == IP_CLASS_A) {        /* IP Address class A */
+		fixed_bit_num = IP_HEAD_BIT_NUM_A;
+		ip_head_mask = IP_HEAD_MASK_A;
+		rule_num = rule_count.a[depth - 1];
+	} else if (ip_class == IP_CLASS_B) { /* IP Address class B */
+		fixed_bit_num = IP_HEAD_BIT_NUM_B;
+		ip_head_mask = IP_HEAD_MASK_B;
+		rule_num = rule_count.b[depth - 1];
+	} else {                             /* IP Address class C */
+		fixed_bit_num = IP_HEAD_BIT_NUM_C;
+		ip_head_mask = IP_HEAD_MASK_C;
+		rule_num = rule_count.c[depth - 1];
+	}
+
+	if (rule_num == 0)
+		return;
+
+	/* the number of rest bits which don't include the most significant
+	 * fixed bits for this IP address class
+	 */
+	class_depth = depth - fixed_bit_num;
+
+	/* range is the maximum number of rules for this depth and
+	 * this IP address class
+	 */
+	range = 1 << class_depth;
+
+	/* only mask the most depth significant generated bits
+	 * except fixed bits for IP address class
+	 */
+	mask = range - 1;
+
+	/* Widen coverage of IP address in generated rules */
+	if (range <= rule_num)
+		step = 1;
+	else
+		step = round((double)range / rule_num);
+
+	/* Only generate rest bits except the most significant
+	 * fixed bits for IP address class
+	 */
+	start = lrand48() & mask;
+	ptr_rule = &large_route_table[num_route_entries];
+	for (k = 0; k < rule_num; k++) {
+		ptr_rule->ip = (start << (RTE_FIB_MAX_DEPTH - depth))
+			| ip_head_mask;
+		ptr_rule->depth = depth;
+		ptr_rule++;
+		start = (start + step) & mask;
+	}
+	num_route_entries += rule_num;
+}
+
+static void insert_rule_in_random_pos(uint32_t ip, uint8_t depth)
+{
+	uint32_t pos;
+	int try_count = 0;
+	struct route_rule tmp;
+
+	do {
+		pos = lrand48();
+		try_count++;
+	} while ((try_count < 10) && (pos > num_route_entries));
+
+	if ((pos > num_route_entries) || (pos >= MAX_RULE_NUM))
+		pos = num_route_entries >> 1;
+
+	tmp = large_route_table[pos];
+	large_route_table[pos].ip = ip;
+	large_route_table[pos].depth = depth;
+	if (num_route_entries < MAX_RULE_NUM)
+		large_route_table[num_route_entries++] = tmp;
+}
+
+static void generate_large_route_rule_table(void)
+{
+	uint32_t ip_class;
+	uint8_t  depth;
+
+	num_route_entries = 0;
+	memset(large_route_table, 0, sizeof(large_route_table));
+
+	for (ip_class = IP_CLASS_A; ip_class <= IP_CLASS_C; ip_class++) {
+		for (depth = 1; depth <= RTE_FIB_MAX_DEPTH; depth++)
+			generate_random_rule_prefix(ip_class, depth);
+	}
+
+	/* Add following rules to keep same as previous large constant table,
+	 * they are 4 rules with private local IP address and 1 all-zeros prefix
+	 * with depth = 8.
+	 */
+	insert_rule_in_random_pos(RTE_IPV4(0, 0, 0, 0), 8);
+	insert_rule_in_random_pos(RTE_IPV4(10, 2, 23, 147), 32);
+	insert_rule_in_random_pos(RTE_IPV4(192, 168, 100, 10), 24);
+	insert_rule_in_random_pos(RTE_IPV4(192, 168, 25, 100), 24);
+	insert_rule_in_random_pos(RTE_IPV4(192, 168, 129, 124), 32);
+}
+
+static void
+print_route_distribution(const struct route_rule *table, uint32_t n)
+{
+	unsigned int i, j;
+
+	printf("Route distribution per prefix width:\n");
+	printf("DEPTH    QUANTITY (PERCENT)\n");
+	printf("---------------------------\n");
+
+	/* Count depths. */
+	for (i = 1; i <= 32; i++) {
+		unsigned int depth_counter = 0;
+		double percent_hits;
+
+		for (j = 0; j < n; j++)
+			if (table[j].depth == (uint8_t) i)
+				depth_counter++;
+
+		percent_hits = ((double)depth_counter)/((double)n) * 100;
+		printf("%.2u%15u (%.2f)\n", i, depth_counter, percent_hits);
+	}
+	printf("\n");
+}
+
+static int
+test_fib_perf(void)
+{
+	struct rte_fib *fib = NULL;
+	struct rte_fib_conf config;
+
+	config.max_routes = 2000000;
+	config.type = RTE_FIB_DIR24_8;
+	config.default_nh = 0;
+	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_4B;
+	config.dir24_8.num_tbl8 = 65535;
+	uint64_t begin, total_time;
+	unsigned int i, j;
+	uint32_t next_hop_add = 0xAA;
+	int status = 0;
+	int64_t count = 0;
+
+	rte_srand(rte_rdtsc());
+
+	generate_large_route_rule_table();
+
+	printf("No. routes = %u\n", (unsigned int) NUM_ROUTE_ENTRIES);
+
+	print_route_distribution(large_route_table,
+		(uint32_t) NUM_ROUTE_ENTRIES);
+
+	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
+	TEST_FIB_ASSERT(fib != NULL);
+
+	/* Measue add. */
+	begin = rte_rdtsc();
+
+	for (i = 0; i < NUM_ROUTE_ENTRIES; i++) {
+		if (rte_fib_add(fib, large_route_table[i].ip,
+				large_route_table[i].depth, next_hop_add) == 0)
+			status++;
+	}
+	/* End Timer. */
+	total_time = rte_rdtsc() - begin;
+
+	printf("Unique added entries = %d\n", status);
+
+	printf("Average FIB Add: %g cycles\n",
+			(double)total_time / NUM_ROUTE_ENTRIES);
+
+	/* Measure bulk Lookup */
+	total_time = 0;
+	count = 0;
+	for (i = 0; i < ITERATIONS; i++) {
+		static uint32_t ip_batch[BATCH_SIZE];
+		uint64_t next_hops[BULK_SIZE];
+
+		/* Create array of random IP addresses */
+		for (j = 0; j < BATCH_SIZE; j++)
+			ip_batch[j] = rte_rand();
+
+		/* Lookup per batch */
+		begin = rte_rdtsc();
+		for (j = 0; j < BATCH_SIZE; j += BULK_SIZE) {
+			uint32_t k;
+			rte_fib_lookup_bulk(fib, &ip_batch[j], next_hops,
+				BULK_SIZE);
+			for (k = 0; k < BULK_SIZE; k++)
+				if (unlikely(!(next_hops[k] != 0)))
+					count++;
+		}
+
+		total_time += rte_rdtsc() - begin;
+	}
+	printf("BULK FIB Lookup: %.1f cycles (fails = %.1f%%)\n",
+			(double)total_time / ((double)ITERATIONS * BATCH_SIZE),
+			(count * 100.0) / (double)(ITERATIONS * BATCH_SIZE));
+
+	/* Delete */
+	status = 0;
+	begin = rte_rdtsc();
+	for (i = 0; i < NUM_ROUTE_ENTRIES; i++) {
+		/* rte_lpm_delete(lpm, ip, depth) */
+		status += rte_fib_delete(fib, large_route_table[i].ip,
+				large_route_table[i].depth);
+	}
+
+	total_time += rte_rdtsc() - begin;
+
+	printf("Average FIB Delete: %g cycles\n",
+			(double)total_time / NUM_ROUTE_ENTRIES);
+
+	rte_fib_free(fib);
+
+	return 0;
+}
+
+REGISTER_TEST_COMMAND(fib_perf_autotest, test_fib_perf);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [dpdk-dev] [PATCH v6 12/12] test/fib: add FIB library ipv6 performance autotests
  2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
                     ` (12 preceding siblings ...)
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 11/12] test/fib: add FIB library performance autotests Vladimir Medvedkin
@ 2019-11-01 15:21   ` Vladimir Medvedkin
  13 siblings, 0 replies; 61+ messages in thread
From: Vladimir Medvedkin @ 2019-11-01 15:21 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, konstantin.ananyev, thomas, aconole

Performance ipv6 tests for the FIB library.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |   6 ++
 app/test/meson.build      |   2 +
 app/test/test_fib6_perf.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 166 insertions(+)
 create mode 100644 app/test/test_fib6_perf.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 3a3f920..f792858 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -129,6 +129,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_RIB) += test_rib6.c
 SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib.c
 SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib6.c
 SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib_perf.c
+SRCS-$(CONFIG_RTE_LIBRTE_FIB) += test_fib6_perf.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 0783979..97e4423 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -725,6 +725,12 @@
         "Report":  None,
     },
     {
+        "Name":    "FIB6 perf autotest",
+        "Command": "fib6_perf_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
+    {
          "Name":    "Efd perf autotest",
          "Command": "efd_perf_autotest",
          "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 929883a..42664d8 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -50,6 +50,7 @@ test_sources = files('commands.c',
 	'test_fib.c',
 	'test_fib_perf.c',
 	'test_fib6.c',
+	'test_fib6_perf.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
 	'test_hash.c',
@@ -268,6 +269,7 @@ perf_test_names = [
         'efd_perf_autotest',
         'lpm6_perf_autotest',
         'fib6_slow_autotest',
+        'fib6_perf_autotest',
         'rcu_qsbr_perf_autotest',
         'red_perf',
         'distributor_perf_autotest',
diff --git a/app/test/test_fib6_perf.c b/app/test/test_fib6_perf.c
new file mode 100644
index 0000000..56c799b
--- /dev/null
+++ b/app/test/test_fib6_perf.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2014 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rte_cycles.h>
+#include <rte_random.h>
+#include <rte_memory.h>
+#include <rte_fib6.h>
+
+#include "test.h"
+#include "test_lpm6_data.h"
+
+#define TEST_FIB_ASSERT(cond) do {				\
+	if (!(cond)) {						\
+		printf("Error at line %d:\n", __LINE__);	\
+		return -1;					\
+	}							\
+} while (0)
+
+#define ITERATIONS (1 << 10)
+#define BATCH_SIZE 100000
+#define NUMBER_TBL8S                                           (1 << 16)
+
+static void
+print_route_distribution(const struct rules_tbl_entry *table, uint32_t n)
+{
+	unsigned int i, j;
+
+	printf("Route distribution per prefix width:\n");
+	printf("DEPTH    QUANTITY (PERCENT)\n");
+	printf("---------------------------\n");
+
+	/* Count depths. */
+	for (i = 1; i <= 128; i++) {
+		unsigned int depth_counter = 0;
+		double percent_hits;
+
+		for (j = 0; j < n; j++)
+			if (table[j].depth == (uint8_t) i)
+				depth_counter++;
+
+		percent_hits = ((double)depth_counter)/((double)n) * 100;
+		printf("%.2u%15u (%.2f)\n", i, depth_counter, percent_hits);
+	}
+	printf("\n");
+}
+
+static inline uint8_t
+bits_in_nh(uint8_t nh_sz)
+{
+	return 8 * (1 << nh_sz);
+}
+
+static inline uint64_t
+get_max_nh(uint8_t nh_sz)
+{
+	return ((1ULL << (bits_in_nh(nh_sz) - 1)) - 1);
+}
+
+static int
+test_fib6_perf(void)
+{
+	struct rte_fib6 *fib = NULL;
+	struct rte_fib6_conf conf;
+	uint64_t begin, total_time;
+	unsigned int i, j;
+	uint64_t next_hop_add;
+	int status = 0;
+	int64_t count = 0;
+	uint8_t ip_batch[NUM_IPS_ENTRIES][16];
+	uint64_t next_hops[NUM_IPS_ENTRIES];
+
+	conf.type = RTE_FIB6_TRIE;
+	conf.default_nh = 0;
+	conf.max_routes = 1000000;
+	conf.trie.nh_sz = RTE_FIB6_TRIE_4B;
+	conf.trie.num_tbl8 = RTE_MIN(get_max_nh(conf.trie.nh_sz), 1000000U);
+
+	rte_srand(rte_rdtsc());
+
+	printf("No. routes = %u\n", (unsigned int) NUM_ROUTE_ENTRIES);
+
+	print_route_distribution(large_route_table,
+		(uint32_t)NUM_ROUTE_ENTRIES);
+
+	/* Only generate IPv6 address of each item in large IPS table,
+	 * here next_hop is not needed.
+	 */
+	generate_large_ips_table(0);
+
+	fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &conf);
+	TEST_FIB_ASSERT(fib != NULL);
+
+	/* Measure add. */
+	begin = rte_rdtsc();
+
+	for (i = 0; i < NUM_ROUTE_ENTRIES; i++) {
+		next_hop_add = (i & ((1 << 14) - 1)) + 1;
+		if (rte_fib6_add(fib, large_route_table[i].ip,
+				large_route_table[i].depth, next_hop_add) == 0)
+			status++;
+	}
+	/* End Timer. */
+	total_time = rte_rdtsc() - begin;
+
+	printf("Unique added entries = %d\n", status);
+	printf("Average FIB Add: %g cycles\n",
+			(double)total_time / NUM_ROUTE_ENTRIES);
+
+	/* Measure bulk Lookup */
+	total_time = 0;
+	count = 0;
+
+	for (i = 0; i < NUM_IPS_ENTRIES; i++)
+		memcpy(ip_batch[i], large_ips_table[i].ip, 16);
+
+	for (i = 0; i < ITERATIONS; i++) {
+
+		/* Lookup per batch */
+		begin = rte_rdtsc();
+		rte_fib6_lookup_bulk(fib, ip_batch, next_hops, NUM_IPS_ENTRIES);
+		total_time += rte_rdtsc() - begin;
+
+		for (j = 0; j < NUM_IPS_ENTRIES; j++)
+			if (next_hops[j] == 0)
+				count++;
+	}
+	printf("BULK FIB Lookup: %.1f cycles (fails = %.1f%%)\n",
+			(double)total_time / ((double)ITERATIONS * BATCH_SIZE),
+			(count * 100.0) / (double)(ITERATIONS * BATCH_SIZE));
+
+	/* Delete */
+	status = 0;
+	begin = rte_rdtsc();
+
+	for (i = 0; i < NUM_ROUTE_ENTRIES; i++) {
+		/* rte_fib_delete(fib, ip, depth) */
+		status += rte_fib6_delete(fib, large_route_table[i].ip,
+				large_route_table[i].depth);
+	}
+
+	total_time = rte_rdtsc() - begin;
+
+	printf("Average FIB Delete: %g cycles\n",
+			(double)total_time / NUM_ROUTE_ENTRIES);
+
+	rte_fib6_free(fib);
+
+	return 0;
+}
+
+REGISTER_TEST_COMMAND(fib6_perf_autotest, test_fib6_perf);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v6 00/12] lib: add RIB and FIB liraries
  2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 " Vladimir Medvedkin
@ 2019-11-05 23:14     ` Thomas Monjalon
  2019-11-06  5:50       ` David Marchand
  0 siblings, 1 reply; 61+ messages in thread
From: Thomas Monjalon @ 2019-11-05 23:14 UTC (permalink / raw)
  To: Vladimir Medvedkin; +Cc: dev, bruce.richardson, konstantin.ananyev, aconole

01/11/2019 16:21, Vladimir Medvedkin:
> This is heavily reworked version of previous RIB library series:
> https://mails.dpdk.org/archives/dev/2018-April/099492.html
> 
>  app/test/Makefile                  |   7 +
>  app/test/autotest_data.py          |  48 +++
>  app/test/meson.build               |  16 +
>  app/test/test_fib.c                | 414 ++++++++++++++++++++
>  app/test/test_fib6.c               | 423 +++++++++++++++++++++
>  app/test/test_fib6_perf.c          | 157 ++++++++
>  app/test/test_fib_perf.c           | 411 ++++++++++++++++++++
>  app/test/test_rib.c                | 351 +++++++++++++++++
>  app/test/test_rib6.c               | 357 +++++++++++++++++
>  config/common_base                 |  11 +
>  doc/api/doxy-api.conf.in           |   2 +
>  lib/Makefile                       |   4 +
>  lib/librte_fib/Makefile            |  25 ++
>  lib/librte_fib/dir24_8.c           | 737 +++++++++++++++++++++++++++++++++++
>  lib/librte_fib/dir24_8.h           |  36 ++
>  lib/librte_fib/meson.build         |   8 +
>  lib/librte_fib/rte_fib.c           | 319 ++++++++++++++++
>  lib/librte_fib/rte_fib.h           | 188 +++++++++
>  lib/librte_fib/rte_fib6.c          | 322 ++++++++++++++++
>  lib/librte_fib/rte_fib6.h          | 193 ++++++++++
>  lib/librte_fib/rte_fib_version.map |  23 ++
>  lib/librte_fib/trie.c              | 760 +++++++++++++++++++++++++++++++++++++
>  lib/librte_fib/trie.h              |  37 ++
>  lib/librte_rib/Makefile            |  25 ++
>  lib/librte_rib/meson.build         |   8 +
>  lib/librte_rib/rte_rib.c           | 532 ++++++++++++++++++++++++++
>  lib/librte_rib/rte_rib.h           | 277 ++++++++++++++
>  lib/librte_rib/rte_rib6.c          | 598 +++++++++++++++++++++++++++++
>  lib/librte_rib/rte_rib6.h          | 334 ++++++++++++++++
>  lib/librte_rib/rte_rib_version.map |  35 ++
>  lib/meson.build                    |   4 +-
>  mk/rte.app.mk                      |   2 +
>  32 files changed, 6663 insertions(+), 1 deletion(-)

You forgot to update MAINTAINERS and the release notes.
I did it while merging.

Applied, thanks



^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v6 00/12] lib: add RIB and FIB liraries
  2019-11-05 23:14     ` Thomas Monjalon
@ 2019-11-06  5:50       ` David Marchand
  2019-11-06  7:50         ` Thomas Monjalon
  2019-11-06 11:50         ` Medvedkin, Vladimir
  0 siblings, 2 replies; 61+ messages in thread
From: David Marchand @ 2019-11-06  5:50 UTC (permalink / raw)
  To: Thomas Monjalon, Vladimir Medvedkin
  Cc: dev, Bruce Richardson, Ananyev, Konstantin, Aaron Conole

On Wed, Nov 6, 2019 at 12:14 AM Thomas Monjalon <thomas@monjalon.net> wrote:
>
> 01/11/2019 16:21, Vladimir Medvedkin:
> > This is heavily reworked version of previous RIB library series:
> > https://mails.dpdk.org/archives/dev/2018-April/099492.html
> >
> >  app/test/Makefile                  |   7 +
> >  app/test/autotest_data.py          |  48 +++
> >  app/test/meson.build               |  16 +
> >  app/test/test_fib.c                | 414 ++++++++++++++++++++
> >  app/test/test_fib6.c               | 423 +++++++++++++++++++++
> >  app/test/test_fib6_perf.c          | 157 ++++++++
> >  app/test/test_fib_perf.c           | 411 ++++++++++++++++++++
> >  app/test/test_rib.c                | 351 +++++++++++++++++
> >  app/test/test_rib6.c               | 357 +++++++++++++++++
> >  config/common_base                 |  11 +
> >  doc/api/doxy-api.conf.in           |   2 +
> >  lib/Makefile                       |   4 +
> >  lib/librte_fib/Makefile            |  25 ++
> >  lib/librte_fib/dir24_8.c           | 737 +++++++++++++++++++++++++++++++++++
> >  lib/librte_fib/dir24_8.h           |  36 ++
> >  lib/librte_fib/meson.build         |   8 +
> >  lib/librte_fib/rte_fib.c           | 319 ++++++++++++++++
> >  lib/librte_fib/rte_fib.h           | 188 +++++++++
> >  lib/librte_fib/rte_fib6.c          | 322 ++++++++++++++++
> >  lib/librte_fib/rte_fib6.h          | 193 ++++++++++
> >  lib/librte_fib/rte_fib_version.map |  23 ++
> >  lib/librte_fib/trie.c              | 760 +++++++++++++++++++++++++++++++++++++
> >  lib/librte_fib/trie.h              |  37 ++
> >  lib/librte_rib/Makefile            |  25 ++
> >  lib/librte_rib/meson.build         |   8 +
> >  lib/librte_rib/rte_rib.c           | 532 ++++++++++++++++++++++++++
> >  lib/librte_rib/rte_rib.h           | 277 ++++++++++++++
> >  lib/librte_rib/rte_rib6.c          | 598 +++++++++++++++++++++++++++++
> >  lib/librte_rib/rte_rib6.h          | 334 ++++++++++++++++
> >  lib/librte_rib/rte_rib_version.map |  35 ++
> >  lib/meson.build                    |   4 +-
> >  mk/rte.app.mk                      |   2 +
> >  32 files changed, 6663 insertions(+), 1 deletion(-)
>
> You forgot to update MAINTAINERS and the release notes.
> I did it while merging.
>
> Applied, thanks

The added tests are timeouting:
https://travis-ci.com/DPDK/dpdk/jobs/253293904
47/86 DPDK:fast-tests / rib_autotest          TIMEOUT 30.01 s
48/86 DPDK:fast-tests / rib6_autotest         TIMEOUT 30.01 s


-- 
David Marchand

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v6 00/12] lib: add RIB and FIB liraries
  2019-11-06  5:50       ` David Marchand
@ 2019-11-06  7:50         ` Thomas Monjalon
  2019-11-06 11:53           ` Medvedkin, Vladimir
  2019-11-06 11:50         ` Medvedkin, Vladimir
  1 sibling, 1 reply; 61+ messages in thread
From: Thomas Monjalon @ 2019-11-06  7:50 UTC (permalink / raw)
  To: Vladimir Medvedkin
  Cc: David Marchand, dev, Bruce Richardson, Ananyev, Konstantin, Aaron Conole

06/11/2019 06:50, David Marchand:
> On Wed, Nov 6, 2019 at 12:14 AM Thomas Monjalon <thomas@monjalon.net> wrote:
> > Applied, thanks
> 
> The added tests are timeouting:
> https://travis-ci.com/DPDK/dpdk/jobs/253293904
> 47/86 DPDK:fast-tests / rib_autotest          TIMEOUT 30.01 s
> 48/86 DPDK:fast-tests / rib6_autotest         TIMEOUT 30.01 s

Vladimir, the slow test were supposed to be moved out of fast tests.
Please can you fix it quickly?



^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v6 00/12] lib: add RIB and FIB liraries
  2019-11-06  5:50       ` David Marchand
  2019-11-06  7:50         ` Thomas Monjalon
@ 2019-11-06 11:50         ` Medvedkin, Vladimir
  1 sibling, 0 replies; 61+ messages in thread
From: Medvedkin, Vladimir @ 2019-11-06 11:50 UTC (permalink / raw)
  To: David Marchand, Thomas Monjalon
  Cc: dev, Richardson, Bruce, Ananyev, Konstantin, Aaron Conole

Hi David,

-----Original Message-----
From: David Marchand <david.marchand@redhat.com> 
Sent: Wednesday, November 6, 2019 5:51 AM
To: Thomas Monjalon <thomas@monjalon.net>; Medvedkin, Vladimir <vladimir.medvedkin@intel.com>
Cc: dev <dev@dpdk.org>; Richardson, Bruce <bruce.richardson@intel.com>; Ananyev, Konstantin <konstantin.ananyev@intel.com>; Aaron Conole <aconole@redhat.com>
Subject: Re: [dpdk-dev] [PATCH v6 00/12] lib: add RIB and FIB liraries

On Wed, Nov 6, 2019 at 12:14 AM Thomas Monjalon <thomas@monjalon.net> wrote:
>
> 01/11/2019 16:21, Vladimir Medvedkin:
> > This is heavily reworked version of previous RIB library series:
> > https://mails.dpdk.org/archives/dev/2018-April/099492.html
> >
> >  app/test/Makefile                  |   7 +
> >  app/test/autotest_data.py          |  48 +++
> >  app/test/meson.build               |  16 +
> >  app/test/test_fib.c                | 414 ++++++++++++++++++++
> >  app/test/test_fib6.c               | 423 +++++++++++++++++++++
> >  app/test/test_fib6_perf.c          | 157 ++++++++
> >  app/test/test_fib_perf.c           | 411 ++++++++++++++++++++
> >  app/test/test_rib.c                | 351 +++++++++++++++++
> >  app/test/test_rib6.c               | 357 +++++++++++++++++
> >  config/common_base                 |  11 +
> >  doc/api/doxy-api.conf.in           |   2 +
> >  lib/Makefile                       |   4 +
> >  lib/librte_fib/Makefile            |  25 ++
> >  lib/librte_fib/dir24_8.c           | 737 +++++++++++++++++++++++++++++++++++
> >  lib/librte_fib/dir24_8.h           |  36 ++
> >  lib/librte_fib/meson.build         |   8 +
> >  lib/librte_fib/rte_fib.c           | 319 ++++++++++++++++
> >  lib/librte_fib/rte_fib.h           | 188 +++++++++
> >  lib/librte_fib/rte_fib6.c          | 322 ++++++++++++++++
> >  lib/librte_fib/rte_fib6.h          | 193 ++++++++++
> >  lib/librte_fib/rte_fib_version.map |  23 ++
> >  lib/librte_fib/trie.c              | 760 +++++++++++++++++++++++++++++++++++++
> >  lib/librte_fib/trie.h              |  37 ++
> >  lib/librte_rib/Makefile            |  25 ++
> >  lib/librte_rib/meson.build         |   8 +
> >  lib/librte_rib/rte_rib.c           | 532 ++++++++++++++++++++++++++
> >  lib/librte_rib/rte_rib.h           | 277 ++++++++++++++
> >  lib/librte_rib/rte_rib6.c          | 598 +++++++++++++++++++++++++++++
> >  lib/librte_rib/rte_rib6.h          | 334 ++++++++++++++++
> >  lib/librte_rib/rte_rib_version.map |  35 ++
> >  lib/meson.build                    |   4 +-
> >  mk/rte.app.mk                      |   2 +
> >  32 files changed, 6663 insertions(+), 1 deletion(-)
>
> You forgot to update MAINTAINERS and the release notes.
> I did it while merging.
>
> Applied, thanks

The added tests are timeouting:
https://travis-ci.com/DPDK/dpdk/jobs/253293904
47/86 DPDK:fast-tests / rib_autotest          TIMEOUT 30.01 s
48/86 DPDK:fast-tests / rib6_autotest         TIMEOUT 30.01 s

Oh, ok, I'll send fix today. On my board these tests run in less than 10 seconds, and our CI runs these tests faster, so I split only really slow FIB tests. I'll send fixes asap.

-- 
David Marchand

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v6 00/12] lib: add RIB and FIB liraries
  2019-11-06  7:50         ` Thomas Monjalon
@ 2019-11-06 11:53           ` Medvedkin, Vladimir
  2019-11-06 11:59             ` Thomas Monjalon
  0 siblings, 1 reply; 61+ messages in thread
From: Medvedkin, Vladimir @ 2019-11-06 11:53 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: David Marchand, dev, Richardson, Bruce, Ananyev, Konstantin,
	Aaron Conole

Hi Thomas,

-----Original Message-----
From: Thomas Monjalon <thomas@monjalon.net> 
Sent: Wednesday, November 6, 2019 7:51 AM
To: Medvedkin, Vladimir <vladimir.medvedkin@intel.com>
Cc: David Marchand <david.marchand@redhat.com>; dev <dev@dpdk.org>; Richardson, Bruce <bruce.richardson@intel.com>; Ananyev, Konstantin <konstantin.ananyev@intel.com>; Aaron Conole <aconole@redhat.com>
Subject: Re: [dpdk-dev] [PATCH v6 00/12] lib: add RIB and FIB liraries

06/11/2019 06:50, David Marchand:
> On Wed, Nov 6, 2019 at 12:14 AM Thomas Monjalon <thomas@monjalon.net> wrote:
> > Applied, thanks
> 
> The added tests are timeouting:
> https://travis-ci.com/DPDK/dpdk/jobs/253293904
> 47/86 DPDK:fast-tests / rib_autotest          TIMEOUT 30.01 s
> 48/86 DPDK:fast-tests / rib6_autotest         TIMEOUT 30.01 s

Vladimir, the slow test were supposed to be moved out of fast tests.
Please can you fix it quickly?

Yes, I'll send a fix now.
In v6 I split only fib_autotests, rib_autotests was not so slow in our CI.



^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v6 00/12] lib: add RIB and FIB liraries
  2019-11-06 11:53           ` Medvedkin, Vladimir
@ 2019-11-06 11:59             ` Thomas Monjalon
  2019-11-06 14:37               ` Aaron Conole
  0 siblings, 1 reply; 61+ messages in thread
From: Thomas Monjalon @ 2019-11-06 11:59 UTC (permalink / raw)
  To: Medvedkin, Vladimir, David Marchand, Aaron Conole
  Cc: dev, Richardson, Bruce, Ananyev, Konstantin

06/11/2019 12:53, Medvedkin, Vladimir:
> From: Thomas Monjalon <thomas@monjalon.net> 
> > 06/11/2019 06:50, David Marchand:
> > > On Wed, Nov 6, 2019 at 12:14 AM Thomas Monjalon <thomas@monjalon.net> wrote:
> > > > Applied, thanks
> > > 
> > > The added tests are timeouting:
> > > https://travis-ci.com/DPDK/dpdk/jobs/253293904
> > > 47/86 DPDK:fast-tests / rib_autotest          TIMEOUT 30.01 s
> > > 48/86 DPDK:fast-tests / rib6_autotest         TIMEOUT 30.01 s
> > 
> > Vladimir, the slow test were supposed to be moved out of fast tests.
> > Please can you fix it quickly?
> 
> Yes, I'll send a fix now.
> In v6 I split only fib_autotests, rib_autotests was not so slow in our CI.

The goal is to have fast tests that developers can run frequently
without being annoyed.
I think the whole run should be below a minute.
It means a single test case should not exceed one second ideally.

Aaron, we should make a status about test duration,
agree on a policy and make a plan to apply such policy.



^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [dpdk-dev] [PATCH v6 00/12] lib: add RIB and FIB liraries
  2019-11-06 11:59             ` Thomas Monjalon
@ 2019-11-06 14:37               ` Aaron Conole
  0 siblings, 0 replies; 61+ messages in thread
From: Aaron Conole @ 2019-11-06 14:37 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Medvedkin, Vladimir, David Marchand, dev, Richardson, Bruce,
	Ananyev, Konstantin

Thomas Monjalon <thomas@monjalon.net> writes:

> 06/11/2019 12:53, Medvedkin, Vladimir:
>> From: Thomas Monjalon <thomas@monjalon.net> 
>> > 06/11/2019 06:50, David Marchand:
>> > > On Wed, Nov 6, 2019 at 12:14 AM Thomas Monjalon <thomas@monjalon.net> wrote:
>> > > > Applied, thanks
>> > > 
>> > > The added tests are timeouting:
>> > > https://travis-ci.com/DPDK/dpdk/jobs/253293904
>> > > 47/86 DPDK:fast-tests / rib_autotest          TIMEOUT 30.01 s
>> > > 48/86 DPDK:fast-tests / rib6_autotest         TIMEOUT 30.01 s
>> > 
>> > Vladimir, the slow test were supposed to be moved out of fast tests.
>> > Please can you fix it quickly?
>> 
>> Yes, I'll send a fix now.
>> In v6 I split only fib_autotests, rib_autotests was not so slow in our CI.
>
> The goal is to have fast tests that developers can run frequently
> without being annoyed.
> I think the whole run should be below a minute.
> It means a single test case should not exceed one second ideally.
>
> Aaron, we should make a status about test duration,
> agree on a policy and make a plan to apply such policy.

Agreed.  I'll write something up.


^ permalink raw reply	[flat|nested] 61+ messages in thread

end of thread, other threads:[~2019-11-06 14:37 UTC | newest]

Thread overview: 61+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-04-26 22:03 [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Medvedkin Vladimir
2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 1/4] Add RIB library Medvedkin Vladimir
2018-04-26 22:17   ` Stephen Hemminger
2018-04-26 22:18   ` Stephen Hemminger
2018-04-26 22:19   ` Stephen Hemminger
2018-04-26 22:19   ` Stephen Hemminger
2018-04-26 22:20   ` Stephen Hemminger
2018-04-27  6:45     ` Vladimir Medvedkin
2018-06-29 13:54   ` Bruce Richardson
2018-06-29 14:02   ` Bruce Richardson
2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 2/4] Add dir24_8 implementation for rib library Medvedkin Vladimir
2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 3/4] Add autotests for RIB library Medvedkin Vladimir
2018-06-29 14:13   ` Bruce Richardson
2018-06-29 15:07   ` Bruce Richardson
2018-06-29 15:31   ` Bruce Richardson
2018-04-26 22:03 ` [dpdk-dev] [PATCH v4 4/4] Add support for lpm and rib bulk lookup Medvedkin Vladimir
2018-04-26 22:24 ` [dpdk-dev] [PATCH v4 0/4] lib/rib: Add Routing Information Base library Stephen Hemminger
2018-04-26 22:27   ` Thomas Monjalon
2018-04-26 22:42     ` Stephen Hemminger
2018-04-26 22:49     ` Stephen Hemminger
2018-04-27  8:27   ` Vladimir Medvedkin
2018-06-29 15:48 ` Bruce Richardson
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 00/12] lib: add RIB and FIB liraries Vladimir Medvedkin
2019-09-12  7:37   ` Morten Brørup
2019-09-12  9:47     ` Medvedkin, Vladimir
2019-09-12 12:00       ` Morten Brørup
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 " Vladimir Medvedkin
2019-11-05 23:14     ` Thomas Monjalon
2019-11-06  5:50       ` David Marchand
2019-11-06  7:50         ` Thomas Monjalon
2019-11-06 11:53           ` Medvedkin, Vladimir
2019-11-06 11:59             ` Thomas Monjalon
2019-11-06 14:37               ` Aaron Conole
2019-11-06 11:50         ` Medvedkin, Vladimir
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 01/12] rib: add RIB library Vladimir Medvedkin
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 02/12] test/rib: add RIB library autotests Vladimir Medvedkin
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 03/12] rib: add ipv6 support for RIB Vladimir Medvedkin
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 04/12] test/rib: add ipv6 support for RIB autotests Vladimir Medvedkin
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 05/12] fib: add FIB library Vladimir Medvedkin
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 06/12] fib: add FIB ipv6 support Vladimir Medvedkin
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 07/12] fib: add DIR24-8 dataplane algorithm Vladimir Medvedkin
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 08/12] fib: add dataplane algorithm for ipv6 Vladimir Medvedkin
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 09/12] test/fib: add FIB library autotests Vladimir Medvedkin
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 10/12] test/fib: add ipv6 support for FIB autotests Vladimir Medvedkin
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 11/12] test/fib: add FIB library performance autotests Vladimir Medvedkin
2019-11-01 15:21   ` [dpdk-dev] [PATCH v6 12/12] test/fib: add FIB library ipv6 " Vladimir Medvedkin
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 01/12] rib: add RIB library Vladimir Medvedkin
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 02/12] test/rib: add RIB library autotests Vladimir Medvedkin
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 03/12] rib: add ipv6 support for RIB Vladimir Medvedkin
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 04/12] test/rib: add ipv6 support for RIB autotests Vladimir Medvedkin
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 05/12] fib: add FIB library Vladimir Medvedkin
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 06/12] fib: add FIB ipv6 support Vladimir Medvedkin
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 07/12] fib: add DIR24-8 dataplane algorithm Vladimir Medvedkin
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 08/12] fib: add dataplane algorithm for ipv6 Vladimir Medvedkin
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 09/12] test/fib: add FIB library autotests Vladimir Medvedkin
2019-09-12 14:07   ` Aaron Conole
2019-10-01 17:12     ` Medvedkin, Vladimir
2019-10-24 15:55       ` Thomas Monjalon
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 10/12] test/fib: add ipv6 support for FIB autotests Vladimir Medvedkin
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 11/12] test/fib: add FIB library performance autotests Vladimir Medvedkin
2019-09-11 17:09 ` [dpdk-dev] [PATCH v5 12/12] test/fib: add FIB library ipv6 " Vladimir Medvedkin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).