Currently, `trie_modify()` admits an update when `rsvd_tbl8s`, the number
of tbl8 levels the route set needs, fits the pool. The dataplane update
then takes tbl8s only within this reservation and is not expected to
fail half way.
In `RTE_FIB6_QSBR_MODE_DQ` mode, a tbl8 released by an update goes to the
defer queue and returns to the pool only once readers report a quiescent
state. Meanwhile `rsvd_tbl8s` already dropped if a prefix was deleted.
The deferred tbl8s are out of the pool but not accounted for, so while a
reader holds the grace period an add may run out of tbl8s inside
`modify_dp()`.
Fix it by counting the tbl8s used by the dataplane in `cur_tbl8s`, which
was unused so far. The tbl8s taken from the pool but not used,
`tbl8_pool_pos - cur_tbl8s`, are those waiting in the defer queue. Take
the reservation of an added prefix before writing the dataplane, and
check at the start of `modify_dp()` that the reserved tbl8s plus the
deferred ones fit the pool. If they do not, reclaim the defer queue once,
and return `-ENOSPC` before touching the dataplane if they still do not
fit.
Bugzilla ID: 1970
Fixes: 200ccb9ebd8e ("fib6: implement RCU rule reclamation")
Cc: [email protected]
Cc: [email protected]
Reported-by: Maxime Leroy <[email protected]>
Signed-off-by: Vladimir Medvedkin <[email protected]>
---
lib/fib/trie.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/lib/fib/trie.c b/lib/fib/trie.c
index 0ae669c689..9ede1a5bf7 100644
--- a/lib/fib/trie.c
+++ b/lib/fib/trie.c
@@ -175,6 +175,7 @@ tbl8_alloc(struct rte_trie_tbl *dp, uint64_t nh)
/*Init tbl8 entries with nexthop from tbl24*/
write_to_dp((void *)tbl8_ptr, nh, dp->nh_sz,
TRIE_TBL8_GRP_NUM_ENT);
+ dp->cur_tbl8s++;
return tbl8_idx;
}
@@ -243,6 +244,7 @@ tbl8_recycle(struct rte_trie_tbl *dp, void *par, uint64_t
tbl8_idx)
break;
}
+ dp->cur_tbl8s--;
if (dp->v == NULL) {
tbl8_cleanup_and_free(dp, tbl8_idx);
} else if (dp->rcu_mode == RTE_FIB6_QSBR_MODE_SYNC) {
@@ -508,6 +510,18 @@ modify_dp(struct rte_trie_tbl *dp, struct rte_rib6 *rib,
if (next_hop > get_max_nh(dp->nh_sz))
return -EINVAL;
+ /*
+ * Check if we can safely allocate new tbl8s further in process of
modifying the dataplane.
+ */
+ uint32_t tbl8s_in_defer_queue = dp->tbl8_pool_pos - dp->cur_tbl8s;
+ if (dp->rsvd_tbl8s + tbl8s_in_defer_queue > dp->number_tbl8s) {
+ rte_rcu_qsbr_dq_reclaim(dp->dq, dp->tbl8_pool_pos -
dp->cur_tbl8s,
+ NULL, NULL, NULL);
+ tbl8s_in_defer_queue = dp->tbl8_pool_pos - dp->cur_tbl8s;
+ if (dp->rsvd_tbl8s + tbl8s_in_defer_queue > dp->number_tbl8s)
+ return -ENOSPC;
+ }
+
ledge = *ip;
do {
tmp = rte_rib6_get_nxt(rib, ip, depth, tmp,
@@ -624,6 +638,7 @@ trie_modify(struct rte_fib6 *fib, const struct
rte_ipv6_addr *ip,
rte_rib6_remove(rib, &ip_masked, depth);
return -ENOSPC;
}
+ dp->rsvd_tbl8s += new_levels;
rte_rib6_set_nh(node, next_hop);
parent = rte_rib6_lookup_parent(node);
@@ -634,11 +649,11 @@ trie_modify(struct rte_fib6 *fib, const struct
rte_ipv6_addr *ip,
}
ret = modify_dp(dp, rib, &ip_masked, depth, next_hop);
if (ret != 0) {
+ dp->rsvd_tbl8s -= new_levels;
rte_rib6_remove(rib, &ip_masked, depth);
return ret;
}
successfully_added:
- dp->rsvd_tbl8s += new_levels;
return 0;
case RTE_FIB6_DEL:
if (node == NULL)
--
2.43.0