DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [EXTERNAL] [PATCH v4 15/27] net/netvsc: replace rte_atomic32 with stdatomic
From: Stephen Hemminger @ 2026-06-25  2:32 UTC (permalink / raw)
  To: Long Li; +Cc: dev@dpdk.org, Wei Hu
In-Reply-To: <SA1PR21MB668328954F3A75443CD9AFC9CE082@SA1PR21MB6683.namprd21.prod.outlook.com>

On Wed, 27 May 2026 00:29:55 +0000
Long Li <longli@microsoft.com> wrote:

> >  	do {
> > -		rid = rte_atomic32_add_return(&hv->rndis_req_id, 1);
> > +		rid = rte_atomic_fetch_add_explicit(&hv->rndis_req_id, 1,
> > +
> > rte_memory_order_seq_cst);  
> 
> Does rte_atomic_fetch_add_explicit() return the old value of hv->rndis_req_id? If yes this is not correct, as the rte_atomic32_add_return() used to return the new value.

This is a request id, it doesn't really matter if it is old value or new one as long as it is consistenly used.
Optionally could just add one to the result.

Also not clear if sequential consistent order is needed here. There are not other dependent loads or stores.

^ permalink raw reply

* Re: [PATCH v10 13/21] net/txgbe: fix link stability for 40G NIC
From: Stephen Hemminger @ 2026-06-25  2:26 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: 'Zaiyu Wang', dev, stable
In-Reply-To: <05c401dd0444$1c9a3ba0$55ceb2e0$@trustnetic.com>

On Thu, 25 Jun 2026 09:44:07 +0800
Jiawen Wu <jiawenwu@trustnetic.com> wrote:

> > > +void txgbe_e56_rx_rd_second_code_40g(struct txgbe_hw *hw, int *SECOND_CODE, int lane)
> > > +{
> > > +	int i, median;
> > > +	unsigned int rdata;
> > > +	u32 addr;
> > > +	int RXS_BBCDR_SECOND_ORDER_ST[RXS_READ_COUNT];
> > > +
> > > +	/* Set ovrd_en=0 to read ASIC value */
> > > +	addr = E56G__RXS0_ANA_OVRDEN_1_ADDR + (lane *  E56PHY_RXS_OFFSET);
> > > +	rdata = rd32_ephy(hw, addr);
> > > +	EPHY_XFLD(E56G__RXS0_ANA_OVRDEN_1, ovrd_en_ana_bbcdr_int_cstm_i) = 0;
> > > +	wr32_ephy(hw, addr, rdata);
> > > +
> > > +	/*
> > > +	 * As status update from RXS hardware is asynchronous to read status of SECOND_ORDER,
> > > +	 * follow sequence mentioned below.
> > > +	 */
> > > +	for (i = 0; i < RXS_READ_COUNT; i = i + 1) {
> > > +		addr = E56G__RXS0_ANA_OVRDVAL_5_ADDR + (lane *  E56PHY_RXS_OFFSET);
> > > +		rdata = rd32_ephy(hw, addr);
> > > +		RXS_BBCDR_SECOND_ORDER_ST[i] = EPHY_XFLD(E56G__RXS0_ANA_OVRDVAL_5,
> > > +							 ana_bbcdr_int_cstm_i);
> > > +		usec_delay(100);
> > > +	}
> > > +
> > > +	/* sort array RXS_BBCDR_SECOND_ORDER_ST[i] */
> > > +	qsort(RXS_BBCDR_SECOND_ORDER_ST, RXS_READ_COUNT, sizeof(int), txgbe_e56_int_cmp);
> > > +
> > > +	median = ((RXS_READ_COUNT + 1) / 2) - 1;
> > > +	*SECOND_CODE = RXS_BBCDR_SECOND_ORDER_ST[median];
> > > +
> > > +	return;
> > > +}  
> > 
> > These extra returns are causing extra checkpatch warnings.
> > I know this is base code but if possible could you remove them.
> > 
> > 
> > WARNING:RETURN_VOID: void function return statements are not generally useful
> > #707: FILE: drivers/net/txgbe/base/txgbe_e56.c:1806:
> > +	return;
> > +}
> > 
> > WARNING:RETURN_VOID: void function return statements are not generally useful
> > #736: FILE: drivers/net/txgbe/base/txgbe_e56.c:1835:
> > +	return;
> > +}  
> 
> Alternatively, it can be declared as a static function. It is only invoked in this .c file.
> 
> 

Checkpatch complains about the extra return (it is old BSD stylism).
Like I said, not a big deal; just showing it.

^ permalink raw reply

* Re: [PATCH v4] dts: report dut/NIC info during DTS run
From: Patrick Robb @ 2026-06-25  2:06 UTC (permalink / raw)
  To: Koushik Bhargav Nimoji; +Cc: luca.vizzarro, dev, abailey, ahassick, lylavoie
In-Reply-To: <20260624213307.2095056-1-knimoji@iol.unh.edu>

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

On Wed, Jun 24, 2026 at 5:33 PM Koushik Bhargav Nimoji <knimoji@iol.unh.edu>
wrote:

> 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
> v4:
>     *Moved nic info gathering step before the nics get
>      binded to their respective drivers
>     *Condensed some areas of code in order to make them
>      more readable
>     *Removed redundant None checks and added some where
>      required
>     *Fixed LshwOutput class to better reflect the lshw
>      command output
> ---
>  dts/framework/test_run.py                    |  8 +++
>  dts/framework/testbed_model/linux_session.py | 68 ++++++++++++++++++++
>  dts/framework/testbed_model/os_session.py    | 11 ++++
>  3 files changed, 87 insertions(+)
>
> diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
> index 94dc6023a7..c92fe90f2e 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
> @@ -347,6 +348,12 @@ def next(self) -> State | None:
>          test_run.ctx.dpdk.setup()
>          test_run.ctx.topology.setup()
>
> +        used_nic_info: list[dict[str, str]] =
> self.test_run.ctx.sut_node.main_session.get_nic_info()
>

drop "used" for nic_info or change to testrun_nic_info?


