DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 04/17] crypto/scheduler: replace strncpy with strlcpy
From: Bruce Richardson @ 2026-06-23 16:59 UTC (permalink / raw)
  To: dev; +Cc: stable, Kai Ji, Akhil Goyal, Fan Zhang, Adam Dybkowski,
	Ruifeng Wang
In-Reply-To: <20260623165150.765443-5-bruce.richardson@intel.com>

On Tue, Jun 23, 2026 at 05:51:34PM +0100, Bruce Richardson wrote:
> Replace strncpy() with safer strlcpy() which always null-terminates.
> 
> Fixes: 85b00824aeef ("crypto/scheduler: rename slave to worker")

Correction, this is not the introducer commit. Instead it's:

Fixes: 50e14527b9d1 ("crypto/scheduler: improve parameters parsing")


> Cc: stable@dpdk.org
> 
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  drivers/crypto/scheduler/scheduler_pmd.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/crypto/scheduler/scheduler_pmd.c b/drivers/crypto/scheduler/scheduler_pmd.c
> index 95ce893f05..ceaefa329b 100644
> --- a/drivers/crypto/scheduler/scheduler_pmd.c
> +++ b/drivers/crypto/scheduler/scheduler_pmd.c
> @@ -229,10 +229,10 @@ cryptodev_scheduler_create(const char *name,
>  			return -ENOMEM;
>  		}
>  
> -		strncpy(sched_ctx->init_worker_names[
> +		strlcpy(sched_ctx->init_worker_names[
>  					sched_ctx->nb_init_workers],
>  				init_params->worker_names[i],
> -				RTE_CRYPTODEV_SCHEDULER_NAME_MAX_LEN - 1);
> +				RTE_CRYPTODEV_SCHEDULER_NAME_MAX_LEN);
>  
>  		sched_ctx->nb_init_workers++;
>  	}
> @@ -443,8 +443,8 @@ parse_worker_arg(const char *key __rte_unused,
>  		return -ENOMEM;
>  	}
>  
> -	strncpy(param->worker_names[param->nb_workers++], value,
> -			RTE_CRYPTODEV_SCHEDULER_NAME_MAX_LEN - 1);
> +	strlcpy(param->worker_names[param->nb_workers++], value,
> +			RTE_CRYPTODEV_SCHEDULER_NAME_MAX_LEN);
>  
>  	return 0;
>  }
> -- 
> 2.53.0
> 

^ permalink raw reply

* Re: [PATCH 14/17] net/qede: replace strncpy with strlcpy
From: Bruce Richardson @ 2026-06-23 17:02 UTC (permalink / raw)
  To: dev; +Cc: stable, Devendra Singh Rawat, Alok Prasad, Harish Patil
In-Reply-To: <20260623165150.765443-15-bruce.richardson@intel.com>

On Tue, Jun 23, 2026 at 05:51:44PM +0100, Bruce Richardson wrote:
> Replace strncpy() with safer strlcpy() which always null-terminates.
> 
> Fixes: 7eca78cefde8 ("net/qede: fix driver version string")

Correction:
Fixes: 2ea6f76aff40 ("qede: add core driver")


