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 <[email protected]>
>>
>> ---
>> 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();
>>
>