DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [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

* [PATCH v5 4/9] bpf/arm64: mask shift count per RFC 9669
From: Stephen Hemminger @ 2026-06-24 17:55 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, stable, Wathsala Vithanage, Konstantin Ananyev,
	Marat Khalili, Jerin Jacob
In-Reply-To: <20260624175815.673064-1-stephen@networkplumber.org>

The ARM JIT was not masking the shift count as required by RFC 9669
(0x3f for 64-bit, 0x1f for 32-bit), so large immediate shift counts
overflowed the UBFM/SBFM encoding and failed the JIT. Mask the
immediate in emit_lsl/emit_lsr/emit_asr.

Fixes: 9f4469d9e83a ("bpf/arm: add logical operations")
Cc: stable@dpdk.org

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

diff --git a/lib/bpf/bpf_jit_arm64.c b/lib/bpf/bpf_jit_arm64.c
index ba7ae4d680..7582370062 100644
--- a/lib/bpf/bpf_jit_arm64.c
+++ b/lib/bpf/bpf_jit_arm64.c
@@ -545,12 +545,14 @@ emit_bitfield(struct a64_jit_ctx *ctx, bool is64, uint8_t rd, uint8_t rn,
 	emit_insn(ctx, insn, check_reg(rd) || check_reg(rn) ||
 		  check_immr_imms(is64, immr, imms));
 }
+
 static void
 emit_lsl(struct a64_jit_ctx *ctx, bool is64, uint8_t rd, uint8_t imm)
 {
 	const unsigned int width = is64 ? 64 : 32;
 	uint8_t imms, immr;
 
+	imm &= width - 1;
 	immr = (width - imm) & (width - 1);
 	imms = width - 1 - imm;
 
@@ -560,13 +562,19 @@ emit_lsl(struct a64_jit_ctx *ctx, bool is64, uint8_t rd, uint8_t imm)
 static void
 emit_lsr(struct a64_jit_ctx *ctx, bool is64, uint8_t rd, uint8_t imm)
 {
-	emit_bitfield(ctx, is64, rd, rd, imm, is64 ? 63 : 31, A64_UBFM);
+	const unsigned int width = is64 ? 64 : 32;
+
+	imm &= width - 1;
+	emit_bitfield(ctx, is64, rd, rd, imm, width - 1, A64_UBFM);
 }
 
 static void
 emit_asr(struct a64_jit_ctx *ctx, bool is64, uint8_t rd, uint8_t imm)
 {
-	emit_bitfield(ctx, is64, rd, rd, imm, is64 ? 63 : 31, A64_SBFM);
+	const unsigned int width = is64 ? 64 : 32;
+
+	imm &= width - 1;
+	emit_bitfield(ctx, is64, rd, rd, imm, width - 1, A64_SBFM);
 }
 
 #define A64_AND 0
-- 
2.53.0


^ permalink raw reply related

* [PATCH v5 3/9] bpf: mask shift count in interpreter per RFC 9669
From: Stephen Hemminger @ 2026-06-24 17:55 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, stable, Konstantin Ananyev, Marat Khalili,
	Ferruh Yigit
In-Reply-To: <20260624175815.673064-1-stephen@networkplumber.org>

The interpreter shifted by the raw immediate or register value, which
is undefined behavior in C when the count is >= the operand width and
trips UBSan. RFC 9669 masks shift counts (0x3f for 64-bit, 0x1f for
32-bit); mask the count in the LSH/RSH/ARSH cases.

Fixes: 94972f35a02e ("bpf: add BPF loading and execution framework")
Cc: stable@dpdk.org

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 lib/bpf/bpf_exec.c | 31 +++++++++++++++++++++----------
 1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/lib/bpf/bpf_exec.c b/lib/bpf/bpf_exec.c
index d423ef28f5..bb03c9cc2c 100644
--- a/lib/bpf/bpf_exec.c
+++ b/lib/bpf/bpf_exec.c
@@ -4,6 +4,7 @@
 
 #include <stdio.h>
 #include <stdint.h>
+#include <limits.h>
 
 #include <eal_export.h>
 #include <rte_common.h>
@@ -43,6 +44,16 @@
 	((reg)[(ins)->dst_reg] = \
 		(type)(reg)[(ins)->dst_reg] op (type)(ins)->imm)
 
+#define BPF_OP_SHIFT_IMM(reg, ins, op, type)	\
+	((reg)[(ins)->dst_reg] =		\
+		(type)(reg)[(ins)->dst_reg] op	\
+		((ins)->imm & (sizeof(type) * CHAR_BIT - 1)))
+
+#define BPF_OP_SHIFT_REG(reg, ins, op, type)	\
+	((reg)[(ins)->dst_reg] =		\
+		(type)(reg)[(ins)->dst_reg] op	\
+		((reg)[(ins)->src_reg] & (sizeof(type) * CHAR_BIT - 1)))
+
 #define BPF_DIV_ZERO_CHECK(bpf, reg, ins, type) do { \
 	if ((type)(reg)[(ins)->src_reg] == 0) { \
 		RTE_BPF_LOG_LINE(ERR, \
@@ -183,10 +194,10 @@ bpf_exec(const struct rte_bpf *bpf, uint64_t reg[EBPF_REG_NUM])
 			BPF_OP_ALU_IMM(reg, ins, |, uint32_t);
 			break;
 		case (BPF_ALU | BPF_LSH | BPF_K):
-			BPF_OP_ALU_IMM(reg, ins, <<, uint32_t);
+			BPF_OP_SHIFT_IMM(reg, ins, <<, uint32_t);
 			break;
 		case (BPF_ALU | BPF_RSH | BPF_K):
-			BPF_OP_ALU_IMM(reg, ins, >>, uint32_t);
+			BPF_OP_SHIFT_IMM(reg, ins, >>, uint32_t);
 			break;
 		case (BPF_ALU | BPF_XOR | BPF_K):
 			BPF_OP_ALU_IMM(reg, ins, ^, uint32_t);
@@ -217,10 +228,10 @@ bpf_exec(const struct rte_bpf *bpf, uint64_t reg[EBPF_REG_NUM])
 			BPF_OP_ALU_REG(reg, ins, |, uint32_t);
 			break;
 		case (BPF_ALU | BPF_LSH | BPF_X):
-			BPF_OP_ALU_REG(reg, ins, <<, uint32_t);
+			BPF_OP_SHIFT_REG(reg, ins, <<, uint32_t);
 			break;
 		case (BPF_ALU | BPF_RSH | BPF_X):
-			BPF_OP_ALU_REG(reg, ins, >>, uint32_t);
+			BPF_OP_SHIFT_REG(reg, ins, >>, uint32_t);
 			break;
 		case (BPF_ALU | BPF_XOR | BPF_X):
 			BPF_OP_ALU_REG(reg, ins, ^, uint32_t);
@@ -262,13 +273,13 @@ bpf_exec(const struct rte_bpf *bpf, uint64_t reg[EBPF_REG_NUM])
 			BPF_OP_ALU_IMM(reg, ins, |, uint64_t);
 			break;
 		case (EBPF_ALU64 | BPF_LSH | BPF_K):
-			BPF_OP_ALU_IMM(reg, ins, <<, uint64_t);
+			BPF_OP_SHIFT_IMM(reg, ins, <<, uint64_t);
 			break;
 		case (EBPF_ALU64 | BPF_RSH | BPF_K):
-			BPF_OP_ALU_IMM(reg, ins, >>, uint64_t);
+			BPF_OP_SHIFT_IMM(reg, ins, >>, uint64_t);
 			break;
 		case (EBPF_ALU64 | EBPF_ARSH | BPF_K):
-			BPF_OP_ALU_IMM(reg, ins, >>, int64_t);
+			BPF_OP_SHIFT_IMM(reg, ins, >>, int64_t);
 			break;
 		case (EBPF_ALU64 | BPF_XOR | BPF_K):
 			BPF_OP_ALU_IMM(reg, ins, ^, uint64_t);
@@ -299,13 +310,13 @@ bpf_exec(const struct rte_bpf *bpf, uint64_t reg[EBPF_REG_NUM])
 			BPF_OP_ALU_REG(reg, ins, |, uint64_t);
 			break;
 		case (EBPF_ALU64 | BPF_LSH | BPF_X):
-			BPF_OP_ALU_REG(reg, ins, <<, uint64_t);
+			BPF_OP_SHIFT_REG(reg, ins, <<, uint64_t);
 			break;
 		case (EBPF_ALU64 | BPF_RSH | BPF_X):
-			BPF_OP_ALU_REG(reg, ins, >>, uint64_t);
+			BPF_OP_SHIFT_REG(reg, ins, >>, uint64_t);
 			break;
 		case (EBPF_ALU64 | EBPF_ARSH | BPF_X):
-			BPF_OP_ALU_REG(reg, ins, >>, int64_t);
+			BPF_OP_SHIFT_REG(reg, ins, >>, int64_t);
 			break;
 		case (EBPF_ALU64 | BPF_XOR | BPF_X):
 			BPF_OP_ALU_REG(reg, ins, ^, uint64_t);
-- 
2.53.0


^ permalink raw reply related

* [PATCH v5 2/9] test/bpf: add JSET test with small immediate
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>

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 v5 1/9] bpf/x86: fix JIT encoding of fixed-width immediates
From: Stephen Hemminger @ 2026-06-24 17:55 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, stable, Marat Khalili, Konstantin Ananyev,
	Ferruh Yigit
In-Reply-To: <20260624175815.673064-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 v5 0/9] bpf: JIT related bug fixes
From: Stephen Hemminger @ 2026-06-24 17:54 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 a pre-existing bug which would crash.
  2. The arm64 JIT was missing the packet-access instructions, found
     previously [1].
  3. Shift counts were not masked to the operand width as RFC 9669
     requires: undefined behavior in the interpreter and an encoding
     failure in the arm64 JIT.
  4. Tests related to JIT were not being run or were missing coverage.

Fixed all of these. Patches are ordered with the most urgent fix (the
x86 crash) first, each fix followed by the test that exercises it. The
arm64 packet-load support is kept ahead of the "check JIT was generated"
patch so the series bisects cleanly on arm64.

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

Changes since v4:
 - address Marat's review of the large-shift test: it now checks the
   RFC 9669 masked result instead of a sentinel, which surfaced two more
   bugs fixed in this version
 - mask shift counts to the operand width: the interpreter shifted by
   the raw count (UB, trips UBSan; patch 3) and the arm64 JIT overflowed
   the UBFM/SBFM immediate (patch 4)
 - reorder so the arm64 BPF_ABS/BPF_IND support precedes the "check JIT
   was generated" patch, keeping the suite bisectable on arm64

[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 (8):
  bpf/x86: fix JIT encoding of fixed-width immediates
  test/bpf: add JSET test with small immediate
  bpf: mask shift count in interpreter per RFC 9669
  bpf/arm64: mask shift count per RFC 9669
  test/bpf: add test for large shift
  bpf/arm64: add BPF_ABS/BPF_IND packet load support
  test/bpf: check that JIT was generated
  test/bpf: check that bpf_convert can be JIT'd

 app/test/test_bpf.c     | 320 +++++++++++++++++++++++++++++++---------
 lib/bpf/bpf_exec.c      |  31 ++--
 lib/bpf/bpf_jit_arm64.c | 165 ++++++++++++++++++++-
 lib/bpf/bpf_jit_x86.c   |   6 +-
 lib/bpf/meson.build     |   2 +
 5 files changed, 436 insertions(+), 88 deletions(-)

-- 
2.53.0


^ permalink raw reply

* Re: [PATCH v5 02/24] bpf: add format instruction function
From: Stephen Hemminger @ 2026-06-24 17:09 UTC (permalink / raw)
  To: Marat Khalili; +Cc: Konstantin Ananyev, dev
In-Reply-To: <20260624121800.40635-3-marat.khalili@huawei.com>

On Wed, 24 Jun 2026 13:17:35 +0100
Marat Khalili <marat.khalili@huawei.com> wrote:

> BPF library already contains BPF instruction formatting functions, but
> they could only be used via `rte_bpf_dump` to dump result into file. Add
> new function `rte_bpf_format` to format instruction in various way
> (hexadecimal, disassembly) into a user-provided buffer, as well as a
> service function `rte_bpf_insn_is_wide` to detect wide instructions.
> 
> Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
> Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
> ---

Is this format similar to what tcpdump -d and objdump produce?

^ permalink raw reply

* Re: [PATCH v2] dts: update dts check format script and resolve errors
From: Koushik Bhargav Nimoji @ 2026-06-24 16:47 UTC (permalink / raw)
  To: luca.vizzarro, patrickrobb1997; +Cc: dev, abailey, ahassick, lylavoie
In-Reply-To: <20260618204525.1010218-1-knimoji@iol.unh.edu>

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

Recheck-request: github-robot: build

On Thu, Jun 18, 2026 at 4:45 PM Koushik Bhargav Nimoji <knimoji@iol.unh.edu>
wrote:

> This patch updates the tool versions used in the dts-check-format.sh
> script; by doing so, formatting and type hinting errors that weren't
> previously visible have now appeared. This patch also resolves those
> new formatting and type hinting errors.
>
> Signed-off-by: Koushik Bhargav Nimoji <knimoji@iol.unh.edu>
> ---
> v2:
>     *Fixed type hinting issues stemming from ParamSpec
>      issues.
>     *Fixed tarball write type parameter for copying
>      directories from one node to another.
> ---
>  dts/api/packet.py                             |  15 +-
>  dts/api/testpmd/__init__.py                   |  12 +-
>  dts/api/testpmd/types.py                      |   6 +-
>  dts/framework/config/__init__.py              |  24 +-
>  dts/framework/config/test_run.py              |  12 +-
>  dts/framework/context.py                      |   8 +-
>  .../interactive_remote_session.py             |   2 +-
>  .../remote_session/interactive_shell.py       |   6 +-
>  .../remote_session/remote_session.py          |   4 +-
>  dts/framework/remote_session/shell_pool.py    |   2 +-
>  dts/framework/settings.py                     |   8 +-
>  dts/framework/testbed_model/cpu.py            |   2 +-
>  .../testbed_model/traffic_generator/scapy.py  |   6 +-
>  .../testbed_model/traffic_generator/trex.py   |   6 +-
>  dts/framework/utils.py                        |  13 +-
>  dts/poetry.lock                               | 368 ++++++++++++++----
>  dts/pyproject.toml                            |   8 +-
>  dts/tests/TestSuite_cryptodev_throughput.py   |   4 +-
>  dts/tests/TestSuite_port_control.py           |   3 +-
>  .../TestSuite_single_core_forward_perf.py     |   2 +-
>  20 files changed, 372 insertions(+), 139 deletions(-)
>
> diff --git a/dts/api/packet.py b/dts/api/packet.py
> index 094a1b7a9d..3dda18e781 100644
> --- a/dts/api/packet.py
> +++ b/dts/api/packet.py
> @@ -87,9 +87,9 @@ def send_packets_and_capture(
>          CapturingTrafficGenerator,
>      )
>
> -    assert isinstance(
> -        get_ctx().func_tg, CapturingTrafficGenerator
> -    ), "Cannot capture with a non-capturing traffic generator"
> +    assert isinstance(get_ctx().func_tg, CapturingTrafficGenerator), (
> +        "Cannot capture with a non-capturing traffic generator"
> +    )
>      tg: CapturingTrafficGenerator = cast(CapturingTrafficGenerator,
> get_ctx().func_tg)
>      # TODO: implement @requires for types of traffic generator
>      packets = adjust_addresses(packets)
> @@ -308,8 +308,7 @@ def _verify_l2_frame(received_packet: Ether,
> contains_l3: bool) -> bool:
>      if contains_l3:
>          expected_src_mac = get_ctx().topology.sut_port_egress.mac_address
>      log_debug(
> -        f"Comparing received src mac '{received_packet.src}' "
> -        f"with expected '{expected_src_mac}'."
> +        f"Comparing received src mac '{received_packet.src}' with
> expected '{expected_src_mac}'."
>      )
>      if received_packet.src != expected_src_mac:
>          return False
> @@ -344,9 +343,9 @@ def assess_performance_by_packet(
>          PerformanceTrafficGenerator,
>      )
>
> -    assert isinstance(
> -        get_ctx().perf_tg, PerformanceTrafficGenerator
> -    ), "Cannot send performance traffic with non-performance traffic
> generator"
> +    assert isinstance(get_ctx().perf_tg, PerformanceTrafficGenerator), (
> +        "Cannot send performance traffic with non-performance traffic
> generator"
> +    )
>      tg: PerformanceTrafficGenerator = cast(PerformanceTrafficGenerator,
> get_ctx().perf_tg)
>      # TODO: implement @requires for types of traffic generator
>      return tg.calculate_traffic_and_stats(packet, duration, send_mpps)
> diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
> index e9187440bb..f4767c6aeb 100644
> --- a/dts/api/testpmd/__init__.py
> +++ b/dts/api/testpmd/__init__.py
> @@ -76,7 +76,9 @@ def _requires_stopped_ports(func: TestPmdMethod) ->
> TestPmdMethod:
>      """
>
>      @functools.wraps(func)
> -    def _wrapper(self: "TestPmd", *args: P.args, **kwargs: P.kwargs) ->
> Any:
> +    def _wrapper(
> +        self: "TestPmd", *args: P.args, **kwargs: P.kwargs
> +    ) -> Callable[Concatenate["TestPmd", P], Any]:
>          if self.ports_started:
>              self._logger.debug("Ports need to be stopped to continue.")
>              self.stop_all_ports()
> @@ -100,7 +102,9 @@ def _requires_started_ports(func: TestPmdMethod) ->
> TestPmdMethod:
>      """
>
>      @functools.wraps(func)
> -    def _wrapper(self: "TestPmd", *args: P.args, **kwargs: P.kwargs) ->
> Any:
> +    def _wrapper(
> +        self: "TestPmd", *args: P.args, **kwargs: P.kwargs
> +    ) -> Callable[Concatenate["TestPmd", P], Any]:
>          if not self.ports_started:
>              self._logger.debug("Ports need to be started to continue.")
>              self.start_all_ports()
> @@ -128,7 +132,9 @@ def _add_remove_mtu(mtu: int = 1500) ->
> Callable[[TestPmdMethod], TestPmdMethod]
>
>      def decorator(func: TestPmdMethod) -> TestPmdMethod:
>          @functools.wraps(func)
> -        def wrapper(self: "TestPmd", *args: P.args, **kwargs: P.kwargs)
> -> Any:
> +        def wrapper(
> +            self: "TestPmd", *args: P.args, **kwargs: P.kwargs
> +        ) -> Callable[Concatenate["TestPmd", P], Any]:
>              original_mtu = self.ports[0].mtu
>              self.set_port_mtu_all(mtu=mtu, verify=False)
>              retval = func(self, *args, **kwargs)
> diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py
> index 0d322aece2..af3263682e 100644
> --- a/dts/api/testpmd/types.py
> +++ b/dts/api/testpmd/types.py
> @@ -279,7 +279,7 @@ def from_list_string(cls, names: str) -> Self:
>          Returns:
>              An instance of this flag.
>          """
> -        flag = cls(0)
> +        flag: RSSOffloadTypesFlag = cls(0)
>          for name in names.split():
>              flag |= cls.from_str(name)
>          return flag
> @@ -960,7 +960,7 @@ def from_list_string(cls, names: str) -> Self:
>          Returns:
>              An instance of this flag.
>          """
> -        flag = cls(0)
> +        flag: PacketOffloadFlag = cls(0)
>          for name in names.split():
>              flag |= cls.from_str(name)
>          return flag
> @@ -1168,7 +1168,7 @@ def from_list_string(cls, names: str) -> Self:
>          Returns:
>              An instance of this flag.
>          """
> -        flag = cls(0)
> +        flag: RtePTypes = cls(0)
>          for name in names.split():
>              flag |= cls.from_str(name)
>          return flag
> diff --git a/dts/framework/config/__init__.py
> b/dts/framework/config/__init__.py
> index d2f0138e4a..a8861894b7 100644
> --- a/dts/framework/config/__init__.py
> +++ b/dts/framework/config/__init__.py
> @@ -85,9 +85,9 @@ def validate_port_links(self) -> Self:
>              sut_node_port_peer = existing_port_links.get(
>                  (self.test_run.system_under_test_node, link.sut_port),
> None
>              )
> -            assert (
> -                sut_node_port_peer is not None
> -            ), f"Invalid SUT node port specified for link
> port_topology.{link_idx}."
> +            assert sut_node_port_peer is not None, (
> +                f"Invalid SUT node port specified for link
> port_topology.{link_idx}."
> +            )
>
>              assert sut_node_port_peer is False or sut_node_port_peer ==
> link.right, (
>                  f"The SUT node port for link port_topology.{link_idx} is "
> @@ -97,9 +97,9 @@ def validate_port_links(self) -> Self:
>              tg_node_port_peer = existing_port_links.get(
>                  (self.test_run.traffic_generator_node, link.tg_port), None
>              )
> -            assert (
> -                tg_node_port_peer is not None
> -            ), f"Invalid TG node port specified for link
> port_topology.{link_idx}."
> +            assert tg_node_port_peer is not None, (
> +                f"Invalid TG node port specified for link
> port_topology.{link_idx}."
> +            )
>
>              assert tg_node_port_peer is False or sut_node_port_peer ==
> link.left, (
>                  f"The TG node port for link port_topology.{link_idx} is "
> @@ -117,16 +117,16 @@ def validate_test_run_against_nodes(self) -> Self:
>          sut_node_name = self.test_run.system_under_test_node
>          sut_node = next((n for n in self.nodes if n.name ==
> sut_node_name), None)
>
> -        assert (
> -            sut_node is not None
> -        ), f"The system_under_test_node {sut_node_name} is not a valid
> node name."
> +        assert sut_node is not None, (
> +            f"The system_under_test_node {sut_node_name} is not a valid
> node name."
> +        )
>
>          tg_node_name = self.test_run.traffic_generator_node
>          tg_node = next((n for n in self.nodes if n.name ==
> tg_node_name), None)
>
> -        assert (
> -            tg_node is not None
> -        ), f"The traffic_generator_name {tg_node_name} is not a valid
> node name."
> +        assert tg_node is not None, (
> +            f"The traffic_generator_name {tg_node_name} is not a valid
> node name."
> +        )
>
>          return self
>
> diff --git a/dts/framework/config/test_run.py
> b/dts/framework/config/test_run.py
> index 76e24d1785..3cd643981d 100644
> --- a/dts/framework/config/test_run.py
> +++ b/dts/framework/config/test_run.py
> @@ -233,9 +233,9 @@ def test_suite_spec(self) -> "TestSuiteSpec":
>          from framework.test_suite import find_by_name
>
>          test_suite_spec = find_by_name(self.test_suite_name)
> -        assert (
> -            test_suite_spec is not None
> -        ), f"{self.test_suite_name} is not a valid test suite module
> name."
> +        assert test_suite_spec is not None, (
> +            f"{self.test_suite_name} is not a valid test suite module
> name."
> +        )
>          return test_suite_spec
>
>      @cached_property
> @@ -384,9 +384,9 @@ def convert_from_string(cls, data: Any) -> Any:
>      @model_validator(mode="after")
>      def verify_distinct_nodes(self) -> Self:
>          """Verify that each side of the link has distinct nodes."""
> -        assert (
> -            self.left.node_type != self.right.node_type
> -        ), "Linking ports of the same node is unsupported."
> +        assert self.left.node_type != self.right.node_type, (
> +            "Linking ports of the same node is unsupported."
> +        )
>          return self
>
>
> diff --git a/dts/framework/context.py b/dts/framework/context.py
> index 8f1021dc96..efe9af0645 100644
> --- a/dts/framework/context.py
> +++ b/dts/framework/context.py
> @@ -60,9 +60,9 @@ def reset(self) -> None:
>                  else _field.default
>              )
>
> -            assert (
> -                default is not MISSING
> -            ), "{LocalContext.__name__} must have defaults on all fields!"
> +            assert default is not MISSING, (
> +                "{LocalContext.__name__} must have defaults on all
> fields!"
> +            )
>
>              setattr(self, _field.name, default)
>
> @@ -108,7 +108,7 @@ def filter_cores(
>  ) -> Callable[[type["TestProtocol"]], Callable]:
>      """Decorates functions that require a temporary update to the lcore
> specifier."""
>
> -    def decorator(func: type["TestProtocol"]) -> Callable:
> +    def decorator(func: type["TestProtocol"]) -> Callable[P,
> type["TestProtocol"]]:
>          @functools.wraps(func)
>          def wrapper(*args: P.args, **kwargs: P.kwargs) -> Any:
>              local_ctx = get_ctx().local
> diff --git a/dts/framework/remote_session/interactive_remote_session.py
> b/dts/framework/remote_session/interactive_remote_session.py
> index c8156b4345..fc42e862bc 100644
> --- a/dts/framework/remote_session/interactive_remote_session.py
> +++ b/dts/framework/remote_session/interactive_remote_session.py
> @@ -109,7 +109,7 @@ def _connect(self) -> None:
>                  self._logger.debug(traceback.format_exc())
>                  self._logger.warning(e)
>                  self._logger.info(
> -                    f"Retrying interactive session connection: retry
> number {retry_attempt +1}"
> +                    f"Retrying interactive session connection: retry
> number {retry_attempt + 1}"
>                  )
>              else:
>                  break
> diff --git a/dts/framework/remote_session/interactive_shell.py
> b/dts/framework/remote_session/interactive_shell.py
> index a65cbce209..6bba58a4f6 100644
> --- a/dts/framework/remote_session/interactive_shell.py
> +++ b/dts/framework/remote_session/interactive_shell.py
> @@ -50,7 +50,9 @@
>  def only_active(func: InteractiveShellMethod) -> InteractiveShellMethod:
>      """This decorator will skip running the method if the SSH channel is
> not active."""
>
> -    def _wrapper(self: "InteractiveShell", *args: P.args, **kwargs:
> P.kwargs) -> R | None:
> +    def _wrapper(
> +        self: "InteractiveShell", *args: P.args, **kwargs: P.kwargs
> +    ) -> Callable[P, "InteractiveShell"] | None:
>          if self._ssh_channel.active:
>              return func(self, *args, **kwargs)
>          return None
> @@ -167,7 +169,7 @@ def start_application(self, prompt: str | None = None,
> add_to_shell_pool: bool =
>                  break
>              except InteractiveSSHTimeoutError:
>                  self._logger.info(
> -                    f"Interactive shell failed to start (attempt
> {attempt+1} out of "
> +                    f"Interactive shell failed to start (attempt {attempt
> + 1} out of "
>                      f"{self._init_attempts})"
>                  )
>          else:
> diff --git a/dts/framework/remote_session/remote_session.py
> b/dts/framework/remote_session/remote_session.py
> index 158325bb7f..fb5f6fedf5 100644
> --- a/dts/framework/remote_session/remote_session.py
> +++ b/dts/framework/remote_session/remote_session.py
> @@ -72,9 +72,7 @@ def __post_init__(self, init_stdout: str, init_stderr:
> str) -> None:
>      def __str__(self) -> str:
>          """Format the command outputs."""
>          return (
> -            f"stdout: '{self.stdout}'\n"
> -            f"stderr: '{self.stderr}'\n"
> -            f"return_code: '{self.return_code}'"
> +            f"stdout: '{self.stdout}'\nstderr:
> '{self.stderr}'\nreturn_code: '{self.return_code}'"
>          )
>
>
> diff --git a/dts/framework/remote_session/shell_pool.py
> b/dts/framework/remote_session/shell_pool.py
> index 241737eab3..710107c6cb 100644
> --- a/dts/framework/remote_session/shell_pool.py
> +++ b/dts/framework/remote_session/shell_pool.py
> @@ -74,7 +74,7 @@ def unregister_shell(self, shell: "InteractiveShell") ->
> None:
>
>      def start_new_pool(self) -> None:
>          """Start a new shell pool."""
> -        self._logger.debug(f"Starting new shell pool and advancing to
> level {self.pool_level+1}.")
> +        self._logger.debug(f"Starting new shell pool and advancing to
> level {self.pool_level + 1}.")
>          self._pools.append(set())
>
>      def terminate_current_pool(self) -> None:
> diff --git a/dts/framework/settings.py b/dts/framework/settings.py
> index b08373b7ea..f329677804 100644
> --- a/dts/framework/settings.py
> +++ b/dts/framework/settings.py
> @@ -249,9 +249,9 @@ def error(self, message) -> NoReturn:
>              if _is_from_env(action):
>                  action_name = _get_action_name(action)
>                  env_var_name = _get_env_var_name(action)
> -                assert (
> -                    env_var_name is not None
> -                ), "Action was set from environment, but no environment
> variable name was found."
> +                assert env_var_name is not None, (
> +                    "Action was set from environment, but no environment
> variable name was found."
> +                )
>                  env_var_value = os.environ.get(env_var_name)
>
>                  message = message.replace(
> @@ -260,7 +260,7 @@ def error(self, message) -> NoReturn:
>                  )
>
>          print(f"{self.prog}: error: {message}\n", file=sys.stderr)
> -        self.exit(2, "For help and usage, " "run the command with the
> --help flag.\n")
> +        self.exit(2, "For help and usage, run the command with the --help
> flag.\n")
>
>
>  class _EnvVarHelpFormatter(ArgumentDefaultsHelpFormatter):
> diff --git a/dts/framework/testbed_model/cpu.py
> b/dts/framework/testbed_model/cpu.py
> index 6e2ecca080..a9471709dd 100644
> --- a/dts/framework/testbed_model/cpu.py
> +++ b/dts/framework/testbed_model/cpu.py
> @@ -105,7 +105,7 @@ def __init__(self, lcore_list: list[int] | list[str] |
> list[LogicalCore] | str)
>
>          # the input lcores may not be sorted
>          self._lcore_list.sort()
> -        self._lcore_str =
> f'{",".join(self._get_consecutive_lcores_range(self._lcore_list))}'
> +        self._lcore_str =
> f"{','.join(self._get_consecutive_lcores_range(self._lcore_list))}"
>
>      @property
>      def lcore_list(self) -> list[int]:
> diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py
> b/dts/framework/testbed_model/traffic_generator/scapy.py
> index c6e9006205..62853a34e4 100644
> --- a/dts/framework/testbed_model/traffic_generator/scapy.py
> +++ b/dts/framework/testbed_model/traffic_generator/scapy.py
> @@ -314,9 +314,9 @@ def __init__(self, tg_node: Node, config:
> ScapyTrafficGeneratorConfig, **kwargs:
>              kwargs: Additional keyword arguments. Supported arguments
> correspond to the parameters
>                  of :meth:`PythonShell.__init__` in this case.
>          """
> -        assert (
> -            tg_node.config.os == OS.linux
> -        ), "Linux is the only supported OS for scapy traffic generation"
> +        assert tg_node.config.os == OS.linux, (
> +            "Linux is the only supported OS for scapy traffic generation"
> +        )
>
>          super().__init__(tg_node=tg_node, config=config, **kwargs)
>
> diff --git a/dts/framework/testbed_model/traffic_generator/trex.py
> b/dts/framework/testbed_model/traffic_generator/trex.py
> index 2abfbfc5ab..d53791b0a6 100644
> --- a/dts/framework/testbed_model/traffic_generator/trex.py
> +++ b/dts/framework/testbed_model/traffic_generator/trex.py
> @@ -95,9 +95,9 @@ def __init__(self, tg_node: Node, config:
> TrexTrafficGeneratorConfig) -> None:
>              tg_node: TG node the TRex instance is operating on.
>              config: Traffic generator config provided for TRex instance.
>          """
> -        assert (
> -            tg_node.config.os == OS.linux
> -        ), "Linux is the only supported OS for trex traffic generation"
> +        assert tg_node.config.os == OS.linux, (
> +            "Linux is the only supported OS for trex traffic generation"
> +        )
>
>          super().__init__(tg_node=tg_node, config=config)
>          self._tg_node_config = tg_node.config
> diff --git a/dts/framework/utils.py b/dts/framework/utils.py
> index 9917ffbfaa..5753c1b7fe 100644
> --- a/dts/framework/utils.py
> +++ b/dts/framework/utils.py
> @@ -21,7 +21,7 @@
>  import tarfile
>  from enum import Enum, Flag
>  from pathlib import Path
> -from typing import Any, Callable
> +from typing import Any, Callable, Literal, TypeAlias, cast
>
>  from scapy.layers.inet import IP, TCP, UDP, Ether
>  from scapy.packet import Packet
> @@ -39,6 +39,8 @@
>      rf"(?:(sut|tg)\.)?({REGEX_FOR_IDENTIFIER})"  # right side
>  )
>
> +Tar_modes: TypeAlias = Literal["w:gz", "w:bz2", "w:xz", "w:tar"]
> +
>
>  def expand_range(range_str: str) -> list[int]:
>      """Process `range_str` into a list of integers.
> @@ -154,7 +156,11 @@ def extension(self) -> str:
>          For other compression formats, the extension will be in the format
>          'tar.{compression format}'.
>          """
> -        return f"{self.value}" if self == self.none else
> f"{type(self).none.value}.{self.value}"
> +        return (
> +            f"{self.value}"
> +            if self == self.none
> +            else f"{TarCompressionFormat.none.value}.{self.value}"
> +        )
>
>
>  def convert_to_list_of_string(value: Any | list[Any]) -> list[str]:
> @@ -207,7 +213,8 @@ def filter_func(tarinfo: tarfile.TarInfo) ->
> tarfile.TarInfo | None:
>          return None
>
>      target_tarball_path =
> dir_path.with_suffix(f".{compress_format.extension}")
> -    with tarfile.open(target_tarball_path, f"w:{compress_format.value}")
> as tar:
> +    tarball_mode = cast(Tar_modes, f"w:{compress_format.value}")
> +    with tarfile.open(target_tarball_path, tarball_mode) as tar:
>          tar.add(dir_path, arcname=dir_path.name,
> filter=create_filter_function(exclude))
>
>      return target_tarball_path
> diff --git a/dts/poetry.lock b/dts/poetry.lock
> index 0ad5d32b85..eda7cc2fec 100644
> --- a/dts/poetry.lock
> +++ b/dts/poetry.lock
> @@ -1,4 +1,4 @@
> -# This file is automatically @generated by Poetry 1.8.3 and should not be
> changed by hand.
> +# This file is automatically @generated by Poetry 2.3.2 and should not be
> changed by hand.
>
>  [[package]]
>  name = "aenum"
> @@ -6,6 +6,7 @@ version = "3.1.15"
>  description = "Advanced Enumerations (compatible with Python's stdlib
> Enum), NamedTuples, and NamedConstants"
>  optional = false
>  python-versions = "*"
> +groups = ["main"]
>  files = [
>      {file = "aenum-3.1.15-py2-none-any.whl", hash =
> "sha256:27b1710b9d084de6e2e695dab78fe9f269de924b51ae2850170ee7e1ca6288a5"},
>      {file = "aenum-3.1.15-py3-none-any.whl", hash =
> "sha256:e0dfaeea4c2bd362144b87377e2c61d91958c5ed0b4daf89cb6f45ae23af6288"},
> @@ -18,17 +19,62 @@ version = "0.7.0"
>  description = "Reusable constraint types to use with typing.Annotated"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main"]
>  files = [
>      {file = "annotated_types-0.7.0-py3-none-any.whl", hash =
> "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
>      {file = "annotated_types-0.7.0.tar.gz", hash =
> "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
>  ]
>
> +[[package]]
> +name = "ast-serialize"
> +version = "0.5.0"
> +description = "Python bindings for mypy AST serialization"
> +optional = false
> +python-versions = ">=3.7"
> +groups = ["dev"]
> +files = [
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-macosx_10_12_x86_64.whl",
> hash =
> "sha256:8f5c14f169eb0972c0c21bada5358b23d6047c76583b005234f865b11f1fa00a"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-macosx_11_0_arm64.whl",
> hash =
> "sha256:7d1a2de9de5be04652f0ed60738356ef94f66db37924a9499fffe98dc491aa0b"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
> hash =
> "sha256:be5173fb66f9b49026d9d5a2ff0fc7c7009077107c0eb285b2d60fdf1fe10bd1"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
> hash =
> "sha256:f8015cd071ac1339924ee2b8098c93e00e155f30a16f40ec9816fcf84f4753f6"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
> hash =
> "sha256:5499e8797edff2a9186aa313ed382c6b422e798e9332d9953badcee6e69a88f2"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl",
> hash =
> "sha256:6848f2a093fb5548751a9a09bff8fcd229e2bbeb0e3331f391b6ae6d26cd9903"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
> hash =
> "sha256:832d4c998e0b091fd60a6d6bceee535483c4d490de9ba85003af835225719261"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash =
> "sha256:16db7c62ec0b8efe1d7afd283a388d8f74f2605d56032e5a37747d2de8dba027"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl",
> hash =
> "sha256:baf5eb061eb5bccade4128ad42da33787d72f6013809cd1b590376ece8b3c937"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl",
> hash =
> "sha256:104e4a35bd7c124173c41760ef9aaea17ddb3f86c65cb643671d59afbe3ee94c"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl",
> hash =
> "sha256:36be371028fc1675acb38a331bde160dbab7ff907fdf00b67eb6911aa106951b"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_i686.whl",
> hash =
> "sha256:061ee58bdb52341c8201a6df41182a977736bae3b7ded87ca7176ca25a8a47ab"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl",
> hash =
> "sha256:b15219e9cdc9f53f6f4cb51c009203507228226148c05c5e8fe451c28b435eb3"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-win32.whl", hash =
> "sha256:842d1c004bb466c7df036f95fabef789570541922b10976b12f5592a69cf0b38"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-win_amd64.whl", hash =
> "sha256:b0c06d760909b095cc466356dfccd05a1c7233a6ca191c020dca2c6a6f16c24c"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-win_arm64.whl", hash =
> "sha256:787baedb0262cc49e8ce37cc15c00ae818e46a165a3b36f5e21ed174998104cb"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash
> =
> "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash =
> "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a"},
> +    {file =
> "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
> hash =
> "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211"},
> +    {file =
> "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
> hash =
> "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf"},
> +    {file =
> "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
> hash =
> "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9"},
> +    {file =
> "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl",
> hash =
> "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee"},
> +    {file =
> "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
> hash =
> "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl",
> hash =
> "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43"},
> +    {file =
> "ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl",
> hash =
> "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl",
> hash =
> "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl",
> hash =
> "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash
> =
> "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl",
> hash =
> "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-win32.whl", hash =
> "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash =
> "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash =
> "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642"},
> +    {file = "ast_serialize-0.5.0.tar.gz", hash =
> "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6"},
> +]
> +
>  [[package]]
>  name = "bcrypt"
>  version = "4.2.1"
>  description = "Modern password hashing for your software and your servers"
>  optional = false
>  python-versions = ">=3.7"
> +groups = ["main"]
>  files = [
>      {file = "bcrypt-4.2.1-cp37-abi3-macosx_10_12_universal2.whl", hash =
> "sha256:1340411a0894b7d3ef562fb233e4b6ed58add185228650942bdc885362f32c17"},
>      {file =
> "bcrypt-4.2.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
> hash =
> "sha256:b1ee315739bc8387aa36ff127afc99120ee452924e0df517a8f3e4c0187a0f5f"},
> @@ -67,6 +113,7 @@ version = "1.17.1"
>  description = "Foreign Function Interface for Python calling C code."
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main", "dev"]
>  files = [
>      {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash =
> "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
>      {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash =
> "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
> @@ -136,6 +183,7 @@ files = [
>      {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash =
> "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
>      {file = "cffi-1.17.1.tar.gz", hash =
> "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
>  ]
> +markers = {dev = "platform_python_implementation != \"PyPy\""}
>
>  [package.dependencies]
>  pycparser = "*"
> @@ -146,6 +194,7 @@ version = "44.0.0"
>  description = "cryptography is a package which provides cryptographic
> recipes and primitives to Python developers."
>  optional = false
>  python-versions = "!=3.9.0,!=3.9.1,>=3.7"
> +groups = ["main", "dev"]
>  files = [
>      {file = "cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl",
> hash =
> "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123"},
>      {file =
> "cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
> hash =
> "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092"},
> @@ -180,10 +229,10 @@ files = [
>  cffi = {version = ">=1.12", markers = "platform_python_implementation !=
> \"PyPy\""}
>
>  [package.extras]
> -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"]
> +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0) ; python_version
> >= \"3.8\""]
>  docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)",
> "sphinxcontrib-spelling (>=7.3.1)"]
> -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"]
> -pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff
> (>=0.3.6)"]
> +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_version >=
> \"3.8\""]
> +pep8test = ["check-sdist ; python_version >= \"3.8\"", "click (>=8.0.1)",
> "mypy (>=1.4)", "ruff (>=0.3.6)"]
>  sdist = ["build (>=1.0.0)"]
>  ssh = ["bcrypt (>=3.1.5)"]
>  test = ["certifi (>=2024)", "cryptography-vectors (==44.0.0)", "pretend
> (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov
> (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
> @@ -195,6 +244,7 @@ version = "5.1.1"
>  description = "Decorators for Humans"
>  optional = false
>  python-versions = ">=3.5"
> +groups = ["main"]
>  files = [
>      {file = "decorator-5.1.1-py3-none-any.whl", hash =
> "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
>      {file = "decorator-5.1.1.tar.gz", hash =
> "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
> @@ -206,6 +256,7 @@ version = "1.2.18"
>  description = "Python @deprecated decorator to deprecate old python
> classes, functions or methods."
>  optional = false
>  python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
> +groups = ["main"]
>  files = [
>      {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash =
> "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"},
>      {file = "deprecated-1.2.18.tar.gz", hash =
> "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"},
> @@ -215,7 +266,7 @@ files = [
>  wrapt = ">=1.10,<2"
>
>  [package.extras]
> -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"]
> +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ;
> python_version >= \"3.12\"", "tox"]
>
>  [[package]]
>  name = "fabric"
> @@ -223,6 +274,7 @@ version = "3.2.2"
>  description = "High level SSH command execution"
>  optional = false
>  python-versions = "*"
> +groups = ["main"]
>  files = [
>      {file = "fabric-3.2.2-py3-none-any.whl", hash =
> "sha256:91c47c0be68b14936c88b34da8a1f55e5710fd28397dac5d4ff2e21558113a6f"},
>      {file = "fabric-3.2.2.tar.gz", hash =
> "sha256:8783ca42e3b0076f08b26901aac6b9d9b1f19c410074e7accfab902c184ff4a3"},
> @@ -243,56 +295,177 @@ version = "2.2.0"
>  description = "Pythonic task execution"
>  optional = false
>  python-versions = ">=3.6"
> +groups = ["main"]
>  files = [
>      {file = "invoke-2.2.0-py3-none-any.whl", hash =
> "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"},
>      {file = "invoke-2.2.0.tar.gz", hash =
> "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"},
>  ]
>
> +[[package]]
> +name = "librt"
> +version = "0.11.0"
> +description = "Mypyc runtime library"
> +optional = false
> +python-versions = ">=3.9"
> +groups = ["dev"]
> +markers = "platform_python_implementation != \"PyPy\""
> +files = [
> +    {file = "librt-0.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash =
> "sha256:6e94ebfcfa2d5e9926d6c3b9aa4617ffc42a845b4321fb84021b872358c82a0f"},
> +    {file = "librt-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash =
> "sha256:ae627397a2f351560440d872d6f7c8dbb4072e57868e7b2fc5b8b430fe489d45"},
> +    {file =
> "librt-0.11.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:dc329359321b67d24efdf4bc69012b0597001649544db662c001db5a0184794c"},
> +    {file =
> "librt-0.11.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:7e82e642ab0f7608ce2fe53d76ca2280a9ee33a1b06556142c7c6fe80a86fc33"},
> +    {file =
> "librt-0.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:88145c15c67731d54283d135b03244028c750cc9edc334a96a4f5950ebdb2884"},
> +    {file =
> "librt-0.11.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:9d36a51b3d93320b686588e27123f4995804dbf1bce81df78c02fc3c6eea9280"},
> +    {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash =
> "sha256:d00f3ac06a2a8b246327f11e186a53a100a4d5c7ed52346367e5ec751d51586c"},
> +    {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash =
> "sha256:461bbceede621f1ffb8839755f8663e886087ee7af16294cab7fb4d782c62eeb"},
> +    {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash =
> "sha256:0cad8a4d6a8ff03c9b76f9414caccd78e7cfbc8a2e12fa334d8e1d9932753783"},
> +    {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash =
> "sha256:f37aa505b3cf60701562eddb32df74b12a9e380c207fd8b06dd157a943ac7ea0"},
> +    {file = "librt-0.11.0-cp310-cp310-win32.whl", hash =
> "sha256:94663a21534637f0e787ec2a2a756022df6e5b7b2335a5cdd7d8e33d68a2af89"},
> +    {file = "librt-0.11.0-cp310-cp310-win_amd64.whl", hash =
> "sha256:dec7db73758c2b54953fd8b7fe348c45188fe26b39ee18446196edd08453a5d4"},
> +    {file = "librt-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash =
> "sha256:93d95bd45b7d58343d8b90d904450a545144eec19a002511163426f8ab1fae29"},
> +    {file = "librt-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash =
> "sha256:4ee278c769a713638cdacd4c0436d72156e75df3ebc0166ab2b9dc43acc386c9"},
> +    {file =
> "librt-0.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:f230cb1cbc9faaa616f9a678f530ebcf186e414b6bcbd88b960e4ba1b92428d5"},
> +    {file =
> "librt-0.11.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:5d63c855d86938d9de93e265c9bd8c705b51ec494de5738340ee93767a686e4b"},
> +    {file =
> "librt-0.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:993f028be9e96a08d31df3479ac80d99be374d17f3b78e4796b3fd3c913d4e89"},
> +    {file =
> "librt-0.11.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:258d73a0aa66a055e65b2e4d1b8cdb23b9d132c5bb915d9547d804fcaed116cc"},
> +    {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash =
> "sha256:0827efe7854718f04aaddf6496e96960a956e676fe1d0f04eb41511fd8ad06d5"},
> +    {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash =
> "sha256:7753e57d6e12d019c0d8786f1c09c709f4c3fcc57c3887b24e36e6c06ec938b7"},
> +    {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash =
> "sha256:11bd19822431cc21af9f27374e7ae2e58103c7d98bda823536a6c47f6bb2bb3d"},
> +    {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash =
> "sha256:22bdf239b219d3993761a148ffa134b19e52e9989c84f845d5d7b71d70a17412"},
> +    {file = "librt-0.11.0-cp311-cp311-win32.whl", hash =
> "sha256:46c60b61e308eb535fbd6fa622b1ee1bb2815691c1ad9c98bf7b84952ec3bc8d"},
> +    {file = "librt-0.11.0-cp311-cp311-win_amd64.whl", hash =
> "sha256:902e546ff044f579ff1c953ff5fce97b636fe9e3943996b2177710c6ef076f73"},
> +    {file = "librt-0.11.0-cp311-cp311-win_arm64.whl", hash =
> "sha256:65ac3bc20f78aa0ee5ae84baa68917f89fef4af63e941084dd019a0d0e749f0c"},
> +    {file = "librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash =
> "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46"},
> +    {file = "librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash =
> "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3"},
> +    {file =
> "librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67"},
> +    {file =
> "librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a"},
> +    {file =
> "librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a"},
> +    {file =
> "librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f"},
> +    {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash =
> "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b"},
> +    {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash =
> "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766"},
> +    {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash =
> "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d"},
> +    {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash =
> "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8"},
> +    {file = "librt-0.11.0-cp312-cp312-win32.whl", hash =
> "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a"},
> +    {file = "librt-0.11.0-cp312-cp312-win_amd64.whl", hash =
> "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9"},
> +    {file = "librt-0.11.0-cp312-cp312-win_arm64.whl", hash =
> "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c"},
> +    {file = "librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash =
> "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894"},
> +    {file = "librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash =
> "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c"},
> +    {file =
> "librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea"},
> +    {file =
> "librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230"},
> +    {file =
> "librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2"},
> +    {file =
> "librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3"},
> +    {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash =
> "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21"},
> +    {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash =
> "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930"},
> +    {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash =
> "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be"},
> +    {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash =
> "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e"},
> +    {file = "librt-0.11.0-cp313-cp313-win32.whl", hash =
> "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e"},
> +    {file = "librt-0.11.0-cp313-cp313-win_amd64.whl", hash =
> "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47"},
> +    {file = "librt-0.11.0-cp313-cp313-win_arm64.whl", hash =
> "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44"},
> +    {file = "librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash =
> "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd"},
> +    {file = "librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash =
> "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4"},
> +    {file =
> "librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8"},
> +    {file =
> "librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b"},
> +    {file =
> "librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175"},
> +    {file =
> "librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03"},
> +    {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash =
> "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c"},
> +    {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash =
> "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3"},
> +    {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash =
> "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96"},
> +    {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash =
> "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe"},
> +    {file = "librt-0.11.0-cp314-cp314-win32.whl", hash =
> "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f"},
> +    {file = "librt-0.11.0-cp314-cp314-win_amd64.whl", hash =
> "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7"},
> +    {file = "librt-0.11.0-cp314-cp314-win_arm64.whl", hash =
> "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1"},
> +    {file = "librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash =
> "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72"},
> +    {file = "librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash =
> "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa"},
> +    {file =
> "librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548"},
> +    {file =
> "librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2"},
> +    {file =
> "librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f"},
> +    {file =
> "librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51"},
> +    {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash =
> "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2"},
> +    {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash =
> "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085"},
> +    {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash =
> "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3"},
> +    {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash =
> "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd"},
> +    {file = "librt-0.11.0-cp314-cp314t-win32.whl", hash =
> "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8"},
> +    {file = "librt-0.11.0-cp314-cp314t-win_amd64.whl", hash =
> "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c"},
> +    {file = "librt-0.11.0-cp314-cp314t-win_arm64.whl", hash =
> "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253"},
> +    {file = "librt-0.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash =
> "sha256:6bd72d903911d995ab666dbd1871f8b1e80925a699af8063fbf50053329fb05f"},
> +    {file = "librt-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash =
> "sha256:0ef69ac715f3cd8e5cd252cb2aebfa72c015492aacc339d5d7bf8fef3c62c677"},
> +    {file =
> "librt-0.11.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:624a40c4a4ad7773315c287276cd024509b2c66ff5904f504bfc08d2c70293ab"},
> +    {file =
> "librt-0.11.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:41dc19fe150b69716c8ece4f76773a9e8813fe3e35e032a58b4d46423fb8d7c0"},
> +    {file =
> "librt-0.11.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:4e8bd98ea9c47ae90b319a087ab28dac493f1ffbc1ecd1f28fcdbf3b7e1108d1"},
> +    {file =
> "librt-0.11.0-cp39-cp39-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:84308fc49423ce6475d1c5d1985cd69a8ca9f0325fc7d5f81bb690a3f3625d4e"},
> +    {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash =
> "sha256:ff0fbaf5f44a21beeb0110f2ab64f45135a9536a834b79c0d1ef018f2786bbfa"},
> +    {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash =
> "sha256:9c028a9442a18e266955d364ce42259136e79a7ba14d773e0d778d5f70cd56f1"},
> +    {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash =
> "sha256:9f1692105a02bcf853f355032a5fdc5494358ef83d8fd22d16de375c85cec3f5"},
> +    {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash =
> "sha256:7a80a71e1fda83cc752a9141e87aae7fef279538597564d670e9ce513f286192"},
> +    {file = "librt-0.11.0-cp39-cp39-win32.whl", hash =
> "sha256:140695816ddf3c86eb972981a26f35efd871c44b0c3aed44c8cd01749386617f"},
> +    {file = "librt-0.11.0-cp39-cp39-win_amd64.whl", hash =
> "sha256:92f7ff819c197fc30473190a12c2856f325ac90aabfccbeb2072d28cc2e234e3"},
> +    {file = "librt-0.11.0.tar.gz", hash =
> "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1"},
> +]
> +
>  [[package]]
>  name = "mypy"
> -version = "1.13.0"
> +version = "2.1.0"
>  description = "Optional static typing for Python"
>  optional = false
> -python-versions = ">=3.8"
> +python-versions = ">=3.10"
> +groups = ["dev"]
>  files = [
> -    {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash =
> "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"},
> -    {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash =
> "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"},
> -    {file =
> "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"},
> -    {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash =
> "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"},
> -    {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash =
> "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"},
> -    {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash =
> "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"},
> -    {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash =
> "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"},
> -    {file =
> "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"},
> -    {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash =
> "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"},
> -    {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash =
> "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"},
> -    {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash =
> "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"},
> -    {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash =
> "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"},
> -    {file =
> "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"},
> -    {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash =
> "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"},
> -    {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash =
> "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"},
> -    {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash =
> "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"},
> -    {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash =
> "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"},
> -    {file =
> "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"},
> -    {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash =
> "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"},
> -    {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash =
> "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"},
> -    {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash =
> "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"},
> -    {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash =
> "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"},
> -    {file =
> "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"},
> -    {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash =
> "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"},
> -    {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash =
> "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"},
> -    {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash =
> "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"},
> -    {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash =
> "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"},
> -    {file =
> "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"},
> -    {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash =
> "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"},
> -    {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash =
> "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"},
> -    {file = "mypy-1.13.0-py3-none-any.whl", hash =
> "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"},
> -    {file = "mypy-1.13.0.tar.gz", hash =
> "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"},
> +    {file = "mypy-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash =
> "sha256:11a6beb180257a805961aea9ec591bbd0bd17f1e18d35b8456d57aee5bedfedc"},
> +    {file = "mypy-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash =
> "sha256:8ef78c1d306bbf9a8a12f526c44902c9c28dffd6c52c52bf6a72641ce18d3849"},
> +    {file =
> "mypy-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:c209a90853081ff01d01ee895cafe10f7db1474e0d95beaeef0f6c1db9119bbd"},
> +    {file =
> "mypy-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:47cebf61abde7c088a4e27718a8b13a81655686b2e9c251f5c0915a802248166"},
> +    {file = "mypy-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash =
> "sha256:d57a90ae5e872138a425ec328edbc9b235d1934c4377881a33ec05b341acc9a8"},
> +    {file = "mypy-2.1.0-cp310-cp310-win_amd64.whl", hash =
> "sha256:aea7f7a8a55b459c34275fc468ada6ca7c173a5e43a68f5dbe588a563d8a06b8"},
> +    {file = "mypy-2.1.0-cp310-cp310-win_arm64.whl", hash =
> "sha256:c989640253f0d76843e9c6c1bbf4bd48c5e85ada61bde4beb37cb3eca035685e"},
> +    {file = "mypy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash =
> "sha256:a683016b16fe2f572dc04c72be7ee0504ac1605a265d0200f5cea695fb788f41"},
> +    {file = "mypy-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash =
> "sha256:1a293c534adb55271fef24a26da04b855540a8c13cc07bc5917b9fd2c394f2ca"},
> +    {file =
> "mypy-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:7406f4d048e71e576f5356d317e5b0a9e666dfd966bd99f9d14ca06e1a341538"},
> +    {file =
> "mypy-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:e0210d626fc8b31ccc90233754c7bc90e1f43205e85d96387f7db1285b55c398"},
> +    {file = "mypy-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash =
> "sha256:3712c20deed54e814eaaa825603bada8ea1c390670a397c95b98405347acc563"},
> +    {file = "mypy-2.1.0-cp311-cp311-win_amd64.whl", hash =
> "sha256:fcaa0e479066e31f7cceb6a3bea39cb22b2ff51a6b2f24f193d19179ba17c389"},
> +    {file = "mypy-2.1.0-cp311-cp311-win_arm64.whl", hash =
> "sha256:0b1a5260c95aa443083f9ed3592662941951bca3d4ca224a5dc517c38b7cf666"},
> +    {file = "mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash =
> "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af"},
> +    {file = "mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash =
> "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6"},
> +    {file =
> "mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211"},
> +    {file =
> "mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b"},
> +    {file = "mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash =
> "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22"},
> +    {file = "mypy-2.1.0-cp312-cp312-win_amd64.whl", hash =
> "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b"},
> +    {file = "mypy-2.1.0-cp312-cp312-win_arm64.whl", hash =
> "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8"},
> +    {file = "mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash =
> "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5"},
> +    {file = "mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash =
> "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e"},
> +    {file =
> "mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e"},
> +    {file =
> "mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285"},
> +    {file = "mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash =
> "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5"},
> +    {file = "mypy-2.1.0-cp313-cp313-win_amd64.whl", hash =
> "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65"},
> +    {file = "mypy-2.1.0-cp313-cp313-win_arm64.whl", hash =
> "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d"},
> +    {file = "mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash =
> "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2"},
> +    {file = "mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash =
> "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f"},
> +    {file =
> "mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4"},
> +    {file =
> "mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef"},
> +    {file = "mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash =
> "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135"},
> +    {file = "mypy-2.1.0-cp314-cp314-win_amd64.whl", hash =
> "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21"},
> +    {file = "mypy-2.1.0-cp314-cp314-win_arm64.whl", hash =
> "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57"},
> +    {file = "mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash =
> "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e"},
> +    {file = "mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash =
> "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780"},
> +    {file =
> "mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd"},
> +    {file =
> "mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08"},
> +    {file = "mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash =
> "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081"},
> +    {file = "mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash =
> "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7"},
> +    {file = "mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash =
> "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6"},
> +    {file = "mypy-2.1.0-py3-none-any.whl", hash =
> "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289"},
> +    {file = "mypy-2.1.0.tar.gz", hash =
> "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633"},
>  ]
>
>  [package.dependencies]
> -mypy-extensions = ">=1.0.0"
> +ast-serialize = ">=0.3.0,<1.0.0"
> +librt = {version = ">=0.11.0", markers = "platform_python_implementation
> != \"PyPy\""}
> +mypy_extensions = ">=1.0.0"
> +pathspec = ">=1.0.0"
>  tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
> -typing-extensions = ">=4.6.0"
> +typing_extensions = [
> +    {version = ">=4.6.0", markers = "python_version < \"3.15\""},
> +    {version = ">=4.14.0", markers = "python_version >= \"3.15\""},
> +]
>
>  [package.extras]
>  dmypy = ["psutil (>=4.0)"]
> @@ -307,6 +480,7 @@ version = "1.0.0"
>  description = "Type system extensions for programs checked with the mypy
> type checker."
>  optional = false
>  python-versions = ">=3.5"
> +groups = ["dev"]
>  files = [
>      {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash =
> "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
>      {file = "mypy_extensions-1.0.0.tar.gz", hash =
> "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
> @@ -318,6 +492,7 @@ version = "3.5.0"
>  description = "SSH2 protocol library"
>  optional = false
>  python-versions = ">=3.6"
> +groups = ["main"]
>  files = [
>      {file = "paramiko-3.5.0-py3-none-any.whl", hash =
> "sha256:1fedf06b085359051cd7d0d270cebe19e755a8a921cc2ddbfa647fb0cd7d68f9"},
>      {file = "paramiko-3.5.0.tar.gz", hash =
> "sha256:ad11e540da4f55cedda52931f1a3f812a8238a7af7f62a60de538cd80bb28124"},
> @@ -329,20 +504,39 @@ cryptography = ">=3.3"
>  pynacl = ">=1.5"
>
>  [package.extras]
> -all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32
> (>=2.1.8)"]
> -gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"]
> +all = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "invoke
> (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system ==
> \"Windows\""]
> +gssapi = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "pyasn1
> (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""]
>  invoke = ["invoke (>=2.0)"]
>
> +[[package]]
> +name = "pathspec"
> +version = "1.1.1"
> +description = "Utility library for gitignore style pattern matching of
> file paths."
> +optional = false
> +python-versions = ">=3.9"
> +groups = ["dev"]
> +files = [
> +    {file = "pathspec-1.1.1-py3-none-any.whl", hash =
> "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"},
> +    {file = "pathspec-1.1.1.tar.gz", hash =
> "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"},
> +]
> +
> +[package.extras]
> +hyperscan = ["hyperscan (>=0.7)"]
> +optional = ["typing-extensions (>=4)"]
> +re2 = ["google-re2 (>=1.1)"]
> +
>  [[package]]
>  name = "pycparser"
>  version = "2.22"
>  description = "C parser in Python"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main", "dev"]
>  files = [
>      {file = "pycparser-2.22-py3-none-any.whl", hash =
> "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
>      {file = "pycparser-2.22.tar.gz", hash =
> "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
>  ]
> +markers = {dev = "platform_python_implementation != \"PyPy\""}
>
>  [[package]]
>  name = "pydantic"
> @@ -350,6 +544,7 @@ version = "2.10.3"
>  description = "Data validation using Python type hints"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main"]
>  files = [
>      {file = "pydantic-2.10.3-py3-none-any.whl", hash =
> "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"},
>      {file = "pydantic-2.10.3.tar.gz", hash =
> "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"},
> @@ -362,7 +557,7 @@ typing-extensions = ">=4.12.2"
>
>  [package.extras]
>  email = ["email-validator (>=2.0.0)"]
> -timezone = ["tzdata"]
> +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system ==
> \"Windows\""]
>
>  [[package]]
>  name = "pydantic-core"
> @@ -370,6 +565,7 @@ version = "2.27.1"
>  description = "Core functionality for Pydantic validation and
> serialization"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main"]
>  files = [
>      {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl",
> hash =
> "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"},
>      {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl",
> hash =
> "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"},
> @@ -482,6 +678,7 @@ version = "1.5.0"
>  description = "Python binding to the Networking and Cryptography (NaCl)
> library"
>  optional = false
>  python-versions = ">=3.6"
> +groups = ["main"]
>  files = [
>      {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash =
> "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"},
>      {file =
> "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl",
> hash =
> "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"},
> @@ -508,6 +705,7 @@ version = "6.0.2"
>  description = "YAML parser and emitter for Python"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main"]
>  files = [
>      {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash =
> "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
>      {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash =
> "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
> @@ -566,29 +764,30 @@ files = [
>
>  [[package]]
>  name = "ruff"
> -version = "0.8.2"
> +version = "0.15.16"
>  description = "An extremely fast Python linter and code formatter,
> written in Rust."
>  optional = false
>  python-versions = ">=3.7"
> +groups = ["dev"]
>  files = [
> -    {file = "ruff-0.8.2-py3-none-linux_armv6l.whl", hash =
> "sha256:c49ab4da37e7c457105aadfd2725e24305ff9bc908487a9bf8d548c6dad8bb3d"},
> -    {file = "ruff-0.8.2-py3-none-macosx_10_12_x86_64.whl", hash =
> "sha256:ec016beb69ac16be416c435828be702ee694c0d722505f9c1f35e1b9c0cc1bf5"},
> -    {file = "ruff-0.8.2-py3-none-macosx_11_0_arm64.whl", hash =
> "sha256:f05cdf8d050b30e2ba55c9b09330b51f9f97d36d4673213679b965d25a785f3c"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
> hash =
> "sha256:60f578c11feb1d3d257b2fb043ddb47501ab4816e7e221fbb0077f0d5d4e7b6f"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash
> =
> "sha256:cbd5cf9b0ae8f30eebc7b360171bd50f59ab29d39f06a670b3e4501a36ba5897"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash =
> "sha256:b402ddee3d777683de60ff76da801fa7e5e8a71038f57ee53e903afbcefdaa58"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash =
> "sha256:705832cd7d85605cb7858d8a13d75993c8f3ef1397b0831289109e953d833d29"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
> hash =
> "sha256:32096b41aaf7a5cc095fa45b4167b890e4c8d3fd217603f3634c92a541de7248"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash =
> "sha256:e769083da9439508833cfc7c23e351e1809e67f47c50248250ce1ac52c21fb93"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash
> =
> "sha256:5fe716592ae8a376c2673fdfc1f5c0c193a6d0411f90a496863c99cd9e2ae25d"},
> -    {file = "ruff-0.8.2-py3-none-musllinux_1_2_aarch64.whl", hash =
> "sha256:81c148825277e737493242b44c5388a300584d73d5774defa9245aaef55448b0"},
> -    {file = "ruff-0.8.2-py3-none-musllinux_1_2_armv7l.whl", hash =
> "sha256:d261d7850c8367704874847d95febc698a950bf061c9475d4a8b7689adc4f7fa"},
> -    {file = "ruff-0.8.2-py3-none-musllinux_1_2_i686.whl", hash =
> "sha256:1ca4e3a87496dc07d2427b7dd7ffa88a1e597c28dad65ae6433ecb9f2e4f022f"},
> -    {file = "ruff-0.8.2-py3-none-musllinux_1_2_x86_64.whl", hash =
> "sha256:729850feed82ef2440aa27946ab39c18cb4a8889c1128a6d589ffa028ddcfc22"},
> -    {file = "ruff-0.8.2-py3-none-win32.whl", hash =
> "sha256:ac42caaa0411d6a7d9594363294416e0e48fc1279e1b0e948391695db2b3d5b1"},
> -    {file = "ruff-0.8.2-py3-none-win_amd64.whl", hash =
> "sha256:2aae99ec70abf43372612a838d97bfe77d45146254568d94926e8ed5bbb409ea"},
> -    {file = "ruff-0.8.2-py3-none-win_arm64.whl", hash =
> "sha256:fb88e2a506b70cfbc2de6fae6681c4f944f7dd5f2fe87233a7233d888bad73e8"},
> -    {file = "ruff-0.8.2.tar.gz", hash =
> "sha256:b84f4f414dda8ac7f75075c1fa0b905ac0ff25361f42e6d5da681a465e0f78e5"},
> +    {file = "ruff-0.15.16-py3-none-linux_armv6l.whl", hash =
> "sha256:6ac3c0b3969cc6cf6b158c4e2f8f682acb58e7d700d8a44b65ecdc72d66ab0b2"},
> +    {file = "ruff-0.15.16-py3-none-macosx_10_12_x86_64.whl", hash =
> "sha256:197c207ed75ffba54a0dec23db4aa939a27a3053073e085e0042433cbdc58e4a"},
> +    {file = "ruff-0.15.16-py3-none-macosx_11_0_arm64.whl", hash =
> "sha256:3a39fec45ab316cc23e7558f23fea4a70403ddb5648ea9a4a3854a16973d0071"},
> +    {file =
> "ruff-0.15.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
> hash =
> "sha256:ba93191d79003116b95128c9d306e045200fdbd0bccb782b110f3cd1d4abc5cf"},
> +    {file =
> "ruff-0.15.16-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
> hash =
> "sha256:c6ee4b90520630120ef032aa5cc10db483852dff950e78b1d717e2993a61ac8d"},
> +    {file =
> "ruff-0.15.16-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash =
> "sha256:4e4215bc938bc3c8215c1472c1aa437e310fee20cd427335fec9d7e609563628"},
> +    {file =
> "ruff-0.15.16-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
> hash =
> "sha256:7c8d26be963b090f10e29abc8b3e74a2a321f6fa34e02424e30b5af89350ecbb"},
> +    {file =
> "ruff-0.15.16-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash
> =
> "sha256:f198cf4123602a2280ed46c307bcbafe41758d6fee5b456b6b6058ca1514b3b4"},
> +    {file =
> "ruff-0.15.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
> hash =
> "sha256:bb27515fa6240fb586ae82b901a59e67d24acff86f2190b433dc542fe0435aeb"},
> +    {file = "ruff-0.15.16-py3-none-manylinux_2_31_riscv64.whl", hash =
> "sha256:a267c46ba1593fc26b8eecbea050b39d40c0b6bb7781ee11c90a02cd10032951"},
> +    {file = "ruff-0.15.16-py3-none-musllinux_1_2_aarch64.whl", hash =
> "sha256:528c68f39a91498a8d50e91ff5985df3d105782bab49cc378e73ac26bff083e8"},
> +    {file = "ruff-0.15.16-py3-none-musllinux_1_2_armv7l.whl", hash =
> "sha256:7ed55c58950df60589a9a7a5d2f8fa5f54ebd287163be805adfe6ee95a9de123"},
> +    {file = "ruff-0.15.16-py3-none-musllinux_1_2_i686.whl", hash =
> "sha256:d482feaf51512b50f9790ceb417a56a61dd1e9d9bf967662b9ed27c01b34f53a"},
> +    {file = "ruff-0.15.16-py3-none-musllinux_1_2_x86_64.whl", hash =
> "sha256:1e15bc8c94513dae2a40cc9ef07c94fdd4ecc9e29dabebeebe170f952322c9e3"},
> +    {file = "ruff-0.15.16-py3-none-win32.whl", hash =
> "sha256:580378f7bd4aa25f72e74aa54948a9622f142b1e509521dd10902e886681cc1e"},
> +    {file = "ruff-0.15.16-py3-none-win_amd64.whl", hash =
> "sha256:408256017284eddf98fff77b29aa4fb30f586042d535b2d9befc6512f400aaec"},
> +    {file = "ruff-0.15.16-py3-none-win_arm64.whl", hash =
> "sha256:8cd61783afb39638a7133ef0d2dfb1e91277593962f81b5a8423eb0b888a6121"},
> +    {file = "ruff-0.15.16.tar.gz", hash =
> "sha256:d05e78d38c78caf020b03789e25106c93017db5a0cb6e2819885018c61343b78"},
>  ]
>
>  [[package]]
> @@ -597,6 +796,7 @@ version = "2.6.1"
>  description = "Scapy: interactive packet manipulation tool"
>  optional = false
>  python-versions = "<4,>=3.7"
> +groups = ["main"]
>  files = [
>      {file = "scapy-2.6.1-py3-none-any.whl", hash =
> "sha256:88a998572049b511a1f3e44f4aa7c62dd39c6ea2aa1bb58434f503956641789d"},
>      {file = "scapy-2.6.1.tar.gz", hash =
> "sha256:7600d7e2383c853e5c3a6e05d37e17643beebf2b3e10d7914dffcc3bc3c6e6c5"},
> @@ -613,6 +813,7 @@ version = "0.10.2"
>  description = "Python Library for Tom's Obvious, Minimal Language"
>  optional = false
>  python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
> +groups = ["dev"]
>  files = [
>      {file = "toml-0.10.2-py2.py3-none-any.whl", hash =
> "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
>      {file = "toml-0.10.2.tar.gz", hash =
> "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
> @@ -624,6 +825,8 @@ version = "2.2.1"
>  description = "A lil' TOML parser"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["dev"]
> +markers = "python_version == \"3.10\""
>  files = [
>      {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash =
> "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
>      {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash =
> "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
> @@ -665,6 +868,7 @@ version = "2.0.0.10"
>  description = "Typing stubs for invoke"
>  optional = false
>  python-versions = ">=3.7"
> +groups = ["dev"]
>  files = [
>      {file = "types-invoke-2.0.0.10.tar.gz", hash =
> "sha256:a54d7ecdc19e0c22cd2786ef2e64c2631715c78eba8a1bf40b511d0608f33a88"},
>      {file = "types_invoke-2.0.0.10-py3-none-any.whl", hash =
> "sha256:2404e4279601fa96e14ef68321fd10a660a828677aabdcaeef6a5189778084ef"},
> @@ -672,13 +876,14 @@ files = [
>
>  [[package]]
>  name = "types-paramiko"
> -version = "3.5.0.20240928"
> +version = "4.0.0.20260518"
>  description = "Typing stubs for paramiko"
>  optional = false
> -python-versions = ">=3.8"
> +python-versions = ">=3.10"
> +groups = ["dev"]
>  files = [
> -    {file = "types-paramiko-3.5.0.20240928.tar.gz", hash =
> "sha256:79dd9b2ee510b76a3b60d8ac1f3f348c45fcecf01347ca79e14db726bbfc442d"},
> -    {file = "types_paramiko-3.5.0.20240928-py3-none-any.whl", hash =
> "sha256:cda0aff4905fe8efe4b5448331a80e943d42a796bd4beb77a3eed3485bc96a85"},
> +    {file = "types_paramiko-4.0.0.20260518-py3-none-any.whl", hash =
> "sha256:0ffaf1a6eb796833a49653cba4c7be13af51c8269d75234972d6239763dda270"},
> +    {file = "types_paramiko-4.0.0.20260518.tar.gz", hash =
> "sha256:286f6830945cba63797eedf375ed87138d93198121253afe66c5d6dbcf91318d"},
>  ]
>
>  [package.dependencies]
> @@ -686,13 +891,14 @@ cryptography = ">=37.0.0"
>
>  [[package]]
>  name = "types-pyyaml"
> -version = "6.0.12.20240917"
> +version = "6.0.12.20260518"
>  description = "Typing stubs for PyYAML"
>  optional = false
> -python-versions = ">=3.8"
> +python-versions = ">=3.10"
> +groups = ["dev"]
>  files = [
> -    {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash =
> "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"},
> -    {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash =
> "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"},
> +    {file = "types_pyyaml-6.0.12.20260518-py3-none-any.whl", hash =
> "sha256:d2150f75a231c9fe9c7463bd29487d93e60bac90400287351384bc2284eba7cd"},
> +    {file = "types_pyyaml-6.0.12.20260518.tar.gz", hash =
> "sha256:d917f83fb38462550338c1297faedd860b3ec83912b96b1e3d73255f7473e466"},
>  ]
>
>  [[package]]
> @@ -701,17 +907,33 @@ version = "4.12.2"
>  description = "Backported and Experimental Type Hints for Python 3.8+"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main", "dev"]
> +markers = "python_version < \"3.15\""
>  files = [
>      {file = "typing_extensions-4.12.2-py3-none-any.whl", hash =
> "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
>      {file = "typing_extensions-4.12.2.tar.gz", hash =
> "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
>  ]
>
> +[[package]]
> +name = "typing-extensions"
> +version = "4.15.0"
> +description = "Backported and Experimental Type Hints for Python 3.9+"
> +optional = false
> +python-versions = ">=3.9"
> +groups = ["main", "dev"]
> +markers = "python_version >= \"3.15\""
> +files = [
> +    {file = "typing_extensions-4.15.0-py3-none-any.whl", hash =
> "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
> +    {file = "typing_extensions-4.15.0.tar.gz", hash =
> "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
> +]
> +
>  [[package]]
>  name = "wrapt"
>  version = "1.17.2"
>  description = "Module for decorators, wrappers and monkey patching."
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main"]
>  files = [
>      {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash =
> "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"},
>      {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash =
> "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"},
> @@ -795,6 +1017,6 @@ files = [
>  ]
>
>  [metadata]
> -lock-version = "2.0"
> +lock-version = "2.1"
>  python-versions = "^3.10"
> -content-hash =
> "aa6dff54827602c89ee125019291965de50d7a471ca48add889ea23aa5fd9b2f"
> +content-hash =
> "1ca3cdf5bf98c528845b5e22169322db8912fe437988635f4ebea088b2079463"
> diff --git a/dts/pyproject.toml b/dts/pyproject.toml
> index 8b061c3cee..b639af65b6 100644
> --- a/dts/pyproject.toml
> +++ b/dts/pyproject.toml
> @@ -28,12 +28,12 @@ aenum = "^3.1.15"
>  pydantic = "^2.9.2"
>
>  [tool.poetry.group.dev.dependencies]
> -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"
>
>  [build-system]
>  requires = ["poetry-core>=1.0.0"]
> diff --git a/dts/tests/TestSuite_cryptodev_throughput.py
> b/dts/tests/TestSuite_cryptodev_throughput.py
> index af0a5680ab..2fc0d8779a 100644
> --- a/dts/tests/TestSuite_cryptodev_throughput.py
> +++ b/dts/tests/TestSuite_cryptodev_throughput.py
> @@ -101,12 +101,12 @@ def _print_stats(self, test_vals: list[dict[str, int
> | float | str]]) -> None:
>          print(f"{'Throughput Results'.center(border_len)}\n{'=' *
> border_len}")
>          for k, v in test_vals[0].items():
>              print(f"|{k.title():<{element_len}}", end="")
> -        print(f"|\n{'='*border_len}")
> +        print(f"|\n{'=' * border_len}")
>
>          for test_val in test_vals:
>              for k, v in test_val.items():
>                  print(f"|{v:<{element_len}}", end="")
> -            print(f"|\n{'='*border_len}")
> +            print(f"|\n{'=' * border_len}")
>
>      def _verify_throughput(
>          self,
> diff --git a/dts/tests/TestSuite_port_control.py
> b/dts/tests/TestSuite_port_control.py
> index 6be47838d0..b51fdc2959 100644
> --- a/dts/tests/TestSuite_port_control.py
> +++ b/dts/tests/TestSuite_port_control.py
> @@ -44,8 +44,7 @@ def _send_packets_and_verify(self) -> None:
>          recv_pakts = [
>              p
>              for p in recv_pakts
> -            if
> -            (
> +            if (
>                  # Remove padding from the bytes.
>                  hasattr(p, "load") and
> p.load.decode("utf-8").replace("\x00", "") == payload
>              )
> diff --git a/dts/tests/TestSuite_single_core_forward_perf.py
> b/dts/tests/TestSuite_single_core_forward_perf.py
> index 1e7ab7b036..acdf8ae2f6 100644
> --- a/dts/tests/TestSuite_single_core_forward_perf.py
> +++ b/dts/tests/TestSuite_single_core_forward_perf.py
> @@ -144,7 +144,7 @@ def single_core_forward_perf(self) -> None:
>          for params in self.test_parameters:
>              verify(
>                  params["pass"] is True,
> -                f"""Packets forwarded is less than {(1
> -self.delta_tolerance)*100}%
> +                f"""Packets forwarded is less than {(1 -
> self.delta_tolerance) * 100}%
>                  of the expected baseline.
>                  Measured MPPS = {params["measured_mpps"]}
>                  Expected MPPS = {params["expected_mpps"]}""",
> --
> 2.54.0
>
>

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

^ permalink raw reply

* Re: [PATCH v10 00/21] Wangxun Fixes
From: Stephen Hemminger @ 2026-06-24 16:43 UTC (permalink / raw)
  To: Zaiyu Wang; +Cc: dev
In-Reply-To: <20260624115254.20348-1-zaiyuwang@trustnetic.com>

On Wed, 24 Jun 2026 19:52:32 +0800
Zaiyu Wang <zaiyuwang@trustnetic.com> wrote:

> This series fixes several issues found on Wangxun Emerald, Sapphire and
> Amber-lite NICs, with a focus on link-related problems.
> ---
> v10:
> - Fixed compilation error on clang
> - Renamed unprefixed symbols
> ---
> v9:
> - Fixed several checkpatch errors
> ---
> v8:
> - Fixed compilation error by replacing RTE_ETH_DEV_TO_PCI with RTE_CLASS_TO_BUS_DEVICE
> ---
> v7:
> - Fixed inverted semantics of is_flat_mem to match SFF8636
> ---
> v6:
> - Fixed more issues identified by AI review
> ---
> v5:
> - Fixed issues identified by AI review
> ---
> v4:
> - Fixed issues identified by devtools scripts
> ---
> v3:
> - Addressed Stephen's comments
> ---
> v2:
> - Fixed compilation error and code style issues
> ---
> 
> Zaiyu Wang (21):
>   net/txgbe: remove duplicate xstats counters
>   net/ngbe: remove duplicate xstats counters
>   net/ngbe: add missing CDR config for YT PHY
>   net/ngbe: fix VF promiscuous and allmulticast
>   net/txgbe: fix inaccuracy in Tx rate limiting
>   net/txgbe: fix link status check condition
>   net/txgbe: fix Tx desc free logic
>   net/txgbe: fix link flow control registers for Amber-Lite
>   net/txgbe: fix link flow control config for Sapphire
>   net/txgbe: fix a mass of unknown interrupts
>   net/txgbe: fix traffic class priority configuration
>   net/txgbe: fix link stability for 25G NIC
>   net/txgbe: fix link stability for 40G NIC
>   net/txgbe: fix link stability for Amber-Lite backplane mode
>   net/txgbe: fix FEC mode configuration on 25G NIC
>   net/txgbe: fix SFP module identification
>   net/txgbe: fix get module info operation
>   net/txgbe: fix get EEPROM operation
>   net/txgbe: fix to reset Tx write-back pointer
>   net/txgbe: fix to enable Tx desc check
>   net/txgbe: fix temperature track for AML NIC
> 
>  drivers/net/ngbe/base/ngbe_phy_yt.c       |    3 +
>  drivers/net/ngbe/ngbe_ethdev.c            |    5 -
>  drivers/net/ngbe/ngbe_ethdev_vf.c         |   11 +-
>  drivers/net/txgbe/base/meson.build        |    2 +
>  drivers/net/txgbe/base/txgbe.h            |    2 +
>  drivers/net/txgbe/base/txgbe_aml.c        |  185 +-
>  drivers/net/txgbe/base/txgbe_aml.h        |    6 +-
>  drivers/net/txgbe/base/txgbe_aml40.c      |  114 +-
>  drivers/net/txgbe/base/txgbe_aml40.h      |    6 +-
>  drivers/net/txgbe/base/txgbe_dcb_hw.c     |    2 +-
>  drivers/net/txgbe/base/txgbe_e56.c        | 3746 +++++++++++++++++++++
>  drivers/net/txgbe/base/txgbe_e56.h        | 1778 ++++++++++
>  drivers/net/txgbe/base/txgbe_e56_bp.c     | 2597 ++++++++++++++
>  drivers/net/txgbe/base/txgbe_e56_bp.h     |  282 ++
>  drivers/net/txgbe/base/txgbe_hw.c         |   54 +-
>  drivers/net/txgbe/base/txgbe_hw.h         |    4 +-
>  drivers/net/txgbe/base/txgbe_osdep.h      |    4 +
>  drivers/net/txgbe/base/txgbe_phy.c        |  362 +-
>  drivers/net/txgbe/base/txgbe_phy.h        |   46 +-
>  drivers/net/txgbe/base/txgbe_regs.h       |   13 +-
>  drivers/net/txgbe/base/txgbe_type.h       |   43 +-
>  drivers/net/txgbe/txgbe_ethdev.c          |  472 ++-
>  drivers/net/txgbe/txgbe_ethdev.h          |    7 +-
>  drivers/net/txgbe/txgbe_rxtx.c            |  111 +-
>  drivers/net/txgbe/txgbe_rxtx.h            |   38 +-
>  drivers/net/txgbe/txgbe_rxtx_vec_common.h |   17 +-
>  26 files changed, 9464 insertions(+), 446 deletions(-)
>  create mode 100644 drivers/net/txgbe/base/txgbe_e56.c
>  create mode 100644 drivers/net/txgbe/base/txgbe_e56.h
>  create mode 100644 drivers/net/txgbe/base/txgbe_e56_bp.c
>  create mode 100644 drivers/net/txgbe/base/txgbe_e56_bp.h
> 


Applied to net-next.
If you want to cleanup some of the checkpatch warnings, I can swap in a new version.

^ permalink raw reply

* Re: [PATCH v6 00/23] net/sxe2: added Linkdata sxe2 ethernet driver
From: Stephen Hemminger @ 2026-06-24 16:38 UTC (permalink / raw)
  To: liujie5; +Cc: dev
In-Reply-To: <20260624020211.3687062-1-liujie5@linkdatatechnology.com>

On Wed, 24 Jun 2026 10:02:11 +0800
liujie5@linkdatatechnology.com wrote:

> From: Jie Liu <liujie5@linkdatatechnology.com>
> 
> This patch set implements core functionality for the SXE2 PMD,
> including basic driver framework, data path setup, and advanced
> offload features (VLAN, RSS,TM, PTP etc.).
> 
> V6:
> 	Refactored sxe2_ptype_tbl from adapter-indirection pattern (adapter->ptype_tbl[]) 
> 	to extern const direct-access pattern, matching txgbe PMD convention
> 
> 	All vector/SIMD Rx paths (SSE, AVX2, AVX512, NEON) index sxe2_ptype_tbl[] directly without local pointer indirection
> 
> 	remove flow_dup_pattern_mode devarg
> 
> Jie Liu (23):
>   net/sxe2: remove software statistics devargs
>   net/sxe2: add Rx framework and packet types callback
>   net/sxe2: support AVX512 vectorized path for Rx and Tx
>   net/sxe2: add AVX2 vector data path for Rx and Tx
>   net/sxe2: add link update callback
>   net/sxe2: support L2 filtering and MAC config
>   drivers: support RSS feature
>   net/sxe2: support TM hierarchy and shaping
>   net/sxe2: support IPsec inline protocol offload
>   net/sxe2: support statistics and multi-process
>   drivers: interrupt handling
>   net/sxe2: add NEON vec Rx/Tx burst functions
>   drivers: add support for VF representors
>   net/sxe2: add support for custom UDP tunnel ports
>   net/sxe2: support firmware version reading
>   net/sxe2: implement get monitor address
>   common/sxe2: add shared SFP module definitions
>   net/sxe2: support SFP module info and EEPROM access
>   net/sxe2: implement private dump info
>   net/sxe2: add mbuf validation in Tx debug mode
>   common/sxe2: add callback for memory event handling
>   net/sxe2: add private devargs parsing
>   net/sxe2: update sxe2 feature matrix docs
> 
>  doc/guides/nics/features/sxe2.ini          |   56 +
>  doc/guides/nics/sxe2.rst                   |  147 ++
>  drivers/common/sxe2/sxe2_common.c          |  156 ++
>  drivers/common/sxe2/sxe2_common.h          |    4 +
>  drivers/common/sxe2/sxe2_flow_public.h     |  633 +++++++
>  drivers/common/sxe2/sxe2_ioctl_chnl.c      |  178 +-
>  drivers/common/sxe2/sxe2_ioctl_chnl_func.h |   18 +
>  drivers/common/sxe2/sxe2_msg.h             |  118 ++
>  drivers/net/sxe2/meson.build               |   52 +
>  drivers/net/sxe2/sxe2_cmd_chnl.c           | 1587 +++++++++++++++-
>  drivers/net/sxe2/sxe2_cmd_chnl.h           |  139 ++
>  drivers/net/sxe2/sxe2_drv_cmd.h            |  523 +++++-
>  drivers/net/sxe2/sxe2_dump.c               |  300 +++
>  drivers/net/sxe2/sxe2_dump.h               |   12 +
>  drivers/net/sxe2/sxe2_ethdev.c             | 1468 ++++++++++++++-
>  drivers/net/sxe2/sxe2_ethdev.h             |  111 +-
>  drivers/net/sxe2/sxe2_ethdev_repr.c        |  609 ++++++
>  drivers/net/sxe2/sxe2_ethdev_repr.h        |   32 +
>  drivers/net/sxe2/sxe2_filter.c             |  895 +++++++++
>  drivers/net/sxe2/sxe2_filter.h             |  100 +
>  drivers/net/sxe2/sxe2_flow.c               | 1391 ++++++++++++++
>  drivers/net/sxe2/sxe2_flow.h               |   30 +
>  drivers/net/sxe2/sxe2_flow_define.h        |  144 ++
>  drivers/net/sxe2/sxe2_flow_parse_action.c  | 1182 ++++++++++++
>  drivers/net/sxe2/sxe2_flow_parse_action.h  |   23 +
>  drivers/net/sxe2/sxe2_flow_parse_engine.c  |  106 ++
>  drivers/net/sxe2/sxe2_flow_parse_engine.h  |   13 +
>  drivers/net/sxe2/sxe2_flow_parse_pattern.c | 1935 +++++++++++++++++++
>  drivers/net/sxe2/sxe2_flow_parse_pattern.h |   46 +
>  drivers/net/sxe2/sxe2_ipsec.c              | 1565 ++++++++++++++++
>  drivers/net/sxe2/sxe2_ipsec.h              |  254 +++
>  drivers/net/sxe2/sxe2_irq.c                | 1026 ++++++++++
>  drivers/net/sxe2/sxe2_irq.h                |   25 +
>  drivers/net/sxe2/sxe2_mac.c                |  530 ++++++
>  drivers/net/sxe2/sxe2_mac.h                |   84 +
>  drivers/net/sxe2/sxe2_mp.c                 |  414 +++++
>  drivers/net/sxe2/sxe2_mp.h                 |   67 +
>  drivers/net/sxe2/sxe2_queue.c              |   17 +-
>  drivers/net/sxe2/sxe2_queue.h              |   15 +-
>  drivers/net/sxe2/sxe2_rss.c                |  584 ++++++
>  drivers/net/sxe2/sxe2_rss.h                |   81 +
>  drivers/net/sxe2/sxe2_rx.c                 |   93 +-
>  drivers/net/sxe2/sxe2_rx.h                 |    2 +
>  drivers/net/sxe2/sxe2_security.c           |  335 ++++
>  drivers/net/sxe2/sxe2_security.h           |   77 +
>  drivers/net/sxe2/sxe2_stats.c              |  586 ++++++
>  drivers/net/sxe2/sxe2_stats.h              |   39 +
>  drivers/net/sxe2/sxe2_switchdev.c          |  332 ++++
>  drivers/net/sxe2/sxe2_switchdev.h          |   33 +
>  drivers/net/sxe2/sxe2_tm.c                 | 1151 ++++++++++++
>  drivers/net/sxe2/sxe2_tm.h                 |   76 +
>  drivers/net/sxe2/sxe2_tx.c                 |    7 +
>  drivers/net/sxe2/sxe2_txrx.c               | 1958 +++++++++++++++++++-
>  drivers/net/sxe2/sxe2_txrx.h               |    8 +
>  drivers/net/sxe2/sxe2_txrx_check_mbuf.c    |  595 ++++++
>  drivers/net/sxe2/sxe2_txrx_check_mbuf.h    |   38 +
>  drivers/net/sxe2/sxe2_txrx_poll.c          |  284 ++-
>  drivers/net/sxe2/sxe2_txrx_vec.c           |   46 +-
>  drivers/net/sxe2/sxe2_txrx_vec.h           |   38 +-
>  drivers/net/sxe2/sxe2_txrx_vec_avx2.c      |  747 ++++++++
>  drivers/net/sxe2/sxe2_txrx_vec_avx512.c    |  867 +++++++++
>  drivers/net/sxe2/sxe2_txrx_vec_common.h    |   54 +-
>  drivers/net/sxe2/sxe2_txrx_vec_neon.c      |  689 +++++++
>  drivers/net/sxe2/sxe2_txrx_vec_sse.c       |   38 +-
>  drivers/net/sxe2/sxe2_vsi.c                |  146 ++
>  drivers/net/sxe2/sxe2_vsi.h                |   12 +-
>  drivers/net/sxe2/sxe2vf_regs.h             |   85 +
>  67 files changed, 24733 insertions(+), 273 deletions(-)
>  create mode 100644 drivers/common/sxe2/sxe2_flow_public.h
>  create mode 100644 drivers/common/sxe2/sxe2_msg.h
>  create mode 100644 drivers/net/sxe2/sxe2_dump.c
>  create mode 100644 drivers/net/sxe2/sxe2_dump.h
>  create mode 100644 drivers/net/sxe2/sxe2_ethdev_repr.c
>  create mode 100644 drivers/net/sxe2/sxe2_ethdev_repr.h
>  create mode 100644 drivers/net/sxe2/sxe2_filter.c
>  create mode 100644 drivers/net/sxe2/sxe2_filter.h
>  create mode 100644 drivers/net/sxe2/sxe2_flow.c
>  create mode 100644 drivers/net/sxe2/sxe2_flow.h
>  create mode 100644 drivers/net/sxe2/sxe2_flow_define.h
>  create mode 100644 drivers/net/sxe2/sxe2_flow_parse_action.c
>  create mode 100644 drivers/net/sxe2/sxe2_flow_parse_action.h
>  create mode 100644 drivers/net/sxe2/sxe2_flow_parse_engine.c
>  create mode 100644 drivers/net/sxe2/sxe2_flow_parse_engine.h
>  create mode 100644 drivers/net/sxe2/sxe2_flow_parse_pattern.c
>  create mode 100644 drivers/net/sxe2/sxe2_flow_parse_pattern.h
>  create mode 100644 drivers/net/sxe2/sxe2_ipsec.c
>  create mode 100644 drivers/net/sxe2/sxe2_ipsec.h
>  create mode 100644 drivers/net/sxe2/sxe2_irq.c
>  create mode 100644 drivers/net/sxe2/sxe2_mac.c
>  create mode 100644 drivers/net/sxe2/sxe2_mac.h
>  create mode 100644 drivers/net/sxe2/sxe2_mp.c
>  create mode 100644 drivers/net/sxe2/sxe2_mp.h
>  create mode 100644 drivers/net/sxe2/sxe2_rss.c
>  create mode 100644 drivers/net/sxe2/sxe2_rss.h
>  create mode 100644 drivers/net/sxe2/sxe2_security.c
>  create mode 100644 drivers/net/sxe2/sxe2_security.h
>  create mode 100644 drivers/net/sxe2/sxe2_stats.c
>  create mode 100644 drivers/net/sxe2/sxe2_stats.h
>  create mode 100644 drivers/net/sxe2/sxe2_switchdev.c
>  create mode 100644 drivers/net/sxe2/sxe2_switchdev.h
>  create mode 100644 drivers/net/sxe2/sxe2_tm.c
>  create mode 100644 drivers/net/sxe2/sxe2_tm.h
>  create mode 100644 drivers/net/sxe2/sxe2_txrx_check_mbuf.c
>  create mode 100644 drivers/net/sxe2/sxe2_txrx_check_mbuf.h
>  create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_avx2.c
>  create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_avx512.c
>  create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_neon.c
>  create mode 100644 drivers/net/sxe2/sxe2vf_regs.h
> 

On the duplicate flow devargs; your observation was correct, mlx5 has similar devarg option.
Sorry that I didn't strongly nudge AI in the right direction earlier.

To be clear:
  - sxe2 should use same devarg as mlx5 for this "allow_duplicate_pattern"
  - default should be 1
  - make sure documentation matches flag.

The patch looks much better; but there is a bisection breaking
patch ordering around 6 and 7.

After I pushed AI review to follow that, the review was the following:
(I means Claude AI, not me Stephen)

[PATCH v6 00/23] sxe2 driver feature additions

The flow-duplicate-pattern situation regressed in the wrong direction.

I reread mlx5's allow_duplicate_pattern in light of your "align to mlx5"
guidance and I was wrong to push for removal in v4 and v5. mlx5 has the same
devarg: name allow_duplicate_pattern, default 1, valid 0|1, and both values
expose a real hardware capability that rte_flow itself does not articulate
(value 0 -> EEXIST on duplicate; value 1 -> hardware queues duplicates as
shadow rules that activate when prior rules are deleted). That is not a
"standard API by boot flag" semantics violation as I had read it - it is a
hardware capability switch, and it is documented in doc/guides/nics/mlx5.rst
exactly the way sxe2's was. My v4-v5 push to drop it was based on a
misreading.

Unfortunately the v6 response is to half-remove the devarg in a way that is
worse than either keeping it or removing it cleanly:

[PATCH v6 22/23] flow-duplicate-pattern parsing removed, field left behind

Verified in the assembled tree:

  drivers/net/sxe2/sxe2_ethdev.h:138:
      uint8_t flow_dup_pattern_mode;          /* field still in struct */

  drivers/net/sxe2/sxe2_flow.c:806:
      rte_flow_error_set(error, EEXIST, ..., NULL,
                         adapter->devargs.flow_dup_pattern_mode ?
                         "Duplicate flow pattern." :
                         "Duplicate flow pattern is not allowed.");

  drivers/common/sxe2/sxe2_flow_public.h:603:
      uint8_t switch_pattern_dup_allow;       /* still in flow metadata */

The devarg's parser, default-setter, register_param_string entry, and
documentation are all gone, but the storage and the read site remain.
The field is zero-initialized and never written, so the ternary always picks
the "is not allowed" string. The hardware-capability path that the value=1
branch used to drive (setting switch_pattern_dup_allow on per-rule metadata)
has no caller now either. None of this is a correctness bug - duplicate
rules are uniformly rejected with EEXIST, which is the conservative
behaviour - but it is dead code that misleads the next reader.

Suggested fix: revert the v5-to-v6 changes that removed the parser and
docs, and instead align with mlx5:

  - Rename the devarg to allow_duplicate_pattern (underscore, matching
    mlx5's spelling).
  - Keep default = 1, matching mlx5's non-HWS default.
  - Reuse mlx5's documentation wording in sxe2.rst, adapted for the
    switch engine. The "only the first rule takes effect, the next
    activates when the first is deleted" semantic from mlx5.rst describes
    what the value=1 path does in hardware, and that wording is what was
    missing from v5's documentation.
  - Keep switch_pattern_dup_allow in flow metadata since it is what
    propagates the policy to the hardware programming path.

Or, if the cleaner path is preferred: actually finish the removal. Drop
flow_dup_pattern_mode from struct sxe2_devargs, replace the ternary in
sxe2_flow.c with the single "is not allowed" string, and drop
switch_pattern_dup_allow from struct (or wire it to a compile-time
constant if hardware programming truly needs it). Either direction is
defensible; the v6 in-between state is not.

[PATCH v6 06/23 and 07/23] patches posted in wrong order in the bundle

The bundle has 07/23 (drivers: support RSS feature) before 06/23
(net/sxe2: support L2 filtering and MAC config). git am applies them in
mbox order, so the series fails to apply: 07/23 depends on symbols 06/23
introduces. The fix is just to repost the bundle in numeric order;
contents are byte-identical to v5 for both patches. Worth fixing before
the next post because anyone running git am < bundle.mbox hits this.

Once the mbox order is fixed and the flow-duplicate-pattern situation is
resolved one way or the other, this is ready.


^ 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