> +        with open(f"{SETTINGS.output_dir}/dut_info.json", "w") as file:
> +            json.dump(used_nic_info, file, indent=3)
> +
> +        self.logger.info(f"DUT NIC info written to:
> {SETTINGS.output_dir}/dut_info.json")
> +
>          if test_run.config.use_virtual_functions:
>              test_run.ctx.topology.instantiate_vf_ports()
>          if test_run.ctx.sut_node.cryptodevs and test_run.config.crypto:
> @@ -370,6 +377,7 @@ 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
>          )
> +
>          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 3a6e97974b..9e9146c372 100644
> --- a/dts/framework/testbed_model/linux_session.py
> +++ b/dts/framework/testbed_model/linux_session.py
> @@ -38,6 +38,8 @@ class LshwConfigurationOutput(TypedDict):
>      driver: str
>      #:
>      link: str
> +    #:
> +    firmware: str
>
>
>  class LshwOutput(TypedDict):
> @@ -61,6 +63,12 @@ class LshwOutput(TypedDict):
>              ...
>      """
>
> +    #:
> +    vendor: NotRequired[str]
> +    #:
> +    product: NotRequired[str]
> +    #:
> +    version: NotRequired[str]
>      #:
>      businfo: str
>      #:
> @@ -197,6 +205,66 @@ 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, str]]:
> +        """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: list[dict[str, str]] = []
> +        for port in self._config.ports:
> +            pci_addr = port.pci
> +
> +            command_result = self.send_command(
>

rename to lshw_result please.


> +                f"sudo lshw -c network -businfo | grep '{pci_addr}' | cut
> -d'@' -f1"
> +            )
> +            if command_result.return_code != 0 and command_result.stdout
> == "":
> +                raise ConfigurationError(f"Unable to get bus type for
> port {pci_addr}.")
> +            bus_type = command_result.stdout
> +
> +            bus_info = f"{bus_type}@{pci_addr}"
> +            nic_port: LshwOutput | None = port_data[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["configuration"]
> +            if config is None:
> +                raise ConfigurationError(
> +                    f"Configuration info for port {pci_addr} could not be
> found on the node."
> +                )
> +
> +            if "logicalname" not in nic_port:
> +                raise ConfigurationError(
> +                    f"Logical name for port {pci_addr} could not be found
> on the node."
> +                )
> +
> +            command_result = self.send_command(
>

ethtool_result


> +                f"ethtool {nic_port['logicalname']} | grep 'Speed:' | awk
> '{{print $2}}'"
> +            )

+            if command_result.return_code == 0 and command_result.stdout:
> +                nic_speed = command_result.stdout
> +            else:
> +                self._logger.error(f"Unable to get speed for NIC:
> {pci_addr}")
> +                nic_speed = None
> +
> +            dut_json = {
> +                "make": nic_port["vendor"] if "vendor" in nic_port else
> "Unknown",
> +                "model": nic_port["product"] if "product" in nic_port
> else "Unknown",
> +                "hardware version": nic_port["version"] if "version" in
> nic_port else "Unknown",
> +                "firmware version": config["firmware"] if "firmware" in
> config else "Unknown",
> +                "deviceBusType": bus_type,
> +                "deviceId": nic_port["serial"] if "serial" in nic_port
> else "Unknown",
> +                "pmd": config["driver"] if "driver" in config else
> "Unknown",
> +                "speed": nic_speed or "Unknown",
> +            }
> +            all_nic_info.append(dut_json)
> +
> +        return all_nic_info
> +
>

What is the intended behavior for cryptodev tests? I realize the ports list
will be empty and we will not enter the initial loop, but is this intended?
Do we want to gether cryptodev info too?


>      def bind_ports_to_driver(self, ports: list[Port], driver_name: str)
> -> None:
>
>
Reviewed-by: Patrick Robb <patrickrobb1997@gmail.com>

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

^ permalink raw reply

* RE: [PATCH v10 13/21] net/txgbe: fix link stability for 40G NIC
From: Jiawen Wu @ 2026-06-25  1:44 UTC (permalink / raw)
  To: 'Stephen Hemminger', 'Zaiyu Wang'; +Cc: dev, stable
In-Reply-To: <20260624091858.20289fc8@phoenix.local>

> > +void txgbe_e56_rx_rd_second_code_40g(struct txgbe_hw *hw, int *SECOND_CODE, int lane)
> > +{
> > +	int i, median;
> > +	unsigned int rdata;
> > +	u32 addr;
> > +	int RXS_BBCDR_SECOND_ORDER_ST[RXS_READ_COUNT];
> > +
> > +	/* Set ovrd_en=0 to read ASIC value */
> > +	addr = E56G__RXS0_ANA_OVRDEN_1_ADDR + (lane *  E56PHY_RXS_OFFSET);
> > +	rdata = rd32_ephy(hw, addr);
> > +	EPHY_XFLD(E56G__RXS0_ANA_OVRDEN_1, ovrd_en_ana_bbcdr_int_cstm_i) = 0;
> > +	wr32_ephy(hw, addr, rdata);
> > +
> > +	/*
> > +	 * As status update from RXS hardware is asynchronous to read status of SECOND_ORDER,
> > +	 * follow sequence mentioned below.
> > +	 */
> > +	for (i = 0; i < RXS_READ_COUNT; i = i + 1) {
> > +		addr = E56G__RXS0_ANA_OVRDVAL_5_ADDR + (lane *  E56PHY_RXS_OFFSET);
> > +		rdata = rd32_ephy(hw, addr);
> > +		RXS_BBCDR_SECOND_ORDER_ST[i] = EPHY_XFLD(E56G__RXS0_ANA_OVRDVAL_5,
> > +							 ana_bbcdr_int_cstm_i);
> > +		usec_delay(100);
> > +	}
> > +
> > +	/* sort array RXS_BBCDR_SECOND_ORDER_ST[i] */
> > +	qsort(RXS_BBCDR_SECOND_ORDER_ST, RXS_READ_COUNT, sizeof(int), txgbe_e56_int_cmp);
> > +
> > +	median = ((RXS_READ_COUNT + 1) / 2) - 1;
> > +	*SECOND_CODE = RXS_BBCDR_SECOND_ORDER_ST[median];
> > +
> > +	return;
> > +}
> 
> These extra returns are causing extra checkpatch warnings.
> I know this is base code but if possible could you remove them.
> 
> 
> WARNING:RETURN_VOID: void function return statements are not generally useful
> #707: FILE: drivers/net/txgbe/base/txgbe_e56.c:1806:
> +	return;
> +}
> 
> WARNING:RETURN_VOID: void function return statements are not generally useful
> #736: FILE: drivers/net/txgbe/base/txgbe_e56.c:1835:
> +	return;
> +}

Alternatively, it can be declared as a static function. It is only invoked in this .c file.



^ permalink raw reply

* [PATCH 2/2] app/testpmd: fix runtime config of Rx split
From: Thomas Monjalon @ 2026-06-24 23:03 UTC (permalink / raw)
  To: dev; +Cc: Song Jiale, Aman Singh, Luca Vizzarro, Patrick Robb,
	Gregory Etelson
In-Reply-To: <20260624230656.2172633-1-thomas@monjalon.net>

When adding selective Rx, it was assumed that the queue configuration
was set when starting testpmd via the command line options.
But it should be possible to configure mempools with --mbuf-size,
start Rx, stop, and configure later a split in the testpmd CLI.
In such a scenario, a regression prevented to start
with more than 1 mempool without configuring a split:

	Configuring Port 0 (socket 0)
	ETHDEV: No Rx segmentation offload configured
	Fail to configure port 0 rx queues

It is fixed by considering having multiple mempools
is not a condition to trigger Rx split.

A test is added in DTS to validate this use case.

Bugzilla ID: 1956
Fixes: 0be0ad196b52 ("app/testpmd: support selective Rx")

Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
 app/test-pmd/testpmd.c          |  3 +--
 dts/tests/TestSuite_rx_split.py | 24 ++++++++++++++++++++++++
 2 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index fcd8a90967..b241a7025b 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -2750,8 +2750,7 @@ rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
 	uint32_t prev_hdrs = 0;
 	int ret;
 
-	if (multi_rx_mempool == 0 &&
-	    (rx_pkt_nb_segs > 1 || mbuf_data_size_n > 1)) {
+	if (multi_rx_mempool == 0 && rx_pkt_nb_segs > 1) {
 		unsigned int nb_segs = RTE_MAX(rx_pkt_nb_segs, (uint8_t)mbuf_data_size_n);
 
 		/* multi-segment configuration */
diff --git a/dts/tests/TestSuite_rx_split.py b/dts/tests/TestSuite_rx_split.py
index 633ba0bf1e..5117a569e2 100644
--- a/dts/tests/TestSuite_rx_split.py
+++ b/dts/tests/TestSuite_rx_split.py
@@ -210,6 +210,30 @@ def expected(packet: bytes) -> bytes:
 
             self._start_and_verify(testpmd, expected)
 
+    @func_test
+    def selective_rx_runtime_config(self) -> None:
+        """Configure selective Rx at runtime after initial startup.
+
+        Steps:
+            Start testpmd with two mbuf-size pools but no rxpkts/rxhdrs.
+            Stop ports, configure buffer split offload, set rxpkts, restart ports.
+            Send an Ether/IP/payload packet.
+
+        Verify:
+            Initial startup succeeds without error.
+            Received packet has Ether + IP headers only after runtime config.
+            Port stats show expected rx_packets and rx_bytes.
+        """
+        with self._create_testpmd(
+            mbuf_size=[512, 0],
+        ) as testpmd:
+            self._start_and_verify(testpmd)
+            testpmd.stop()
+            testpmd.stop_all_ports()
+            testpmd.send_command("port config 0 rx_offload buffer_split on")
+            testpmd.send_command(f"set rxpkts {ETHER_IP_HDR_LEN},0")
+            self._start_and_verify(testpmd, ETHER_IP_HDR_LEN)
+
     @func_test
     def selective_rx_no_offload(self) -> None:
         """Configure selective Rx with buffer split disabled.
-- 
2.54.0


^ permalink raw reply related

* [PATCH 1/2] dts: simplify packet check in Rx split
From: Thomas Monjalon @ 2026-06-24 23:03 UTC (permalink / raw)
  To: dev; +Cc: Song Jiale, Luca Vizzarro, Patrick Robb
In-Reply-To: <20260624230656.2172633-1-thomas@monjalon.net>

Add shortcuts to the function verifying a received packet
match its expected content.
Previous version worked with a callback function defining
the expected received packet based on the sent packet.
This new version allows to pass a header length,
so only the header is expected, or directly the expected bytes.
If the parameter is None, the full packet is expected.
This is shorter than defining a callback in many cases.

Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
 dts/tests/TestSuite_rx_split.py | 35 +++++++++++++++++----------------
 1 file changed, 18 insertions(+), 17 deletions(-)

diff --git a/dts/tests/TestSuite_rx_split.py b/dts/tests/TestSuite_rx_split.py
index 5f5a2e6187..633ba0bf1e 100644
--- a/dts/tests/TestSuite_rx_split.py
+++ b/dts/tests/TestSuite_rx_split.py
@@ -56,11 +56,24 @@ def _build_packet(self) -> Packet:
         packet = Ether() / IP() / Raw(load=PAYLOAD)
         return adjust_addresses([packet])[0]
 
-    def _start_and_verify(self, testpmd: TestPmd, expected: Callable[[bytes], bytes]) -> None:
+    def _start_and_verify(
+        self,
+        testpmd: TestPmd,
+        expected: Callable[[bytes], bytes] | bytes | int | None = None,
+    ) -> None:
         """Start testpmd, send the default packet, and verify received bytes."""
         testpmd.start()
         packet = self._build_packet()
-        self._send_and_verify(testpmd, packet, expected(bytes(packet)))
+        raw = bytes(packet)
+        if expected is None:
+            raw_expected = raw
+        elif isinstance(expected, int):
+            raw_expected = raw[:expected]
+        elif isinstance(expected, bytes):
+            raw_expected = expected
+        else:
+            raw_expected = expected(raw)
+        self._send_and_verify(testpmd, packet, raw_expected)
 
     def _send_and_verify(self, testpmd: TestPmd, tg_packet: Packet, expected: bytes) -> None:
         """Clear stats, send a packet, and verify received content and stats.
@@ -128,11 +141,7 @@ def selective_rx_headers(self) -> None:
             rx_segments_length=[ETHER_IP_HDR_LEN, 0],
             mbuf_size=[256, 0],
         ) as testpmd:
-
-            def expected(packet: bytes) -> bytes:
-                return packet[:ETHER_IP_HDR_LEN]
-
-            self._start_and_verify(testpmd, expected)
+            self._start_and_verify(testpmd, ETHER_IP_HDR_LEN)
 
     @func_test
     def selective_rx_headers_discard_length(self) -> None:
@@ -151,11 +160,7 @@ def selective_rx_headers_discard_length(self) -> None:
             rx_segments_length=[ETHER_IP_HDR_LEN, len(PAYLOAD)],
             mbuf_size=[256, 0],
         ) as testpmd:
-
-            def expected(packet: bytes) -> bytes:
-                return packet[:ETHER_IP_HDR_LEN]
-
-            self._start_and_verify(testpmd, expected)
+            self._start_and_verify(testpmd, ETHER_IP_HDR_LEN)
 
     @func_test
     def selective_rx_payload_only(self) -> None:
@@ -173,11 +178,7 @@ def selective_rx_payload_only(self) -> None:
             rx_segments_length=[ETHER_IP_HDR_LEN, len(PAYLOAD)],
             mbuf_size=[0, 512],
         ) as testpmd:
-
-            def expected(_: bytes) -> bytes:
-                return PAYLOAD
-
-            self._start_and_verify(testpmd, expected)
+            self._start_and_verify(testpmd, PAYLOAD)
 
     @func_test
     def selective_rx_two_segments(self) -> None:
-- 
2.54.0


^ permalink raw reply related

* [PATCH 0/2] fix Rx split in testpmd
From: Thomas Monjalon @ 2026-06-24 23:03 UTC (permalink / raw)
  To: dev; +Cc: Song Jiale

Adding selective Rx in DPDK 26.07-rc1
introduced a regression when configuring Rx split
at runtime with mempools defined on start.

While fixing it, a test is added in DTS,
and a refactoring in DTS is added as first patch
to make the second change smaller.

Thomas Monjalon (2):
  dts: simplify packet check in Rx split
  app/testpmd: fix runtime config of Rx split

 app/test-pmd/testpmd.c          |  3 +-
 dts/tests/TestSuite_rx_split.py | 59 +++++++++++++++++++++++----------
 2 files changed, 43 insertions(+), 19 deletions(-)

-- 
2.54.0


^ permalink raw reply

* Re: [PATCH v5] pcapng: add user-supplied timestamp support
From: Stephen Hemminger @ 2026-06-24 22:17 UTC (permalink / raw)
  To: Dawid Wesierski; +Cc: dev, marek.kasiewicz, thomas, mb
In-Reply-To: <20260624215858.710217-1-dawid.wesierski@intel.com>

On Wed, 24 Jun 2026 17:57:01 -0400
Dawid Wesierski <dawid.wesierski@intel.com> wrote:

> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pcapng_copy_ts, 26.07)
> +struct rte_mbuf *
> +rte_pcapng_copy_ts(uint16_t port_id, uint32_t queue,
> +		const struct rte_mbuf *md,
> +		struct rte_mempool *mp,
> +		uint32_t length,
> +		enum rte_pcapng_direction direction,
> +		const char *comment,
> +		uint64_t timestamp)
>  {

I don't think you understood previous feedback.
  - use API versioning to provide additional argument to rte_pcapng_copy
  - do not introduce another function

The whole adjustment stuff just exists because the scaling parameters
are not available in rte_pcapng_copy() which is stateless and doesn't
want to have to lookup something in callback pdump_copy_burst.

Would like to have a more generic mechanism in future where timestamp
comes from hardware if available on rx. Then would need to have per-port
scaling conversion function.

^ permalink raw reply

* Re: [PATCH v5] pcapng: add user-supplied timestamp support
From: Stephen Hemminger @ 2026-06-24 22:07 UTC (permalink / raw)
  To: Dawid Wesierski; +Cc: dev, marek.kasiewicz, thomas, mb
In-Reply-To: <20260624215858.710217-1-dawid.wesierski@intel.com>

On Wed, 24 Jun 2026 17:57:01 -0400
Dawid Wesierski <dawid.wesierski@intel.com> wrote:

> +	/*
> +	 * Use caller provided timestamp.
> +	 * If none provided, use current TSC and set flag.
> +	 */
> +	if (timestamp == 0)
> +		timestamp = rte_get_tsc_cycles() | PCAPNG_TSC_FLAG;
> +

If you are going to use high bit as flag, then probably need to 
enforce that that bit is not set on input from user.

^ permalink raw reply

* [PATCH v4] dts: report dut/NIC info during DTS run
From: Koushik Bhargav Nimoji @ 2026-06-24 21:33 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   
v4:
    *Moved nic info gathering step before the nics get
     binded to their respective drivers 
    *Condensed some areas of code in order to make them
     more readable
    *Removed redundant None checks and added some where
     required
    *Fixed LshwOutput class to better reflect the lshw
     command output
---
 dts/framework/test_run.py                    |  8 +++
 dts/framework/testbed_model/linux_session.py | 68 ++++++++++++++++++++
 dts/framework/testbed_model/os_session.py    | 11 ++++
 3 files changed, 87 insertions(+)

diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 94dc6023a7..c92fe90f2e 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
@@ -347,6 +348,12 @@ def next(self) -> State | None:
         test_run.ctx.dpdk.setup()
         test_run.ctx.topology.setup()
 
+        used_nic_info: list[dict[str, str]] = 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)
+
+        self.logger.info(f"DUT NIC info written to: {SETTINGS.output_dir}/dut_info.json")
+
         if test_run.config.use_virtual_functions:
             test_run.ctx.topology.instantiate_vf_ports()
         if test_run.ctx.sut_node.cryptodevs and test_run.config.crypto:
@@ -370,6 +377,7 @@ 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
         )
+
         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 3a6e97974b..9e9146c372 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -38,6 +38,8 @@ class LshwConfigurationOutput(TypedDict):
     driver: str
     #:
     link: str
+    #:
+    firmware: str
 
 
 class LshwOutput(TypedDict):
