Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion modules/infra/api/gr_infra.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -455,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) {
Expand Down
70 changes: 70 additions & 0 deletions modules/infra/cli/fib_pool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 Maxime Leroy, Free Mobile

#include <gr_api.h>
#include <gr_cli.h>
#include <gr_display.h>
#include <gr_infra.h>

#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);
}
1 change: 1 addition & 0 deletions modules/infra/cli/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ cli_src += files(
'affinity.c',
'bond.c',
'events.c',
'fib_pool.c',
'graph.c',
'iface.c',
'vrf.c',
Expand Down
3 changes: 0 additions & 3 deletions modules/infra/cli/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
25 changes: 6 additions & 19 deletions modules/infra/cli/vrf.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,28 @@
#include <gr_infra.h>

#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
vrf_list_info(struct gr_api_client *, const struct gr_iface *iface, char *buf, size_t len) {
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:;
}

Expand All @@ -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;
}

Expand Down
82 changes: 82 additions & 0 deletions modules/infra/control/fib_pool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 Maxime Leroy, Free Mobile

#include <gr_api.h>
#include <gr_fib_pool.h>
#include <gr_infra.h>
#include <gr_log.h>
#include <gr_module.h>
#include <gr_rcu.h>

#include <rte_fib.h>
#include <rte_fib_tbl8_pool.h>

// 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 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).
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_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);
}
8 changes: 8 additions & 0 deletions modules/infra/control/gr_fib_pool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 Maxime Leroy, Free Mobile

#pragma once

#include <rte_fib_tbl8_pool.h>

struct rte_fib_tbl8_pool *gr_fib_tbl8_pool(void);
1 change: 1 addition & 0 deletions modules/infra/control/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
src += files(
'bond.c',
'ctlplane.c',
'fib_pool.c',
'graph.c',
'group_nexthop.c',
'iface.c',
Expand Down
19 changes: 5 additions & 14 deletions modules/infra/control/vrf.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,24 +310,17 @@ 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);

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) {
Expand All @@ -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);
}
}

Expand Down
2 changes: 0 additions & 2 deletions modules/ip/api/gr_ip4.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 0 additions & 10 deletions modules/ip/cli/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading
Loading