> Cc: stable@dpdk.org
> 
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  drivers/net/qede/qede_ethdev.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/qede/qede_ethdev.c b/drivers/net/qede/qede_ethdev.c
> index 4efc2dd349..2f37f956cb 100644
> --- a/drivers/net/qede/qede_ethdev.c
> +++ b/drivers/net/qede/qede_ethdev.c
> @@ -2596,7 +2596,7 @@ static int qede_common_dev_init(struct rte_eth_dev *eth_dev, bool is_vf)
>  	params.drv_minor = QEDE_PMD_VERSION_MINOR;
>  	params.drv_rev = QEDE_PMD_VERSION_REVISION;
>  	params.drv_eng = QEDE_PMD_VERSION_PATCH;
> -	strncpy((char *)params.name, QEDE_PMD_VER_PREFIX,
> +	strlcpy((char *)params.name, QEDE_PMD_VER_PREFIX,
>  		QEDE_PMD_DRV_VER_STR_SIZE);
>  
>  	qede_assign_rxtx_handlers(eth_dev, true);
> -- 
> 2.53.0
> 

^ permalink raw reply

* RE: [PATCH v3 0/9] ENETC driver related changes series
From: Gagandeep Singh @ 2026-06-23 17:21 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: dev@dpdk.org, Hemant Agrawal
In-Reply-To: <20260623084657.0923ab04@phoenix.local>

Hi,

> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Tuesday, June 23, 2026 9:17 PM
> To: Gagandeep Singh <G.Singh@nxp.com>
> Cc: dev@dpdk.org; Hemant Agrawal <hemant.agrawal@nxp.com>
> Subject: Re: [PATCH v3 0/9] ENETC driver related changes series
> 
> On Tue, 23 Jun 2026 11:29:55 +0530
> Gagandeep Singh <g.singh@nxp.com> wrote:
> 
> > V3 changes:
> >   - Added documentation for all devargs in enetc4.rst.
> >   - Fixed kvlist memory leak issue.
> >
> > V2 changes:
> >   - Fixed an un-used variable compilation issue reported on fedora:43-gcc-
> minsize
> >   - Fixed various AI reported issues:
> > 	- Release notes updated for all new devargs
> > 	- enect4.ini features doc updated for scattered RX.
> > 	- removed Not required RTE_PTYPE_UNKNOWN.
> > 	- Fixed mid-frame mbuf leak in SG case.
> > 	- Enabled SG for enetc4 PF also.
> > 	- move to calloc from rte_zmalloc in parse_txq_prior().
> > 	- added vaidation checks on strdup, strtoul.
> > 	- added NC devargs to use cacheable ops conditionally.
> > 	- removed dead code like bd_base_p etc.
> > 	- Fixed rte_cpu_to_le_16() conversion on flags and combined
> > 	  all flags related patches in one patch.
> > 	- Fixed memory leak issue due to TXQ priority patch.
> >    - There were some false positives, I have ignored them:
> > 	Race condition on flags field:
> > 		clean_tx_ring only touches HW-completed BDs
> (next_to_clean→hwci),
> > 		never newly-submitted BDs; doorbell hasn't fired yet.
> > 	Missing dcbf in clean_tx_ring:
> > 		DPDK is single-threaded per queue; TX path always overwrites
> > 		flags completely before dcbf.
> > 	TX dcbf granularity with wrap:
> > 		Safe (AI admits it).
> > 	RX refill flush at wrap:
> > 		In-loop dcbf at i & mask == 0 already flushes aligned groups;
> > 		trailing flush only needed for partial groups.
> > 	RX reading before invalidate:
> > 		dccivac precedes the read for every group in the loop
> >
> > Gagandeep Singh (7):
> >   net/enetc: fix TX BD structure
> >   net/enetc: fix queue initialization
> >   net/enetc: support ESP packet type in packet parsing
> >   net/enetc: update random MAC generation code
> >   net/enetc: add option to disable VSI messaging
> >   net/enetc: add devargs to control VSI-PSI timeout and delay
> >   net/enetc4: add cacheable BD ring support with SW cache maintenance
> >
> > Vanshika Shukla (2):
> >   net/enetc: support scatter-gather
> >   net/enetc: set user configurable priority to TX rings
> >
> >  doc/guides/nics/enetc4.rst             |  62 +++-
> >  doc/guides/nics/features/enetc4.ini    |   1 +
> >  doc/guides/rel_notes/release_26_07.rst |  10 +
> >  drivers/net/enetc/base/enetc_hw.h      |  13 +-
> >  drivers/net/enetc/enetc.h              |  31 +-
> >  drivers/net/enetc/enetc4_ethdev.c      | 172 ++++++++--
> >  drivers/net/enetc/enetc4_vf.c          | 206 ++++++++++--
> >  drivers/net/enetc/enetc_ethdev.c       |  25 +-
> >  drivers/net/enetc/enetc_rxtx.c         | 430 ++++++++++++++++++++++---
> >  9 files changed, 831 insertions(+), 119 deletions(-)
> >
> 
> Did followup AI review and it had some more things that need fixing:
> 
> Error
> =====
> 
> [PATCH v2 7/9] net/enetc: add devargs to control VSI-PSI timeout and delay
> 
> drivers/net/enetc/enetc4_vf.c, enetc4_vf_dev_init()
> 
>   kvlist is leaked on the two invalid-value error paths. It is
>   allocated by rte_kvargs_parse() (line 1347) and only freed at
>   line 1385, but both
> 
>       return -1;   /* invalid VSI Timeout, line 1367 */
>       return -1;   /* invalid VSI Delay,   line 1380 */
> 
>   return before that free. A malformed enetc4_vsi_timeout= or
>   enetc4_vsi_delay= leaks the kvargs structure on every probe.
> 
>   Free before returning, e.g.:
> 
>       if (errno != 0 || hw->vsi_timeout == 0) {
>               ENETC_PMD_ERR("Invalid VSI Timeout value = %u",
>                               hw->vsi_timeout);
>               rte_kvargs_free(kvlist);
>               return -1;
>       }
> 
>   (same for the delay path), or restructure with a goto.
> 
> 
> Warning
> =======
> 
> Series (patches 6-9)
> 
>   The new runtime devargs - enetc4_vsi_disable, enetc4_vsi_timeout,
>   enetc4_vsi_delay, enetc4_txq_prior, and nc - are registered via
>   RTE_PMD_REGISTER_PARAM_STRING and noted in the release notes, but
>   doc/guides/nics/enetc4.rst has no Runtime Configuration section
>   describing them. Convention is to document devargs in the NIC guide
>   so users can find the syntax (e.g. the nc=1 / 'a|b|c' priority list
>   formats are non-obvious).
> 
> Info
> ====
> 
> [PATCH v2 5/9] and [PATCH v2 9/9] - RX multi-segment reassembly
> 
>   In enetc_clean_rx_ring_nc() and enetc_clean_rx_ring_cacheable(),
>   on the frame-last BD:
> 
>       first_seg->pkt_len -= rx_ring->crc_len;
> 
>   reduces pkt_len but leaves the final segment's data_len unchanged,
>   so pkt_len != sum(data_len) when crc_len is non-zero. The old
>   single-segment path kept them equal (pkt_len = data_len = buf_len
>   - crc_len).
> 
>   This is currently unreachable: enetc4 does not advertise
>   RTE_ETH_RX_OFFLOAD_KEEP_CRC, so crc_len is always 0 and the
>   subtraction is a no-op. Flagging only so the asymmetry is on record
>   if KEEP_CRC is ever added - at that point the last segment's
>   data_len would need the same adjustment (and the CRC may straddle
>   the last two segments).

It seems that AI reviewed the V2 series instead of the V3 series. I have manually verified that the first two issues are fixed in the V3 series. For the last issue, I will submit a separate patch as a new feature, introducing KEEP_CRC for the enetc4 driver. Additionally, AI has acknowledged that there will be no functional issue as of now, since crc_len will be only 0.

^ permalink raw reply

* Re: [PATCH v3 0/9] ENETC driver related changes series
From: Stephen Hemminger @ 2026-06-23 17:32 UTC (permalink / raw)
  To: Gagandeep Singh; +Cc: dev@dpdk.org, Hemant Agrawal
In-Reply-To: <AMBPR04MB1228197B164EA32B50E63FC03E1EE2@AMBPR04MB12281.eurprd04.prod.outlook.com>

On Tue, 23 Jun 2026 17:21:59 +0000
Gagandeep Singh <G.Singh@nxp.com> wrote:

> It seems that AI reviewed the V2 series instead of the V3 series. I have manually verified that the first two issues are fixed in the V3 series. For the last issue, I will submit a separate patch as a new feature, introducing KEEP_CRC for the enetc4 driver. Additionally, AI has acknowledged that there will be no functional issue as of now, since crc_len will be only 0.


It was my bad, was looking earlier in that chat.
Looks clean will apply

For V3:

v3 re-review. I diffed every patch against v2; the only substantive code change is in 7/9, and the rest of the delta is documentation.

Both v2 blockers are resolved:

The kvlist leak in enetc4_vf_dev_init() (was Error) is fixed — rte_kvargs_free(kvlist) now precedes both the invalid-timeout and invalid-delay return -1 paths.

The missing devargs documentation (was Warning) is now in doc/guides/nics/enetc4.rst: a new "Driver Arguments (devargs)" section split into VF-specific (enetc4_vsi_disable, enetc4_vsi_timeout, enetc4_vsi_delay) and PF/Common (enetc4_txq_prior, nc) subsections, each with a usage example. Section underlines are the correct length (19/19 and 17/17), so it builds clean under -W; defaults and applicability match the code.

No new issues introduced — patches 1–6 and 8 are byte-identical to v2 apart from the doc hunks, and patch 9's only delta is the nc doc plus line-number offsets shifted by patch 7's two added lines.

For the record, the one item carried over from v2 is unchanged and remains non-blocking: in the RX reassembly paths, first_seg->pkt_len -= crc_len at frame-last doesn't adjust the final segment's data_len, so pkt_len != sum(data_len) if crc_len != 0. This stays latent because enetc4 doesn't advertise RTE_ETH_RX_OFFLOAD_KEEP_CRC (crc_len is always 0). Worth a guard only if KEEP_CRC is ever added; not a reason to hold the series.

No open errors, no open warnings.

^ permalink raw reply

* Re: [PATCH 00/17] drivers: replace strncpy with strlcpy
From: Stephen Hemminger @ 2026-06-23 17:33 UTC (permalink / raw)
  To: Bruce Richardson; +Cc: dev
In-Reply-To: <20260623165150.765443-1-bruce.richardson@intel.com>

On Tue, 23 Jun 2026 17:51:30 +0100
Bruce Richardson <bruce.richardson@intel.com> wrote:

> Many uses of strncpy in DPDK drivers can be directly replaced by
> a call to strlcpy instead, which is safer in that it always null-
> terminates the string. This AI assisted patchset makes those
> simple replacements, adjusting lengths as appropriate.
> 
> After this set, there are still a number of drivers with strncpy calls
> in them, but those are not simple strncpy->strlcpy replacements, so
> left for later rework.

Maybe a coccinelle script?

^ permalink raw reply

* [PATCH v2] dts: report dut/NIC info during DTS run
From: Koushik Bhargav Nimoji @ 2026-06-23 18:14 UTC (permalink / raw)
  To: luca.vizzarro, patrickrobb1997
  Cc: dev, abailey, ahassick, lylavoie, Koushik Bhargav Nimoji
In-Reply-To: <20260602163647.101815-1-knimoji@iol.unh.edu>

This patch gathers NIC info during a DTS run and writes it to an output
json file. This allows the json file to be used when reporting results
on the DTS results dashboard.

Signed-off-by: Koushik Bhargav Nimoji <knimoji@iol.unh.edu>
---
v2:
    *Resolved merge conflicts
---
 dts/framework/test_run.py                    | 10 +++
 dts/framework/testbed_model/linux_session.py | 75 ++++++++++++++++++++
 dts/framework/testbed_model/os_session.py    | 11 +++
 3 files changed, 96 insertions(+)

diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 94dc6023a7..eebea280d7 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -98,6 +98,7 @@
         "InternalError" -> "exit":ew
 """
 
+import json
 import random
 from collections import deque
 from collections.abc import Iterable
@@ -370,6 +371,15 @@ def next(self) -> State | None:
         test_run.supported_capabilities = get_supported_capabilities(
             test_run.ctx.sut_node, test_run.ctx.topology, test_run.required_capabilities
         )
+
+        used_nic_info: list[dict[str, object]] = (
+            self.test_run.ctx.sut_node.main_session.get_nic_info()
+        )
+        with open(f"{SETTINGS.output_dir}/dut_info.json", "w") as file:
+            json.dump(used_nic_info, file, indent=3)
+            file.close()
+        self.logger.info(f"DUT NIC info written to: {SETTINGS.output_dir}/dut_info.json")
+
         return TestRunExecution(test_run, self.result)
 
     def on_error(self, ex: BaseException) -> State | None:
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 30c89ad70d..5f4b78cf1c 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -197,6 +197,81 @@ def unbind_ports(self, ports: list[Port]):
         if self._lshw_net_info:
             del self._lshw_net_info
 
+    def get_nic_info(self) -> list[dict[str, object]]:
+        """Overrides :meth`~.os_session.OSSession.get_nic_info`.
+
+        Raises:
+            ConfigurationError: If the NIC info could not be found.
+        """
+        port_data = {
+            port.get("businfo"): port for port in self._lshw_net_info if port.get("businfo")
+        }
+
+        all_nic_info = []
+        for port in self._config.ports:
+            pci_addr = port.pci
+
+            command_result = self.send_command(
+                f"sudo lshw -c network -businfo | grep '{pci_addr}' | cut -d'@' -f1"
+            )
+            bus_type = (
+                command_result.stdout
+                if command_result.return_code == 0 and command_result.stdout
+                else None
+            )
+            if bus_type is None:
+                raise ConfigurationError(f"Unable to get bus type for port {pci_addr}.")
+            bus_info = f"{bus_type}@{pci_addr}"
+
+            nic_port: LshwOutput | None = port_data.get(bus_info)
+            if nic_port is None:
+                raise ConfigurationError(f"Port {pci_addr} could not be found on the node.")
+
+            config: LshwConfigurationOutput | None = nic_port.get("configuration")
+            if config is None:
+                raise ConfigurationError(
+                    f"Configuration info for port {pci_addr} could not be found on the node."
+                )
+
+            command_result = self.send_command(
+                f"sudo lspci -vv -s {pci_addr} | grep 'Engineering changes'"
+            )
+            hardware_version = (
+                command_result.stdout.split(":")[-1].strip()
+                if command_result.return_code == 0 and command_result.stdout
+                else None
+            )
+            if hardware_version is None:
+                self._logger.error(f"Unable to get hardware version for NIC: {pci_addr}")
+
+            nic_name = nic_port.get("logicalname")
+            nic_speed = None
+            if nic_name is not None:
+                command_result = self.send_command(
+                    f"ethtool {nic_name} | grep 'Speed:' | awk '{{print $2}}'"
+                )
+                nic_speed = (
+                    command_result.stdout
+                    if command_result.return_code == 0 and command_result.stdout
+                    else None
+                )
+            if nic_speed is None:
+                self._logger.error(f"Unable to get speed for NIC: {pci_addr}")
+
+            dut_json = {
+                "make": nic_port.get("vendor"),
+                "model": nic_port.get("product"),
+                "hardware version": hardware_version or "Unknown",
+                "firmware version": config.get("firmware"),
+                "deviceBusType": bus_type,
+                "deviceId": nic_port.get("serial"),
+                "pmd": config.get("driver"),
+                "speed": nic_speed or "Unknown",
+            }
+            all_nic_info.append(dut_json)
+
+        return all_nic_info
+
     def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
         """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
 
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index f2dc9b20a9..a16ba50045 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -581,6 +581,17 @@ def unbind_ports(self, ports: list[Port]) -> None:
             ports: The list of ports to unbind.
         """
 
+    @abstractmethod
+    def get_nic_info(self) -> list[dict[str, object]]:
+        """Get NIC information.
+
+        Returns:
+            NIC info as a list of dictionaries.
+
+        Raises:
+            ConfigurationError: If the NIC info could not be found.
+        """
+
     @abstractmethod
     def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
         """Bind `ports` to the given `driver_name`.
-- 
2.54.0


^ permalink raw reply related

* [PATCH v3] dts: report dut/NIC info during DTS run
From: Koushik Bhargav Nimoji @ 2026-06-23 18:49 UTC (permalink / raw)
  To: luca.vizzarro, patrickrobb1997
  Cc: dev, abailey, ahassick, lylavoie, Koushik Bhargav Nimoji
In-Reply-To: <20260602163647.101815-1-knimoji@iol.unh.edu>

This patch gathers NIC info during a DTS run and writes it to an output
json file. This allows the json file to be used when reporting results
on the DTS results dashboard.

Signed-off-by: Koushik Bhargav Nimoji <knimoji@iol.unh.edu>
---
v2:
    *Resolved merge conflicts
v3:
    *Fixed an issue with retrieving
     the NIC's hardware version    
---
 dts/framework/test_run.py                    | 10 +++
 dts/framework/testbed_model/linux_session.py | 64 ++++++++++++++++++++
 dts/framework/testbed_model/os_session.py    | 11 ++++
 3 files changed, 85 insertions(+)

diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 94dc6023a7..eebea280d7 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -98,6 +98,7 @@
         "InternalError" -> "exit":ew
 """
 
+import json
 import random
 from collections import deque
 from collections.abc import Iterable
@@ -370,6 +371,15 @@ def next(self) -> State | None:
         test_run.supported_capabilities = get_supported_capabilities(
             test_run.ctx.sut_node, test_run.ctx.topology, test_run.required_capabilities
         )
+
+        used_nic_info: list[dict[str, object]] = (
+            self.test_run.ctx.sut_node.main_session.get_nic_info()
+        )
+        with open(f"{SETTINGS.output_dir}/dut_info.json", "w") as file:
+            json.dump(used_nic_info, file, indent=3)
+            file.close()
+        self.logger.info(f"DUT NIC info written to: {SETTINGS.output_dir}/dut_info.json")
+
         return TestRunExecution(test_run, self.result)
 
     def on_error(self, ex: BaseException) -> State | None:
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 30c89ad70d..67eba33ce8 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -197,6 +197,70 @@ def unbind_ports(self, ports: list[Port]):
         if self._lshw_net_info:
             del self._lshw_net_info
 
+    def get_nic_info(self) -> list[dict[str, object]]:
+        """Overrides :meth`~.os_session.OSSession.get_nic_info`.
+
+        Raises:
+            ConfigurationError: If the NIC info could not be found.
+        """
+        port_data = {
+            port.get("businfo"): port for port in self._lshw_net_info if port.get("businfo")
+        }
+
+        all_nic_info = []
+        for port in self._config.ports:
+            pci_addr = port.pci
+
+            command_result = self.send_command(
+                f"sudo lshw -c network -businfo | grep '{pci_addr}' | cut -d'@' -f1"
+            )
+            bus_type = (
+                command_result.stdout
+                if command_result.return_code == 0 and command_result.stdout
+                else None
+            )
+            if bus_type is None:
+                raise ConfigurationError(f"Unable to get bus type for port {pci_addr}.")
+            bus_info = f"{bus_type}@{pci_addr}"
+
+            nic_port: LshwOutput | None = port_data.get(bus_info)
+            if nic_port is None:
+                raise ConfigurationError(f"Port {pci_addr} could not be found on the node.")
+
+            config: LshwConfigurationOutput | None = nic_port.get("configuration")
+            if config is None:
+                raise ConfigurationError(
+                    f"Configuration info for port {pci_addr} could not be found on the node."
+                )
+
+            nic_name = nic_port.get("logicalname")
+            nic_speed = None
+            if nic_name is not None:
+                command_result = self.send_command(
+                    f"ethtool {nic_name} | grep 'Speed:' | awk '{{print $2}}'"
+                )
+                nic_speed = (
+                    command_result.stdout
+                    if command_result.return_code == 0 and command_result.stdout
+                    else None
+                )
+            if nic_speed is None:
+                self._logger.error(f"Unable to get speed for NIC: {pci_addr}")
+
+            dut_json = {
+                "make": nic_port.get("vendor"),
+                "model": nic_port.get("product"),
+                "hardware version": nic_port.get("version") or "Unknown",
+                "firmware version": config.get("firmware"),
+                "deviceBusType": bus_type,
+                "deviceId": nic_port.get("serial"),
+                "pmd": config.get("driver"),
+                "speed": nic_speed or "Unknown",
+            }
+            all_nic_info.append(dut_json)
+
+        return all_nic_info
+
     def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
         """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
 
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index f2dc9b20a9..a16ba50045 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -581,6 +581,17 @@ def unbind_ports(self, ports: list[Port]) -> None:
             ports: The list of ports to unbind.
         """
 
+    @abstractmethod
+    def get_nic_info(self) -> list[dict[str, object]]:
+        """Get NIC information.
+
+        Returns:
+            NIC info as a list of dictionaries.
+
+        Raises:
+            ConfigurationError: If the NIC info could not be found.
+        """
+
     @abstractmethod
     def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
         """Bind `ports` to the given `driver_name`.
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v3 6/6] test/bpf: check that bpf_convert can be JIT'd
From: Stephen Hemminger @ 2026-06-23 20:58 UTC (permalink / raw)
  To: Marat Khalili; +Cc: dev@dpdk.org, Konstantin Ananyev
