<div dir="ltr">Hi David<div><br></div><div>I am not sure it works either, if the lcore are manual set with a gap: `--lcores=0,7` (from `eal_parse_lcores`):</div><div>- lcore 0 will get core_index = 0</div><div>- lcore 7 will get core_index = 1</div><div><br></div><div>When calling `rte_thread_register` we will hit lcore=1 as first not-assigned lcore and set core_index=1 as well.</div><div><br></div><div>It seems like a solution should be to have a bitmap of the currently used core_index stored in the global config.</div><div><br></div><div>Please let me know what you think about that.</div><div><br></div><div>Maxime Peim</div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Mon, Jun 8, 2026 at 6:35 PM David Marchand <<a href="mailto:david.marchand@redhat.com">david.marchand@redhat.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Mon, 8 Jun 2026 at 18:10, David Marchand <<a href="mailto:david.marchand@redhat.com" target="_blank">david.marchand@redhat.com</a>> wrote:<br>
><br>
> On Wed, 22 Apr 2026 at 09:54, Maxime Peim <<a href="mailto:maxime.peim@gmail.com" target="_blank">maxime.peim@gmail.com</a>> wrote:<br>
> ><br>
> > Threads registered via rte_thread_register() are assigned a valid<br>
> > lcore_id by eal_lcore_non_eal_allocate(), but their core_index in<br>
> > lcore_config is left at -1. This value was set during rte_eal_cpu_init()<br>
> > for lcores with ROLE_OFF (undetected CPUs) and is never updated when the<br>
> > lcore is later allocated to a non-EAL thread.<br>
> ><br>
> > As a result, rte_lcore_index() returns -1 for registered non-EAL<br>
> > threads. Libraries that use rte_lcore_index() to select per-lcore<br>
> > caches fall back to a shared global path when it returns -1, causing<br>
> > severe contention under concurrent access from multiple registered<br>
> > threads.<br>
> ><br>
> > A concrete example is the mlx5 indexed memory pool (mlx5_ipool), which<br>
> > uses rte_lcore_index() in mlx5_ipool_malloc_cache() to select a per-core<br>
> > cache slot. When core_index is -1, all registered threads are funneled<br>
> > into a single shared slot protected by a spinlock. In testing with VPP<br>
> > (which registers worker threads via rte_thread_register()), this caused<br>
> > async flow rule insertion throughput to drop from ~6.4M rules/sec to<br>
> > ~1.2M rules/sec with 4 workers -- a 5x regression attributable entirely<br>
> > to spinlock contention in the ipool allocator.<br>
> ><br>
> > Fix by setting core_index to the next sequential index (cfg->lcore_count)<br>
> > in eal_lcore_non_eal_allocate() before incrementing the count. Also reset<br>
> > core_index back to -1 on the error rollback path and in<br>
> > eal_lcore_non_eal_release() for correctness.<br>
> ><br>
> > Fixes: 5c307ba2a5b1 ("eal: register non-EAL threads as lcores")<br>
> Cc: <a href="mailto:stable@dpdk.org" target="_blank">stable@dpdk.org</a><br>
><br>
> > Signed-off-by: Maxime Peim <<a href="mailto:maxime.peim@gmail.com" target="_blank">maxime.peim@gmail.com</a>><br>
> Acked-by: David Marchand <<a href="mailto:david.marchand@redhat.com" target="_blank">david.marchand@redhat.com</a>><br>
><br>
<br>
Hum, I did not push the change.<br>
Re-reading this code, we have an issue if some external thread<br>
unregisters in the middle.<br>
<br>
What do you think of the additional hunk:<br>
<br>
$ git diff<br>
diff --git a/lib/eal/common/eal_common_lcore.c<br>
b/lib/eal/common/eal_common_lcore.c<br>
index ae085d73e4..6f53f20d90 100644<br>
--- a/lib/eal/common/eal_common_lcore.c<br>
+++ b/lib/eal/common/eal_common_lcore.c<br>
@@ -372,13 +372,16 @@ eal_lcore_non_eal_allocate(void)<br>
        struct rte_config *cfg = rte_eal_get_configuration();<br>
        struct lcore_callback *callback;<br>
        struct lcore_callback *prev;<br>
+       unsigned int index = 0;<br>
        unsigned int lcore_id;<br>
<br>
        rte_rwlock_write_lock(&lcore_lock);<br>
        for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {<br>
-               if (cfg->lcore_role[lcore_id] != ROLE_OFF)<br>
+               if (cfg->lcore_role[lcore_id] != ROLE_OFF) {<br>
+                       index++;<br>
                        continue;<br>
-               lcore_config[lcore_id].core_index = cfg->lcore_count;<br>
+               }<br>
+               lcore_config[lcore_id].core_index = index;<br>
                cfg->lcore_role[lcore_id] = ROLE_NON_EAL;<br>
                cfg->lcore_count++;<br>
                break;<br>
<br>
<br>
-- <br>
David Marchand<br>
<br>
</blockquote></div>