@@ -61,6 +63,12 @@ class LshwOutput(TypedDict):
             ...
     """
 
+    #:
+    vendor: NotRequired[str]
+    #:
+    product: NotRequired[str]
+    #:
+    version: NotRequired[str]
     #:
     businfo: str
     #:
@@ -197,6 +205,66 @@ 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, str]]:
+        """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: list[dict[str, str]] = []
+        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"
+            )
+            if command_result.return_code != 0 and command_result.stdout == "":
+                raise ConfigurationError(f"Unable to get bus type for port {pci_addr}.")
+            bus_type = command_result.stdout
+
+            bus_info = f"{bus_type}@{pci_addr}"
+            nic_port: LshwOutput | None = port_data[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["configuration"]
+            if config is None:
+                raise ConfigurationError(
+                    f"Configuration info for port {pci_addr} could not be found on the node."
+                )
+
+            if "logicalname" not in nic_port:
+                raise ConfigurationError(
+                    f"Logical name for port {pci_addr} could not be found on the node."
+                )
+
+            command_result = self.send_command(
+                f"ethtool {nic_port['logicalname']} | grep 'Speed:' | awk '{{print $2}}'"
+            )
+            if command_result.return_code == 0 and command_result.stdout:
+                nic_speed = command_result.stdout
+            else:
+                self._logger.error(f"Unable to get speed for NIC: {pci_addr}")
+                nic_speed = None
+
+            dut_json = {
+                "make": nic_port["vendor"] if "vendor" in nic_port else "Unknown",
+                "model": nic_port["product"] if "product" in nic_port else "Unknown",
+                "hardware version": nic_port["version"] if "version" in nic_port else "Unknown",
+                "firmware version": config["firmware"] if "firmware" in config else "Unknown",
+                "deviceBusType": bus_type,
+                "deviceId": nic_port["serial"] if "serial" in nic_port else "Unknown",
+                "pmd": config["driver"] if "driver" in config else "Unknown",
+                "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..f88427a53d 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, str]]:
+        """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 v5] pcapng: add user-supplied timestamp support
From: Dawid Wesierski @ 2026-06-24 21:57 UTC (permalink / raw)
  To: dev; +Cc: dawid.wesierski, marek.kasiewicz, thomas, stephen, mb
In-Reply-To: <20260623141302.486601-1-dawid.wesierski@intel.com>

Introduce rte_pcapng_copy_ts() alongside the existing rte_pcapng_copy()
so that callers with a hardware PTP or pre-captured timestamp can inject
an exact epoch-ns value directly into the packet record.

Timestamp handling in rte_pcapng_copy_ts():
 - ts != 0: caller-supplied nanoseconds since the Unix epoch, stored as-is.
 - ts == 0: TSC captured at copy time with bit 63 set as a sentinel.
   rte_pcapng_write_packets() detects the sentinel and converts the TSC to
   epoch ns using the file's calibrated clock.  The TSC will not reach
   bit 63 for centuries, and epoch-ns values stay below bit 63 until 2554,
   so the bit is safe to use as a disambiguation flag.

rte_pcapng_copy() is retained as a real exported function (not an inline
wrapper) so the stable ABI symbol is preserved.  It simply calls
rte_pcapng_copy_ts(..., 0) to capture the current TSC.

rte_pcapng_tsc_to_ns() is added as a new experimental helper (addressing
review requests from Stephen Hemminger and Morten Brørup).  It exposes the
same calibrated, drift-compensated, divide-free TSC-to-epoch-ns conversion
used internally by rte_pcapng_write_packets(), allowing callers to convert
a TSC captured at packet arrival time before passing it to
rte_pcapng_copy_ts().

Signed-off-by: Marek Kasiewicz <marek.kasiewicz@intel.com>
Signed-off-by: Dawid Wesierski <dawid.wesierski@intel.com>
---

Hi Stephen, Morten,
Thank you for the feedback on the previous versions,
In this version i added unit Tests test case in app/test/test_pcapng.c to
verify the TSC-to-NS conversion and the custom timestamp injection.


Regards,
Dawid Węsierski

 .mailmap                |   2 +
 app/test/test_pcapng.c  | 108 ++++++++++++++++++++++++++++++++++++++++
 lib/pcapng/rte_pcapng.c |  42 +++++++++++++---
 lib/pcapng/rte_pcapng.h |  53 ++++++++++++++++++++
 4 files changed, 199 insertions(+), 6 deletions(-)

diff --git a/.mailmap b/.mailmap
index 4001e5fb0e..a7d97a631e 100644
--- a/.mailmap
+++ b/.mailmap
@@ -366,6 +366,7 @@ David Zeng <zengxhsh@cn.ibm.com>
 Davide Caratti <dcaratti@redhat.com>
 Dawid Gorecki <dgr@semihalf.com>
 Dawid Jurczak <dawid_jurek@vp.pl>
+Dawid Wesierski <dawid.wesierski@intel.com> Wesierski, Dawid <dawid.wesierski@intel.com>
 Dawid Zielinski <dawid.zielinski@intel.com>
 Dawid Łukwiński <dawid.lukwinski@intel.com>
 Daxue Gao <daxuex.gao@intel.com>
@@ -1014,6 +1015,7 @@ Marcin Wilk <marcin.wilk@caviumnetworks.com>
 Marcin Wojtas <mw@semihalf.com>
 Marcin Zapolski <marcinx.a.zapolski@intel.com>
 Marco Varlese <mvarlese@suse.de>
+Marek Kasiewicz <marek.kasiewicz@intel.com>
 Marek Mical <marekx.mical@intel.com>
 Marek Zalfresso-jundzillo <marekx.zalfresso-jundzillo@intel.com>
 Maria Lingemark <maria.lingemark@ericsson.com>
diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c
index 298bcbd31f..0554c33369 100644
--- a/app/test/test_pcapng.c
+++ b/app/test/test_pcapng.c
@@ -672,6 +672,113 @@ test_write_before_open(void)
 	return -1;
 }
 
+static int
+test_pcapng_timestamp(void)
+{
+	char file_name[PATH_MAX] = "/tmp/pcapng_test_XXXXXX.pcapng";
+	rte_pcapng_t *pcapng = NULL;
+	int ret, tmp_fd;
+	struct dummy_mbuf mbfs;
+	struct rte_mbuf *orig, *mc;
+	uint64_t now_ns, tsc, ns_from_tsc, pcap_ts;
+
+	tmp_fd = mkstemps(file_name, strlen(".pcapng"));
+	if (tmp_fd == -1) {
+		perror("mkstemps() failure");
+		goto fail;
+	}
+
+	pcapng = rte_pcapng_fdopen(tmp_fd, NULL, NULL, "pcapng_ts_test", NULL);
+	if (pcapng == NULL) {
+		printf("rte_pcapng_fdopen failed\n");
+		close(tmp_fd);
+		goto fail;
+	}
+
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB, NULL, NULL, NULL);
+	if (ret < 0) {
+		printf("can not add port %u\n", port_id);
+		goto fail;
+	}
+
+	/* Test 1: rte_pcapng_tsc_to_ns */
+	tsc = rte_get_tsc_cycles();
+	now_ns = current_timestamp();
+	ns_from_tsc = rte_pcapng_tsc_to_ns(pcapng, tsc);
+
+	/* Check if TSC-derived NS is reasonably close to wall clock NS (within 100ms) */
+	if (ns_from_tsc > now_ns + 100000000 || ns_from_tsc < now_ns - 100000000) {
+		printf("TSC to NS conversion failed: tsc=%"PRIu64
+		       " ns_from_tsc=%"PRIu64" now_ns=%"PRIu64"\n",
+		       tsc, ns_from_tsc, now_ns);
+		goto fail;
+	}
+
+	/* Test 2: rte_pcapng_copy_ts with explicit timestamp */
+	mbuf1_prepare(&mbfs);
+	orig = &mbfs.mb[0];
+	pcap_ts = now_ns + 1000000000; /* 1 second in future to be distinct */
+
+	mc = rte_pcapng_copy_ts(port_id, 0, orig, mp, rte_pktmbuf_pkt_len(orig),
+				RTE_PCAPNG_DIRECTION_IN, "custom_ts", pcap_ts);
+	if (mc == NULL) {
+		printf("rte_pcapng_copy_ts failed\n");
+		goto fail;
+	}
+
+	/* Write it */
+	ret = rte_pcapng_write_packets(pcapng, &mc, 1);
+	rte_pktmbuf_free(mc);
+	if (ret <= 0) {
+		printf("Write of custom timestamp packet failed\n");
+		goto fail;
+	}
+
+	rte_pcapng_close(pcapng);
+
+	/* Validate the file using libpcap */
+	/* We expect 1 packet with timestamp exactly pcap_ts */
+	{
+		char errbuf[PCAP_ERRBUF_SIZE];
+		pcap_t *pcap;
+		struct pcap_pkthdr h;
+		const u_char *bytes;
+		uint64_t ns;
+
+		pcap = pcap_open_offline_with_tstamp_precision(file_name,
+							       PCAP_TSTAMP_PRECISION_NANO,
+							       errbuf);
+		if (pcap == NULL) {
+			printf("pcap_open_offline failed: %s\n", errbuf);
+			goto fail;
+		}
+
+		bytes = pcap_next(pcap, &h);
+		if (bytes == NULL) {
+			printf("No packets in file\n");
+			pcap_close(pcap);
+			goto fail;
+		}
+
+		ns = (uint64_t)h.ts.tv_sec * NS_PER_S + h.ts.tv_usec;
+		if (ns != pcap_ts) {
+			printf("Timestamp mismatch: expected %"PRIu64" got %"PRIu64"\n",
+			       pcap_ts, ns);
+			pcap_close(pcap);
+			goto fail;
+		}
+		pcap_close(pcap);
+	}
+
+	remove(file_name);
+	return 0;
+
+fail:
+	if (pcapng)
+		rte_pcapng_close(pcapng);
+	return -1;
+}
+
 static void
 test_cleanup(void)
 {
@@ -688,6 +795,7 @@ unit_test_suite test_pcapng_suite  = {
 		TEST_CASE(test_add_interface),
 		TEST_CASE(test_write_packets),
 		TEST_CASE(test_write_before_open),
+		TEST_CASE(test_pcapng_timestamp),
 		TEST_CASES_END()
 	}
 };
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index b5d1026891..84c427fb2d 100644
--- a/lib/pcapng/rte_pcapng.c
+++ b/lib/pcapng/rte_pcapng.c
@@ -37,6 +37,9 @@
 /* upper bound for strings in pcapng option data */
 #define PCAPNG_STR_MAX	UINT16_MAX
 
+/* Flag to indicate timestamp is in TSC cycles (bit 63) */
+#define PCAPNG_TSC_FLAG	(1ULL << 63)
+
 /*
  * Converter from TSC values to nanoseconds since Unix epoch.
  * Uses reciprocal multiply to avoid runtime division.
@@ -480,6 +483,13 @@ rte_pcapng_mbuf_size(uint32_t length)
 		+ sizeof(uint32_t);		  /*  length */
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pcapng_tsc_to_ns, 26.07)
+uint64_t
+rte_pcapng_tsc_to_ns(const rte_pcapng_t *self, uint64_t tsc)
+{
+	return tsc_to_ns_epoch(&self->clock, tsc);
+}
+
 /* More generalized version rte_vlan_insert() */
 static int
 pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
@@ -554,11 +564,24 @@ rte_pcapng_copy(uint16_t port_id, uint32_t queue,
 		uint32_t length,
 		enum rte_pcapng_direction direction,
 		const char *comment)
+{
+	return rte_pcapng_copy_ts(port_id, queue, md, mp, length, direction,
+				  comment, 0);
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pcapng_copy_ts, 26.07)
+struct rte_mbuf *
+rte_pcapng_copy_ts(uint16_t port_id, uint32_t queue,
+		const struct rte_mbuf *md,
+		struct rte_mempool *mp,
+		uint32_t length,
+		enum rte_pcapng_direction direction,
+		const char *comment,
+		uint64_t timestamp)
 {
 	struct pcapng_enhance_packet_block *epb;
 	uint32_t orig_len, pkt_len, padding, flags;
 	struct pcapng_option *opt;
-	uint64_t timestamp;
 	uint16_t optlen;
 	struct rte_mbuf *mc;
 	bool rss_hash;
@@ -690,8 +713,13 @@ rte_pcapng_copy(uint16_t port_id, uint32_t queue,
 	/* Interface index is filled in later during write */
 	mc->port = port_id;
 
-	/* Put timestamp in cycles here - adjust in packet write */
-	timestamp = rte_get_tsc_cycles();
+	/*
+	 * Use caller provided timestamp.
+	 * If none provided, use current TSC and set flag.
+	 */
+	if (timestamp == 0)
+		timestamp = rte_get_tsc_cycles() | PCAPNG_TSC_FLAG;
+
 	epb->timestamp_hi = timestamp >> 32;
 	epb->timestamp_lo = (uint32_t)timestamp;
 	epb->capture_length = pkt_len;
@@ -743,9 +771,11 @@ rte_pcapng_write_packets(rte_pcapng_t *self,
 		 */
 		cycles = (uint64_t)epb->timestamp_hi << 32;
 		cycles += epb->timestamp_lo;
-		timestamp = tsc_to_ns_epoch(&self->clock, cycles);
-		epb->timestamp_hi = timestamp >> 32;
-		epb->timestamp_lo = (uint32_t)timestamp;
+		if (cycles & PCAPNG_TSC_FLAG) {
+			timestamp = tsc_to_ns_epoch(&self->clock, cycles & ~PCAPNG_TSC_FLAG);
+			epb->timestamp_hi = timestamp >> 32;
+			epb->timestamp_lo = (uint32_t)timestamp;
+		}
 
 		/*
 		 * Handle case of highly fragmented and large burst size
diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h
index d8d328f710..42c42ca60c 100644
--- a/lib/pcapng/rte_pcapng.h
+++ b/lib/pcapng/rte_pcapng.h
@@ -140,6 +140,59 @@ rte_pcapng_copy(uint16_t port_id, uint32_t queue,
 		uint32_t length,
 		enum rte_pcapng_direction direction, const char *comment);
 
+/**
+ * Format an mbuf for writing to file with a custom timestamp.
+ *
+ * @param port_id
+ *   The Ethernet port on which packet was received
+ *   or is going to be transmitted.
+ * @param queue
+ *   The queue on the Ethernet port where packet was received
+ *   or is going to be transmitted.
+ * @param mp
+ *   The mempool from which the "clone" mbufs are allocated.
+ * @param m
+ *   The mbuf to copy
+ * @param length
+ *   The upper limit on bytes to copy.  Passing UINT32_MAX
+ *   means all data (after offset).
+ * @param direction
+ *   The direction of the packer: receive, transmit or unknown.
+ * @param comment
+ *   Optional per packet comment.
+ *   Truncated to UINT16_MAX characters.
+ * @param timestamp
+ *   Nanoseconds since the Unix epoch. If zero, TSC is captured and
+ *   converted at write time.
+ *
+ * @return
+ *   - The pointer to the new mbuf formatted for pcapng_write
+ *   - NULL on error such as invalid port or out of memory.
+ */
+__rte_experimental
+struct rte_mbuf *
+rte_pcapng_copy_ts(uint16_t port_id, uint32_t queue,
+		const struct rte_mbuf *m, struct rte_mempool *mp,
+		uint32_t length,
+		enum rte_pcapng_direction direction,
+		const char *comment, uint64_t timestamp);
+
+/**
+ * Convert a TSC value to nanoseconds since the Unix epoch.
+ *
+ * Uses the calibrated clock of the capture file.
+ *
+ * @param self
+ *  The handle to the packet capture file
+ * @param tsc
+ *  The TSC value to convert
+ * @return
+ *  Nanoseconds since Unix epoch
+ */
+__rte_experimental
+uint64_t
+rte_pcapng_tsc_to_ns(const rte_pcapng_t *self, uint64_t tsc);
+
 
 /**
  * Determine optimum mbuf data size.
-- 
2.47.3

---------------------------------------------------------------------
Intel Technology Poland sp. z o.o.
ul. Slowackiego 173 | 80-298 Gdansk | Sad Rejonowy Gdansk Polnoc | VII Wydzial Gospodarczy Krajowego Rejestru Sadowego - KRS 101882 | NIP 957-07-52-316 | Kapital zakladowy 200.000 PLN.
Spolka oswiadcza, ze posiada status duzego przedsiebiorcy w rozumieniu ustawy z dnia 8 marca 2013 r. o przeciwdzialaniu nadmiernym opoznieniom w transakcjach handlowych.

Ta wiadomosc wraz z zalacznikami jest przeznaczona dla okreslonego adresata i moze zawierac informacje poufne. W razie przypadkowego otrzymania tej wiadomosci, prosimy o powiadomienie nadawcy oraz trwale jej usuniecie; jakiekolwiek przegladanie lub rozpowszechnianie jest zabronione.
This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). If you are not the intended recipient, please contact the sender and delete all copies; any review or distribution by others is strictly prohibited.

^ permalink raw reply related

* RE: [EXTERNAL] [v3] crypto/qat: require IPsec MB for HMAC precomputes
From: Akhil Goyal @ 2026-06-24 20:04 UTC (permalink / raw)
  To: Emma Finn, Kai Ji; +Cc: dev@dpdk.org
In-Reply-To: <20260622075651.1980461-1-emma.finn@intel.com>

> IPsec MB library (v1.4.0+) is now required for HMAC precomputes as
> OpenSSL 3.0 removed SHA*_Transform APIs. OpenSSL remains optional
> for DOCSIS BPI cipher fallback via EVP API.
> 
> On x86: IPsec MB required, OpenSSL optional (DOCSIS fallback)
> On ARM: IPsec MB required, OpenSSL required (DOCSIS support)
> 
> Signed-off-by: Emma Finn <emma.finn@intel.com>
> ---
> v2:
> * Fix resource leak in ossl_legacy_provider_load()
> * Added release note
> v3:
> * Removed checks for openssl <= 3.0
> ---
>  doc/guides/cryptodevs/qat.rst          |  28 +-
>  doc/guides/rel_notes/release_26_07.rst |   8 +
>  drivers/common/qat/meson.build         |  56 ++--
>  drivers/crypto/qat/qat_sym_session.c   | 448 ++-----------------------
>  4 files changed, 97 insertions(+), 443 deletions(-)
Applied to dpdk-next-crypto

^ permalink raw reply

* RE: [EXTERNAL] [PATCH] examples: use strlcpy and strlcat
From: Akhil Goyal @ 2026-06-24 20:03 UTC (permalink / raw)
  To: Bruce Richardson, dev@dpdk.org
  Cc: stable@dpdk.org, Cristian Dumitrescu, Radu Nicolau, Fan Zhang,
	Anatoly Burakov, Sivaprasad Tummala, Jasvinder Singh,
	Sergio Gonzalez Monroy, Ferruh Yigit, Pablo de Lara,
	Declan Doherty, Alan Carew
In-Reply-To: <20260623154109.722441-1-bruce.richardson@intel.com>

> Replace strncpy and other unbounded string functions, e.g. strcpy,
> strcat, with the safer alternatives strlcpy and strlcat, so that we can
> guarantee null termination of strings.
> 
> Fixes: 4bbf8e30aa5e ("examples/ip_pipeline: add CLI interface")
> Fixes: 5f657a7fbe86 ("examples/pipeline: add message passing mechanism")
> Fixes: 83f58a7b7b0a ("examples/pipeline: add commands for direct registers")
> Fixes: 0d547ed03717 ("examples/ipsec-secgw: support configuration file")
> Fixes: 63e8c07c7245 ("examples/ipsec-secgw: fix configuration parsing")
> Fixes: 41e97c2ea9e6 ("examples/l2fwd-crypto: extend crypto information")
> Fixes: e8ae9b662506 ("examples/vm_power: channel manager and monitor in
> host")
> Cc: stable@dpdk.org
> 
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
Applied to dpdk-next-crypto
Thanks.

^ permalink raw reply

* RE: [PATCH] crypto/cnxk: fix out of place AES GCM
From: Akhil Goyal @ 2026-06-24 19:46 UTC (permalink / raw)
  To: Tejasree Kondoj, Daphne Priscilla F, dev@dpdk.org
  Cc: stable@dpdk.org, Anoob Joseph, Daphne Priscilla F
In-Reply-To: <PH7PR18MB5177F3AA522F8CEAF8B25253A8E02@PH7PR18MB5177.namprd18.prod.outlook.com>

> Acked-by: Tejasree Kondoj <ktejasree@marvell.com>
> 
> > -----Original Message-----
> > From: Daphne Priscilla <df@marvell.com>
> > Sent: Friday, June 12, 2026 11:50 AM
> > To: dev@dpdk.org
> > Cc: stable@dpdk.org; Akhil Goyal <gakhil@marvell.com>; Tejasree Kondoj
> > <ktejasree@marvell.com>; Anoob Joseph <anoobj@marvell.com>; Daphne
> > Priscilla F <df@marvell.com>
> > Subject: [PATCH] crypto/cnxk: fix out of place AES GCM
> >
> > For AES-GCM out of place, when AAD is present in inbuf before the data, it is
> > treated as passthrough data. This results in AAD being present in outbuf
> > header, but test expects outbuf header to remain zero. Passthrough data is
> > now diverted to metabuf so outbuf header remains zero.
> >
> > Fixes: 7c19abdd0cf1 ("common/cnxk: support 103XX CPT")
> > Cc: stable@dpdk.org
> >
> > Signed-off-by: Daphne Priscilla <df@marvell.com>
> > ---
> >  .mailmap                                 |  1 +
> >  drivers/common/cnxk/roc_se.h             |  2 +-
> >  drivers/crypto/cnxk/cnxk_cryptodev_ops.c |  3 +
> >  drivers/crypto/cnxk/cnxk_se.h            | 96 ++++++++++++++++++++++--
> >  4 files changed, 93 insertions(+), 9 deletions(-)
Applied to dpdk-next-crypto

^ permalink raw reply

* RE: [v2] crypto/cnxk: add ML crypto support
From: Akhil Goyal @ 2026-06-24 19:42 UTC (permalink / raw)
  To: Gowrishankar Muthukrishnan, dev@dpdk.org, Nithin Kumar Dabilpuram,
	Kiran Kumar Kokkilagadda, Sunil Kumar Kori,
	Satha Koteswara Rao Kottidi, Harman Kalra, Ankur Dwivedi,
	Anoob Joseph, Tejasree Kondoj
  Cc: Stephen Hemminger, Gowrishankar Muthukrishnan
In-Reply-To: <20260621075706.23184-1-gmuthukrishn@marvell.com>

> Subject: [v2] crypto/cnxk: add ML crypto support
> 
> Add ML-KEM and ML-DSA support.
> 
> Signed-off-by: Gowrishankar Muthukrishnan <gmuthukrishn@marvell.com>
> ---
> v2:
>  - Addressed comments from v1.
> ---
>  doc/guides/cryptodevs/features/cn20k.ini      |   3 +
>  doc/guides/cryptodevs/features/default.ini    |   1 +
>  doc/guides/rel_notes/release_26_07.rst        |   3 +
>  drivers/common/cnxk/hw/cpt.h                  |   1 +
>  drivers/common/cnxk/meson.build               |   1 +
>  drivers/common/cnxk/roc_cpt.c                 |   4 +
>  drivers/common/cnxk/roc_platform.h            |   5 +
>  .../common/cnxk/roc_platform_base_symbols.c   |   2 +
>  drivers/common/cnxk/roc_re.h                  |  31 ++
>  drivers/common/cnxk/roc_re_ml_tables.c        | 248 +++++++++++
>  drivers/common/cnxk/roc_re_ml_tables.h        |  19 +
>  drivers/crypto/cnxk/cnxk_ae.h                 | 399 +++++++++++++++++-
>  drivers/crypto/cnxk/cnxk_cryptodev.c          |  11 +
>  drivers/crypto/cnxk/cnxk_cryptodev.h          |   4 +-
>  .../crypto/cnxk/cnxk_cryptodev_capabilities.c |  62 ++-
>  drivers/crypto/cnxk/cnxk_cryptodev_ops.c      |  45 +-
>  16 files changed, 817 insertions(+), 22 deletions(-)
>  create mode 100644 drivers/common/cnxk/roc_re.h
>  create mode 100644 drivers/common/cnxk/roc_re_ml_tables.c
>  create mode 100644 drivers/common/cnxk/roc_re_ml_tables.h
Acked-by: Akhil Goyal <gakhil@marvell.com>
Applied to dpdk-next-crypto


^ permalink raw reply

* RE: [PATCH v3 1/2] app/crypto-perf: support ML KEM
From: Akhil Goyal @ 2026-06-24 19:39 UTC (permalink / raw)
  To: Pratik Senapati, dev@dpdk.org; +Cc: kai.ji@intel.com
In-Reply-To: <20260624070250.1509369-1-psenapati@marvell.com>

> Subject: [PATCH v3 1/2] app/crypto-perf: support ML KEM
> 
> Add ML-KEM512 support to test-crypto-perf.
> 
> Signed-off-by: Pratik Senapati <psenapati@marvell.com>
> ---
>  app/test-crypto-perf/cperf_ops.c             |  61 +++
>  app/test-crypto-perf/cperf_options.h         |   2 +
>  app/test-crypto-perf/cperf_options_parsing.c |  20 +
>  app/test-crypto-perf/cperf_test_common.c     |   3 +-
>  app/test-crypto-perf/cperf_test_vectors.c    | 470 +++++++++++++++++++
>  app/test-crypto-perf/cperf_test_vectors.h    |  27 ++
>  app/test-crypto-perf/main.c                  |  21 +-
>  doc/guides/tools/cryptoperf.rst              |   1 +
>  8 files changed, 603 insertions(+), 2 deletions(-)

Series Acked-by: Akhil Goyal <gakhil@marvell.com>

Applied to dpdk-next-crypto


^ permalink raw reply

* RE: [EXTERNAL] [PATCH 0/3] Fixes for inline ipsec test cases
From: Akhil Goyal @ 2026-06-24 19:09 UTC (permalink / raw)
  To: Bruce Richardson, dev@dpdk.org
In-Reply-To: <20260622111835.233554-1-bruce.richardson@intel.com>

> A series of small fixes for the inline ipsec test cases, with the third
> patch being the most significant - skipping tests where we don't have the
> necessary HW support to run the tests. Previously this was leading to test
> failures when the tests should never have been run due to missing HW
> capabilities.
> 
> Bruce Richardson (3):
>   test/security_inline_proto: remove fast-free Tx flag
>   test/security_inline_proto: fix MTU calculation underflow
>   test/security_inline_proto: check for capabilities
> 
>  app/test/test_security_inline_proto.c | 75 +++++++++++++++++++++++++--
>  1 file changed, 70 insertions(+), 5 deletions(-)
> 
Series Acked-by: Akhil Goyal <gakhil@marvell.com>

Applied to dpdk-next-crypto

^ permalink raw reply

* RE: [PATCH v2 0/3] update tests with CN20K support
From: Akhil Goyal @ 2026-06-24 18:58 UTC (permalink / raw)
  To: Tejasree Kondoj, Fan Zhang
  Cc: Vidya Sagar Velumuri, Anoob Joseph, dev@dpdk.org
In-Reply-To: <20260623042723.89226-1-ktejasree@marvell.com>



> -----Original Message-----
> From: Tejasree Kondoj <ktejasree@marvell.com>
> Sent: Tuesday, June 23, 2026 9:57 AM
> To: Akhil Goyal <gakhil@marvell.com>; Fan Zhang <fanzhang.oss@gmail.com>
> Cc: Vidya Sagar Velumuri <vvelumuri@marvell.com>; Anoob Joseph
> <anoobj@marvell.com>; dev@dpdk.org
> Subject: [PATCH v2 0/3] update tests with CN20K support
> 
> Add CN20K sym/asym autotests and Rx-inject multi-segment unit test.
> 
> v2:
> - Removed asym sessionless test patch
> - Fixed test names ordering
> 
> Vidya Sagar Velumuri (3):
>   test/crypto: add asym autotest support for cn20k
>   test/crypto: add autotest support for cn20k
>   test/crypto: add unit test for Rx inject multi seg
> 
>  app/test/test_cryptodev.c      | 42 +++++++++++++++++++++++++++++++++-
>  app/test/test_cryptodev.h      |  1 +
>  app/test/test_cryptodev_asym.c |  7 ++++++
>  3 files changed, 49 insertions(+), 1 deletion(-)
Series Acked-by: Akhil Goyal <gakhil@marvell.com>

Applied to dpdk-next-crypto

^ permalink raw reply

* Re: [PATCH v3 1/2] dts: add code coverage reporting to DTS
From: Andrew Bailey @ 2026-06-24 18:55 UTC (permalink / raw)
  To: Koushik Bhargav Nimoji
  Cc: luca.vizzarro, patrickrobb1997, dev, ahassick, lylavoie
In-Reply-To: <20260622170223.1152746-1-knimoji@iol.unh.edu>

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

Reviewed-by: Andrew Bailey <abailey@iol.unh.edu>

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

^ permalink raw reply

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

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

>
> +
> +        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()
>

file.close() is redundant and can be removed.
More importantly, the info is gathered after the dut nics have been bound
to the dpdk driver. This is an issue if a driver is bound to vfio-pci since
it has limited visibility from the commands used to gather info. I tested
this with broadcom cards which showed various info fields that were empty
or None. This is resolved by moving this snippet before the DUT ports are
bound. Keep in mind to test your patches against multiple NICs,
preferably with different drivers.


> +        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]]:
>

object may be too generic, are there issues tightening the type hinting on
this signature?


> +        """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 = []
>

all_nic_info should be type hinted


> +        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}.")
>

There is a redundant check on setting the variable and then verifying.
Since you are throwing an exception if there is no valid value, you could
do a check once and throw an error and then set the variable after with no
further checks required. This makes it more readable in my opinion but let
me know if you disagree, for example...

 if command.code != 0 or command.stdout == '':
     raise ConfigurationError()
bus_type = command.stdout

+            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}")
>