In-Reply-To: <c510bf24a3cd492693fc54f0b558656f@huawei.com>

On Tue, 23 Jun 2026 13:57:35 +0000
Marat Khalili <marat.khalili@huawei.com> wrote:

> > +	{
> > +		struct rte_bpf_jit jit;
> > +
> > +		rte_bpf_get_jit(bpf, &jit);  
> 
> Out of abundance of caution I would also prefill jit with zeroes and check the
> return code here.

Makes sense, but the test already was just doing same thing elsewhere:

static int
run_test(const struct bpf_test *tst)
{
	int32_t ret, rv;
...
	bpf = rte_bpf_load(&tst->prm);
...
	/* repeat the same test with jit, when possible */
	rte_bpf_get_jit(bpf, &jit);
	if (jit.func != NULL) {

^ permalink raw reply

* RE: [EXTERNAL] [PATCH v3 10/11] bus/vmbus: store name in bus specific device
From: Long Li @ 2026-06-23 21:30 UTC (permalink / raw)
  To: David Marchand, dev@dpdk.org
  Cc: thomas@monjalon.net, stephen@networkplumber.org,
	bruce.richardson@intel.com, fengchengwen@huawei.com,
	hemant.agrawal@nxp.com, Wei Hu
In-Reply-To: <20260623105439.2144694-11-david.marchand@redhat.com>

> The device name is allocated with strdup() during scan and freed in several
> places. However, when this bus cleanup is converted to use the EAL generic
> helper, freeing the device object will require a custom helper to also free the
> device name (and for this, a cast will be needed).
> 
> Instead, add an embedded name array to rte_vmbus_device structure (char
> name[RTE_DEV_NAME_MAX_LEN]) which is sufficient for all VMBUS device
> names (UUID format: 36 characters, or shorter legacy format).
> 
> This simplifies the device freeing to a simple free() call.
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> Acked-by: Bruce Richardson <bruce.richardson@intel.com>

Reviewed-by: Long Li <longli@microsoft.com>


> ---
>  drivers/bus/vmbus/bus_vmbus_driver.h |  1 +
> drivers/bus/vmbus/linux/vmbus_bus.c  | 10 +++-------
>  2 files changed, 4 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/bus/vmbus/bus_vmbus_driver.h
> b/drivers/bus/vmbus/bus_vmbus_driver.h
> index 888d856141..706ff1fcf5 100644
> --- a/drivers/bus/vmbus/bus_vmbus_driver.h
> +++ b/drivers/bus/vmbus/bus_vmbus_driver.h
> @@ -38,6 +38,7 @@ enum hv_uio_map {
>   */
>  struct rte_vmbus_device {
>  	struct rte_device device;              /**< Inherit core device */
> +	char name[RTE_DEV_NAME_MAX_LEN];       /**< VMBUS device name
> */
>  	rte_uuid_t device_id;		       /**< VMBUS device id */
>  	rte_uuid_t class_id;		       /**< VMBUS device type */
>  	uint32_t relid;			       /**< id for primary */
> diff --git a/drivers/bus/vmbus/linux/vmbus_bus.c
> b/drivers/bus/vmbus/linux/vmbus_bus.c
> index 77d904ad6d..779ea50b92 100644
> --- a/drivers/bus/vmbus/linux/vmbus_bus.c
> +++ b/drivers/bus/vmbus/linux/vmbus_bus.c
> @@ -280,15 +280,14 @@ vmbus_scan_one(const char *name)
>  	char filename[PATH_MAX];
>  	char dirname[PATH_MAX];
>  	unsigned long tmp;
> -	char *dev_name;
> 
>  	dev = calloc(1, sizeof(*dev));
>  	if (dev == NULL)
>  		return -1;
> 
> -	dev->device.name = dev_name = strdup(name);
> -	if (!dev->device.name)
> +	if (rte_strscpy(dev->name, name, sizeof(dev->name)) < 0)
>  		goto error;
> +	dev->device.name = dev->name;
> 
>  	/* sysfs base directory
>  	 *   /sys/bus/vmbus/devices/7a08391f-f5a0-4ac0-9802-d13fd964f8df
> @@ -305,7 +304,6 @@ vmbus_scan_one(const char *name)
> 
>  	/* skip non-network devices */
>  	if (rte_uuid_compare(dev->class_id, vmbus_nic_uuid) != 0) {
> -		free(dev_name);
>  		free(dev);
>  		return 0;
>  	}
> @@ -330,7 +328,7 @@ vmbus_scan_one(const char *name)
>  		dev->monitor_id = UINT8_MAX;
>  	}
> 
> -	dev->device.devargs = rte_bus_find_devargs(&rte_vmbus_bus,
> dev_name);
> +	dev->device.devargs = rte_bus_find_devargs(&rte_vmbus_bus, dev-
> >name);
> 
>  	dev->device.numa_node = SOCKET_ID_ANY;
>  	if (vmbus_use_numa(dev)) {
> @@ -360,7 +358,6 @@ vmbus_scan_one(const char *name)
>  		} else { /* already registered */
>  			VMBUS_LOG(NOTICE,
>  				"%s already registered", name);
> -			free(dev_name);
>  			free(dev);
>  		}
>  		return 0;
> @@ -371,7 +368,6 @@ vmbus_scan_one(const char *name)
>  error:
>  	VMBUS_LOG(DEBUG, "failed");
> 
> -	free(dev_name);
>  	free(dev);
>  	return -1;
>  }
> --
> 2.54.0


^ permalink raw reply

* RE: [EXTERNAL] [PATCH v3 11/11] bus/vmbus: support unplug
From: Long Li @ 2026-06-23 21:30 UTC (permalink / raw)
  To: David Marchand, dev@dpdk.org
  Cc: thomas@monjalon.net, stephen@networkplumber.org,
	bruce.richardson@intel.com, fengchengwen@huawei.com,
	hemant.agrawal@nxp.com, Wei Hu
In-Reply-To: <20260623105439.2144694-12-david.marchand@redhat.com>

> Add .unplug callback to handle driver removal, device unmapping, and
> interrupt cleanup. This enables use of the generic bus cleanup helper.
> 
> The cleanup function was already performing these operations, so it seems
> safe to expose them through the unplug operation.
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>

Reviewed-by: Long Li <longli@microsoft.com>


> ---
>  doc/guides/rel_notes/release_26_07.rst |  4 +++
>  drivers/bus/vmbus/vmbus_common.c       | 41 ++++++++++++--------------
>  2 files changed, 23 insertions(+), 22 deletions(-)
> 
> diff --git a/doc/guides/rel_notes/release_26_07.rst
> b/doc/guides/rel_notes/release_26_07.rst
> index 5d7aa8d1bf..55d3b44527 100644
> --- a/doc/guides/rel_notes/release_26_07.rst
> +++ b/doc/guides/rel_notes/release_26_07.rst
> @@ -114,6 +114,10 @@ New Features
> 
>    Added no-IOMMU mode for devices without or not enabling IOMMU/SVA.
> 
> +* **Added unplug operation support to VMBUS bus.**
> +
> +  Implemented device unplug operation to allow runtime removal of VMBUS
> devices.
> +
>  * **Added selective Rx in ethdev API.**
> 
>    Some parts of packets may be discarded in Rx diff --git
> a/drivers/bus/vmbus/vmbus_common.c
> b/drivers/bus/vmbus/vmbus_common.c
> index a6e3a24a7c..cd6e851e4c 100644
> --- a/drivers/bus/vmbus/vmbus_common.c
> +++ b/drivers/bus/vmbus/vmbus_common.c
> @@ -144,34 +144,29 @@ rte_vmbus_probe(void)  }
> 
>  static int
> -rte_vmbus_cleanup(struct rte_bus *bus)
> +vmbus_unplug_device(struct rte_device *rte_dev)
>  {
> -	struct rte_vmbus_device *dev;
> -	int error = 0;
> -
> -	RTE_BUS_FOREACH_DEV(dev, bus) {
> -		const struct rte_vmbus_driver *drv;
> -		int ret;
> -
> -		if (!rte_dev_is_probed(&dev->device))
> -			continue;
> -		drv = RTE_BUS_DRIVER(dev->device.driver, *drv);
> -		if (drv->remove == NULL)
> -			continue;
> +	const struct rte_vmbus_driver *drv = RTE_BUS_DRIVER(rte_dev-
> >driver, *drv);
> +	struct rte_vmbus_device *dev = RTE_BUS_DEVICE(rte_dev, *dev);
> +	int ret = 0;
> 
> +	if (drv->remove != NULL) {
>  		ret = drv->remove(dev);
>  		if (ret < 0)
> -			error = -1;
> +			return ret;
> +	}
> 
> -		rte_vmbus_unmap_device(dev);
> -		rte_intr_instance_free(dev->intr_handle);
> +	rte_vmbus_unmap_device(dev);
> +	rte_intr_instance_free(dev->intr_handle);
> +	dev->intr_handle = NULL;
> 
> -		dev->device.driver = NULL;
> -		rte_bus_remove_device(bus, &dev->device);
> -		free(dev);
> -	}
> +	return 0;
> +}
> 
> -	return error;
> +static void
> +vmbus_free_device(struct rte_device *dev) {
> +	free(RTE_BUS_DEVICE(dev, struct rte_vmbus_device));
>  }
> 
>  static int
> @@ -222,10 +217,12 @@ rte_vmbus_unregister(struct rte_vmbus_driver
> *driver)  struct rte_bus rte_vmbus_bus = {
>  	.scan = rte_vmbus_scan,
>  	.probe = rte_bus_generic_probe,
> -	.cleanup = rte_vmbus_cleanup,
> +	.free_device = vmbus_free_device,
> +	.cleanup = rte_bus_generic_cleanup,
>  	.find_device = rte_bus_generic_find_device,
>  	.match = vmbus_bus_match,
>  	.probe_device = vmbus_probe_device,
> +	.unplug_device = vmbus_unplug_device,
>  	.parse = vmbus_parse,
>  	.dev_compare = vmbus_dev_compare,
>  };
> --
> 2.54.0


^ permalink raw reply

* [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support
From: Mark Blasko @ 2026-06-23 21:53 UTC (permalink / raw)
  To: dev, Ciara Loftus, Maryam Tahhan
  Cc: Mark Blasko, Joshua Washington, Jasper Tran O'Leary

Enable dynamic RX timestamping in the AF_XDP Poll Mode Driver.
This extracts the ingress timestamp prepended to the packet
headroom by the XDP program and populates it in the mbuf.

Signed-off-by: Mark Blasko <blasko@google.com>
Reviewed-by: Joshua Washington <joshwash@google.com>
Reviewed-by: Jasper Tran O'Leary <jtranoleary@google.com>
---
 doc/guides/rel_notes/release_26_07.rst |  5 +++
 drivers/net/af_xdp/rte_eth_af_xdp.c    | 56 +++++++++++++++++++++++++-
 2 files changed, 60 insertions(+), 1 deletion(-)

diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index 6eba91a5e9..727442258f 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -63,6 +63,11 @@ New Features
     ``rte_eal_init`` and the application is responsible for probing each device,
   * ``--auto-probing`` enables the initial bus probing, which is the current default behavior.
 
+* **Updated AF_XDP ethernet driver.**
+
+  * Added support for dynamic RX metadata and timestamping offload
+    (``RTE_ETH_RX_OFFLOAD_TIMESTAMP``).
+
 * **Added LinkData sxe2 ethernet driver.**
 
   Added network driver for the LinkData network adapters.
diff --git a/drivers/net/af_xdp/rte_eth_af_xdp.c b/drivers/net/af_xdp/rte_eth_af_xdp.c
index 2cdb533276..c90e232d57 100644
--- a/drivers/net/af_xdp/rte_eth_af_xdp.c
+++ b/drivers/net/af_xdp/rte_eth_af_xdp.c
@@ -62,6 +62,13 @@
 #define PF_XDP AF_XDP
 #endif
 
+struct af_xdp_rx_metadata {
+	uint64_t rx_timestamp;
+};
+
+static int timestamp_dynfield_offset = -1;
+static uint64_t timestamp_dynflag;
+
 RTE_LOG_REGISTER_DEFAULT(af_xdp_logtype, NOTICE);
 #define RTE_LOGTYPE_NET_AF_XDP af_xdp_logtype
 
@@ -144,6 +151,7 @@ struct pkt_rx_queue {
 	struct pollfd fds[1];
 	int xsk_queue_idx;
 	int busy_budget;
+	bool rx_timestamp_enabled;
 };
 
 struct tx_stats {
@@ -398,6 +406,20 @@ af_xdp_rx_zc(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 
 		rte_pktmbuf_pkt_len(bufs[i]) = len;
 		rte_pktmbuf_data_len(bufs[i]) = len;
+
+		if (rxq->rx_timestamp_enabled &&
+		    timestamp_dynfield_offset >= 0) {
+			struct af_xdp_rx_metadata *meta;
+
+			meta = (struct af_xdp_rx_metadata *)
+				((char *)rte_pktmbuf_mtod(bufs[i], void *) -
+				 sizeof(struct af_xdp_rx_metadata));
+			*RTE_MBUF_DYNFIELD(bufs[i],
+					   timestamp_dynfield_offset,
+					   uint64_t *) = meta->rx_timestamp;
+			bufs[i]->ol_flags |= timestamp_dynflag;
+		}
+
 		rx_bytes += len;
 	}
 
@@ -457,6 +479,18 @@ af_xdp_rx_cp(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 		len = desc->len;
 		pkt = xsk_umem__get_data(rxq->umem->mz->addr, addr);
 
+		if (rxq->rx_timestamp_enabled &&
+		    timestamp_dynfield_offset >= 0) {
+			struct af_xdp_rx_metadata *meta;
+
+			meta = (struct af_xdp_rx_metadata *)((char *)pkt -
+				sizeof(struct af_xdp_rx_metadata));
+			*RTE_MBUF_DYNFIELD(mbufs[i],
+					   timestamp_dynfield_offset,
+					   uint64_t *) = meta->rx_timestamp;
+			mbufs[i]->ol_flags |= timestamp_dynflag;
+		}
+
 		rte_memcpy(rte_pktmbuf_mtod(mbufs[i], void *), pkt, len);
 		rte_ring_enqueue(umem->buf_ring, (void *)addr);
 		rte_pktmbuf_pkt_len(mbufs[i]) = len;
@@ -743,6 +777,23 @@ eth_dev_start(struct rte_eth_dev *dev)
 {
 	uint16_t i;
 
+	if (dev->data->dev_conf.rxmode.offloads &
+	    RTE_ETH_RX_OFFLOAD_TIMESTAMP) {
+		int rc;
+
+		rc = rte_mbuf_dyn_rx_timestamp_register(
+				&timestamp_dynfield_offset,
+				&timestamp_dynflag);
+		if (rc) {
+			AF_XDP_LOG_LINE(ERR,
+				"Failed to register mbuf timestamp field");
+			return rc;
+		}
+		AF_XDP_LOG_LINE(INFO,
+			"Registered mbuf timestamp field, offset: %d",
+			timestamp_dynfield_offset);
+	}
+
 	dev->data->dev_link.link_status = RTE_ETH_LINK_UP;
 	for (i = 0; i < dev->data->nb_rx_queues; i++) {
 		dev->data->rx_queue_state[i] = RTE_ETH_QUEUE_STATE_STARTED;
@@ -870,6 +921,8 @@ eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 	dev_info->max_rx_queues = internals->queue_cnt;
 	dev_info->max_tx_queues = internals->queue_cnt;
 
+	dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_TIMESTAMP;
+
 	dev_info->min_mtu = RTE_ETHER_MIN_MTU;
 #if defined(XDP_UMEM_UNALIGNED_CHUNK_FLAG)
 	dev_info->max_rx_pktlen = getpagesize() -
@@ -1873,7 +1926,8 @@ eth_rx_queue_setup(struct rte_eth_dev *dev,
 	process_private->rxq_xsk_fds[rx_queue_id] = rxq->fds[0].fd;
 
 	rxq->port = dev->data->port_id;
-
+	rxq->rx_timestamp_enabled = !!(dev->data->dev_conf.rxmode.offloads &
+					RTE_ETH_RX_OFFLOAD_TIMESTAMP);
 	dev->data->rx_queues[rx_queue_id] = rxq;
 	return 0;
 
-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply related

* Re: [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support
From: Stephen Hemminger @ 2026-06-23 22:06 UTC (permalink / raw)
  To: Mark Blasko
  Cc: dev, Ciara Loftus, Maryam Tahhan, Joshua Washington,
	Jasper Tran O'Leary
In-Reply-To: <20260623215325.814776-1-blasko@google.com>

On Tue, 23 Jun 2026 21:53:24 +0000
Mark Blasko <blasko@google.com> wrote:

> +		if (rxq->rx_timestamp_enabled &&
> +		    timestamp_dynfield_offset >= 0) {
> +			struct af_xdp_rx_metadata *meta;
> +
> +			meta = (struct af_xdp_rx_metadata *)
> +				((char *)rte_pktmbuf_mtod(bufs[i], void *) -
> +				 sizeof(struct af_xdp_rx_metadata));
> +			*RTE_MBUF_DYNFIELD(bufs[i],
> +					   timestamp_dynfield_offset,
> +					   uint64_t *) = meta->rx_timestamp;
> +			bufs[i]->ol_flags |= timestamp_dynflag;
> +		}
> +

Why does XDP time stamp need to be different than how other drivers
already do timestamps. See AF_PACKET and TAP device?

Should not be driver specific here.

^ permalink raw reply

* Re: [PATCH v2] dts: update dts check format script and resolve errors
From: Patrick Robb @ 2026-06-23 22:34 UTC (permalink / raw)
  To: Koushik Bhargav Nimoji
  Cc: luca.vizzarro, dev, abailey, ahassick, lylavoie,
	NBU-Contact-Thomas Monjalon
In-Reply-To: <20260618204525.1010218-1-knimoji@iol.unh.edu>

[-- Attachment #1: Type: text/plain, Size: 1255 bytes --]

Looks like your patch is failing some of the checks on patchwork, including
doc build: https://github.com/ovsrobot/dpdk/actions/runs/27789008643

Remember to run a doc build locally before sending any patches:

meson setup build
ninja -C build doc

Otherwise, please provide a little more info regarding your info. So, you
have updated some of the dependencies used in the dts check format script.
I think what I see from a quick look that is relevant is:

-mypy = "^1.13.0"
+mypy = "^2.1.0"
 toml = "^0.10.2"
-ruff = "^0.8.1"
-types-paramiko = "^3.5.0.20240928"
+ruff = "^0.15.16"
+types-paramiko = "^4.0.0.20260518"
 types-invoke = "^2.0.0.10"
-types-pyyaml = "^6.0.12.20240917"
+types-pyyaml = "^6.0.12.20260518"

What is being done broadly? All dependencies covered by poetry are being
updated? or just the subset included in format checks? Are dependencies
being brought to current latest or something different?

I remember Thomas mentioning that DTS was not checking the
dts-check-format.sh at DPDK Summit and that confused me. Perhaps he is
running DTS and dts-check-format.sh outside of poetry (which we have said
is okay to do) and he is on newer versions of the formatting dependencies
than what we currently have committed to the poetry.lock.

[-- Attachment #2: Type: text/html, Size: 1633 bytes --]

^ permalink raw reply

* Re: [PATCH v1 1/2] dts: update parsing for cryptodev latency
From: Patrick Robb @ 2026-06-23 22:44 UTC (permalink / raw)
  To: Andrew Bailey; +Cc: luca.vizzarro, dev, lylavoie, ahassick, knimoji
In-Reply-To: <20260513152715.133381-1-abailey@iol.unh.edu>

[-- Attachment #1: Type: text/plain, Size: 54 bytes --]

Reviewed-by: Patrick Robb <patrickrobb1997@gmail.com>

[-- Attachment #2: Type: text/html, Size: 128 bytes --]

^ permalink raw reply

* [PATCH v4 0/7] bpf: JIT related bug fixes
From: Stephen Hemminger @ 2026-06-23 23:23 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger
In-Reply-To: <20260608203322.1116296-1-stephen@networkplumber.org>

While implementing JIT for packet capture ran into several issues:
  1. x86 JIT had pre-existing bug which would crash
  2. ARM64 BPF JIT was missing instructions for packet access.
     Which had been discovered previously [1]
  3. Tests related to JIT were not being run or missing coverage.

Fixed all of these. Patches are ordered so that most urgent fix
is first, followed by the test that should have caught the problem.

The arm64 epilogue branch fix (patch 4) was originally posted by
Christophe Fontaine [1]; that series stalled, so it is carried here
with his authorship.

Changes since v3:
 - incorporate review feedback
 - rebase to current main
 - extend the x86 fix to all fixed-width immediates, not just JSET:
   TEST is always imm32, while ROR and the shift group are always
   imm8; patch 1 retitled to match
 - add a regression test for a large shift count (patch 3)

Changes since v2:
 - found more places where the x86 JIT emitted invalid opcodes for
   fixed-width immediates

Changes since v1:
 - add the x86 JSET encoding fix and its regression test, found once
   the convert test ran generated code through the JIT
 - carry Christophe's arm64 epilogue fix with his sign-off
 - convert test now runs the converted filters through the JIT, not
   just loading them
 - kept Marat's ack on the "check JIT was generated" patch; dropped it
   on the convert test since that changed substantially

[1] https://inbox.dpdk.org/dev/20260319114500.9757-2-cfontain@redhat.com/

Christophe Fontaine (1):
  bpf/arm64: fix offset type to allow a negative jump

Stephen Hemminger (6):
  bpf/x86: fix JIT encoding of fixed-width immediates
  test/bpf: add JSET test with small immediate
  test/bpf: add test for large shift
  test/bpf: check that JIT was generated
  bpf/arm64: add BPF_ABS/BPF_IND packet load support
  test/bpf: check that bpf_convert can be JIT'd

 app/test/test_bpf.c     | 327 +++++++++++++++++++++++++++++++---------
 lib/bpf/bpf_jit_arm64.c | 153 ++++++++++++++++++-
 lib/bpf/bpf_jit_x86.c   |   6 +-
 lib/bpf/meson.build     |   2 +
 4 files changed, 412 insertions(+), 76 deletions(-)

-- 
2.53.0


^ permalink raw reply

* [PATCH v4 1/7] bpf/x86: fix JIT encoding of fixed-width immediates
From: Stephen Hemminger @ 2026-06-23 23:23 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, stable, Marat Khalili, Konstantin Ananyev,
	Ferruh Yigit
In-Reply-To: <20260623232522.257208-1-stephen@networkplumber.org>

Several places in the x86 JIT size an immediate with imm_size(), which
returns 1 or 4 bytes depending on the value. That is wrong for opcodes
whose immediate width is fixed by the encoding, and it breaks in both
directions.

TEST (0xF7 /0, used for BPF_JSET) has no imm8 form; the immediate is
always 32 bits. For a small mask such as BPF_JSET | BPF_K #0x1,
imm_size() returns 1, so the JIT emits a 1-byte immediate. The CPU
still consumes 4, swallowing 3 bytes of the following Jcc. The
instruction stream desyncs and the program crashes.

ROR and the shifts (0xC1 group) have the opposite problem: their
immediate is always imm8. For a count >= 128, imm_size() returns 4 and
the JIT emits 3 stray bytes, again desyncing the stream.

Size each immediate by its encoding: 32 bits for TEST, 8 bits for ROR
and the shifts.

Bugzilla ID: 1959
Fixes: cc752e43e079 ("bpf: add JIT compilation for x86_64 ISA")
Cc: stable@dpdk.org

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
Acked-by: Marat Khalili <marat.khalili@huawei.com>
---
 lib/bpf/bpf_jit_x86.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/bpf/bpf_jit_x86.c b/lib/bpf/bpf_jit_x86.c
index 54eb279643..912d3f69bc 100644
--- a/lib/bpf/bpf_jit_x86.c
+++ b/lib/bpf/bpf_jit_x86.c
@@ -300,7 +300,7 @@ emit_ror_imm(struct bpf_jit_state *st, uint32_t dreg, uint32_t imm)
 	emit_rex(st, BPF_ALU, 0, dreg);
 	emit_bytes(st, &ops, sizeof(ops));
 	emit_modregrm(st, MOD_DIRECT, mods, dreg);
-	emit_imm(st, imm, imm_size(imm));
+	emit_imm(st, imm, sizeof(uint8_t));
 }
 
 /*
@@ -441,7 +441,7 @@ emit_shift_imm(struct bpf_jit_state *st, uint32_t op, uint32_t dreg,
 	uint32_t imm)
 {
 	emit_shift(st, op, dreg);
-	emit_imm(st, imm, imm_size(imm));
+	emit_imm(st, imm, sizeof(uint8_t));
 }
 
 /*
@@ -921,7 +921,7 @@ emit_tst_imm(struct bpf_jit_state *st, uint32_t op, uint32_t dreg, uint32_t imm)
 	emit_rex(st, op, 0, dreg);
 	emit_bytes(st, &ops, sizeof(ops));
 	emit_modregrm(st, MOD_DIRECT, mods, dreg);
-	emit_imm(st, imm, imm_size(imm));
+	emit_imm(st, imm, sizeof(int32_t));
 }
 
 static void
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 2/7] test/bpf: add JSET test with small immediate
From: Stephen Hemminger @ 2026-06-23 23:23 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Marat Khalili, Konstantin Ananyev
In-Reply-To: <20260623232522.257208-1-stephen@networkplumber.org>

The existing jump test only used a 32-bit JSET mask,
so the broken imm8 encoding of TEST in the x86 JIT was never exercised.
Add a case with a byte-sized mask;
run_test() runs it through the interpreter and the JIT.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
Acked-by: Marat Khalili <marat.khalili@huawei.com>
---
 app/test/test_bpf.c | 82 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
index 6b07e72295..232e9e2a98 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -3158,7 +3158,89 @@ static const struct ebpf_insn test_ld_mbuf3_prog[] = {
 };
 
 /* all bpf test cases */
+/*
+ * JSET with a byte-sized mask: exercises the imm8 path of the TEST
+ * encoding in the x86 JIT (a 32-bit mask takes a different path).
+ */
+static const struct ebpf_insn test_jset1_prog[] = {
+	{
+		.code = (BPF_ALU | EBPF_MOV | BPF_K),
+		.dst_reg = EBPF_REG_0,
+		.imm = 0,
+	},
+	{
+		.code = (BPF_LDX | BPF_MEM | BPF_B),
+		.dst_reg = EBPF_REG_2,
+		.src_reg = EBPF_REG_1,
+		.off = offsetof(struct dummy_offset, u8),
+	},
+	/* bit 0 is set in the input: branch is taken */
+	{
+		.code = (BPF_JMP | BPF_JSET | BPF_K),
+		.dst_reg = EBPF_REG_2,
+		.imm = 0x1,
+		.off = 1,
+	},
+	{
+		.code = (BPF_JMP | BPF_JA),
+		.off = 1,
+	},
+	{
+		.code = (EBPF_ALU64 | BPF_OR | BPF_K),
+		.dst_reg = EBPF_REG_0,
+		.imm = 0x1,
+	},
+	/* bit 1 is clear in the input: branch is not taken */
+	{
+		.code = (BPF_JMP | BPF_JSET | BPF_K),
+		.dst_reg = EBPF_REG_2,
+		.imm = 0x2,
+		.off = 1,
+	},
+	{
+		.code = (BPF_JMP | BPF_JA),
+		.off = 1,
+	},
+	{
+		.code = (EBPF_ALU64 | BPF_OR | BPF_K),
+		.dst_reg = EBPF_REG_0,
+		.imm = 0x2,
+	},
+	{
+		.code = (BPF_JMP | EBPF_EXIT),
+	},
+};
+
+static void
+test_jset1_prepare(void *arg)
+{
+	struct dummy_offset *df = arg;
+
+	memset(df, 0, sizeof(*df));
+	df->u8 = 0x1;	/* bit 0 set, bit 1 clear */
+}
+
+static int
+test_jset1_check(uint64_t rc, const void *arg)
+{
+	return cmp_res(__func__, 0x1, rc, arg, arg, 0);
+}
+
 static const struct bpf_test tests[] = {
+	{
+		.name = "test_jset1",
+		.arg_sz = sizeof(struct dummy_offset),
+		.prm = {
+			.ins = test_jset1_prog,
+			.nb_ins = RTE_DIM(test_jset1_prog),
+			.prog_arg = {
+				.type = RTE_BPF_ARG_PTR,
+				.size = sizeof(struct dummy_offset),
+			},
+		},
+		.prepare = test_jset1_prepare,
+		.check_result = test_jset1_check,
+	},
 	{
 		.name = "test_store1",
 		.arg_sz = sizeof(struct dummy_offset),
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 3/7] test/bpf: add test for large shift
From: Stephen Hemminger @ 2026-06-23 23:23 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Konstantin Ananyev, Marat Khalili
In-Reply-To: <20260623232522.257208-1-stephen@networkplumber.org>

The JIT compiler had issues with immediate values on shift instructions
so add a new test to cover that case.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 app/test/test_bpf.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
index 232e9e2a98..b54e36910b 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -2005,6 +2005,58 @@ test_div1_check(uint64_t rc, const void *arg)
 	return cmp_res(__func__, 0, rc, dve.out, dvt->out, sizeof(dve.out));
 }
 
+/*
+ * Shift by an immediate that doesn't fit in a signed byte: the C1 shift
+ * group takes a fixed 1-byte immediate, but imm_size() returns 4 for
+ * counts >= 128, so the x86 JIT emits 3 stray bytes and desyncs the
+ * instruction stream. The shift results are discarded (a count >= 64 is
+ * UB in the interpreter); the test returns a known constant, which the
+ * corrupted stream fails to produce.
+ */
+static const struct ebpf_insn test_shift_big_imm_prog[] = {
+	{
+		.code = (BPF_ALU | EBPF_MOV | BPF_K),
+		.dst_reg = EBPF_REG_2,
+		.imm = 0x1,
+	},
+	{
+		.code = (EBPF_ALU64 | BPF_LSH | BPF_K),
+		.dst_reg = EBPF_REG_2,
+		.imm = 137,
+	},
+	{
+		.code = (EBPF_ALU64 | BPF_RSH | BPF_K),
+		.dst_reg = EBPF_REG_2,
+		.imm = 200,
+	},
+	{
+		.code = (EBPF_ALU64 | EBPF_ARSH | BPF_K),
+		.dst_reg = EBPF_REG_2,
+		.imm = 255,
+	},
+	/* known result; a desynced stream won't reproduce it */
+	{
+		.code = (BPF_ALU | EBPF_MOV | BPF_K),
+		.dst_reg = EBPF_REG_0,
+		.imm = 0x55,
+	},
+	{
+		.code = (BPF_JMP | EBPF_EXIT),
+	},
+};
+
+static void
+test_shift_big_imm_prepare(void *arg)
+{
+	memset(arg, 0, sizeof(struct dummy_offset));
+}
+
+static int
+test_shift_big_imm_check(uint64_t rc, const void *arg)
+{
+	return cmp_res(__func__, 0x55, rc, arg, arg, 0);
+}
+
 /* call test-cases */
 static const struct ebpf_insn test_call1_prog[] = {
 
@@ -3409,6 +3461,20 @@ static const struct bpf_test tests[] = {
 		.prepare = test_mul1_prepare,
 		.check_result = test_div1_check,
 	},
+	{
+		.name = "test_shift_big_imm",
+		.arg_sz = sizeof(struct dummy_offset),
+		.prm = {
+			.ins = test_shift_big_imm_prog,
+			.nb_ins = RTE_DIM(test_shift_big_imm_prog),
+			.prog_arg = {
+				.type = RTE_BPF_ARG_PTR,
+				.size = sizeof(struct dummy_offset),
+			},
+		},
+		.prepare = test_shift_big_imm_prepare,
+		.check_result = test_shift_big_imm_check,
+	},
 	{
 		.name = "test_call1",
 		.arg_sz = sizeof(struct dummy_offset),
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 4/7] bpf/arm64: fix offset type to allow a negative jump
From: Stephen Hemminger @ 2026-06-23 23:23 UTC (permalink / raw)
  To: dev
  Cc: Christophe Fontaine, stable, Stephen Hemminger,
	Wathsala Vithanage, Konstantin Ananyev, Marat Khalili,
	Jerin Jacob
In-Reply-To: <20260623232522.257208-1-stephen@networkplumber.org>

From: Christophe Fontaine <cfontain@redhat.com>

The DPDK BPF JIT standalone test test_ld_mbuf1 fails on arm64.
It does:
	r6 = r1                    // mbuf
	r0 = *(u8 *)pkt[0]         // BPF_ABS
	if ((r0 & 0xf0) == 0x40)
		goto parse
	r0 = 0
	exit                       // epilogue E0
parse:
	r0 = *(u8 *)pkt[r0 + 3]    // BPF_IND
	...
	exit

emit_return_zero_if_src_zero() returns 0 by branching to a function
epilogue. The target maybe a previous epilogue so branch
might be backwards; therefore the offset needs to be negative.

The offset was stored in a uint16_t, so a negative value wrapped to a
large positive number; emit_b() then branched past the end of the
program and faulted at run time.

Fixes: 111e2a747a4f ("bpf/arm: add basic arithmetic operations")
Cc: stable@dpdk.org

Signed-off-by: Christophe Fontaine <cfontain@redhat.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 lib/bpf/bpf_jit_arm64.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/bpf/bpf_jit_arm64.c b/lib/bpf/bpf_jit_arm64.c
index ba7ae4d680..776d7c8e97 100644
--- a/lib/bpf/bpf_jit_arm64.c
+++ b/lib/bpf/bpf_jit_arm64.c
@@ -957,10 +957,12 @@ static void
 emit_return_zero_if_src_zero(struct a64_jit_ctx *ctx, bool is64, uint8_t src)
 {
 	uint8_t r0 = ebpf_to_a64_reg(ctx, EBPF_REG_0);
-	uint16_t jump_to_epilogue;
+	int32_t jump_to_epilogue;
 
 	emit_cbnz(ctx, is64, src, 3);
 	emit_mov_imm(ctx, is64, r0, 0);
+
+	/* maybe backwards branch to earlier epilogue */
 	jump_to_epilogue = (ctx->program_start + ctx->program_sz) - ctx->idx;
 	emit_b(ctx, jump_to_epilogue);
 }
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 5/7] test/bpf: check that JIT was generated
From: Stephen Hemminger @ 2026-06-23 23:23 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Marat Khalili, Konstantin Ananyev
In-Reply-To: <20260623232522.257208-1-stephen@networkplumber.org>

Avoid silently ignoring JIT failures. The test cases should
all succeed JIT compilation; if not it is a bug in the JIT
implementation and should be reported.

Introduce a configuration setting RTE_BPF_JIT_SUPPORTED
which is cleaner that adding ARCH specific #ifdef.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
Acked-by: Marat Khalili <marat.khalili@huawei.com>
---
 app/test/test_bpf.c | 8 ++++++++
 lib/bpf/meson.build | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
index b54e36910b..9adffcce64 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -3656,6 +3656,14 @@ run_test(const struct bpf_test *tst)
 				rv, strerror(rv));
 		}
 	}
+#ifdef RTE_BPF_JIT_SUPPORTED
+	else {
+		/* a JIT backend exists for this arch, so it must compile */
+		printf("%s@%d: %s: no JIT code generated;\n",
+			__func__, __LINE__, tst->name);
+		ret = -1;
+	}
+#endif
 
 	rte_bpf_destroy(bpf);
 	return ret;
diff --git a/lib/bpf/meson.build b/lib/bpf/meson.build
index 7e8a300e3f..04ede96689 100644
--- a/lib/bpf/meson.build
+++ b/lib/bpf/meson.build
@@ -27,8 +27,10 @@ sources = files(
 )
 
 if arch_subdir == 'x86' and dpdk_conf.get('RTE_ARCH_64')
+    dpdk_conf.set('RTE_BPF_JIT_SUPPORTED', 1)
     sources += files('bpf_jit_x86.c')
 elif dpdk_conf.has('RTE_ARCH_ARM64')
+    dpdk_conf.set('RTE_BPF_JIT_SUPPORTED', 1)
     sources += files('bpf_jit_arm64.c')
 endif
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 6/7] bpf/arm64: add BPF_ABS/BPF_IND packet load support
From: Stephen Hemminger @ 2026-06-23 23:23 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, Wathsala Vithanage, Konstantin Ananyev,
	Marat Khalili
In-Reply-To: <20260623232522.257208-1-stephen@networkplumber.org>

The arm64 JIT rejected BPF_LD | BPF_ABS and BPF_LD | BPF_IND with
"invalid opcode", so cBPF programs converted by rte_bpf_convert() could
not be JITed. Add these opcodes, mirroring the x86 JIT: a fast path for
data held in the first mbuf segment, and a __rte_pktmbuf_read() slow
path for everything else.

The forward branches over the call cannot use fixed distances:
emit_call() materializes the helper address with a variable number of
mov/movk instructions, so the block sizes are not known up front. Size
the three blocks (fast path, slow path, common tail) in a dry run, then
emit for real with the branches resolved from the measured offsets.

Programs using these opcodes use the call register layout, since the
slow path makes a function call.

Bugzilla ID: 1427

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 lib/bpf/bpf_jit_arm64.c | 149 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 148 insertions(+), 1 deletion(-)

diff --git a/lib/bpf/bpf_jit_arm64.c b/lib/bpf/bpf_jit_arm64.c
index 776d7c8e97..7b2a1595e8 100644
--- a/lib/bpf/bpf_jit_arm64.c
+++ b/lib/bpf/bpf_jit_arm64.c
@@ -1125,6 +1125,135 @@ emit_branch(struct a64_jit_ctx *ctx, uint8_t op, uint32_t i, int16_t off)
 	emit_b_cond(ctx, ebpf_to_a64_cond(op), jump_offset_get(ctx, i, off));
 }
 
+/* LD_ABS/LD_IND code block offsets (in arm64 instructions) */
+enum {
+	LDMB_FAST_OFS, /* fast path */
+	LDMB_SLOW_OFS, /* slow path */
+	LDMB_FIN_OFS,  /* common tail */
+	LDMB_OFS_NUM
+};
+
+/*
+ * Helper for emit_ld_mbuf(): fast path.
+ * Compute the packet offset; if it lies inside the first segment leave the
+ * data pointer in R0, otherwise branch to the slow path.
+ */
+static void
+emit_ldmb_fast_path(struct a64_jit_ctx *ctx, uint8_t src, uint8_t mode,
+		    uint32_t sz, int32_t imm, const uint32_t ofs[LDMB_OFS_NUM])
+{
+	uint8_t r0 = ebpf_to_a64_reg(ctx, EBPF_REG_0);
+	uint8_t r6 = ebpf_to_a64_reg(ctx, EBPF_REG_6);
+	uint8_t tmp1 = ebpf_to_a64_reg(ctx, TMP_REG_1);
+	uint8_t tmp2 = ebpf_to_a64_reg(ctx, TMP_REG_2);
+	uint8_t tmp3 = ebpf_to_a64_reg(ctx, TMP_REG_3);
+
+	/* off = imm (+ src for BPF_IND) */
+	emit_mov_imm(ctx, 1, tmp1, imm);
+	if (mode == BPF_IND)
+		emit_add(ctx, 1, tmp1, src);
+
+	/* if ((int64_t)(mbuf->data_len - off) < sz) goto slow_path */
+	emit_mov_imm(ctx, 1, tmp2, offsetof(struct rte_mbuf, data_len));
+	emit_ldr(ctx, BPF_H, tmp2, r6, tmp2);
+	emit_sub(ctx, 1, tmp2, tmp1);
+	emit_mov_imm(ctx, 1, tmp3, sz);
+	emit_cmp(ctx, 1, tmp2, tmp3);
+	emit_b_cond(ctx, A64_LT, (int32_t)(ofs[LDMB_SLOW_OFS] - ctx->idx));
+
+	/* R0 = mbuf->buf_addr + mbuf->data_off + off */
+	emit_mov_imm(ctx, 1, tmp2, offsetof(struct rte_mbuf, data_off));
+	emit_ldr(ctx, BPF_H, tmp2, r6, tmp2);
+	emit_mov_imm(ctx, 1, r0, offsetof(struct rte_mbuf, buf_addr));
+	emit_ldr(ctx, EBPF_DW, r0, r6, r0);
+	emit_add(ctx, 1, r0, tmp2);
+	emit_add(ctx, 1, r0, tmp1);
+
+	emit_b(ctx, (int32_t)(ofs[LDMB_FIN_OFS] - ctx->idx));
+}
+
+/*
+ * Helper for emit_ld_mbuf(): slow path.
+ * R0 = __rte_pktmbuf_read(mbuf, off, sz, buf); return 0 if NULL.
+ * The scratch buffer is the space reserved by __rte_bpf_validate() at the
+ * bottom of the eBPF stack frame, i.e. (frame_pointer - stack_ofs).
+ */
+static void
+emit_ldmb_slow_path(struct a64_jit_ctx *ctx, uint32_t sz, uint32_t stack_ofs)
+{
+	uint8_t r0 = ebpf_to_a64_reg(ctx, EBPF_REG_0);
+	uint8_t r6 = ebpf_to_a64_reg(ctx, EBPF_REG_6);
+	uint8_t fp = ebpf_to_a64_reg(ctx, EBPF_FP);
+	uint8_t tmp1 = ebpf_to_a64_reg(ctx, TMP_REG_1);
+
+	/* arguments of __rte_pktmbuf_read(mbuf, off, len, buf) */
+	emit_mov_64(ctx, A64_R(1), tmp1);		/* off (held in tmp1) */
+	emit_mov_64(ctx, A64_R(0), r6);			/* mbuf */
+	emit_mov_imm(ctx, 0, A64_R(2), sz);		/* len */
+	emit_sub_imm_64(ctx, A64_R(3), fp, stack_ofs);	/* buf */
+
+	emit_call(ctx, tmp1, (void *)(uintptr_t)__rte_pktmbuf_read);
+	emit_return_zero_if_src_zero(ctx, 1, r0);
+}
+
+/*
+ * Helper for emit_ld_mbuf(): common tail.
+ * Load the value pointed to by R0 and convert from network byte order.
+ */
+static void
+emit_ldmb_fin(struct a64_jit_ctx *ctx, uint8_t opsz, uint32_t sz)
+{
+	uint8_t r0 = ebpf_to_a64_reg(ctx, EBPF_REG_0);
+
+	emit_ldr(ctx, opsz, r0, r0, A64_ZR);
+	if (opsz != BPF_B)
+		emit_be(ctx, r0, sz * 8);
+}
+
+/*
+ * emit code for BPF_ABS/BPF_IND load.
+ * generates the following construction:
+ * fast_path:
+ *   off = src + imm
+ *   if (mbuf->data_len - off < sz)
+ *      goto slow_path;
+ *   ptr = mbuf->buf_addr + mbuf->data_off + off;
+ *   goto fin_part;
+ * slow_path:
+ *   typeof(sz) buf;  // scratch space reserved on the eBPF stack
+ *   ptr = __rte_pktmbuf_read(mbuf, off, sz, &buf);
+ *   if (ptr == NULL)
+ *      return 0;
+ * fin_part:
+ *   res = *(typeof(sz))ptr;
+ *   res = ntoh(res);
+ */
+static void
+emit_ld_mbuf(struct a64_jit_ctx *ctx, uint8_t op, uint8_t src, int32_t imm,
+	     uint32_t stack_ofs)
+{
+	uint8_t mode = BPF_MODE(op);
+	uint8_t opsz = BPF_SIZE(op);
+	uint32_t sz = bpf_size(opsz);
+	uint32_t ofs[LDMB_OFS_NUM];
+
+	/* seed offsets so the dry-run branches stay in range */
+	ofs[LDMB_FAST_OFS] = ofs[LDMB_SLOW_OFS] = ofs[LDMB_FIN_OFS] = ctx->idx;
+
+	/* dry run to record block offsets */
+	emit_ldmb_fast_path(ctx, src, mode, sz, imm, ofs);
+	ofs[LDMB_SLOW_OFS] = ctx->idx;
+	emit_ldmb_slow_path(ctx, sz, stack_ofs);
+	ofs[LDMB_FIN_OFS] = ctx->idx;
+	emit_ldmb_fin(ctx, opsz, sz);
+
+	/* rewind and emit for real with resolved offsets */
+	ctx->idx = ofs[LDMB_FAST_OFS];
+	emit_ldmb_fast_path(ctx, src, mode, sz, imm, ofs);
+	emit_ldmb_slow_path(ctx, sz, stack_ofs);
+	emit_ldmb_fin(ctx, opsz, sz);
+}
+
 static void
 check_program_has_call(struct a64_jit_ctx *ctx, struct rte_bpf *bpf)
 {
@@ -1137,8 +1266,17 @@ check_program_has_call(struct a64_jit_ctx *ctx, struct rte_bpf *bpf)
 		op = ins->code;
 
 		switch (op) {
-		/* Call imm */
+		/*
+		 * BPF_ABS/BPF_IND can fall through to __rte_pktmbuf_read(),
+		 * so they need the call-clobbered register layout as well.
+		 */
 		case (BPF_JMP | EBPF_CALL):
+		case (BPF_LD | BPF_ABS | BPF_B):
+		case (BPF_LD | BPF_ABS | BPF_H):
+		case (BPF_LD | BPF_ABS | BPF_W):
+		case (BPF_LD | BPF_IND | BPF_B):
+		case (BPF_LD | BPF_IND | BPF_H):
+		case (BPF_LD | BPF_IND | BPF_W):
 			ctx->foundcall = 1;
 			return;
 		}
@@ -1340,6 +1478,15 @@ emit(struct a64_jit_ctx *ctx, struct rte_bpf *bpf)
 			emit_mov_imm(ctx, 1, dst, u64);
 			i++;
 			break;
+		/* R0 = ntoh(*(size *)(mbuf data + (src) + imm)) */
+		case (BPF_LD | BPF_ABS | BPF_B):
+		case (BPF_LD | BPF_ABS | BPF_H):
+		case (BPF_LD | BPF_ABS | BPF_W):
+		case (BPF_LD | BPF_IND | BPF_B):
+		case (BPF_LD | BPF_IND | BPF_H):
+		case (BPF_LD | BPF_IND | BPF_W):
+			emit_ld_mbuf(ctx, op, src, imm, bpf->stack_sz);
+			break;
 		/* *(size *)(dst + off) = src */
 		case (BPF_STX | BPF_MEM | BPF_B):
 		case (BPF_STX | BPF_MEM | BPF_H):
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 7/7] test/bpf: check that bpf_convert can be JIT'd
From: Stephen Hemminger @ 2026-06-23 23:23 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Konstantin Ananyev, Marat Khalili
In-Reply-To: <20260623232522.257208-1-stephen@networkplumber.org>

Run each converted filter through both the interpreter and the JIT and
check they agree, catching JIT miscompiles.

test_bpf_filter and test_bpf_match did nearly the same thing: compile,
load and run a filter against the dummy packet. Combine them into
test_bpf_match, which now builds the packet itself and returns whether
the filter matched. Callers run it for both load methods.

The dummy packet is a UDP packet to a fixed destination MAC, source
and destination ports, so the filter results are deterministic. None
of the sample filters should match it, so assert that; a convert or
JIT bug that flips a result is then caught. The destination MAC and
source port are chosen so the negative ethernet and port filters do
not match, and "port not 53 and not arp" is dropped as it matches
any non-ARP packet that lacks port 53.

Reduce log output to make it easier to match which expression might be
causing issues.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 app/test/test_bpf.c | 171 ++++++++++++++++++++++++++------------------
 1 file changed, 100 insertions(+), 71 deletions(-)

diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
index 9adffcce64..8934c98c3c 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -32,6 +32,7 @@ test_bpf(void)
 #include <rte_bpf.h>
 #include <rte_ether.h>
 #include <rte_ip.h>
+#include <rte_udp.h>
 
 
 /* Tests of most simple BPF programs (no instructions, one instruction etc.) */
@@ -4763,11 +4764,13 @@ load_cbpf_program_convert(struct bpf_program *cbpf_program, const char *str)
 		return NULL;
 	}
 
+#ifdef DEBUG
 	printf("bpf convert(\"%s\") produced:\n", str);
 	rte_bpf_dump(stdout, prm->ins, prm->nb_ins);
 
 	printf("%s \"%s\"\n", __func__, str);
 	test_bpf_dump(cbpf_program, prm);
+#endif
 
 	bpf = rte_bpf_load(prm);
 	rte_free(prm);
@@ -4792,18 +4795,65 @@ load_cbpf_program_direct(struct bpf_program *cbpf_program, const char *str __rte
 	});
 }
 
+static const load_cbpf_program_t cbpf_program_loaders[] = {
+	load_cbpf_program_convert,
+	load_cbpf_program_direct,
+};
+
+/* Setup Ethernet/IP/UDP headers in a dummy packet buffer for filter tests */
+static void
+dummy_ip_prep(void *data, uint16_t plen)
+{
+	struct {
+		struct rte_ether_hdr eth_hdr;
+		struct rte_ipv4_hdr ip_hdr;
+		struct rte_udp_hdr udp_hdr;
+	} *hdr = data;
+
+	hdr->eth_hdr = (struct rte_ether_hdr) {
+		.dst_addr.addr_bytes = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e },
+		.ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4),
+	};
+	hdr->ip_hdr = (struct rte_ipv4_hdr) {
+		.version_ihl = RTE_IPV4_VHL_DEF,
+		.total_length = rte_cpu_to_be_16(plen),
+		.time_to_live = IPDEFTTL,
+		.next_proto_id = IPPROTO_UDP,
+		.src_addr = rte_cpu_to_be_32(RTE_IPV4_LOOPBACK),
+		.dst_addr = rte_cpu_to_be_32(RTE_IPV4_BROADCAST),
+	};
+	hdr->udp_hdr = (struct rte_udp_hdr) {
+		.src_port = rte_cpu_to_be_16(49152),	/* fixed, avoids filter ports */
+		.dst_port = rte_cpu_to_be_16(9),	/* discard port */
+		.dgram_len = rte_cpu_to_be_16(plen - sizeof(struct rte_ipv4_hdr)),
+		.dgram_cksum = 0,
+	};
+}
+
+/*
+ * Compile a pcap filter, load it with the given loader, then run it against
+ * a standard dummy packet with both the interpreter and (when available) the
+ * JIT, checking the two agree.
+ *
+ * Returns 1 if the filter matched, 0 if it did not, and -1 on any error
+ * (compile, load, or interpreter/JIT mismatch).
+ */
 static int
-test_bpf_match(pcap_t *pcap, const char *str, struct rte_mbuf *mb,
+test_bpf_match(pcap_t *pcap, const char *str,
 	load_cbpf_program_t load_cbpf_program)
 {
+	uint8_t tbuf[RTE_MBUF_DEFAULT_BUF_SIZE];
+	const uint32_t plen = 100;
 	struct bpf_program fcode;
-	struct rte_bpf *bpf;
+	struct rte_mbuf mb = { 0 };
+	struct rte_bpf *bpf = NULL;
 	int ret = -1;
 	uint64_t rc;
 
+	printf("%s '%s'\n", __func__, str);
 	if (pcap_compile(pcap, &fcode, str, 1, PCAP_NETMASK_UNKNOWN)) {
 		printf("%s@%d: pcap_compile(\"%s\") failed: %s;\n",
-		       __func__, __LINE__,  str, pcap_geterr(pcap));
+		       __func__, __LINE__, str, pcap_geterr(pcap));
 		return -1;
 	}
 
@@ -4811,15 +4861,41 @@ test_bpf_match(pcap_t *pcap, const char *str, struct rte_mbuf *mb,
 	if (bpf == NULL) {
 		printf("%s@%d: failed to load cbpf program for \"%s\", error=%d(%s);\n",
 			__func__, __LINE__, str, rte_errno, strerror(rte_errno));
+		test_bpf_dump(&fcode, NULL);
 		goto error;
 	}
 
-	rc = rte_bpf_exec(bpf, mb);
-	/* The return code from bpf capture filter is non-zero if matched */
-	ret = (rc == 0);
+	dummy_mbuf_prep(&mb, tbuf, sizeof(tbuf), plen);
+	dummy_ip_prep(rte_pktmbuf_mtod(&mb, void *), plen);
+
+	rc = rte_bpf_exec(bpf, &mb);
+
+	/* Verify the JIT, when available, produces the same result. */
+	{
+		struct rte_bpf_jit jit;
+
+		rte_bpf_get_jit(bpf, &jit);
+		if (jit.func != NULL) {
+			fflush(stdout);
+			if (jit.func(&mb) != rc) {
+				printf("%s@%d: JIT return code does not match\n",
+				       __func__, __LINE__);
+				goto error;
+			}
+		}
+#ifdef RTE_BPF_JIT_SUPPORTED
+		else {
+			printf("%s@%d: no JIT code generated\n",
+			       __func__, __LINE__);
+			goto error;
+		}
+#endif
+	}
+
+	/* The return code from a bpf capture filter is non-zero if matched. */
+	ret = (rc != 0);
 error:
-	if (bpf)
-		rte_bpf_destroy(bpf);
+	rte_bpf_destroy(bpf);
 	pcap_freecode(&fcode);
 	return ret;
 }
@@ -4828,44 +4904,13 @@ test_bpf_match(pcap_t *pcap, const char *str, struct rte_mbuf *mb,
 static int
 test_bpf_filter_sanity(pcap_t *pcap)
 {
-	static const load_cbpf_program_t cbpf_program_loaders[] = {
-		load_cbpf_program_convert,
-		load_cbpf_program_direct,
-	};
-
-	const uint32_t plen = 100;
-	struct rte_mbuf mb, *m;
-	uint8_t tbuf[RTE_MBUF_DEFAULT_BUF_SIZE];
-	struct {
-		struct rte_ether_hdr eth_hdr;
-		struct rte_ipv4_hdr ip_hdr;
-	} *hdr;
-
-	memset(&mb, 0, sizeof(mb));
-	dummy_mbuf_prep(&mb, tbuf, sizeof(tbuf), plen);
-	m = &mb;
-
-	hdr = rte_pktmbuf_mtod(m, typeof(hdr));
-	hdr->eth_hdr = (struct rte_ether_hdr) {
-		.dst_addr.addr_bytes = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
-		.ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4),
-	};
-	hdr->ip_hdr = (struct rte_ipv4_hdr) {
-		.version_ihl = RTE_IPV4_VHL_DEF,
-		.total_length = rte_cpu_to_be_16(plen),
-		.time_to_live = IPDEFTTL,
-		.next_proto_id = IPPROTO_RAW,
-		.src_addr = rte_cpu_to_be_32(RTE_IPV4_LOOPBACK),
-		.dst_addr = rte_cpu_to_be_32(RTE_IPV4_BROADCAST),
-	};
-
-	for (int li = 0; li != RTE_DIM(cbpf_program_loaders); ++li) {
-		if (test_bpf_match(pcap, "ip", m, cbpf_program_loaders[li]) != 0) {
+	for (unsigned int li = 0; li != RTE_DIM(cbpf_program_loaders); ++li) {
+		if (test_bpf_match(pcap, "ip", cbpf_program_loaders[li]) != 1) {
 			printf("%s@%d: filter \"ip\" doesn't match test data\n",
 			       __func__, __LINE__);
 			return -1;
 		}
-		if (test_bpf_match(pcap, "not ip", m, cbpf_program_loaders[li]) == 0) {
+		if (test_bpf_match(pcap, "not ip", cbpf_program_loaders[li]) != 0) {
 			printf("%s@%d: filter \"not ip\" does match test data\n",
 			       __func__, __LINE__);
 			return -1;
@@ -4889,7 +4934,6 @@ static const char * const sample_filters[] = {
 	"port 53",
 	"host 192.0.2.1 and not (port 80 or port 25)",
 	"host 2001:4b98:db0::8 and not port 80 and not port 25",
-	"port not 53 and not arp",
 	"(tcp[0:2] > 1500 and tcp[0:2] < 1550) or (tcp[2:2] > 1500 and tcp[2:2] < 1550)",
 	"ether proto 0x888e",
 	"ether[0] & 1 = 0 and ip[16] >= 224",
@@ -4916,35 +4960,10 @@ static const char * const sample_filters[] = {
 	"or host 192.0.2.1 or host 192.0.2.100 or host 192.0.2.200"),
 };
 
-static int
-test_bpf_filter(pcap_t *pcap, const char *s, load_cbpf_program_t load_cbpf_program)
-{
-	struct bpf_program fcode;
-	struct rte_bpf *bpf;
-
-	if (pcap_compile(pcap, &fcode, s, 1, PCAP_NETMASK_UNKNOWN)) {
-		printf("%s@%d: pcap_compile(\"%s\") failed: %s;\n",
-		       __func__, __LINE__, s, pcap_geterr(pcap));
-		return -1;
-	}
-
-	bpf = load_cbpf_program(&fcode, s);
-	if (bpf == NULL) {
-		printf("%s@%d: failed to load cbpf program for \"%s\", error=%d(%s);\n",
-			__func__, __LINE__, s, rte_errno, strerror(rte_errno));
-		test_bpf_dump(&fcode, NULL);
-	}
-
-	rte_bpf_destroy(bpf);
-
-	pcap_freecode(&fcode);
-	return (bpf == NULL) ? -1 : 0;
-}
-
 static int
 test_bpf_convert(void)
 {
-	unsigned int i;
+	unsigned int i, li;
 	pcap_t *pcap;
 	int rc;
 
@@ -4956,8 +4975,18 @@ test_bpf_convert(void)
 
 	rc = test_bpf_filter_sanity(pcap);
 	for (i = 0; i < RTE_DIM(sample_filters); i++) {
-		rc |= test_bpf_filter(pcap, sample_filters[i], load_cbpf_program_convert);
-		rc |= test_bpf_filter(pcap, sample_filters[i], load_cbpf_program_direct);
+		for (li = 0; li < RTE_DIM(cbpf_program_loaders); li++) {
+			int m = test_bpf_match(pcap, sample_filters[i],
+					       cbpf_program_loaders[li]);
+
+			/* None of the sample filters match the dummy packet. */
+			if (m != 0) {
+				if (m > 0)
+					printf("%s@%d: filter \"%s\" unexpectedly matched\n",
+					       __func__, __LINE__, sample_filters[i]);
+				rc = -1;
+			}
+		}
 	}
 
 	pcap_close(pcap);
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2] eal: return error on devargs truncation in hotplug MP messages
From: Long Li @ 2026-06-24  0:05 UTC (permalink / raw)
  To: dev; +Cc: stable, david.marchand, Long Li
In-Reply-To: <20260325014506.1866374-1-longli@microsoft.com>

The EAL hotplug multi-process messaging uses a fixed-size buffer
(EAL_DEV_MP_DEV_ARGS_MAX_LEN, 128 bytes) for device arguments.
When devargs exceeds this limit, strlcpy silently truncates the
string. This causes secondary processes to receive incomplete
devargs during hotplug re-add, leading to failed port
re-initialization.

For example, a MANA PCI device with 6 mac= arguments:

  mac=AA:BB:CC:DD:EE:01,mac=AA:BB:CC:DD:EE:02,
  mac=AA:BB:CC:DD:EE:03,mac=AA:BB:CC:DD:EE:04,
  mac=AA:BB:CC:DD:EE:05,mac=AA:BB:CC:DD:EE:06

produces a 131-byte devargs string that gets silently truncated
to 127 bytes, losing the last MAC address.

Return -E2BIG from rte_dev_probe() when devargs would be truncated,
instead of silently corrupting data. rte_dev_remove() does not need
the same check because the length was already validated at probe time.

Fixes: 244d5130719c ("eal: enable hotplug on multi-process")
Cc: stable@dpdk.org

Signed-off-by: Long Li <longli@microsoft.com>
---
v2:
 - Added Fixes: tag and Cc: stable@dpdk.org.
 - Moved the length check before memset() in rte_dev_probe().
 - Removed the redundant length check from rte_dev_remove();
   devargs length is already validated at probe time.
 - Dropped the [2/2] meson-options patch from this series; it will
   be sent separately.

 lib/eal/common/eal_common_dev.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/lib/eal/common/eal_common_dev.c b/lib/eal/common/eal_common_dev.c
index 48b631532a..f3fc4d585e 100644
--- a/lib/eal/common/eal_common_dev.c
+++ b/lib/eal/common/eal_common_dev.c
@@ -271,6 +271,12 @@ rte_dev_probe(const char *devargs)
 	struct rte_device *dev;
 	int ret;
 
+	if (strlen(devargs) >= EAL_DEV_MP_DEV_ARGS_MAX_LEN) {
+		EAL_LOG(ERR, "devargs truncated (len %zu, max %d)",
+			strlen(devargs), EAL_DEV_MP_DEV_ARGS_MAX_LEN);
+		return -E2BIG;
+	}
+
 	memset(&req, 0, sizeof(req));
 	req.t = EAL_DEV_REQ_TYPE_ATTACH;
 	strlcpy(req.devargs, devargs, EAL_DEV_MP_DEV_ARGS_MAX_LEN);
-- 
2.43.0


^ permalink raw reply related

* RE: [EXTERNAL] Re: [PATCH 1/2] eal: return error on devargs truncation in hotplug MP messages
From: Long Li @ 2026-06-24  0:06 UTC (permalink / raw)
  To: David Marchand
  Cc: dev@dpdk.org, bruce.richardson@intel.com,
	stephen@networkplumber.org, Burakov, Anatoly
In-Reply-To: <CAJFAV8ye5NG=-xkXjNX5+jBpJgV1S5oe02_+rAa9BYwNv_wmAA@mail.gmail.com>

> On Wed, 25 Mar 2026 at 02:45, Long Li <longli@microsoft.com> wrote:
> >
> > The EAL hotplug multi-process messaging uses a fixed-size buffer
> > (EAL_DEV_MP_DEV_ARGS_MAX_LEN, 128 bytes) for device arguments.
> > When devargs exceeds this limit, strlcpy silently truncates the
> > string. This causes secondary processes to receive incomplete devargs
> > during hotplug re-add, leading to failed port re-initialization.
> >
> > For example, a MANA PCI device with 6 mac= arguments:
> >
> >   mac=AA:BB:CC:DD:EE:01,mac=AA:BB:CC:DD:EE:02,
> >   mac=AA:BB:CC:DD:EE:03,mac=AA:BB:CC:DD:EE:04,
> >   mac=AA:BB:CC:DD:EE:05,mac=AA:BB:CC:DD:EE:06
> >
> > produces a 131-byte devargs string that gets silently truncated to 127
> > bytes, losing the last MAC address.
> >
> > Return -E2BIG from rte_dev_probe() and rte_dev_remove() when devargs
> > would be truncated, instead of silently corrupting data.
> >
> > Signed-off-by: Long Li <longli@microsoft.com>
> 
> Worth a Fixes: tag and Cc: stable.
> 
> > ---
> >  lib/eal/common/eal_common_dev.c | 11 +++++++++++
> >  1 file changed, 11 insertions(+)
> >
> > diff --git a/lib/eal/common/eal_common_dev.c
> > b/lib/eal/common/eal_common_dev.c index 7185de0cb9..de24d14d28
> 100644
> > --- a/lib/eal/common/eal_common_dev.c
> > +++ b/lib/eal/common/eal_common_dev.c
> > @@ -250,6 +250,11 @@ rte_dev_probe(const char *devargs)
> >
> >         memset(&req, 0, sizeof(req));
> >         req.t = EAL_DEV_REQ_TYPE_ATTACH;
> > +       if (strlen(devargs) >= EAL_DEV_MP_DEV_ARGS_MAX_LEN) {
> > +               EAL_LOG(ERR, "devargs truncated (len %zu, max %d)",
> > +                       strlen(devargs), EAL_DEV_MP_DEV_ARGS_MAX_LEN);
> > +               return -E2BIG;
> > +       }
> >         strlcpy(req.devargs, devargs, EAL_DEV_MP_DEV_ARGS_MAX_LEN);
> 
> Please move the check before the memset().
> 
> >
> >         if (rte_eal_process_type() != RTE_PROC_PRIMARY) { @@ -397,6
> > +402,12 @@ rte_dev_remove(struct rte_device *dev)
> >
> >         memset(&req, 0, sizeof(req));
> >         req.t = EAL_DEV_REQ_TYPE_DETACH;
> > +       if (strlen(devargs) >= EAL_DEV_MP_DEV_ARGS_MAX_LEN) {
> > +               EAL_LOG(ERR, "devargs truncated (len %zu, max %d)",
> > +                       strlen(devargs), EAL_DEV_MP_DEV_ARGS_MAX_LEN);
> > +               free(devargs);
> > +               return -E2BIG;
> > +       }
> >         strlcpy(req.devargs, devargs, EAL_DEV_MP_DEV_ARGS_MAX_LEN);
> >         free(devargs);
> >
> 
> Why do we need to validate devargs on cleanup?
> Its length should have been validated during probe.
> 
> 
> --
> David Marchand

I have sent v2 with all the comments addressed.

Thanks,

Long


^ permalink raw reply

* Re: [PATCH v2 1/2] test/dma: update the sg test to verify wrap around case
From: fengchengwen @ 2026-06-24  0:29 UTC (permalink / raw)
  To: Tejasree Kondoj, Akhil Goyal, Kevin Laatz, Bruce Richardson
  Cc: Vidya Sagar Velumuri, Anoob Joseph, dev
In-Reply-To: <20260622135208.87697-2-ktejasree@marvell.com>

On 6/22/2026 9:52 PM, Tejasree Kondoj wrote:
> Run the sg test in a loop to verify wrap around case.
> Total number commands submitted to be more than the number descriptors
> allocated to verify the scenario.
> 
> Signed-off-by: Vidya Sagar Velumuri <vvelumuri@marvell.com>
> Signed-off-by: Tejasree Kondoj <ktejasree@marvell.com>
> ---
>  app/test/test_dmadev.c     | 45 ++++++++++++++++++++++++--------------
>  app/test/test_dmadev_api.c |  1 -
>  app/test/test_dmadev_api.h |  2 ++
>  3 files changed, 31 insertions(+), 17 deletions(-)
> 
> diff --git a/app/test/test_dmadev.c b/app/test/test_dmadev.c
> index 5488a1af33..b30f2214e5 100644
> --- a/app/test/test_dmadev.c
> +++ b/app/test/test_dmadev.c
> @@ -393,36 +393,28 @@ test_stop_start(int16_t dev_id, uint16_t vchan)
>  }
>  
>  static int
> -test_enqueue_sg_copies(int16_t dev_id, uint16_t vchan)
> +test_enqueue_sg(int16_t dev_id, uint16_t vchan, unsigned int n_sge, unsigned int test_len)
>  {
> -	unsigned int src_len, dst_len, n_sge, len, i, j, k;
>  	char orig_src[COPY_LEN], orig_dst[COPY_LEN];
> -	struct rte_dma_info info = { 0 };
> +	unsigned int src_len, dst_len, i, j, k;
>  	enum rte_dma_status_code status;
>  	uint16_t id, n_src, n_dst;
>  
> -	if (rte_dma_info_get(dev_id, &info) < 0)
> -		ERR_RETURN("Failed to get dev info");
> -
> -	if (info.max_sges < 2)
> -		ERR_RETURN("Test needs minimum 2 SG pointers");
> -
> -	n_sge = info.max_sges;
> -
>  	for (n_src = 1; n_src <= n_sge; n_src++) {
>  		for (n_dst = 1; n_dst <= n_sge; n_dst++) {
>  			/* Normalize SG buffer lengths */
> -			len = COPY_LEN;
> -			len -= (len % (n_src * n_dst));
> -			dst_len = len / n_dst;
> -			src_len = len / n_src;
> -
>  			struct rte_dma_sge *sg_src = alloca(sizeof(struct rte_dma_sge) * n_sge);
>  			struct rte_dma_sge *sg_dst = alloca(sizeof(struct rte_dma_sge) * n_sge);
>  			struct rte_mbuf **src = alloca(sizeof(struct rte_mbuf *) * n_sge);
>  			struct rte_mbuf **dst = alloca(sizeof(struct rte_mbuf *) * n_sge);
>  			char **src_data = alloca(sizeof(char *) * n_sge);
>  			char **dst_data = alloca(sizeof(char *) * n_sge);
> +			unsigned int len = test_len - (test_len % (n_src * n_dst));
> +
> +			dst_len = len / n_dst;
> +			src_len = len / n_src;
> +			if (dst_len == 0 || src_len == 0)
> +				continue;
>  
>  			for (i = 0 ; i < len; i++)
>  				orig_src[i] = rte_rand() & 0xFF;
> @@ -514,6 +506,27 @@ test_enqueue_sg_copies(int16_t dev_id, uint16_t vchan)
>  	return 0;
>  }
>  
> +static int
> +test_enqueue_sg_copies(int16_t dev_id, uint16_t vchan)
> +{
> +	struct rte_dma_info info = { 0 };
> +	unsigned int n_sge, len;
> +	int loop_count = 0;
> +
> +	if (rte_dma_info_get(dev_id, &info) < 0)
> +		ERR_RETURN("Failed to get dev info");
> +
> +	n_sge = RTE_MIN(info.max_sges, TEST_SG_MAX);

test_enqueue_sg() has protection:
+			if (dst_len == 0 || src_len == 0)
+				continue;

So no need RTE_MIN, just info.max_sges, in this way we will test the device's capability.

> +	len = COPY_LEN;
> +
> +	do {
> +		test_enqueue_sg(dev_id, vchan, n_sge, len);

We need check the retcode of test_enqueue_sg, else this case will return OK even the
copy failed.

> +		loop_count++;
> +	} while (loop_count * n_sge * n_sge < TEST_RINGSIZE * 3);
> +
> +	return 0;
> +}
> +
>  static int
>  test_single_sva_copy(int16_t dev_id, uint16_t vchan, const char *mem_src,
>  		     char *src, char *dst, uint32_t len)
> diff --git a/app/test/test_dmadev_api.c b/app/test/test_dmadev_api.c
> index 1ba053696b..4bb8f9e820 100644
> --- a/app/test/test_dmadev_api.c
> +++ b/app/test/test_dmadev_api.c
> @@ -16,7 +16,6 @@ extern int test_dma_api(uint16_t dev_id);
>  
>  #define TEST_MEMCPY_SIZE	1024
>  #define TEST_WAIT_US_VAL	50000
> -#define TEST_SG_MAX		64
>  
>  static int16_t test_dev_id;
>  static int16_t invalid_dev_id;
> diff --git a/app/test/test_dmadev_api.h b/app/test/test_dmadev_api.h
> index 33fbc5bd41..a03f7acd4f 100644
> --- a/app/test/test_dmadev_api.h
> +++ b/app/test/test_dmadev_api.h
> @@ -2,4 +2,6 @@
>   * Copyright(c) 2021 HiSilicon Limited
>   */
>  
> +#define TEST_SG_MAX		64

No need public this macro.

> +
>  int test_dma_api(uint16_t dev_id);


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox