DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [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