this code can be condensed as well, redundant check but logic is good.

If the intention is to report unknown in this case, it should be mentioned
in this log message.
Also I'm not sure if this has a logger level error if everything runs fine
in this case.
Maybe info or warning level is sufficient.


> +

+            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
>
>

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

^ permalink raw reply

* [PATCH v5 9/9] test/bpf: check that bpf_convert can be JIT'd
From: Stephen Hemminger @ 2026-06-24 17:55 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Marat Khalili, Konstantin Ananyev
In-Reply-To: <20260624175815.673064-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>
Acked-by: Marat Khalili <marat.khalili@huawei.com>
---
 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 16a1004e51..f56f6c7d7e 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.) */
@@ -4756,11 +4757,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);
@@ -4785,18 +4788,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;
 	}
 
@@ -4804,15 +4854,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;
 }
@@ -4821,44 +4897,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;
@@ -4882,7 +4927,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",
@@ -4909,35 +4953,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;
 
@@ -4949,8 +4968,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 v5 8/9] test/bpf: check that JIT was generated
From: Stephen Hemminger @ 2026-06-24 17:55 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Marat Khalili, Konstantin Ananyev
In-Reply-To: <20260624175815.673064-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 than using an 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 0e5894a532..16a1004e51 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -3649,6 +3649,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 v5 7/9] bpf/arm64: add BPF_ABS/BPF_IND packet load support
From: Stephen Hemminger @ 2026-06-24 17:55 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, Wathsala Vithanage, Konstantin Ananyev,
	Marat Khalili
In-Reply-To: <20260624175815.673064-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 51906c7f0d..62fe5bf0dc 100644
--- a/lib/bpf/bpf_jit_arm64.c
+++ b/lib/bpf/bpf_jit_arm64.c
@@ -1133,6 +1133,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)
 {
@@ -1145,8 +1274,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;
 		}
@@ -1348,6 +1486,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 v5 6/9] bpf/arm64: fix offset type to allow a negative jump
From: Stephen Hemminger @ 2026-06-24 17:55 UTC (permalink / raw)
  To: dev
  Cc: Christophe Fontaine, stable, Stephen Hemminger, Marat Khalili,
	Wathsala Vithanage, Konstantin Ananyev, Jerin Jacob
In-Reply-To: <20260624175815.673064-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 may be 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>
Acked-by: Marat Khalili <marat.khalili@huawei.com>
---
 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 7582370062..51906c7f0d 100644
--- a/lib/bpf/bpf_jit_arm64.c
+++ b/lib/bpf/bpf_jit_arm64.c
@@ -965,10 +965,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 v5 5/9] test/bpf: add test for large shift
From: Stephen Hemminger @ 2026-06-24 17:55 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Konstantin Ananyev, Marat Khalili
In-Reply-To: <20260624175815.673064-1-stephen@networkplumber.org>

