<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 11pt; color: rgb(0, 0, 0);">
Acked-by: Viacheslav Ovsiienko <viacheslavo@nvidia.com></div>
<div id="appendonsend"></div>
<hr style="display: inline-block; width: 98%;">
<div id="divRplyFwdMsg">
<div style="direction: ltr; font-family: Calibri, sans-serif; font-size: 11pt; color: rgb(0, 0, 0);">
<b>From:</b> Shuaisong Yang <yangshuaisong@h-partners.com><br>
<b>Sent:</b> Thursday, November 5, 2026 4:08 PM<br>
<b>To:</b> NBU-Contact-Thomas Monjalon (EXTERNAL) <thomas@monjalon.net>; Dariusz Sosnowski <dsosnowski@nvidia.com>; Slava Ovsiienko <viacheslavo@nvidia.com>; Bing Zhao <bingz@nvidia.com>; Ori Kam <orika@nvidia.com>; Suanming Mou <suanmingm@nvidia.com>; Matan
 Azrad <matan@nvidia.com>; Dmitry Kozlyuk <dmitry.kozliuk@gmail.com><br>
<b>Cc:</b> dev@dpdk.org <dev@dpdk.org>; stable@dpdk.org <stable@dpdk.org>; stephen@networkplumber.org <stephen@networkplumber.org>; fengchengwen@huawei.com <fengchengwen@huawei.com>; yangshuaisong@h-partners.com <yangshuaisong@h-partners.com>; yangxingui@huawei.com
 <yangxingui@huawei.com>; liuyonglong@huawei.com <liuyonglong@huawei.com><br>
<b>Subject:</b> [PATCH v3] common/mlx5: fix high SMMU TLB miss with mempool alignment</div>
<div style="direction: ltr;"> </div>
</div>
<div style="font-size: 11pt;">On Kunpeng SoC with mlx CX7, dpdk-l3fwd with intra-NUMA core pinning<br>
under SMMU nonstrict/strict mode shows about 30% performance degradation<br>
compared to cross-NUMA pinning. With SMMU disabled or passthrough mode,<br>
intra-NUMA performs as expected (slightly better than cross-NUMA).<br>
<br>
CX7 in NUMA1<br>
NUMA node0 CPU(s):    0-39<br>
NUMA node1 CPU(s):    40-79<br>
<br>
intra-NUMA:<br>
dpdk-l3fwd -l 40-55 -n 4 -a 0000:17:00.1,mprq_en=1 -- -p 0x1 -P \<br>
   --config='(0,0,40),(0,1,41),(0,2,42),(0,3,43),(0,4,44),\<br>
             (0,5,45),(0,6,46),(0,7,47),(0,8,48),(0,9,49),\<br>
             (0,10,50),(0,11,51),(0,12,52),(0,13,53),\<br>
             (0,14,54),(0,15,55)' \<br>
   --rx-queue-size=4096 --tx-queue-size=4096 --rx-burst=64<br>
<br>
cross-NUMA:<br>
dpdk-l3fwd -l 11-26 -n 4 -a 0000:17:00.1,mprq_en=1 -- -p 0x1 -P \<br>
   --config='(0,0,11),(0,1,12),(0,2,13),(0,3,14),(0,4,15),\<br>
             (0,5,16),(0,6,17),(0,7,18),(0,8,19),(0,9,20),\<br>
             (0,10,21),(0,11,22),(0,12,23),(0,13,24),\<br>
             (0,14,25),(0,15,26)' \<br>
   --rx-queue-size=4096 --tx-queue-size=4096 --rx-burst=64<br>
<br>
The root cause is that under SMMU enabled mode, the mempool allocated<br>
for intra-NUMA pinning is aligned to system page size instead of<br>
hugepage size, while cross-NUMA pinning correctly uses hugepage size<br>
alignment. This causes high TLB miss rates under SMMU.<br>
<br>
For non-external memory, align all memory ranges to hugepage boundaries<br>
during mempool registration to ensure hugepage_sz alignment, thereby<br>
reducing TLB misses and fixing the intra-NUMA performance degradation.<br>
External memory keeps the original behavior.<br>
<br>
Fixes: 690b2a88c2f7 ("common/mlx5: add mempool registration facilities")<br>
Cc: stable@dpdk.org<br>
<br>
Signed-off-by: Shuaisong Yang <yangshuaisong@h-partners.com><br>
Signed-off-by: Xingui Yang <yangxingui@huawei.com><br>
<br>
---<br>
<br>
v2:<br>
- Address review comments from Slava:<br>
  - Call rte_mem_virt2memseg_list() in a single point inside the loop.<br>
  - Skip the alignment block when hugepage_sz equals rte_mem_page_size(),<br>
    since mlx5_mempool_get_chunks() has already done the alignment.<br>
  - Skip the alignment for external memory with early goto, since ranges<br>
    are already aligned to the correct page size.<br>
- Update function documentation and comments.<br>
<br>
v3:<br>
- Drop the "if (is_extmem) goto out;" early exit: external memory now<br>
  follows the original behavior again (if the entire pool resides within<br>
  a single hugepage, all ranges are combined into one of the hugepage<br>
  size and share_hugepage is set), while non-external memory keeps the<br>
  new hugepage alignment and adjacent ranges merging.<br>
- Move the .mailmap entry to its alphabetical position.<br>
---<br>
 .mailmap                             |  1 +<br>
 drivers/common/mlx5/mlx5_common_mr.c | 85 +++++++++++++++++++++-------<br>
 2 files changed, 64 insertions(+), 22 deletions(-)<br>
<br>
diff --git a/.mailmap b/.mailmap<br>
index 754dd9e760..b4a4646568 100644<br>
--- a/.mailmap<br>
+++ b/.mailmap<br>
@@ -1584,6 +1584,7 @@ Shreyansh Jain <shreyansh.jain@nxp.com><br>
 Shrikrishna Khare <skhare@vmware.com><br>
 Shu Shen <shu.shen@radisys.com><br>
 Shuai Zhu <shuaix.zhu@intel.com><br>
+Shuaisong Yang <yangshuaisong@h-partners.com><br>
 Shuanglin Wang <shuanglin.wang@broadcom.com><br>
 Shujing Dong <shujing.dong@corigine.com><br>
 Shuki Katzenelson <shuki@lightbitslabs.com><br>
diff --git a/drivers/common/mlx5/mlx5_common_mr.c b/drivers/common/mlx5/mlx5_common_mr.c<br>
index cf39aa93e7..c215ef2bb6 100644<br>
--- a/drivers/common/mlx5/mlx5_common_mr.c<br>
+++ b/drivers/common/mlx5/mlx5_common_mr.c<br>
@@ -1524,7 +1524,11 @@ mlx5_get_mempool_ranges(struct rte_mempool *mp, bool is_extmem,<br>
  * @param[in] is_extmem<br>
  *   Whether the pool is contains only external pinned buffers.<br>
  * @param[out] out<br>
- *   Receives memory ranges to register, aligned to the system page size.<br>
+ *   Receives memory ranges to register. For non-external memory, ranges<br>
+ *   are aligned to the hugepage size if all ranges are on hugepages<br>
+ *   of the same size, otherwise aligned to the system page size.<br>
+ *   For external memory, if the entire pool resides within a single<br>
+ *   hugepage, all ranges are combined into one of the hugepage size.<br>
  *   The caller must release them with free().<br>
  * @param[out] out_n<br>
  *   Receives the number of @p out items.<br>
@@ -1541,7 +1545,8 @@ mlx5_mempool_reg_analyze(struct rte_mempool *mp, bool is_extmem,<br>
 {<br>
         struct mlx5_range *ranges = NULL;<br>
         unsigned int i, ranges_n = 0;<br>
-       struct rte_memseg_list *msl;<br>
+       uint64_t hugepage_sz = 0;<br>
+       bool same_hugepage_sz = true;<br>
 <br>
         if (mlx5_get_mempool_ranges(mp, is_extmem, &ranges, &ranges_n) < 0) {<br>
                 DRV_LOG(ERR, "Cannot get address ranges for mempool %s",<br>
@@ -1550,33 +1555,69 @@ mlx5_mempool_reg_analyze(struct rte_mempool *mp, bool is_extmem,<br>
         }<br>
         /* Check if the hugepage of the pool can be shared. */<br>
         *share_hugepage = false;<br>
-       msl = rte_mem_virt2memseg_list((void *)ranges[0].start);<br>
-       if (msl != NULL) {<br>
-               uint64_t hugepage_sz = 0;<br>
+       /* Check that all ranges are on pages of the same size. */<br>
+       for (i = 0; i < ranges_n; i++) {<br>
+               struct rte_memseg_list *range_msl;<br>
+               range_msl = rte_mem_virt2memseg_list((void *)ranges[i].start);<br>
+               if (range_msl == NULL) {<br>
+                       same_hugepage_sz = false;<br>
+                       break;<br>
+               }<br>
+               if (hugepage_sz == 0)<br>
+                       hugepage_sz = range_msl->page_sz;<br>
+               else if (range_msl->page_sz != hugepage_sz) {<br>
+                       same_hugepage_sz = false;<br>
+                       break;<br>
+               }<br>
+       }<br>
+       /*<br>
+        * For non-external memory, align ranges to the hugepage boundaries<br>
+        * and merge adjacent ones. If the entire pool fits in a single<br>
+        * hugepage, the MR for this hugepage can be shared across mempools.<br>
+        */<br>
+       if (!is_extmem && same_hugepage_sz &&<br>
+           hugepage_sz > rte_mem_page_size()) {<br>
+               unsigned int orig_ranges_n = ranges_n;<br>
 <br>
-               /* Check that all ranges are on pages of the same size. */<br>
                 for (i = 0; i < ranges_n; i++) {<br>
-                       if (hugepage_sz != 0 && hugepage_sz != msl->page_sz)<br>
-                               break;<br>
-                       hugepage_sz = msl->page_sz;<br>
+                       ranges[i].start = RTE_ALIGN_FLOOR(ranges[i].start,<br>
+                                                         hugepage_sz);<br>
+                       ranges[i].end = RTE_ALIGN_CEIL(ranges[i].end,<br>
+                                                       hugepage_sz);<br>
                 }<br>
-               if (i == ranges_n) {<br>
-                       /*<br>
-                        * If the entire pool is within one hugepage,<br>
-                        * combine all ranges into one of the hugepage size.<br>
-                        */<br>
-                       uintptr_t reg_start = ranges[0].start;<br>
-                       uintptr_t reg_end = ranges[ranges_n - 1].end;<br>
-                       uintptr_t hugepage_start =<br>
-                               RTE_ALIGN_FLOOR(reg_start, hugepage_sz);<br>
-                       uintptr_t hugepage_end = hugepage_start + hugepage_sz;<br>
-                       if (reg_end < hugepage_end) {<br>
-                               ranges[0].start = hugepage_start;<br>
+               ranges_n = 1;<br>
+               for (i = 1; i < orig_ranges_n; i++) {<br>
+                       if (ranges[ranges_n - 1].end >= ranges[i].start)<br>
+                               ranges[ranges_n - 1].end =<br>
+                                       RTE_MAX(ranges[ranges_n - 1].end,<br>
+                                               ranges[i].end);<br>
+                       else<br>
+                               ranges[ranges_n++] = ranges[i];<br>
+               }<br>
+               if (ranges_n == 1) {<br>
+                       uintptr_t hugepage_end = ranges[0].start + hugepage_sz;<br>
+<br>
+                       if (ranges[0].end <= hugepage_end) {<br>
                                 ranges[0].end = hugepage_end;<br>
-                               ranges_n = 1;<br>
                                 *share_hugepage = true;<br>
                         }<br>
                 }<br>
+       } else if (is_extmem && same_hugepage_sz && hugepage_sz > 0) {<br>
+               /*<br>
+                * If the entire pool is within one hugepage,<br>
+                * combine all ranges into one of the hugepage size.<br>
+                */<br>
+               uintptr_t reg_start = ranges[0].start;<br>
+               uintptr_t reg_end = ranges[ranges_n - 1].end;<br>
+               uintptr_t hugepage_start =<br>
+                       RTE_ALIGN_FLOOR(reg_start, hugepage_sz);<br>
+               uintptr_t hugepage_end = hugepage_start + hugepage_sz;<br>
+               if (reg_end < hugepage_end) {<br>
+                       ranges[0].start = hugepage_start;<br>
+                       ranges[0].end = hugepage_end;<br>
+                       ranges_n = 1;<br>
+                       *share_hugepage = true;<br>
+               }<br>
         }<br>
         *out = ranges;<br>
         *out_n = ranges_n;<br>
--<br>
2.33.0<br>
<br>
</div>
</body>
</html>