Currently, `install_to_dp()` builds one tbl8 level more than a route
range needs when the range ends on a byte boundary. If the range
spans a whole table at its first uncommon byte, e.g. a /32 or a /40,
`build_common_root()` allocates a tbl8 one level too deep, all of
its entries get the same next hop and `recycle_root_path()` frees it
again. Below /24, an edge aligned on /24, e.g. of a /16, gets a tbl8 that
`write_edge()` fills and recycles at once.
These temporary tbl8s are not covered by `rsvd_tbl8s`, which counts
only the levels the route set keeps. With a nearly full pool, an add
that passed the reservation check runs out of tbl8s in the middle of the
install, the tbl8 already linked into the dataplane is leaked and a later
add that fits is refused. A /32 can not be added to a pool of one tbl8,
nor deleted from a full pool.
Fix it by not building these levels.
Bugzilla ID: 1970
Fixes: c3e12e0f0354 ("fib: add dataplane algorithm for IPv6")
Cc: [email protected]
Reported-by: Maxime Leroy <[email protected]>
Signed-off-by: Vladimir Medvedkin <[email protected]>
---
lib/fib/trie.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/lib/fib/trie.c b/lib/fib/trie.c
index 187360d1c8..0ae669c689 100644
--- a/lib/fib/trie.c
+++ b/lib/fib/trie.c
@@ -410,20 +410,33 @@ install_to_dp(struct rte_trie_tbl *dp, const struct
rte_ipv6_addr *ledge,
break;
}
+ /*
+ * A range covering a whole tbl8 is a single entry one level up.
+ * Write that entry rather than build a tbl8 only to recycle it.
+ */
+ if (common_bytes >= TBL24_BYTES) {
+ for (i = common_bytes; i <= IPV6_MAX_IDX; i++) {
+ if (ledge->a[i] != 0 || redge.a[i] != UINT8_MAX)
+ break;
+ }
+ if (i > IPV6_MAX_IDX)
+ common_bytes--;
+ }
+
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--) {
+ for (i = IPV6_MAX_IDX; i >= first_tbl8_byte + !(common_bytes < 3); i--)
{
if (ledge->a[i] != 0)
break;
}
llen = i - first_tbl8_byte + (common_bytes < 3);
- for (i = IPV6_MAX_IDX; i > first_tbl8_byte; i--) {
+ for (i = IPV6_MAX_IDX; i >= first_tbl8_byte + !(common_bytes < 3); i--)
{
if (redge.a[i] != UINT8_MAX)
break;
}
--
2.43.0