There were multiple bugs with immediate values in shift instructions.
The code was not masking as required by RFC.

Add new tests that cover these instructions.

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

diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
index 232e9e2a98..0e5894a532 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -2005,6 +2005,51 @@ test_div1_check(uint64_t rc, const void *arg)
 	return cmp_res(__func__, 0, rc, dve.out, dvt->out, sizeof(dve.out));
 }
 
+/*
+ * Shift counts are masked to the operand width (RFC 9669: 0x3f for 64-bit,
+ * 0x1f for 32-bit). Counts >= 128 also exercise the x86 imm_size() path that
+ * used to desync the stream, and the arm64 UBFM/SBFM immediate encoding.
+ */
+static const struct ebpf_insn test_shift_big_imm_prog[] = {
+	{
+		.code = (EBPF_ALU64 | EBPF_MOV | BPF_K),
+		.dst_reg = EBPF_REG_0,
+		.imm = 1
+	},
+	{
+		.code = (EBPF_ALU64 | BPF_LSH | BPF_K),
+		.dst_reg = EBPF_REG_0,
+		.imm = 191
+	},
+	{
+		.code = (EBPF_ALU64 | EBPF_ARSH | BPF_K),
+		.dst_reg = EBPF_REG_0,
+		.imm = 200
+	},
+	{
+		.code = (EBPF_ALU64 | BPF_RSH | BPF_K),
+		.dst_reg = EBPF_REG_0,
+		.imm = 130
+	},
+	{
+		.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)
+{
+	uint64_t expect = 0x3FE0000000000000ULL;
+
+	return cmp_res(__func__, expect, rc, arg, arg, 0);
+}
+
 /* call test-cases */
 static const struct ebpf_insn test_call1_prog[] = {
 
@@ -3409,6 +3454,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


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