From 39f732f3c7ec70ccc130d4318037ca2b0c76c5de Mon Sep 17 00:00:00 2001 From: Maxime Leroy Date: Mon, 30 Mar 2026 13:52:44 +0200 Subject: [PATCH 1/3] fib: remove per-VRF tbl8 configuration The tbl8 pool will be shared across all VRFs with automatic resizing in a follow-up commit. Per-VRF tbl8 sizing is no longer meaningful. Remove num_tbl8 from: - gr_iface_info_vrf_fib API struct - VRF CLI (fib4-tbl8/fib6-tbl8 options) - fib info API (gr_fib4_info/gr_fib6_info) - fib show table column - prometheus metrics (fib4_max_tbl8, fib4_used_tbl8, etc.) - smoke tests The auto-derived num_tbl8 is still computed internally from max_routes and passed to DPDK at FIB creation time. Signed-off-by: Maxime Leroy --- modules/infra/api/gr_infra.h | 1 - modules/infra/cli/route.c | 3 - modules/infra/cli/vrf.c | 25 +-- modules/infra/control/vrf.c | 19 +-- modules/ip/api/gr_ip4.h | 2 - modules/ip/cli/route.c | 10 -- modules/ip/control/route.c | 38 +---- modules/ip6/api/gr_ip6.h | 2 - modules/ip6/cli/route.c | 10 -- modules/ip6/control/route.c | 38 +---- smoke/config_vrf_test.sh | 17 +- subprojects/dpdk.wrap | 1 - .../fib-expose-tbl8-usage-statistics.patch | 145 ------------------ 13 files changed, 20 insertions(+), 291 deletions(-) delete mode 100644 subprojects/packagefiles/dpdk/fib-expose-tbl8-usage-statistics.patch diff --git a/modules/infra/api/gr_infra.h b/modules/infra/api/gr_infra.h index 9983ff463..71abe1c72 100644 --- a/modules/infra/api/gr_infra.h +++ b/modules/infra/api/gr_infra.h @@ -131,7 +131,6 @@ struct gr_iface_info_port { // Per-AF FIB configuration. struct gr_iface_info_vrf_fib { uint32_t max_routes; // 0 = default - uint32_t num_tbl8; // 0 = auto }; // Info structure for GR_IFACE_TYPE_VRF interfaces. diff --git a/modules/infra/cli/route.c b/modules/infra/cli/route.c index 1ccf69225..ed101c07f 100644 --- a/modules/infra/cli/route.c +++ b/modules/infra/cli/route.c @@ -124,9 +124,6 @@ static cmd_status_t route_config_show(struct gr_api_client *c, const struct ec_p gr_table_column(table, "VRF", GR_DISP_LEFT); // 0 gr_table_column(table, "FAMILY", GR_DISP_LEFT); // 1 gr_table_column(table, "ROUTES", GR_DISP_RIGHT); // 2 -#ifdef HAVE_RTE_FIB_TBL8_GET_STATS - gr_table_column(table, "TBL8", GR_DISP_RIGHT); // 3 -#endif STAILQ_FOREACH (ops, &route_ops, next) { if (ops->config_show == NULL) diff --git a/modules/infra/cli/vrf.c b/modules/infra/cli/vrf.c index 6ec38c363..e11c86be3 100644 --- a/modules/infra/cli/vrf.c +++ b/modules/infra/cli/vrf.c @@ -7,23 +7,19 @@ #include #define VRF_ATTRS_CMD \ - "(rib4-routes RIB4_ROUTES),(fib4-tbl8 FIB4_TBL8)" \ - ",(rib6-routes RIB6_ROUTES),(fib6-tbl8 FIB6_TBL8)" \ + "(rib4-routes RIB4_ROUTES)" \ + ",(rib6-routes RIB6_ROUTES)" \ ",(description DESCR)" #define VRF_ATTRS_ARGS \ with_help("Max IPv4 routes.", ec_node_uint("RIB4_ROUTES", 1, UINT32_MAX, 10)), \ - with_help("IPv4 TBL8 groups.", ec_node_uint("FIB4_TBL8", 1, UINT32_MAX, 10)), \ with_help("Max IPv6 routes.", ec_node_uint("RIB6_ROUTES", 1, UINT32_MAX, 10)), \ - with_help("IPv6 TBL8 groups.", ec_node_uint("FIB6_TBL8", 1, UINT32_MAX, 10)), \ with_help("Interface description.", ec_node("any", "DESCR")) static void vrf_show(struct gr_api_client *, const struct gr_iface *iface, struct gr_object *o) { const struct gr_iface_info_vrf *info = PAYLOAD(iface); gr_object_field(o, "rib4_max_routes", GR_DISP_INT, "%u", info->ipv4.max_routes); - gr_object_field(o, "fib4_num_tbl8", GR_DISP_INT, "%u", info->ipv4.num_tbl8); gr_object_field(o, "rib6_max_routes", GR_DISP_INT, "%u", info->ipv6.max_routes); - gr_object_field(o, "fib6_num_tbl8", GR_DISP_INT, "%u", info->ipv6.num_tbl8); } static void @@ -31,12 +27,8 @@ vrf_list_info(struct gr_api_client *, const struct gr_iface *iface, char *buf, s const struct gr_iface_info_vrf *info = PAYLOAD(iface); size_t n = 0; - SAFE_BUF( - snprintf, len, "ip4 routes=%u tbl8=%u", info->ipv4.max_routes, info->ipv4.num_tbl8 - ); - SAFE_BUF( - snprintf, len, " ip6 routes=%u tbl8=%u", info->ipv6.max_routes, info->ipv6.num_tbl8 - ); + SAFE_BUF(snprintf, len, "ip4 routes=%u", info->ipv4.max_routes); + SAFE_BUF(snprintf, len, " ip6 routes=%u", info->ipv6.max_routes); err:; } @@ -62,20 +54,15 @@ static uint64_t parse_vrf_args( return 0; } - if (arg_str(p, "RIB4_ROUTES") != NULL || arg_str(p, "FIB4_TBL8") != NULL) { - // In update mode, parse_iface_args populates info with the current - // interface state. Zero the FIB config so only explicitly provided - // fields are sent. The daemon treats 0 as "auto-derive". + if (arg_str(p, "RIB4_ROUTES") != NULL) { info->ipv4 = (struct gr_iface_info_vrf_fib) {0}; arg_u32(p, "RIB4_ROUTES", &info->ipv4.max_routes); - arg_u32(p, "FIB4_TBL8", &info->ipv4.num_tbl8); set_attrs |= GR_VRF_SET_FIB; } - if (arg_str(p, "RIB6_ROUTES") != NULL || arg_str(p, "FIB6_TBL8") != NULL) { + if (arg_str(p, "RIB6_ROUTES") != NULL) { info->ipv6 = (struct gr_iface_info_vrf_fib) {0}; arg_u32(p, "RIB6_ROUTES", &info->ipv6.max_routes); - arg_u32(p, "FIB6_TBL8", &info->ipv6.num_tbl8); set_attrs |= GR_VRF_SET_FIB; } diff --git a/modules/infra/control/vrf.c b/modules/infra/control/vrf.c index 7c8a362bb..ffbf23985 100644 --- a/modules/infra/control/vrf.c +++ b/modules/infra/control/vrf.c @@ -310,7 +310,7 @@ static int iface_vrf_reconfig( api = vrf_fib_api(info, af); if (api == NULL) continue; - if (api->max_routes == 0 && api->num_tbl8 == 0) + if (api->max_routes == 0) continue; assert(fib_ops[af] != NULL); @@ -318,16 +318,9 @@ static int iface_vrf_reconfig( fib_conf = vrf_fib_conf(vrf, af); old = *fib_conf; - if (api->max_routes) { - fib_conf->max_routes = api->max_routes; - if (!api->num_tbl8) - fib_conf->num_tbl8 = 0; - } - if (api->num_tbl8) - fib_conf->num_tbl8 = api->num_tbl8; + fib_conf->max_routes = api->max_routes; - if (fib_conf->max_routes == old.max_routes - && fib_conf->num_tbl8 == old.num_tbl8) + if (fib_conf->max_routes == old.max_routes) continue; if (fib_ops[af]->reconfig(iface) < 0) { @@ -336,14 +329,12 @@ static int iface_vrf_reconfig( } LOG(INFO, - "resized %s FIB VRF %s(%u) max_routes %u -> %u num_tbl8 %u -> %u", + "resized %s FIB VRF %s(%u) max_routes %u -> %u", gr_af_name(af), iface->name, iface->id, old.max_routes, - fib_conf->max_routes, - old.num_tbl8, - fib_conf->num_tbl8); + fib_conf->max_routes); } } diff --git a/modules/ip/api/gr_ip4.h b/modules/ip/api/gr_ip4.h index 1cae27888..4b23f2e65 100644 --- a/modules/ip/api/gr_ip4.h +++ b/modules/ip/api/gr_ip4.h @@ -156,8 +156,6 @@ struct gr_fib4_info { uint16_t vrf_id; uint32_t max_routes; // configured maximum number of routes uint32_t used_routes; // number of routes currently installed - uint32_t num_tbl8; // allocated tbl8 groups (total) - uint32_t used_tbl8; // tbl8 groups currently in use }; // Set default FIB configuration for new VRFs. diff --git a/modules/ip/cli/route.c b/modules/ip/cli/route.c index 809871682..89f7d40cf 100644 --- a/modules/ip/cli/route.c +++ b/modules/ip/cli/route.c @@ -128,16 +128,6 @@ static int route4_config_show(struct gr_api_client *c, uint16_t vrf_id, struct g info->max_routes, info->max_routes ? 100.0 * info->used_routes / info->max_routes : 0 ); -#ifdef HAVE_RTE_FIB_TBL8_GET_STATS - gr_table_cell( - table, - 3, - "%u/%u (%.1f%%)", - info->used_tbl8, - info->num_tbl8, - info->num_tbl8 ? 100.0 * info->used_tbl8 / info->num_tbl8 : 0 - ); -#endif if (gr_table_print_row(table) < 0) break; diff --git a/modules/ip/control/route.c b/modules/ip/control/route.c index 9aa4f25d9..b3d9abf93 100644 --- a/modules/ip/control/route.c +++ b/modules/ip/control/route.c @@ -50,10 +50,6 @@ static inline uint32_t fib4_get_max_routes(const struct iface *vrf) { return iface_info_vrf(vrf)->ipv4.max_routes; } -static inline uint32_t fib4_get_num_tbl8(const struct iface *vrf) { - return iface_info_vrf(vrf)->ipv4.num_tbl8; -} - static struct rte_fib *get_fib(uint16_t vrf_id) { struct iface *iface = get_vrf_iface(vrf_id); if (iface == NULL) @@ -74,7 +70,7 @@ static struct rte_fib *create_fib(const struct iface *vrf) { .rib_ext_sz = sizeof(gr_nh_origin_t), .dir24_8 = { .nh_sz = RTE_FIB_DIR24_8_8B, - .num_tbl8 = fib4_get_num_tbl8(vrf), + .num_tbl8 = fib4_auto_tbl8(fib4_get_max_routes(vrf)), }, }; struct rte_fib *fib; @@ -105,15 +101,12 @@ static int fib4_init(struct iface *vrf) { if (!conf->max_routes) conf->max_routes = max_routes_default; - if (!conf->num_tbl8) - conf->num_tbl8 = fib4_auto_tbl8(conf->max_routes); LOG(INFO, - "creating IPv4 FIB for VRF %s(%u) max_routes=%u num_tbl8=%u", + "creating IPv4 FIB for VRF %s(%u) max_routes=%u", vrf->name, vrf->id, - conf->max_routes, - conf->num_tbl8); + conf->max_routes); fib = create_fib(vrf); if (fib == NULL) @@ -568,10 +561,6 @@ void rib4_cleanup(struct nexthop *nh) { METRIC_GAUGE(m_routes, "rib4_routes", "Number of IPv4 routes by origin."); METRIC_GAUGE(m_max_routes, "rib4_max_routes", "Maximum number of IPv4 routes."); -#ifdef HAVE_RTE_FIB_TBL8_GET_STATS -METRIC_GAUGE(m_max_tbl8, "fib4_max_tbl8", "Maximum number of IPv4 FIB tbl8 groups."); -METRIC_GAUGE(m_used_tbl8, "fib4_used_tbl8", "Used IPv4 FIB tbl8 groups."); -#endif static void rib4_metrics_collect(struct gr_metrics_writer *w) { struct gr_metrics_ctx ctx; @@ -595,15 +584,6 @@ static void rib4_metrics_collect(struct gr_metrics_writer *w) { gr_metrics_ctx_init(&ctx, w, "vrf", vrf, NULL); gr_metric_emit(&ctx, &m_max_routes, fib4_get_max_routes(vrf_iface)); -#ifdef HAVE_RTE_FIB_TBL8_GET_STATS - uint32_t used_tbl8, total_tbl8; - struct rte_fib *fib = iface_info_vrf(vrf_iface)->fib4; - if (fib != NULL) { - rte_fib_tbl8_get_stats(fib, &used_tbl8, &total_tbl8); - gr_metric_emit(&ctx, &m_max_tbl8, total_tbl8); - gr_metric_emit(&ctx, &m_used_tbl8, used_tbl8); - } -#endif } } @@ -712,8 +692,6 @@ static int fib4_reconfig(struct iface *vrf) { if (!conf->max_routes) conf->max_routes = max_routes_default; - if (!conf->num_tbl8) - conf->num_tbl8 = fib4_auto_tbl8(conf->max_routes); old_fib = iface_info_vrf(vrf)->fib4; new_fib = create_fib(vrf); @@ -746,8 +724,6 @@ static struct api_out fib4_info_list(const void *request, struct api_ctx *ctx) { info.vrf_id = GR_VRF_ID_UNDEF; info.max_routes = max_routes_default; info.used_routes = 0; - info.num_tbl8 = fib4_auto_tbl8(max_routes_default); - info.used_tbl8 = 0; api_send(ctx, sizeof(info), &info); } @@ -760,14 +736,6 @@ static struct api_out fib4_info_list(const void *request, struct api_ctx *ctx) { info.vrf_id = v; info.max_routes = fib4_get_max_routes(vrf); info.used_routes = fib4_total_routes(v); -#ifdef HAVE_RTE_FIB_TBL8_GET_STATS - struct rte_fib *fib = iface_info_vrf(vrf)->fib4; - if (fib != NULL) - rte_fib_tbl8_get_stats(fib, &info.used_tbl8, &info.num_tbl8); -#else - info.num_tbl8 = fib4_get_num_tbl8(vrf); - info.used_tbl8 = 0; -#endif api_send(ctx, sizeof(info), &info); } diff --git a/modules/ip6/api/gr_ip6.h b/modules/ip6/api/gr_ip6.h index c4533ff2b..6c48af970 100644 --- a/modules/ip6/api/gr_ip6.h +++ b/modules/ip6/api/gr_ip6.h @@ -123,8 +123,6 @@ struct gr_fib6_info { uint16_t vrf_id; uint32_t max_routes; // configured maximum number of routes uint32_t used_routes; // number of routes currently installed - uint32_t num_tbl8; // allocated tbl8 groups (total) - uint32_t used_tbl8; // tbl8 groups currently in use }; // Set default FIB configuration for new VRFs. diff --git a/modules/ip6/cli/route.c b/modules/ip6/cli/route.c index 0ff48b3e2..518e393ad 100644 --- a/modules/ip6/cli/route.c +++ b/modules/ip6/cli/route.c @@ -128,16 +128,6 @@ static int route6_config_show(struct gr_api_client *c, uint16_t vrf_id, struct g info->max_routes, info->max_routes ? 100.0 * info->used_routes / info->max_routes : 0 ); -#ifdef HAVE_RTE_FIB_TBL8_GET_STATS - gr_table_cell( - table, - 3, - "%u/%u (%.1f%%)", - info->used_tbl8, - info->num_tbl8, - info->num_tbl8 ? 100.0 * info->used_tbl8 / info->num_tbl8 : 0 - ); -#endif if (gr_table_print_row(table) < 0) break; diff --git a/modules/ip6/control/route.c b/modules/ip6/control/route.c index d5afb7165..553100bfe 100644 --- a/modules/ip6/control/route.c +++ b/modules/ip6/control/route.c @@ -50,10 +50,6 @@ static inline uint32_t fib6_get_max_routes(const struct iface *vrf) { return iface_info_vrf(vrf)->ipv6.max_routes; } -static inline uint32_t fib6_get_num_tbl8(const struct iface *vrf) { - return iface_info_vrf(vrf)->ipv6.num_tbl8; -} - static struct rte_fib6 *get_fib6(uint16_t vrf_id) { struct iface *iface = get_vrf_iface(vrf_id); if (iface == NULL) @@ -74,7 +70,7 @@ static struct rte_fib6 *create_fib6(const struct iface *vrf) { .rib_ext_sz = sizeof(gr_nh_origin_t), .trie = { .nh_sz = RTE_FIB6_TRIE_8B, - .num_tbl8 = fib6_get_num_tbl8(vrf), + .num_tbl8 = fib6_auto_tbl8(fib6_get_max_routes(vrf)), }, }; struct rte_fib6 *fib; @@ -105,15 +101,12 @@ static int fib6_init(struct iface *vrf) { if (!conf->max_routes) conf->max_routes = max_routes_default; - if (!conf->num_tbl8) - conf->num_tbl8 = fib6_auto_tbl8(conf->max_routes); LOG(INFO, - "creating IPv6 FIB for VRF %s(%u) max_routes=%u num_tbl8=%u", + "creating IPv6 FIB for VRF %s(%u) max_routes=%u", vrf->name, vrf->id, - conf->max_routes, - conf->num_tbl8); + conf->max_routes); fib = create_fib6(vrf); if (fib == NULL) @@ -608,10 +601,6 @@ void rib6_cleanup(struct nexthop *nh) { METRIC_GAUGE(m_routes, "rib6_routes", "Number of IPv6 routes by origin."); METRIC_GAUGE(m_max_routes, "rib6_max_routes", "Maximum number of IPv6 routes."); -#ifdef HAVE_RTE_FIB_TBL8_GET_STATS -METRIC_GAUGE(m_max_tbl8, "fib6_max_tbl8", "Maximum number of IPv6 FIB tbl8 groups."); -METRIC_GAUGE(m_used_tbl8, "fib6_used_tbl8", "Used IPv6 FIB tbl8 groups."); -#endif static void rib6_metrics_collect(struct gr_metrics_writer *w) { struct gr_metrics_ctx ctx; @@ -635,15 +624,6 @@ static void rib6_metrics_collect(struct gr_metrics_writer *w) { gr_metrics_ctx_init(&ctx, w, "vrf", vrf, NULL); gr_metric_emit(&ctx, &m_max_routes, fib6_get_max_routes(vrf_iface)); -#ifdef HAVE_RTE_FIB_TBL8_GET_STATS - uint32_t used_tbl8, total_tbl8; - struct rte_fib6 *fib = iface_info_vrf(vrf_iface)->fib6; - if (fib != NULL) { - rte_fib6_tbl8_get_stats(fib, &used_tbl8, &total_tbl8); - gr_metric_emit(&ctx, &m_max_tbl8, total_tbl8); - gr_metric_emit(&ctx, &m_used_tbl8, used_tbl8); - } -#endif } } @@ -752,8 +732,6 @@ static int fib6_reconfig(struct iface *vrf) { if (!conf->max_routes) conf->max_routes = max_routes_default; - if (!conf->num_tbl8) - conf->num_tbl8 = fib6_auto_tbl8(conf->max_routes); old_fib = iface_info_vrf(vrf)->fib6; new_fib = create_fib6(vrf); @@ -786,8 +764,6 @@ static struct api_out fib6_info_list(const void *request, struct api_ctx *ctx) { info.vrf_id = GR_VRF_ID_UNDEF; info.max_routes = max_routes_default; info.used_routes = 0; - info.num_tbl8 = fib6_auto_tbl8(max_routes_default); - info.used_tbl8 = 0; api_send(ctx, sizeof(info), &info); } @@ -800,14 +776,6 @@ static struct api_out fib6_info_list(const void *request, struct api_ctx *ctx) { info.vrf_id = v; info.max_routes = fib6_get_max_routes(vrf); info.used_routes = fib6_total_routes(v); -#ifdef HAVE_RTE_FIB_TBL8_GET_STATS - struct rte_fib6 *fib = iface_info_vrf(vrf)->fib6; - if (fib != NULL) - rte_fib6_tbl8_get_stats(fib, &info.used_tbl8, &info.num_tbl8); -#else - info.num_tbl8 = fib6_get_num_tbl8(vrf); - info.used_tbl8 = 0; -#endif api_send(ctx, sizeof(info), &info); } diff --git a/smoke/config_vrf_test.sh b/smoke/config_vrf_test.sh index 6606391d7..64fcabb4e 100755 --- a/smoke/config_vrf_test.sh +++ b/smoke/config_vrf_test.sh @@ -64,23 +64,12 @@ grcli route show vrf fibtest count=$(grcli -j route show vrf fibtest | jq length) [ "$count" -eq 17 ] -# FIB resize via iface set with tbl8 override -grcli interface set vrf fibtest fib4-tbl8 64 -grcli -j interface show name fibtest \ - | jq -e '.fib4_num_tbl8 == 64' || fail "fib4_num_tbl8 should be 64" -grcli -j interface show name fibtest \ - | jq -e '.rib4_max_routes == 1024' || fail "rib4_max_routes should still be 1024 after tbl8 set" - -# Ensure routes survived resize (same max_routes) -count=$(grcli -j route show vrf fibtest | jq length) -[ "$count" -eq 17 ] - -# Ensure tbl8 is auto-derived when max_routes changes +# Ensure routes survived resize (max_routes change) grcli interface set vrf fibtest rib4-routes 2048 grcli -j interface show name fibtest \ | jq -e '.rib4_max_routes == 2048' || fail "rib4_max_routes should be 2048" -grcli -j interface show name fibtest \ - | jq -e '.fib4_num_tbl8 != 64' || fail "fib4_num_tbl8 should be auto-derived after max_routes change" +count=$(grcli -j route show vrf fibtest | jq length) +[ "$count" -eq 17 ] # Ensure routes and addresses were dropped on resize down grcli interface set vrf fibtest rib4-routes 1 rib6-routes 8 diff --git a/subprojects/dpdk.wrap b/subprojects/dpdk.wrap index a5df25c0e..e282d7805 100644 --- a/subprojects/dpdk.wrap +++ b/subprojects/dpdk.wrap @@ -5,7 +5,6 @@ depth = 1 diff_files = dpdk/iavf-fix-reported-max-TX-and-RX-queues-in-ethdev-inf.patch, dpdk/net-tap-add-software-MAC-address-filtering.patch, - dpdk/fib-expose-tbl8-usage-statistics.patch, dpdk/fib-fix-prefix-addition-handling.patch, dpdk/hash-avoid-leaking-entries-on-RCU-defer-queue-failur.patch, dpdk/hash-free-replaced-data-on-overwrite-when-RCU-is-con.patch diff --git a/subprojects/packagefiles/dpdk/fib-expose-tbl8-usage-statistics.patch b/subprojects/packagefiles/dpdk/fib-expose-tbl8-usage-statistics.patch deleted file mode 100644 index 90fa7c062..000000000 --- a/subprojects/packagefiles/dpdk/fib-expose-tbl8-usage-statistics.patch +++ /dev/null @@ -1,145 +0,0 @@ -From f5ae6fbe403f9638e08c24e4b9c6a3858bc87041 Mon Sep 17 00:00:00 2001 -From: Robin Jarry -Date: Tue, 24 Feb 2026 14:27:12 +0100 -Subject: [PATCH dpdk] fib: expose tbl8 usage statistics - -Both dir24_8 (IPv4) and trie (IPv6) algorithms already track tbl8 group -allocation internally but there is no way for applications to query this -information. Monitoring tbl8 usage is important to detect exhaustion -before route insertions start failing. - -Add rte_fib_tbl8_get_stats() and rte_fib6_tbl8_get_stats() to let -applications retrieve the number of tbl8 groups currently in use and the -total capacity. For dir24_8, the used count comes from cur_tbl8s. For -trie, it comes from tbl8_pool_pos which reflects the actual number of -allocated entries since the pool is a stack. - -Signed-off-by: Robin Jarry ---- - lib/fib/rte_fib.c | 22 ++++++++++++++++++++++ - lib/fib/rte_fib.h | 18 ++++++++++++++++++ - lib/fib/rte_fib6.c | 22 ++++++++++++++++++++++ - lib/fib/rte_fib6.h | 18 ++++++++++++++++++ - 4 files changed, 80 insertions(+) - -diff --git a/lib/fib/rte_fib.c b/lib/fib/rte_fib.c -index 184210f38070..a7e8615f052f 100644 ---- a/lib/fib/rte_fib.c -+++ b/lib/fib/rte_fib.c -@@ -364,3 +364,25 @@ rte_fib_rcu_qsbr_add(struct rte_fib *fib, struct rte_fib_rcu_config *cfg) - return -ENOTSUP; - } - } -+ -+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_fib_tbl8_get_stats, 26.07) -+int -+rte_fib_tbl8_get_stats(struct rte_fib *fib, uint32_t *used, uint32_t *total) -+{ -+ struct dir24_8_tbl *dp; -+ -+ if (fib == NULL) -+ return -EINVAL; -+ -+ switch (fib->type) { -+ case RTE_FIB_DIR24_8: -+ dp = fib->dp; -+ if (used != NULL) -+ *used = dp->cur_tbl8s; -+ if (total != NULL) -+ *total = dp->number_tbl8s; -+ return 0; -+ default: -+ return -ENOTSUP; -+ } -+} -diff --git a/lib/fib/rte_fib.h b/lib/fib/rte_fib.h -index b16a653535cf..a79389cd4087 100644 ---- a/lib/fib/rte_fib.h -+++ b/lib/fib/rte_fib.h -@@ -283,6 +283,24 @@ __rte_experimental - int - rte_fib_rcu_qsbr_add(struct rte_fib *fib, struct rte_fib_rcu_config *cfg); - -+/** -+ * Retrieve tbl8 allocation statistics from the FIB. -+ * -+ * @param fib -+ * FIB object handle -+ * @param used -+ * Number of tbl8 groups currently in use (can be NULL) -+ * @param total -+ * Total number of tbl8 groups allocated at creation (can be NULL) -+ * @return -+ * 0 on success -+ * -EINVAL if fib is NULL -+ * -ENOTSUP if the FIB type does not support tbl8 statistics -+ */ -+__rte_experimental -+int -+rte_fib_tbl8_get_stats(struct rte_fib *fib, uint32_t *used, uint32_t *total); -+ - #ifdef __cplusplus - } - #endif -diff --git a/lib/fib/rte_fib6.c b/lib/fib/rte_fib6.c -index 770becdb611e..8a586710f429 100644 ---- a/lib/fib/rte_fib6.c -+++ b/lib/fib/rte_fib6.c -@@ -361,3 +361,25 @@ rte_fib6_rcu_qsbr_add(struct rte_fib6 *fib, struct rte_fib6_rcu_config *cfg) - return -ENOTSUP; - } - } -+ -+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_fib6_tbl8_get_stats, 26.07) -+int -+rte_fib6_tbl8_get_stats(struct rte_fib6 *fib, uint32_t *used, uint32_t *total) -+{ -+ struct rte_trie_tbl *dp; -+ -+ if (fib == NULL) -+ return -EINVAL; -+ -+ switch (fib->type) { -+ case RTE_FIB6_TRIE: -+ dp = fib->dp; -+ if (used != NULL) -+ *used = dp->tbl8_pool_pos; -+ if (total != NULL) -+ *total = dp->number_tbl8s; -+ return 0; -+ default: -+ return -ENOTSUP; -+ } -+} -diff --git a/lib/fib/rte_fib6.h b/lib/fib/rte_fib6.h -index 4527328bf00d..17fea82e8aee 100644 ---- a/lib/fib/rte_fib6.h -+++ b/lib/fib/rte_fib6.h -@@ -272,6 +272,24 @@ __rte_experimental - int - rte_fib6_rcu_qsbr_add(struct rte_fib6 *fib, struct rte_fib6_rcu_config *cfg); - -+/** -+ * Retrieve tbl8 allocation statistics from the FIB. -+ * -+ * @param fib -+ * FIB object handle -+ * @param used -+ * Number of tbl8 groups currently in use (can be NULL) -+ * @param total -+ * Total number of tbl8 groups allocated at creation (can be NULL) -+ * @return -+ * 0 on success -+ * -EINVAL if fib is NULL -+ * -ENOTSUP if the FIB type does not support tbl8 statistics -+ */ -+__rte_experimental -+int -+rte_fib6_tbl8_get_stats(struct rte_fib6 *fib, uint32_t *used, uint32_t *total); -+ - #ifdef __cplusplus - } - #endif --- -2.53.0 - From 752d9b6890f6d377c243dd4fea31aee5fc6c0ff2 Mon Sep 17 00:00:00 2001 From: Maxime Leroy Date: Mon, 30 Mar 2026 14:01:16 +0200 Subject: [PATCH 2/3] fib: use shared resizable tbl8 pool across all VRFs Replace per-VRF tbl8 allocation with a single shared tbl8 pool for all FIB instances (IPv4 and IPv6). The pool starts small (256 groups) and resizes automatically via RCU-safe pointer swap when full. This eliminates the need to pre-size tbl8 per VRF and reduces overall memory usage by sharing tbl8 groups across VRFs. New DPDK patches: - fib: add shared tbl8 pool for dir24_8 and trie backends - fib: add resizable tbl8 pool New grout module: fib_pool - Creates a global tbl8 pool at init with RCU configured - IPv4 and IPv6 FIBs attach to the same pool via tbl8_pool config - Pool freed at shutdown after all VRFs are destroyed Signed-off-by: Maxime Leroy --- modules/infra/control/fib_pool.c | 57 ++ modules/infra/control/gr_fib_pool.h | 8 + modules/infra/control/meson.build | 1 + modules/ip/control/route.c | 14 +- modules/ip6/control/route.c | 15 +- subprojects/dpdk.wrap | 4 + .../dpdk/fib-add-resizable-tbl8-pool.patch | 601 ++++++++++++ .../dpdk/fib-add-shared-tbl8-pool.patch | 921 ++++++++++++++++++ ...bl8-definitions-between-fib-and-fib6.patch | 720 ++++++++++++++ ...t-fib6-zero-initialize-config-struct.patch | 65 ++ 10 files changed, 2383 insertions(+), 23 deletions(-) create mode 100644 modules/infra/control/fib_pool.c create mode 100644 modules/infra/control/gr_fib_pool.h create mode 100644 subprojects/packagefiles/dpdk/fib-add-resizable-tbl8-pool.patch create mode 100644 subprojects/packagefiles/dpdk/fib-add-shared-tbl8-pool.patch create mode 100644 subprojects/packagefiles/dpdk/fib-share-tbl8-definitions-between-fib-and-fib6.patch create mode 100644 subprojects/packagefiles/dpdk/test-fib6-zero-initialize-config-struct.patch diff --git a/modules/infra/control/fib_pool.c b/modules/infra/control/fib_pool.c new file mode 100644 index 000000000..b971683f6 --- /dev/null +++ b/modules/infra/control/fib_pool.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2026 Maxime Leroy, Free Mobile + +#include +#include +#include +#include + +#include +#include + +// Shared tbl8 pool for all FIBs (IPv4 and IPv6). +// Starts small and resizes automatically when full. +#define FIB_TBL8_POOL_INIT 256 + +static struct rte_fib_tbl8_pool *tbl8_pool; + +struct rte_fib_tbl8_pool *gr_fib_tbl8_pool(void) { + return tbl8_pool; +} + +static void fib_pool_init(struct event_base *) { + // Both dir24_8 (IPv4) and trie (IPv6) use 8B nexthops. + // RTE_FIB_DIR24_8_8B == RTE_FIB6_TRIE_8B (enforced by static_assert in DPDK). + struct rte_fib_tbl8_pool_conf conf = { + .num_tbl8 = FIB_TBL8_POOL_INIT, + .max_tbl8 = 1 << 20, + .nh_sz = RTE_FIB_DIR24_8_8B, + .socket_id = SOCKET_ID_ANY, + }; + tbl8_pool = rte_fib_tbl8_pool_create("fib_tbl8", &conf); + if (tbl8_pool == NULL) + ABORT("rte_fib_tbl8_pool_create: %s", rte_strerror(rte_errno)); + + struct rte_fib_tbl8_pool_rcu_config rcu = { + .v = gr_datapath_rcu(), + }; + int ret = rte_fib_tbl8_pool_rcu_qsbr_add(tbl8_pool, &rcu); + if (ret < 0) + ABORT("rte_fib_tbl8_pool_rcu_qsbr_add: %s", rte_strerror(-ret)); +} + +static void fib_pool_fini(struct event_base *) { + rte_fib_tbl8_pool_free(tbl8_pool); + tbl8_pool = NULL; +} + +static struct gr_module fib_pool_module = { + .name = "fib_pool", + .depends_on = "rcu", + .init = fib_pool_init, + .fini = fib_pool_fini, +}; + +RTE_INIT(fib_pool_constructor) { + gr_register_module(&fib_pool_module); +} diff --git a/modules/infra/control/gr_fib_pool.h b/modules/infra/control/gr_fib_pool.h new file mode 100644 index 000000000..76b24cf90 --- /dev/null +++ b/modules/infra/control/gr_fib_pool.h @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2026 Maxime Leroy, Free Mobile + +#pragma once + +#include + +struct rte_fib_tbl8_pool *gr_fib_tbl8_pool(void); diff --git a/modules/infra/control/meson.build b/modules/infra/control/meson.build index c9008981c..feb63fe92 100644 --- a/modules/infra/control/meson.build +++ b/modules/infra/control/meson.build @@ -4,6 +4,7 @@ src += files( 'bond.c', 'ctlplane.c', + 'fib_pool.c', 'graph.c', 'group_nexthop.c', 'iface.c', diff --git a/modules/ip/control/route.c b/modules/ip/control/route.c index b3d9abf93..bf0b2be64 100644 --- a/modules/ip/control/route.c +++ b/modules/ip/control/route.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -37,15 +38,6 @@ static uint32_t route_counts[GR_MAX_IFACES][UINT_NUM_VALUES(gr_nh_origin_t)]; static uint32_t max_routes_default = 1 << 16; -// Derive num_tbl8 from max_routes for IPv4 DIR24_8. -// Only prefixes longer than /24 consume tbl8 groups. In real-world BGP -// tables, less than 0.1% of prefixes are longer than /24. A ratio of -// 1/500 with a minimum of 256 provides ample headroom. -static inline uint32_t fib4_auto_tbl8(uint32_t max_routes) { - uint32_t n = max_routes / 500; - return n < 256 ? 256 : n; -} - static inline uint32_t fib4_get_max_routes(const struct iface *vrf) { return iface_info_vrf(vrf)->ipv4.max_routes; } @@ -70,7 +62,7 @@ static struct rte_fib *create_fib(const struct iface *vrf) { .rib_ext_sz = sizeof(gr_nh_origin_t), .dir24_8 = { .nh_sz = RTE_FIB_DIR24_8_8B, - .num_tbl8 = fib4_auto_tbl8(fib4_get_max_routes(vrf)), + .tbl8_pool = gr_fib_tbl8_pool(), }, }; struct rte_fib *fib; @@ -744,7 +736,7 @@ static struct api_out fib4_info_list(const void *request, struct api_ctx *ctx) { static struct gr_module route4_module = { .name = "ip_route", - .depends_on = "nexthop", + .depends_on = "nexthop,fib_pool", }; static void fib4_fini(struct iface *vrf) { diff --git a/modules/ip6/control/route.c b/modules/ip6/control/route.c index 553100bfe..1933806ba 100644 --- a/modules/ip6/control/route.c +++ b/modules/ip6/control/route.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -36,16 +37,6 @@ static uint32_t route_counts[GR_MAX_IFACES][UINT_NUM_VALUES(gr_nh_origin_t)]; static uint32_t max_routes_default = 1 << 16; -// Derive num_tbl8 from max_routes for IPv6 TRIE. -// The trie uses 8-bit levels beyond the first 24 bits. IPv6 routes at -// /48 consume up to 3 tbl8 groups each. Sharing reduces actual usage -// but a ratio of 4x is needed to handle real-world prefix distributions -// without exhaustion. -static inline uint32_t fib6_auto_tbl8(uint32_t max_routes) { - uint32_t n = max_routes * 4; - return n < 256 ? 256 : n; -} - static inline uint32_t fib6_get_max_routes(const struct iface *vrf) { return iface_info_vrf(vrf)->ipv6.max_routes; } @@ -70,7 +61,7 @@ static struct rte_fib6 *create_fib6(const struct iface *vrf) { .rib_ext_sz = sizeof(gr_nh_origin_t), .trie = { .nh_sz = RTE_FIB6_TRIE_8B, - .num_tbl8 = fib6_auto_tbl8(fib6_get_max_routes(vrf)), + .tbl8_pool = gr_fib_tbl8_pool(), }, }; struct rte_fib6 *fib; @@ -784,7 +775,7 @@ static struct api_out fib6_info_list(const void *request, struct api_ctx *ctx) { static struct gr_module route6_module = { .name = "ip6_route", - .depends_on = "nexthop", + .depends_on = "nexthop,fib_pool", }; static void fib6_fini(struct iface *vrf) { diff --git a/subprojects/dpdk.wrap b/subprojects/dpdk.wrap index e282d7805..db030481d 100644 --- a/subprojects/dpdk.wrap +++ b/subprojects/dpdk.wrap @@ -6,6 +6,10 @@ diff_files = dpdk/iavf-fix-reported-max-TX-and-RX-queues-in-ethdev-inf.patch, dpdk/net-tap-add-software-MAC-address-filtering.patch, dpdk/fib-fix-prefix-addition-handling.patch, + dpdk/test-fib6-zero-initialize-config-struct.patch, + dpdk/fib-share-tbl8-definitions-between-fib-and-fib6.patch, + dpdk/fib-add-shared-tbl8-pool.patch, + dpdk/fib-add-resizable-tbl8-pool.patch, dpdk/hash-avoid-leaking-entries-on-RCU-defer-queue-failur.patch, dpdk/hash-free-replaced-data-on-overwrite-when-RCU-is-con.patch diff --git a/subprojects/packagefiles/dpdk/fib-add-resizable-tbl8-pool.patch b/subprojects/packagefiles/dpdk/fib-add-resizable-tbl8-pool.patch new file mode 100644 index 000000000..73a9baa7c --- /dev/null +++ b/subprojects/packagefiles/dpdk/fib-add-resizable-tbl8-pool.patch @@ -0,0 +1,601 @@ +From 8d641cd4d11f1c5b75b79b382164f54cecc502a0 Mon Sep 17 00:00:00 2001 +From: Maxime Leroy +Date: Tue, 31 Mar 2026 15:04:46 +0200 +Subject: [PATCH 4/5] fib: add resizable tbl8 pool + +Add dynamic resize support to the shared tbl8 pool. When all groups +are in use, the pool doubles its capacity via an RCU-safe pointer +swap. + +The resize mechanism: +1. Allocate new tbl8 array (double the current size) +2. Copy existing data +3. Patch all registered dp->tbl8 consumer pointers via SLIST +4. rte_rcu_qsbr_synchronize() to wait for all readers +5. Free old tbl8 array + +The pool maintains a SLIST of consumer pointers (dp->tbl8) that are +registered at FIB creation and unregistered at FIB destruction. + +A new fib_tbl8_pool_alloc() function replaces the per-backend +tbl8_alloc logic: it handles get + RCU reclaim retry + resize retry + +group initialization in one place. + +RCU is required for resize and is configured either: +- Explicitly via rte_fib_tbl8_pool_rcu_qsbr_add() for external pools +- Automatically propagated from rte_fib_rcu_qsbr_add() for internal + pools + +New public API: + - rte_fib_tbl8_pool_rcu_qsbr_add() + +New config field: + - rte_fib_tbl8_pool_conf.max_tbl8 (maximum capacity, 0 keeps + the pool fixed-size) + +Signed-off-by: Maxime Leroy + +diff --git a/lib/fib/dir24_8.c b/lib/fib/dir24_8.c +index b8e588a56a..3e8d8d7321 100644 +--- a/lib/fib/dir24_8.c ++++ b/lib/fib/dir24_8.c +@@ -155,26 +155,8 @@ dir24_8_get_lookup_fn(void *p, enum rte_fib_lookup_type type, bool be_addr) + static int + tbl8_alloc(struct dir24_8_tbl *dp, uint64_t nh) + { +- int64_t tbl8_idx; +- uint8_t *tbl8_ptr; +- +- tbl8_idx = fib_tbl8_pool_get(dp->pool); +- +- /* If there are no tbl8 groups try to reclaim one. */ +- if (unlikely(tbl8_idx == -ENOSPC && dp->dq && +- !rte_rcu_qsbr_dq_reclaim(dp->dq, 1, NULL, NULL, NULL))) +- tbl8_idx = fib_tbl8_pool_get(dp->pool); +- +- if (tbl8_idx < 0) +- return tbl8_idx; +- tbl8_ptr = (uint8_t *)dp->tbl8 + +- ((tbl8_idx * FIB_TBL8_GRP_NUM_ENT) << +- dp->nh_sz); +- /*Init tbl8 entries with nexthop from tbl24*/ +- fib_tbl8_write((void *)tbl8_ptr, nh| +- DIR24_8_EXT_ENT, dp->nh_sz, +- FIB_TBL8_GRP_NUM_ENT); +- return tbl8_idx; ++ return fib_tbl8_pool_alloc(dp->pool, nh | DIR24_8_EXT_ENT, ++ dp->dq); + } + + static void +@@ -436,7 +418,9 @@ dir24_8_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth, + tmp = rte_rib_get_nxt(rib, ip, 24, NULL, + RTE_RIB_GET_NXT_COVER); + if ((tmp == NULL) && +- (dp->rsvd_tbl8s >= dp->pool->num_tbl8s)) ++ (dp->rsvd_tbl8s >= (dp->pool->max_tbl8s ? ++ dp->pool->max_tbl8s : ++ dp->pool->num_tbl8s))) + return -ENOSPC; + + } +@@ -549,6 +533,13 @@ dir24_8_create(const char *name, int socket_id, struct rte_fib_conf *fib_conf) + dp->def_nh = def_nh; + dp->nh_sz = nh_sz; + ++ if (fib_tbl8_pool_register(pool, &dp->tbl8) != 0) { ++ rte_errno = ENOMEM; ++ fib_tbl8_pool_unref(pool); ++ rte_free(dp); ++ return NULL; ++ } ++ + /* Init table with default value */ + fib_tbl8_write(dp->tbl24, (def_nh << 1), nh_sz, 1 << 24); + +@@ -560,6 +551,7 @@ dir24_8_free(void *p) + { + struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p; + ++ fib_tbl8_pool_unregister(dp->pool, &dp->tbl8); + rte_rcu_qsbr_dq_delete(dp->dq); + fib_tbl8_pool_unref(dp->pool); + rte_free(dp); +@@ -578,6 +570,21 @@ dir24_8_rcu_qsbr_add(struct dir24_8_tbl *dp, struct rte_fib_rcu_config *cfg, + if (dp->v != NULL) + return -EEXIST; + ++ /* Propagate RCU to the pool for resize if it is resizable */ ++ if (dp->pool->max_tbl8s > 0) { ++ if (dp->pool->v != NULL && dp->pool->v != cfg->v) ++ return -EINVAL; ++ if (dp->pool->v == NULL) { ++ struct rte_fib_tbl8_pool_rcu_config pool_rcu = { ++ .v = cfg->v, ++ }; ++ int rc = rte_fib_tbl8_pool_rcu_qsbr_add( ++ dp->pool, &pool_rcu); ++ if (rc != 0) ++ return rc; ++ } ++ } ++ + if (cfg->mode == RTE_FIB_QSBR_MODE_SYNC) { + /* No other things to do. */ + } else if (cfg->mode == RTE_FIB_QSBR_MODE_DQ) { +diff --git a/lib/fib/fib_tbl8_pool.c b/lib/fib/fib_tbl8_pool.c +index 5f8ba74219..722155b46d 100644 +--- a/lib/fib/fib_tbl8_pool.c ++++ b/lib/fib/fib_tbl8_pool.c +@@ -2,14 +2,18 @@ + * Copyright(c) 2026 Maxime Leroy, Free Mobile + */ + ++#include + #include ++#include + #include + + #include ++#include + #include + #include + #include + ++#include "fib_log.h" + #include "fib_tbl8_pool.h" + + static void +@@ -62,6 +66,151 @@ fib_tbl8_pool_rcu_free_cb(void *p, void *data, + fib_tbl8_pool_cleanup_and_free(pool, tbl8_idx); + } + ++RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_fib_tbl8_pool_resize, 26.07) ++int ++rte_fib_tbl8_pool_resize(struct rte_fib_tbl8_pool *pool, ++ uint32_t new_num_tbl8) ++{ ++ uint32_t new_num, old_num; ++ uint64_t *new_tbl8; ++ uint32_t *new_fl; ++ char mem_name[64]; ++ struct fib_tbl8_consumer *c; ++ ++ if (pool == NULL) ++ return -EINVAL; ++ if (pool->v == NULL) ++ return -EINVAL; ++ ++ old_num = pool->num_tbl8s; ++ new_num = new_num_tbl8; ++ if (pool->max_tbl8s != 0 && new_num > pool->max_tbl8s) ++ new_num = pool->max_tbl8s; ++ if (new_num <= old_num) ++ return -ENOSPC; ++ ++ FIB_LOG(INFO, "Resizing tbl8 pool from %u to %u groups", ++ old_num, new_num); ++ ++ snprintf(mem_name, sizeof(mem_name), "TBL8_RS_%u", new_num); ++ new_tbl8 = rte_zmalloc_socket(mem_name, ++ FIB_TBL8_GRP_NUM_ENT * (1ULL << pool->nh_sz) * (new_num + 1), ++ RTE_CACHE_LINE_SIZE, pool->socket_id); ++ if (new_tbl8 == NULL) ++ return -ENOMEM; ++ ++ snprintf(mem_name, sizeof(mem_name), "TBL8_FL_RS_%u", new_num); ++ new_fl = rte_zmalloc_socket(mem_name, ++ sizeof(uint32_t) * new_num, ++ RTE_CACHE_LINE_SIZE, pool->socket_id); ++ if (new_fl == NULL) { ++ rte_free(new_tbl8); ++ return -ENOMEM; ++ } ++ ++ /* Copy existing tbl8 data */ ++ memcpy(new_tbl8, pool->tbl8, ++ FIB_TBL8_GRP_NUM_ENT * (1ULL << pool->nh_sz) * (old_num + 1)); ++ ++ /* ++ * Rebuild the free list: copy the existing in-use portion, ++ * then append new indices at the top. ++ */ ++ memcpy(new_fl, pool->free_list, sizeof(uint32_t) * old_num); ++ uint32_t i; ++ for (i = old_num; i < new_num; i++) ++ new_fl[i] = i; ++ ++ uint64_t *old_tbl8 = pool->tbl8; ++ uint32_t *old_fl = pool->free_list; ++ ++ pool->free_list = new_fl; ++ pool->num_tbl8s = new_num; ++ ++ /* ++ * Ensure copied tbl8 contents are visible before publishing ++ * the new pointer on weakly ordered architectures. ++ */ ++ atomic_thread_fence(memory_order_release); ++ ++ pool->tbl8 = new_tbl8; ++ ++ /* Update all registered consumer tbl8 pointers */ ++ SLIST_FOREACH(c, &pool->consumers, next) ++ *c->tbl8_ptr = new_tbl8; ++ ++ /* ++ * If RCU is configured, readers may still be accessing old_tbl8. ++ * Synchronize before freeing. ++ */ ++ if (pool->v != NULL) ++ rte_rcu_qsbr_synchronize(pool->v, RTE_QSBR_THRID_INVALID); ++ ++ rte_free(old_tbl8); ++ rte_free(old_fl); ++ ++ return 0; ++} ++ ++int ++fib_tbl8_pool_alloc(struct rte_fib_tbl8_pool *pool, uint64_t nh, ++ struct rte_rcu_qsbr_dq *dq) ++{ ++ int32_t tbl8_idx; ++ uint8_t *tbl8_ptr; ++ ++ tbl8_idx = fib_tbl8_pool_get(pool); ++ ++ /* If there are no tbl8 groups try to reclaim one. */ ++ if (unlikely(tbl8_idx == -ENOSPC && dq && ++ !rte_rcu_qsbr_dq_reclaim(dq, 1, NULL, NULL, NULL))) ++ tbl8_idx = fib_tbl8_pool_get(pool); ++ ++ /* Still full -- try to grow the pool */ ++ if (unlikely(tbl8_idx == -ENOSPC && ++ rte_fib_tbl8_pool_resize(pool, pool->num_tbl8s * 2) == 0)) ++ tbl8_idx = fib_tbl8_pool_get(pool); ++ ++ if (tbl8_idx < 0) ++ return tbl8_idx; ++ ++ tbl8_ptr = (uint8_t *)pool->tbl8 + ++ ((tbl8_idx * FIB_TBL8_GRP_NUM_ENT) << pool->nh_sz); ++ /* Init tbl8 entries with nexthop */ ++ fib_tbl8_write((void *)tbl8_ptr, nh, pool->nh_sz, ++ FIB_TBL8_GRP_NUM_ENT); ++ return tbl8_idx; ++} ++ ++int ++fib_tbl8_pool_register(struct rte_fib_tbl8_pool *pool, uint64_t **tbl8_ptr) ++{ ++ struct fib_tbl8_consumer *c; ++ ++ c = calloc(1, sizeof(*c)); ++ if (c == NULL) ++ return -ENOMEM; ++ ++ c->tbl8_ptr = tbl8_ptr; ++ SLIST_INSERT_HEAD(&pool->consumers, c, next); ++ return 0; ++} ++ ++void ++fib_tbl8_pool_unregister(struct rte_fib_tbl8_pool *pool, uint64_t **tbl8_ptr) ++{ ++ struct fib_tbl8_consumer *c; ++ ++ SLIST_FOREACH(c, &pool->consumers, next) { ++ if (c->tbl8_ptr == tbl8_ptr) { ++ SLIST_REMOVE(&pool->consumers, c, ++ fib_tbl8_consumer, next); ++ free(c); ++ return; ++ } ++ } ++} ++ + void + fib_tbl8_pool_ref(struct rte_fib_tbl8_pool *pool) + { +@@ -71,6 +220,7 @@ fib_tbl8_pool_ref(struct rte_fib_tbl8_pool *pool) + static void + pool_free(struct rte_fib_tbl8_pool *pool) + { ++ RTE_ASSERT(SLIST_EMPTY(&pool->consumers)); + rte_free(pool->free_list); + rte_free(pool->tbl8); + rte_free(pool); +@@ -92,7 +242,9 @@ rte_fib_tbl8_pool_create(const char *name, + char mem_name[64]; + + if (name == NULL || conf == NULL || conf->num_tbl8 == 0 || +- conf->nh_sz > 3) { ++ conf->nh_sz > 3 || ++ (conf->max_tbl8 != 0 && ++ conf->max_tbl8 < conf->num_tbl8)) { + rte_errno = EINVAL; + return NULL; + } +@@ -107,8 +259,10 @@ rte_fib_tbl8_pool_create(const char *name, + + pool->nh_sz = conf->nh_sz; + pool->num_tbl8s = conf->num_tbl8; ++ pool->max_tbl8s = conf->max_tbl8; + pool->socket_id = conf->socket_id; + pool->refcnt = 1; ++ SLIST_INIT(&pool->consumers); + + snprintf(mem_name, sizeof(mem_name), "TBL8_%s", name); + pool->tbl8 = rte_zmalloc_socket(mem_name, +@@ -146,3 +300,21 @@ rte_fib_tbl8_pool_free(struct rte_fib_tbl8_pool *pool) + + fib_tbl8_pool_unref(pool); + } ++ ++RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_fib_tbl8_pool_rcu_qsbr_add, 26.07) ++int ++rte_fib_tbl8_pool_rcu_qsbr_add(struct rte_fib_tbl8_pool *pool, ++ const struct rte_fib_tbl8_pool_rcu_config *cfg) ++{ ++ if (pool == NULL || cfg == NULL || cfg->v == NULL) ++ return -EINVAL; ++ ++ if (pool->v != NULL) ++ return -EEXIST; ++ ++ if (pool->max_tbl8s == 0) ++ return -ENOTSUP; ++ ++ pool->v = cfg->v; ++ return 0; ++} +diff --git a/lib/fib/fib_tbl8_pool.h b/lib/fib/fib_tbl8_pool.h +index 285f06d87f..edd0aedf0f 100644 +--- a/lib/fib/fib_tbl8_pool.h ++++ b/lib/fib/fib_tbl8_pool.h +@@ -17,19 +17,30 @@ + #include + #include + ++#include ++ + #include + + #include "fib_tbl8.h" + #include "rte_fib_tbl8_pool.h" + ++/** Consumer entry -- tracks each FIB's tbl8 pointer for resize updates. */ ++struct fib_tbl8_consumer { ++ SLIST_ENTRY(fib_tbl8_consumer) next; ++ uint64_t **tbl8_ptr; /**< Points to the FIB's dp->tbl8 field */ ++}; ++ + struct rte_fib_tbl8_pool { + uint64_t *tbl8; /**< tbl8 group array */ + uint32_t *free_list; /**< Stack of free group indices */ + uint32_t cur_tbl8s; /**< Number of allocated groups */ +- uint32_t num_tbl8s; /**< Total number of tbl8 groups */ ++ uint32_t num_tbl8s; /**< Current capacity */ ++ uint32_t max_tbl8s; /**< Maximum capacity (0 = fixed) */ + uint8_t nh_sz; /**< Nexthop entry size (0-3) */ + int socket_id; + uint32_t refcnt; /**< Reference count */ ++ struct rte_rcu_qsbr *v; /**< RCU QSBR variable (for resize) */ ++ SLIST_HEAD(, fib_tbl8_consumer) consumers; /**< Registered FIBs */ + }; + + /** +@@ -71,4 +82,32 @@ fib_tbl8_pool_ref(struct rte_fib_tbl8_pool *pool); + void + fib_tbl8_pool_unref(struct rte_fib_tbl8_pool *pool); + ++/** ++ * Allocate a tbl8 group, resizing the pool if needed. ++ * ++ * Tries fib_tbl8_pool_get() first; on ENOSPC, tries RCU reclaim via @p dq, ++ * then attempts fib_tbl8_pool_resize(). Initialises the group with @p nh. ++ * ++ * @return group index on success, negative errno on failure. ++ */ ++int ++fib_tbl8_pool_alloc(struct rte_fib_tbl8_pool *pool, uint64_t nh, ++ struct rte_rcu_qsbr_dq *dq); ++ ++/** ++ * Register a FIB consumer so its tbl8 pointer is updated on resize. ++ * ++ * @param pool Pool handle. ++ * @param tbl8_ptr Address of the consumer's tbl8 pointer (e.g. &dp->tbl8). ++ * @return 0 on success, negative errno on failure. ++ */ ++int ++fib_tbl8_pool_register(struct rte_fib_tbl8_pool *pool, uint64_t **tbl8_ptr); ++ ++/** ++ * Unregister a FIB consumer. ++ */ ++void ++fib_tbl8_pool_unregister(struct rte_fib_tbl8_pool *pool, uint64_t **tbl8_ptr); ++ + #endif /* _FIB_TBL8_POOL_H_ */ +diff --git a/lib/fib/rte_fib_tbl8_pool.h b/lib/fib/rte_fib_tbl8_pool.h +index e362efe74b..d37ddedff3 100644 +--- a/lib/fib/rte_fib_tbl8_pool.h ++++ b/lib/fib/rte_fib_tbl8_pool.h +@@ -21,6 +21,12 @@ + * rte_fib_tbl8_pool_free(). The pool is freed when the last + * reference is dropped. + * ++ * Resizing: if max_tbl8 is set in the pool configuration, the pool ++ * can grow on demand up to that limit. This requires an RCU QSBR ++ * variable (rte_fib_tbl8_pool_rcu_qsbr_add). When max_tbl8 is 0 ++ * (default), the pool has a fixed capacity and no RCU is needed ++ * for pool operation. ++ * + * Thread safety: none. The pool is not thread-safe. All operations + * on FIBs sharing the same pool (route updates, FIB creation and + * destruction, pool create/free) must be serialized by the caller. +@@ -28,6 +34,8 @@ + + #include + ++#include ++ + #ifdef __cplusplus + extern "C" { + #endif +@@ -36,11 +44,17 @@ struct rte_fib_tbl8_pool; + + /** tbl8 pool configuration */ + struct rte_fib_tbl8_pool_conf { +- uint32_t num_tbl8; /**< Number of tbl8 groups */ ++ uint32_t num_tbl8; /**< Initial number of tbl8 groups */ ++ uint32_t max_tbl8; /**< Max tbl8 groups (0 = fixed, no resize) */ + uint8_t nh_sz; /**< Nexthop size: 0=1B, 1=2B, 2=4B, 3=8B */ + int socket_id; /**< NUMA socket for memory allocation */ + }; + ++/** RCU QSBR configuration for tbl8 pool resize. */ ++struct rte_fib_tbl8_pool_rcu_config { ++ struct rte_rcu_qsbr *v; /**< RCU QSBR variable */ ++}; ++ + /** + * Create a tbl8 pool. + * +@@ -69,6 +83,46 @@ __rte_experimental + void + rte_fib_tbl8_pool_free(struct rte_fib_tbl8_pool *pool); + ++/** ++ * Associate an RCU QSBR variable with the pool. ++ * ++ * Required for resizable pools so that the old tbl8 array can be ++ * reclaimed safely after a resize. ++ * ++ * @param pool ++ * Pool handle ++ * @param cfg ++ * RCU configuration ++ * @return ++ * 0 on success, negative errno on failure ++ */ ++__rte_experimental ++int ++rte_fib_tbl8_pool_rcu_qsbr_add(struct rte_fib_tbl8_pool *pool, ++ const struct rte_fib_tbl8_pool_rcu_config *cfg); ++ ++/** ++ * Resize the tbl8 pool to a given capacity. ++ * ++ * The new capacity must be greater than the current capacity and ++ * must not exceed max_tbl8 (if set). Requires RCU to be configured. ++ * ++ * @param pool ++ * Pool handle ++ * @param new_num_tbl8 ++ * Target number of tbl8 groups ++ * @return ++ * 0 on success ++ * -EINVAL if RCU is not configured (see rte_fib_tbl8_pool_rcu_qsbr_add) ++ * -ENOSPC if pool cannot grow (at max capacity or ++ * new_num_tbl8 <= current capacity) ++ * -ENOMEM if memory allocation failed ++ */ ++__rte_experimental ++int ++rte_fib_tbl8_pool_resize(struct rte_fib_tbl8_pool *pool, ++ uint32_t new_num_tbl8); ++ + #ifdef __cplusplus + } + #endif +diff --git a/lib/fib/trie.c b/lib/fib/trie.c +index 798d322b1e..7b9c11f81f 100644 +--- a/lib/fib/trie.c ++++ b/lib/fib/trie.c +@@ -102,24 +102,7 @@ trie_get_lookup_fn(void *p, enum rte_fib6_lookup_type type) + static int + tbl8_alloc(struct rte_trie_tbl *dp, uint64_t nh) + { +- int64_t tbl8_idx; +- uint8_t *tbl8_ptr; +- +- tbl8_idx = fib_tbl8_pool_get(dp->pool); +- +- /* If there are no tbl8 groups try to reclaim one. */ +- if (unlikely(tbl8_idx == -ENOSPC && dp->dq && +- !rte_rcu_qsbr_dq_reclaim(dp->dq, 1, NULL, NULL, NULL))) +- tbl8_idx = fib_tbl8_pool_get(dp->pool); +- +- if (tbl8_idx < 0) +- return tbl8_idx; +- tbl8_ptr = get_tbl_p_by_idx(dp->tbl8, +- tbl8_idx * FIB_TBL8_GRP_NUM_ENT, dp->nh_sz); +- /*Init tbl8 entries with nexthop from tbl24*/ +- fib_tbl8_write((void *)tbl8_ptr, nh, dp->nh_sz, +- FIB_TBL8_GRP_NUM_ENT); +- return tbl8_idx; ++ return fib_tbl8_pool_alloc(dp->pool, nh, dp->dq); + } + + static void +@@ -531,7 +514,9 @@ trie_modify(struct rte_fib6 *fib, const struct rte_ipv6_addr *ip, + return 0; + } + +- if ((depth > 24) && (dp->rsvd_tbl8s + depth_diff > dp->pool->num_tbl8s)) ++ if ((depth > 24) && (dp->rsvd_tbl8s + depth_diff > ++ (dp->pool->max_tbl8s ? dp->pool->max_tbl8s : ++ dp->pool->num_tbl8s))) + return -ENOSPC; + + node = rte_rib6_insert(rib, &ip_masked, depth); +@@ -643,6 +628,13 @@ trie_create(const char *name, int socket_id, + dp->pool = pool; + dp->tbl8 = pool->tbl8; + ++ if (fib_tbl8_pool_register(pool, &dp->tbl8) != 0) { ++ rte_errno = ENOMEM; ++ fib_tbl8_pool_unref(pool); ++ rte_free(dp); ++ return NULL; ++ } ++ + fib_tbl8_write(&dp->tbl24, (def_nh << 1), nh_sz, 1 << 24); + + return dp; +@@ -653,6 +645,7 @@ trie_free(void *p) + { + struct rte_trie_tbl *dp = (struct rte_trie_tbl *)p; + ++ fib_tbl8_pool_unregister(dp->pool, &dp->tbl8); + rte_rcu_qsbr_dq_delete(dp->dq); + fib_tbl8_pool_unref(dp->pool); + rte_free(dp); +@@ -671,6 +664,21 @@ trie_rcu_qsbr_add(struct rte_trie_tbl *dp, struct rte_fib6_rcu_config *cfg, + if (dp->v != NULL) + return -EEXIST; + ++ /* Propagate RCU to the pool for resize if it is resizable */ ++ if (dp->pool->max_tbl8s > 0) { ++ if (dp->pool->v != NULL && dp->pool->v != cfg->v) ++ return -EINVAL; ++ if (dp->pool->v == NULL) { ++ struct rte_fib_tbl8_pool_rcu_config pool_rcu = { ++ .v = cfg->v, ++ }; ++ int rc = rte_fib_tbl8_pool_rcu_qsbr_add( ++ dp->pool, &pool_rcu); ++ if (rc != 0) ++ return rc; ++ } ++ } ++ + switch (cfg->mode) { + case RTE_FIB6_QSBR_MODE_DQ: + /* Init QSBR defer queue. */ +-- +2.43.0 + diff --git a/subprojects/packagefiles/dpdk/fib-add-shared-tbl8-pool.patch b/subprojects/packagefiles/dpdk/fib-add-shared-tbl8-pool.patch new file mode 100644 index 000000000..389d717a2 --- /dev/null +++ b/subprojects/packagefiles/dpdk/fib-add-shared-tbl8-pool.patch @@ -0,0 +1,921 @@ +From af412c5f2e4a95eee6a93ab0601445b6c9712b7c Mon Sep 17 00:00:00 2001 +From: Maxime Leroy +Date: Tue, 31 Mar 2026 16:32:11 +0200 +Subject: [PATCH 3/5] fib: add shared tbl8 pool + +Replace the per-FIB tbl8 allocation with a common refcounted tbl8 +pool (fib_tbl8_pool). + +A FIB can either use an internal pool (created transparently from the +existing num_tbl8 config parameter) or attach to an external shared +pool via the new tbl8_pool config field. The shared pool allows +multiple FIB instances (e.g. one per VRF) to draw tbl8 groups from +the same memory, reducing overall allocation. + +The pool is refcounted: internal pools start at refcount 1 and are +freed when the owning FIB is destroyed. External pools are +incremented on FIB attach and decremented on FIB detach; the creator +releases its reference via rte_fib_tbl8_pool_free(). + +The per-FIB RCU defer queue callback is shared across both backends +(fib_tbl8_pool_rcu_free_cb). + +New public API: + - rte_fib_tbl8_pool_create() + - rte_fib_tbl8_pool_free() + +Signed-off-by: Maxime Leroy + +diff --git a/lib/fib/dir24_8.c b/lib/fib/dir24_8.c +index ae64bda0d6..b8e588a56a 100644 +--- a/lib/fib/dir24_8.c ++++ b/lib/fib/dir24_8.c +@@ -152,41 +152,18 @@ dir24_8_get_lookup_fn(void *p, enum rte_fib_lookup_type type, bool be_addr) + return NULL; + } + +-/* +- * Get an index of a free tbl8 from the pool +- */ +-static inline int32_t +-tbl8_get(struct dir24_8_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 dir24_8_tbl *dp, uint32_t tbl8_ind) +-{ +- dp->tbl8_pool[--dp->tbl8_pool_pos] = tbl8_ind; +-} +- + static int + tbl8_alloc(struct dir24_8_tbl *dp, uint64_t nh) + { + int64_t tbl8_idx; + uint8_t *tbl8_ptr; + +- tbl8_idx = tbl8_get(dp); ++ tbl8_idx = fib_tbl8_pool_get(dp->pool); + + /* If there are no tbl8 groups try to reclaim one. */ + if (unlikely(tbl8_idx == -ENOSPC && dp->dq && + !rte_rcu_qsbr_dq_reclaim(dp->dq, 1, NULL, NULL, NULL))) +- tbl8_idx = tbl8_get(dp); ++ tbl8_idx = fib_tbl8_pool_get(dp->pool); + + if (tbl8_idx < 0) + return tbl8_idx; +@@ -200,24 +177,6 @@ tbl8_alloc(struct dir24_8_tbl *dp, uint64_t nh) + return tbl8_idx; + } + +-static void +-tbl8_cleanup_and_free(struct dir24_8_tbl *dp, uint64_t tbl8_idx) +-{ +- uint8_t *ptr = (uint8_t *)dp->tbl8 + (tbl8_idx * FIB_TBL8_GRP_NUM_ENT << dp->nh_sz); +- +- memset(ptr, 0, FIB_TBL8_GRP_NUM_ENT << dp->nh_sz); +- tbl8_put(dp, tbl8_idx); +-} +- +-static void +-__rcu_qsbr_free_resource(void *p, void *data, unsigned int n __rte_unused) +-{ +- struct dir24_8_tbl *dp = p; +- uint64_t tbl8_idx = *(uint64_t *)data; +- +- tbl8_cleanup_and_free(dp, tbl8_idx); +-} +- + static void + tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx) + { +@@ -276,10 +235,10 @@ tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx) + } + + if (dp->v == NULL) { +- tbl8_cleanup_and_free(dp, tbl8_idx); ++ fib_tbl8_pool_cleanup_and_free(dp->pool, tbl8_idx); + } else if (dp->rcu_mode == RTE_FIB_QSBR_MODE_SYNC) { + rte_rcu_qsbr_synchronize(dp->v, RTE_QSBR_THRID_INVALID); +- tbl8_cleanup_and_free(dp, tbl8_idx); ++ fib_tbl8_pool_cleanup_and_free(dp->pool, tbl8_idx); + } else { /* RTE_FIB_QSBR_MODE_DQ */ + if (rte_rcu_qsbr_dq_enqueue(dp->dq, &tbl8_idx)) + FIB_LOG(ERR, "Failed to push QSBR FIFO"); +@@ -310,14 +269,14 @@ install_to_fib(struct dir24_8_tbl *dp, uint32_t ledge, uint32_t redge, + * needs tbl8 for ledge and redge. + */ + tbl8_idx = tbl8_alloc(dp, tbl24_tmp); +- tmp_tbl8_idx = tbl8_get(dp); ++ tmp_tbl8_idx = fib_tbl8_pool_get(dp->pool); + if (tbl8_idx < 0) + return -ENOSPC; + else if (tmp_tbl8_idx < 0) { +- tbl8_put(dp, tbl8_idx); ++ fib_tbl8_pool_cleanup_and_free(dp->pool, tbl8_idx); + return -ENOSPC; + } +- tbl8_put(dp, tmp_tbl8_idx); ++ fib_tbl8_pool_put(dp->pool, tmp_tbl8_idx); + /*update dir24 entry with tbl8 index*/ + fib_tbl8_write(get_tbl24_p(dp, ledge, + dp->nh_sz), (tbl8_idx << 1)| +@@ -477,7 +436,7 @@ dir24_8_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth, + tmp = rte_rib_get_nxt(rib, ip, 24, NULL, + RTE_RIB_GET_NXT_COVER); + if ((tmp == NULL) && +- (dp->rsvd_tbl8s >= dp->number_tbl8s)) ++ (dp->rsvd_tbl8s >= dp->pool->num_tbl8s)) + return -ENOSPC; + + } +@@ -533,66 +492,62 @@ 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; ++ struct rte_fib_tbl8_pool *pool; + 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; + } + +- uint32_t i; +- + def_nh = fib_conf->default_nh; + nh_sz = fib_conf->dir24_8.nh_sz; +- num_tbl8 = fib_conf->dir24_8.num_tbl8; ++ ++ if (fib_conf->dir24_8.tbl8_pool != NULL) { ++ /* External shared pool */ ++ pool = fib_conf->dir24_8.tbl8_pool; ++ if (pool->nh_sz != nh_sz) { ++ rte_errno = EINVAL; ++ return NULL; ++ } ++ fib_tbl8_pool_ref(pool); ++ } else { ++ /* Internal pool */ ++ if ((fib_conf->dir24_8.num_tbl8 > ++ get_max_nh(fib_conf->dir24_8.nh_sz)) || ++ (fib_conf->dir24_8.num_tbl8 == 0)) { ++ rte_errno = EINVAL; ++ return NULL; ++ } ++ struct rte_fib_tbl8_pool_conf pool_conf = { ++ .num_tbl8 = fib_conf->dir24_8.num_tbl8, ++ .nh_sz = nh_sz, ++ .socket_id = socket_id, ++ }; ++ pool = rte_fib_tbl8_pool_create(name, &pool_conf); ++ if (pool == NULL) ++ return NULL; ++ } + + 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) + sizeof(uint32_t), + RTE_CACHE_LINE_SIZE, socket_id); + if (dp == NULL) { ++ fib_tbl8_pool_unref(pool); + rte_errno = ENOMEM; + return NULL; + } + +- snprintf(mem_name, sizeof(mem_name), "TBL8_%p", dp); +- uint64_t tbl8_sz = FIB_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->pool = pool; ++ dp->tbl8 = pool->tbl8; + 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; +- } +- +- /* Init pool with all tbl8 indices free */ +- for (i = 0; i < dp->number_tbl8s; i++) +- dp->tbl8_pool[i] = i; +- dp->tbl8_pool_pos = 0; + + /* Init table with default value */ + fib_tbl8_write(dp->tbl24, (def_nh << 1), nh_sz, 1 << 24); +@@ -606,8 +561,7 @@ dir24_8_free(void *p) + struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p; + + rte_rcu_qsbr_dq_delete(dp->dq); +- rte_free(dp->tbl8_pool); +- rte_free(dp->tbl8); ++ fib_tbl8_pool_unref(dp->pool); + rte_free(dp); + } + +@@ -639,8 +593,8 @@ dir24_8_rcu_qsbr_add(struct dir24_8_tbl *dp, struct rte_fib_rcu_config *cfg, + if (params.max_reclaim_size == 0) + params.max_reclaim_size = RTE_FIB_RCU_DQ_RECLAIM_MAX; + params.esize = sizeof(uint64_t); +- params.free_fn = __rcu_qsbr_free_resource; +- params.p = dp; ++ params.free_fn = fib_tbl8_pool_rcu_free_cb; ++ params.p = dp->pool; + params.v = cfg->v; + dp->dq = rte_rcu_qsbr_dq_create(¶ms); + if (dp->dq == NULL) { +diff --git a/lib/fib/dir24_8.h b/lib/fib/dir24_8.h +index e75bd120ad..287b91ef4b 100644 +--- a/lib/fib/dir24_8.h ++++ b/lib/fib/dir24_8.h +@@ -14,7 +14,7 @@ + #include + #include + +-#include "fib_tbl8.h" ++#include "fib_tbl8_pool.h" + + /** + * @file +@@ -26,9 +26,7 @@ + #define DIR24_8_TBL24_MASK 0xffffff00 + + struct dir24_8_tbl { +- uint32_t number_tbl8s; /**< Total number of tbl8s */ + uint32_t rsvd_tbl8s; /**< Number of reserved tbl8s */ +- uint32_t tbl8_pool_pos; /**< Next free index in pool */ + enum rte_fib_dir24_8_nh_sz nh_sz; /**< Size of nexthop entry */ + /* RCU config. */ + enum rte_fib_qsbr_mode rcu_mode;/* Blocking, defer queue. */ +@@ -36,7 +34,7 @@ struct dir24_8_tbl { + struct rte_rcu_qsbr_dq *dq; /* RCU QSBR defer queue. */ + uint64_t def_nh; /**< Default next hop */ + uint64_t *tbl8; /**< tbl8 table. */ +- uint32_t *tbl8_pool; /**< Stack of free tbl8 indices */ ++ struct rte_fib_tbl8_pool *pool; /**< tbl8 pool */ + /* tbl24 table. */ + alignas(RTE_CACHE_LINE_SIZE) uint64_t tbl24[]; + }; +diff --git a/lib/fib/fib_tbl8_pool.c b/lib/fib/fib_tbl8_pool.c +new file mode 100644 +index 0000000000..5f8ba74219 +--- /dev/null ++++ b/lib/fib/fib_tbl8_pool.c +@@ -0,0 +1,148 @@ ++/* SPDX-License-Identifier: BSD-3-Clause ++ * Copyright(c) 2026 Maxime Leroy, Free Mobile ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "fib_tbl8_pool.h" ++ ++static void ++pool_init_free_list(struct rte_fib_tbl8_pool *pool) ++{ ++ uint32_t i; ++ ++ /* put entire range of indexes to the tbl8 pool */ ++ for (i = 0; i < pool->num_tbl8s; i++) ++ pool->free_list[i] = i; ++ ++ pool->cur_tbl8s = 0; ++} ++ ++int32_t ++fib_tbl8_pool_get(struct rte_fib_tbl8_pool *pool) ++{ ++ if (pool->cur_tbl8s == pool->num_tbl8s) ++ /* no more free tbl8 */ ++ return -ENOSPC; ++ ++ /* next index */ ++ return pool->free_list[pool->cur_tbl8s++]; ++} ++ ++void ++fib_tbl8_pool_put(struct rte_fib_tbl8_pool *pool, uint32_t idx) ++{ ++ RTE_ASSERT(pool->cur_tbl8s > 0); ++ pool->free_list[--pool->cur_tbl8s] = idx; ++} ++ ++void ++fib_tbl8_pool_cleanup_and_free(struct rte_fib_tbl8_pool *pool, uint64_t idx) ++{ ++ uint8_t *ptr = (uint8_t *)pool->tbl8 + ++ ((idx * FIB_TBL8_GRP_NUM_ENT) << pool->nh_sz); ++ ++ memset(ptr, 0, FIB_TBL8_GRP_NUM_ENT << pool->nh_sz); ++ fib_tbl8_pool_put(pool, idx); ++} ++ ++void ++fib_tbl8_pool_rcu_free_cb(void *p, void *data, ++ unsigned int n __rte_unused) ++{ ++ struct rte_fib_tbl8_pool *pool = p; ++ uint64_t tbl8_idx = *(uint64_t *)data; ++ ++ fib_tbl8_pool_cleanup_and_free(pool, tbl8_idx); ++} ++ ++void ++fib_tbl8_pool_ref(struct rte_fib_tbl8_pool *pool) ++{ ++ pool->refcnt++; ++} ++ ++static void ++pool_free(struct rte_fib_tbl8_pool *pool) ++{ ++ rte_free(pool->free_list); ++ rte_free(pool->tbl8); ++ rte_free(pool); ++} ++ ++void ++fib_tbl8_pool_unref(struct rte_fib_tbl8_pool *pool) ++{ ++ if (--pool->refcnt == 0) ++ pool_free(pool); ++} ++ ++RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_fib_tbl8_pool_create, 26.07) ++struct rte_fib_tbl8_pool * ++rte_fib_tbl8_pool_create(const char *name, ++ const struct rte_fib_tbl8_pool_conf *conf) ++{ ++ struct rte_fib_tbl8_pool *pool; ++ char mem_name[64]; ++ ++ if (name == NULL || conf == NULL || conf->num_tbl8 == 0 || ++ conf->nh_sz > 3) { ++ rte_errno = EINVAL; ++ return NULL; ++ } ++ ++ snprintf(mem_name, sizeof(mem_name), "TBL8_POOL_%s", name); ++ pool = rte_zmalloc_socket(mem_name, sizeof(*pool), ++ RTE_CACHE_LINE_SIZE, conf->socket_id); ++ if (pool == NULL) { ++ rte_errno = ENOMEM; ++ return NULL; ++ } ++ ++ pool->nh_sz = conf->nh_sz; ++ pool->num_tbl8s = conf->num_tbl8; ++ pool->socket_id = conf->socket_id; ++ pool->refcnt = 1; ++ ++ snprintf(mem_name, sizeof(mem_name), "TBL8_%s", name); ++ pool->tbl8 = rte_zmalloc_socket(mem_name, ++ FIB_TBL8_GRP_NUM_ENT * (1ULL << pool->nh_sz) * ++ (pool->num_tbl8s + 1), ++ RTE_CACHE_LINE_SIZE, conf->socket_id); ++ if (pool->tbl8 == NULL) { ++ rte_errno = ENOMEM; ++ rte_free(pool); ++ return NULL; ++ } ++ ++ snprintf(mem_name, sizeof(mem_name), "TBL8_FL_%s", name); ++ pool->free_list = rte_zmalloc_socket(mem_name, ++ sizeof(uint32_t) * pool->num_tbl8s, ++ RTE_CACHE_LINE_SIZE, conf->socket_id); ++ if (pool->free_list == NULL) { ++ rte_errno = ENOMEM; ++ rte_free(pool->tbl8); ++ rte_free(pool); ++ return NULL; ++ } ++ ++ pool_init_free_list(pool); ++ ++ return pool; ++} ++ ++RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_fib_tbl8_pool_free, 26.07) ++void ++rte_fib_tbl8_pool_free(struct rte_fib_tbl8_pool *pool) ++{ ++ if (pool == NULL) ++ return; ++ ++ fib_tbl8_pool_unref(pool); ++} +diff --git a/lib/fib/fib_tbl8_pool.h b/lib/fib/fib_tbl8_pool.h +new file mode 100644 +index 0000000000..285f06d87f +--- /dev/null ++++ b/lib/fib/fib_tbl8_pool.h +@@ -0,0 +1,74 @@ ++/* SPDX-License-Identifier: BSD-3-Clause ++ * Copyright(c) 2026 Maxime Leroy, Free Mobile ++ */ ++ ++#ifndef _FIB_TBL8_POOL_H_ ++#define _FIB_TBL8_POOL_H_ ++ ++/** ++ * @file ++ * Internal tbl8 pool header. ++ * ++ * The pool is not thread-safe. When multiple FIBs share a pool, ++ * all operations (route modifications, FIB creation/destruction) ++ * must be serialized by the caller. ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "fib_tbl8.h" ++#include "rte_fib_tbl8_pool.h" ++ ++struct rte_fib_tbl8_pool { ++ uint64_t *tbl8; /**< tbl8 group array */ ++ uint32_t *free_list; /**< Stack of free group indices */ ++ uint32_t cur_tbl8s; /**< Number of allocated groups */ ++ uint32_t num_tbl8s; /**< Total number of tbl8 groups */ ++ uint8_t nh_sz; /**< Nexthop entry size (0-3) */ ++ int socket_id; ++ uint32_t refcnt; /**< Reference count */ ++}; ++ ++/** ++ * Get a free tbl8 group index from the pool. ++ * @return index on success, -ENOSPC if pool is full ++ */ ++int32_t ++fib_tbl8_pool_get(struct rte_fib_tbl8_pool *pool); ++ ++/** ++ * Return a tbl8 group index to the pool. ++ */ ++void ++fib_tbl8_pool_put(struct rte_fib_tbl8_pool *pool, uint32_t idx); ++ ++/** ++ * Clear a tbl8 group and return its index to the pool. ++ */ ++void ++fib_tbl8_pool_cleanup_and_free(struct rte_fib_tbl8_pool *pool, uint64_t idx); ++ ++/** ++ * RCU defer queue callback for tbl8 group reclamation. ++ * Shared by dir24_8 and trie backends. ++ * Use as params.free_fn with params.p = pool. ++ */ ++void ++fib_tbl8_pool_rcu_free_cb(void *p, void *data, unsigned int n); ++ ++/** ++ * Increment pool reference count. ++ */ ++void ++fib_tbl8_pool_ref(struct rte_fib_tbl8_pool *pool); ++ ++/** ++ * Decrement pool reference count. Free the pool if it reaches 0. ++ */ ++void ++fib_tbl8_pool_unref(struct rte_fib_tbl8_pool *pool); ++ ++#endif /* _FIB_TBL8_POOL_H_ */ +diff --git a/lib/fib/meson.build b/lib/fib/meson.build +index 573fc50ff1..6ecd954b26 100644 +--- a/lib/fib/meson.build ++++ b/lib/fib/meson.build +@@ -2,8 +2,9 @@ + # Copyright(c) 2018 Vladimir Medvedkin + # Copyright(c) 2019 Intel Corporation + +-sources = files('rte_fib.c', 'rte_fib6.c', 'dir24_8.c', 'trie.c') +-headers = files('rte_fib.h', 'rte_fib6.h') ++sources = files('rte_fib.c', 'rte_fib6.c', 'dir24_8.c', 'trie.c', ++ 'fib_tbl8_pool.c') ++headers = files('rte_fib.h', 'rte_fib6.h', 'rte_fib_tbl8_pool.h') + deps += ['rib'] + deps += ['rcu'] + deps += ['net'] +diff --git a/lib/fib/rte_fib.h b/lib/fib/rte_fib.h +index 874bf1688b..fa4d2999f8 100644 +--- a/lib/fib/rte_fib.h ++++ b/lib/fib/rte_fib.h +@@ -19,6 +19,7 @@ + + #include + #include ++#include + + #ifdef __cplusplus + extern "C" { +@@ -107,6 +108,8 @@ struct rte_fib_conf { + struct { + enum rte_fib_dir24_8_nh_sz nh_sz; + uint32_t num_tbl8; ++ /** Shared tbl8 pool (NULL = internal pool) */ ++ struct rte_fib_tbl8_pool *tbl8_pool; + } dir24_8; + }; + unsigned int flags; /**< Optional feature flags from RTE_FIB_F_* **/ +diff --git a/lib/fib/rte_fib6.h b/lib/fib/rte_fib6.h +index 4527328bf0..655a4c9501 100644 +--- a/lib/fib/rte_fib6.h ++++ b/lib/fib/rte_fib6.h +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #ifdef __cplusplus + extern "C" { +@@ -95,6 +96,8 @@ struct rte_fib6_conf { + struct { + enum rte_fib_trie_nh_sz nh_sz; + uint32_t num_tbl8; ++ /** Shared tbl8 pool (NULL = internal pool) */ ++ struct rte_fib_tbl8_pool *tbl8_pool; + } trie; + }; + }; +diff --git a/lib/fib/rte_fib_tbl8_pool.h b/lib/fib/rte_fib_tbl8_pool.h +new file mode 100644 +index 0000000000..e362efe74b +--- /dev/null ++++ b/lib/fib/rte_fib_tbl8_pool.h +@@ -0,0 +1,76 @@ ++/* SPDX-License-Identifier: BSD-3-Clause ++ * Copyright(c) 2026 Maxime Leroy, Free Mobile ++ */ ++ ++#ifndef _RTE_FIB_TBL8_POOL_H_ ++#define _RTE_FIB_TBL8_POOL_H_ ++ ++/** ++ * @file ++ * Shared tbl8 pool for FIB backends. ++ * ++ * A tbl8 pool manages a shared array of tbl8 groups that can be used ++ * across multiple FIB instances (e.g., one per VRF). ++ * ++ * Two modes of operation: ++ * - Internal pool: set num_tbl8 in the FIB config and leave tbl8_pool ++ * NULL. The pool is created and destroyed with the FIB. ++ * - External shared pool: create with rte_fib_tbl8_pool_create(), pass ++ * the handle via the tbl8_pool config field. Each FIB holds a ++ * reference; the creator releases its reference with ++ * rte_fib_tbl8_pool_free(). The pool is freed when the last ++ * reference is dropped. ++ * ++ * Thread safety: none. The pool is not thread-safe. All operations ++ * on FIBs sharing the same pool (route updates, FIB creation and ++ * destruction, pool create/free) must be serialized by the caller. ++ */ ++ ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct rte_fib_tbl8_pool; ++ ++/** tbl8 pool configuration */ ++struct rte_fib_tbl8_pool_conf { ++ uint32_t num_tbl8; /**< Number of tbl8 groups */ ++ uint8_t nh_sz; /**< Nexthop size: 0=1B, 1=2B, 2=4B, 3=8B */ ++ int socket_id; /**< NUMA socket for memory allocation */ ++}; ++ ++/** ++ * Create a tbl8 pool. ++ * ++ * @param name ++ * Pool name (for memory allocation tracking) ++ * @param conf ++ * Pool configuration ++ * @return ++ * Pool handle on success, NULL on failure with rte_errno set ++ */ ++__rte_experimental ++struct rte_fib_tbl8_pool * ++rte_fib_tbl8_pool_create(const char *name, ++ const struct rte_fib_tbl8_pool_conf *conf); ++ ++/** ++ * Release the creator's reference on a tbl8 pool. ++ * ++ * The pool is freed when the last reference is dropped (i.e. after ++ * all FIBs using this pool have been destroyed). ++ * ++ * @param pool ++ * Pool handle (NULL is allowed) ++ */ ++__rte_experimental ++void ++rte_fib_tbl8_pool_free(struct rte_fib_tbl8_pool *pool); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _RTE_FIB_TBL8_POOL_H_ */ +diff --git a/lib/fib/trie.c b/lib/fib/trie.c +index 198fc54395..798d322b1e 100644 +--- a/lib/fib/trie.c ++++ b/lib/fib/trie.c +@@ -99,53 +99,18 @@ trie_get_lookup_fn(void *p, enum rte_fib6_lookup_type type) + return NULL; + } + +-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); ++ tbl8_idx = fib_tbl8_pool_get(dp->pool); + + /* If there are no tbl8 groups try to reclaim one. */ + if (unlikely(tbl8_idx == -ENOSPC && dp->dq && + !rte_rcu_qsbr_dq_reclaim(dp->dq, 1, NULL, NULL, NULL))) +- tbl8_idx = tbl8_get(dp); ++ tbl8_idx = fib_tbl8_pool_get(dp->pool); + + if (tbl8_idx < 0) + return tbl8_idx; +@@ -157,23 +122,6 @@ tbl8_alloc(struct rte_trie_tbl *dp, uint64_t nh) + return tbl8_idx; + } + +-static void +-tbl8_cleanup_and_free(struct rte_trie_tbl *dp, uint64_t tbl8_idx) +-{ +- uint8_t *ptr = (uint8_t *)dp->tbl8 + (tbl8_idx * FIB_TBL8_GRP_NUM_ENT << dp->nh_sz); +- +- memset(ptr, 0, FIB_TBL8_GRP_NUM_ENT << dp->nh_sz); +- tbl8_put(dp, tbl8_idx); +-} +- +-static void +-__rcu_qsbr_free_resource(void *p, void *data, unsigned int n __rte_unused) +-{ +- struct rte_trie_tbl *dp = p; +- uint64_t tbl8_idx = *(uint64_t *)data; +- tbl8_cleanup_and_free(dp, tbl8_idx); +-} +- + static void + tbl8_recycle(struct rte_trie_tbl *dp, void *par, uint64_t tbl8_idx) + { +@@ -223,10 +171,10 @@ tbl8_recycle(struct rte_trie_tbl *dp, void *par, uint64_t tbl8_idx) + } + + if (dp->v == NULL) { +- tbl8_cleanup_and_free(dp, tbl8_idx); ++ fib_tbl8_pool_cleanup_and_free(dp->pool, tbl8_idx); + } else if (dp->rcu_mode == RTE_FIB6_QSBR_MODE_SYNC) { + rte_rcu_qsbr_synchronize(dp->v, RTE_QSBR_THRID_INVALID); +- tbl8_cleanup_and_free(dp, tbl8_idx); ++ fib_tbl8_pool_cleanup_and_free(dp->pool, tbl8_idx); + } else { /* RTE_FIB6_QSBR_MODE_DQ */ + if (rte_rcu_qsbr_dq_enqueue(dp->dq, &tbl8_idx)) + FIB_LOG(ERR, "Failed to push QSBR FIFO"); +@@ -583,7 +531,7 @@ trie_modify(struct rte_fib6 *fib, const struct rte_ipv6_addr *ip, + return 0; + } + +- if ((depth > 24) && (dp->rsvd_tbl8s + depth_diff > dp->number_tbl8s)) ++ if ((depth > 24) && (dp->rsvd_tbl8s + depth_diff > dp->pool->num_tbl8s)) + return -ENOSPC; + + node = rte_rib6_insert(rib, &ip_masked, depth); +@@ -636,63 +584,66 @@ trie_create(const char *name, int socket_id, + { + char mem_name[TRIE_NAMESIZE]; + struct rte_trie_tbl *dp = NULL; ++ struct rte_fib_tbl8_pool *pool; + 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; ++ ++ if (conf->trie.tbl8_pool != NULL) { ++ /* External shared pool: validate nh_sz matches. */ ++ pool = conf->trie.tbl8_pool; ++ if (pool->nh_sz != nh_sz) { ++ rte_errno = EINVAL; ++ return NULL; ++ } ++ fib_tbl8_pool_ref(pool); ++ } else { ++ /* Internal pool: create from config. */ ++ struct rte_fib_tbl8_pool_conf pool_conf = { ++ .num_tbl8 = conf->trie.num_tbl8, ++ .nh_sz = nh_sz, ++ .socket_id = socket_id, ++ }; ++ ++ if (conf->trie.num_tbl8 == 0 || ++ conf->trie.num_tbl8 > ++ get_max_nh(nh_sz)) { ++ rte_errno = EINVAL; ++ return NULL; ++ } ++ ++ pool = rte_fib_tbl8_pool_create(name, &pool_conf); ++ if (pool == NULL) ++ return NULL; ++ } + + 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) + sizeof(uint32_t), + RTE_CACHE_LINE_SIZE, socket_id); + if (dp == NULL) { ++ fib_tbl8_pool_unref(pool); + rte_errno = ENOMEM; +- return dp; +- } +- +- fib_tbl8_write(&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, FIB_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; ++ dp->pool = pool; ++ dp->tbl8 = pool->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); ++ fib_tbl8_write(&dp->tbl24, (def_nh << 1), nh_sz, 1 << 24); + + return dp; + } +@@ -703,8 +654,7 @@ trie_free(void *p) + struct rte_trie_tbl *dp = (struct rte_trie_tbl *)p; + + rte_rcu_qsbr_dq_delete(dp->dq); +- rte_free(dp->tbl8_pool); +- rte_free(dp->tbl8); ++ fib_tbl8_pool_unref(dp->pool); + rte_free(dp); + } + +@@ -735,8 +685,8 @@ trie_rcu_qsbr_add(struct rte_trie_tbl *dp, struct rte_fib6_rcu_config *cfg, + if (params.max_reclaim_size == 0) + params.max_reclaim_size = RTE_FIB6_RCU_DQ_RECLAIM_MAX; + params.esize = sizeof(uint64_t); +- params.free_fn = __rcu_qsbr_free_resource; +- params.p = dp; ++ params.free_fn = fib_tbl8_pool_rcu_free_cb; ++ params.p = dp->pool; + params.v = cfg->v; + dp->dq = rte_rcu_qsbr_dq_create(¶ms); + if (dp->dq == NULL) { +diff --git a/lib/fib/trie.h b/lib/fib/trie.h +index 30fa886792..61df56b1bb 100644 +--- a/lib/fib/trie.h ++++ b/lib/fib/trie.h +@@ -11,7 +11,7 @@ + #include + #include + +-#include "fib_tbl8.h" ++#include "fib_tbl8_pool.h" + + /** + * @file +@@ -26,13 +26,11 @@ + #define TRIE_EXT_ENT 1 + + struct rte_trie_tbl { +- uint32_t number_tbl8s; /**< Total number of tbl8s */ + uint32_t rsvd_tbl8s; /**< Number of reserved 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; ++ struct rte_fib_tbl8_pool *pool; /**< tbl8 pool */ + /* RCU config. */ + enum rte_fib6_qsbr_mode rcu_mode; /**< Blocking, defer queue. */ + struct rte_rcu_qsbr *v; /**< RCU QSBR variable. */ +-- +2.43.0 + diff --git a/subprojects/packagefiles/dpdk/fib-share-tbl8-definitions-between-fib-and-fib6.patch b/subprojects/packagefiles/dpdk/fib-share-tbl8-definitions-between-fib-and-fib6.patch new file mode 100644 index 000000000..187f52d72 --- /dev/null +++ b/subprojects/packagefiles/dpdk/fib-share-tbl8-definitions-between-fib-and-fib6.patch @@ -0,0 +1,720 @@ +From 649368c160e26b4fd04a9cbe5f407ab1278eede7 Mon Sep 17 00:00:00 2001 +From: Maxime Leroy +Date: Tue, 31 Mar 2026 16:20:56 +0200 +Subject: [PATCH 2/5] fib: share tbl8 definitions between fib and fib6 + +Extract common tbl8 definitions shared by dir24_8 and trie backends +into a new fib_tbl8.h header: + - FIB_TBL8_GRP_NUM_ENT constant (was DIR24_8_TBL8_GRP_NUM_ENT + and TRIE_TBL8_GRP_NUM_ENT) + - enum fib_nh_sz with static_asserts against public enums + - fib_tbl8_write() inline (was write_to_fib and write_to_dp) + +Convert dir24_8 tbl8 index allocator from bitmap to stack, aligned +with the trie backend which already uses a stack-based allocator. + +Signed-off-by: Maxime Leroy + +diff --git a/lib/fib/dir24_8.c b/lib/fib/dir24_8.c +index 489d2ef427..ae64bda0d6 100644 +--- a/lib/fib/dir24_8.c ++++ b/lib/fib/dir24_8.c +@@ -17,6 +17,11 @@ + #include "dir24_8.h" + #include "fib_log.h" + ++static_assert((int)FIB_NH_SZ_1B == (int)RTE_FIB_DIR24_8_1B, "nh_sz 1B mismatch"); ++static_assert((int)FIB_NH_SZ_2B == (int)RTE_FIB_DIR24_8_2B, "nh_sz 2B mismatch"); ++static_assert((int)FIB_NH_SZ_4B == (int)RTE_FIB_DIR24_8_4B, "nh_sz 4B mismatch"); ++static_assert((int)FIB_NH_SZ_8B == (int)RTE_FIB_DIR24_8_8B, "nh_sz 8B mismatch"); ++ + #ifdef CC_AVX512_SUPPORT + + #include "dir24_8_avx512.h" +@@ -147,57 +152,27 @@ dir24_8_get_lookup_fn(void *p, enum rte_fib_lookup_type type, bool be_addr) + return NULL; + } + +-static void +-write_to_fib(void *ptr, uint64_t val, enum rte_fib_dir24_8_nh_sz size, int n) ++/* ++ * Get an index of a free tbl8 from the pool ++ */ ++static inline int32_t ++tbl8_get(struct dir24_8_tbl *dp) + { +- 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; ++ if (dp->tbl8_pool_pos == dp->number_tbl8s) ++ /* no more free tbl8 */ ++ return -ENOSPC; + +- 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 = rte_ctz64(~dp->tbl8_idxes[i]); +- dp->tbl8_idxes[i] |= (1ULL << bit_idx); +- return (i << BITMAP_SLAB_BIT_SIZE_LOG2) + bit_idx; +- } +- 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_free_idx(struct dir24_8_tbl *dp, int idx) ++tbl8_put(struct dir24_8_tbl *dp, uint32_t tbl8_ind) + { +- dp->tbl8_idxes[idx >> BITMAP_SLAB_BIT_SIZE_LOG2] &= +- ~(1ULL << (idx & BITMAP_SLAB_BITMASK)); ++ dp->tbl8_pool[--dp->tbl8_pool_pos] = tbl8_ind; + } + + static int +@@ -206,34 +181,32 @@ tbl8_alloc(struct dir24_8_tbl *dp, uint64_t nh) + int64_t tbl8_idx; + uint8_t *tbl8_ptr; + +- tbl8_idx = tbl8_get_idx(dp); ++ tbl8_idx = tbl8_get(dp); + + /* If there are no tbl8 groups try to reclaim one. */ + if (unlikely(tbl8_idx == -ENOSPC && dp->dq && + !rte_rcu_qsbr_dq_reclaim(dp->dq, 1, NULL, NULL, NULL))) +- tbl8_idx = tbl8_get_idx(dp); ++ tbl8_idx = tbl8_get(dp); + + if (tbl8_idx < 0) + return tbl8_idx; + tbl8_ptr = (uint8_t *)dp->tbl8 + +- ((tbl8_idx * DIR24_8_TBL8_GRP_NUM_ENT) << ++ ((tbl8_idx * FIB_TBL8_GRP_NUM_ENT) << + dp->nh_sz); + /*Init tbl8 entries with nexthop from tbl24*/ +- write_to_fib((void *)tbl8_ptr, nh| ++ fib_tbl8_write((void *)tbl8_ptr, nh| + DIR24_8_EXT_ENT, dp->nh_sz, +- DIR24_8_TBL8_GRP_NUM_ENT); +- dp->cur_tbl8s++; ++ FIB_TBL8_GRP_NUM_ENT); + return tbl8_idx; + } + + static void + tbl8_cleanup_and_free(struct dir24_8_tbl *dp, uint64_t tbl8_idx) + { +- uint8_t *ptr = (uint8_t *)dp->tbl8 + (tbl8_idx * DIR24_8_TBL8_GRP_NUM_ENT << dp->nh_sz); ++ uint8_t *ptr = (uint8_t *)dp->tbl8 + (tbl8_idx * FIB_TBL8_GRP_NUM_ENT << dp->nh_sz); + +- memset(ptr, 0, DIR24_8_TBL8_GRP_NUM_ENT << dp->nh_sz); +- tbl8_free_idx(dp, tbl8_idx); +- dp->cur_tbl8s--; ++ memset(ptr, 0, FIB_TBL8_GRP_NUM_ENT << dp->nh_sz); ++ tbl8_put(dp, tbl8_idx); + } + + static void +@@ -258,9 +231,9 @@ tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx) + switch (dp->nh_sz) { + case RTE_FIB_DIR24_8_1B: + ptr8 = &((uint8_t *)dp->tbl8)[tbl8_idx * +- DIR24_8_TBL8_GRP_NUM_ENT]; ++ FIB_TBL8_GRP_NUM_ENT]; + nh = *ptr8; +- for (i = 1; i < DIR24_8_TBL8_GRP_NUM_ENT; i++) { ++ for (i = 1; i < FIB_TBL8_GRP_NUM_ENT; i++) { + if (nh != ptr8[i]) + return; + } +@@ -269,9 +242,9 @@ tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx) + break; + case RTE_FIB_DIR24_8_2B: + ptr16 = &((uint16_t *)dp->tbl8)[tbl8_idx * +- DIR24_8_TBL8_GRP_NUM_ENT]; ++ FIB_TBL8_GRP_NUM_ENT]; + nh = *ptr16; +- for (i = 1; i < DIR24_8_TBL8_GRP_NUM_ENT; i++) { ++ for (i = 1; i < FIB_TBL8_GRP_NUM_ENT; i++) { + if (nh != ptr16[i]) + return; + } +@@ -280,9 +253,9 @@ tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx) + break; + case RTE_FIB_DIR24_8_4B: + ptr32 = &((uint32_t *)dp->tbl8)[tbl8_idx * +- DIR24_8_TBL8_GRP_NUM_ENT]; ++ FIB_TBL8_GRP_NUM_ENT]; + nh = *ptr32; +- for (i = 1; i < DIR24_8_TBL8_GRP_NUM_ENT; i++) { ++ for (i = 1; i < FIB_TBL8_GRP_NUM_ENT; i++) { + if (nh != ptr32[i]) + return; + } +@@ -291,9 +264,9 @@ tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx) + break; + case RTE_FIB_DIR24_8_8B: + ptr64 = &((uint64_t *)dp->tbl8)[tbl8_idx * +- DIR24_8_TBL8_GRP_NUM_ENT]; ++ FIB_TBL8_GRP_NUM_ENT]; + nh = *ptr64; +- for (i = 1; i < DIR24_8_TBL8_GRP_NUM_ENT; i++) { ++ for (i = 1; i < FIB_TBL8_GRP_NUM_ENT; i++) { + if (nh != ptr64[i]) + return; + } +@@ -337,32 +310,32 @@ install_to_fib(struct dir24_8_tbl *dp, uint32_t ledge, uint32_t redge, + * needs tbl8 for ledge and redge. + */ + tbl8_idx = tbl8_alloc(dp, tbl24_tmp); +- tmp_tbl8_idx = tbl8_get_idx(dp); ++ tmp_tbl8_idx = tbl8_get(dp); + if (tbl8_idx < 0) + return -ENOSPC; + else if (tmp_tbl8_idx < 0) { +- tbl8_free_idx(dp, tbl8_idx); ++ tbl8_put(dp, tbl8_idx); + return -ENOSPC; + } +- tbl8_free_idx(dp, tmp_tbl8_idx); ++ tbl8_put(dp, tmp_tbl8_idx); + /*update dir24 entry with tbl8 index*/ +- write_to_fib(get_tbl24_p(dp, ledge, ++ fib_tbl8_write(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) + ++ (((tbl8_idx * FIB_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)| ++ fib_tbl8_write((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), ++ fib_tbl8_write(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); +@@ -372,17 +345,17 @@ install_to_fib(struct dir24_8_tbl *dp, uint32_t ledge, uint32_t redge, + if (tbl8_idx < 0) + return -ENOSPC; + /*update dir24 entry with tbl8 index*/ +- write_to_fib(get_tbl24_p(dp, redge, ++ fib_tbl8_write(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) << ++ ((tbl8_idx * FIB_TBL8_GRP_NUM_ENT) << + dp->nh_sz); + /*update tbl8 with new next hop*/ +- write_to_fib((void *)tbl8_ptr, (next_hop << 1)| ++ fib_tbl8_write((void *)tbl8_ptr, (next_hop << 1)| + DIR24_8_EXT_ENT, + dp->nh_sz, redge & ~DIR24_8_TBL24_MASK); + tbl8_recycle(dp, redge, tbl8_idx); +@@ -395,18 +368,18 @@ install_to_fib(struct dir24_8_tbl *dp, uint32_t ledge, uint32_t redge, + if (tbl8_idx < 0) + return -ENOSPC; + /*update dir24 entry with tbl8 index*/ +- write_to_fib(get_tbl24_p(dp, ledge, dp->nh_sz), ++ fib_tbl8_write(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) + ++ (((tbl8_idx * FIB_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)| ++ fib_tbl8_write((void *)tbl8_ptr, (next_hop << 1)| + DIR24_8_EXT_ENT, + dp->nh_sz, redge - ledge); + tbl8_recycle(dp, ledge, tbl8_idx); +@@ -576,10 +549,11 @@ dir24_8_create(const char *name, int socket_id, struct rte_fib_conf *fib_conf) + return NULL; + } + ++ uint32_t i; ++ + 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); ++ num_tbl8 = fib_conf->dir24_8.num_tbl8; + + snprintf(mem_name, sizeof(mem_name), "DP_%s", name); + dp = rte_zmalloc_socket(name, sizeof(struct dir24_8_tbl) + +@@ -590,11 +564,8 @@ dir24_8_create(const char *name, int socket_id, struct rte_fib_conf *fib_conf) + 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) * ++ uint64_t tbl8_sz = FIB_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); +@@ -608,16 +579,24 @@ dir24_8_create(const char *name, int socket_id, struct rte_fib_conf *fib_conf) + 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, ++ dp->tbl8_pool = rte_zmalloc_socket(mem_name, ++ sizeof(uint32_t) * dp->number_tbl8s, + RTE_CACHE_LINE_SIZE, socket_id); +- if (dp->tbl8_idxes == NULL) { ++ if (dp->tbl8_pool == NULL) { + rte_errno = ENOMEM; + rte_free(dp->tbl8); + rte_free(dp); + return NULL; + } + ++ /* Init pool with all tbl8 indices free */ ++ for (i = 0; i < dp->number_tbl8s; i++) ++ dp->tbl8_pool[i] = i; ++ dp->tbl8_pool_pos = 0; ++ ++ /* Init table with default value */ ++ fib_tbl8_write(dp->tbl24, (def_nh << 1), nh_sz, 1 << 24); ++ + return dp; + } + +@@ -627,7 +606,7 @@ dir24_8_free(void *p) + struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p; + + rte_rcu_qsbr_dq_delete(dp->dq); +- rte_free(dp->tbl8_idxes); ++ rte_free(dp->tbl8_pool); + rte_free(dp->tbl8); + rte_free(dp); + } +diff --git a/lib/fib/dir24_8.h b/lib/fib/dir24_8.h +index b343b5d686..e75bd120ad 100644 +--- a/lib/fib/dir24_8.h ++++ b/lib/fib/dir24_8.h +@@ -14,24 +14,21 @@ + #include + #include + ++#include "fib_tbl8.h" ++ + /** + * @file + * DIR24_8 algorithm + */ + + #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 */ ++ uint32_t tbl8_pool_pos; /**< Next free index in pool */ + enum rte_fib_dir24_8_nh_sz nh_sz; /**< Size of nexthop entry */ + /* RCU config. */ + enum rte_fib_qsbr_mode rcu_mode;/* Blocking, defer queue. */ +@@ -39,7 +36,7 @@ struct dir24_8_tbl { + struct rte_rcu_qsbr_dq *dq; /* RCU QSBR defer queue. */ + uint64_t def_nh; /**< Default next hop */ + uint64_t *tbl8; /**< tbl8 table. */ +- uint64_t *tbl8_idxes; /**< bitmap containing free tbl8 idxes*/ ++ uint32_t *tbl8_pool; /**< Stack of free tbl8 indices */ + /* tbl24 table. */ + alignas(RTE_CACHE_LINE_SIZE) uint64_t tbl24[]; + }; +@@ -72,7 +69,7 @@ get_tbl24_idx(uint32_t ip) + 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; ++ return (res >> 1) * FIB_TBL8_GRP_NUM_ENT + (uint8_t)ip; + } + + static inline uint64_t +@@ -133,14 +130,14 @@ static inline void dir24_8_lookup_bulk_##suffix(void *p, const uint32_t *ips, \ + 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)]; \ ++ ((tmp >> 1) * FIB_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)]; \ ++ ((tmp >> 1) * FIB_TBL8_GRP_NUM_ENT)]; \ + next_hops[i] = tmp >> 1; \ + } \ + } \ +diff --git a/lib/fib/fib_tbl8.h b/lib/fib/fib_tbl8.h +new file mode 100644 +index 0000000000..b345c1e489 +--- /dev/null ++++ b/lib/fib/fib_tbl8.h +@@ -0,0 +1,50 @@ ++/* SPDX-License-Identifier: BSD-3-Clause ++ * Copyright(c) 2026 Maxime Leroy, Free Mobile ++ */ ++ ++#ifndef _FIB_TBL8_H_ ++#define _FIB_TBL8_H_ ++ ++/** ++ * @file ++ * Common tbl8 definitions shared by dir24_8 and trie backends. ++ */ ++ ++#include ++ ++#define FIB_TBL8_GRP_NUM_ENT 256U ++ ++/** Nexthop size (log2 of byte width) */ ++enum fib_nh_sz { ++ FIB_NH_SZ_1B = 0, ++ FIB_NH_SZ_2B = 1, ++ FIB_NH_SZ_4B = 2, ++ FIB_NH_SZ_8B = 3, ++}; ++ ++static inline void ++fib_tbl8_write(void *ptr, uint64_t val, uint8_t nh_sz, int n) ++{ ++ int i; ++ ++ switch (nh_sz) { ++ case FIB_NH_SZ_1B: ++ for (i = 0; i < n; i++) ++ ((uint8_t *)ptr)[i] = (uint8_t)val; ++ break; ++ case FIB_NH_SZ_2B: ++ for (i = 0; i < n; i++) ++ ((uint16_t *)ptr)[i] = (uint16_t)val; ++ break; ++ case FIB_NH_SZ_4B: ++ for (i = 0; i < n; i++) ++ ((uint32_t *)ptr)[i] = (uint32_t)val; ++ break; ++ case FIB_NH_SZ_8B: ++ for (i = 0; i < n; i++) ++ ((uint64_t *)ptr)[i] = (uint64_t)val; ++ break; ++ } ++} ++ ++#endif /* _FIB_TBL8_H_ */ +diff --git a/lib/fib/trie.c b/lib/fib/trie.c +index fa5d9ec6b0..198fc54395 100644 +--- a/lib/fib/trie.c ++++ b/lib/fib/trie.c +@@ -16,6 +16,10 @@ + #include "fib_log.h" + #include "trie.h" + ++static_assert((int)FIB_NH_SZ_2B == (int)RTE_FIB6_TRIE_2B, "nh_sz 2B mismatch"); ++static_assert((int)FIB_NH_SZ_4B == (int)RTE_FIB6_TRIE_4B, "nh_sz 4B mismatch"); ++static_assert((int)FIB_NH_SZ_8B == (int)RTE_FIB6_TRIE_8B, "nh_sz 8B mismatch"); ++ + #ifdef CC_AVX512_SUPPORT + + #include "trie_avx512.h" +@@ -95,30 +99,6 @@ trie_get_lookup_fn(void *p, enum rte_fib6_lookup_type type) + 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) + { +@@ -170,19 +150,19 @@ tbl8_alloc(struct rte_trie_tbl *dp, uint64_t nh) + if (tbl8_idx < 0) + return tbl8_idx; + tbl8_ptr = get_tbl_p_by_idx(dp->tbl8, +- tbl8_idx * TRIE_TBL8_GRP_NUM_ENT, dp->nh_sz); ++ tbl8_idx * FIB_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); ++ fib_tbl8_write((void *)tbl8_ptr, nh, dp->nh_sz, ++ FIB_TBL8_GRP_NUM_ENT); + return tbl8_idx; + } + + static void + tbl8_cleanup_and_free(struct rte_trie_tbl *dp, uint64_t tbl8_idx) + { +- uint8_t *ptr = (uint8_t *)dp->tbl8 + (tbl8_idx * TRIE_TBL8_GRP_NUM_ENT << dp->nh_sz); ++ uint8_t *ptr = (uint8_t *)dp->tbl8 + (tbl8_idx * FIB_TBL8_GRP_NUM_ENT << dp->nh_sz); + +- memset(ptr, 0, TRIE_TBL8_GRP_NUM_ENT << dp->nh_sz); ++ memset(ptr, 0, FIB_TBL8_GRP_NUM_ENT << dp->nh_sz); + tbl8_put(dp, tbl8_idx); + } + +@@ -206,39 +186,39 @@ tbl8_recycle(struct rte_trie_tbl *dp, void *par, uint64_t tbl8_idx) + switch (dp->nh_sz) { + case RTE_FIB6_TRIE_2B: + ptr16 = &((uint16_t *)dp->tbl8)[tbl8_idx * +- TRIE_TBL8_GRP_NUM_ENT]; ++ FIB_TBL8_GRP_NUM_ENT]; + nh = *ptr16; + if (nh & TRIE_EXT_ENT) + return; +- for (i = 1; i < TRIE_TBL8_GRP_NUM_ENT; i++) { ++ for (i = 1; i < FIB_TBL8_GRP_NUM_ENT; i++) { + if (nh != ptr16[i]) + return; + } +- write_to_dp(par, nh, dp->nh_sz, 1); ++ fib_tbl8_write(par, nh, dp->nh_sz, 1); + break; + case RTE_FIB6_TRIE_4B: + ptr32 = &((uint32_t *)dp->tbl8)[tbl8_idx * +- TRIE_TBL8_GRP_NUM_ENT]; ++ FIB_TBL8_GRP_NUM_ENT]; + nh = *ptr32; + if (nh & TRIE_EXT_ENT) + return; +- for (i = 1; i < TRIE_TBL8_GRP_NUM_ENT; i++) { ++ for (i = 1; i < FIB_TBL8_GRP_NUM_ENT; i++) { + if (nh != ptr32[i]) + return; + } +- write_to_dp(par, nh, dp->nh_sz, 1); ++ fib_tbl8_write(par, nh, dp->nh_sz, 1); + break; + case RTE_FIB6_TRIE_8B: + ptr64 = &((uint64_t *)dp->tbl8)[tbl8_idx * +- TRIE_TBL8_GRP_NUM_ENT]; ++ FIB_TBL8_GRP_NUM_ENT]; + nh = *ptr64; + if (nh & TRIE_EXT_ENT) + return; +- for (i = 1; i < TRIE_TBL8_GRP_NUM_ENT; i++) { ++ for (i = 1; i < FIB_TBL8_GRP_NUM_ENT; i++) { + if (nh != ptr64[i]) + return; + } +- write_to_dp(par, nh, dp->nh_sz, 1); ++ fib_tbl8_write(par, nh, dp->nh_sz, 1); + break; + } + +@@ -265,7 +245,7 @@ get_idx(const struct rte_ipv6_addr *ip, uint32_t prev_idx, int bytes, int first_ + bitshift = (int8_t)(((first_byte + bytes - 1) - i)*BYTE_SIZE); + idx |= ip->a[i] << bitshift; + } +- return (prev_idx * TRIE_TBL8_GRP_NUM_ENT) + idx; ++ return (prev_idx * FIB_TBL8_GRP_NUM_ENT) + idx; + } + + static inline uint64_t +@@ -303,7 +283,7 @@ recycle_root_path(struct rte_trie_tbl *dp, const uint8_t *ip_part, + + if (common_tbl8 != 0) { + p = get_tbl_p_by_idx(dp->tbl8, (val >> 1) * +- TRIE_TBL8_GRP_NUM_ENT + *ip_part, dp->nh_sz); ++ FIB_TBL8_GRP_NUM_ENT + *ip_part, dp->nh_sz); + recycle_root_path(dp, ip_part + 1, common_tbl8 - 1, p); + } + tbl8_recycle(dp, prev, val >> 1); +@@ -327,7 +307,7 @@ build_common_root(struct rte_trie_tbl *dp, const struct rte_ipv6_addr *ip, + idx = tbl8_alloc(dp, val); + if (unlikely(idx < 0)) + return idx; +- write_to_dp(tbl_ptr, (idx << 1) | ++ fib_tbl8_write(tbl_ptr, (idx << 1) | + TRIE_EXT_ENT, dp->nh_sz, 1); + prev_idx = idx; + } else +@@ -336,7 +316,7 @@ build_common_root(struct rte_trie_tbl *dp, const struct rte_ipv6_addr *ip, + j = i; + cur_tbl = dp->tbl8; + } +- *tbl = get_tbl_p_by_idx(cur_tbl, prev_idx * TRIE_TBL8_GRP_NUM_ENT, ++ *tbl = get_tbl_p_by_idx(cur_tbl, prev_idx * FIB_TBL8_GRP_NUM_ENT, + dp->nh_sz); + return 0; + } +@@ -361,22 +341,22 @@ write_edge(struct rte_trie_tbl *dp, const uint8_t *ip_part, uint64_t next_hop, + val = (tbl8_idx << 1)|TRIE_EXT_ENT; + } + p = get_tbl_p_by_idx(dp->tbl8, (tbl8_idx * +- TRIE_TBL8_GRP_NUM_ENT) + *ip_part, dp->nh_sz); ++ FIB_TBL8_GRP_NUM_ENT) + *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(RTE_PTR_ADD(p, (uintptr_t)(1) << dp->nh_sz), ++ fib_tbl8_write(RTE_PTR_ADD(p, (uintptr_t)(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 * +- TRIE_TBL8_GRP_NUM_ENT, dp->nh_sz), ++ fib_tbl8_write(get_tbl_p_by_idx(dp->tbl8, tbl8_idx * ++ FIB_TBL8_GRP_NUM_ENT, 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); ++ fib_tbl8_write(ent, val, dp->nh_sz, 1); + return ret; + } + +@@ -444,7 +424,7 @@ install_to_dp(struct rte_trie_tbl *dp, const struct rte_ipv6_addr *ledge, + 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, ++ fib_tbl8_write(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); +@@ -686,10 +666,10 @@ trie_create(const char *name, int socket_id, + return dp; + } + +- write_to_dp(&dp->tbl24, (def_nh << 1), nh_sz, 1 << 24); ++ fib_tbl8_write(&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 * ++ dp->tbl8 = rte_zmalloc_socket(mem_name, FIB_TBL8_GRP_NUM_ENT * + (1ll << nh_sz) * (num_tbl8 + 1), + RTE_CACHE_LINE_SIZE, socket_id); + if (dp->tbl8 == NULL) { +diff --git a/lib/fib/trie.h b/lib/fib/trie.h +index c34cc2c057..30fa886792 100644 +--- a/lib/fib/trie.h ++++ b/lib/fib/trie.h +@@ -11,6 +11,8 @@ + #include + #include + ++#include "fib_tbl8.h" ++ + /** + * @file + * RTE IPv6 Longest Prefix Match (LPM) +@@ -18,21 +20,14 @@ + + /* @internal Total number of tbl24 entries. */ + #define TRIE_TBL24_NUM_ENT (1 << 24) +-/* @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 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. */ +@@ -124,7 +119,7 @@ static inline void rte_trie_lookup_bulk_##suffix(void *p, \ + j = 3; \ + while (is_entry_extended(tmp)) { \ + tmp = ((type *)dp->tbl8)[ips[i].a[j++] + \ +- ((tmp >> 1) * TRIE_TBL8_GRP_NUM_ENT)]; \ ++ ((tmp >> 1) * FIB_TBL8_GRP_NUM_ENT)]; \ + } \ + next_hops[i] = tmp >> 1; \ + } \ +-- +2.43.0 + diff --git a/subprojects/packagefiles/dpdk/test-fib6-zero-initialize-config-struct.patch b/subprojects/packagefiles/dpdk/test-fib6-zero-initialize-config-struct.patch new file mode 100644 index 000000000..457cdc631 --- /dev/null +++ b/subprojects/packagefiles/dpdk/test-fib6-zero-initialize-config-struct.patch @@ -0,0 +1,65 @@ +From ec6218479628c38cc098f422747acc11d76b39d1 Mon Sep 17 00:00:00 2001 +From: Maxime Leroy +Date: Tue, 31 Mar 2026 16:00:26 +0200 +Subject: [PATCH 1/5] test/fib6: zero-initialize config struct + +Initialize rte_fib6_conf with { 0 } to avoid using uninitialized +fields, aligned with how it is already done in test_fib.c. + +This is needed because the struct will gain new optional fields +(tbl8_pool) that must default to NULL. + +Signed-off-by: Maxime Leroy + +diff --git a/app/test/test_fib6.c b/app/test/test_fib6.c +index 843a4086c1..4bf1d4b045 100644 +--- a/app/test/test_fib6.c ++++ b/app/test/test_fib6.c +@@ -38,7 +38,7 @@ int32_t + test_create_invalid(void) + { + struct rte_fib6 *fib = NULL; +- struct rte_fib6_conf config; ++ struct rte_fib6_conf config = { 0 }; + + config.max_routes = MAX_ROUTES; + config.rib_ext_sz = 0; +@@ -97,7 +97,7 @@ int32_t + test_multiple_create(void) + { + struct rte_fib6 *fib = NULL; +- struct rte_fib6_conf config; ++ struct rte_fib6_conf config = { 0 }; + int32_t i; + + config.rib_ext_sz = 0; +@@ -124,7 +124,7 @@ int32_t + test_free_null(void) + { + struct rte_fib6 *fib = NULL; +- struct rte_fib6_conf config; ++ struct rte_fib6_conf config = { 0 }; + + config.max_routes = MAX_ROUTES; + config.rib_ext_sz = 0; +@@ -148,7 +148,7 @@ int32_t + test_add_del_invalid(void) + { + struct rte_fib6 *fib = NULL; +- struct rte_fib6_conf config; ++ struct rte_fib6_conf config = { 0 }; + uint64_t nh = 100; + struct rte_ipv6_addr ip = RTE_IPV6_ADDR_UNSPEC; + int ret; +@@ -342,7 +342,7 @@ int32_t + test_lookup(void) + { + struct rte_fib6 *fib = NULL; +- struct rte_fib6_conf config; ++ struct rte_fib6_conf config = { 0 }; + uint64_t def_nh = 100; + int ret; + +-- +2.43.0 + From b439b30ae517ac9c09964626fa572db28f2dca4a Mon Sep 17 00:00:00 2001 From: Maxime Leroy Date: Mon, 30 Mar 2026 14:31:50 +0200 Subject: [PATCH 3/3] fib: add tbl8 pool show and resize CLI Add CLI commands to inspect and manually resize the shared tbl8 pool: grcli fib pool -- show used/total tbl8 groups grcli fib pool resize N -- resize pool to N groups New DPDK patch: - fib: add tbl8 pool stats and resize public API New grout API: - GR_FIB_POOL_STATS_GET - GR_FIB_POOL_RESIZE Signed-off-by: Maxime Leroy --- modules/infra/api/gr_infra.h | 22 ++++++ modules/infra/cli/fib_pool.c | 70 +++++++++++++++++++ modules/infra/cli/meson.build | 1 + modules/infra/control/fib_pool.c | 25 +++++++ smoke/config_vrf_test.sh | 6 ++ subprojects/dpdk.wrap | 1 + .../dpdk/fib-add-tbl8-pool-stats-API.patch | 69 ++++++++++++++++++ 7 files changed, 194 insertions(+) create mode 100644 modules/infra/cli/fib_pool.c create mode 100644 subprojects/packagefiles/dpdk/fib-add-tbl8-pool-stats-API.patch diff --git a/modules/infra/api/gr_infra.h b/modules/infra/api/gr_infra.h index 71abe1c72..c84b53999 100644 --- a/modules/infra/api/gr_infra.h +++ b/modules/infra/api/gr_infra.h @@ -454,6 +454,28 @@ struct gr_affinity_cpu_set_req { // struct gr_affinity_cpu_set_resp { }; +// fib pool //////////////////////////////////////////////////////////////////// + +// Get tbl8 pool statistics. +#define GR_FIB_POOL_STATS_GET REQUEST_TYPE(GR_INFRA_MODULE, 0x0080) + +// struct gr_fib_pool_stats_get_req { }; + +struct gr_fib_pool_stats_get_resp { + uint32_t used; // tbl8 groups currently in use + uint32_t total; // total tbl8 groups (current capacity) + uint32_t max; // maximum tbl8 groups (resize limit) +}; + +// Resize the tbl8 pool to a given capacity. +#define GR_FIB_POOL_RESIZE REQUEST_TYPE(GR_INFRA_MODULE, 0x0081) + +struct gr_fib_pool_resize_req { + uint32_t num_tbl8; // target number of tbl8 groups +}; + +// struct gr_fib_pool_resize_resp { }; + // Helper function to convert iface type enum to string static inline const char *gr_iface_type_name(gr_iface_type_t type) { switch (type) { diff --git a/modules/infra/cli/fib_pool.c b/modules/infra/cli/fib_pool.c new file mode 100644 index 000000000..7b14d26a5 --- /dev/null +++ b/modules/infra/cli/fib_pool.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2026 Maxime Leroy, Free Mobile + +#include +#include +#include +#include + +#define FIB_POOL_CTX(root) CLI_CONTEXT(root, CTX_ARG("fib", "FIB"), CTX_ARG("pool", "tbl8 pool")) + +static cmd_status_t fib_pool_show(struct gr_api_client *c, const struct ec_pnode *) { + struct gr_fib_pool_stats_get_resp *resp; + void *resp_ptr = NULL; + + if (gr_api_client_send_recv(c, GR_FIB_POOL_STATS_GET, 0, NULL, &resp_ptr) < 0) + return CMD_ERROR; + + resp = resp_ptr; + struct gr_object *o = gr_object_new(NULL); + gr_object_field(o, "used", GR_DISP_INT, "%u", resp->used); + gr_object_field(o, "total", GR_DISP_INT, "%u", resp->total); + gr_object_field(o, "max", GR_DISP_INT, "%u", resp->max); + gr_object_free(o); + + free(resp_ptr); + return CMD_SUCCESS; +} + +static cmd_status_t fib_pool_set(struct gr_api_client *c, const struct ec_pnode *p) { + struct gr_fib_pool_resize_req req = {0}; + + if (arg_u32(p, "NUM_TBL8", &req.num_tbl8) < 0) + return CMD_ERROR; + + if (gr_api_client_send_recv(c, GR_FIB_POOL_RESIZE, sizeof(req), &req, NULL) < 0) + return CMD_ERROR; + + return CMD_SUCCESS; +} + +static int ctx_init(struct ec_node *root) { + int ret; + + ret = CLI_COMMAND( + FIB_POOL_CTX(root), + "set NUM_TBL8", + fib_pool_set, + "Resize the tbl8 pool.", + with_help( + "Target number of tbl8 groups.", ec_node_uint("NUM_TBL8", 1, UINT32_MAX, 10) + ) + ); + if (ret < 0) + return ret; + + ret = CLI_COMMAND( + FIB_POOL_CTX(root), "[show]", fib_pool_show, "Show tbl8 pool statistics." + ); + + return ret; +} + +static struct cli_context ctx = { + .name = "fib_pool", + .init = ctx_init, +}; + +static void __attribute__((constructor, used)) init(void) { + cli_context_register(&ctx); +} diff --git a/modules/infra/cli/meson.build b/modules/infra/cli/meson.build index 9c1e6b608..e2ae97c3b 100644 --- a/modules/infra/cli/meson.build +++ b/modules/infra/cli/meson.build @@ -6,6 +6,7 @@ cli_src += files( 'affinity.c', 'bond.c', 'events.c', + 'fib_pool.c', 'graph.c', 'iface.c', 'vrf.c', diff --git a/modules/infra/control/fib_pool.c b/modules/infra/control/fib_pool.c index b971683f6..eb3ab1a31 100644 --- a/modules/infra/control/fib_pool.c +++ b/modules/infra/control/fib_pool.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2026 Maxime Leroy, Free Mobile +#include #include +#include #include #include #include @@ -19,6 +21,27 @@ struct rte_fib_tbl8_pool *gr_fib_tbl8_pool(void) { return tbl8_pool; } +static struct api_out fib_pool_stats_get(const void *, struct api_ctx *) { + struct gr_fib_pool_stats_get_resp *resp = malloc(sizeof(*resp)); + if (resp == NULL) + return api_out(ENOMEM, 0, NULL); + + rte_fib_tbl8_pool_get_stats(tbl8_pool, &resp->used, &resp->total, &resp->max); + + return api_out(0, sizeof(*resp), resp); +} + +static struct api_out fib_pool_resize(const void *request, struct api_ctx *) { + const struct gr_fib_pool_resize_req *req = request; + int ret; + + ret = rte_fib_tbl8_pool_resize(tbl8_pool, req->num_tbl8); + if (ret < 0) + return api_out(-ret, 0, NULL); + + return api_out(0, 0, NULL); +} + static void fib_pool_init(struct event_base *) { // Both dir24_8 (IPv4) and trie (IPv6) use 8B nexthops. // RTE_FIB_DIR24_8_8B == RTE_FIB6_TRIE_8B (enforced by static_assert in DPDK). @@ -53,5 +76,7 @@ static struct gr_module fib_pool_module = { }; RTE_INIT(fib_pool_constructor) { + gr_api_handler(GR_FIB_POOL_STATS_GET, fib_pool_stats_get); + gr_api_handler(GR_FIB_POOL_RESIZE, fib_pool_resize); gr_register_module(&fib_pool_module); } diff --git a/smoke/config_vrf_test.sh b/smoke/config_vrf_test.sh index 64fcabb4e..d78f2fcd7 100755 --- a/smoke/config_vrf_test.sh +++ b/smoke/config_vrf_test.sh @@ -64,6 +64,12 @@ grcli route show vrf fibtest count=$(grcli -j route show vrf fibtest | jq length) [ "$count" -eq 17 ] +# Verify tbl8 pool stats and manual resize +grcli fib pool +grcli fib pool set 512 +grcli -j fib pool \ + | jq -e '.total == 512' || fail "tbl8 pool should be 512 after resize" + # Ensure routes survived resize (max_routes change) grcli interface set vrf fibtest rib4-routes 2048 grcli -j interface show name fibtest \ diff --git a/subprojects/dpdk.wrap b/subprojects/dpdk.wrap index db030481d..5b09afec8 100644 --- a/subprojects/dpdk.wrap +++ b/subprojects/dpdk.wrap @@ -10,6 +10,7 @@ diff_files = dpdk/fib-share-tbl8-definitions-between-fib-and-fib6.patch, dpdk/fib-add-shared-tbl8-pool.patch, dpdk/fib-add-resizable-tbl8-pool.patch, + dpdk/fib-add-tbl8-pool-stats-API.patch, dpdk/hash-avoid-leaking-entries-on-RCU-defer-queue-failur.patch, dpdk/hash-free-replaced-data-on-overwrite-when-RCU-is-con.patch diff --git a/subprojects/packagefiles/dpdk/fib-add-tbl8-pool-stats-API.patch b/subprojects/packagefiles/dpdk/fib-add-tbl8-pool-stats-API.patch new file mode 100644 index 000000000..9d619fb4e --- /dev/null +++ b/subprojects/packagefiles/dpdk/fib-add-tbl8-pool-stats-API.patch @@ -0,0 +1,69 @@ +From d8a9f08e77f0577b766744d294a5fe4a187bea64 Mon Sep 17 00:00:00 2001 +From: Maxime Leroy +Date: Mon, 30 Mar 2026 15:00:12 +0200 +Subject: [PATCH 5/5] fib: add tbl8 pool stats API + +Add rte_fib_tbl8_pool_get_stats() to retrieve the number of used and +total tbl8 groups from a pool handle directly, without going through +a FIB instance. + +Signed-off-by: Maxime Leroy + +diff --git a/lib/fib/fib_tbl8_pool.c b/lib/fib/fib_tbl8_pool.c +index 722155b46d..a6494ad2c8 100644 +--- a/lib/fib/fib_tbl8_pool.c ++++ b/lib/fib/fib_tbl8_pool.c +@@ -318,3 +318,20 @@ rte_fib_tbl8_pool_rcu_qsbr_add(struct rte_fib_tbl8_pool *pool, + pool->v = cfg->v; + return 0; + } ++ ++RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_fib_tbl8_pool_get_stats, 26.07) ++int ++rte_fib_tbl8_pool_get_stats(struct rte_fib_tbl8_pool *pool, ++ uint32_t *used, uint32_t *total, uint32_t *max) ++{ ++ if (pool == NULL) ++ return -EINVAL; ++ ++ if (used != NULL) ++ *used = pool->cur_tbl8s; ++ if (total != NULL) ++ *total = pool->num_tbl8s; ++ if (max != NULL) ++ *max = pool->max_tbl8s; ++ return 0; ++} +diff --git a/lib/fib/rte_fib_tbl8_pool.h b/lib/fib/rte_fib_tbl8_pool.h +index d37ddedff3..49a2589a5b 100644 +--- a/lib/fib/rte_fib_tbl8_pool.h ++++ b/lib/fib/rte_fib_tbl8_pool.h +@@ -123,6 +123,25 @@ int + rte_fib_tbl8_pool_resize(struct rte_fib_tbl8_pool *pool, + uint32_t new_num_tbl8); + ++/** ++ * Retrieve tbl8 pool statistics. ++ * ++ * @param pool ++ * Pool handle ++ * @param used ++ * Number of tbl8 groups currently in use (can be NULL) ++ * @param total ++ * Total number of tbl8 groups (current capacity, can be NULL) ++ * @param max ++ * Maximum number of tbl8 groups (0 = fixed, can be NULL) ++ * @return ++ * 0 on success, -EINVAL if pool is NULL ++ */ ++__rte_experimental ++int ++rte_fib_tbl8_pool_get_stats(struct rte_fib_tbl8_pool *pool, ++ uint32_t *used, uint32_t *total, uint32_t *max); ++ + #ifdef __cplusplus + } + #endif +-- +2.43.0 +