* [PATCH] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
@ 2026-06-23 3:23 Chengwen Feng
2026-06-24 3:23 ` [PATCH v2] " Chengwen Feng
` (3 more replies)
0 siblings, 4 replies; 16+ messages in thread
From: Chengwen Feng @ 2026-06-23 3:23 UTC (permalink / raw)
To: thomas, stephen; +Cc: dev
Currently lcore_conf is a global static array. When multiple
ports/lcores are distributed across different NUMA nodes, datapath
suffers from severe cross-NUMA memory access penalty.
On Kunpeng platform, sizeof(struct lcore_conf) reaches 133760 bytes.
Such a huge per-lcore structure significantly amplifies cross-NUMA
overhead in multi-port multi-NUMA deployment scenarios.
This commit refactors lcore_conf to pointer array and implements
per-lcore NUMA-aware hugepage allocation, allocating each lcore's
configuration on its local socket to eliminate remote memory access.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
examples/l3fwd/l3fwd.h | 2 +-
examples/l3fwd/l3fwd_acl.c | 2 +-
examples/l3fwd/l3fwd_em.c | 8 ++++----
examples/l3fwd/l3fwd_fib.c | 6 +++---
examples/l3fwd/l3fwd_lpm.c | 8 ++++----
examples/l3fwd/main.c | 42 ++++++++++++++++++++++++++++++--------
6 files changed, 46 insertions(+), 22 deletions(-)
diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 349fc37c79..20c457ee7f 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -104,7 +104,7 @@ extern uint32_t hash_entry_number;
extern xmm_t val_eth[RTE_MAX_ETHPORTS];
-extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+extern struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
extern struct parm_cfg parm_config;
diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
index 4ee3411d2a..0605f6feb9 100644
--- a/examples/l3fwd/l3fwd_acl.c
+++ b/examples/l3fwd/l3fwd_acl.c
@@ -1090,7 +1090,7 @@ acl_main_loop(__rte_unused void *dummy)
prev_tsc = 0;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
socketid = rte_lcore_to_socket_id(lcore_id);
if (qconf->n_rx_queue == 0) {
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index d8748a0edd..9aa23759d3 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -597,7 +597,7 @@ em_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -685,7 +685,7 @@ em_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
while (!force_quit) {
@@ -747,7 +747,7 @@ em_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -877,7 +877,7 @@ em_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
if (dst_ports == NULL)
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
index 4fc6bf90d5..32c8847130 100644
--- a/examples/l3fwd/l3fwd_fib.c
+++ b/examples/l3fwd/l3fwd_fib.c
@@ -192,7 +192,7 @@ fib_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -282,7 +282,7 @@ fib_event_loop(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -446,7 +446,7 @@ fib_process_event_vector(struct rte_event_vector *vec, uint8_t *type_arr,
uint16_t nh;
int i;
- lconf = &lcore_conf[rte_lcore_id()];
+ lconf = lcore_conf[rte_lcore_id()];
/* Reset counters. */
ipv4_cnt = 0;
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index 2d2651e750..77701503f7 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -154,7 +154,7 @@ lpm_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -270,7 +270,7 @@ lpm_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
while (!force_quit) {
@@ -324,7 +324,7 @@ lpm_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -468,7 +468,7 @@ lpm_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
dst_port_list =
rte_zmalloc("", sizeof(uint16_t) * evt_rsrc->vector_size,
RTE_CACHE_LINE_SIZE);
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index df035b508c..c533a90bb4 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -96,7 +96,7 @@ uint32_t enabled_port_mask;
/* Used only in exact match mode. */
bool ipv6_enabled; /**< ipv6 is false by default. */
-struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
struct parm_cfg parm_config;
@@ -368,17 +368,17 @@ init_lcore_rx_queues(void)
for (i = 0; i < nb_lcore_params; ++i) {
lcore = lcore_params[i].lcore_id;
- nb_rx_queue = lcore_conf[lcore].n_rx_queue;
+ nb_rx_queue = lcore_conf[lcore]->n_rx_queue;
if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
printf("error: too many queues (%u) for lcore: %u\n",
(unsigned int)nb_rx_queue + 1, lcore);
return -1;
} else {
- lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
+ lcore_conf[lcore]->rx_queue_list[nb_rx_queue].port_id =
lcore_params[i].port_id;
- lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
+ lcore_conf[lcore]->rx_queue_list[nb_rx_queue].queue_id =
lcore_params[i].queue_id;
- lcore_conf[lcore].n_rx_queue++;
+ lcore_conf[lcore]->n_rx_queue++;
}
}
return 0;
@@ -1118,6 +1118,26 @@ print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
printf("%s%s", name, buf);
}
+static int
+init_lcore_conf(void)
+{
+ unsigned int lcore_id;
+ int socketid;
+
+ for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+ if (rte_lcore_is_enabled(lcore_id) == 0)
+ continue;
+
+ socketid = rte_lcore_to_socket_id(lcore_id);
+ lcore_conf[lcore_id] = rte_zmalloc_socket(NULL, sizeof(struct lcore_conf),
+ RTE_CACHE_LINE_SIZE, socketid);
+ if (lcore_conf[lcore_id] == NULL)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
int
init_mem(uint16_t portid, unsigned int nb_mbuf)
{
@@ -1189,7 +1209,7 @@ init_mem(uint16_t portid, unsigned int nb_mbuf)
}
#endif
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
qconf->ipv4_lookup_struct =
l3fwd_lkp.get_ipv4_lookup_struct(socketid);
qconf->ipv6_lookup_struct =
@@ -1492,7 +1512,7 @@ l3fwd_poll_resource_setup(void)
"rte_eth_tx_queue_setup: err=%d, "
"port=%d\n", ret, portid);
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
qconf->tx_queue_id[portid] = queueid;
queueid++;
@@ -1505,7 +1525,7 @@ l3fwd_poll_resource_setup(void)
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
if (rte_lcore_is_enabled(lcore_id) == 0)
continue;
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
fflush(stdout);
/* init RX queues */
@@ -1667,6 +1687,10 @@ main(int argc, char **argv)
argc -= ret;
argv += ret;
+ ret = init_lcore_conf();
+ if (ret)
+ rte_exit(EXIT_FAILURE, "Init lcore conf failed!\n");
+
force_quit = false;
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
@@ -1749,7 +1773,7 @@ main(int argc, char **argv)
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
if (rte_lcore_is_enabled(lcore_id) == 0)
continue;
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
portid = qconf->rx_queue_list[queue].port_id;
queueid = qconf->rx_queue_list[queue].queue_id;
--
2.17.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-06-23 3:23 [PATCH] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf Chengwen Feng
@ 2026-06-24 3:23 ` Chengwen Feng
2026-06-24 4:26 ` Stephen Hemminger
2026-07-12 0:36 ` Thomas Monjalon
2026-07-15 9:22 ` [PATCH] " Chengwen Feng
` (2 subsequent siblings)
3 siblings, 2 replies; 16+ messages in thread
From: Chengwen Feng @ 2026-06-24 3:23 UTC (permalink / raw)
To: thomas, stephen; +Cc: dev
Currently lcore_conf is a global static array. When multiple
ports/lcores are distributed across different NUMA nodes, datapath
suffers from severe cross-NUMA memory access penalty.
On Kunpeng platform, sizeof(struct lcore_conf) reaches 133760 bytes.
Such a huge per-lcore structure significantly amplifies cross-NUMA
overhead in multi-port multi-NUMA deployment scenarios.
This commit refactors lcore_conf to pointer array and implements
per-lcore NUMA-aware hugepage allocation, allocating each lcore's
configuration on its local socket to eliminate remote memory access.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
v2: Fix AI review comment: add cleanup when init lcore-conf fail
---
examples/l3fwd/l3fwd.h | 2 +-
examples/l3fwd/l3fwd_acl.c | 2 +-
examples/l3fwd/l3fwd_em.c | 8 +++---
examples/l3fwd/l3fwd_fib.c | 6 ++---
examples/l3fwd/l3fwd_lpm.c | 8 +++---
examples/l3fwd/main.c | 52 +++++++++++++++++++++++++++++++-------
6 files changed, 56 insertions(+), 22 deletions(-)
diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 349fc37c79..20c457ee7f 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -104,7 +104,7 @@ extern uint32_t hash_entry_number;
extern xmm_t val_eth[RTE_MAX_ETHPORTS];
-extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+extern struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
extern struct parm_cfg parm_config;
diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
index 4ee3411d2a..0605f6feb9 100644
--- a/examples/l3fwd/l3fwd_acl.c
+++ b/examples/l3fwd/l3fwd_acl.c
@@ -1090,7 +1090,7 @@ acl_main_loop(__rte_unused void *dummy)
prev_tsc = 0;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
socketid = rte_lcore_to_socket_id(lcore_id);
if (qconf->n_rx_queue == 0) {
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index d8748a0edd..9aa23759d3 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -597,7 +597,7 @@ em_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -685,7 +685,7 @@ em_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
while (!force_quit) {
@@ -747,7 +747,7 @@ em_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -877,7 +877,7 @@ em_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
if (dst_ports == NULL)
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
index 4fc6bf90d5..32c8847130 100644
--- a/examples/l3fwd/l3fwd_fib.c
+++ b/examples/l3fwd/l3fwd_fib.c
@@ -192,7 +192,7 @@ fib_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -282,7 +282,7 @@ fib_event_loop(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -446,7 +446,7 @@ fib_process_event_vector(struct rte_event_vector *vec, uint8_t *type_arr,
uint16_t nh;
int i;
- lconf = &lcore_conf[rte_lcore_id()];
+ lconf = lcore_conf[rte_lcore_id()];
/* Reset counters. */
ipv4_cnt = 0;
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index 2d2651e750..77701503f7 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -154,7 +154,7 @@ lpm_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -270,7 +270,7 @@ lpm_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
while (!force_quit) {
@@ -324,7 +324,7 @@ lpm_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -468,7 +468,7 @@ lpm_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
dst_port_list =
rte_zmalloc("", sizeof(uint16_t) * evt_rsrc->vector_size,
RTE_CACHE_LINE_SIZE);
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index df035b508c..af863407d0 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -96,7 +96,7 @@ uint32_t enabled_port_mask;
/* Used only in exact match mode. */
bool ipv6_enabled; /**< ipv6 is false by default. */
-struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
struct parm_cfg parm_config;
@@ -368,17 +368,17 @@ init_lcore_rx_queues(void)
for (i = 0; i < nb_lcore_params; ++i) {
lcore = lcore_params[i].lcore_id;
- nb_rx_queue = lcore_conf[lcore].n_rx_queue;
+ nb_rx_queue = lcore_conf[lcore]->n_rx_queue;
if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
printf("error: too many queues (%u) for lcore: %u\n",
(unsigned int)nb_rx_queue + 1, lcore);
return -1;
} else {
- lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
+ lcore_conf[lcore]->rx_queue_list[nb_rx_queue].port_id =
lcore_params[i].port_id;
- lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
+ lcore_conf[lcore]->rx_queue_list[nb_rx_queue].queue_id =
lcore_params[i].queue_id;
- lcore_conf[lcore].n_rx_queue++;
+ lcore_conf[lcore]->n_rx_queue++;
}
}
return 0;
@@ -1118,6 +1118,36 @@ print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
printf("%s%s", name, buf);
}
+static int
+init_lcore_conf(void)
+{
+ unsigned int lcore_id;
+ int socketid;
+
+ for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+ if (rte_lcore_is_enabled(lcore_id) == 0)
+ continue;
+
+ socketid = rte_lcore_to_socket_id(lcore_id);
+ lcore_conf[lcore_id] = rte_zmalloc_socket(NULL, sizeof(struct lcore_conf),
+ RTE_CACHE_LINE_SIZE, socketid);
+ if (lcore_conf[lcore_id] == NULL)
+ goto cleanup;
+ }
+
+ return 0;
+
+cleanup:
+ for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+ if (lcore_conf[lcore_id] != NULL) {
+ rte_free(lcore_conf[lcore_id]);
+ lcore_conf[lcore_id] = NULL;
+ }
+ }
+
+ return -ENOMEM;
+}
+
int
init_mem(uint16_t portid, unsigned int nb_mbuf)
{
@@ -1189,7 +1219,7 @@ init_mem(uint16_t portid, unsigned int nb_mbuf)
}
#endif
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
qconf->ipv4_lookup_struct =
l3fwd_lkp.get_ipv4_lookup_struct(socketid);
qconf->ipv6_lookup_struct =
@@ -1492,7 +1522,7 @@ l3fwd_poll_resource_setup(void)
"rte_eth_tx_queue_setup: err=%d, "
"port=%d\n", ret, portid);
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
qconf->tx_queue_id[portid] = queueid;
queueid++;
@@ -1505,7 +1535,7 @@ l3fwd_poll_resource_setup(void)
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
if (rte_lcore_is_enabled(lcore_id) == 0)
continue;
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
fflush(stdout);
/* init RX queues */
@@ -1667,6 +1697,10 @@ main(int argc, char **argv)
argc -= ret;
argv += ret;
+ ret = init_lcore_conf();
+ if (ret)
+ rte_exit(EXIT_FAILURE, "Init lcore conf failed!\n");
+
force_quit = false;
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
@@ -1749,7 +1783,7 @@ main(int argc, char **argv)
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
if (rte_lcore_is_enabled(lcore_id) == 0)
continue;
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
portid = qconf->rx_queue_list[queue].port_id;
queueid = qconf->rx_queue_list[queue].queue_id;
--
2.17.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v2] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-06-24 3:23 ` [PATCH v2] " Chengwen Feng
@ 2026-06-24 4:26 ` Stephen Hemminger
2026-06-24 6:05 ` fengchengwen
2026-07-12 0:36 ` Thomas Monjalon
1 sibling, 1 reply; 16+ messages in thread
From: Stephen Hemminger @ 2026-06-24 4:26 UTC (permalink / raw)
To: Chengwen Feng; +Cc: thomas, dev
On Wed, 24 Jun 2026 11:23:57 +0800
Chengwen Feng <fengchengwen@huawei.com> wrote:
> -extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
> +extern struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
>
Since almost no systems have that many cores. Why not allocate it dynamically
based on rte_lcore_count at runtime?
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-06-24 4:26 ` Stephen Hemminger
@ 2026-06-24 6:05 ` fengchengwen
0 siblings, 0 replies; 16+ messages in thread
From: fengchengwen @ 2026-06-24 6:05 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: thomas, dev
On 6/24/2026 12:26 PM, Stephen Hemminger wrote:
> On Wed, 24 Jun 2026 11:23:57 +0800
> Chengwen Feng <fengchengwen@huawei.com> wrote:
>
>> -extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
>> +extern struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
>>
>
> Since almost no systems have that many cores. Why not allocate it dynamically
> based on rte_lcore_count at runtime?
I understand that the original implementation did this for
simplicity (e.g., it can quickly index to the conf based on
lcore-id).
In my patch, I still keep the array, but the elements are
changed to pointers, and only elements corresponding to
actually valid lcores are allocated.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-06-24 3:23 ` [PATCH v2] " Chengwen Feng
2026-06-24 4:26 ` Stephen Hemminger
@ 2026-07-12 0:36 ` Thomas Monjalon
1 sibling, 0 replies; 16+ messages in thread
From: Thomas Monjalon @ 2026-07-12 0:36 UTC (permalink / raw)
To: Chengwen Feng; +Cc: stephen, dev
24/06/2026 05:23, Chengwen Feng:
> Currently lcore_conf is a global static array. When multiple
> ports/lcores are distributed across different NUMA nodes, datapath
> suffers from severe cross-NUMA memory access penalty.
>
> On Kunpeng platform, sizeof(struct lcore_conf) reaches 133760 bytes.
> Such a huge per-lcore structure significantly amplifies cross-NUMA
> overhead in multi-port multi-NUMA deployment scenarios.
>
> This commit refactors lcore_conf to pointer array and implements
> per-lcore NUMA-aware hugepage allocation, allocating each lcore's
> configuration on its local socket to eliminate remote memory access.
>
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
There is an issue with this change as found by AI:
init_lcore_conf() is called before parse_args(),
so the new lcore_conf allocation happens before --no-numa can set numa_on = 0.
As a result, init_lcore_conf() always uses rte_lcore_to_socket_id()
and allocates each lcore_conf on the lcore's NUMA socket,
even when the user requested non-NUMA allocation.
This can also make startup fail if an enabled lcore is on a socket
without hugepage memory, although --no-numa should force allocations on socket 0.
init_lcore_conf() should be moved after parse_args(), and it should use something like:
socketid = numa_on ? rte_lcore_to_socket_id(lcore_id) : 0;
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-06-23 3:23 [PATCH] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf Chengwen Feng
2026-06-24 3:23 ` [PATCH v2] " Chengwen Feng
@ 2026-07-15 9:22 ` Chengwen Feng
2026-10-29 2:54 ` [PATCH v3] " Chengwen Feng
2026-10-29 12:09 ` [PATCH v4] " Chengwen Feng
3 siblings, 0 replies; 16+ messages in thread
From: Chengwen Feng @ 2026-07-15 9:22 UTC (permalink / raw)
To: thomas, stephen; +Cc: dev
Currently lcore_conf is a global static array. When multiple
ports/lcores are distributed across different NUMA nodes, datapath
suffers from severe cross-NUMA memory access penalty.
On Kunpeng platform, sizeof(struct lcore_conf) reaches 133760 bytes.
Such a huge per-lcore structure significantly amplifies cross-NUMA
overhead in multi-port multi-NUMA deployment scenarios.
This commit refactors lcore_conf to pointer array and implements
per-lcore NUMA-aware hugepage allocation, allocating each lcore's
configuration on its local socket to eliminate remote memory access.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
v3: Fix Thomas' comment: support no-numa parameter
v2: Fix AI review comment: add cleanup when init lcore-conf fail
---
examples/l3fwd/l3fwd.h | 2 +-
examples/l3fwd/l3fwd_acl.c | 2 +-
examples/l3fwd/l3fwd_em.c | 8 +++---
examples/l3fwd/l3fwd_fib.c | 6 ++---
examples/l3fwd/l3fwd_lpm.c | 8 +++---
examples/l3fwd/main.c | 52 +++++++++++++++++++++++++++++++-------
6 files changed, 56 insertions(+), 22 deletions(-)
diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 349fc37c79..20c457ee7f 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -104,7 +104,7 @@ extern uint32_t hash_entry_number;
extern xmm_t val_eth[RTE_MAX_ETHPORTS];
-extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+extern struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
extern struct parm_cfg parm_config;
diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
index 4ee3411d2a..0605f6feb9 100644
--- a/examples/l3fwd/l3fwd_acl.c
+++ b/examples/l3fwd/l3fwd_acl.c
@@ -1090,7 +1090,7 @@ acl_main_loop(__rte_unused void *dummy)
prev_tsc = 0;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
socketid = rte_lcore_to_socket_id(lcore_id);
if (qconf->n_rx_queue == 0) {
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index d8748a0edd..9aa23759d3 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -597,7 +597,7 @@ em_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -685,7 +685,7 @@ em_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
while (!force_quit) {
@@ -747,7 +747,7 @@ em_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -877,7 +877,7 @@ em_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
if (dst_ports == NULL)
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
index 4fc6bf90d5..32c8847130 100644
--- a/examples/l3fwd/l3fwd_fib.c
+++ b/examples/l3fwd/l3fwd_fib.c
@@ -192,7 +192,7 @@ fib_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -282,7 +282,7 @@ fib_event_loop(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -446,7 +446,7 @@ fib_process_event_vector(struct rte_event_vector *vec, uint8_t *type_arr,
uint16_t nh;
int i;
- lconf = &lcore_conf[rte_lcore_id()];
+ lconf = lcore_conf[rte_lcore_id()];
/* Reset counters. */
ipv4_cnt = 0;
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index 2d2651e750..77701503f7 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -154,7 +154,7 @@ lpm_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -270,7 +270,7 @@ lpm_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
while (!force_quit) {
@@ -324,7 +324,7 @@ lpm_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -468,7 +468,7 @@ lpm_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
dst_port_list =
rte_zmalloc("", sizeof(uint16_t) * evt_rsrc->vector_size,
RTE_CACHE_LINE_SIZE);
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index 6866811526..b1ad0af8cb 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -95,7 +95,7 @@ uint32_t enabled_port_mask;
/* Used only in exact match mode. */
bool ipv6_enabled; /**< ipv6 is false by default. */
-struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
struct parm_cfg parm_config;
@@ -367,17 +367,17 @@ init_lcore_rx_queues(void)
for (i = 0; i < nb_lcore_params; ++i) {
lcore = lcore_params[i].lcore_id;
- nb_rx_queue = lcore_conf[lcore].n_rx_queue;
+ nb_rx_queue = lcore_conf[lcore]->n_rx_queue;
if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
printf("error: too many queues (%u) for lcore: %u\n",
(unsigned int)nb_rx_queue + 1, lcore);
return -1;
} else {
- lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
+ lcore_conf[lcore]->rx_queue_list[nb_rx_queue].port_id =
lcore_params[i].port_id;
- lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
+ lcore_conf[lcore]->rx_queue_list[nb_rx_queue].queue_id =
lcore_params[i].queue_id;
- lcore_conf[lcore].n_rx_queue++;
+ lcore_conf[lcore]->n_rx_queue++;
}
}
return 0;
@@ -1117,6 +1117,36 @@ print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
printf("%s%s", name, buf);
}
+static int
+init_lcore_conf(void)
+{
+ unsigned int lcore_id;
+ int socketid;
+
+ for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+ if (rte_lcore_is_enabled(lcore_id) == 0)
+ continue;
+
+ socketid = numa_on ? rte_lcore_to_socket_id(lcore_id) : 0;
+ lcore_conf[lcore_id] = rte_zmalloc_socket(NULL, sizeof(struct lcore_conf),
+ RTE_CACHE_LINE_SIZE, socketid);
+ if (lcore_conf[lcore_id] == NULL)
+ goto cleanup;
+ }
+
+ return 0;
+
+cleanup:
+ for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+ if (lcore_conf[lcore_id] != NULL) {
+ rte_free(lcore_conf[lcore_id]);
+ lcore_conf[lcore_id] = NULL;
+ }
+ }
+
+ return -ENOMEM;
+}
+
int
init_mem(uint16_t portid, unsigned int nb_mbuf)
{
@@ -1188,7 +1218,7 @@ init_mem(uint16_t portid, unsigned int nb_mbuf)
}
#endif
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
qconf->ipv4_lookup_struct =
l3fwd_lkp.get_ipv4_lookup_struct(socketid);
qconf->ipv6_lookup_struct =
@@ -1489,7 +1519,7 @@ l3fwd_poll_resource_setup(void)
"rte_eth_tx_queue_setup: err=%d, "
"port=%d\n", ret, portid);
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
qconf->tx_queue_id[portid] = queueid;
queueid++;
@@ -1502,7 +1532,7 @@ l3fwd_poll_resource_setup(void)
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
if (rte_lcore_is_enabled(lcore_id) == 0)
continue;
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
fflush(stdout);
/* init RX queues */
@@ -1683,6 +1713,10 @@ main(int argc, char **argv)
if (ret < 0)
rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n");
+ ret = init_lcore_conf();
+ if (ret)
+ rte_exit(EXIT_FAILURE, "Init lcore conf failed!\n");
+
RTE_LOG(INFO, L3FWD, "Using Rx burst %u Tx burst %u\n", rx_burst_size, tx_burst_size);
/* Setup function pointers for lookup method. */
@@ -1746,7 +1780,7 @@ main(int argc, char **argv)
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
if (rte_lcore_is_enabled(lcore_id) == 0)
continue;
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
portid = qconf->rx_queue_list[queue].port_id;
queueid = qconf->rx_queue_list[queue].queue_id;
--
2.17.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v3] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-06-23 3:23 [PATCH] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf Chengwen Feng
2026-06-24 3:23 ` [PATCH v2] " Chengwen Feng
2026-07-15 9:22 ` [PATCH] " Chengwen Feng
@ 2026-10-29 2:54 ` Chengwen Feng
2026-10-29 9:19 ` Konstantin Ananyev
2026-10-29 12:09 ` [PATCH v4] " Chengwen Feng
3 siblings, 1 reply; 16+ messages in thread
From: Chengwen Feng @ 2026-10-29 2:54 UTC (permalink / raw)
To: thomas, stephen; +Cc: dev
Currently lcore_conf is a global static array. When multiple
ports/lcores are distributed across different NUMA nodes, datapath
suffers from severe cross-NUMA memory access penalty.
On Kunpeng platform, sizeof(struct lcore_conf) reaches 133760 bytes.
Such a huge per-lcore structure significantly amplifies cross-NUMA
overhead in multi-port multi-NUMA deployment scenarios.
This commit refactors lcore_conf to pointer array and implements
per-lcore NUMA-aware hugepage allocation, allocating each lcore's
configuration on its local socket to eliminate remote memory access.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
v3: Fix Thomas' comment: support no-numa parameter
Deinit lcore_conf on exit
v2: Fix AI review comment: add cleanup when init lcore-conf fail
---
examples/l3fwd/l3fwd.h | 2 +-
examples/l3fwd/l3fwd_acl.c | 2 +-
examples/l3fwd/l3fwd_em.c | 8 +++---
examples/l3fwd/l3fwd_fib.c | 6 ++--
examples/l3fwd/l3fwd_lpm.c | 8 +++---
examples/l3fwd/main.c | 59 ++++++++++++++++++++++++++++++++------
6 files changed, 63 insertions(+), 22 deletions(-)
diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 349fc37c79..20c457ee7f 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -104,7 +104,7 @@ extern uint32_t hash_entry_number;
extern xmm_t val_eth[RTE_MAX_ETHPORTS];
-extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+extern struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
extern struct parm_cfg parm_config;
diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
index 4ee3411d2a..0605f6feb9 100644
--- a/examples/l3fwd/l3fwd_acl.c
+++ b/examples/l3fwd/l3fwd_acl.c
@@ -1090,7 +1090,7 @@ acl_main_loop(__rte_unused void *dummy)
prev_tsc = 0;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
socketid = rte_lcore_to_socket_id(lcore_id);
if (qconf->n_rx_queue == 0) {
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index d8748a0edd..9aa23759d3 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -597,7 +597,7 @@ em_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -685,7 +685,7 @@ em_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
while (!force_quit) {
@@ -747,7 +747,7 @@ em_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -877,7 +877,7 @@ em_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
if (dst_ports == NULL)
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
index 4fc6bf90d5..32c8847130 100644
--- a/examples/l3fwd/l3fwd_fib.c
+++ b/examples/l3fwd/l3fwd_fib.c
@@ -192,7 +192,7 @@ fib_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -282,7 +282,7 @@ fib_event_loop(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -446,7 +446,7 @@ fib_process_event_vector(struct rte_event_vector *vec, uint8_t *type_arr,
uint16_t nh;
int i;
- lconf = &lcore_conf[rte_lcore_id()];
+ lconf = lcore_conf[rte_lcore_id()];
/* Reset counters. */
ipv4_cnt = 0;
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index 2d2651e750..77701503f7 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -154,7 +154,7 @@ lpm_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -270,7 +270,7 @@ lpm_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
while (!force_quit) {
@@ -324,7 +324,7 @@ lpm_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -468,7 +468,7 @@ lpm_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
dst_port_list =
rte_zmalloc("", sizeof(uint16_t) * evt_rsrc->vector_size,
RTE_CACHE_LINE_SIZE);
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index 6866811526..4db565e515 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -95,7 +95,7 @@ uint32_t enabled_port_mask;
/* Used only in exact match mode. */
bool ipv6_enabled; /**< ipv6 is false by default. */
-struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
struct parm_cfg parm_config;
@@ -367,17 +367,17 @@ init_lcore_rx_queues(void)
for (i = 0; i < nb_lcore_params; ++i) {
lcore = lcore_params[i].lcore_id;
- nb_rx_queue = lcore_conf[lcore].n_rx_queue;
+ nb_rx_queue = lcore_conf[lcore]->n_rx_queue;
if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
printf("error: too many queues (%u) for lcore: %u\n",
(unsigned int)nb_rx_queue + 1, lcore);
return -1;
} else {
- lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
+ lcore_conf[lcore]->rx_queue_list[nb_rx_queue].port_id =
lcore_params[i].port_id;
- lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
+ lcore_conf[lcore]->rx_queue_list[nb_rx_queue].queue_id =
lcore_params[i].queue_id;
- lcore_conf[lcore].n_rx_queue++;
+ lcore_conf[lcore]->n_rx_queue++;
}
}
return 0;
@@ -1117,6 +1117,41 @@ print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
printf("%s%s", name, buf);
}
+static void
+deinit_lcore_conf(void)
+{
+ unsigned int lcore_id;
+
+ for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+ if (lcore_conf[lcore_id] != NULL) {
+ rte_free(lcore_conf[lcore_id]);
+ lcore_conf[lcore_id] = NULL;
+ }
+ }
+}
+
+static int
+init_lcore_conf(void)
+{
+ unsigned int lcore_id;
+ int socketid;
+
+ for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+ if (rte_lcore_is_enabled(lcore_id) == 0)
+ continue;
+
+ socketid = numa_on ? rte_lcore_to_socket_id(lcore_id) : 0;
+ lcore_conf[lcore_id] = rte_zmalloc_socket(NULL, sizeof(struct lcore_conf),
+ RTE_CACHE_LINE_SIZE, socketid);
+ if (lcore_conf[lcore_id] == NULL) {
+ deinit_lcore_conf();
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
int
init_mem(uint16_t portid, unsigned int nb_mbuf)
{
@@ -1188,7 +1223,7 @@ init_mem(uint16_t portid, unsigned int nb_mbuf)
}
#endif
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
qconf->ipv4_lookup_struct =
l3fwd_lkp.get_ipv4_lookup_struct(socketid);
qconf->ipv6_lookup_struct =
@@ -1489,7 +1524,7 @@ l3fwd_poll_resource_setup(void)
"rte_eth_tx_queue_setup: err=%d, "
"port=%d\n", ret, portid);
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
qconf->tx_queue_id[portid] = queueid;
queueid++;
@@ -1502,7 +1537,7 @@ l3fwd_poll_resource_setup(void)
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
if (rte_lcore_is_enabled(lcore_id) == 0)
continue;
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
fflush(stdout);
/* init RX queues */
@@ -1683,6 +1718,10 @@ main(int argc, char **argv)
if (ret < 0)
rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n");
+ ret = init_lcore_conf();
+ if (ret)
+ rte_exit(EXIT_FAILURE, "Init lcore conf failed!\n");
+
RTE_LOG(INFO, L3FWD, "Using Rx burst %u Tx burst %u\n", rx_burst_size, tx_burst_size);
/* Setup function pointers for lookup method. */
@@ -1746,7 +1785,7 @@ main(int argc, char **argv)
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
if (rte_lcore_is_enabled(lcore_id) == 0)
continue;
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
portid = qconf->rx_queue_list[queue].port_id;
queueid = qconf->rx_queue_list[queue].queue_id;
@@ -1810,6 +1849,8 @@ main(int argc, char **argv)
/* clean up config file routes */
l3fwd_lkp.free_routes();
+ deinit_lcore_conf();
+
/* clean up the EAL */
rte_eal_cleanup();
--
2.17.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v3] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-10-29 2:54 ` [PATCH v3] " Chengwen Feng
@ 2026-10-29 9:19 ` Konstantin Ananyev
2026-10-29 12:12 ` fengchengwen
0 siblings, 1 reply; 16+ messages in thread
From: Konstantin Ananyev @ 2026-10-29 9:19 UTC (permalink / raw)
To: Chengwen Feng, thomas, stephen; +Cc: dev
> Currently lcore_conf is a global static array. When multiple
> ports/lcores are distributed across different NUMA nodes, datapath
> suffers from severe cross-NUMA memory access penalty.
>
> On Kunpeng platform, sizeof(struct lcore_conf) reaches 133760 bytes.
> Such a huge per-lcore structure significantly amplifies cross-NUMA
> overhead in multi-port multi-NUMA deployment scenarios.
>
> This commit refactors lcore_conf to pointer array and implements
> per-lcore NUMA-aware hugepage allocation, allocating each lcore's
> configuration on its local socket to eliminate remote memory access.
>
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
>
> ---
> v3: Fix Thomas' comment: support no-numa parameter
> Deinit lcore_conf on exit
> v2: Fix AI review comment: add cleanup when init lcore-conf fail
>
> ---
> examples/l3fwd/l3fwd.h | 2 +-
> examples/l3fwd/l3fwd_acl.c | 2 +-
> examples/l3fwd/l3fwd_em.c | 8 +++---
> examples/l3fwd/l3fwd_fib.c | 6 ++--
> examples/l3fwd/l3fwd_lpm.c | 8 +++---
> examples/l3fwd/main.c | 59 ++++++++++++++++++++++++++++++++------
> 6 files changed, 63 insertions(+), 22 deletions(-)
>
> diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
> index 349fc37c79..20c457ee7f 100644
> --- a/examples/l3fwd/l3fwd.h
> +++ b/examples/l3fwd/l3fwd.h
> @@ -104,7 +104,7 @@ extern uint32_t hash_entry_number;
>
> extern xmm_t val_eth[RTE_MAX_ETHPORTS];
>
> -extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
> +extern struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
>
> extern struct parm_cfg parm_config;
>
> diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
> index 4ee3411d2a..0605f6feb9 100644
> --- a/examples/l3fwd/l3fwd_acl.c
> +++ b/examples/l3fwd/l3fwd_acl.c
> @@ -1090,7 +1090,7 @@ acl_main_loop(__rte_unused void *dummy)
>
> prev_tsc = 0;
> lcore_id = rte_lcore_id();
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
> socketid = rte_lcore_to_socket_id(lcore_id);
>
> if (qconf->n_rx_queue == 0) {
> diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
> index d8748a0edd..9aa23759d3 100644
> --- a/examples/l3fwd/l3fwd_em.c
> +++ b/examples/l3fwd/l3fwd_em.c
> @@ -597,7 +597,7 @@ em_main_loop(__rte_unused void *dummy)
> US_PER_S * BURST_TX_DRAIN_US;
>
> lcore_id = rte_lcore_id();
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
>
> const uint16_t n_rx_q = qconf->n_rx_queue;
> const uint16_t n_tx_p = qconf->n_tx_port;
> @@ -685,7 +685,7 @@ em_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
> return;
>
> lcore_id = rte_lcore_id();
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
>
> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
> while (!force_quit) {
> @@ -747,7 +747,7 @@ em_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
>
> lcore_id = rte_lcore_id();
>
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
>
> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>
> @@ -877,7 +877,7 @@ em_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
> if (dst_ports == NULL)
> return;
> lcore_id = rte_lcore_id();
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
>
> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>
> diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
> index 4fc6bf90d5..32c8847130 100644
> --- a/examples/l3fwd/l3fwd_fib.c
> +++ b/examples/l3fwd/l3fwd_fib.c
> @@ -192,7 +192,7 @@ fib_main_loop(__rte_unused void *dummy)
> US_PER_S * BURST_TX_DRAIN_US;
>
> lcore_id = rte_lcore_id();
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
>
> const uint16_t n_rx_q = qconf->n_rx_queue;
> const uint16_t n_tx_p = qconf->n_tx_port;
> @@ -282,7 +282,7 @@ fib_event_loop(struct l3fwd_event_resources *evt_rsrc,
>
> lcore_id = rte_lcore_id();
>
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
>
> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>
> @@ -446,7 +446,7 @@ fib_process_event_vector(struct rte_event_vector *vec, uint8_t *type_arr,
> uint16_t nh;
> int i;
>
> - lconf = &lcore_conf[rte_lcore_id()];
> + lconf = lcore_conf[rte_lcore_id()];
>
> /* Reset counters. */
> ipv4_cnt = 0;
> diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
> index 2d2651e750..77701503f7 100644
> --- a/examples/l3fwd/l3fwd_lpm.c
> +++ b/examples/l3fwd/l3fwd_lpm.c
> @@ -154,7 +154,7 @@ lpm_main_loop(__rte_unused void *dummy)
> US_PER_S * BURST_TX_DRAIN_US;
>
> lcore_id = rte_lcore_id();
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
>
> const uint16_t n_rx_q = qconf->n_rx_queue;
> const uint16_t n_tx_p = qconf->n_tx_port;
> @@ -270,7 +270,7 @@ lpm_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
> return;
>
> lcore_id = rte_lcore_id();
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
>
> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
> while (!force_quit) {
> @@ -324,7 +324,7 @@ lpm_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
>
> lcore_id = rte_lcore_id();
>
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
>
> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>
> @@ -468,7 +468,7 @@ lpm_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
> return;
>
> lcore_id = rte_lcore_id();
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
> dst_port_list =
> rte_zmalloc("", sizeof(uint16_t) * evt_rsrc->vector_size,
> RTE_CACHE_LINE_SIZE);
> diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
> index 6866811526..4db565e515 100644
> --- a/examples/l3fwd/main.c
> +++ b/examples/l3fwd/main.c
> @@ -95,7 +95,7 @@ uint32_t enabled_port_mask;
> /* Used only in exact match mode. */
> bool ipv6_enabled; /**< ipv6 is false by default. */
>
> -struct lcore_conf lcore_conf[RTE_MAX_LCORE];
> +struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
>
> struct parm_cfg parm_config;
>
> @@ -367,17 +367,17 @@ init_lcore_rx_queues(void)
>
> for (i = 0; i < nb_lcore_params; ++i) {
> lcore = lcore_params[i].lcore_id;
> - nb_rx_queue = lcore_conf[lcore].n_rx_queue;
> + nb_rx_queue = lcore_conf[lcore]->n_rx_queue;
> if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
> printf("error: too many queues (%u) for lcore: %u\n",
> (unsigned int)nb_rx_queue + 1, lcore);
> return -1;
> } else {
> - lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
> + lcore_conf[lcore]->rx_queue_list[nb_rx_queue].port_id =
> lcore_params[i].port_id;
> - lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
> + lcore_conf[lcore]->rx_queue_list[nb_rx_queue].queue_id =
> lcore_params[i].queue_id;
> - lcore_conf[lcore].n_rx_queue++;
> + lcore_conf[lcore]->n_rx_queue++;
> }
> }
> return 0;
> @@ -1117,6 +1117,41 @@ print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
> printf("%s%s", name, buf);
> }
>
> +static void
> +deinit_lcore_conf(void)
> +{
> + unsigned int lcore_id;
> +
> + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> + if (lcore_conf[lcore_id] != NULL) {
> + rte_free(lcore_conf[lcore_id]);
> + lcore_conf[lcore_id] = NULL;
> + }
> + }
> +}
> +
> +static int
> +init_lcore_conf(void)
> +{
> + unsigned int lcore_id;
> + int socketid;
> +
> + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> + if (rte_lcore_is_enabled(lcore_id) == 0)
> + continue;
> +
> + socketid = numa_on ? rte_lcore_to_socket_id(lcore_id) : 0;
Shouldn't it be SOCKET_ID_ANY (-1) instead of zero, when NUMA is disabled?
Apart from that nit: LGTM.
> + lcore_conf[lcore_id] = rte_zmalloc_socket(NULL, sizeof(struct lcore_conf),
> + RTE_CACHE_LINE_SIZE, socketid);
> + if (lcore_conf[lcore_id] == NULL) {
> + deinit_lcore_conf();
> + return -ENOMEM;
> + }
> + }
> +
> + return 0;
> +}
> +
> int
> init_mem(uint16_t portid, unsigned int nb_mbuf)
> {
> @@ -1188,7 +1223,7 @@ init_mem(uint16_t portid, unsigned int nb_mbuf)
> }
> #endif
>
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
> qconf->ipv4_lookup_struct =
> l3fwd_lkp.get_ipv4_lookup_struct(socketid);
> qconf->ipv6_lookup_struct =
> @@ -1489,7 +1524,7 @@ l3fwd_poll_resource_setup(void)
> "rte_eth_tx_queue_setup: err=%d, "
> "port=%d\n", ret, portid);
>
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
> qconf->tx_queue_id[portid] = queueid;
> queueid++;
>
> @@ -1502,7 +1537,7 @@ l3fwd_poll_resource_setup(void)
> for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> if (rte_lcore_is_enabled(lcore_id) == 0)
> continue;
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
> printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
> fflush(stdout);
> /* init RX queues */
> @@ -1683,6 +1718,10 @@ main(int argc, char **argv)
> if (ret < 0)
> rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n");
>
> + ret = init_lcore_conf();
> + if (ret)
> + rte_exit(EXIT_FAILURE, "Init lcore conf failed!\n");
> +
> RTE_LOG(INFO, L3FWD, "Using Rx burst %u Tx burst %u\n", rx_burst_size, tx_burst_size);
>
> /* Setup function pointers for lookup method. */
> @@ -1746,7 +1785,7 @@ main(int argc, char **argv)
> for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> if (rte_lcore_is_enabled(lcore_id) == 0)
> continue;
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
> for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
> portid = qconf->rx_queue_list[queue].port_id;
> queueid = qconf->rx_queue_list[queue].queue_id;
> @@ -1810,6 +1849,8 @@ main(int argc, char **argv)
> /* clean up config file routes */
> l3fwd_lkp.free_routes();
>
> + deinit_lcore_conf();
> +
> /* clean up the EAL */
> rte_eal_cleanup();
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v4] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-06-23 3:23 [PATCH] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf Chengwen Feng
` (2 preceding siblings ...)
2026-10-29 2:54 ` [PATCH v3] " Chengwen Feng
@ 2026-10-29 12:09 ` Chengwen Feng
2026-10-30 10:48 ` Konstantin Ananyev
3 siblings, 1 reply; 16+ messages in thread
From: Chengwen Feng @ 2026-10-29 12:09 UTC (permalink / raw)
To: thomas, stephen; +Cc: dev
Currently lcore_conf is a global static array. When multiple
ports/lcores are distributed across different NUMA nodes, datapath
suffers from severe cross-NUMA memory access penalty.
On Kunpeng platform, sizeof(struct lcore_conf) reaches 133760 bytes.
Such a huge per-lcore structure significantly amplifies cross-NUMA
overhead in multi-port multi-NUMA deployment scenarios.
This commit refactors lcore_conf to pointer array and implements
per-lcore NUMA-aware hugepage allocation, allocating each lcore's
configuration on its local socket to eliminate remote memory access.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
v4: Use SOCKET_ID_AN instead of zero when NUMA is disabled
v3: Fix Thomas' comment: support no-numa parameter
Deinit lcore_conf on exit
v2: Fix AI review comment: add cleanup when init lcore-conf fail
---
examples/l3fwd/l3fwd.h | 2 +-
examples/l3fwd/l3fwd_acl.c | 2 +-
examples/l3fwd/l3fwd_em.c | 8 +++---
examples/l3fwd/l3fwd_fib.c | 6 ++--
examples/l3fwd/l3fwd_lpm.c | 8 +++---
examples/l3fwd/main.c | 59 ++++++++++++++++++++++++++++++++------
6 files changed, 63 insertions(+), 22 deletions(-)
diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 349fc37c79..20c457ee7f 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -104,7 +104,7 @@ extern uint32_t hash_entry_number;
extern xmm_t val_eth[RTE_MAX_ETHPORTS];
-extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+extern struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
extern struct parm_cfg parm_config;
diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
index 4ee3411d2a..0605f6feb9 100644
--- a/examples/l3fwd/l3fwd_acl.c
+++ b/examples/l3fwd/l3fwd_acl.c
@@ -1090,7 +1090,7 @@ acl_main_loop(__rte_unused void *dummy)
prev_tsc = 0;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
socketid = rte_lcore_to_socket_id(lcore_id);
if (qconf->n_rx_queue == 0) {
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index d8748a0edd..9aa23759d3 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -597,7 +597,7 @@ em_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -685,7 +685,7 @@ em_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
while (!force_quit) {
@@ -747,7 +747,7 @@ em_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -877,7 +877,7 @@ em_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
if (dst_ports == NULL)
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
index 4fc6bf90d5..32c8847130 100644
--- a/examples/l3fwd/l3fwd_fib.c
+++ b/examples/l3fwd/l3fwd_fib.c
@@ -192,7 +192,7 @@ fib_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -282,7 +282,7 @@ fib_event_loop(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -446,7 +446,7 @@ fib_process_event_vector(struct rte_event_vector *vec, uint8_t *type_arr,
uint16_t nh;
int i;
- lconf = &lcore_conf[rte_lcore_id()];
+ lconf = lcore_conf[rte_lcore_id()];
/* Reset counters. */
ipv4_cnt = 0;
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index 2d2651e750..77701503f7 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -154,7 +154,7 @@ lpm_main_loop(__rte_unused void *dummy)
US_PER_S * BURST_TX_DRAIN_US;
lcore_id = rte_lcore_id();
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
const uint16_t n_rx_q = qconf->n_rx_queue;
const uint16_t n_tx_p = qconf->n_tx_port;
@@ -270,7 +270,7 @@ lpm_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
while (!force_quit) {
@@ -324,7 +324,7 @@ lpm_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
@@ -468,7 +468,7 @@ lpm_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
return;
lcore_id = rte_lcore_id();
- lconf = &lcore_conf[lcore_id];
+ lconf = lcore_conf[lcore_id];
dst_port_list =
rte_zmalloc("", sizeof(uint16_t) * evt_rsrc->vector_size,
RTE_CACHE_LINE_SIZE);
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index 6866811526..626f187454 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -95,7 +95,7 @@ uint32_t enabled_port_mask;
/* Used only in exact match mode. */
bool ipv6_enabled; /**< ipv6 is false by default. */
-struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
struct parm_cfg parm_config;
@@ -367,17 +367,17 @@ init_lcore_rx_queues(void)
for (i = 0; i < nb_lcore_params; ++i) {
lcore = lcore_params[i].lcore_id;
- nb_rx_queue = lcore_conf[lcore].n_rx_queue;
+ nb_rx_queue = lcore_conf[lcore]->n_rx_queue;
if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
printf("error: too many queues (%u) for lcore: %u\n",
(unsigned int)nb_rx_queue + 1, lcore);
return -1;
} else {
- lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
+ lcore_conf[lcore]->rx_queue_list[nb_rx_queue].port_id =
lcore_params[i].port_id;
- lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
+ lcore_conf[lcore]->rx_queue_list[nb_rx_queue].queue_id =
lcore_params[i].queue_id;
- lcore_conf[lcore].n_rx_queue++;
+ lcore_conf[lcore]->n_rx_queue++;
}
}
return 0;
@@ -1117,6 +1117,41 @@ print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
printf("%s%s", name, buf);
}
+static void
+deinit_lcore_conf(void)
+{
+ unsigned int lcore_id;
+
+ for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+ if (lcore_conf[lcore_id] != NULL) {
+ rte_free(lcore_conf[lcore_id]);
+ lcore_conf[lcore_id] = NULL;
+ }
+ }
+}
+
+static int
+init_lcore_conf(void)
+{
+ unsigned int lcore_id;
+ int socketid;
+
+ for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+ if (rte_lcore_is_enabled(lcore_id) == 0)
+ continue;
+
+ socketid = numa_on ? (int)rte_lcore_to_socket_id(lcore_id) : SOCKET_ID_ANY;
+ lcore_conf[lcore_id] = rte_zmalloc_socket(NULL, sizeof(struct lcore_conf),
+ RTE_CACHE_LINE_SIZE, socketid);
+ if (lcore_conf[lcore_id] == NULL) {
+ deinit_lcore_conf();
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
int
init_mem(uint16_t portid, unsigned int nb_mbuf)
{
@@ -1188,7 +1223,7 @@ init_mem(uint16_t portid, unsigned int nb_mbuf)
}
#endif
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
qconf->ipv4_lookup_struct =
l3fwd_lkp.get_ipv4_lookup_struct(socketid);
qconf->ipv6_lookup_struct =
@@ -1489,7 +1524,7 @@ l3fwd_poll_resource_setup(void)
"rte_eth_tx_queue_setup: err=%d, "
"port=%d\n", ret, portid);
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
qconf->tx_queue_id[portid] = queueid;
queueid++;
@@ -1502,7 +1537,7 @@ l3fwd_poll_resource_setup(void)
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
if (rte_lcore_is_enabled(lcore_id) == 0)
continue;
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
fflush(stdout);
/* init RX queues */
@@ -1683,6 +1718,10 @@ main(int argc, char **argv)
if (ret < 0)
rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n");
+ ret = init_lcore_conf();
+ if (ret)
+ rte_exit(EXIT_FAILURE, "Init lcore conf failed!\n");
+
RTE_LOG(INFO, L3FWD, "Using Rx burst %u Tx burst %u\n", rx_burst_size, tx_burst_size);
/* Setup function pointers for lookup method. */
@@ -1746,7 +1785,7 @@ main(int argc, char **argv)
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
if (rte_lcore_is_enabled(lcore_id) == 0)
continue;
- qconf = &lcore_conf[lcore_id];
+ qconf = lcore_conf[lcore_id];
for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
portid = qconf->rx_queue_list[queue].port_id;
queueid = qconf->rx_queue_list[queue].queue_id;
@@ -1810,6 +1849,8 @@ main(int argc, char **argv)
/* clean up config file routes */
l3fwd_lkp.free_routes();
+ deinit_lcore_conf();
+
/* clean up the EAL */
rte_eal_cleanup();
--
2.17.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v3] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-10-29 9:19 ` Konstantin Ananyev
@ 2026-10-29 12:12 ` fengchengwen
2026-10-30 10:52 ` Konstantin Ananyev
0 siblings, 1 reply; 16+ messages in thread
From: fengchengwen @ 2026-10-29 12:12 UTC (permalink / raw)
To: Konstantin Ananyev, thomas, stephen; +Cc: dev
On 10/29/2026 5:19 PM, Konstantin Ananyev wrote:
>
>
>
>> Currently lcore_conf is a global static array. When multiple
>> ports/lcores are distributed across different NUMA nodes, datapath
>> suffers from severe cross-NUMA memory access penalty.
>>
>> On Kunpeng platform, sizeof(struct lcore_conf) reaches 133760 bytes.
>> Such a huge per-lcore structure significantly amplifies cross-NUMA
>> overhead in multi-port multi-NUMA deployment scenarios.
>>
>> This commit refactors lcore_conf to pointer array and implements
>> per-lcore NUMA-aware hugepage allocation, allocating each lcore's
>> configuration on its local socket to eliminate remote memory access.
>>
>> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
>>
>> ---
>> v3: Fix Thomas' comment: support no-numa parameter
>> Deinit lcore_conf on exit
>> v2: Fix AI review comment: add cleanup when init lcore-conf fail
>>
>> ---
>> examples/l3fwd/l3fwd.h | 2 +-
>> examples/l3fwd/l3fwd_acl.c | 2 +-
>> examples/l3fwd/l3fwd_em.c | 8 +++---
>> examples/l3fwd/l3fwd_fib.c | 6 ++--
>> examples/l3fwd/l3fwd_lpm.c | 8 +++---
>> examples/l3fwd/main.c | 59 ++++++++++++++++++++++++++++++++------
>> 6 files changed, 63 insertions(+), 22 deletions(-)
>>
>> diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
>> index 349fc37c79..20c457ee7f 100644
>> --- a/examples/l3fwd/l3fwd.h
>> +++ b/examples/l3fwd/l3fwd.h
>> @@ -104,7 +104,7 @@ extern uint32_t hash_entry_number;
>> extern xmm_t val_eth[RTE_MAX_ETHPORTS];
>> -extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
>> +extern struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
>> extern struct parm_cfg parm_config;
>> diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
>> index 4ee3411d2a..0605f6feb9 100644
>> --- a/examples/l3fwd/l3fwd_acl.c
>> +++ b/examples/l3fwd/l3fwd_acl.c
>> @@ -1090,7 +1090,7 @@ acl_main_loop(__rte_unused void *dummy)
>> prev_tsc = 0;
>> lcore_id = rte_lcore_id();
>> - qconf = &lcore_conf[lcore_id];
>> + qconf = lcore_conf[lcore_id];
>> socketid = rte_lcore_to_socket_id(lcore_id);
>> if (qconf->n_rx_queue == 0) {
>> diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
>> index d8748a0edd..9aa23759d3 100644
>> --- a/examples/l3fwd/l3fwd_em.c
>> +++ b/examples/l3fwd/l3fwd_em.c
>> @@ -597,7 +597,7 @@ em_main_loop(__rte_unused void *dummy)
>> US_PER_S * BURST_TX_DRAIN_US;
>> lcore_id = rte_lcore_id();
>> - qconf = &lcore_conf[lcore_id];
>> + qconf = lcore_conf[lcore_id];
>> const uint16_t n_rx_q = qconf->n_rx_queue;
>> const uint16_t n_tx_p = qconf->n_tx_port;
>> @@ -685,7 +685,7 @@ em_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
>> return;
>> lcore_id = rte_lcore_id();
>> - lconf = &lcore_conf[lcore_id];
>> + lconf = lcore_conf[lcore_id];
>> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>> while (!force_quit) {
>> @@ -747,7 +747,7 @@ em_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
>> lcore_id = rte_lcore_id();
>> - lconf = &lcore_conf[lcore_id];
>> + lconf = lcore_conf[lcore_id];
>> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>> @@ -877,7 +877,7 @@ em_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
>> if (dst_ports == NULL)
>> return;
>> lcore_id = rte_lcore_id();
>> - lconf = &lcore_conf[lcore_id];
>> + lconf = lcore_conf[lcore_id];
>> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>> diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
>> index 4fc6bf90d5..32c8847130 100644
>> --- a/examples/l3fwd/l3fwd_fib.c
>> +++ b/examples/l3fwd/l3fwd_fib.c
>> @@ -192,7 +192,7 @@ fib_main_loop(__rte_unused void *dummy)
>> US_PER_S * BURST_TX_DRAIN_US;
>> lcore_id = rte_lcore_id();
>> - qconf = &lcore_conf[lcore_id];
>> + qconf = lcore_conf[lcore_id];
>> const uint16_t n_rx_q = qconf->n_rx_queue;
>> const uint16_t n_tx_p = qconf->n_tx_port;
>> @@ -282,7 +282,7 @@ fib_event_loop(struct l3fwd_event_resources *evt_rsrc,
>> lcore_id = rte_lcore_id();
>> - lconf = &lcore_conf[lcore_id];
>> + lconf = lcore_conf[lcore_id];
>> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>> @@ -446,7 +446,7 @@ fib_process_event_vector(struct rte_event_vector *vec, uint8_t *type_arr,
>> uint16_t nh;
>> int i;
>> - lconf = &lcore_conf[rte_lcore_id()];
>> + lconf = lcore_conf[rte_lcore_id()];
>> /* Reset counters. */
>> ipv4_cnt = 0;
>> diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
>> index 2d2651e750..77701503f7 100644
>> --- a/examples/l3fwd/l3fwd_lpm.c
>> +++ b/examples/l3fwd/l3fwd_lpm.c
>> @@ -154,7 +154,7 @@ lpm_main_loop(__rte_unused void *dummy)
>> US_PER_S * BURST_TX_DRAIN_US;
>> lcore_id = rte_lcore_id();
>> - qconf = &lcore_conf[lcore_id];
>> + qconf = lcore_conf[lcore_id];
>> const uint16_t n_rx_q = qconf->n_rx_queue;
>> const uint16_t n_tx_p = qconf->n_tx_port;
>> @@ -270,7 +270,7 @@ lpm_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
>> return;
>> lcore_id = rte_lcore_id();
>> - lconf = &lcore_conf[lcore_id];
>> + lconf = lcore_conf[lcore_id];
>> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>> while (!force_quit) {
>> @@ -324,7 +324,7 @@ lpm_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
>> lcore_id = rte_lcore_id();
>> - lconf = &lcore_conf[lcore_id];
>> + lconf = lcore_conf[lcore_id];
>> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>> @@ -468,7 +468,7 @@ lpm_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
>> return;
>> lcore_id = rte_lcore_id();
>> - lconf = &lcore_conf[lcore_id];
>> + lconf = lcore_conf[lcore_id];
>> dst_port_list =
>> rte_zmalloc("", sizeof(uint16_t) * evt_rsrc->vector_size,
>> RTE_CACHE_LINE_SIZE);
>> diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
>> index 6866811526..4db565e515 100644
>> --- a/examples/l3fwd/main.c
>> +++ b/examples/l3fwd/main.c
>> @@ -95,7 +95,7 @@ uint32_t enabled_port_mask;
>> /* Used only in exact match mode. */
>> bool ipv6_enabled; /**< ipv6 is false by default. */
>> -struct lcore_conf lcore_conf[RTE_MAX_LCORE];
>> +struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
>> struct parm_cfg parm_config;
>> @@ -367,17 +367,17 @@ init_lcore_rx_queues(void)
>> for (i = 0; i < nb_lcore_params; ++i) {
>> lcore = lcore_params[i].lcore_id;
>> - nb_rx_queue = lcore_conf[lcore].n_rx_queue;
>> + nb_rx_queue = lcore_conf[lcore]->n_rx_queue;
>> if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
>> printf("error: too many queues (%u) for lcore: %u\n",
>> (unsigned int)nb_rx_queue + 1, lcore);
>> return -1;
>> } else {
>> - lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
>> + lcore_conf[lcore]->rx_queue_list[nb_rx_queue].port_id =
>> lcore_params[i].port_id;
>> - lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
>> + lcore_conf[lcore]->rx_queue_list[nb_rx_queue].queue_id =
>> lcore_params[i].queue_id;
>> - lcore_conf[lcore].n_rx_queue++;
>> + lcore_conf[lcore]->n_rx_queue++;
>> }
>> }
>> return 0;
>> @@ -1117,6 +1117,41 @@ print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
>> printf("%s%s", name, buf);
>> }
>> +static void
>> +deinit_lcore_conf(void)
>> +{
>> + unsigned int lcore_id;
>> +
>> + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
>> + if (lcore_conf[lcore_id] != NULL) {
>> + rte_free(lcore_conf[lcore_id]);
>> + lcore_conf[lcore_id] = NULL;
>> + }
>> + }
>> +}
>> +
>> +static int
>> +init_lcore_conf(void)
>> +{
>> + unsigned int lcore_id;
>> + int socketid;
>> +
>> + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
>> + if (rte_lcore_is_enabled(lcore_id) == 0)
>> + continue;
>> +
>> + socketid = numa_on ? rte_lcore_to_socket_id(lcore_id) : 0;
>
> Shouldn't it be SOCKET_ID_ANY (-1) instead of zero, when NUMA is disabled?
OK, already fix in v4
BTW: SOCKET_ID_ANY seemed need redefined as RTE_SOCKET_ID_ANY, If there is no
objection, I will make the changes in the next independent patch.
Thanks,
Chengwen
> Apart from that nit: LGTM.
>> + lcore_conf[lcore_id] = rte_zmalloc_socket(NULL, sizeof(struct lcore_conf),
>> + RTE_CACHE_LINE_SIZE, socketid);
>> + if (lcore_conf[lcore_id] == NULL) {
>> + deinit_lcore_conf();
>> + return -ENOMEM;
>> + }
>> + }
>> +
>> + return 0;
>> +}
>> +
>> int
>> init_mem(uint16_t portid, unsigned int nb_mbuf)
>> {
>> @@ -1188,7 +1223,7 @@ init_mem(uint16_t portid, unsigned int nb_mbuf)
>> }
>> #endif
>> - qconf = &lcore_conf[lcore_id];
>> + qconf = lcore_conf[lcore_id];
>> qconf->ipv4_lookup_struct =
>> l3fwd_lkp.get_ipv4_lookup_struct(socketid);
>> qconf->ipv6_lookup_struct =
>> @@ -1489,7 +1524,7 @@ l3fwd_poll_resource_setup(void)
>> "rte_eth_tx_queue_setup: err=%d, "
>> "port=%d\n", ret, portid);
>> - qconf = &lcore_conf[lcore_id];
>> + qconf = lcore_conf[lcore_id];
>> qconf->tx_queue_id[portid] = queueid;
>> queueid++;
>> @@ -1502,7 +1537,7 @@ l3fwd_poll_resource_setup(void)
>> for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
>> if (rte_lcore_is_enabled(lcore_id) == 0)
>> continue;
>> - qconf = &lcore_conf[lcore_id];
>> + qconf = lcore_conf[lcore_id];
>> printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
>> fflush(stdout);
>> /* init RX queues */
>> @@ -1683,6 +1718,10 @@ main(int argc, char **argv)
>> if (ret < 0)
>> rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n");
>> + ret = init_lcore_conf();
>> + if (ret)
>> + rte_exit(EXIT_FAILURE, "Init lcore conf failed!\n");
>> +
>> RTE_LOG(INFO, L3FWD, "Using Rx burst %u Tx burst %u\n", rx_burst_size, tx_burst_size);
>> /* Setup function pointers for lookup method. */
>> @@ -1746,7 +1785,7 @@ main(int argc, char **argv)
>> for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
>> if (rte_lcore_is_enabled(lcore_id) == 0)
>> continue;
>> - qconf = &lcore_conf[lcore_id];
>> + qconf = lcore_conf[lcore_id];
>> for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
>> portid = qconf->rx_queue_list[queue].port_id;
>> queueid = qconf->rx_queue_list[queue].queue_id;
>> @@ -1810,6 +1849,8 @@ main(int argc, char **argv)
>> /* clean up config file routes */
>> l3fwd_lkp.free_routes();
>> + deinit_lcore_conf();
>> +
>> /* clean up the EAL */
>> rte_eal_cleanup();
>>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-10-29 12:09 ` [PATCH v4] " Chengwen Feng
@ 2026-10-30 10:48 ` Konstantin Ananyev
0 siblings, 0 replies; 16+ messages in thread
From: Konstantin Ananyev @ 2026-10-30 10:48 UTC (permalink / raw)
To: Chengwen Feng, thomas, stephen; +Cc: dev
> Currently lcore_conf is a global static array. When multiple
> ports/lcores are distributed across different NUMA nodes, datapath
> suffers from severe cross-NUMA memory access penalty.
>
> On Kunpeng platform, sizeof(struct lcore_conf) reaches 133760 bytes.
> Such a huge per-lcore structure significantly amplifies cross-NUMA
> overhead in multi-port multi-NUMA deployment scenarios.
>
> This commit refactors lcore_conf to pointer array and implements
> per-lcore NUMA-aware hugepage allocation, allocating each lcore's
> configuration on its local socket to eliminate remote memory access.
>
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> ---
> v4: Use SOCKET_ID_AN instead of zero when NUMA is disabled
> v3: Fix Thomas' comment: support no-numa parameter
> Deinit lcore_conf on exit
> v2: Fix AI review comment: add cleanup when init lcore-conf fail
>
> ---
> examples/l3fwd/l3fwd.h | 2 +-
> examples/l3fwd/l3fwd_acl.c | 2 +-
> examples/l3fwd/l3fwd_em.c | 8 +++---
> examples/l3fwd/l3fwd_fib.c | 6 ++--
> examples/l3fwd/l3fwd_lpm.c | 8 +++---
> examples/l3fwd/main.c | 59 ++++++++++++++++++++++++++++++++------
> 6 files changed, 63 insertions(+), 22 deletions(-)
>
> diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
> index 349fc37c79..20c457ee7f 100644
> --- a/examples/l3fwd/l3fwd.h
> +++ b/examples/l3fwd/l3fwd.h
> @@ -104,7 +104,7 @@ extern uint32_t hash_entry_number;
>
> extern xmm_t val_eth[RTE_MAX_ETHPORTS];
>
> -extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
> +extern struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
>
> extern struct parm_cfg parm_config;
>
> diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
> index 4ee3411d2a..0605f6feb9 100644
> --- a/examples/l3fwd/l3fwd_acl.c
> +++ b/examples/l3fwd/l3fwd_acl.c
> @@ -1090,7 +1090,7 @@ acl_main_loop(__rte_unused void *dummy)
>
> prev_tsc = 0;
> lcore_id = rte_lcore_id();
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
> socketid = rte_lcore_to_socket_id(lcore_id);
>
> if (qconf->n_rx_queue == 0) {
> diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
> index d8748a0edd..9aa23759d3 100644
> --- a/examples/l3fwd/l3fwd_em.c
> +++ b/examples/l3fwd/l3fwd_em.c
> @@ -597,7 +597,7 @@ em_main_loop(__rte_unused void *dummy)
> US_PER_S * BURST_TX_DRAIN_US;
>
> lcore_id = rte_lcore_id();
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
>
> const uint16_t n_rx_q = qconf->n_rx_queue;
> const uint16_t n_tx_p = qconf->n_tx_port;
> @@ -685,7 +685,7 @@ em_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
> return;
>
> lcore_id = rte_lcore_id();
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
>
> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
> while (!force_quit) {
> @@ -747,7 +747,7 @@ em_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
>
> lcore_id = rte_lcore_id();
>
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
>
> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>
> @@ -877,7 +877,7 @@ em_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
> if (dst_ports == NULL)
> return;
> lcore_id = rte_lcore_id();
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
>
> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>
> diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
> index 4fc6bf90d5..32c8847130 100644
> --- a/examples/l3fwd/l3fwd_fib.c
> +++ b/examples/l3fwd/l3fwd_fib.c
> @@ -192,7 +192,7 @@ fib_main_loop(__rte_unused void *dummy)
> US_PER_S * BURST_TX_DRAIN_US;
>
> lcore_id = rte_lcore_id();
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
>
> const uint16_t n_rx_q = qconf->n_rx_queue;
> const uint16_t n_tx_p = qconf->n_tx_port;
> @@ -282,7 +282,7 @@ fib_event_loop(struct l3fwd_event_resources *evt_rsrc,
>
> lcore_id = rte_lcore_id();
>
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
>
> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>
> @@ -446,7 +446,7 @@ fib_process_event_vector(struct rte_event_vector *vec, uint8_t *type_arr,
> uint16_t nh;
> int i;
>
> - lconf = &lcore_conf[rte_lcore_id()];
> + lconf = lcore_conf[rte_lcore_id()];
>
> /* Reset counters. */
> ipv4_cnt = 0;
> diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
> index 2d2651e750..77701503f7 100644
> --- a/examples/l3fwd/l3fwd_lpm.c
> +++ b/examples/l3fwd/l3fwd_lpm.c
> @@ -154,7 +154,7 @@ lpm_main_loop(__rte_unused void *dummy)
> US_PER_S * BURST_TX_DRAIN_US;
>
> lcore_id = rte_lcore_id();
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
>
> const uint16_t n_rx_q = qconf->n_rx_queue;
> const uint16_t n_tx_p = qconf->n_tx_port;
> @@ -270,7 +270,7 @@ lpm_event_loop_single(struct l3fwd_event_resources *evt_rsrc,
> return;
>
> lcore_id = rte_lcore_id();
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
>
> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
> while (!force_quit) {
> @@ -324,7 +324,7 @@ lpm_event_loop_burst(struct l3fwd_event_resources *evt_rsrc,
>
> lcore_id = rte_lcore_id();
>
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
>
> RTE_LOG(INFO, L3FWD, "entering %s on lcore %u\n", __func__, lcore_id);
>
> @@ -468,7 +468,7 @@ lpm_event_loop_vector(struct l3fwd_event_resources *evt_rsrc,
> return;
>
> lcore_id = rte_lcore_id();
> - lconf = &lcore_conf[lcore_id];
> + lconf = lcore_conf[lcore_id];
> dst_port_list =
> rte_zmalloc("", sizeof(uint16_t) * evt_rsrc->vector_size,
> RTE_CACHE_LINE_SIZE);
> diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
> index 6866811526..626f187454 100644
> --- a/examples/l3fwd/main.c
> +++ b/examples/l3fwd/main.c
> @@ -95,7 +95,7 @@ uint32_t enabled_port_mask;
> /* Used only in exact match mode. */
> bool ipv6_enabled; /**< ipv6 is false by default. */
>
> -struct lcore_conf lcore_conf[RTE_MAX_LCORE];
> +struct lcore_conf *lcore_conf[RTE_MAX_LCORE];
>
> struct parm_cfg parm_config;
>
> @@ -367,17 +367,17 @@ init_lcore_rx_queues(void)
>
> for (i = 0; i < nb_lcore_params; ++i) {
> lcore = lcore_params[i].lcore_id;
> - nb_rx_queue = lcore_conf[lcore].n_rx_queue;
> + nb_rx_queue = lcore_conf[lcore]->n_rx_queue;
> if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
> printf("error: too many queues (%u) for lcore: %u\n",
> (unsigned int)nb_rx_queue + 1, lcore);
> return -1;
> } else {
> - lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
> + lcore_conf[lcore]->rx_queue_list[nb_rx_queue].port_id =
> lcore_params[i].port_id;
> - lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
> + lcore_conf[lcore]->rx_queue_list[nb_rx_queue].queue_id =
> lcore_params[i].queue_id;
> - lcore_conf[lcore].n_rx_queue++;
> + lcore_conf[lcore]->n_rx_queue++;
> }
> }
> return 0;
> @@ -1117,6 +1117,41 @@ print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
> printf("%s%s", name, buf);
> }
>
> +static void
> +deinit_lcore_conf(void)
> +{
> + unsigned int lcore_id;
> +
> + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> + if (lcore_conf[lcore_id] != NULL) {
> + rte_free(lcore_conf[lcore_id]);
> + lcore_conf[lcore_id] = NULL;
> + }
> + }
> +}
> +
> +static int
> +init_lcore_conf(void)
> +{
> + unsigned int lcore_id;
> + int socketid;
> +
> + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> + if (rte_lcore_is_enabled(lcore_id) == 0)
> + continue;
> +
> + socketid = numa_on ? (int)rte_lcore_to_socket_id(lcore_id) : SOCKET_ID_ANY;
> + lcore_conf[lcore_id] = rte_zmalloc_socket(NULL, sizeof(struct lcore_conf),
> + RTE_CACHE_LINE_SIZE, socketid);
> + if (lcore_conf[lcore_id] == NULL) {
> + deinit_lcore_conf();
> + return -ENOMEM;
> + }
> + }
> +
> + return 0;
> +}
> +
> int
> init_mem(uint16_t portid, unsigned int nb_mbuf)
> {
> @@ -1188,7 +1223,7 @@ init_mem(uint16_t portid, unsigned int nb_mbuf)
> }
> #endif
>
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
> qconf->ipv4_lookup_struct =
> l3fwd_lkp.get_ipv4_lookup_struct(socketid);
> qconf->ipv6_lookup_struct =
> @@ -1489,7 +1524,7 @@ l3fwd_poll_resource_setup(void)
> "rte_eth_tx_queue_setup: err=%d, "
> "port=%d\n", ret, portid);
>
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
> qconf->tx_queue_id[portid] = queueid;
> queueid++;
>
> @@ -1502,7 +1537,7 @@ l3fwd_poll_resource_setup(void)
> for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> if (rte_lcore_is_enabled(lcore_id) == 0)
> continue;
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
> printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
> fflush(stdout);
> /* init RX queues */
> @@ -1683,6 +1718,10 @@ main(int argc, char **argv)
> if (ret < 0)
> rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n");
>
> + ret = init_lcore_conf();
> + if (ret)
> + rte_exit(EXIT_FAILURE, "Init lcore conf failed!\n");
> +
> RTE_LOG(INFO, L3FWD, "Using Rx burst %u Tx burst %u\n", rx_burst_size, tx_burst_size);
>
> /* Setup function pointers for lookup method. */
> @@ -1746,7 +1785,7 @@ main(int argc, char **argv)
> for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> if (rte_lcore_is_enabled(lcore_id) == 0)
> continue;
> - qconf = &lcore_conf[lcore_id];
> + qconf = lcore_conf[lcore_id];
> for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
> portid = qconf->rx_queue_list[queue].port_id;
> queueid = qconf->rx_queue_list[queue].queue_id;
> @@ -1810,6 +1849,8 @@ main(int argc, char **argv)
> /* clean up config file routes */
> l3fwd_lkp.free_routes();
>
> + deinit_lcore_conf();
> +
> /* clean up the EAL */
> rte_eal_cleanup();
>
Acked-by: Konstantin Ananyev <konstantin.v.ananyev@yandex.ru>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v3] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-10-29 12:12 ` fengchengwen
@ 2026-10-30 10:52 ` Konstantin Ananyev
2026-10-30 12:59 ` Thomas Monjalon
0 siblings, 1 reply; 16+ messages in thread
From: Konstantin Ananyev @ 2026-10-30 10:52 UTC (permalink / raw)
To: fengchengwen, thomas, stephen; +Cc: dev
> On 10/29/2026 5:19 PM, Konstantin Ananyev wrote:
>>
>>
>>> Currently lcore_conf is a global static array. When multiple
>>> ports/lcores are distributed across different NUMA nodes, datapath
>>> suffers from severe cross-NUMA memory access penalty.
>>>
>>> On Kunpeng platform, sizeof(struct lcore_conf) reaches 133760 bytes.
>>> Such a huge per-lcore structure significantly amplifies cross-NUMA
>>> overhead in multi-port multi-NUMA deployment scenarios.
>>>
>>> This commit refactors lcore_conf to pointer array and implements
>>> per-lcore NUMA-aware hugepage allocation, allocating each lcore's
>>> configuration on its local socket to eliminate remote memory access.
>>>
>>> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
>>>
>>> ---
...
>>> +}
>>> +
>>> +static int
>>> +init_lcore_conf(void)
>>> +{
>>> + unsigned int lcore_id;
>>> + int socketid;
>>> +
>>> + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
>>> + if (rte_lcore_is_enabled(lcore_id) == 0)
>>> + continue;
>>> +
>>> + socketid = numa_on ? rte_lcore_to_socket_id(lcore_id) : 0;
>> Shouldn't it be SOCKET_ID_ANY (-1) instead of zero, when NUMA is disabled?
> OK, already fix in v4
>
> BTW: SOCKET_ID_ANY seemed need redefined as RTE_SOCKET_ID_ANY, If there is no
> objection, I will make the changes in the next independent patch.
Yep, that's probably a good thing to do, and probably 26.11 is a good
time to do that.
BTW, I think it was discussed before, but I can't remember why we didn't
fix it so far.
Konstantin
>
> Thanks,
> Chengwen
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v3] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-10-30 10:52 ` Konstantin Ananyev
@ 2026-10-30 12:59 ` Thomas Monjalon
2026-10-31 1:37 ` fengchengwen
0 siblings, 1 reply; 16+ messages in thread
From: Thomas Monjalon @ 2026-10-30 12:59 UTC (permalink / raw)
To: fengchengwen, Konstantin Ananyev; +Cc: stephen, dev
30/10/2026 11:52, Konstantin Ananyev:
> > BTW: SOCKET_ID_ANY seemed need redefined as RTE_SOCKET_ID_ANY, If there is no
> > objection, I will make the changes in the next independent patch.
>
> Yep, that's probably a good thing to do, and probably 26.11 is a good
> time to do that.
> BTW, I think it was discussed before, but I can't remember why we didn't
> fix it so far.
1/ We should not break this API easily as it is used a lot.
If we do the change we need to keep the compat alias for some time.
2/ The term SOCKET is used in many places instead of NUMA.
There is a confusion between CPU and memory which deserves to be fixed.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v3] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-10-30 12:59 ` Thomas Monjalon
@ 2026-10-31 1:37 ` fengchengwen
2026-11-01 14:26 ` Thomas Monjalon
0 siblings, 1 reply; 16+ messages in thread
From: fengchengwen @ 2026-10-31 1:37 UTC (permalink / raw)
To: Thomas Monjalon, Konstantin Ananyev; +Cc: stephen, dev
On 10/30/2026 8:59 PM, Thomas Monjalon wrote:
> 30/10/2026 11:52, Konstantin Ananyev:
>>> BTW: SOCKET_ID_ANY seemed need redefined as RTE_SOCKET_ID_ANY, If there is no
>>> objection, I will make the changes in the next independent patch.
>>
>> Yep, that's probably a good thing to do, and probably 26.11 is a good
>> time to do that.
>> BTW, I think it was discussed before, but I can't remember why we didn't
>> fix it so far.
>
> 1/ We should not break this API easily as it is used a lot.
> If we do the change we need to keep the compat alias for some time.
>
> 2/ The term SOCKET is used in many places instead of NUMA.
> There is a confusion between CPU and memory which deserves to be fixed.
>
My plan is a 3-patch series:
1. Add RTE_SOCKET_ID_ANY and deprecation documentation.
2. Replace all in-tree SOCKET_ID_ANY with RTE_SOCKET_ID_ANY.
3. Mark SOCKET_ID_ANY with RTE_DEPRECATED after in-tree migration completes,
so warnings only hit out-of-tree users while DPDK itself builds cleanly.
I will avoid the bigger "socket -> NUMA node" terminology refactor in this series.
That would be a massive semantic change with large churn, better handled via separate
RFC later.
If this plan looks acceptable, I will start the implementation.
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v3] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-10-31 1:37 ` fengchengwen
@ 2026-11-01 14:26 ` Thomas Monjalon
2026-11-02 4:13 ` fengchengwen
0 siblings, 1 reply; 16+ messages in thread
From: Thomas Monjalon @ 2026-11-01 14:26 UTC (permalink / raw)
To: Konstantin Ananyev, fengchengwen; +Cc: stephen, dev
31/10/2026 02:37, fengchengwen:
> On 10/30/2026 8:59 PM, Thomas Monjalon wrote:
> > 30/10/2026 11:52, Konstantin Ananyev:
> >>> BTW: SOCKET_ID_ANY seemed need redefined as RTE_SOCKET_ID_ANY, If there is no
> >>> objection, I will make the changes in the next independent patch.
> >>
> >> Yep, that's probably a good thing to do, and probably 26.11 is a good
> >> time to do that.
> >> BTW, I think it was discussed before, but I can't remember why we didn't
> >> fix it so far.
> >
> > 1/ We should not break this API easily as it is used a lot.
> > If we do the change we need to keep the compat alias for some time.
> >
> > 2/ The term SOCKET is used in many places instead of NUMA.
> > There is a confusion between CPU and memory which deserves to be fixed.
> >
>
> My plan is a 3-patch series:
> 1. Add RTE_SOCKET_ID_ANY and deprecation documentation.
> 2. Replace all in-tree SOCKET_ID_ANY with RTE_SOCKET_ID_ANY.
> 3. Mark SOCKET_ID_ANY with RTE_DEPRECATED after in-tree migration completes,
> so warnings only hit out-of-tree users while DPDK itself builds cleanly.
>
> I will avoid the bigger "socket -> NUMA node" terminology refactor in this series.
> That would be a massive semantic change with large churn, better handled via separate
> RFC later.
>
> If this plan looks acceptable, I will start the implementation.
My doubt is that while introducing a new API,
it may be better to apply it in correct places with correct definition,
avoiding mixing CPU and memory.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v3] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf
2026-11-01 14:26 ` Thomas Monjalon
@ 2026-11-02 4:13 ` fengchengwen
0 siblings, 0 replies; 16+ messages in thread
From: fengchengwen @ 2026-11-02 4:13 UTC (permalink / raw)
To: Thomas Monjalon, Konstantin Ananyev; +Cc: stephen, dev
On 11/1/2026 10:26 PM, Thomas Monjalon wrote:
> 31/10/2026 02:37, fengchengwen:
>> On 10/30/2026 8:59 PM, Thomas Monjalon wrote:
>>> 30/10/2026 11:52, Konstantin Ananyev:
>>>>> BTW: SOCKET_ID_ANY seemed need redefined as RTE_SOCKET_ID_ANY, If there is no
>>>>> objection, I will make the changes in the next independent patch.
>>>>
>>>> Yep, that's probably a good thing to do, and probably 26.11 is a good
>>>> time to do that.
>>>> BTW, I think it was discussed before, but I can't remember why we didn't
>>>> fix it so far.
>>>
>>> 1/ We should not break this API easily as it is used a lot.
>>> If we do the change we need to keep the compat alias for some time.
>>>
>>> 2/ The term SOCKET is used in many places instead of NUMA.
>>> There is a confusion between CPU and memory which deserves to be fixed.
>>>
>>
>> My plan is a 3-patch series:
>> 1. Add RTE_SOCKET_ID_ANY and deprecation documentation.
>> 2. Replace all in-tree SOCKET_ID_ANY with RTE_SOCKET_ID_ANY.
>> 3. Mark SOCKET_ID_ANY with RTE_DEPRECATED after in-tree migration completes,
>> so warnings only hit out-of-tree users while DPDK itself builds cleanly.
>>
>> I will avoid the bigger "socket -> NUMA node" terminology refactor in this series.
>> That would be a massive semantic change with large churn, better handled via separate
>> RFC later.
>>
>> If this plan looks acceptable, I will start the implementation.
>
> My doubt is that while introducing a new API,
> it may be better to apply it in correct places with correct definition,
> avoiding mixing CPU and memory.
OK, I will try to implement it (avoiding mixing CPU and memory)
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-11-02 4:13 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-23 3:23 [PATCH] examples/l3fwd: dynamic NUMA-aware alloc lcore_conf Chengwen Feng
2026-06-24 3:23 ` [PATCH v2] " Chengwen Feng
2026-06-24 4:26 ` Stephen Hemminger
2026-06-24 6:05 ` fengchengwen
2026-07-12 0:36 ` Thomas Monjalon
2026-07-15 9:22 ` [PATCH] " Chengwen Feng
2026-10-29 2:54 ` [PATCH v3] " Chengwen Feng
2026-10-29 9:19 ` Konstantin Ananyev
2026-10-29 12:12 ` fengchengwen
2026-10-30 10:52 ` Konstantin Ananyev
2026-10-30 12:59 ` Thomas Monjalon
2026-10-31 1:37 ` fengchengwen
2026-11-01 14:26 ` Thomas Monjalon
2026-11-02 4:13 ` fengchengwen
2026-10-29 12:09 ` [PATCH v4] " Chengwen Feng
2026-10-30 10:48 ` Konstantin Ananyev
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox