* [PATCH v3 04/10] common/mlx5: add null MR functions
From: Thomas Monjalon @ 2026-05-28 12:46 UTC (permalink / raw)
To: dev
Cc: Stephen Hemminger, Gregory Etelson, Dariusz Sosnowski,
Viacheslav Ovsiienko, Bing Zhao, Ori Kam, Suanming Mou,
Matan Azrad
In-Reply-To: <20260528124758.1984528-1-thomas@monjalon.net>
From: Gregory Etelson <getelson@nvidia.com>
Add functions to allocate and free a null Memory Region (MR)
using ibverbs on Linux.
There is no implementation for DevX on Windows.
Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
drivers/common/mlx5/linux/mlx5_common_verbs.c | 35 +++++++++++++++++++
drivers/common/mlx5/mlx5_common_mr.h | 9 +++++
drivers/common/mlx5/windows/mlx5_common_os.c | 16 +++++++++
3 files changed, 60 insertions(+)
diff --git a/drivers/common/mlx5/linux/mlx5_common_verbs.c b/drivers/common/mlx5/linux/mlx5_common_verbs.c
index 2322d9d033..6d44e1f566 100644
--- a/drivers/common/mlx5/linux/mlx5_common_verbs.c
+++ b/drivers/common/mlx5/linux/mlx5_common_verbs.c
@@ -161,3 +161,38 @@ mlx5_os_set_reg_mr_cb(mlx5_reg_mr_t *reg_mr_cb, mlx5_dereg_mr_t *dereg_mr_cb)
*reg_mr_cb = mlx5_common_verbs_reg_mr;
*dereg_mr_cb = mlx5_common_verbs_dereg_mr;
}
+
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_alloc_null_mr)
+struct mlx5_pmd_mr *
+mlx5_os_alloc_null_mr(struct rte_device *dev, void *pd)
+{
+ struct ibv_mr *ibv_mr;
+ struct mlx5_pmd_mr *null_mr;
+
+ null_mr = mlx5_malloc(MLX5_MEM_ZERO, sizeof(*null_mr), 0, dev->numa_node);
+ if (!null_mr)
+ return NULL;
+ ibv_mr = mlx5_glue->alloc_null_mr(pd);
+ if (!ibv_mr) {
+ mlx5_free(null_mr);
+ return NULL;
+ }
+ *null_mr = (struct mlx5_pmd_mr) {
+ .lkey = rte_cpu_to_be_32(ibv_mr->lkey),
+ .addr = ibv_mr->addr,
+ .len = ibv_mr->length,
+ .obj = (void *)ibv_mr,
+ };
+ return null_mr;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_free_null_mr)
+void
+mlx5_os_free_null_mr(struct mlx5_pmd_mr *null_mr)
+{
+ if (!null_mr)
+ return;
+ if (null_mr->obj)
+ claim_zero(mlx5_glue->dereg_mr(null_mr->obj));
+ mlx5_free(null_mr);
+}
diff --git a/drivers/common/mlx5/mlx5_common_mr.h b/drivers/common/mlx5/mlx5_common_mr.h
index cf7c685e9b..00f3d832c3 100644
--- a/drivers/common/mlx5/mlx5_common_mr.h
+++ b/drivers/common/mlx5/mlx5_common_mr.h
@@ -21,6 +21,8 @@
#include "mlx5_common_mp.h"
#include "mlx5_common_defs.h"
+struct rte_device;
+
/* mlx5 PMD MR struct. */
struct mlx5_pmd_mr {
uint32_t lkey;
@@ -258,6 +260,13 @@ __rte_internal
void
mlx5_os_set_reg_mr_cb(mlx5_reg_mr_t *reg_mr_cb, mlx5_dereg_mr_t *dereg_mr_cb);
+__rte_internal
+struct mlx5_pmd_mr *
+mlx5_os_alloc_null_mr(struct rte_device *dev, void *pd);
+__rte_internal
+void
+mlx5_os_free_null_mr(struct mlx5_pmd_mr *null_mr);
+
__rte_internal
int
mlx5_mr_mempool_register(struct mlx5_common_device *cdev,
diff --git a/drivers/common/mlx5/windows/mlx5_common_os.c b/drivers/common/mlx5/windows/mlx5_common_os.c
index 16fcc5f9fc..692517a9bf 100644
--- a/drivers/common/mlx5/windows/mlx5_common_os.c
+++ b/drivers/common/mlx5/windows/mlx5_common_os.c
@@ -454,6 +454,22 @@ mlx5_os_set_reg_mr_cb(mlx5_reg_mr_t *reg_mr_cb, mlx5_dereg_mr_t *dereg_mr_cb)
*dereg_mr_cb = mlx5_os_dereg_mr;
}
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_alloc_null_mr)
+struct mlx5_pmd_mr *
+mlx5_os_alloc_null_mr(struct rte_device *dev, void *pd)
+{
+ RTE_SET_USED(dev);
+ RTE_SET_USED(pd);
+ return NULL;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_free_null_mr)
+void
+mlx5_os_free_null_mr(struct mlx5_pmd_mr *null_mr)
+{
+ RTE_SET_USED(null_mr);
+}
+
/*
* In Windows, no need to wrap the MR, no known issue for it in kernel.
* Use the regular function to create direct MR.
--
2.54.0
^ permalink raw reply related
* [PATCH v3 05/10] net/mlx5: fix Rx split segment counter type
From: Thomas Monjalon @ 2026-05-28 12:46 UTC (permalink / raw)
To: dev
Cc: Stephen Hemminger, stable, Dariusz Sosnowski,
Viacheslav Ovsiienko, Bing Zhao, Ori Kam, Suanming Mou,
Matan Azrad
In-Reply-To: <20260528124758.1984528-1-thomas@monjalon.net>
In the API, rx_nseg and max_nseg are uint16_t.
In mlx5, MLX5_MAX_RXQ_NSEG is 32.
So there is no reason to have rxseg_n as uint32_t.
Reduce the fields to uint16_t and move them to avoid struct holes.
Fixes: 9f209b59c8b0 ("net/mlx5: support Rx buffer split description")
Fixes: 572c9d4bda08 ("net/mlx5: fix shared Rx queue segment configuration match")
Cc: stable@dpdk.org
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
drivers/net/mlx5/mlx5_rx.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/mlx5/mlx5_rx.h b/drivers/net/mlx5/mlx5_rx.h
index dffab3955b..01b563d981 100644
--- a/drivers/net/mlx5/mlx5_rx.h
+++ b/drivers/net/mlx5/mlx5_rx.h
@@ -164,9 +164,9 @@ struct __rte_cache_aligned mlx5_rxq_data {
uint64_t flow_meta_mask;
int32_t flow_meta_offset;
uint32_t flow_meta_port_mask;
- uint32_t rxseg_n; /* Number of split segment descriptions. */
struct mlx5_eth_rxseg rxseg[MLX5_MAX_RXQ_NSEG];
/* Buffer split segment descriptions - sizes, offsets, pools. */
+ uint16_t rxseg_n; /* Number of split segment descriptions. */
uint16_t rq_win_cnt; /* Number of packets in the sliding window data. */
uint16_t rq_win_idx_mask; /* Sliding window index wrapping mask. */
uint16_t rq_win_idx; /* Index of the first element in sliding window. */
@@ -191,9 +191,9 @@ struct mlx5_rxq_ctrl {
unsigned int irq:1; /* Whether IRQ is enabled. */
uint32_t flow_tunnels_n[MLX5_FLOW_TUNNEL]; /* Tunnels counters. */
uint32_t wqn; /* WQ number. */
- uint32_t rxseg_n; /* Number of split segment descriptions. */
struct rte_eth_rxseg_split rxseg[MLX5_MAX_RXQ_NSEG];
/* Saved original buffer split segment configuration. */
+ uint16_t rxseg_n; /* Number of split segment descriptions. */
uint16_t dump_file_n; /* Number of dump files. */
};
--
2.54.0
^ permalink raw reply related
* [PATCH v3 06/10] net/mlx5: support selective Rx
From: Thomas Monjalon @ 2026-05-28 12:46 UTC (permalink / raw)
To: dev
Cc: Stephen Hemminger, Gregory Etelson, Dariusz Sosnowski,
Viacheslav Ovsiienko, Bing Zhao, Ori Kam, Suanming Mou,
Matan Azrad
In-Reply-To: <20260528124758.1984528-1-thomas@monjalon.net>
From: Gregory Etelson <getelson@nvidia.com>
Selective Rx may save some PCI bandwidth.
Implement selective Rx in the (quite slow) scalar SPRQ Rx path
mlx5_rx_burst() where the performance impact
of the added condition branches is acceptable.
Other Rx functions do not support this feature.
When using selective Rx, mlx5_rx_burst will be selected.
A null Memory Region (MR) is always allocated
at shared device context initialization.
The selective Rx capability is not advertised
if this special MR allocation fails.
For each Rx segment configured with a NULL mempool,
a "null mbuf" is created.
It is a fake mbuf allocated outside any mempool,
used as a placeholder in the Rx ring.
The null MR lkey is used in the WQE for these segments
so the NIC writes received data to a discard buffer.
The mbuf data room size is resolved from the first segment having a pool.
For null segments, the buffer length is from the last seen pool,
so that the WQE stride size remains consistent.
In mlx5_rx_burst, discarded segments are not chained
into the packet mbuf list, NB_SEGS is decremented accordingly,
and no replacement buffer is allocated.
A separate data_seg_len accumulator tracks the total length
of delivered segments only.
The packet length is adjusted to reflect only the data
actually delivered to the application.
Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
doc/guides/nics/features/mlx5.ini | 1 +
doc/guides/nics/mlx5.rst | 86 +++++++++++++++++++-------
doc/guides/rel_notes/release_26_07.rst | 4 ++
drivers/net/mlx5/mlx5.c | 7 +++
drivers/net/mlx5/mlx5.h | 1 +
drivers/net/mlx5/mlx5_ethdev.c | 25 ++++++++
drivers/net/mlx5/mlx5_rx.c | 26 +++++++-
drivers/net/mlx5/mlx5_rx.h | 1 +
drivers/net/mlx5/mlx5_rxq.c | 45 ++++++++++++--
drivers/net/mlx5/mlx5_trigger.c | 52 +++++++++++++---
10 files changed, 211 insertions(+), 37 deletions(-)
diff --git a/doc/guides/nics/features/mlx5.ini b/doc/guides/nics/features/mlx5.ini
index 3b3eda28b8..ae8c83057b 100644
--- a/doc/guides/nics/features/mlx5.ini
+++ b/doc/guides/nics/features/mlx5.ini
@@ -16,6 +16,7 @@ Burst mode info = Y
Power mgmt address monitor = Y
MTU update = Y
Buffer split on Rx = Y
+Selective Rx = Y
Scattered Rx = Y
LRO = Y
TSO = Y
diff --git a/doc/guides/nics/mlx5.rst b/doc/guides/nics/mlx5.rst
index 137fbaf889..b4455f3e7d 100644
--- a/doc/guides/nics/mlx5.rst
+++ b/doc/guides/nics/mlx5.rst
@@ -84,6 +84,9 @@ The Rx / Tx data path use different techniques to offer the best performance.
with :ref:`multi-packet Rx queues (MPRQ) <mlx5_mprq_params>`.
This feature is disabled by default.
+- Some PCI bandwidth is saved by receiving partial packets
+ with :ref:`selective Rx <mlx5_selective_rx>`.
+
More details about Rx implementations and their configurations are provided
in the chapter about :ref:`mlx5_rx_functions`.
@@ -879,6 +882,8 @@ MLX5 supports various methods to report statistics:
Basic port statistics can be queried using ``rte_eth_stats_get()``.
The received and sent statistics are through SW only
and counts the number of packets received or sent successfully by the PMD.
+In the case of :ref:`selective Rx <mlx5_selective_rx>`,
+the ``ibytes`` counter matches segments delivered, not the skipped ones.
The ``imissed`` counter is the amount of packets that could not be delivered
to SW because a queue was full.
Packets not received due to congestion in the bus or on the NIC
@@ -992,25 +997,26 @@ These configurations may also have an impact on the behavior:
.. table:: Rx burst functions
- +-------------------+------------------------+---------+-----------------+------+-------+---------+
- || Function Name || Parameters to Enable || Scatter|| Error Recovery || CQE || Large|| Shared |
- | | | | || comp|| MTU | RxQ |
- +===================+========================+=========+=================+======+=======+=========+
- | rx_burst | rx_vec_en=0 | Yes | Yes | Yes | Yes | No |
- +-------------------+------------------------+---------+-----------------+------+-------+---------+
- | rx_burst_vec | rx_vec_en=1 (default) | No | if CQE comp off | Yes | No | No |
- +-------------------+------------------------+---------+-----------------+------+-------+---------+
- | rx_burst_mprq || mprq_en=1 | No | Yes | Yes | Yes | No |
- | || RxQs >= rxqs_min_mprq | | | | | |
- +-------------------+------------------------+---------+-----------------+------+-------+---------+
- | rx_burst_mprq_vec || rx_vec_en=1 (default) | No | if CQE comp off | Yes | Yes | No |
- | || mprq_en=1 | | | | | |
- | || RxQs >= rxqs_min_mprq | | | | | |
- +-------------------+------------------------+---------+-----------------+------+-------+---------+
- | rx_burst | at least one Rx queue | Yes | Yes | Yes | Yes | Yes |
- | (out of order) | on the device | | | | | |
- | | is shared | | | | | |
- +-------------------+------------------------+---------+-----------------+------+-------+---------+
+ +----------+-----------------------+---------+--------+----------+------+-------+--------+
+ || Function|| Parameters to Enable || Scatter|| Selec-|| Error || CQE || Large|| Shared|
+ || Name | | || tive || Recovery|| comp|| MTU || RxQ |
+ +==========+=======================+=========+========+==========+======+=======+========+
+ | rx_burst | rx_vec_en=0 | Yes | Yes | Yes | Yes | Yes | No |
+ +----------+-----------------------+---------+--------+----------+------+-------+--------+
+ | _vec | rx_vec_en=1 (default) | No | No || if CQE | Yes | No | No |
+ | | | | || comp off| | | |
+ +----------+-----------------------+---------+--------+----------+------+-------+--------+
+ | _mprq || mprq_en=1 | No | No | Yes | Yes | Yes | No |
+ | || RxQs >= rxqs_min_mprq| | | | | | |
+ +----------+-----------------------+---------+--------+----------+------+-------+--------+
+ | _mprq_vec|| rx_vec_en=1 (default)| No | No || if CQE | Yes | Yes | No |
+ | || mprq_en=1 | | || comp off| | | |
+ | || RxQs >= rxqs_min_mprq| | | | | | |
+ +----------+-----------------------+---------+--------+----------+------+-------+--------+
+ || _out_of || at least one Rx queue| Yes | No | Yes | Yes | Yes | Yes |
+ || _order || on the device | | | | | | |
+ | || is shared | | | | | | |
+ +----------+-----------------------+---------+--------+----------+------+-------+--------+
Rx/Tx Tuning
@@ -1105,13 +1111,14 @@ Rx interrupt X
:ref:`Rx threshold <mlx5_rx_threshold>` X X
:ref:`Rx drop delay <mlx5_drop>` X X
:ref:`Rx timestamp <mlx5_rx_timstp>` X X
+:ref:`buffer split <mlx5_buf_split>` X X
+:ref:`selective Rx <mlx5_selective_rx>` X
+:ref:`multi-segment <mlx5_multiseg>` X X
:ref:`Tx scheduling <mlx5_tx_sched>` X
:ref:`Tx rate limit <mlx5_rate_limit>` X
:ref:`Tx inline <mlx5_tx_inline>` X X
:ref:`Tx fast free <mlx5_tx_fast_free>` X X
:ref:`Tx affinity <mlx5_aggregated>` X
-:ref:`buffer split <mlx5_buf_split>` X X
-:ref:`multi-segment <mlx5_multiseg>` X X
promiscuous X X
multicast promiscuous X X
multiple MAC addresses X
@@ -2248,13 +2255,50 @@ OFED 5.1-2
DPDK 20.11
========= ==========
+Runtime configuration
+^^^^^^^^^^^^^^^^^^^^^
+
+The offload flag ``RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT`` is required.
+
+When calling ``rte_eth_rx_queue_setup()``,
+the input ``rte_eth_rxconf::rx_seg`` defines the configuration of the segments,
+mainly offset and length.
+
Limitations
^^^^^^^^^^^
+#. Splitting per protocol header is not supported.
+
#. Buffer split offload is supported with regular Rx burst routine only,
no MPRQ feature or vectorized code can be engaged.
+.. _mlx5_selective_rx:
+
+Selective Rx
+~~~~~~~~~~~~
+
+Some PCI bandwidth can be saved
+by :ref:`skipping some parts of Rx data <nic_features_selective_rx>`.
+It is enabled when using :ref:`buffer split <mlx5_buf_split>`
+and configuring no mempool in some segments to discard.
+
+Runtime configuration
+^^^^^^^^^^^^^^^^^^^^^
+
+The offload flag ``RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT`` is required.
+
+When calling ``rte_eth_rx_queue_setup()``,
+the segment to discard (``rte_eth_rxconf::rx_seg::split``)
+is marked by the absence of mempool (``mp = NULL``).
+
+Limitations
+^^^^^^^^^^^
+
+#. Selective Rx is supported with regular Rx burst routine only,
+ no MPRQ feature or vectorized code can be engaged.
+
+
.. _mlx5_multiseg:
Multi-Segment Scatter/Gather
diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index fc4dc7fb9b..22aaf0d12c 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -79,6 +79,10 @@ New Features
* Added support for transmitting LLDP packets based on mbuf packet type.
* Implemented AVX2 context descriptor transmit paths.
+* **Updated NVIDIA mlx5 ethernet driver.**
+
+ * Added support for selective Rx in scalar SPRQ Rx path.
+
* **Updated PCAP ethernet driver.**
* Added support for VLAN insertion and stripping.
diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c
index f190654756..61c26d1206 100644
--- a/drivers/net/mlx5/mlx5.c
+++ b/drivers/net/mlx5/mlx5.c
@@ -1975,6 +1975,9 @@ mlx5_alloc_shared_dev_ctx(const struct mlx5_dev_spawn_data *spawn,
/* Init counter pool list header and lock. */
LIST_INIT(&sh->hws_cpool_list);
rte_spinlock_init(&sh->cpool_lock);
+ sh->null_mr = mlx5_os_alloc_null_mr(sh->cdev->dev, sh->cdev->pd);
+ if (!sh->null_mr)
+ DRV_LOG(DEBUG, "Fail to initialize NULL MR, selective Rx is disabled.");
exit:
pthread_mutex_unlock(&mlx5_dev_ctx_list_mutex);
return sh;
@@ -2139,6 +2142,10 @@ mlx5_free_shared_dev_ctx(struct mlx5_dev_ctx_shared *sh)
MLX5_ASSERT(sh->geneve_tlv_option_resource == NULL);
pthread_mutex_destroy(&sh->txpp.mutex);
mlx5_lwm_unset(sh);
+ if (sh->null_mr) {
+ mlx5_os_free_null_mr(sh->null_mr);
+ sh->null_mr = NULL;
+ }
mlx5_physical_device_destroy(sh->phdev);
mlx5_free(sh);
return;
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index 92a00cfaa8..bd6ef35b53 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -1674,6 +1674,7 @@ struct mlx5_dev_ctx_shared {
rte_spinlock_t cpool_lock;
LIST_HEAD(hws_cpool_list, mlx5_hws_cnt_pool) hws_cpool_list; /* Count pool list. */
struct mlx5_dev_registers registers;
+ struct mlx5_pmd_mr *null_mr;
struct mlx5_dev_shared_port port[]; /* per device port data array. */
};
diff --git a/drivers/net/mlx5/mlx5_ethdev.c b/drivers/net/mlx5/mlx5_ethdev.c
index a29cdeeb50..7b7536fa1e 100644
--- a/drivers/net/mlx5/mlx5_ethdev.c
+++ b/drivers/net/mlx5/mlx5_ethdev.c
@@ -381,6 +381,7 @@ mlx5_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
info->rx_seg_capa.multi_pools = !priv->config.mprq.enabled;
info->rx_seg_capa.offset_allowed = !priv->config.mprq.enabled;
info->rx_seg_capa.offset_align_log2 = 0;
+ info->rx_seg_capa.selective_rx = !!priv->sh->null_mr;
info->rx_offload_capa = (mlx5_get_rx_port_offloads() |
info->rx_queue_offload_capa);
info->tx_offload_capa = mlx5_get_tx_port_offloads(dev);
@@ -708,6 +709,25 @@ mlx5_dev_set_mtu(struct rte_eth_dev *dev, uint16_t mtu)
return -rte_errno;
}
+static bool
+mlx5_selective_rx_enabled(struct rte_eth_dev *dev)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+
+ for (uint32_t q = 0; q < priv->rxqs_n; ++q) {
+ struct mlx5_rxq_ctrl *rxq_ctrl = mlx5_rxq_ctrl_get(dev, q);
+
+ if (rxq_ctrl == NULL || rxq_ctrl->is_hairpin)
+ continue;
+ for (uint16_t s = 0; s < rxq_ctrl->rxq.rxseg_n; s++) {
+ if (rxq_ctrl->rxq.rxseg[s].mp == NULL)
+ return true;
+ }
+ }
+
+ return false;
+}
+
/**
* Configure the RX function to use.
*
@@ -723,6 +743,11 @@ mlx5_select_rx_function(struct rte_eth_dev *dev)
eth_rx_burst_t rx_pkt_burst = mlx5_rx_burst;
MLX5_ASSERT(dev != NULL);
+ if (mlx5_selective_rx_enabled(dev)) {
+ DRV_LOG(DEBUG, "port %u forced to scalar SPRQ Rx (selective Rx configured)",
+ dev->data->port_id);
+ return rx_pkt_burst;
+ }
if (mlx5_shared_rq_enabled(dev)) {
rx_pkt_burst = mlx5_rx_burst_out_of_order;
DRV_LOG(DEBUG, "port %u forced to use SPRQ"
diff --git a/drivers/net/mlx5/mlx5_rx.c b/drivers/net/mlx5/mlx5_rx.c
index 185bfd4fff..586bf6c935 100644
--- a/drivers/net/mlx5/mlx5_rx.c
+++ b/drivers/net/mlx5/mlx5_rx.c
@@ -486,7 +486,7 @@ mlx5_rxq_initialize(struct mlx5_rxq_data *rxq)
rxq->wqes)[i];
addr = rte_pktmbuf_mtod(buf, uintptr_t);
byte_count = DATA_LEN(buf);
- lkey = mlx5_rx_mb2mr(rxq, buf);
+ lkey = buf->pool ? mlx5_rx_mb2mr(rxq, buf) : rxq->sh->null_mr->lkey;
}
/* scat->addr must be able to store a pointer. */
MLX5_ASSERT(sizeof(scat->addr) >= sizeof(uintptr_t));
@@ -1044,11 +1044,13 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
const unsigned int sges_n = rxq->sges_n;
struct rte_mbuf *pkt = NULL;
struct rte_mbuf *seg = NULL;
+ struct rte_mbuf *tail = NULL;
volatile struct mlx5_cqe *cqe =
&(*rxq->cqes)[rxq->cq_ci & cqe_mask];
unsigned int i = 0;
unsigned int rq_ci = rxq->rq_ci << sges_n;
int len = 0; /* keep its value across iterations. */
+ uint32_t data_seg_len = 0;
while (pkts_n) {
uint16_t skip_cnt;
@@ -1058,12 +1060,17 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
struct rte_mbuf *rep = (*rxq->elts)[idx];
volatile struct mlx5_mini_cqe8 *mcqe = NULL;
- if (pkt)
- NEXT(seg) = rep;
+ if (pkt) {
+ if (rep->pool)
+ NEXT(tail) = rep;
+ else
+ --NB_SEGS(pkt);
+ }
seg = rep;
rte_prefetch0(seg);
rte_prefetch0(cqe);
rte_prefetch0(wqe);
+ if (seg->pool) {
/* Allocate the buf from the same pool. */
rep = rte_mbuf_raw_alloc(seg->pool);
if (unlikely(rep == NULL)) {
@@ -1088,12 +1095,14 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
rq_ci <<= sges_n;
break;
}
+ }
if (!pkt) {
cqe = &(*rxq->cqes)[rxq->cq_ci & cqe_mask];
len = mlx5_rx_poll_len(rxq, cqe, cqe_n, cqe_mask,
&mcqe, &skip_cnt, false, NULL);
if (unlikely(len & MLX5_ERROR_CQE_MASK)) {
/* We drop packets with non-critical errors */
+ if (seg->pool)
rte_mbuf_raw_free(rep);
if (len == MLX5_CRITICAL_ERROR_CQE_RET) {
rq_ci = rxq->rq_ci << sges_n;
@@ -1107,6 +1116,7 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
continue;
}
if (len == 0) {
+ if (seg->pool)
rte_mbuf_raw_free(rep);
break;
}
@@ -1127,6 +1137,8 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
pkt->tso_segsz = len / cqe->lro_num_seg;
}
}
+ if (seg->pool) {
+ tail = seg;
DATA_LEN(rep) = DATA_LEN(seg);
PKT_LEN(rep) = PKT_LEN(seg);
SET_DATA_OFF(rep, DATA_OFF(seg));
@@ -1141,17 +1153,25 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
/* If there's only one MR, no need to replace LKey in WQE. */
if (unlikely(mlx5_mr_btree_len(&rxq->mr_ctrl.cache_bh) > 1))
wqe->lkey = mlx5_rx_mb2mr(rxq, rep);
+ }
if (len > DATA_LEN(seg)) {
+ if (seg->pool)
+ data_seg_len += DATA_LEN(seg);
len -= DATA_LEN(seg);
++NB_SEGS(pkt);
++rq_ci;
continue;
}
+ if (seg->pool) {
DATA_LEN(seg) = len;
+ data_seg_len += len;
+ }
+ PKT_LEN(pkt) = RTE_MIN(PKT_LEN(pkt), data_seg_len);
#ifdef MLX5_PMD_SOFT_COUNTERS
/* Increment bytes counter. */
rxq->stats.ibytes += PKT_LEN(pkt);
#endif
+ data_seg_len = 0;
/* Return packet. */
*(pkts++) = pkt;
pkt = NULL;
diff --git a/drivers/net/mlx5/mlx5_rx.h b/drivers/net/mlx5/mlx5_rx.h
index 01b563d981..cd48ee37ef 100644
--- a/drivers/net/mlx5/mlx5_rx.h
+++ b/drivers/net/mlx5/mlx5_rx.h
@@ -96,6 +96,7 @@ struct mlx5_eth_rxseg {
uint16_t length; /**< Segment data length, configures split point. */
uint16_t offset; /**< Data offset from beginning of mbuf data buffer. */
uint32_t reserved; /**< Reserved field. */
+ struct rte_mbuf *null_mbuf; /**< For selective Rx. */
};
/* RX queue descriptor. */
diff --git a/drivers/net/mlx5/mlx5_rxq.c b/drivers/net/mlx5/mlx5_rxq.c
index 48d982a8c2..3fae189fa4 100644
--- a/drivers/net/mlx5/mlx5_rxq.c
+++ b/drivers/net/mlx5/mlx5_rxq.c
@@ -151,6 +151,7 @@ rxq_alloc_elts_sprq(struct mlx5_rxq_ctrl *rxq_ctrl)
struct mlx5_eth_rxseg *seg = &rxq_ctrl->rxq.rxseg[i % sges_n];
struct rte_mbuf *buf;
+ if (seg->mp) {
buf = rte_pktmbuf_alloc(seg->mp);
if (buf == NULL) {
if (rxq_ctrl->share_group == 0)
@@ -167,6 +168,9 @@ rxq_alloc_elts_sprq(struct mlx5_rxq_ctrl *rxq_ctrl)
/* Only vectored Rx routines rely on headroom size. */
MLX5_ASSERT(!has_vec_support ||
DATA_OFF(buf) >= RTE_PKTMBUF_HEADROOM);
+ } else {
+ buf = seg->null_mbuf;
+ }
/* Buffer is supposed to be empty. */
MLX5_ASSERT(rte_pktmbuf_data_len(buf) == 0);
MLX5_ASSERT(rte_pktmbuf_pkt_len(buf) == 0);
@@ -324,10 +328,14 @@ rxq_free_elts_sprq(struct mlx5_rxq_ctrl *rxq_ctrl)
rxq->rq_pi = elts_ci;
}
for (i = 0; i != q_n; ++i) {
- if ((*rxq->elts)[i] != NULL)
+ if ((*rxq->elts)[i] != NULL && (*rxq->elts)[i]->pool != NULL)
rte_pktmbuf_free_seg((*rxq->elts)[i]);
(*rxq->elts)[i] = NULL;
}
+ for (i = 0; i < rxq->rxseg_n; i++) {
+ mlx5_free(rxq->rxseg[i].null_mbuf);
+ rxq->rxseg[i].null_mbuf = NULL;
+ }
}
/**
@@ -1815,7 +1823,9 @@ mlx5_rxq_new(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
int ret;
struct mlx5_priv *priv = dev->data->dev_private;
struct mlx5_rxq_ctrl *tmpl;
- unsigned int mb_len = rte_pktmbuf_data_room_size(rx_seg[0].mp);
+ struct rte_mempool *first_mp = NULL;
+ struct rte_mempool *last_mp = NULL;
+ unsigned int mb_len;
struct mlx5_port_config *config = &priv->config;
uint64_t offloads = conf->offloads |
dev->data->dev_conf.rxmode.offloads;
@@ -1827,7 +1837,7 @@ mlx5_rxq_new(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
unsigned int non_scatter_min_mbuf_size = max_rx_pktlen +
RTE_PKTMBUF_HEADROOM;
unsigned int max_lro_size = 0;
- unsigned int first_mb_free_size = mb_len - RTE_PKTMBUF_HEADROOM;
+ unsigned int first_mb_free_size;
uint32_t mprq_log_actual_stride_num = 0;
uint32_t mprq_log_actual_stride_size = 0;
bool rx_seg_en = n_seg != 1 || rx_seg[0].offset || rx_seg[0].length;
@@ -1845,6 +1855,21 @@ mlx5_rxq_new(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
const struct rte_eth_rxseg_split *qs_seg = rx_seg;
unsigned int tail_len;
+ /* Find first segment with a mempool. */
+ for (uint16_t seg = 0; seg < n_seg; seg++) {
+ if (rx_seg[seg].mp != NULL) {
+ first_mp = rx_seg[seg].mp;
+ break;
+ }
+ }
+ if (first_mp == NULL) {
+ DRV_LOG(ERR, "port %u Rx queue %u has no mempool", dev->data->port_id, idx);
+ rte_errno = EINVAL;
+ return NULL;
+ }
+ mb_len = rte_pktmbuf_data_room_size(first_mp);
+ first_mb_free_size = mb_len - RTE_PKTMBUF_HEADROOM;
+
if (mprq_en) {
/* Trim the number of descs needed. */
desc >>= mprq_log_actual_stride_num;
@@ -1884,13 +1909,20 @@ mlx5_rxq_new(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
do {
struct mlx5_eth_rxseg *hw_seg =
&tmpl->rxq.rxseg[tmpl->rxq.rxseg_n];
- uint32_t buf_len, offset, seg_len;
+ uint32_t buf_len = 0, offset, seg_len;
/*
* For the buffers beyond descriptions offset is zero,
* the first buffer contains head room.
*/
- buf_len = rte_pktmbuf_data_room_size(qs_seg->mp);
+ if (qs_seg->mp != NULL) {
+ last_mp = qs_seg->mp;
+ buf_len = rte_pktmbuf_data_room_size(qs_seg->mp);
+ } else if (last_mp != NULL) {
+ buf_len = rte_pktmbuf_data_room_size(last_mp);
+ } else {
+ buf_len = mb_len;
+ }
offset = (tmpl->rxq.rxseg_n >= n_seg ? 0 : qs_seg->offset) +
(tmpl->rxq.rxseg_n ? 0 : RTE_PKTMBUF_HEADROOM);
/*
@@ -2077,7 +2109,8 @@ mlx5_rxq_new(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
/* Save port ID. */
tmpl->rxq.port_id = dev->data->port_id;
tmpl->sh = priv->sh;
- tmpl->rxq.mp = rx_seg[0].mp;
+ tmpl->rxq.sh = priv->sh;
+ tmpl->rxq.mp = first_mp;
tmpl->rxq.elts_n = log2above(desc);
tmpl->rxq.rq_repl_thresh = MLX5_VPMD_RXQ_RPLNSH_THRESH(desc_n);
tmpl->rxq.elts = (struct rte_mbuf *(*)[])(tmpl + 1);
diff --git a/drivers/net/mlx5/mlx5_trigger.c b/drivers/net/mlx5/mlx5_trigger.c
index a070aaecfd..5b04d9a234 100644
--- a/drivers/net/mlx5/mlx5_trigger.c
+++ b/drivers/net/mlx5/mlx5_trigger.c
@@ -116,6 +116,27 @@ mlx5_txq_start(struct rte_eth_dev *dev)
return -rte_errno;
}
+static struct rte_mbuf *
+mlx5_alloc_null_mbuf(uint32_t data_len)
+{
+ size_t alloc_size = sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM +
+ rte_align32pow2(data_len);
+ struct rte_mbuf *m;
+
+ m = mlx5_malloc(MLX5_MEM_ZERO, alloc_size, 0, SOCKET_ID_ANY);
+ if (m == NULL)
+ return NULL;
+ m->buf_addr = RTE_PTR_ADD(m, sizeof(*m));
+ m->buf_len = alloc_size - sizeof(*m);
+ rte_mbuf_iova_set(m, rte_mem_virt2iova(m->buf_addr));
+ m->data_off = RTE_PKTMBUF_HEADROOM;
+ m->refcnt = 1;
+ m->nb_segs = 1;
+ m->port = RTE_MBUF_PORT_INVALID;
+ m->pool = NULL;
+ return m;
+}
+
/**
* Register Rx queue mempools and fill the Rx queue cache.
* This function tolerates repeated mempool registration.
@@ -130,7 +151,8 @@ static int
mlx5_rxq_mempool_register(struct mlx5_rxq_ctrl *rxq_ctrl)
{
struct rte_mempool *mp;
- uint32_t s;
+ struct mlx5_eth_rxseg *seg;
+ uint16_t s;
int ret = 0;
mlx5_mr_flush_local_cache(&rxq_ctrl->rxq.mr_ctrl);
@@ -139,21 +161,37 @@ mlx5_rxq_mempool_register(struct mlx5_rxq_ctrl *rxq_ctrl)
return mlx5_mr_mempool_populate_cache(&rxq_ctrl->rxq.mr_ctrl,
rxq_ctrl->rxq.mprq_mp);
for (s = 0; s < rxq_ctrl->rxq.rxseg_n; s++) {
- bool is_extmem;
-
- mp = rxq_ctrl->rxq.rxseg[s].mp;
- is_extmem = (rte_pktmbuf_priv_flags(mp) &
+ seg = &rxq_ctrl->rxq.rxseg[s];
+ mp = seg->mp;
+ if (mp) { /* Regular segment */
+ bool is_extmem = (rte_pktmbuf_priv_flags(mp) &
RTE_PKTMBUF_POOL_F_PINNED_EXT_BUF) != 0;
ret = mlx5_mr_mempool_register(rxq_ctrl->sh->cdev, mp,
is_extmem);
if (ret < 0 && rte_errno != EEXIST)
- return ret;
+ goto error;
ret = mlx5_mr_mempool_populate_cache(&rxq_ctrl->rxq.mr_ctrl,
mp);
if (ret < 0)
- return ret;
+ goto error;
+ } else { /* NULL segment used in selective Rx */
+ seg->null_mbuf = mlx5_alloc_null_mbuf(seg->length);
+ if (seg->null_mbuf == NULL) {
+ rte_errno = ENOMEM;
+ ret = -rte_errno;
+ goto error;
+ }
+ }
}
return 0;
+
+error:
+ while (s-- > 0) {
+ seg = &rxq_ctrl->rxq.rxseg[s];
+ mlx5_free(seg->null_mbuf);
+ seg->null_mbuf = NULL;
+ }
+ return ret;
}
/**
--
2.54.0
^ permalink raw reply related
* [PATCH v3 07/10] net/mlx5: reindent previous changes
From: Thomas Monjalon @ 2026-05-28 12:46 UTC (permalink / raw)
To: dev
Cc: Stephen Hemminger, Dariusz Sosnowski, Viacheslav Ovsiienko,
Bing Zhao, Ori Kam, Suanming Mou, Matan Azrad
In-Reply-To: <20260528124758.1984528-1-thomas@monjalon.net>
Fix indent which was left untouched to help reviews.
This must be squashed before merging.
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
drivers/net/mlx5/mlx5_rx.c | 82 ++++++++++++++++-----------------
drivers/net/mlx5/mlx5_rxq.c | 32 ++++++-------
drivers/net/mlx5/mlx5_trigger.c | 18 ++++----
3 files changed, 65 insertions(+), 67 deletions(-)
diff --git a/drivers/net/mlx5/mlx5_rx.c b/drivers/net/mlx5/mlx5_rx.c
index 586bf6c935..ad40511422 100644
--- a/drivers/net/mlx5/mlx5_rx.c
+++ b/drivers/net/mlx5/mlx5_rx.c
@@ -1071,30 +1071,30 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
rte_prefetch0(cqe);
rte_prefetch0(wqe);
if (seg->pool) {
- /* Allocate the buf from the same pool. */
- rep = rte_mbuf_raw_alloc(seg->pool);
- if (unlikely(rep == NULL)) {
- ++rxq->stats.rx_nombuf;
- if (!pkt) {
- /*
- * no buffers before we even started,
- * bail out silently.
- */
+ /* Allocate the buf from the same pool. */
+ rep = rte_mbuf_raw_alloc(seg->pool);
+ if (unlikely(rep == NULL)) {
+ ++rxq->stats.rx_nombuf;
+ if (!pkt) {
+ /*
+ * no buffers before we even started,
+ * bail out silently.
+ */
+ break;
+ }
+ while (pkt != seg) {
+ MLX5_ASSERT(pkt != (*rxq->elts)[idx]);
+ rep = NEXT(pkt);
+ NEXT(pkt) = NULL;
+ NB_SEGS(pkt) = 1;
+ rte_mbuf_raw_free(pkt);
+ pkt = rep;
+ }
+ rq_ci >>= sges_n;
+ ++rq_ci;
+ rq_ci <<= sges_n;
break;
}
- while (pkt != seg) {
- MLX5_ASSERT(pkt != (*rxq->elts)[idx]);
- rep = NEXT(pkt);
- NEXT(pkt) = NULL;
- NB_SEGS(pkt) = 1;
- rte_mbuf_raw_free(pkt);
- pkt = rep;
- }
- rq_ci >>= sges_n;
- ++rq_ci;
- rq_ci <<= sges_n;
- break;
- }
}
if (!pkt) {
cqe = &(*rxq->cqes)[rxq->cq_ci & cqe_mask];
@@ -1103,7 +1103,7 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
if (unlikely(len & MLX5_ERROR_CQE_MASK)) {
/* We drop packets with non-critical errors */
if (seg->pool)
- rte_mbuf_raw_free(rep);
+ rte_mbuf_raw_free(rep);
if (len == MLX5_CRITICAL_ERROR_CQE_RET) {
rq_ci = rxq->rq_ci << sges_n;
break;
@@ -1117,7 +1117,7 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
}
if (len == 0) {
if (seg->pool)
- rte_mbuf_raw_free(rep);
+ rte_mbuf_raw_free(rep);
break;
}
pkt = seg;
@@ -1138,21 +1138,21 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
}
}
if (seg->pool) {
- tail = seg;
- DATA_LEN(rep) = DATA_LEN(seg);
- PKT_LEN(rep) = PKT_LEN(seg);
- SET_DATA_OFF(rep, DATA_OFF(seg));
- PORT(rep) = PORT(seg);
- (*rxq->elts)[idx] = rep;
- /*
- * Fill NIC descriptor with the new buffer. The lkey and size
- * of the buffers are already known, only the buffer address
- * changes.
- */
- wqe->addr = rte_cpu_to_be_64(rte_pktmbuf_mtod(rep, uintptr_t));
- /* If there's only one MR, no need to replace LKey in WQE. */
- if (unlikely(mlx5_mr_btree_len(&rxq->mr_ctrl.cache_bh) > 1))
- wqe->lkey = mlx5_rx_mb2mr(rxq, rep);
+ tail = seg;
+ DATA_LEN(rep) = DATA_LEN(seg);
+ PKT_LEN(rep) = PKT_LEN(seg);
+ SET_DATA_OFF(rep, DATA_OFF(seg));
+ PORT(rep) = PORT(seg);
+ (*rxq->elts)[idx] = rep;
+ /*
+ * Fill NIC descriptor with the new buffer. The lkey and size
+ * of the buffers are already known, only the buffer address
+ * changes.
+ */
+ wqe->addr = rte_cpu_to_be_64(rte_pktmbuf_mtod(rep, uintptr_t));
+ /* If there's only one MR, no need to replace LKey in WQE. */
+ if (unlikely(mlx5_mr_btree_len(&rxq->mr_ctrl.cache_bh) > 1))
+ wqe->lkey = mlx5_rx_mb2mr(rxq, rep);
}
if (len > DATA_LEN(seg)) {
if (seg->pool)
@@ -1163,8 +1163,8 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
continue;
}
if (seg->pool) {
- DATA_LEN(seg) = len;
- data_seg_len += len;
+ DATA_LEN(seg) = len;
+ data_seg_len += len;
}
PKT_LEN(pkt) = RTE_MIN(PKT_LEN(pkt), data_seg_len);
#ifdef MLX5_PMD_SOFT_COUNTERS
diff --git a/drivers/net/mlx5/mlx5_rxq.c b/drivers/net/mlx5/mlx5_rxq.c
index 3fae189fa4..6ca29f7543 100644
--- a/drivers/net/mlx5/mlx5_rxq.c
+++ b/drivers/net/mlx5/mlx5_rxq.c
@@ -152,22 +152,22 @@ rxq_alloc_elts_sprq(struct mlx5_rxq_ctrl *rxq_ctrl)
struct rte_mbuf *buf;
if (seg->mp) {
- buf = rte_pktmbuf_alloc(seg->mp);
- if (buf == NULL) {
- if (rxq_ctrl->share_group == 0)
- DRV_LOG(ERR, "port %u queue %u empty mbuf pool",
- RXQ_PORT_ID(rxq_ctrl),
- rxq_ctrl->rxq.idx);
- else
- DRV_LOG(ERR, "share group %u queue %u empty mbuf pool",
- rxq_ctrl->share_group,
- rxq_ctrl->share_qid);
- rte_errno = ENOMEM;
- goto error;
- }
- /* Only vectored Rx routines rely on headroom size. */
- MLX5_ASSERT(!has_vec_support ||
- DATA_OFF(buf) >= RTE_PKTMBUF_HEADROOM);
+ buf = rte_pktmbuf_alloc(seg->mp);
+ if (buf == NULL) {
+ if (rxq_ctrl->share_group == 0)
+ DRV_LOG(ERR, "port %u queue %u empty mbuf pool",
+ RXQ_PORT_ID(rxq_ctrl),
+ rxq_ctrl->rxq.idx);
+ else
+ DRV_LOG(ERR, "share group %u queue %u empty mbuf pool",
+ rxq_ctrl->share_group,
+ rxq_ctrl->share_qid);
+ rte_errno = ENOMEM;
+ goto error;
+ }
+ /* Only vectored Rx routines rely on headroom size. */
+ MLX5_ASSERT(!has_vec_support ||
+ DATA_OFF(buf) >= RTE_PKTMBUF_HEADROOM);
} else {
buf = seg->null_mbuf;
}
diff --git a/drivers/net/mlx5/mlx5_trigger.c b/drivers/net/mlx5/mlx5_trigger.c
index 5b04d9a234..ac966c51b4 100644
--- a/drivers/net/mlx5/mlx5_trigger.c
+++ b/drivers/net/mlx5/mlx5_trigger.c
@@ -164,16 +164,14 @@ mlx5_rxq_mempool_register(struct mlx5_rxq_ctrl *rxq_ctrl)
seg = &rxq_ctrl->rxq.rxseg[s];
mp = seg->mp;
if (mp) { /* Regular segment */
- bool is_extmem = (rte_pktmbuf_priv_flags(mp) &
- RTE_PKTMBUF_POOL_F_PINNED_EXT_BUF) != 0;
- ret = mlx5_mr_mempool_register(rxq_ctrl->sh->cdev, mp,
- is_extmem);
- if (ret < 0 && rte_errno != EEXIST)
- goto error;
- ret = mlx5_mr_mempool_populate_cache(&rxq_ctrl->rxq.mr_ctrl,
- mp);
- if (ret < 0)
- goto error;
+ bool is_extmem = (rte_pktmbuf_priv_flags(mp) &
+ RTE_PKTMBUF_POOL_F_PINNED_EXT_BUF) != 0;
+ ret = mlx5_mr_mempool_register(rxq_ctrl->sh->cdev, mp, is_extmem);
+ if (ret < 0 && rte_errno != EEXIST)
+ goto error;
+ ret = mlx5_mr_mempool_populate_cache(&rxq_ctrl->rxq.mr_ctrl, mp);
+ if (ret < 0)
+ goto error;
} else { /* NULL segment used in selective Rx */
seg->null_mbuf = mlx5_alloc_null_mbuf(seg->length);
if (seg->null_mbuf == NULL) {
--
2.54.0
^ permalink raw reply related
* [PATCH v3 08/10] common/mlx5: remove callbacks for MR registration
From: Thomas Monjalon @ 2026-05-28 12:46 UTC (permalink / raw)
To: dev
Cc: Stephen Hemminger, Dariusz Sosnowski, Viacheslav Ovsiienko,
Bing Zhao, Ori Kam, Suanming Mou, Matan Azrad, Fan Zhang,
Ashish Gupta
In-Reply-To: <20260528124758.1984528-1-thomas@monjalon.net>
The functions register/unregister for a Memory Region (MR)
were not called directly.
There are only 2 implementations for Linux and Windows,
no need of handling this difference with function pointers.
The callback pointers are replaced with direct calls
and link time decision based on the Operating System.
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
drivers/common/mlx5/linux/mlx5_common_verbs.c | 26 +++----------
drivers/common/mlx5/mlx5_common.c | 6 +--
drivers/common/mlx5/mlx5_common_mr.c | 37 ++++++++-----------
drivers/common/mlx5/mlx5_common_mr.h | 26 +++----------
drivers/common/mlx5/windows/mlx5_common_os.c | 23 ++----------
drivers/compress/mlx5/mlx5_compress.c | 4 +-
drivers/crypto/mlx5/mlx5_crypto.h | 2 -
drivers/crypto/mlx5/mlx5_crypto_gcm.c | 6 +--
drivers/net/mlx5/mlx5.h | 3 +-
drivers/net/mlx5/mlx5_flow_aso.c | 21 +++++------
drivers/net/mlx5/mlx5_flow_hw.c | 11 ++----
drivers/net/mlx5/mlx5_flow_quota.c | 6 +--
drivers/net/mlx5/mlx5_hws_cnt.c | 19 ++++------
13 files changed, 61 insertions(+), 129 deletions(-)
diff --git a/drivers/common/mlx5/linux/mlx5_common_verbs.c b/drivers/common/mlx5/linux/mlx5_common_verbs.c
index 6d44e1f566..5e23c5844d 100644
--- a/drivers/common/mlx5/linux/mlx5_common_verbs.c
+++ b/drivers/common/mlx5/linux/mlx5_common_verbs.c
@@ -106,10 +106,10 @@ mlx5_set_context_attr(struct rte_device *dev, struct ibv_context *ctx)
* @return
* 0 on successful registration, -1 otherwise
*/
-RTE_EXPORT_INTERNAL_SYMBOL(mlx5_common_verbs_reg_mr)
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_reg_mr)
int
-mlx5_common_verbs_reg_mr(void *pd, void *addr, size_t length,
- struct mlx5_pmd_mr *pmd_mr)
+mlx5_os_reg_mr(void *pd, void *addr, size_t length,
+ struct mlx5_pmd_mr *pmd_mr)
{
struct ibv_mr *ibv_mr;
@@ -136,9 +136,9 @@ mlx5_common_verbs_reg_mr(void *pd, void *addr, size_t length,
* pmd_mr struct set with lkey, address, length and pointer to mr object
*
*/
-RTE_EXPORT_INTERNAL_SYMBOL(mlx5_common_verbs_dereg_mr)
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_dereg_mr)
void
-mlx5_common_verbs_dereg_mr(struct mlx5_pmd_mr *pmd_mr)
+mlx5_os_dereg_mr(struct mlx5_pmd_mr *pmd_mr)
{
if (pmd_mr && pmd_mr->obj != NULL) {
claim_zero(mlx5_glue->dereg_mr(pmd_mr->obj));
@@ -146,22 +146,6 @@ mlx5_common_verbs_dereg_mr(struct mlx5_pmd_mr *pmd_mr)
}
}
-/**
- * Set the reg_mr and dereg_mr callbacks.
- *
- * @param[out] reg_mr_cb
- * Pointer to reg_mr func
- * @param[out] dereg_mr_cb
- * Pointer to dereg_mr func
- */
-RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_set_reg_mr_cb)
-void
-mlx5_os_set_reg_mr_cb(mlx5_reg_mr_t *reg_mr_cb, mlx5_dereg_mr_t *dereg_mr_cb)
-{
- *reg_mr_cb = mlx5_common_verbs_reg_mr;
- *dereg_mr_cb = mlx5_common_verbs_dereg_mr;
-}
-
RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_alloc_null_mr)
struct mlx5_pmd_mr *
mlx5_os_alloc_null_mr(struct rte_device *dev, void *pd)
diff --git a/drivers/common/mlx5/mlx5_common.c b/drivers/common/mlx5/mlx5_common.c
index f71dbe4637..87de6d0ff0 100644
--- a/drivers/common/mlx5/mlx5_common.c
+++ b/drivers/common/mlx5/mlx5_common.c
@@ -1135,7 +1135,7 @@ mlx5_common_dev_dma_map(struct rte_device *rte_dev, void *addr,
return -1;
}
mr = mlx5_create_mr_ext(dev->pd, (uintptr_t)addr, len,
- SOCKET_ID_ANY, dev->mr_scache.reg_mr_cb);
+ SOCKET_ID_ANY);
if (!mr) {
DRV_LOG(WARNING, "Device %s unable to DMA map", rte_dev->name);
rte_errno = EINVAL;
@@ -1165,7 +1165,7 @@ mlx5_common_dev_dma_map(struct rte_device *rte_dev, void *addr,
ret = mlx5_mr_expand_cache(&dev->mr_scache, size,
rte_dev->numa_node);
if (ret < 0) {
- mlx5_mr_free(mr, dev->mr_scache.dereg_mr_cb);
+ mlx5_mr_free(mr);
rte_errno = ret;
return -1;
}
@@ -1221,7 +1221,7 @@ mlx5_common_dev_dma_unmap(struct rte_device *rte_dev, void *addr,
}
LIST_REMOVE(mr, mr);
DRV_LOG(DEBUG, "MR(%p) is removed from list.", (void *)mr);
- mlx5_mr_free(mr, dev->mr_scache.dereg_mr_cb);
+ mlx5_mr_free(mr);
mlx5_mr_rebuild_cache(&dev->mr_scache);
/*
* No explicit wmb is needed after updating dev_gen due to
diff --git a/drivers/common/mlx5/mlx5_common_mr.c b/drivers/common/mlx5/mlx5_common_mr.c
index 64ffc7f4ea..aa2d5e88a4 100644
--- a/drivers/common/mlx5/mlx5_common_mr.c
+++ b/drivers/common/mlx5/mlx5_common_mr.c
@@ -492,12 +492,12 @@ mlx5_mr_lookup_cache(struct mlx5_mr_share_cache *share_cache,
* Pointer to MR to free.
*/
void
-mlx5_mr_free(struct mlx5_mr *mr, mlx5_dereg_mr_t dereg_mr_cb)
+mlx5_mr_free(struct mlx5_mr *mr)
{
if (mr == NULL)
return;
DRV_LOG(DEBUG, "freeing MR(%p):", (void *)mr);
- dereg_mr_cb(&mr->pmd_mr);
+ mlx5_os_dereg_mr(&mr->pmd_mr);
rte_bitmap_free(mr->ms_bmp);
mlx5_free(mr);
}
@@ -545,7 +545,7 @@ mlx5_mr_garbage_collect(struct mlx5_mr_share_cache *share_cache)
struct mlx5_mr *mr = mr_next;
mr_next = LIST_NEXT(mr, mr);
- mlx5_mr_free(mr, share_cache->dereg_mr_cb);
+ mlx5_mr_free(mr);
}
}
@@ -821,7 +821,7 @@ mlx5_mr_create_primary(void *pd,
data.start = RTE_ALIGN_FLOOR(addr, msl->page_sz);
data.end = data.start + msl->page_sz;
rte_mcfg_mem_read_unlock();
- mlx5_mr_free(mr, share_cache->dereg_mr_cb);
+ mlx5_mr_free(mr);
goto alloc_resources;
}
MLX5_ASSERT(data.msl == data_re.msl);
@@ -845,7 +845,7 @@ mlx5_mr_create_primary(void *pd,
* Must be unlocked before calling rte_free() because
* mlx5_mr_mem_event_free_cb() can be called inside.
*/
- mlx5_mr_free(mr, share_cache->dereg_mr_cb);
+ mlx5_mr_free(mr);
return entry->lkey;
}
/*
@@ -912,7 +912,7 @@ mlx5_mr_create_primary(void *pd,
* mlx5_alloc_buf_extern() which eventually calls rte_malloc_socket()
* through mlx5_alloc_verbs_buf().
*/
- share_cache->reg_mr_cb(pd, (void *)data.start, len, &mr->pmd_mr);
+ mlx5_os_reg_mr(pd, (void *)data.start, len, &mr->pmd_mr);
if (mr->pmd_mr.obj == NULL) {
DRV_LOG(DEBUG, "Fail to create an MR for address (%p)",
(void *)addr);
@@ -948,7 +948,7 @@ mlx5_mr_create_primary(void *pd,
* calling rte_free() because mlx5_mr_mem_event_free_cb() can be called
* inside.
*/
- mlx5_mr_free(mr, share_cache->dereg_mr_cb);
+ mlx5_mr_free(mr);
return UINT32_MAX;
}
@@ -1139,9 +1139,6 @@ mlx5_mr_release_cache(struct mlx5_mr_share_cache *share_cache)
int
mlx5_mr_create_cache(struct mlx5_mr_share_cache *share_cache, int socket)
{
- /* Set the reg_mr and dereg_mr callback functions */
- mlx5_os_set_reg_mr_cb(&share_cache->reg_mr_cb,
- &share_cache->dereg_mr_cb);
rte_rwlock_init(&share_cache->rwlock);
rte_rwlock_init(&share_cache->mprwlock);
/* Initialize B-tree and allocate memory for global MR cache table. */
@@ -1189,8 +1186,7 @@ mlx5_mr_flush_local_cache(struct mlx5_mr_ctrl *mr_ctrl)
* Pointer to MR structure on success, NULL otherwise.
*/
struct mlx5_mr *
-mlx5_create_mr_ext(void *pd, uintptr_t addr, size_t len, int socket_id,
- mlx5_reg_mr_t reg_mr_cb)
+mlx5_create_mr_ext(void *pd, uintptr_t addr, size_t len, int socket_id)
{
struct mlx5_mr *mr = NULL;
@@ -1199,7 +1195,7 @@ mlx5_create_mr_ext(void *pd, uintptr_t addr, size_t len, int socket_id,
RTE_CACHE_LINE_SIZE, socket_id);
if (mr == NULL)
return NULL;
- reg_mr_cb(pd, (void *)addr, len, &mr->pmd_mr);
+ mlx5_os_reg_mr(pd, (void *)addr, len, &mr->pmd_mr);
if (mr->pmd_mr.obj == NULL) {
DRV_LOG(WARNING,
"Fail to create MR for address (%p)",
@@ -1624,14 +1620,13 @@ mlx5_mempool_reg_create(struct rte_mempool *mp, unsigned int mrs_n,
* Whether @p mpr owns its MRs exclusively, i.e. they are not shared.
*/
static void
-mlx5_mempool_reg_destroy(struct mlx5_mr_share_cache *share_cache,
- struct mlx5_mempool_reg *mpr, bool standalone)
+mlx5_mempool_reg_destroy(struct mlx5_mempool_reg *mpr, bool standalone)
{
if (standalone) {
unsigned int i;
for (i = 0; i < mpr->mrs_n; i++)
- share_cache->dereg_mr_cb(&mpr->mrs[i].pmd_mr);
+ mlx5_os_dereg_mr(&mpr->mrs[i].pmd_mr);
mlx5_free(mpr->mrs);
}
mlx5_free(mpr);
@@ -1748,7 +1743,7 @@ mlx5_mr_mempool_register_primary(struct mlx5_mr_share_cache *share_cache,
const struct mlx5_range *range = &ranges[i];
size_t len = range->end - range->start;
- if (share_cache->reg_mr_cb(pd, (void *)range->start, len,
+ if (mlx5_os_reg_mr(pd, (void *)range->start, len,
&mr->pmd_mr) < 0) {
DRV_LOG(ERR,
"Failed to create an MR in PD %p for address range "
@@ -1763,7 +1758,7 @@ mlx5_mr_mempool_register_primary(struct mlx5_mr_share_cache *share_cache,
mp->name);
}
if (i != ranges_n) {
- mlx5_mempool_reg_destroy(share_cache, new_mpr, true);
+ mlx5_mempool_reg_destroy(new_mpr, true);
rte_errno = EINVAL;
goto exit;
}
@@ -1785,13 +1780,13 @@ mlx5_mr_mempool_register_primary(struct mlx5_mr_share_cache *share_cache,
if (mpr != NULL) {
DRV_LOG(DEBUG, "Mempool %s is already registered for PD %p",
mp->name, pd);
- mlx5_mempool_reg_destroy(share_cache, new_mpr, true);
+ mlx5_mempool_reg_destroy(new_mpr, true);
rte_errno = EEXIST;
goto exit;
} else if (old_mpr != NULL) {
DRV_LOG(DEBUG, "Mempool %s registration for PD %p updated for external memory",
mp->name, pd);
- mlx5_mempool_reg_destroy(share_cache, old_mpr, standalone);
+ mlx5_mempool_reg_destroy(old_mpr, standalone);
}
exit:
free(ranges);
@@ -1860,7 +1855,7 @@ mlx5_mr_mempool_unregister_primary(struct mlx5_mr_share_cache *share_cache,
rte_errno = ENOENT;
return -1;
}
- mlx5_mempool_reg_destroy(share_cache, mpr, standalone);
+ mlx5_mempool_reg_destroy(mpr, standalone);
return 0;
}
diff --git a/drivers/common/mlx5/mlx5_common_mr.h b/drivers/common/mlx5/mlx5_common_mr.h
index 00f3d832c3..5fb931a1b5 100644
--- a/drivers/common/mlx5/mlx5_common_mr.h
+++ b/drivers/common/mlx5/mlx5_common_mr.h
@@ -32,13 +32,6 @@ struct mlx5_pmd_mr {
struct mlx5_devx_obj *mkey; /* devx mkey object. */
};
-/**
- * mr operations typedef
- */
-typedef int (*mlx5_reg_mr_t)(void *pd, void *addr, size_t length,
- struct mlx5_pmd_mr *pmd_mr);
-typedef void (*mlx5_dereg_mr_t)(struct mlx5_pmd_mr *pmd_mr);
-
/* Memory Region object. */
struct mlx5_mr {
LIST_ENTRY(mlx5_mr) mr; /**< Pointer to the prev/next entry. */
@@ -88,8 +81,6 @@ struct __rte_packed_begin mlx5_mr_share_cache {
struct mlx5_mr_list mr_list; /* Registered MR list. */
struct mlx5_mr_list mr_free_list; /* Freed MR list. */
struct mlx5_mempool_reg_list mempool_reg_list; /* Mempool database. */
- mlx5_reg_mr_t reg_mr_cb; /* Callback to reg_mr func */
- mlx5_dereg_mr_t dereg_mr_cb; /* Callback to dereg_mr func */
} __rte_packed_end;
/* Multi-Packet RQ buffer header. */
@@ -233,9 +224,8 @@ struct mlx5_mr *
mlx5_mr_lookup_list(struct mlx5_mr_share_cache *share_cache,
struct mr_cache_entry *entry, uintptr_t addr);
struct mlx5_mr *
-mlx5_create_mr_ext(void *pd, uintptr_t addr, size_t len, int socket_id,
- mlx5_reg_mr_t reg_mr_cb);
-void mlx5_mr_free(struct mlx5_mr *mr, mlx5_dereg_mr_t dereg_mr_cb);
+mlx5_create_mr_ext(void *pd, uintptr_t addr, size_t len, int socket_id);
+void mlx5_mr_free(struct mlx5_mr *mr);
__rte_internal
uint32_t
mlx5_mr_create(struct mlx5_common_device *cdev,
@@ -246,19 +236,13 @@ __rte_internal
uint32_t
mlx5_mr_addr2mr_bh(struct mlx5_mr_ctrl *mr_ctrl, uintptr_t addr);
-/* mlx5_common_verbs.c */
-
__rte_internal
int
-mlx5_common_verbs_reg_mr(void *pd, void *addr, size_t length,
- struct mlx5_pmd_mr *pmd_mr);
+mlx5_os_reg_mr(void *pd, void *addr, size_t length,
+ struct mlx5_pmd_mr *pmd_mr);
__rte_internal
void
-mlx5_common_verbs_dereg_mr(struct mlx5_pmd_mr *pmd_mr);
-
-__rte_internal
-void
-mlx5_os_set_reg_mr_cb(mlx5_reg_mr_t *reg_mr_cb, mlx5_dereg_mr_t *dereg_mr_cb);
+mlx5_os_dereg_mr(struct mlx5_pmd_mr *pmd_mr);
__rte_internal
struct mlx5_pmd_mr *
diff --git a/drivers/common/mlx5/windows/mlx5_common_os.c b/drivers/common/mlx5/windows/mlx5_common_os.c
index 692517a9bf..bf1b654da3 100644
--- a/drivers/common/mlx5/windows/mlx5_common_os.c
+++ b/drivers/common/mlx5/windows/mlx5_common_os.c
@@ -377,7 +377,8 @@ mlx5_os_umem_dereg(void *pumem)
* @return
* 0 on successful registration, -1 otherwise
*/
-static int
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_reg_mr)
+int
mlx5_os_reg_mr(void *pd,
void *addr, size_t length, struct mlx5_pmd_mr *pmd_mr)
{
@@ -425,7 +426,8 @@ mlx5_os_reg_mr(void *pd,
* @param[in] pmd_mr
* Pointer to PMD mr object
*/
-static void
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_dereg_mr)
+void
mlx5_os_dereg_mr(struct mlx5_pmd_mr *pmd_mr)
{
if (!pmd_mr)
@@ -437,23 +439,6 @@ mlx5_os_dereg_mr(struct mlx5_pmd_mr *pmd_mr)
memset(pmd_mr, 0, sizeof(*pmd_mr));
}
-/**
- * Set the reg_mr and dereg_mr callbacks.
- *
- * @param[out] reg_mr_cb
- * Pointer to reg_mr func
- * @param[out] dereg_mr_cb
- * Pointer to dereg_mr func
- *
- */
-RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_set_reg_mr_cb)
-void
-mlx5_os_set_reg_mr_cb(mlx5_reg_mr_t *reg_mr_cb, mlx5_dereg_mr_t *dereg_mr_cb)
-{
- *reg_mr_cb = mlx5_os_reg_mr;
- *dereg_mr_cb = mlx5_os_dereg_mr;
-}
-
RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_alloc_null_mr)
struct mlx5_pmd_mr *
mlx5_os_alloc_null_mr(struct rte_device *dev, void *pd)
diff --git a/drivers/compress/mlx5/mlx5_compress.c b/drivers/compress/mlx5/mlx5_compress.c
index e5325c6150..1361dab630 100644
--- a/drivers/compress/mlx5/mlx5_compress.c
+++ b/drivers/compress/mlx5/mlx5_compress.c
@@ -117,7 +117,7 @@ mlx5_compress_qp_release(struct rte_compressdev *dev, uint16_t qp_id)
if (qp->opaque_mr.obj != NULL) {
void *opaq = qp->opaque_mr.addr;
- mlx5_common_verbs_dereg_mr(&qp->opaque_mr);
+ mlx5_os_dereg_mr(&qp->opaque_mr);
rte_free(opaq);
}
mlx5_mr_btree_free(&qp->mr_ctrl.cache_bh);
@@ -199,7 +199,7 @@ mlx5_compress_qp_setup(struct rte_compressdev *dev, uint16_t qp_id,
qp->priv = priv;
qp->ops = (struct rte_comp_op **)RTE_ALIGN((uintptr_t)(qp + 1),
RTE_CACHE_LINE_SIZE);
- if (mlx5_common_verbs_reg_mr(priv->cdev->pd, opaq_buf, qp->entries_n *
+ if (mlx5_os_reg_mr(priv->cdev->pd, opaq_buf, qp->entries_n *
sizeof(union mlx5_gga_compress_opaque),
&qp->opaque_mr) != 0) {
rte_free(opaq_buf);
diff --git a/drivers/crypto/mlx5/mlx5_crypto.h b/drivers/crypto/mlx5/mlx5_crypto.h
index f9f127e9e6..93a2bb2c78 100644
--- a/drivers/crypto/mlx5/mlx5_crypto.h
+++ b/drivers/crypto/mlx5/mlx5_crypto.h
@@ -40,8 +40,6 @@ struct mlx5_crypto_priv {
TAILQ_ENTRY(mlx5_crypto_priv) next;
struct mlx5_common_device *cdev; /* Backend mlx5 device. */
struct rte_cryptodev *crypto_dev;
- mlx5_reg_mr_t reg_mr_cb; /* Callback to reg_mr func */
- mlx5_dereg_mr_t dereg_mr_cb; /* Callback to dereg_mr func */
struct mlx5_uar uar; /* User Access Region. */
uint32_t max_segs_num; /* Maximum supported data segs. */
uint32_t max_klm_num; /* Maximum supported klm. */
diff --git a/drivers/crypto/mlx5/mlx5_crypto_gcm.c b/drivers/crypto/mlx5/mlx5_crypto_gcm.c
index 89f32c7722..1a2600655a 100644
--- a/drivers/crypto/mlx5/mlx5_crypto_gcm.c
+++ b/drivers/crypto/mlx5/mlx5_crypto_gcm.c
@@ -219,7 +219,6 @@ mlx5_crypto_gcm_mkey_klm_update(struct mlx5_crypto_priv *priv,
static int
mlx5_crypto_gcm_qp_release(struct rte_cryptodev *dev, uint16_t qp_id)
{
- struct mlx5_crypto_priv *priv = dev->data->dev_private;
struct mlx5_crypto_qp *qp = dev->data->queue_pairs[qp_id];
if (qp->umr_qp_obj.qp != NULL)
@@ -231,7 +230,7 @@ mlx5_crypto_gcm_qp_release(struct rte_cryptodev *dev, uint16_t qp_id)
if (qp->mr.obj != NULL) {
void *opaq = qp->mr.addr;
- priv->dereg_mr_cb(&qp->mr);
+ mlx5_os_dereg_mr(&qp->mr);
rte_free(opaq);
}
mlx5_crypto_indirect_mkeys_release(qp, qp->entries_n);
@@ -363,7 +362,7 @@ mlx5_crypto_gcm_qp_setup(struct rte_cryptodev *dev, uint16_t qp_id,
rte_errno = ENOMEM;
goto err;
}
- if (priv->reg_mr_cb(priv->cdev->pd, mr_buf, mr_size, &qp->mr) != 0) {
+ if (mlx5_os_reg_mr(priv->cdev->pd, mr_buf, mr_size, &qp->mr) != 0) {
rte_free(mr_buf);
DRV_LOG(ERR, "Failed to register opaque MR.");
rte_errno = ENOMEM;
@@ -1186,7 +1185,6 @@ mlx5_crypto_gcm_init(struct mlx5_crypto_priv *priv)
/* Override AES-GCM specified ops. */
dev_ops->sym_session_configure = mlx5_crypto_sym_gcm_session_configure;
- mlx5_os_set_reg_mr_cb(&priv->reg_mr_cb, &priv->dereg_mr_cb);
dev_ops->queue_pair_setup = mlx5_crypto_gcm_qp_setup;
dev_ops->queue_pair_release = mlx5_crypto_gcm_qp_release;
if (mlx5_crypto_is_ipsec_opt(priv)) {
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index bd6ef35b53..a4d5392e8f 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -2706,8 +2706,7 @@ int mlx5_aso_cnt_query(struct mlx5_dev_ctx_shared *sh,
int mlx5_aso_ct_queue_init(struct mlx5_dev_ctx_shared *sh,
struct mlx5_aso_ct_pools_mng *ct_mng,
uint32_t nb_queues);
-int mlx5_aso_ct_queue_uninit(struct mlx5_dev_ctx_shared *sh,
- struct mlx5_aso_ct_pools_mng *ct_mng);
+int mlx5_aso_ct_queue_uninit(struct mlx5_aso_ct_pools_mng *ct_mng);
int
mlx5_aso_sq_create(struct mlx5_common_device *cdev, struct mlx5_aso_sq *sq,
void *uar, uint16_t log_desc_n);
diff --git a/drivers/net/mlx5/mlx5_flow_aso.c b/drivers/net/mlx5/mlx5_flow_aso.c
index 5e2a81ef9c..cd84ab1966 100644
--- a/drivers/net/mlx5/mlx5_flow_aso.c
+++ b/drivers/net/mlx5/mlx5_flow_aso.c
@@ -19,17 +19,15 @@
/**
* Free MR resources.
*
- * @param[in] cdev
- * Pointer to the mlx5 common device.
* @param[in] mr
* MR to free.
*/
static void
-mlx5_aso_dereg_mr(struct mlx5_common_device *cdev, struct mlx5_pmd_mr *mr)
+mlx5_aso_dereg_mr(struct mlx5_pmd_mr *mr)
{
void *addr = mr->addr;
- cdev->mr_scache.dereg_mr_cb(mr);
+ mlx5_os_dereg_mr(mr);
mlx5_free(addr);
memset(mr, 0, sizeof(*mr));
}
@@ -59,7 +57,7 @@ mlx5_aso_reg_mr(struct mlx5_common_device *cdev, size_t length,
DRV_LOG(ERR, "Failed to create ASO bits mem for MR.");
return -1;
}
- ret = cdev->mr_scache.reg_mr_cb(cdev->pd, mr->addr, length, mr);
+ ret = mlx5_os_reg_mr(cdev->pd, mr->addr, length, mr);
if (ret) {
DRV_LOG(ERR, "Failed to create direct Mkey.");
mlx5_free(mr->addr);
@@ -362,7 +360,7 @@ mlx5_aso_queue_init(struct mlx5_dev_ctx_shared *sh,
if (mlx5_aso_sq_create(cdev, &sh->aso_age_mng->aso_sq,
sh->tx_uar.obj,
MLX5_ASO_QUEUE_LOG_DESC)) {
- mlx5_aso_dereg_mr(cdev, &sh->aso_age_mng->aso_sq.mr);
+ mlx5_aso_dereg_mr(&sh->aso_age_mng->aso_sq.mr);
return -1;
}
mlx5_aso_age_init_sq(&sh->aso_age_mng->aso_sq);
@@ -399,14 +397,14 @@ mlx5_aso_queue_uninit(struct mlx5_dev_ctx_shared *sh,
switch (aso_opc_mod) {
case ASO_OPC_MOD_FLOW_HIT:
- mlx5_aso_dereg_mr(sh->cdev, &sh->aso_age_mng->aso_sq.mr);
+ mlx5_aso_dereg_mr(&sh->aso_age_mng->aso_sq.mr);
sq = &sh->aso_age_mng->aso_sq;
break;
case ASO_OPC_MOD_POLICER:
mlx5_aso_mtr_queue_uninit(sh, NULL, &sh->mtrmng->pools_mng);
break;
case ASO_OPC_MOD_CONNECTION_TRACKING:
- mlx5_aso_ct_queue_uninit(sh, sh->ct_mng);
+ mlx5_aso_ct_queue_uninit(sh->ct_mng);
break;
default:
DRV_LOG(ERR, "Unknown ASO operation mode");
@@ -1147,15 +1145,14 @@ __mlx5_aso_ct_get_pool(struct mlx5_dev_ctx_shared *sh,
}
int
-mlx5_aso_ct_queue_uninit(struct mlx5_dev_ctx_shared *sh,
- struct mlx5_aso_ct_pools_mng *ct_mng)
+mlx5_aso_ct_queue_uninit(struct mlx5_aso_ct_pools_mng *ct_mng)
{
uint32_t i;
/* 64B per object for query. */
for (i = 0; i < ct_mng->nb_sq; i++) {
if (ct_mng->aso_sqs[i].mr.addr)
- mlx5_aso_dereg_mr(sh->cdev, &ct_mng->aso_sqs[i].mr);
+ mlx5_aso_dereg_mr(&ct_mng->aso_sqs[i].mr);
mlx5_aso_destroy_sq(&ct_mng->aso_sqs[i]);
}
return 0;
@@ -1197,7 +1194,7 @@ mlx5_aso_ct_queue_init(struct mlx5_dev_ctx_shared *sh,
error:
do {
if (ct_mng->aso_sqs[i].mr.addr)
- mlx5_aso_dereg_mr(sh->cdev, &ct_mng->aso_sqs[i].mr);
+ mlx5_aso_dereg_mr(&ct_mng->aso_sqs[i].mr);
mlx5_aso_destroy_sq(&ct_mng->aso_sqs[i]);
} while (i--);
ct_mng->nb_sq = 0;
diff --git a/drivers/net/mlx5/mlx5_flow_hw.c b/drivers/net/mlx5/mlx5_flow_hw.c
index b6bb9f12a6..7cc601d681 100644
--- a/drivers/net/mlx5/mlx5_flow_hw.c
+++ b/drivers/net/mlx5/mlx5_flow_hw.c
@@ -11086,12 +11086,9 @@ flow_hw_create_nic_ctrl_tables(struct rte_eth_dev *dev, struct rte_flow_error *e
}
static void
-flow_hw_ct_mng_destroy(struct rte_eth_dev *dev,
- struct mlx5_aso_ct_pools_mng *ct_mng)
+flow_hw_ct_mng_destroy(struct mlx5_aso_ct_pools_mng *ct_mng)
{
- struct mlx5_priv *priv = dev->data->dev_private;
-
- mlx5_aso_ct_queue_uninit(priv->sh, ct_mng);
+ mlx5_aso_ct_queue_uninit(ct_mng);
mlx5_free(ct_mng);
}
@@ -11230,7 +11227,7 @@ mlx5_flow_ct_init(struct rte_eth_dev *dev,
priv->hws_ctpool = NULL;
}
if (priv->ct_mng) {
- flow_hw_ct_mng_destroy(dev, priv->ct_mng);
+ flow_hw_ct_mng_destroy(priv->ct_mng);
priv->ct_mng = NULL;
}
return ret;
@@ -11804,7 +11801,7 @@ __mlx5_flow_hw_resource_release(struct rte_eth_dev *dev, bool ctx_close)
priv->hws_ctpool = NULL;
}
if (priv->ct_mng) {
- flow_hw_ct_mng_destroy(dev, priv->ct_mng);
+ flow_hw_ct_mng_destroy(priv->ct_mng);
priv->ct_mng = NULL;
}
mlx5_flow_quota_destroy(dev);
diff --git a/drivers/net/mlx5/mlx5_flow_quota.c b/drivers/net/mlx5/mlx5_flow_quota.c
index d94167d0b0..b661bd376e 100644
--- a/drivers/net/mlx5/mlx5_flow_quota.c
+++ b/drivers/net/mlx5/mlx5_flow_quota.c
@@ -412,12 +412,11 @@ mlx5_quota_alloc_sq(struct mlx5_priv *priv)
static void
mlx5_quota_destroy_read_buf(struct mlx5_priv *priv)
{
- struct mlx5_dev_ctx_shared *sh = priv->sh;
struct mlx5_quota_ctx *qctx = &priv->quota_ctx;
if (qctx->mr.lkey) {
void *addr = qctx->mr.addr;
- sh->cdev->mr_scache.dereg_mr_cb(&qctx->mr);
+ mlx5_os_dereg_mr(&qctx->mr);
mlx5_free(addr);
}
if (qctx->read_buf)
@@ -446,8 +445,7 @@ mlx5_quota_alloc_read_buf(struct mlx5_priv *priv)
DRV_LOG(DEBUG, "QUOTA: failed to allocate MTR ASO READ buffer [1]");
return -ENOMEM;
}
- ret = sh->cdev->mr_scache.reg_mr_cb(sh->cdev->pd, buf,
- rd_buf_size, &qctx->mr);
+ ret = mlx5_os_reg_mr(sh->cdev->pd, buf, rd_buf_size, &qctx->mr);
if (ret) {
DRV_LOG(DEBUG, "QUOTA: failed to register MTR ASO READ MR");
return -errno;
diff --git a/drivers/net/mlx5/mlx5_hws_cnt.c b/drivers/net/mlx5/mlx5_hws_cnt.c
index 1b6acb7a3b..d0c4ead71b 100644
--- a/drivers/net/mlx5/mlx5_hws_cnt.c
+++ b/drivers/net/mlx5/mlx5_hws_cnt.c
@@ -259,12 +259,11 @@ mlx5_hws_aging_check(struct mlx5_priv *priv, struct mlx5_hws_cnt_pool *cpool)
}
static void
-mlx5_hws_cnt_raw_data_free(struct mlx5_dev_ctx_shared *sh,
- struct mlx5_hws_cnt_raw_data_mng *mng)
+mlx5_hws_cnt_raw_data_free(struct mlx5_hws_cnt_raw_data_mng *mng)
{
if (mng == NULL)
return;
- sh->cdev->mr_scache.dereg_mr_cb(&mng->mr);
+ mlx5_os_dereg_mr(&mng->mr);
mlx5_free(mng->raw);
mlx5_free(mng);
}
@@ -296,8 +295,7 @@ mlx5_hws_cnt_raw_data_alloc(struct mlx5_dev_ctx_shared *sh, uint32_t n,
NULL, "failed to allocate raw counters memory");
goto error;
}
- ret = sh->cdev->mr_scache.reg_mr_cb(sh->cdev->pd, mng->raw, sz,
- &mng->mr);
+ ret = mlx5_os_reg_mr(sh->cdev->pd, mng->raw, sz, &mng->mr);
if (ret) {
rte_flow_error_set(error, errno,
RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
@@ -306,7 +304,7 @@ mlx5_hws_cnt_raw_data_alloc(struct mlx5_dev_ctx_shared *sh, uint32_t n,
}
return mng;
error:
- mlx5_hws_cnt_raw_data_free(sh, mng);
+ mlx5_hws_cnt_raw_data_free(mng);
return NULL;
}
@@ -639,8 +637,7 @@ mlx5_hws_cnt_pool_dcs_alloc(struct mlx5_dev_ctx_shared *sh,
}
static void
-mlx5_hws_cnt_pool_dcs_free(struct mlx5_dev_ctx_shared *sh,
- struct mlx5_hws_cnt_pool *cpool)
+mlx5_hws_cnt_pool_dcs_free(struct mlx5_hws_cnt_pool *cpool)
{
uint32_t idx;
@@ -649,7 +646,7 @@ mlx5_hws_cnt_pool_dcs_free(struct mlx5_dev_ctx_shared *sh,
for (idx = 0; idx < MLX5_HWS_CNT_DCS_NUM; idx++)
mlx5_devx_cmd_destroy(cpool->dcs_mng.dcs[idx].obj);
if (cpool->raw_mng) {
- mlx5_hws_cnt_raw_data_free(sh, cpool->raw_mng);
+ mlx5_hws_cnt_raw_data_free(cpool->raw_mng);
cpool->raw_mng = NULL;
}
}
@@ -842,8 +839,8 @@ mlx5_hws_cnt_pool_destroy(struct mlx5_dev_ctx_shared *sh,
}
mlx5_hws_cnt_pool_action_destroy(cpool);
if (cpool->cfg.host_cpool == NULL) {
- mlx5_hws_cnt_pool_dcs_free(sh, cpool);
- mlx5_hws_cnt_raw_data_free(sh, cpool->raw_mng);
+ mlx5_hws_cnt_pool_dcs_free(cpool);
+ mlx5_hws_cnt_raw_data_free(cpool->raw_mng);
}
mlx5_free((void *)cpool->cfg.name);
mlx5_hws_cnt_pool_deinit(cpool);
--
2.54.0
^ permalink raw reply related
* [PATCH v3 09/10] dts: fix topology capability comparison
From: Thomas Monjalon @ 2026-05-28 12:46 UTC (permalink / raw)
To: dev
Cc: Stephen Hemminger, stable, Luca Vizzarro, Patrick Robb, Dean Marx,
Jeremy Spewock, Juraj Linkeš
In-Reply-To: <20260528124758.1984528-1-thomas@monjalon.net>
TopologyCapability.__gt__() was delegating to __lt__(),
which caused infinite recursion when "other" is not a TopologyCapability:
other.__lt__(self) returns NotImplemented,
Python retries with self.__gt__(other),
and the cycle repeats.
dts/framework/testbed_model/capability.py", line 579, in __gt__
return other < self
^^^^^^^^^^^^
RecursionError: maximum recursion depth exceeded
Similarly, __le__() was delegating to "not __gt__()",
which returns True for non-comparable types instead of False.
Fix both by checking is_comparable_with() first
and comparing topology_type directly, consistent with __lt__().
Fixes: 039256daa8bf ("dts: add topology capability")
Cc: stable@dpdk.org
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
dts/framework/testbed_model/capability.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py
index 960370fc72..96e1cd449f 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/framework/testbed_model/capability.py
@@ -574,7 +574,9 @@ def __gt__(self, other: Any) -> bool:
Returns:
:data:`True` if the instance's topology type is more complex than the compared object's.
"""
- return other < self
+ if not self.is_comparable_with(other):
+ return False
+ return self.topology_type > other.topology_type
def __le__(self, other: Any) -> bool:
"""Compare the :attr:`~TopologyCapability.topology_type`s.
@@ -586,7 +588,9 @@ def __le__(self, other: Any) -> bool:
:data:`True` if the instance's topology type is less complex or equal than
the compared object's.
"""
- return not self > other
+ if not self.is_comparable_with(other):
+ return False
+ return self.topology_type <= other.topology_type
def __hash__(self):
"""Each instance is identified by :attr:`topology_type`."""
--
2.54.0
^ permalink raw reply related
* [PATCH v3 10/10] dts: add selective Rx tests
From: Thomas Monjalon @ 2026-05-28 12:46 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, Luca Vizzarro, Patrick Robb
In-Reply-To: <20260528124758.1984528-1-thomas@monjalon.net>
Add TestSuite_rx_split with 7 test cases:
- 3 positive: headers only, payload only, two non-contiguous segments
- 4 negative: missing offload flag, out-of-range, overlap, all-discard
Add selective Rx capability detection via testpmd "show port info".
The test suite could be completed later for the basic buffer split
configuration based on offsets or protocols.
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
dts/api/capabilities.py | 2 +
dts/api/testpmd/__init__.py | 17 ++
dts/api/testpmd/types.py | 6 +
dts/framework/testbed_model/capability.py | 2 +
dts/tests/TestSuite_rx_split.py | 262 ++++++++++++++++++++++
5 files changed, 289 insertions(+)
create mode 100644 dts/tests/TestSuite_rx_split.py
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 09bc538523..b0c1d81d36 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -136,6 +136,8 @@ class NicCapability(IntEnum):
#: Device supports all VLAN capabilities.
PORT_RX_OFFLOAD_VLAN = auto()
QUEUE_RX_OFFLOAD_VLAN = auto()
+ #: Device supports selective Rx.
+ SELECTIVE_RX = auto()
#: Device supports Rx queue setup after device started.
RUNTIME_RX_QUEUE_SETUP = auto()
#: Device supports Tx queue setup after device started.
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index e9187440bb..6973a64573 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -1409,6 +1409,23 @@ def get_capabilities_show_port_info(
self.ports[0].device_capabilities,
)
+ def get_capabilities_selective_rx(
+ self,
+ supported_capabilities: MutableSet["NicCapability"],
+ unsupported_capabilities: MutableSet["NicCapability"],
+ ) -> None:
+ """Get selective Rx capability from show port info.
+
+ Args:
+ supported_capabilities: Supported capabilities will be added to this set.
+ unsupported_capabilities: Unsupported capabilities will be added to this set.
+ """
+ port_info = self.show_port_info(self.ports[0].id)
+ if port_info.selective_rx:
+ supported_capabilities.add(NicCapability.SELECTIVE_RX)
+ else:
+ unsupported_capabilities.add(NicCapability.SELECTIVE_RX)
+
def get_capabilities_mcast_filtering(
self,
supported_capabilities: MutableSet["NicCapability"],
diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py
index 0d322aece2..6f1eaf47cc 100644
--- a/dts/api/testpmd/types.py
+++ b/dts/api/testpmd/types.py
@@ -614,6 +614,12 @@ def _validate(info: str) -> str | None:
metadata=VLANOffloadFlag.make_parser(),
)
+ #: Selective Rx support
+ selective_rx: bool = field(
+ default=False,
+ metadata=TextParser.find(r"Selective Rx: supported"),
+ )
+
#: Maximum size of RX buffer
max_rx_bufsize: int | None = field(
default=None, metadata=TextParser.find_int(r"Maximum size of RX buffer: (\d+)")
diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py
index 96e1cd449f..b10799ea4b 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/framework/testbed_model/capability.py
@@ -324,6 +324,8 @@ def mapping(cap: NicCapability) -> TestPmdNicCapability:
| NicCapability.FLOW_SHARED_OBJECT_KEEP
):
return (TestPmd.get_capabilities_show_port_info, None)
+ case NicCapability.SELECTIVE_RX:
+ return (TestPmd.get_capabilities_selective_rx, None)
case NicCapability.MCAST_FILTERING:
return (TestPmd.get_capabilities_mcast_filtering, None)
case NicCapability.FLOW_CTRL:
diff --git a/dts/tests/TestSuite_rx_split.py b/dts/tests/TestSuite_rx_split.py
new file mode 100644
index 0000000000..42ff70fe64
--- /dev/null
+++ b/dts/tests/TestSuite_rx_split.py
@@ -0,0 +1,262 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2026 NVIDIA Corporation & Affiliates
+
+"""Rx split test suite.
+
+Test configuring a packet split on Rx,
+and discarding some segments (selective Rx) at NIC level.
+"""
+
+from typing import Any
+
+from scapy.layers.inet import IP
+from scapy.layers.l2 import Ether
+from scapy.packet import Raw
+
+from api.capabilities import (
+ NicCapability,
+ requires_nic_capability,
+)
+from api.packet import send_packet_and_capture
+from api.test import fail, verify
+from api.testpmd import TestPmd
+from api.testpmd.config import SimpleForwardingModes
+from api.testpmd.types import RxOffloadCapability, TxOffloadCapability
+from framework.exception import InteractiveCommandExecutionError
+from framework.test_suite import TestSuite, func_test
+
+PAYLOAD = bytes(range(256))
+ETHER_HDR_LEN = len(Ether())
+IP_HDR_LEN = len(IP())
+ETHER_IP_HDR_LEN = ETHER_HDR_LEN + IP_HDR_LEN
+
+
+@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_BUFFER_SPLIT)
+@requires_nic_capability(NicCapability.SELECTIVE_RX)
+class TestRxSplit(TestSuite):
+ """Rx split test suite.
+
+ Configure testpmd with various Rx segment offset/length combinations
+ and verify that only the requested portions of the packet are received
+ and forwarded.
+ """
+
+ def _create_testpmd(self, **kwargs: Any) -> TestPmd:
+ """Create a TestPmd instance with defaults overridden by kwargs."""
+ defaults: dict[str, Any] = {
+ "forward_mode": SimpleForwardingModes.mac,
+ "rx_offloads": RxOffloadCapability.BUFFER_SPLIT,
+ "enable_scatter": True,
+ }
+ return TestPmd(**{**defaults, **kwargs})
+
+ def _build_packet(self) -> Ether:
+ """Build a test packet with an incrementing byte pattern payload."""
+ return Ether() / IP() / Raw(load=PAYLOAD)
+
+ def _send_and_verify(
+ self,
+ testpmd: TestPmd,
+ packet: Ether,
+ expected_bytes: bytes,
+ ) -> None:
+ """Clear stats, send a packet, and verify received content and stats.
+
+ Args:
+ testpmd: The running testpmd instance.
+ packet: The packet to send.
+ expected_bytes: Expected raw bytes of the received packet.
+ """
+ expected_len = len(expected_bytes)
+ testpmd.clear_port_stats_all(verify=False)
+
+ received = send_packet_and_capture(packet)
+ verify(
+ len(received) > 0,
+ "Did not receive any packets.",
+ )
+
+ recv_bytes = bytes(received[0])
+ verify(
+ len(recv_bytes) == expected_len,
+ f"Expected packet length {expected_len}, got {len(recv_bytes)}.",
+ )
+ verify(
+ recv_bytes == expected_bytes,
+ "Received packet content does not match expected bytes.",
+ )
+
+ all_stats, _ = testpmd.show_port_stats_all()
+ total_rx_packets = sum(s.rx_packets for s in all_stats)
+ total_rx_bytes = sum(s.rx_bytes for s in all_stats)
+ verify(
+ total_rx_packets == 1,
+ f"Expected 1 Rx packet, got {total_rx_packets}.",
+ )
+ verify(
+ total_rx_bytes == expected_len,
+ f"Expected {expected_len} Rx bytes, got {total_rx_bytes}.",
+ )
+
+ @func_test
+ def selective_rx_headers(self) -> None:
+ """Keep only the Ethernet + IP headers, discard the payload.
+
+ Steps:
+ Start testpmd with rxoffs/rxpkts and buffer split enabled.
+ Send an Ether/IP/payload packet.
+
+ Verify:
+ Received packet has Ether + IP headers only.
+ Port stats show expected rx_packets and rx_bytes.
+ """
+ with self._create_testpmd(
+ rx_segments_offsets=[0],
+ rx_segments_length=[ETHER_IP_HDR_LEN],
+ ) as testpmd:
+ testpmd.start()
+ packet = self._build_packet()
+ expected = bytes(packet)[:ETHER_IP_HDR_LEN]
+ self._send_and_verify(testpmd, packet, expected)
+
+ @func_test
+ def selective_rx_payload_only(self) -> None:
+ """Skip the Ethernet + IP headers, keep only the payload.
+
+ Steps:
+ Start testpmd with rxoffs/rxpkts and buffer split enabled.
+ Send an Ether/IP/payload packet.
+
+ Verify:
+ Received packet is matching the original payload.
+ Port stats show expected rx_packets and rx_bytes.
+ """
+ with self._create_testpmd(
+ rx_segments_offsets=[ETHER_IP_HDR_LEN],
+ rx_segments_length=[len(PAYLOAD)],
+ ) as testpmd:
+ testpmd.start()
+ self._send_and_verify(testpmd, self._build_packet(), PAYLOAD)
+
+ @func_test
+ def selective_rx_two_segments(self) -> None:
+ """Keep the IP header and the middle of the payload, skip the rest.
+
+ Steps:
+ Start testpmd with rxoffs/rxpkts, buffer split
+ and multi-segment Tx enabled.
+ Send an Ether/IP/payload packet.
+
+ Verify:
+ Received packet is matching the IP header and middle of payload.
+ Port stats show expected rx_packets and rx_bytes.
+ """
+ payload_offset = 100
+ payload_length = 100
+ with self._create_testpmd(
+ tx_offloads=TxOffloadCapability.MULTI_SEGS,
+ rx_segments_offsets=[ETHER_HDR_LEN, ETHER_IP_HDR_LEN + payload_offset],
+ rx_segments_length=[IP_HDR_LEN, payload_length],
+ ) as testpmd:
+ testpmd.start()
+ packet = self._build_packet()
+ raw = bytes(packet)
+ payload_start = ETHER_IP_HDR_LEN + payload_offset
+ expected = (
+ raw[ETHER_HDR_LEN:ETHER_IP_HDR_LEN]
+ + raw[payload_start : payload_start + payload_length]
+ )
+ self._send_and_verify(testpmd, packet, expected)
+
+ @func_test
+ def selective_rx_no_offload(self) -> None:
+ """Configure selective Rx with buffer split disabled.
+
+ Steps:
+ Start testpmd with rxoffs/rxpkts, buffer split
+ and device start disabled.
+ Attempt to start ports.
+
+ Verify:
+ Queue configuration fails.
+ """
+ with self._create_testpmd(
+ rx_offloads=None,
+ rx_segments_offsets=[0],
+ rx_segments_length=[ETHER_IP_HDR_LEN],
+ disable_device_start=True,
+ ) as testpmd:
+ try:
+ testpmd.start_all_ports()
+ fail("Expected configuration to fail with buffer split disabled.")
+ except InteractiveCommandExecutionError:
+ pass
+
+ @func_test
+ def selective_rx_offset_out_of_range(self) -> None:
+ """Configure selective Rx with an offset beyond max_rx_pktlen.
+
+ Steps:
+ Start testpmd with rxoffs too big, buffer split enabled,
+ and device start disabled.
+ Attempt to start ports.
+
+ Verify:
+ Queue configuration fails.
+ """
+ with self._create_testpmd(
+ rx_segments_offsets=[20000],
+ rx_segments_length=[100],
+ disable_device_start=True,
+ ) as testpmd:
+ try:
+ testpmd.start_all_ports()
+ fail("Expected configuration to fail with out-of-range offset.")
+ except InteractiveCommandExecutionError:
+ pass
+
+ @func_test
+ def selective_rx_overlap(self) -> None:
+ """Configure selective Rx with overlapping segments.
+
+ Steps:
+ Start testpmd with overlapping rxoffs/rxpkts, buffer split enabled,
+ and device start disabled.
+ Attempt to start ports.
+
+ Verify:
+ Queue configuration fails.
+ """
+ with self._create_testpmd(
+ rx_segments_offsets=[0, 10],
+ rx_segments_length=[64, 64],
+ disable_device_start=True,
+ ) as testpmd:
+ try:
+ testpmd.start_all_ports()
+ fail("Expected configuration to fail with overlapping segments.")
+ except InteractiveCommandExecutionError:
+ pass
+
+ @func_test
+ def selective_rx_all_discard(self) -> None:
+ """Configure selective Rx with only discard segment.
+
+ Steps:
+ Start testpmd with rxoffs/rxpkts=0 (null segment), buffer split enabled,
+ and device start disabled.
+ Attempt to start ports.
+
+ Verify:
+ Queue configuration fails.
+ """
+ with self._create_testpmd(
+ rx_segments_offsets=[0],
+ rx_segments_length=[0],
+ disable_device_start=True,
+ ) as testpmd:
+ try:
+ testpmd.start_all_ports()
+ fail("Expected configuration to fail with only discard segment.")
+ except InteractiveCommandExecutionError:
+ pass
--
2.54.0
^ permalink raw reply related
* [v6 0/2] net/hinic3: Fix the hinic3 driver issue
From: Feifei Wang @ 2026-05-28 13:01 UTC (permalink / raw)
To: dev
In-Reply-To: <20260526121820.1093-2-wff_light@vip.163.com>
v1: The RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO flag is added to support the VXLAN TSO function
v2: Modify the commit information issue and supplement the commit information
v3: Revise review comments. First, deterine whether the hardware supports it, then add the flag bit
v4: Fix the compilation error caused by leading spaces
v5: Add fix tag
v6: MOdify SP230 NIC vf device id
Feifei Wang (2):
net/hinic3: Fix VXLAN TSO issue
net/hinic3: Modify SP230 VF device id
drivers/net/hinic3/base/hinic3_csr.h | 2 +-
drivers/net/hinic3/hinic3_ethdev.c | 8 ++++++--
2 files changed, 7 insertions(+), 3 deletions(-)
--
2.47.0.windows.2
^ permalink raw reply
* [v6 2/2] net/hinic3: Modify SP230 VF device id
From: Feifei Wang @ 2026-05-28 13:01 UTC (permalink / raw)
To: dev; +Cc: Feifei Wang, Feifei Wang
In-Reply-To: <20260528130122.1213-1-wff_light@vip.163.com>
From: Feifei Wang <wangfeifei40@huawei.com>
Modify SP230 the VF device ID of the SP230 NIC.
Signed-off-by: Feifei Wang <wangfeifei@huawei.com>
---
drivers/net/hinic3/base/hinic3_csr.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/hinic3/base/hinic3_csr.h b/drivers/net/hinic3/base/hinic3_csr.h
index eceb34e..7cae241 100644
--- a/drivers/net/hinic3/base/hinic3_csr.h
+++ b/drivers/net/hinic3/base/hinic3_csr.h
@@ -11,7 +11,7 @@
#define HINIC3_DEV_ID_VF_SP620 0x375F
#define HINIC3_DEV_ID_SP230 0x0229
-#define HINIC3_DEV_ID_VF_SP230 0x3750
+#define HINIC3_DEV_ID_VF_SP230 0x022a
#define HINIC3_DEV_ID_SP920 0x0224
--
2.47.0.windows.2
^ permalink raw reply related
* [v6 1/2] net/hinic3: Fix VXLAN TSO issue
From: Feifei Wang @ 2026-05-28 13:01 UTC (permalink / raw)
To: dev; +Cc: Feifei Wang, stable
In-Reply-To: <20260528130122.1213-1-wff_light@vip.163.com>
From: Feifei Wang <wangfeifei40@huawei.com>
VXLAN TSO lacks a flag bit, causing the processing function
to determine that the hardware does not support it, leading
to improper handling.
Fixes: 7608f0367d ("net/hinic3: add dev ops")
Cc: stable@dpdk.org
Signed-off-by: Feifei Wang <wangfeifei40@huawei.com>
---
drivers/net/hinic3/hinic3_ethdev.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/net/hinic3/hinic3_ethdev.c b/drivers/net/hinic3/hinic3_ethdev.c
index f4eb788..66c5c3a 100644
--- a/drivers/net/hinic3/hinic3_ethdev.c
+++ b/drivers/net/hinic3/hinic3_ethdev.c
@@ -652,8 +652,12 @@ hinic3_dev_configure(struct rte_eth_dev *dev)
static void
hinic3_dev_tnl_tso_support(struct rte_eth_dev_info *info, struct hinic3_nic_dev *nic_dev)
{
+ if (HINIC3_SUPPORT_VXLAN_OFFLOAD(nic_dev))
+ info->tx_offload_capa |= RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO;
+
if (HINIC3_SUPPORT_GENEVE_OFFLOAD(nic_dev))
info->tx_offload_capa |= RTE_ETH_TX_OFFLOAD_GENEVE_TNL_TSO;
+
if (HINIC3_SUPPORT_IPXIP_OFFLOAD(nic_dev))
info->tx_offload_capa |= RTE_ETH_TX_OFFLOAD_IPIP_TNL_TSO;
}
@@ -698,8 +702,8 @@ hinic3_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
RTE_ETH_TX_OFFLOAD_SCTP_CKSUM |
RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM |
RTE_ETH_TX_OFFLOAD_TCP_TSO | RTE_ETH_TX_OFFLOAD_MULTI_SEGS;
- if (nic_dev->feature_cap & NIC_F_HTN_CMDQ)
- hinic3_dev_tnl_tso_support(info, nic_dev);
+
+ hinic3_dev_tnl_tso_support(info, nic_dev);
info->hash_key_size = HINIC3_RSS_KEY_SIZE;
info->reta_size = HINIC3_RSS_INDIR_SIZE;
--
2.47.0.windows.2
^ permalink raw reply related
* Re: [PATCH v5 04/27] net/intel/common: add common flow attr validation
From: Bruce Richardson @ 2026-05-28 13:13 UTC (permalink / raw)
To: Burakov, Anatoly; +Cc: dev
In-Reply-To: <9dbc8a94-ceca-41f0-b759-a23cbae76902@intel.com>
On Thu, May 28, 2026 at 02:39:47PM +0200, Burakov, Anatoly wrote:
> On 5/27/2026 3:28 PM, Bruce Richardson wrote:
> > On Mon, May 25, 2026 at 03:06:23PM +0100, Anatoly Burakov wrote:
> > > There are a lot of commonalities between what kinds of flow attr each Intel
> > > driver supports. Add a helper function that will validate attr based on
> > > common requirements and (optional) parameter checks.
> > >
> > > Things we check for:
> > > - Rejecting NULL attr (obviously)
> > > - Default to ingress flows
> > > - Transfer, group, priority, and egress are not allowed unless requested
> > >
> > > Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
> > > ---
> > > drivers/net/intel/common/flow_check.h | 69 +++++++++++++++++++++++++++
> > > 1 file changed, 69 insertions(+)
> > >
> > > diff --git a/drivers/net/intel/common/flow_check.h b/drivers/net/intel/common/flow_check.h
> > > index 74fb28ae3d..0572028664 100644
> > > --- a/drivers/net/intel/common/flow_check.h
> > > +++ b/drivers/net/intel/common/flow_check.h
> > > @@ -54,6 +54,7 @@ ci_flow_action_type_in_list(const enum rte_flow_action_type type,
> > > /* Forward declarations */
> > > struct ci_flow_actions;
> > > struct ci_flow_actions_check_param;
> > > +struct ci_flow_attr_check_param;
> > > static inline const char *
> > > ci_flow_action_type_to_str(enum rte_flow_action_type type)
> > > @@ -271,6 +272,74 @@ ci_flow_check_actions(const struct rte_flow_action *actions,
> > > return parsed_actions->count == 0 ? -EINVAL : 0;
> > > }
> > > +/**
> > > + * Parameter structure for attr check.
> > > + */
> > > +struct ci_flow_attr_check_param {
> > > + bool allow_priority; /**< True if priority attribute is allowed. */
> > > + bool allow_transfer; /**< True if transfer attribute is allowed. */
> > > + bool allow_group; /**< True if group attribute is allowed. */
> > > + bool expect_egress; /**< True if egress attribute is expected. */
> > > +};
> > > +
> > > +/**
> > > + * Validate rte_flow_attr structure against specified constraints.
> > > + *
> > > + * @param attr Pointer to rte_flow_attr structure to validate.
> > > + * @param attr_param Pointer to ci_flow_attr_check_param structure specifying constraints.
> > > + * @param error Pointer to rte_flow_error structure for error reporting.
> > > + *
> > > + * @return 0 on success, negative errno on failure.
> > > + */
> > > +static inline int
> > > +ci_flow_check_attr(const struct rte_flow_attr *attr,
> > > + const struct ci_flow_attr_check_param *attr_param,
> > > + struct rte_flow_error *error)
> > > +{
> > > + if (attr == NULL) {
> > > + return rte_flow_error_set(error, EINVAL,
> > > + RTE_FLOW_ERROR_TYPE_ATTR, attr,
> > > + "NULL attribute");
> > > + }
> > > +
> > > + /* Direction must be either ingress or egress */
> > > + if (attr->ingress == attr->egress) {
> > > + return rte_flow_error_set(error, EINVAL,
> > > + RTE_FLOW_ERROR_TYPE_ATTR, attr,
> > > + "Either ingress or egress must be set");
> > > + }
> > > +
> > > + /* Expect ingress by default */
> > > + if (attr->egress && (attr_param == NULL || !attr_param->expect_egress)) {
> > > + return rte_flow_error_set(error, EINVAL,
> > > + RTE_FLOW_ERROR_TYPE_ATTR_EGRESS, attr,
> > > + "Egress not supported");
> > > + }
> >
> > I think "allow_egress" is possibly a better name here. "expect_egress"
> > implies that egress must be present, but the logic here seems to be only
> > allowing egress if the flag is provided.
>
> No, the check right above this prevents unspecified egress *if* ingress is
> also specified.
So the error message above should be "Either ingress or egress must be set,
but not both"?
> Meaning, one of egress/ingress *must* be specified, and if
> it's egress, it is only allowed when we are expecting egress.
>
Yes, so it's either ingress or egress but egress is only allowed in some
cases. But nothing in this check prevents ingress alone when expect_egress
is set, so egress is allowed but not required, so I still think
"allow_egress" is a better name. "expect_egress" is a bit ambivilent, in
that it's unclear how strong that expectation is - is it an error, or just
a warning if the expectation is not met, for example?
/Bruce
^ permalink raw reply
* RE: [PATCH v3] net/intel: optimize for fast-free hint
From: Loftus, Ciara @ 2026-05-28 13:23 UTC (permalink / raw)
To: Richardson, Bruce, dev@dpdk.org
Cc: mb@smartsharesystems.com, Richardson, Bruce
In-Reply-To: <20260519110637.964965-1-bruce.richardson@intel.com>
> Subject: [PATCH v3] net/intel: optimize for fast-free hint
>
snip
> diff --git a/drivers/net/intel/common/tx_scalar.h
> b/drivers/net/intel/common/tx_scalar.h
> index 9fcd2e4733..d27df34dfa 100644
> --- a/drivers/net/intel/common/tx_scalar.h
> +++ b/drivers/net/intel/common/tx_scalar.h
> @@ -197,16 +197,64 @@ ci_tx_xmit_cleanup(struct ci_tx_queue *txq)
> const uint16_t rs_idx = (last_desc_cleaned == nb_tx_desc - 1) ?
> 0 :
> (last_desc_cleaned + 1) >> txq->log2_rs_thresh;
> - uint16_t desc_to_clean_to = (rs_idx << txq->log2_rs_thresh) + (txq-
> >tx_rs_thresh - 1);
> + const uint16_t dd_idx = txq->rs_last_id[rs_idx];
> + const uint16_t first_to_clean = rs_idx << txq->log2_rs_thresh;
>
> - /* Check if descriptor is done */
> - if ((txd[txq->rs_last_id[rs_idx]].cmd_type_offset_bsz &
> - rte_cpu_to_le_64(CI_TXD_QW1_DTYPE_M)) !=
> -
> rte_cpu_to_le_64(CI_TX_DESC_DTYPE_DESC_DONE))
> + /* Check if descriptor is done - all drivers use 0xF as done value in bits
> 3:0 */
> + if ((txd[dd_idx].cmd_type_offset_bsz &
> rte_cpu_to_le_64(CI_TXD_QW1_DTYPE_M)) !=
> + rte_cpu_to_le_64(CI_TX_DESC_DTYPE_DESC_DONE))
> + /* Descriptor not yet processed by hardware */
> return -1;
>
> + /* DD bit is set, descriptors are done. Now free the mbufs. */
> + /* Note: nb_tx_desc is guaranteed to be a multiple of tx_rs_thresh,
> + * validated during queue setup. This means cleanup never wraps
> around
> + * the ring within a single burst (e.g., ring=256, rs_thresh=32 gives
> + * bursts of 0-31, 32-63, ..., 224-255).
> + */
> + const uint16_t nb_to_clean = txq->tx_rs_thresh;
> + struct ci_tx_entry *sw_ring = txq->sw_ring;
> +
> + /* fast_free_mp is NULL only when the fast free is disabled*/
> + if (txq->fast_free_mp != NULL) {
> + /* FAST_FREE path: mbufs are already reset, just return to
> pool */
> + struct rte_mbuf *free[CI_TX_MAX_FREE_BUF_SZ];
> + uint16_t nb_free = 0;
> +
> + /* Get cached mempool pointer, or cache it on first use */
> + struct rte_mempool *mp =
> + likely(txq->fast_free_mp != (void *)UINTPTR_MAX) ?
> + txq->fast_free_mp :
> + (txq->fast_free_mp = sw_ring[dd_idx].mbuf->pool);
Is the mbuf you are dereferencing here guaranteed to be non NULL?
Other than that concern:
Acked-by: Ciara Loftus <ciara.loftus@intel.com>
> +
> + /* Pack non-NULL mbufs in-place at start of sw_ring range.
> + * No modulo needed in loop since we're guaranteed not to
> wrap.
> + */
> + for (uint16_t i = 0; i < nb_to_clean; i++) {
^ permalink raw reply
* Re: [PATCH v5 04/27] net/intel/common: add common flow attr validation
From: Burakov, Anatoly @ 2026-05-28 14:13 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev
In-Reply-To: <ahg_hFvDIpwzYEyQ@bricha3-mobl1.ger.corp.intel.com>
On 5/28/2026 3:13 PM, Bruce Richardson wrote:
> On Thu, May 28, 2026 at 02:39:47PM +0200, Burakov, Anatoly wrote:
>> On 5/27/2026 3:28 PM, Bruce Richardson wrote:
>>> On Mon, May 25, 2026 at 03:06:23PM +0100, Anatoly Burakov wrote:
>>>> There are a lot of commonalities between what kinds of flow attr each Intel
>>>> driver supports. Add a helper function that will validate attr based on
>>>> common requirements and (optional) parameter checks.
>>>>
>>>> Things we check for:
>>>> - Rejecting NULL attr (obviously)
>>>> - Default to ingress flows
>>>> - Transfer, group, priority, and egress are not allowed unless requested
>>>>
>>>> Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
>>>> ---
>>>> drivers/net/intel/common/flow_check.h | 69 +++++++++++++++++++++++++++
>>>> 1 file changed, 69 insertions(+)
>>>>
>>>> diff --git a/drivers/net/intel/common/flow_check.h b/drivers/net/intel/common/flow_check.h
>>>> index 74fb28ae3d..0572028664 100644
>>>> --- a/drivers/net/intel/common/flow_check.h
>>>> +++ b/drivers/net/intel/common/flow_check.h
>>>> @@ -54,6 +54,7 @@ ci_flow_action_type_in_list(const enum rte_flow_action_type type,
>>>> /* Forward declarations */
>>>> struct ci_flow_actions;
>>>> struct ci_flow_actions_check_param;
>>>> +struct ci_flow_attr_check_param;
>>>> static inline const char *
>>>> ci_flow_action_type_to_str(enum rte_flow_action_type type)
>>>> @@ -271,6 +272,74 @@ ci_flow_check_actions(const struct rte_flow_action *actions,
>>>> return parsed_actions->count == 0 ? -EINVAL : 0;
>>>> }
>>>> +/**
>>>> + * Parameter structure for attr check.
>>>> + */
>>>> +struct ci_flow_attr_check_param {
>>>> + bool allow_priority; /**< True if priority attribute is allowed. */
>>>> + bool allow_transfer; /**< True if transfer attribute is allowed. */
>>>> + bool allow_group; /**< True if group attribute is allowed. */
>>>> + bool expect_egress; /**< True if egress attribute is expected. */
>>>> +};
>>>> +
>>>> +/**
>>>> + * Validate rte_flow_attr structure against specified constraints.
>>>> + *
>>>> + * @param attr Pointer to rte_flow_attr structure to validate.
>>>> + * @param attr_param Pointer to ci_flow_attr_check_param structure specifying constraints.
>>>> + * @param error Pointer to rte_flow_error structure for error reporting.
>>>> + *
>>>> + * @return 0 on success, negative errno on failure.
>>>> + */
>>>> +static inline int
>>>> +ci_flow_check_attr(const struct rte_flow_attr *attr,
>>>> + const struct ci_flow_attr_check_param *attr_param,
>>>> + struct rte_flow_error *error)
>>>> +{
>>>> + if (attr == NULL) {
>>>> + return rte_flow_error_set(error, EINVAL,
>>>> + RTE_FLOW_ERROR_TYPE_ATTR, attr,
>>>> + "NULL attribute");
>>>> + }
>>>> +
>>>> + /* Direction must be either ingress or egress */
>>>> + if (attr->ingress == attr->egress) {
>>>> + return rte_flow_error_set(error, EINVAL,
>>>> + RTE_FLOW_ERROR_TYPE_ATTR, attr,
>>>> + "Either ingress or egress must be set");
>>>> + }
>>>> +
>>>> + /* Expect ingress by default */
>>>> + if (attr->egress && (attr_param == NULL || !attr_param->expect_egress)) {
>>>> + return rte_flow_error_set(error, EINVAL,
>>>> + RTE_FLOW_ERROR_TYPE_ATTR_EGRESS, attr,
>>>> + "Egress not supported");
>>>> + }
>>>
>>> I think "allow_egress" is possibly a better name here. "expect_egress"
>>> implies that egress must be present, but the logic here seems to be only
>>> allowing egress if the flag is provided.
>>
>> No, the check right above this prevents unspecified egress *if* ingress is
>> also specified.
>
> So the error message above should be "Either ingress or egress must be set,
> but not both"?
Sure, yes, a rulle cannot be both egress and ingress.
>
>> Meaning, one of egress/ingress *must* be specified, and if
>> it's egress, it is only allowed when we are expecting egress.
>>
> Yes, so it's either ingress or egress but egress is only allowed in some
> cases. But nothing in this check prevents ingress alone when expect_egress
> is set, so egress is allowed but not required, so I still think
> "allow_egress" is a better name. "expect_egress" is a bit ambivilent, in
> that it's unclear how strong that expectation is - is it an error, or just
> a warning if the expectation is not met, for example?
Rules cannot be both ingress and egress, and they cannot be neither
ingress and egress. That leaves two options: either the rule is ingress,
or the rule is egress.
If the rule isn't ingress, it by definition will be egress in order to
pass earlier checks. The intent is also that a rule that is "expected to
be egress" must be egress to be accepted.
However, now that I think of it, you're right in that the condition is
subtly wrong. By default, we expect the rule to be ingress (i.e. not
setting "expect egress" implies we are expecting ingress), but we only
*check* if the rule is egress - we would *not* check if the rule is
egress. I think the condition should be replaced with:
1) if attr_param == NULL, rule *cannot* ever be expected to be egress
because we specify egress expectations in attr_param - so, rule being
egress when attr_param == NULL is an error
2) if attr_param != NULL, egress *must* match expect_egress
So, basically:
if ((attr_param == NULL && attr->egress) || (attr->egress !=
attr_param->expect_egress)
this should better match the intent.
>
> /Bruce
--
Thanks,
Anatoly
^ permalink raw reply
* Re: [PATCH v1 4/5] eal: fix async IPC resource leaks on partial failure
From: Thomas Monjalon @ 2026-05-28 14:24 UTC (permalink / raw)
To: Anatoly Burakov; +Cc: dev, Jianfeng Tan
In-Reply-To: <af8325076295d7f5400d6ff19451ea54828b63e9.1773936429.git.anatoly.burakov@intel.com>
Hello Anatoly,
19/03/2026 17:07, Anatoly Burakov:
> Use a numeric request ID for alarm callback lookup so stale callbacks
> from rolled-back requests become harmless no-ops.
It seems you forgot to convert 2 calls to rte_eal_alarm_set().
[...]
> struct pending_request {
> TAILQ_ENTRY(pending_request) next;
> + unsigned long id;
[...]
> +static struct pending_request *
> +find_pending_request_by_id(unsigned long id)
> +{
> + struct pending_request *r;
> +
> + TAILQ_FOREACH(r, &pending_requests.requests, next) {
> + if (r->id == id)
> + return r;
> + }
> +
> + return NULL;
> +}
[...]
> async_reply_handle(void *arg)
> {
> struct pending_request *req;
> + /* alarm arg carries the request ID packed into a void * via uintptr_t */
No, that's wrong.
The pointer passed in some rte_eal_alarm_set() from patch 2
is a struct pending_request.
> + unsigned long id = (uintptr_t)arg;
Either you get the id field from arg,
or you pass dummy->id to rte_eal_alarm_set().
> pthread_mutex_lock(&pending_requests.lock);
> - req = async_reply_handle_thread_unsafe(arg);
> + req = find_pending_request_by_id(id);
> + if (req != NULL)
> + req = async_reply_handle_thread_unsafe(req);
> pthread_mutex_unlock(&pending_requests.lock);
^ permalink raw reply
* RE: [RFC v3 2/3] lib: add fastmem library
From: Varghese, Vipin @ 2026-05-28 14:45 UTC (permalink / raw)
To: Morten Brørup, Bruce Richardson, Mattias Rönnblom,
dev@dpdk.org
Cc: Konstantin Ananyev, Mattias Rönnblom, Yogaraj Baskaravel,
Stephen Hemminger
In-Reply-To: <98CBD80474FA8B44BF855DF32C47DC35F658AD@smartserver.smartshare.dk>
Public
Hi @Morten Brørup
<snipped>
>
> > +/**
> > + * Pre-reserve backing memory.
> > + *
> > + * Ensures that at least @p size bytes of memzone-backed memory are
> > + * available to the allocator on @p socket_id, reserving additional
> > + * memzones from EAL as needed to reach that total. Subsequent
> > + * allocations served from the pre-reserved memory do not incur
> > + * memzone-reservation cost.
> > + *
> > + * The reservation is cumulative: repeated calls to
> > + * rte_fastmem_reserve() with the same @p socket_id grow the
> > + * reservation monotonically. Reserved memory is never returned to
> > + * the system during the allocator's lifetime.
> > + *
> > + * A typical use is to call rte_fastmem_reserve() once at
> > + * application startup, with a size chosen to cover the expected
> > + * steady-state working set. Allocations and frees during
> > + * steady-state operation then avoid memzone reservations entirely.
> > + *
> > + * @param size
> > + * The minimum amount of backing memory, in bytes, to make
> > + * available on @p socket_id. The allocator may reserve more than
> > + * the requested amount due to internal rounding (e.g., to memzone
> > + * or block granularity).
> > + *
> > + * @param socket_id
> > + * The NUMA socket on which to reserve memory, or SOCKET_ID_ANY
> > + * to leave the choice to the allocator. With SOCKET_ID_ANY, the
> > + * allocator starts on the calling lcore's socket (or the first
> > + * configured socket if the caller is not bound to one) and falls
> > + * back to other sockets if the preferred socket cannot satisfy
> > + * the reservation.
> > + *
> > + * @return
> > + * - 0: Success.
> > + * - -ENOMEM: Insufficient huge-page memory to satisfy the request.
> > + * - -EINVAL: Invalid @p socket_id.
> > + */
> > +__rte_experimental
> > +int
> > +rte_fastmem_reserve(size_t size, int socket_id);
>
> @Bruce,
> I vaguely recall that we discussed something about busses and sockets a long time
> ago, but I cannot remember the details.
> Is socket_id the right type (and parameter name) to identify a memory bus?
>
> @Vipin,
> You have been working on topology awareness. Same question to you:
> Is socket_id the right type (and parameter name) to identify a memory bus?
Short answer: socket_id is no longer a precise or sufficient abstraction to represent a memory bus.
Based on the topology work with libhwloc, we’ve observed the following across Ampere, Intel, and AMD platforms:
Features like SNC (Sub-NUMA Clustering) on Intel and NPS (NUMA Per Socket) on AMD change how socket_id maps to hardware.
In these modes:
1) A single physical socket can expose multiple NUMA domains.
2)These NUMA domains align more closely with memory controller groupings (i.e., memory buses) rather than the full socket.
Depending on the architecture:
a) Memory controllers may be collocated with compute cores or placed on separate tiles.
b) As a result, socket_id can represent different scopes (full socket vs. sub-socket domains), making it inconsistent.
Hence practically: In some configurations, socket_id ≈ memory domain. In others, it is coarser than the actual memory bus topology.
To address this ambiguity, in the topology patches (v5/v6), we are moving toward clearer separation:
a. Cache domains (L1/L2/L3/L4) for compute locality
b. NUMA domains (memory + IO) as the unit for allocation locality
This direction better reflects real hardware and avoids overloading socket_id with multiple meanings.
Happy to align this with the topology model we’re introducing so the abstraction remains consistent going forward.
Thanks,
Vipin
^ permalink raw reply
* Re: [PATCH v5 04/27] net/intel/common: add common flow attr validation
From: Bruce Richardson @ 2026-05-28 15:09 UTC (permalink / raw)
To: Burakov, Anatoly; +Cc: dev
In-Reply-To: <a98ae9f0-fce3-49b9-a4df-d76e08696c2c@intel.com>
On Thu, May 28, 2026 at 04:13:59PM +0200, Burakov, Anatoly wrote:
> On 5/28/2026 3:13 PM, Bruce Richardson wrote:
> > On Thu, May 28, 2026 at 02:39:47PM +0200, Burakov, Anatoly wrote:
> > > On 5/27/2026 3:28 PM, Bruce Richardson wrote:
> > > > On Mon, May 25, 2026 at 03:06:23PM +0100, Anatoly Burakov wrote:
> > > > > There are a lot of commonalities between what kinds of flow attr each Intel
> > > > > driver supports. Add a helper function that will validate attr based on
> > > > > common requirements and (optional) parameter checks.
> > > > >
> > > > > Things we check for:
> > > > > - Rejecting NULL attr (obviously)
> > > > > - Default to ingress flows
> > > > > - Transfer, group, priority, and egress are not allowed unless requested
> > > > >
> > > > > Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
> > > > > ---
> > > > > drivers/net/intel/common/flow_check.h | 69 +++++++++++++++++++++++++++
> > > > > 1 file changed, 69 insertions(+)
> > > > >
> > > > > diff --git a/drivers/net/intel/common/flow_check.h b/drivers/net/intel/common/flow_check.h
> > > > > index 74fb28ae3d..0572028664 100644
> > > > > --- a/drivers/net/intel/common/flow_check.h
> > > > > +++ b/drivers/net/intel/common/flow_check.h
> > > > > @@ -54,6 +54,7 @@ ci_flow_action_type_in_list(const enum rte_flow_action_type type,
> > > > > /* Forward declarations */
> > > > > struct ci_flow_actions;
> > > > > struct ci_flow_actions_check_param;
> > > > > +struct ci_flow_attr_check_param;
> > > > > static inline const char *
> > > > > ci_flow_action_type_to_str(enum rte_flow_action_type type)
> > > > > @@ -271,6 +272,74 @@ ci_flow_check_actions(const struct rte_flow_action *actions,
> > > > > return parsed_actions->count == 0 ? -EINVAL : 0;
> > > > > }
> > > > > +/**
> > > > > + * Parameter structure for attr check.
> > > > > + */
> > > > > +struct ci_flow_attr_check_param {
> > > > > + bool allow_priority; /**< True if priority attribute is allowed. */
> > > > > + bool allow_transfer; /**< True if transfer attribute is allowed. */
> > > > > + bool allow_group; /**< True if group attribute is allowed. */
> > > > > + bool expect_egress; /**< True if egress attribute is expected. */
> > > > > +};
> > > > > +
> > > > > +/**
> > > > > + * Validate rte_flow_attr structure against specified constraints.
> > > > > + *
> > > > > + * @param attr Pointer to rte_flow_attr structure to validate.
> > > > > + * @param attr_param Pointer to ci_flow_attr_check_param structure specifying constraints.
> > > > > + * @param error Pointer to rte_flow_error structure for error reporting.
> > > > > + *
> > > > > + * @return 0 on success, negative errno on failure.
> > > > > + */
> > > > > +static inline int
> > > > > +ci_flow_check_attr(const struct rte_flow_attr *attr,
> > > > > + const struct ci_flow_attr_check_param *attr_param,
> > > > > + struct rte_flow_error *error)
> > > > > +{
> > > > > + if (attr == NULL) {
> > > > > + return rte_flow_error_set(error, EINVAL,
> > > > > + RTE_FLOW_ERROR_TYPE_ATTR, attr,
> > > > > + "NULL attribute");
> > > > > + }
> > > > > +
> > > > > + /* Direction must be either ingress or egress */
> > > > > + if (attr->ingress == attr->egress) {
> > > > > + return rte_flow_error_set(error, EINVAL,
> > > > > + RTE_FLOW_ERROR_TYPE_ATTR, attr,
> > > > > + "Either ingress or egress must be set");
> > > > > + }
> > > > > +
> > > > > + /* Expect ingress by default */
> > > > > + if (attr->egress && (attr_param == NULL || !attr_param->expect_egress)) {
> > > > > + return rte_flow_error_set(error, EINVAL,
> > > > > + RTE_FLOW_ERROR_TYPE_ATTR_EGRESS, attr,
> > > > > + "Egress not supported");
> > > > > + }
> > > >
> > > > I think "allow_egress" is possibly a better name here. "expect_egress"
> > > > implies that egress must be present, but the logic here seems to be only
> > > > allowing egress if the flag is provided.
> > >
> > > No, the check right above this prevents unspecified egress *if* ingress is
> > > also specified.
> >
> > So the error message above should be "Either ingress or egress must be set,
> > but not both"?
>
> Sure, yes, a rulle cannot be both egress and ingress.
>
> >
> > > Meaning, one of egress/ingress *must* be specified, and if
> > > it's egress, it is only allowed when we are expecting egress.
> > >
> > Yes, so it's either ingress or egress but egress is only allowed in some
> > cases. But nothing in this check prevents ingress alone when expect_egress
> > is set, so egress is allowed but not required, so I still think
> > "allow_egress" is a better name. "expect_egress" is a bit ambivilent, in
> > that it's unclear how strong that expectation is - is it an error, or just
> > a warning if the expectation is not met, for example?
>
> Rules cannot be both ingress and egress, and they cannot be neither ingress
> and egress. That leaves two options: either the rule is ingress, or the rule
> is egress.
>
> If the rule isn't ingress, it by definition will be egress in order to pass
> earlier checks. The intent is also that a rule that is "expected to be
> egress" must be egress to be accepted.
>
> However, now that I think of it, you're right in that the condition is
> subtly wrong. By default, we expect the rule to be ingress (i.e. not setting
> "expect egress" implies we are expecting ingress), but we only *check* if
> the rule is egress - we would *not* check if the rule is egress. I think the
> condition should be replaced with:
>
> 1) if attr_param == NULL, rule *cannot* ever be expected to be egress
> because we specify egress expectations in attr_param - so, rule being egress
> when attr_param == NULL is an error
> 2) if attr_param != NULL, egress *must* match expect_egress
>
> So, basically:
>
> if ((attr_param == NULL && attr->egress) || (attr->egress !=
> attr_param->expect_egress)
>
> this should better match the intent.
>
Yes, that is clearer. In this case, is "expect_egress" better renamed to
"egress_only" or "require_egress" to make it clear it's an absolute
requirement?
Also, just a suggestion for readability's sake, maybe merge the conditions
for checking ingress and egress but not both or neither into explicit check
branches for each case for clarity.
if (attr_param != NULL && attr_param->require_egress) {
/* this must be an egress rule, ingress unset, egress set */
if (attr->ingress != 0 || attr->egress != 1)
....
} else {
/* this must be an ingress rule, ingress set, egress unset */
if (attr->ingress != 1 || attr->egress != 0)
....
}
/Bruce
^ permalink raw reply
* [PATCH] doc: announce removal of flow director
From: Stephen Hemminger @ 2026-05-28 17:00 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
There are still leftover bits in ethdev related to flow director (fdir).
Will be removing these in 26.11 so give final notice.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
doc/guides/rel_notes/deprecation.rst | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/doc/guides/rel_notes/deprecation.rst b/doc/guides/rel_notes/deprecation.rst
index 17f90a6352..f9f1d97f78 100644
--- a/doc/guides/rel_notes/deprecation.rst
+++ b/doc/guides/rel_notes/deprecation.rst
@@ -158,3 +158,11 @@ Deprecation Notices
* net/iavf: The dynamic mbuf field used to detect LLDP packets on the
transmit path in the iavf PMD will be removed in a future release.
After removal, only packet type-based detection will be supported.
+
+* ethdev: The legacy filtering data structures in ``rte_eth_ctrl.h``,
+ including the flow director (FDIR) types (``rte_eth_fdir_*``, ``rte_fdir_*``)
+ and the ntuple/ethertype/SYN/tunnel/input-set filter types, are superseded
+ by the ``rte_flow`` API and will be removed in DPDK 26.11. The associated
+ driver-facing definitions in ``ethdev_driver.h`` (``RTE_ETH_FILTER_FDIR``,
+ ``struct rte_eth_fdir_conf``, ``enum rte_eth_fdir_pballoc_type``,
+ ``enum rte_fdir_status_mode``) will be removed at the same time.
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v15 0/6] Add AI code review tools and AGENTS
From: Thomas Monjalon @ 2026-05-28 18:09 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
In-Reply-To: <20260521224550.927338-1-stephen@networkplumber.org>
22/05/2026 00:44, Stephen Hemminger:
> Add guidelines and tooling for AI-assisted code review of DPDK
> patches.
>
> AGENTS.md provides a two-tier review framework: correctness bugs
> (resource leaks, use-after-free, race conditions) are reported at
> >=50% confidence; style issues require >80% with false positive
> suppression. Mechanical checks handled by checkpatches.sh are
> excluded to avoid redundant findings.
>
> The analyze-patch.py and review-doc.py scripts support multiple AI
> providers (Anthropic, OpenAI, xAI, Google) with mbox splitting,
> prompt caching, direct SMTP sending, and token usage tracking with
> optional cost estimation.
>
> v15 - reflow AGENTS per doc guidelines
> - reduce redundancy in AGENTS (i.e don't overlap other tools)
> - move scripts to devtools/ai
> - move common code from scripts to one file
> - incorporate AI and human reviews
Applied with minor adjustments, thanks.
Note: as discussed together, I've renamed analyze-patch.py
to review-patch.py for consistency.
Let's see how the community use and improve this new tooling.
^ permalink raw reply
* Re: [PATCH] doc: announce removal of flow director
From: Thomas Monjalon @ 2026-05-28 18:19 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
In-Reply-To: <20260528170059.155964-1-stephen@networkplumber.org>
28/05/2026 19:00, Stephen Hemminger:
> There are still leftover bits in ethdev related to flow director (fdir).
> Will be removing these in 26.11 so give final notice.
>
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> ---
> +* ethdev: The legacy filtering data structures in ``rte_eth_ctrl.h``,
> + including the flow director (FDIR) types (``rte_eth_fdir_*``, ``rte_fdir_*``)
> + and the ntuple/ethertype/SYN/tunnel/input-set filter types, are superseded
> + by the ``rte_flow`` API and will be removed in DPDK 26.11. The associated
> + driver-facing definitions in ``ethdev_driver.h`` (``RTE_ETH_FILTER_FDIR``,
> + ``struct rte_eth_fdir_conf``, ``enum rte_eth_fdir_pballoc_type``,
> + ``enum rte_fdir_status_mode``) will be removed at the same time.
The problem is that some drivers rely on these structures for rte_flow.
Note: this file is big, please keep it sorted by moving this
close to other ethdev and flow deprecations.
^ permalink raw reply
* Re: [EXTERNAL] Re: [PATCH v3 0/2] net/mana: add device reset support
From: Stephen Hemminger @ 2026-05-28 18:24 UTC (permalink / raw)
To: Wei Hu; +Cc: Wei Hu, dev@dpdk.org, Long Li
In-Reply-To: <SE3P153MB18483B2B0A91A49747176C88BB092@SE3P153MB1848.APCP153.PROD.OUTLOOK.COM>
On Thu, 28 May 2026 07:30:03 +0000
Wei Hu <weh@microsoft.com> wrote:
> > so they are invisible to rte_rcu_qsbr_check in the primary, and
> > the secondary MP handler (mana_mp_reset_enter) does not call
> > qsbr_check at all -- it just sets db_page = NULL and munmaps.
> >
> > The dev_state check at the top of secondary tx_burst is racy:
> > the page can be munmapped after the in-loop read of db_page but
> > before the doorbell write at the bottom. The "All secondary
> > threads are quiescent" log line in mana_mp_reset_enter is not
> > true.
> >
> > The secondary needs a real reader-side primitive -- its own
> > qsbr with secondary lcore registration, or an rwlock the MP
> > handler takes before munmap.
> >
>
> Thanks for the v3 review, @Stephen. I will send out v4 to incorporate most
> of the review comments except for this one.
>
> The review on this point is not correct. Here I am providing analysis from
> AI and my own test results to show why.
>
> The concern is that "rte_rcu_qsbr_thread_register is only called
> from mana_dev_configure, which the secondary never runs", so
> secondary tids are unregistered and invisible to rte_rcu_qsbr_check.
Thanks, I have become way to familiar with AI reaching false conclusions.
^ permalink raw reply
* Re: [PATCH] doc: announce removal of flow director
From: Stephen Hemminger @ 2026-05-28 18:29 UTC (permalink / raw)
To: Thomas Monjalon; +Cc: dev
In-Reply-To: <ED4MTOXhQ4iCVj0Gg4IMwg@monjalon.net>
On Thu, 28 May 2026 20:19:15 +0200
Thomas Monjalon <thomas@monjalon.net> wrote:
> 28/05/2026 19:00, Stephen Hemminger:
> > There are still leftover bits in ethdev related to flow director (fdir).
> > Will be removing these in 26.11 so give final notice.
> >
> > Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> > ---
> > +* ethdev: The legacy filtering data structures in ``rte_eth_ctrl.h``,
> > + including the flow director (FDIR) types (``rte_eth_fdir_*``, ``rte_fdir_*``)
> > + and the ntuple/ethertype/SYN/tunnel/input-set filter types, are superseded
> > + by the ``rte_flow`` API and will be removed in DPDK 26.11. The associated
> > + driver-facing definitions in ``ethdev_driver.h`` (``RTE_ETH_FILTER_FDIR``,
> > + ``struct rte_eth_fdir_conf``, ``enum rte_eth_fdir_pballoc_type``,
> > + ``enum rte_fdir_status_mode``) will be removed at the same time.
>
> The problem is that some drivers rely on these structures for rte_flow.
>
> Note: this file is big, please keep it sorted by moving this
> close to other ethdev and flow deprecations.
There are three cases of drivers using fdir:
- simple ones which are just borrowing things like rte_filter_type
addressing these is trivial; mostly just keep rte_filter_type for now
- Intel ones like i40e and ixgbe which still expose fdir api's.
Can deprecate the PMD API's and move the fdir structures into intel commonc code.
- drivers where fdir was used in variable names but never really used the infrastructure.
Will have proposed patches in a few days.
^ permalink raw reply
* [PATCH] examples: Fix vm_power_manager scratch area to /run/dpdk/powermanager
From: Sudheendra Sampath @ 2026-05-28 19:04 UTC (permalink / raw)
To: dev; +Cc: Sudheendra Sampath, Anatoly Burakov, Sivaprasad Tummala
This patch for bug 1832 will do the following:
1. If /run/dpdk is not present, it will create it first with and
then create powermanager directory underneath it.
2. If /run/dpdk is present, it will verify it is actually a directory
before creating subdirectory, powermanager.
All directory permissions are 0700.
Signed-off-by: Sudheendra Sampath <giveback4fun@gmail.com>
---
examples/vm_power_manager/channel_manager.c | 27 ++++++++++++++++++++-
examples/vm_power_manager/channel_manager.h | 2 +-
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/examples/vm_power_manager/channel_manager.c b/examples/vm_power_manager/channel_manager.c
index b69449c61d..60d767ea98 100644
--- a/examples/vm_power_manager/channel_manager.c
+++ b/examples/vm_power_manager/channel_manager.c
@@ -420,8 +420,33 @@ add_all_channels(const char *vm_name)
if (d == NULL) {
RTE_LOG(ERR, CHANNEL_MANAGER, "Error opening directory '%s': %s\n",
CHANNEL_MGR_SOCKET_PATH, strerror(errno));
- return -1;
+
+ const char *run_dpdk = "/run/dpdk";
+ struct stat path_stat;
+ int ret;
+
+ if (stat(run_dpdk, &path_stat) != 0) {
+ ret = mkdir(run_dpdk, 0700);
+ if (ret < 0 && errno != EEXIST) {
+ RTE_LOG(ERR, CHANNEL_MANAGER, "Error creating '%s': %s",
+ run_dpdk, strerror(errno));
+ return -1;
+ }
+ }
+
+ /* Make sure /run/dpdk is a directory */
+ if (!S_ISDIR(path_stat.st_mode)) {
+ return -1;
+ }
+
+ ret = mkdir(CHANNEL_MGR_SOCKET_PATH, 0700);
+ if (ret < 0 && errno != EEXIST) {
+ RTE_LOG(ERR, CHANNEL_MANAGER, "Error creating '%s': %s",
+ CHANNEL_MGR_SOCKET_PATH, strerror(errno));
+ return -1;
+ }
}
+
while ((dir = readdir(d)) != NULL) {
if (!strncmp(dir->d_name, ".", 1) ||
!strncmp(dir->d_name, "..", 2))
diff --git a/examples/vm_power_manager/channel_manager.h b/examples/vm_power_manager/channel_manager.h
index 6f70539815..5fc93ae0be 100644
--- a/examples/vm_power_manager/channel_manager.h
+++ b/examples/vm_power_manager/channel_manager.h
@@ -22,7 +22,7 @@ extern "C" {
#define CHANNEL_MGR_DEFAULT_HV_PATH "qemu:///system"
/* File socket directory */
-#define CHANNEL_MGR_SOCKET_PATH "/tmp/powermonitor/"
+#define CHANNEL_MGR_SOCKET_PATH "/run/dpdk/powermonitor/"
/* FIFO file name template */
#define CHANNEL_MGR_FIFO_PATTERN_NAME "fifo"
--
2.43.0
^ permalink raw reply related
* RE: [RFC v3 2/3] lib: add fastmem library
From: Morten Brørup @ 2026-05-28 19:56 UTC (permalink / raw)
To: Varghese, Vipin, Bruce Richardson, Mattias Rönnblom, dev
Cc: Konstantin Ananyev, Mattias Rönnblom, Yogaraj Baskaravel,
Stephen Hemminger
In-Reply-To: <DS0PR12MB6608CB2B4897A5CB91D2D1C482092@DS0PR12MB6608.namprd12.prod.outlook.com>
> From: Varghese, Vipin [mailto:Vipin.Varghese@amd.com]
> Sent: Thursday, 28 May 2026 16.45
>
> Public
>
> Hi @Morten Brørup
>
> <snipped>
>
> >
> > > +/**
> > > + * Pre-reserve backing memory.
> > > + *
> > > + * Ensures that at least @p size bytes of memzone-backed memory
> are
> > > + * available to the allocator on @p socket_id, reserving
> additional
> > > + * memzones from EAL as needed to reach that total. Subsequent
> > > + * allocations served from the pre-reserved memory do not incur
> > > + * memzone-reservation cost.
> > > + *
> > > + * The reservation is cumulative: repeated calls to
> > > + * rte_fastmem_reserve() with the same @p socket_id grow the
> > > + * reservation monotonically. Reserved memory is never returned to
> > > + * the system during the allocator's lifetime.
> > > + *
> > > + * A typical use is to call rte_fastmem_reserve() once at
> > > + * application startup, with a size chosen to cover the expected
> > > + * steady-state working set. Allocations and frees during
> > > + * steady-state operation then avoid memzone reservations
> entirely.
> > > + *
> > > + * @param size
> > > + * The minimum amount of backing memory, in bytes, to make
> > > + * available on @p socket_id. The allocator may reserve more than
> > > + * the requested amount due to internal rounding (e.g., to
> memzone
> > > + * or block granularity).
> > > + *
> > > + * @param socket_id
> > > + * The NUMA socket on which to reserve memory, or SOCKET_ID_ANY
> > > + * to leave the choice to the allocator. With SOCKET_ID_ANY, the
> > > + * allocator starts on the calling lcore's socket (or the first
> > > + * configured socket if the caller is not bound to one) and falls
> > > + * back to other sockets if the preferred socket cannot satisfy
> > > + * the reservation.
> > > + *
> > > + * @return
> > > + * - 0: Success.
> > > + * - -ENOMEM: Insufficient huge-page memory to satisfy the
> request.
> > > + * - -EINVAL: Invalid @p socket_id.
> > > + */
> > > +__rte_experimental
> > > +int
> > > +rte_fastmem_reserve(size_t size, int socket_id);
> >
> > @Bruce,
> > I vaguely recall that we discussed something about busses and sockets
> a long time
> > ago, but I cannot remember the details.
> > Is socket_id the right type (and parameter name) to identify a memory
> bus?
> >
> > @Vipin,
> > You have been working on topology awareness. Same question to you:
> > Is socket_id the right type (and parameter name) to identify a memory
> bus?
>
> Short answer: socket_id is no longer a precise or sufficient
> abstraction to represent a memory bus.
> Based on the topology work with libhwloc, we’ve observed the following
> across Ampere, Intel, and AMD platforms:
>
> Features like SNC (Sub-NUMA Clustering) on Intel and NPS (NUMA Per
> Socket) on AMD change how socket_id maps to hardware.
> In these modes:
>
> 1) A single physical socket can expose multiple NUMA domains.
> 2)These NUMA domains align more closely with memory controller
> groupings (i.e., memory buses) rather than the full socket.
>
>
> Depending on the architecture:
> a) Memory controllers may be collocated with compute cores or placed on
> separate tiles.
> b) As a result, socket_id can represent different scopes (full socket
> vs. sub-socket domains), making it inconsistent.
>
>
>
> Hence practically: In some configurations, socket_id ≈ memory domain.
> In others, it is coarser than the actual memory bus topology.
>
> To address this ambiguity, in the topology patches (v5/v6), we are
> moving toward clearer separation:
>
> a. Cache domains (L1/L2/L3/L4) for compute locality
> b. NUMA domains (memory + IO) as the unit for allocation locality
>
> This direction better reflects real hardware and avoids overloading
> socket_id with multiple meanings.
>
> Happy to align this with the topology model we’re introducing so the
> abstraction remains consistent going forward.
> Thanks,
> Vipin
Thank you for the quick and detailed response, Vipin!
I haven't looked deeply into the v5/v6 topology patches yet (it's on my TODO list).
The rte_fastmem library builds on top of the rte_memzone library.
So, if the rte_memzone library is updated to replace the meaning of its "socket_id" parameter with some NUMA domain identifier (we better rename the "socket_id" to a new name "numa_domain_id"), then the rte_fastmem library could remain unaffected, and its "socket_id" parameter would be passed on directly to the rte_memzone library's "numa_domain_id"?
This is my conclusion: At this point, proper support for allocating memory in specific NUMA domains is an rte_memzone library issue, and nothing to worry about for the rte_fastmem library - it will be automatically supported in rte_fastmem when supported by rte_memzone.
^ permalink raw reply
* [PATCH] net/iavf: fix vectorization high ping latency
From: Anurag Mandal @ 2026-05-28 20:51 UTC (permalink / raw)
To: dev; +Cc: bruce.richardson, vladimir.medvedkin, Anurag Mandal, stable
High ping latency is observed when icmp echo requests are
sent from a VPP VF and icmp echo replies get delayed by
~3 seconds which at times, also results in packet loss.
With WB_ON_ITR, the descriptor writeback interval lives in
the IAVF_VFINT_ITRN1 register. This register only resets
during a VF reset, so it could be left with a stale or
uninitialized value causing unreliable or high latency
writeback like high ping latency with VPP VF.
This patch fixes the issue by adding explicit reinitialization
of the separate ITR index interval register (IAVF_VFINT_ITRN1)
to a known 2us value and does not rely on the dynamic control
register (IAVF_VFINT_DYN_CTLN1) for the same.
Also, added a low interval value of 2us in IAVF_VFINT_DYN_CTLN1
ITR index 0 ensuring prompt writeback in polling mode
regardless of what the PF's adaptive algorithm has set in ITRN.
Fixes: ead06572bd8f ("net/iavf: fix performance with writeback policy")
Fixes: a08f9cb698c3 ("net/iavf: fix Rx queue interrupt setting")
Cc: stable@dpdk.org
Signed-off-by: Anurag Mandal <anurag.mandal@intel.com>
---
drivers/net/intel/iavf/iavf_ethdev.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index bdf650b822..971c10cefe 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -862,6 +862,16 @@ static int iavf_config_rx_queues_irqs(struct rte_eth_dev *dev,
(0 << IAVF_VFINT_DYN_CTLN1_ITR_INDX_SHIFT) |
IAVF_VFINT_DYN_CTLN1_WB_ON_ITR_MASK |
(2UL << IAVF_VFINT_DYN_CTLN1_INTERVAL_SHIFT));
+ /* The interval value lives in the separate IAVF_VFINT_ITRN1
+ * index register, which is only cleared on a VF reset
+ * It is not implicitly re-initialized by the DYN_CTLN1 write
+ * above, so if it was left dirty by a previous configuration,
+ * program it explicitly here to the same 2us interval for
+ * ITR index 0.
+ */
+ IAVF_WRITE_REG(hw,
+ IAVF_VFINT_ITRN1(0, vf->msix_base - 1),
+ 2UL);
/* debug - check for success! the return value
* should be 2, offset is 0x2800
*/
@@ -2078,9 +2088,16 @@ iavf_dev_rx_queue_intr_disable(struct rte_eth_dev *dev, uint16_t queue_id)
return -EIO;
}
+ /* Set the ITR for index zero, to 2us to make sure that
+ * sufficient time for aggregation to occur, but not to
+ * increase the latency drastically.
+ */
+
IAVF_WRITE_REG(hw,
IAVF_VFINT_DYN_CTLN1(msix_intr - IAVF_RX_VEC_START),
- IAVF_VFINT_DYN_CTLN1_WB_ON_ITR_MASK);
+ (0 << IAVF_VFINT_DYN_CTLN1_ITR_INDX_SHIFT) |
+ IAVF_VFINT_DYN_CTLN1_WB_ON_ITR_MASK |
+ (2UL << IAVF_VFINT_DYN_CTLN1_INTERVAL_SHIFT));
IAVF_WRITE_FLUSH(hw);
return 0;
--
2.25.1
^ permalink raw reply related
* Re: [v6 0/2] net/hinic3: Fix the hinic3 driver issue
From: Stephen Hemminger @ 2026-05-28 21:00 UTC (permalink / raw)
To: Feifei Wang; +Cc: dev
In-Reply-To: <20260528130122.1213-1-wff_light@vip.163.com>
On Thu, 28 May 2026 21:01:18 +0800
Feifei Wang <wff_light@vip.163.com> wrote:
> v1: The RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO flag is added to support the VXLAN TSO function
>
> v2: Modify the commit information issue and supplement the commit information
>
> v3: Revise review comments. First, deterine whether the hardware supports it, then add the flag bit
>
> v4: Fix the compilation error caused by leading spaces
>
> v5: Add fix tag
>
> v6: MOdify SP230 NIC vf device id
>
> Feifei Wang (2):
> net/hinic3: Fix VXLAN TSO issue
> net/hinic3: Modify SP230 VF device id
>
> drivers/net/hinic3/base/hinic3_csr.h | 2 +-
> drivers/net/hinic3/hinic3_ethdev.c | 8 ++++++--
> 2 files changed, 7 insertions(+), 3 deletions(-)
>
Applied to next-net.
Needed to add Fixes, and fix the Signed-off-by mail address on second patch.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox