* [PATCH v2 0/5] dts: add cryptodev testing support
@ 2026-02-10 19:34 Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 1/5] dts: add find float method to text parser Andrew Bailey
` (7 more replies)
0 siblings, 8 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-02-10 19:34 UTC (permalink / raw)
To: luca.vizzarro, probb, dev; +Cc: dmarx, Andrew Bailey
This patch series adds support to use the dpdk-test-crypto-perf
application. This application utilizes crypto devices that must be
listed in the format of a port under the `cryptodevs:` label. Along
with this, the application does not utilize a traffic generator and
therefore a new test suite decorator has been made to signify this.
Crypto tests must be enabled in test_run.yaml as `crypto: true`.
It is not supported to enable crypto testing along with functional
or performance testing. Specific capabilities of a crypto device
cannot be gathered at setup and therefore, non-relevant tests skip at
runtime. Finally, the application must be run with virtual functions
and the process of creating and binding them has been automated within
this series.
---
v2:
* Updated example yaml files for test_run and tests_config.
* Expanded test suite coverage.
* Resolved git problems where patches were removing code added by the
previous commit.
* Updated test suite to only run tests that are configured in the
tests_config yaml.
Andrew Bailey (5):
dts: add find float method to text parser
dts: add cryptodev package to DTS
dts: add cryptodev throughput test suite
dts: add crypto test decorator
dts: automate VFIO-PCI modprobe in node setup
.../tests.TestSuite_cryptodev_throughput.rst | 8 +
dts/api/cryptodev/__init__.py | 134 ++++
dts/api/cryptodev/config.py | 499 +++++++++++++
dts/api/cryptodev/types.py | 185 +++++
dts/configurations/test_run.example.yaml | 1 +
dts/configurations/tests_config.example.yaml | 5 +
dts/framework/config/node.py | 4 +
dts/framework/config/test_run.py | 3 +
dts/framework/params/types.py | 65 ++
dts/framework/parser.py | 28 +
dts/framework/remote_session/dpdk_shell.py | 5 +-
dts/framework/test_run.py | 5 +
dts/framework/test_suite.py | 6 +
dts/framework/testbed_model/linux_session.py | 84 +++
dts/framework/testbed_model/node.py | 10 +
dts/framework/testbed_model/os_session.py | 34 +
dts/framework/testbed_model/topology.py | 92 ++-
dts/tests/TestSuite_cryptodev_throughput.py | 691 ++++++++++++++++++
18 files changed, 1856 insertions(+), 3 deletions(-)
create mode 100644 doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
create mode 100644 dts/api/cryptodev/__init__.py
create mode 100644 dts/api/cryptodev/config.py
create mode 100644 dts/api/cryptodev/types.py
create mode 100644 dts/tests/TestSuite_cryptodev_throughput.py
--
2.50.1
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v2 1/5] dts: add find float method to text parser
2026-02-10 19:34 [PATCH v2 0/5] dts: add cryptodev testing support Andrew Bailey
@ 2026-02-10 19:34 ` Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 2/5] dts: add cryptodev package to DTS Andrew Bailey
` (6 subsequent siblings)
7 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-02-10 19:34 UTC (permalink / raw)
To: luca.vizzarro, probb, dev; +Cc: dmarx, Andrew Bailey
Currently, there is no way to gather floats from text using the parser.
Adding a new method to find floats will allow testsuites to utilize
valuable float values that are output from applications.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/framework/parser.py | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 4170cdb1dd..3075c36857 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -220,6 +220,34 @@ def find_int(
return TextParser.wrap(TextParser.find(pattern), partial(int, base=int_base))
+ @staticmethod
+ def find_float(
+ pattern: str | re.Pattern[str],
+ flags: re.RegexFlag = re.RegexFlag(0),
+ ) -> ParserFn:
+ """Makes a parser function that converts the match of :meth:`~find` to float.
+
+ This function is compatible only with a pattern containing one capturing group.
+
+ Args:
+ pattern: The regular expression pattern.
+ flags: The regular expression flags. Ignored if the given pattern is already compiled.
+
+ Raises:
+ InternalError: If the pattern does not have exactly one capturing group.
+
+ Returns:
+ ParserFn: A dictionary for the `dataclasses.field` metadata argument containing the
+ :meth:`~find` parser function wrapped by the float built-in.
+ """
+ if isinstance(pattern, str):
+ pattern = re.compile(pattern, flags)
+
+ if pattern.groups != 1:
+ raise InternalError("only one capturing group is allowed with this parser function")
+
+ return TextParser.wrap(TextParser.find(pattern), partial(float))
+
"""============ END PARSER FUNCTIONS ============"""
@classmethod
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v2 2/5] dts: add cryptodev package to DTS
2026-02-10 19:34 [PATCH v2 0/5] dts: add cryptodev testing support Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 1/5] dts: add find float method to text parser Andrew Bailey
@ 2026-02-10 19:34 ` Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 3/5] dts: add cryptodev throughput test suite Andrew Bailey
` (5 subsequent siblings)
7 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-02-10 19:34 UTC (permalink / raw)
To: luca.vizzarro, probb, dev; +Cc: dmarx, Andrew Bailey
Running the DPDK test crypto performance application is essential for
testing cryptodev performance in DTS. This application takes numerous
arguments and can be run in four modes; Throughput, Latency,
PMD-cyclecount, and Verify. The package to add in this commit allows for
this application to be run in any of these modes with user supplied
arguments.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/api/cryptodev/__init__.py | 134 +++++
dts/api/cryptodev/config.py | 499 +++++++++++++++++++
dts/api/cryptodev/types.py | 185 +++++++
dts/framework/config/node.py | 4 +
dts/framework/params/types.py | 65 +++
dts/framework/remote_session/dpdk_shell.py | 5 +-
dts/framework/test_run.py | 5 +
dts/framework/testbed_model/linux_session.py | 60 +++
dts/framework/testbed_model/node.py | 10 +
dts/framework/testbed_model/os_session.py | 30 ++
dts/framework/testbed_model/topology.py | 92 +++-
11 files changed, 1086 insertions(+), 3 deletions(-)
create mode 100644 dts/api/cryptodev/__init__.py
create mode 100644 dts/api/cryptodev/config.py
create mode 100644 dts/api/cryptodev/types.py
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
new file mode 100644
index 0000000000..9955657398
--- /dev/null
+++ b/dts/api/cryptodev/__init__.py
@@ -0,0 +1,134 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""Cryptodev-pmd non-interactive shell.
+
+Typical usage example in a TestSuite::
+
+ cryptodev = CryptodevPmd(CryptoPmdParams)
+ stats = cryptodev.run_app()
+"""
+
+import re
+from typing import TYPE_CHECKING, Any
+
+from typing_extensions import Unpack
+
+from api.cryptodev.config import CryptoPmdParams, TestType
+from api.cryptodev.types import (
+ CryptodevResults,
+ LatencyResults,
+ PmdCyclecountResults,
+ ThroughputResults,
+ VerifyResults,
+)
+from framework.context import get_ctx
+from framework.exception import RemoteCommandExecutionError, SkippedTestException
+from framework.remote_session.dpdk_shell import compute_eal_params
+
+if TYPE_CHECKING:
+ from framework.params.types import CryptoPmdParamsDict
+from pathlib import PurePath
+
+
+class Cryptodev:
+ """non-interactive cryptodev application.
+
+ Attributes:
+ _app_params: app parameters to pass to dpdk-test-crypto-perf application
+ """
+
+ _app_params: dict[str, Any]
+
+ def __init__(self, **app_params: Unpack["CryptoPmdParamsDict"]) -> None:
+ """Initialize the cryptodev application.
+
+ Args:
+ app_params: The application parameters as keyword arguments.
+
+ Raises:
+ ValueError: if the build environment is `None`
+ """
+ self._app_params = {}
+ for k, v in app_params.items():
+ if v is not None:
+ self._app_params[k] = (
+ self.vector_directory.joinpath(str(v)) if k == "test_file" else v
+ )
+ dpdk = get_ctx().dpdk.build
+ if dpdk is None:
+ raise ValueError("No DPDK build environment exists.")
+ self._path = dpdk.get_app("test-crypto-perf")
+
+ @property
+ def path(self) -> PurePath:
+ """Get the path to the cryptodev application.
+
+ Returns:
+ The path to the cryptodev application.
+ """
+ return PurePath(self._path)
+
+ @property
+ def vector_directory(self) -> PurePath:
+ """Get the path to the cryptodev vector files.
+
+ Returns:
+ The path to the cryptodev vector files.
+ """
+ return get_ctx().dpdk_build.remote_dpdk_tree_path.joinpath("app/test-crypto-perf/data/")
+
+ def run_app(self, numvfs: int | None = 0) -> list[CryptodevResults]:
+ """Run the cryptodev application with the given app parameters.
+
+ Raises:
+ SkippedTestException: If the device type is not supported on the main session.
+ RemoteCommandExecutionError: If there is an error running the command.
+
+ Returns:
+ list[CryptodevResults]: The list of parsed results for the cryptodev application.
+ """
+ try:
+ result = get_ctx().dpdk.run_dpdk_app(
+ self.path,
+ compute_eal_params(
+ CryptoPmdParams(
+ allowed_ports=get_ctx().topology.get_crypto_vfs(numvfs),
+ memory_channels=get_ctx().dpdk.config.memory_channels,
+ **self._app_params,
+ ),
+ ),
+ timeout=120,
+ )
+ except RemoteCommandExecutionError as e:
+ if "Crypto device type does not support capabilities requested" in e._command_stderr:
+ raise SkippedTestException(
+ f"{self._app_params['devtype']} does not support the requested capabilities"
+ )
+ elif "Failed to initialise requested crypto device type" in e._command_stderr:
+ raise SkippedTestException(
+ f"could not run application with devtype {self._app_params['devtype']}"
+ )
+ elif "failed to parse device" in e._command_stderr:
+ raise SkippedTestException(
+ f"dependencies missing for virtual device {self._app_params['vdevs'][0].name}"
+ )
+ raise e
+
+ regex = r"^\s+\d+.*$"
+ parser_options = re.MULTILINE
+ parser: type[CryptodevResults]
+
+ match self._app_params["ptest"]:
+ case TestType.throughput:
+ parser = ThroughputResults
+ case TestType.latency:
+ regex = r"total operations:.*time[^\n]*"
+ parser_options |= re.DOTALL
+ parser = LatencyResults
+ case TestType.pmd_cyclecount:
+ parser = PmdCyclecountResults
+ case TestType.verify:
+ parser = VerifyResults
+
+ return [parser.parse(line) for line in re.findall(regex, result.stdout, parser_options)]
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
new file mode 100644
index 0000000000..c6ec083a43
--- /dev/null
+++ b/dts/api/cryptodev/config.py
@@ -0,0 +1,499 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+"""Module containing types and parameter classes for cryptodev-pmd application."""
+
+from dataclasses import dataclass, field
+from enum import auto
+from typing import Literal
+
+from framework.params import Params, Switch
+from framework.params.eal import EalParams
+from framework.utils import StrEnum
+
+Silent = Literal[""]
+
+
+class DeviceType(StrEnum):
+ """Enum for cryptodev device types.
+
+ Attributes:
+ crypto_aesni_gcm: AES-NI GCM device type.
+ crypto_aesni_mb: AES-NI MB device type.
+ crypto_armv8: ARMv8 device type.
+ crypto_cn10k: CN10K device type.
+ crypto_cn9k: CN9K device type.
+ crypto_dpaa_sec: DPAA SEC device type.
+ crypto_dpaa2_sec: DPAA2 SEC device type.
+ crypto_kasumi: KASUMI device type.
+ crypto_mvsam: MVSAM device type.
+ crypto_null: NULL device type.
+ crypto_octeontx: OCTEONTX device type.
+ crypto_openssl: OpenSSL device type.
+ crypto_qat: QAT device type.
+ crypto_scheduler: Scheduler device type.
+ crypto_snow3g: SNOW3G device type.
+ crypto_zuc: ZUC device type.
+ """
+
+ crypto_aesni_gcm = auto()
+ crypto_aesni_mb = auto()
+ crypto_armv8 = auto()
+ crypto_cn10k = auto()
+ crypto_cn9k = auto()
+ crypto_dpaa_sec = auto()
+ crypto_dpaa2_sec = auto()
+ crypto_kasumi = auto()
+ crypto_mvsam = auto()
+ crypto_null = auto()
+ crypto_octeontx = auto()
+ crypto_openssl = auto()
+ crypto_qat = auto()
+ crypto_scheduler = auto()
+ crypto_snow3g = auto()
+ crypto_zuc = auto()
+
+
+def get_device_from_str(device: str) -> DeviceType | None:
+ """Get a device type from the given string.
+
+ Args:
+ device: the string representation of the desired device.
+
+ Returns:
+ the device as a DeviceType object.
+ """
+ for item in DeviceType:
+ if device in item.value:
+ return item
+ return None
+
+
+class OperationType(StrEnum):
+ """Enum for cryptodev operation types.
+
+ Attributes:
+ aead: AEAD operation type.
+ auth_only: Authentication only operation type.
+ auth_then_cipher: Authentication then cipher operation type.
+ cipher_only: Cipher only operation type.
+ cipher_then_auth: Cipher then authentication operation type.
+ docsis: DOCSIS operation type.
+ ecdsa_p192r1 = ECDSA P-192R1 operation type.
+ ecdsa_p224r1 = ECDSA P-224R1 operation type.
+ ecdsa_p256r1: ECDSA P-256R1 operation type.
+ ecdsa_p384r1 = ECDSA P-384R1 operation type.
+ ecdsa_p521r1 = ECDSA P-521R1 operation type.
+ eddsa_25519: EdDSA 25519 operation type.
+ modex: Modex operation type.
+ ipsec: IPsec operation type.
+ pdcp: PDCP operation type.
+ rsa: RSA operation type.
+ sm2: SM2 operation type.
+ tls_record: TLS record operation type.
+ """
+
+ aead = auto()
+ auth_only = "auth-only"
+ auth_then_cipher = "auth-then-cipher"
+ cipher_only = "cipher-only"
+ cipher_then_auth = "cipher-then-auth"
+ docsis = auto()
+ ecdsa_p192r1 = auto()
+ ecdsa_p224r1 = auto()
+ ecdsa_p256r1 = auto()
+ ecdsa_p384r1 = auto()
+ ecdsa_p521r1 = auto()
+ eddsa_25519 = auto()
+ modex = auto()
+ ipsec = auto()
+ pdcp = auto()
+ rsa = auto()
+ sm2 = auto()
+ tls_record = "tls-record"
+
+ def __str__(self) -> str:
+ """Override str to return the value."""
+ return self.value
+
+
+class CipherAlgorithm(StrEnum):
+ """Enum for cryptodev cipher algorithms.
+
+ Attributes:
+ aes_cbc: AES CBC cipher algorithm.
+ aes_ctr: AES CTR cipher algorithm.
+ aes_docsisbpi: AES DOCSIS BPI cipher algorithm.
+ aes_ecb: AES ECB cipher algorithm.
+ aes_f8: AES F8 cipher algorithm.
+ aes_xts: AES XTS cipher algorithm.
+ arc4: ARC4 cipher algorithm.
+ null: NULL cipher algorithm.
+ three_des_cbc: 3DES CBC cipher algorithm.
+ three_des_ctr: 3DES CTR cipher algorithm.
+ three_des_ecb: 3DES ECB cipher algorithm.
+ kasumi_f8: KASUMI F8 cipher algorithm.
+ snow3g_uea2: SNOW3G UEA2 cipher algorithm.
+ zuc_eea3: ZUC EEA3 cipher algorithm.
+ """
+
+ aes_cbc = "aes-cbc"
+ aes_ctr = "aes-ctr"
+ aes_docsisbpi = "aes-docsisbpi"
+ aes_ecb = "aes-ecb"
+ aes_f8 = "aes-f8"
+ aes_gcm = "aes-gcm"
+ aes_xts = "aes-xts"
+ arc4 = auto()
+ null = auto()
+ three_des_cbc = "3des-cbc"
+ three_des_ctr = "3des-ctr"
+ three_des_ecb = "3des-ecb"
+ kasumi_f8 = "kasumi-f8"
+ snow3g_uea2 = "snow3g-uea2"
+ zuc_eea3 = "zuc-eea3"
+
+ def __str__(self):
+ """Override str to return the value."""
+ return self.value
+
+
+class AuthenticationAlgorithm(StrEnum):
+ """Enum for cryptodev authentication algorithms.
+
+ Attributes:
+ aes_cbc_mac: AES CBC MAC authentication algorithm.
+ aes_cmac: AES CMAC authentication algorithm.
+ aes_gmac: AES GMAC authentication algorithm.
+ aes_xcbc_mac: AES XCBC MAC authentication algorithm.
+ kasumi_f9: KASUMI F9 authentication algorithm.
+ md5: MD5 authentication algorithm.
+ md5_hmac: MD5 HMAC authentication algorithm.
+ sha1: SHA1 authentication algorithm.
+ sha1_hmac: SHA1 HMAC authentication algorithm.
+ sha2_224: SHA2-224 authentication algorithm.
+ sha2_224_hmac: SHA2-224 HMAC authentication algorithm.
+ sha2_256: SHA2-256 authentication algorithm.
+ sha2_256_hmac: SHA2-256 HMAC authentication algorithm.
+ sha2_384: SHA2-384 authentication algorithm.
+ sha2_384_hmac: SHA2-384 HMAC authentication algorithm.
+ sha2_512: SHA2-512 authentication algorithm.
+ sha2_512_hmac: SHA2-512 HMAC authentication algorithm.
+ snow3g_uia2: SNOW3G UIA2 authentication algorithm.
+ zuc_eia3: ZUC EIA3 authentication algorithm.
+ """
+
+ aes_cbc_mac = "aes-cbc-mac"
+ aes_cmac = "aes-cmac"
+ aes_gmac = "aes-gmac"
+ aes_xcbc_mac = "aes-xcbc-mac"
+ kasumi_f9 = "kasumi-f9"
+ md5 = auto()
+ md5_hmac = "md5-hmac"
+ sha1 = auto()
+ sha1_hmac = "sha1-hmac"
+ sha2_224 = "sha2-224"
+ sha2_224_hmac = "sha2-224-hmac"
+ sha2_256 = "sha2-256"
+ sha2_256_hmac = "sha2-256-hmac"
+ sha2_384 = "sha2-384"
+ sha2_384_hmac = "sha2-384-hmac"
+ sha2_512 = "sha2-512"
+ sha2_512_hmac = "sha2-512-hmac"
+ snow3g_uia2 = "snow3g-uia2"
+ zuc_eia3 = "zuc-eia3"
+
+ def __str__(self) -> str:
+ """Override str to return the value."""
+ return self.value
+
+
+class TestType(StrEnum):
+ """Enum for cryptodev test types.
+
+ Attributes:
+ latency: Latency test type.
+ pmd_cyclecount: PMD cyclecount test type.
+ throughput: Throughput test type.
+ verify: Verify test type.
+ """
+
+ latency = auto()
+ pmd_cyclecount = "pmd-cyclecount"
+ throughput = auto()
+ verify = auto()
+
+ def __str__(self) -> str:
+ """Override str to return the value."""
+ return self.value
+
+
+class EncryptDecryptSwitch(StrEnum):
+ """Enum for cryptodev encrypt/decrypt operations.
+
+ Attributes:
+ decrypt: Decrypt operation.
+ encrypt: Encrypt operation.
+ """
+
+ decrypt = auto()
+ encrypt = auto()
+
+
+class AuthenticationOpMode(StrEnum):
+ """Enum for cryptodev authentication operation modes.
+
+ Attributes:
+ generate: Generate operation mode.
+ verify: Verify operation mode.
+ """
+
+ generate = auto()
+ verify = auto()
+
+
+class AeadAlgName(StrEnum):
+ """Enum for cryptodev AEAD algorithms.
+
+ Attributes:
+ aes_ccm: AES CCM algorithm.
+ aes_gcm: AES GCM algorithm.
+ """
+
+ aes_ccm = "aes-ccm"
+ aes_gcm = "aes-gcm"
+
+ def __str__(self):
+ """Override str to return the value."""
+ return self.value
+
+
+class AsymOpMode(StrEnum):
+ """Enum for cryptodev asymmetric operation modes.
+
+ Attributes:
+ decrypt: Decrypt operation mode.
+ encrypt: Encrypt operation mode.
+ sign: Sign operation mode.
+ verify: Verify operation mode.
+ """
+
+ decrypt = auto()
+ encrypt = auto()
+ sign = auto()
+ verify = auto()
+
+
+class PDCPDomain(StrEnum):
+ """Enum for cryptodev PDCP domains.
+
+ Attributes:
+ control: Control domain.
+ user: User domain.
+ """
+
+ control = auto()
+ user = auto()
+
+
+class RSAPrivKeyType(StrEnum):
+ """Enum for cryptodev RSA private key types.
+
+ Attributes:
+ exp: Exponent key type.
+ qt: QT key type.
+ """
+
+ exp = auto()
+ qt = auto()
+
+
+class TLSVersion(StrEnum):
+ """Enum for cryptodev TLS versions.
+
+ Attributes:
+ DTLS1_2: DTLS 1.2 version.
+ TLS1_2: TLS 1.2 version.
+ TLS1_3: TLS 1.3 version.
+ """
+
+ DTLS1_2 = "DTLS1.2"
+ TLS1_2 = "TLS1.2"
+ TLS1_3 = "TLS1.3"
+
+ def __str__(self):
+ """Override str to return the value."""
+ return self.value
+
+
+class RSAPrivKeytype(StrEnum):
+ """Enum for cryptodev RSA private key types.
+
+ Attributes:
+ exp: Exponent key type.
+ qt: QT key type.
+ """
+
+ exp = auto()
+ qt = auto()
+
+
+class BurstSizeRange:
+ """Class for burst size parameter.
+
+ Attributes:
+ burst_size: The burst size range, this list must be less than 32 elements.
+ """
+
+ burst_size: int | list[int]
+
+ def __init__(self, min: int, inc: int, max: int) -> None:
+ """Initialize the burst size range.
+
+ Raises:
+ ValueError: If the burst size range is more than 32 elements.
+ """
+ if (max - min) % inc > 32:
+ raise ValueError("Burst size range must be less than 32 elements.")
+ self.burst_size = list(range(min, max + 1, inc))
+
+
+class ListWrapper:
+ """Class for wrapping a list of integers.
+
+ One of the arguments for the cryptodev application is a list of integers. However, when
+ passing a list directly, it causes a syntax error in the command line. This class wraps
+ a list of integers and converts it to a comma-separated string for a proper command.
+ """
+
+ def __init__(self, value: list[int]) -> None:
+ """Initialize the list wrapper."""
+ self.value = value
+
+ def __str__(self) -> str:
+ """Convert the list to a comma-separated string."""
+ return ",".join(str(v) for v in self.value)
+
+
+@dataclass(slots=True, kw_only=True)
+class CryptoPmdParams(EalParams):
+ """Parameters for cryptodev-pmd application.
+
+ Attributes:
+ aead_aad_sz: set the size of AEAD AAD.
+ aead_algo: set AEAD algorithm name from class `AeadAlgName`.
+ aead_iv_sz: set the size of AEAD iv.
+ aead_key_sz: set the size of AEAD key.
+ aead_op: set AEAD operation mode from class `EncryptDecryptSwitch`.
+ asym_op: set asymmetric operation mode from class `AsymOpMode`.
+ auth_algo: set authentication algorithm name.
+ auth_aad_sz: set the size of authentication AAD.
+ auth_iv_sz: set the size of authentication iv.
+ auth_key_sz: set the size of authentication key.
+ auth_op: set authentication operation mode from class `AuthenticationOpMode`.
+ buffer_sz: Set the size of a single packet (plaintext or ciphertext in it).
+ burst_sz: Set the number of packets per burst. This can be set as a single value or
+ range of values defined by class `BurstSizeRange`. Default is 16.
+ burst_sz: Set the number of packets per burst. This can be set as a single value or
+ range of values defined by class `BurstSizeRange`. Default is 16.
+ cipher_algo: Set cipher algorithm name from class `CipherAlgorithm`.
+ cipher_iv_sz: set the size of cipher iv.
+ cipher_key_sz: set the size of cipher key.
+ cipher_op: set cipher operation mode from class `EncryptDecryptSwitch`.
+ csv_friendly: Enable test result output CSV friendly rather than human friendly.
+ desc_nb: set the number of descriptors for each crypto device.
+ devtype: Set the device name from class `DeviceType`.
+ digest_sz: set the size of digest.
+ docsis_hdr_sz: set DOCSIS header size(n) in bytes.
+ enable_sdap: enable service data adaptation protocol.
+ imix: Set the distribution of packet sizes. A list of weights must be passed, containing the
+ same number of items than buffer-sz, so each item in this list will be the weight of the
+ packet size on the same position in the buffer-sz parameter (a list has to be passed in
+ that parameter).
+ low_prio_qp_mask: set low priority queue pairs set in the hexadecimal mask. This is an
+ optional parameter, if not set all queue pairs will be on the same high priority.
+ modex_len: set modex length for asymmetric crypto perf test. Supported lengths are 60,
+ 128, 255, 448. Default length is 128.
+ optype: Set operation type from class `OpType`.
+ out_of_place: Enable out-of-place crypto operations mode.
+ pdcp_sn_sz: set PDCP sequebce number size(n) in bits. Valid values of n are 5/7/12/15/18.
+ pdcp_domain: Set PDCP domain to specify short_mac/control/user plane from class
+ `PDCPDomain`.
+ pdcp_ses_hfn_en: enable fixed session based HFN instead of per packet HFN.
+ pmd_cyclecount_delay_pmd: Add a delay (in milliseconds) between enqueue and dequeue in
+ pmd-cyclecount benchmarking mode (useful when benchmarking hardware acceleration).
+ pool_sz: Set the number if mbufs to be allocated in the mbuf pool.
+ ptest: Set performance throughput test type from class `TestType`.
+ rsa_modlen: Set RSA modulus length (in bits) for asymmetric crypto perf test.
+ To be used with RSA asymmetric crypto ops.Supported lengths are 1024, 2048, 4096, 8192.
+ Default length is 1024.
+ rsa_priv_keytype: set RSA private key type from class `RSAPrivKeytype`. To be used with RSA
+ asymmetric crypto ops.
+ segment_sz: Set the size of the segment to use, for Scatter Gather List testing. Use list of
+ values in buffer-sz in descending order if segment-sz is used. By default, it is set to
+ the size of the maximum buffer size, including the digest size, so a single segment is
+ created.
+ sessionless: Enable session-less crypto operations mode.
+ shared_session: Enable sharing sessions between all queue pairs on a single crypto PMD. This
+ can be useful for benchmarking this setup, or finding and debugging concurrency errors
+ that can occur while using sessions on multiple lcores simultaneously.
+ silent: Disable options dump.
+ test_file: Set test vector file path. See the Test Vector File chapter.
+ test_name: Set specific test name section in the test vector file.
+ tls_version: Set TLS/DTLS protocol version for perf test from class `TLSVersion`.
+ Default is TLS1.2.
+ total_ops: Set the number of total operations performed.
+ """
+
+ aead_aad_sz: int | None = field(default=None, metadata=Params.long("aead-aad-sz"))
+ aead_algo: AeadAlgName | None = field(default=None, metadata=Params.long("aead-algo"))
+ aead_iv_sz: int | None = field(default=None, metadata=Params.long("aead-iv-sz"))
+ aead_key_sz: int | None = field(default=None, metadata=Params.long("aead-key-sz"))
+ aead_op: EncryptDecryptSwitch | None = field(default=None, metadata=Params.long("aead-op"))
+ asym_op: AsymOpMode | None = field(default=None, metadata=Params.long("asym-op"))
+ auth_algo: AuthenticationAlgorithm | None = field(
+ default=None, metadata=Params.long("auth-algo")
+ )
+ auth_iv_sz: int | None = field(default=None, metadata=Params.long("auth-iv-sz"))
+ auth_key_sz: int | None = field(default=None, metadata=Params.long("auth-key-sz"))
+ auth_op: AuthenticationOpMode | None = field(default=None, metadata=Params.long("auth-op"))
+ buffer_sz: BurstSizeRange | ListWrapper | int | None = field(
+ default=None, metadata=Params.long("buffer-sz")
+ )
+ burst_sz: BurstSizeRange | ListWrapper | int | None = field(
+ default=None, metadata=Params.long("burst-sz")
+ )
+ cipher_algo: CipherAlgorithm | None = field(default=None, metadata=Params.long("cipher-algo"))
+ cipher_iv_sz: int | None = field(default=None, metadata=Params.long("cipher-iv-sz"))
+ cipher_key_sz: int | None = field(default=None, metadata=Params.long("cipher-key-sz"))
+ cipher_op: EncryptDecryptSwitch | None = field(default=None, metadata=Params.long("cipher-op"))
+ csv_friendly: Switch = field(default=None, metadata=Params.long("csv-friendly"))
+ desc_nb: int | None = field(default=None, metadata=Params.long("desc-nb"))
+ devtype: DeviceType = field(metadata=Params.long("devtype"))
+ digest_sz: int | None = field(default=None, metadata=Params.long("digest-sz"))
+ docsis_hdr_sz: int | None = field(default=None, metadata=Params.long("docsis-hdr-sz"))
+ enable_sdap: Switch = None
+ imix: int | None = field(default=None, metadata=Params.long("imix"))
+ low_prio_qp_mask: int | None = field(default=None, metadata=Params.convert_value(hex))
+ modex_len: int | None = field(default=None, metadata=Params.long("modex-len"))
+ optype: OperationType | None = field(default=None, metadata=Params.long("optype"))
+ out_of_place: Switch = None
+ pdcp_sn_sz: int | None = None
+ pdcp_domain: PDCPDomain | None = field(default=None, metadata=Params.long("pdcp-domain"))
+ pdcp_ses_hfn_en: Switch | None = field(default=None, metadata=Params.long("pdcp-ses-hfn-en"))
+ pmd_cyclecount_delay_pmd: int | None = field(
+ default=None, metadata=Params.long("pmd-cyclecount-delay-pmd")
+ )
+ pool_sz: int | None = field(default=None, metadata=Params.long("pool-sz"))
+ ptest: TestType = field(default=TestType.throughput, metadata=Params.long("ptest"))
+ rsa_modlen: int | None = field(default=None, metadata=Params.long("rsa-modlen"))
+ rsa_priv_keytype: RSAPrivKeytype | None = field(
+ default=None, metadata=Params.long("rsa-priv-keytype")
+ )
+ segment_sz: int | None = field(default=None, metadata=Params.long("segment-sz"))
+ sessionless: Switch = None
+ shared_session: Switch = None
+ silent: Silent | None = field(default="", metadata=Params.long("silent"))
+ test_file: str | None = field(default=None, metadata=Params.long("test-file"))
+ test_name: str | None = field(default=None, metadata=Params.long("test-name"))
+ tls_version: TLSVersion | None = field(default=None, metadata=Params.long("tls-version"))
+ total_ops: int | None = field(default=100000, metadata=Params.long("total-ops"))
diff --git a/dts/api/cryptodev/types.py b/dts/api/cryptodev/types.py
new file mode 100644
index 0000000000..df73a86fa4
--- /dev/null
+++ b/dts/api/cryptodev/types.py
@@ -0,0 +1,185 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""Cryptodev types module.
+
+Exposes types used in the Cryptodev API.
+"""
+
+from dataclasses import dataclass, field
+
+from framework.parser import TextParser
+
+
+@dataclass
+class CryptodevResults(TextParser):
+ """Base class for all cryptodev results."""
+
+ buffer_size: int
+
+ def __iter__(self):
+ """Iteration method to parse result objects.
+
+ Yields:
+ tuple[str, int | float]: a field name and its value.
+ """
+ for field_name in self.__dataclass_fields__:
+ yield field_name, getattr(self, field_name)
+
+
+@dataclass
+class ThroughputResults(CryptodevResults):
+ """A parser for throughput test output."""
+
+ #:
+ lcore_id: int = field(metadata=TextParser.find_int(r"\s*(\d+)"))
+ #: buffer size used in the run
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"\s+(?:\d+\s+)(\d+)"),
+ )
+ #: burst size used in the run
+ burst_size: int = field(
+ metadata=TextParser.find_int(r"\s+(?:\d+\s+){2}(\d+)"),
+ )
+ #: total packets enqueued
+ enqueued: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){3}(\d+)"))
+ #: total packets dequeued
+ dequeued: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){4}(\d+)"))
+ #: packets that failed enqueue
+ failed_enqueue: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){5}(\d+)"))
+ #: packets that failed dequeue
+ failed_dequeue: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){6}(\d+)"))
+ #: mega operations per second
+ mops: float = field(metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}([\d.]+)"))
+ #: gigabits per second
+ gbps: float = field(metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}(?:[\d.]+\s+)([\d.]+)"))
+ #: cpu cycles per buffer
+ cycles_per_buffer: float = field(
+ metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}(?:[\d.]+\s+){2}([\d.]+)")
+ )
+
+
+@dataclass
+class LatencyResults(CryptodevResults):
+ """A parser for latency test output."""
+
+ #: buffer size ran with app
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"Buf(?:.*\n\s+\d+\s+)?(?:fer size:\s+)?(\d+)"),
+ )
+ #: burst size ran with app
+ burst_size: int = field(
+ metadata=TextParser.find_int(rf"Burst(?:.*\n\s+\d+\s+){2}?(?: size:\s+)?(\d+)"),
+ )
+ #: total operations ran
+ total_ops: int = field(metadata=TextParser.find_int(r"total operations:\s+(\d+)"))
+ #: number of bursts
+ num_of_bursts: int = field(metadata=TextParser.find_int(r"Number of bursts:\s+(\d+)"))
+ #: minimum enqueued packets
+ min_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+){2}(\d+)"))
+ #: maximum enqueued packets
+ max_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+){3}(\d+)"))
+ #: average enqueued packets
+ avg_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+)(\d+)"))
+ #: total enqueued packets
+ total_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(\d+)"))
+ #: minimum dequeued packets
+ min_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+){2}(\d+)"))
+ #: maximum dequeued packets
+ max_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+){3}(\d+)"))
+ #: average dequeued packets
+ avg_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+)(\d+)"))
+ #: total dequeued packets
+ total_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(\d+)"))
+ #: minimum cycles per buffer
+ min_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+){3}([\d.]+)"))
+ #: maximum cycles per buffer
+ max_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+){2}([\d.]+)"))
+ #: average cycles per buffer
+ avg_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+)([\d.]+)"))
+ #: total cycles per buffer
+ total_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+([\d.]+)"))
+ #: mimum time in microseconds
+ min_time_us: float = field(
+ metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+){3}([\d.]+)")
+ )
+ #: maximum time in microseconds
+ max_time_us: float = field(
+ metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+){2}([\d.]+)")
+ )
+ #: average time in microseconds
+ avg_time_us: float = field(
+ metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+)([\d.]+)")
+ )
+ #: total time in microseconds
+ total_time_us: float = field(metadata=TextParser.find_float(r"time \[us\]\s+([\d.]+)"))
+
+
+@dataclass
+class PmdCyclecountResults(CryptodevResults):
+ """A parser for PMD cycle count test output."""
+
+ #:
+ lcore_id: int = field(metadata=TextParser.find_int(r"lcore\s+(?:id.*\n\s+)?(\d+)"))
+ #: buffer size used with app run
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"Buf(?:.*\n\s+(?:\d+\s+))?(?:fer size:\s+)?(\d+)"),
+ )
+ #: burst size used with app run
+ burst_size: int = field(
+ metadata=TextParser.find_int(r"Burst(?:.*\n\s+(?:\d+\s+){2})?(?: size:\s+)?(\d+)"),
+ )
+ #: packets enqueued
+ enqueued: int = field(metadata=TextParser.find_int(r"Enqueued.*\n\s+(?:\d+\s+){3}(\d+)"))
+ #: packets dequeued
+ dequeued: int = field(metadata=TextParser.find_int(r"Dequeued.*\n\s+(?:\d+\s+){4}(\d+)"))
+ #: number of enqueue packet retries
+ enqueue_retries: int = field(
+ metadata=TextParser.find_int(r"Enq Retries.*\n\s+(?:\d+\s+){5}(\d+)")
+ )
+ #: number of dequeue packet retries
+ dequeue_retries: int = field(
+ metadata=TextParser.find_int(r"Deq Retries.*\n\s+(?:\d+\s+){6}(\d+)")
+ )
+ #: number of cycles per operation
+ cycles_per_operation: float = field(
+ metadata=TextParser.find_float(r"Cycles/Op.*\n\s+(?:\d+\s+){7}([\d.]+)")
+ )
+ #: number of cycles per enqueue
+ cycles_per_enqueue: float = field(
+ metadata=TextParser.find_float(r"Cycles/Enq.*\n\s+(?:\d+\s+){7}(?:[\d.]+\s+)([\d.]+)")
+ )
+ #: number of cycles per dequeue
+ cycles_per_dequeue: float = field(
+ metadata=TextParser.find_float(r"Cycles/Deq.*\n\s+(?:\d+\s+){7}(?:[\d.]+\s+){2}([\d.]+)"),
+ )
+
+
+@dataclass
+class VerifyResults(CryptodevResults):
+ """A parser for verify test output."""
+
+ #:
+ lcore_id: int = field(metadata=TextParser.find_int(r"lcore\s+(?:id.*\n\s+)?(\d+)"))
+ #: buffer size ran with app
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"Buf(?:.*\n\s+(?:\d+\s+))?(?:fer size:\s+)?(\d+)"),
+ )
+ #: burst size ran with app
+ burst_size: int = field(
+ metadata=TextParser.find_int(r"Burst(?:.*\n\s+(?:\d+\s+){2})?(?: size:\s+)?(\d+)"),
+ )
+ #: number of packets enqueued
+ enqueued: int = field(metadata=TextParser.find_int(r"Enqueued.*\n\s+(?:\d+\s+){3}(\d+)"))
+ #: number of packets dequeued
+ dequeued: int = field(metadata=TextParser.find_int(r"Dequeued.*\n\s+(?:\d+\s+){4}(\d+)"))
+ #: number of packets enqueue failed
+ failed_enqueued: int = field(
+ metadata=TextParser.find_int(r"Failed Enq.*\n\s+(?:\d+\s+){5}(\d+)")
+ )
+ #: number of packets dequeue failed
+ failed_dequeued: int = field(
+ metadata=TextParser.find_int(r"Failed Deq.*\n\s+(?:\d+\s+){6}(\d+)")
+ )
+ #: total number of failed operations
+ failed_ops: int = field(metadata=TextParser.find_int(r"Failed Ops.*\n\s+(?:\d+\s+){7}(\d+)"))
diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
index 438a1bdc8f..36068c1ef8 100644
--- a/dts/framework/config/node.py
+++ b/dts/framework/config/node.py
@@ -70,6 +70,10 @@ class NodeConfiguration(FrozenModel):
hugepages: HugepageConfiguration | None = Field(None, alias="hugepages_2mb")
#: The ports that can be used in testing.
ports: list[PortConfig] = Field(min_length=1)
+ #: The pci info used by crypto devices
+ cryptodevs: list[PortConfig] = Field(default=[], min_length=0)
+ #: The crypto driver used by crypto devices
+ crypto_driver: str | None = None
@model_validator(mode="after")
def verify_unique_port_names(self) -> Self:
diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py
index 5bc4bd37d9..3c7650474c 100644
--- a/dts/framework/params/types.py
+++ b/dts/framework/params/types.py
@@ -15,6 +15,23 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
from pathlib import PurePath
from typing import TypedDict
+from api.cryptodev.config import (
+ AeadAlgName,
+ AsymOpMode,
+ AuthenticationAlgorithm,
+ AuthenticationOpMode,
+ BurstSizeRange,
+ CipherAlgorithm,
+ DeviceType,
+ EncryptDecryptSwitch,
+ ListWrapper,
+ OperationType,
+ PDCPDomain,
+ RSAPrivKeytype,
+ Silent,
+ TestType,
+ TLSVersion,
+)
from api.testpmd.config import (
AnonMempoolAllocationMode,
EthPeer,
@@ -55,6 +72,54 @@ class EalParamsDict(TypedDict, total=False):
other_eal_param: Params | None
+class CryptoPmdParamsDict(EalParamsDict, total=False):
+ """:class:`TypedDict` equivalent of :class:`~.cryptodev.CryptoPmdParams`."""
+
+ aead_aad_sz: int | None
+ aead_algo: AeadAlgName | None
+ aead_iv_sz: int | None
+ aead_key_sz: int | None
+ aead_op: EncryptDecryptSwitch | None
+ asym_op: AsymOpMode | None
+ auth_algo: AuthenticationAlgorithm | None
+ auth_iv_sz: int | None
+ auth_key_sz: int | None
+ auth_op: AuthenticationOpMode | None
+ buffer_sz: BurstSizeRange | ListWrapper | int | None
+ burst_sz: BurstSizeRange | ListWrapper | int | None
+ cipher_algo: CipherAlgorithm | None
+ cipher_iv_sz: int | None
+ cipher_key_sz: int | None
+ cipher_op: EncryptDecryptSwitch | None
+ csv_friendly: Switch
+ desc_nb: int | None
+ devtype: DeviceType | None
+ digest_sz: int | None
+ docsis_hdr_sz: int | None
+ enable_sdap: Switch
+ imix: int | None
+ low_prio_qp_mask: int | None
+ modex_len: int | None
+ optype: OperationType | None
+ out_of_place: Switch
+ pdcp_sn_sz: int | None
+ pdcp_domain: PDCPDomain | None
+ pdcp_ses_hfn_en: Switch | None
+ pmd_cyclecount_delay_pmd: int | None
+ pool_sz: int | None
+ ptest: TestType
+ rsa_modlen: int | None
+ rsa_priv_keytype: RSAPrivKeytype | None
+ segment_sz: int | None
+ sessionless: Switch
+ shared_session: Switch
+ silent: Silent | None
+ test_file: str | None
+ test_name: str | None
+ tls_version: TLSVersion | None
+ total_ops: int | None
+
+
class TestPmdParamsDict(EalParamsDict, total=False):
""":class:`TypedDict` equivalent of :class:`~.testpmd.TestPmdParams`."""
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index 51b97d4ff6..b94d336d4e 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -46,7 +46,10 @@ def compute_eal_params(
params.prefix = prefix
if params.allowed_ports is None:
- params.allowed_ports = ctx.topology.sut_dpdk_ports
+ if ctx.topology.crypto_vf_ports:
+ params.allowed_ports = [ctx.topology.crypto_vf_ports[0]]
+ else:
+ params.allowed_ports = ctx.topology.sut_dpdk_ports
return params
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index ff0a12c9ce..36e6c5a44c 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -349,6 +349,9 @@ def next(self) -> State | None:
if test_run.config.use_virtual_functions:
test_run.ctx.topology.instantiate_vf_ports()
+ if test_run.ctx.sut_node.cryptodevs:
+ test_run.ctx.topology.instantiate_crypto_vf_ports()
+ test_run.ctx.topology.configure_cryptodevs("dpdk")
test_run.ctx.topology.configure_ports("sut", "dpdk")
if test_run.ctx.func_tg:
@@ -442,6 +445,8 @@ def next(self) -> State | None:
"""Next state."""
if self.test_run.config.use_virtual_functions:
self.test_run.ctx.topology.delete_vf_ports()
+ if self.test_run.ctx.sut_node.cryptodevs:
+ self.test_run.ctx.topology.delete_crypto_vf_ports()
self.test_run.ctx.shell_pool.terminate_current_pool()
if self.test_run.ctx.func_tg and self.test_run.ctx.func_tg.is_setup:
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 711d4d97c3..a9eb8b693d 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -10,6 +10,7 @@
"""
import json
+import re
from collections.abc import Iterable
from functools import cached_property
from pathlib import PurePath
@@ -223,6 +224,32 @@ def devbind_script_path(self) -> PurePath:
"""
raise InternalError("Accessed devbind script path before setup.")
+ def create_crypto_vfs(self, pf_port: list[Port]) -> None:
+ """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ for port in pf_port:
+ self.delete_crypto_vfs(port)
+ for port in pf_port:
+ sys_bus_path = f"/sys/bus/pci/drivers/{port.config.os_driver}/{port.pci}".replace(
+ ":", "\\:"
+ )
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
+ )
+ self.send_command(
+ f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
+ )
+
+ self.refresh_lshw()
+
def create_vfs(self, pf_port: Port) -> None:
"""Overrides :meth:`~.os_session.OSSession.create_vfs`.
@@ -239,6 +266,25 @@ def create_vfs(self, pf_port: Port) -> None:
self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
self.refresh_lshw()
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/drivers/{pf_port.config.os_driver}/{pf_port.pci}".replace(
+ ":", "\\:"
+ )
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if curr_num_vfs == 0:
+ return self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
+ self.send_command(
+ f"dpdk-devbind.py -u {pf_port.pci}".replace(":", "\\:"), privileged=True, timeout=30
+ )
+ self.send_command(
+ f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
+ privileged=True,
+ )
+ self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
+
def delete_vfs(self, pf_port: Port) -> None:
"""Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
@@ -250,6 +296,20 @@ def delete_vfs(self, pf_port: Port) -> None:
else:
self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/drivers/{pf_port.config.os_driver}/{pf_port.pci}".replace(
+ ":", "\\:"
+ )
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ f"readlink {sys_bus_path}/virtfn*",
+ privileged=True,
+ )
+ return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
+ return []
+
def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
"""Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
index 6c3e63a2a1..5c3e02a320 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -54,6 +54,8 @@ class Node:
arch: Architecture
lcores: list[LogicalCore]
ports: list[Port]
+ cryptodevs: list[Port]
+ crypto_driver: str | None
_logger: DTSLogger
_other_sessions: list[OSSession]
_node_info: OSSessionInfo | None
@@ -82,6 +84,14 @@ def __init__(self, node_config: NodeConfiguration) -> None:
self._other_sessions = []
self._setup = False
self.ports = [Port(self, port_config) for port_config in self.config.ports]
+ self.cryptodevs = [
+ Port(self, cryptodev_config) for cryptodev_config in self.config.cryptodevs
+ ]
+ self.crypto_driver = self.config.crypto_driver
+ if self.cryptodevs:
+ self.main_session.load_vfio(self.cryptodevs[0])
+ elif self.ports and self.ports[0].config.os_driver_for_dpdk == "vfio-pci":
+ self.main_session.load_vfio(self.ports[0])
self._logger.info(f"Created node: {self.name}")
def setup(self) -> None:
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index b94c3e527b..2eeeea6967 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -615,6 +615,14 @@ def configure_port_mtu(self, mtu: int, port: Port) -> None:
port: Port to set `mtu` on.
"""
+ @abstractmethod
+ def create_crypto_vfs(self, pf_ports: list[Port]) -> None:
+ """Creatues virtual functions for each port in 'pf_ports'.
+
+ Checks how many virtual functions each crypto device in 'pf_ports' supports, and creates
+ that number of VFs on the port.
+ """
+
@abstractmethod
def create_vfs(self, pf_port: Port) -> None:
"""Creates virtual functions for `pf_port`.
@@ -630,6 +638,16 @@ def create_vfs(self, pf_port: Port) -> None:
maximum for `pf_port`.
"""
+ @abstractmethod
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Deletes virtual functions for crypto device 'pf_port'.
+
+ Checks how many virtual functions are currently active and removes all if any exist.
+
+ Args:
+ pf_port: The crypto device port to delete virtual functions on.
+ """
+
@abstractmethod
def delete_vfs(self, pf_port: Port) -> None:
"""Deletes virtual functions for `pf_port`.
@@ -656,3 +674,15 @@ def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
A list containing all of the PCI addresses of the VFs on the port. If the port has no
VFs then the list will be empty.
"""
+
+ @abstractmethod
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Find the PCI addresses of all virtual functions (VFs) of a crypto device on `pf_port`.
+
+ Args:
+ pf_port: The port to find the VFs on.
+
+ Returns:
+ A list containing all of the PCI addresses of the VFs on the port. If the port has no
+ VFs then the list will be empty.
+ """
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
index 13b4b58a74..a64b258c79 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -8,6 +8,7 @@
The link information then implies what type of topology is available.
"""
+import re
from collections import defaultdict
from collections.abc import Iterator
from dataclasses import dataclass
@@ -58,6 +59,8 @@ class Topology:
tg_ports: list[Port]
pf_ports: list[Port]
vf_ports: list[Port]
+ crypto_pf_ports: list[Port]
+ crypto_vf_ports: list[Port]
@classmethod
def from_port_links(cls, port_links: Iterator[PortLink]) -> Self:
@@ -85,7 +88,7 @@ def from_port_links(cls, port_links: Iterator[PortLink]) -> Self:
msg = "More than two links in a topology are not supported."
raise ConfigurationError(msg)
- return cls(type, sut_ports, tg_ports, [], [])
+ return cls(type, sut_ports, tg_ports, [], [], [], [])
def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node, list[Port]]:
"""Retrieve node and its ports for the current topology.
@@ -105,6 +108,42 @@ def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node,
msg = f"Invalid node `{node_identifier}` given."
raise InternalError(msg)
+ def get_crypto_vfs(self, numvfs: int | None = None) -> list[Port]:
+ """Retrieve virtual functions from active crypto vfs.
+
+ If numvfs is `None`, returns all crypto virtual functions. Otherwise, returns
+ `numvfs` number of virtual functions per physical function rounded down. If a number of
+ `numvfs` is less than the number of physical functions, one virtual function is returned.
+
+ Args:
+ numvfs: Number of virtual functions to return if provided.
+
+ Returns:
+ List containing the requested number of vfs, evenly distributed among
+ physcal functions rounded down.
+ """
+ return_list = []
+ device = 1
+ if len(self.crypto_pf_ports) == 0:
+ return []
+ if numvfs is None:
+ print("returning all")
+ return_list.extend(self.crypto_vf_ports)
+ return return_list
+ mod = numvfs // len(self.crypto_pf_ports)
+ if numvfs < len(self.crypto_pf_ports):
+ print("Returning first")
+ return_list.extend([self.crypto_vf_ports[0]])
+ return return_list
+ for i in range(mod):
+ if i % 8 == 0 and i != 0:
+ device += 1
+ return_list.extend(
+ filter(lambda x: re.search(rf"0000:\d+:0{device}.{i}", x.pci), self.crypto_vf_ports)
+ )
+ print(f"Returning: {return_list}")
+ return return_list
+
def setup(self) -> None:
"""Setup topology ports.
@@ -145,6 +184,34 @@ def _setup_ports(self, node_identifier: NodeIdentifier) -> None:
f"for port {port.name} in node {node.name}."
)
+ def instantiate_crypto_vf_ports(self) -> None:
+ """Create max number of virtual functions allowed on the SUT node.
+
+ Raises:
+ InternalError: If crypto virtual functions could not be created on a port.
+ """
+ from framework.context import get_ctx
+
+ ctx = get_ctx()
+
+ for port in ctx.sut_node.cryptodevs:
+ self.crypto_pf_ports.append(port)
+ self.delete_crypto_vf_ports()
+
+ ctx.sut_node.main_session.create_crypto_vfs(self.crypto_pf_ports)
+ for port in self.crypto_pf_ports:
+ addr_list = ctx.sut_node.main_session.get_pci_addr_of_crypto_vfs(port)
+ if addr_list == []:
+ raise InternalError(f"Failed to create crypto virtual function on port {port.pci}")
+ for addr in addr_list:
+ vf_config = PortConfig(
+ name=f"{port.name}-crypto-vf-{addr}",
+ pci=addr,
+ os_driver_for_dpdk=port.config.os_driver_for_dpdk,
+ os_driver=port.config.os_driver,
+ )
+ self.crypto_vf_ports.append(Port(node=port.node, config=vf_config))
+
def instantiate_vf_ports(self) -> None:
"""Create, setup, and add virtual functions to the list of ports on the SUT node.
@@ -189,6 +256,27 @@ def delete_vf_ports(self) -> None:
self.sut_ports.clear()
self.sut_ports.extend(self.pf_ports)
+ def delete_crypto_vf_ports(self) -> None:
+ """Delete crypto virtual functions from the SUT node during test run teardown."""
+ from framework.context import get_ctx
+
+ ctx = get_ctx()
+
+ for port in self.crypto_pf_ports:
+ ctx.sut_node.main_session.delete_crypto_vfs(port)
+ self.sut_ports.clear()
+ self.sut_ports.extend(self.pf_ports)
+
+ def configure_cryptodevs(self, driver: DriverKind):
+ """Configure the crypto device virtual functoins on the sut and bind to specified driver.
+
+ Args:
+ driver: The driver to bind the cryptofunctions
+ """
+ from framework.context import get_ctx
+
+ self._bind_ports_to_drivers(get_ctx().sut_node, self.crypto_vf_ports, driver)
+
def configure_ports(
self, node_identifier: NodeIdentifier, drivers: DriverKind | tuple[DriverKind, ...]
) -> None:
@@ -227,7 +315,7 @@ def _bind_ports_to_drivers(
for port_id, port in enumerate(ports):
driver_kind = drivers[port_id] if isinstance(drivers, tuple) else drivers
desired_driver = port.driver_by_kind(driver_kind)
- if port.current_driver != desired_driver:
+ if port in self.crypto_vf_ports or port.current_driver != desired_driver:
driver_to_ports[desired_driver].append(port)
for driver_name, ports in driver_to_ports.items():
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v2 3/5] dts: add cryptodev throughput test suite
2026-02-10 19:34 [PATCH v2 0/5] dts: add cryptodev testing support Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 1/5] dts: add find float method to text parser Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 2/5] dts: add cryptodev package to DTS Andrew Bailey
@ 2026-02-10 19:34 ` Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 4/5] dts: add crypto test decorator Andrew Bailey
` (4 subsequent siblings)
7 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-02-10 19:34 UTC (permalink / raw)
To: luca.vizzarro, probb, dev; +Cc: dmarx, Andrew Bailey
Add cryptodev performance test suite. This test covers throughput
performance metrics of supplied crypto devices using the
dpdk-test-crypto-perf application. The results of this application will
be compared against user supplied performance metrics and fail if signs
of regression are apparent.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
.../tests.TestSuite_cryptodev_throughput.rst | 8 +
dts/configurations/tests_config.example.yaml | 5 +
dts/tests/TestSuite_cryptodev_throughput.py | 689 ++++++++++++++++++
3 files changed, 702 insertions(+)
create mode 100644 doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
create mode 100644 dts/tests/TestSuite_cryptodev_throughput.py
diff --git a/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst b/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
new file mode 100644
index 0000000000..5df5e7547e
--- /dev/null
+++ b/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+cryptodev_throughput Test Suite
+===============================
+
+.. automodule:: tests.TestSuite_cryptodev_throughput
+ :members:
+ :show-inheritance:
diff --git a/dts/configurations/tests_config.example.yaml b/dts/configurations/tests_config.example.yaml
index 64fa630aa0..c953affa7a 100644
--- a/dts/configurations/tests_config.example.yaml
+++ b/dts/configurations/tests_config.example.yaml
@@ -15,3 +15,8 @@ hello_world:
# num_descriptors: 1024
# expected_mpps: 1.0
# delta_tolerance: 0.05
+# cryptodev_throughput:
+# snow3g_uea2_snow3g_uia2: # Add the name of the test suite you would like to run
+# - buff_size: 64
+# gbps: 5.0 # Set gigabits per second according to your devices throughput for this given buffer size
+# delta_tolerance: 0.05
\ No newline at end of file
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
new file mode 100644
index 0000000000..ef8fb026c2
--- /dev/null
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -0,0 +1,689 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""DPDK cryptodev performance test suite.
+
+The main goal of this testsuite is to utilize the dpdk-test-cryptodev application to gather
+performance metrics for various cryptographic operations supported by DPDK cryptodev-pmd.
+It will then compare the results against predefined baseline given in the test_config file to
+ensure performance standards are met.
+"""
+
+from api.capabilities import (
+ LinkTopology,
+ requires_link_topology,
+)
+from api.cryptodev import Cryptodev
+from api.cryptodev.config import (
+ AeadAlgName,
+ AuthenticationAlgorithm,
+ AuthenticationOpMode,
+ CipherAlgorithm,
+ DeviceType,
+ EncryptDecryptSwitch,
+ ListWrapper,
+ OperationType,
+ TestType,
+ get_device_from_str,
+)
+from api.cryptodev.types import (
+ CryptodevResults,
+)
+from api.test import verify
+from framework.context import get_ctx
+from framework.exception import SkippedTestException
+from framework.test_suite import BaseConfig, TestSuite, func_test
+from framework.testbed_model.virtual_device import VirtualDevice
+
+config_list: list[dict[str, int | float | str]] = [
+ {"buff_size": 64, "gbps": 1.00},
+ {"buff_size": 512, "gbps": 1.00},
+ {"buff_size": 2048, "gbps": 1.00},
+]
+
+
+class Config(BaseConfig):
+ """Performance test metrics.
+
+ Attributes:
+ delta_tolerance: The allowed tolerance below a given baseline.
+ throughput_test_parameters: The test parameters to use in the test suite.
+ """
+
+ delta_tolerance: float = 0.05
+
+ throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = {
+ "aes_cbc": config_list,
+ "aes_cbc_sha1": config_list,
+ "aes_cbc_sha2": config_list,
+ "aes_cbc_sha2_digest_16": config_list,
+ "aead_aes_gcm": config_list,
+ "aes_docsisbpi": config_list,
+ "aes_sha1": config_list,
+ "snow3g_uea2_snow3g_uia2": config_list,
+ "zuc_eea3_zuc_eia3": config_list,
+ "kasumi_f8_kasumi_f9": config_list,
+ "open_ssl_vdev": config_list,
+ "aesni_mb_vdev": config_list,
+ "aesni_gcm_vdev": config_list,
+ "kasumi_vdev": config_list,
+ "zuc_vdev": config_list,
+ "snow3g_vdev": config_list,
+ }
+
+
+@requires_link_topology(LinkTopology.NO_LINK)
+class TestCryptodevThroughput(TestSuite):
+ """DPDK Crypto Device Testing Suite."""
+
+ config: Config
+
+ def set_up_suite(self) -> None:
+ """Set up the test suite."""
+ self.throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = (
+ self.config.throughput_test_parameters
+ )
+ self.delta_tolerance: float = self.config.delta_tolerance
+ device: DeviceType | None = get_device_from_str(str(get_ctx().sut_node.crypto_driver))
+ self.driver: DeviceType = device if device else DeviceType.crypto_qat
+ self.buffer_sizes = {}
+
+ for k, v in self.throughput_test_parameters.items():
+ self.buffer_sizes[k] = ListWrapper([int(run["buff_size"]) for run in v])
+
+ def _print_stats(self, test_vals: list[dict[str, int | float | str]]) -> None:
+ element_len = len("Delta Tolerance")
+ border_len = (element_len + 1) * (len(test_vals[0]))
+
+ 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}")
+
+ for test_val in test_vals:
+ for k, v in test_val.items():
+ print(f"|{v:<{element_len}}", end="")
+ print(f"|\n{'='*border_len}")
+
+ def _verify_throughput(
+ self,
+ results: list[CryptodevResults],
+ key: str,
+ ) -> list[dict[str, int | float | str]]:
+ result_list: list[dict[str, int | float | str]] = []
+
+ for result in results:
+ result_dict = {}
+ parameters: dict[str, int | float | str] = list(
+ filter(
+ lambda x: x["buff_size"] == result.buffer_size,
+ self.throughput_test_parameters[key],
+ )
+ )[0]
+ test_result = True
+ for arg, target_val in parameters.items():
+ match arg:
+ case "buff_size":
+ result_dict["Buffer Size"] = target_val
+ continue
+ case "gbps":
+ # result did not meet the given gbps parameter, check if within delta.
+ if target_val > getattr(result, "gbps"):
+ delta = round((1 - (getattr(result, "gbps") / target_val)), 5)
+ if delta > self.delta_tolerance:
+ test_result = False
+ else:
+ delta = round((1 - target_val / getattr(result, arg)), 5)
+ result_dict["gbps delta"] = delta
+ result_dict["delta tolerance"] = self.delta_tolerance
+ result_dict["gbps"] = getattr(result, "gbps")
+ result_dict["gbps target"] = target_val
+ result_dict["passed"] = "PASS" if test_result else "FAIL"
+ result_list.append(result_dict)
+ return result_list
+
+ @func_test
+ def aes_cbc(self) -> None:
+ """aes_cbc test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_only,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aes_cbc"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ def aes_cbc_sha1(self) -> None:
+ """aes_cbc_sha1 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc_sha1" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha1_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ digest_sz=12,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aes_cbc_sha1"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc_sha1")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ def aes_cbc_sha2(self) -> None:
+ """aes_cbc_sha2 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc_sha2" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha2_256_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ digest_sz=32,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aes_cbc_sha2"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc_sha2")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def aes_cbc_sha2_digest_16(self) -> None:
+ """aes_cbc_sha2_digest_16 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc_sha2_digest_16" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha2_256_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ digest_sz=16,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aes_cbc_sha2_digest_16"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc_sha2_digest_16")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def aead_aes_gcm(self) -> None:
+ """aead_aes_gcm test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aead_aes_gcm" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.aead,
+ aead_algo=AeadAlgName.aes_gcm,
+ aead_op=EncryptDecryptSwitch.encrypt,
+ aead_key_sz=16,
+ aead_iv_sz=12,
+ aead_aad_sz=16,
+ digest_sz=16,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aead_aes_gcm"],
+ )
+ results = self._verify_throughput(app.run_app(), "aead_aes_gcm")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def aes_docsisbpi(self) -> None:
+ """aes_docsiscpi test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_docsisbpi" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_only,
+ cipher_algo=CipherAlgorithm.aes_docsisbpi,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=32,
+ cipher_iv_sz=16,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aes_docsisbpi"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_docsisbpi")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @crypto_test
+ def aes_sha1(self) -> None:
+ """aes_sha1 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_sha1" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.auth_only,
+ auth_algo=AuthenticationAlgorithm.sha1_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ digest_sz=12,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aes_sha1"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_sha1")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def snow3g_uea2_snow3g_uia2(self) -> None:
+ """snow3g_uea2_snow3g_uia2 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "snow3g_uea2_snow3g_uia2" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.snow3g_uea2,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.snow3g_uia2,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["snow3g_uea2_snow3g_uia2"],
+ )
+ results = self._verify_throughput(app.run_app(), "snow3g_uea2_snow3g_uia2")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def zuc_eea3_zuc_eia3(self) -> None:
+ """zuc_eea3_zuc_eia3 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "zuc_eea3_zuc_eia3" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.zuc_eea3,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.zuc_eia3,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["zuc_eea3_zuc_eia3"],
+ )
+ results = self._verify_throughput(app.run_app(), "zuc_eea3_zuc_eia3")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def kasumi_f8_kasumi_f9(self) -> None:
+ """kasumi_f8 kasumi_f9 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "kasumi_f8_kasumi_f9" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.kasumi_f8,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=8,
+ auth_algo=AuthenticationAlgorithm.kasumi_f9,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ digest_sz=4,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["kasumi_f8_kasumi_f9"],
+ )
+ results = self._verify_throughput(app.run_app(), "kasumi_f8_kasumi_f9")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ # BEGIN VDEV TESTS
+
+
+ def aesni_mb_vdev(self) -> None:
+ """aesni_mb virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_aesni_mb and supplied buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aesni_mb_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ vdevs=[VirtualDevice("aesni_mb")],
+ ptest=TestType.throughput,
+ devtype=DeviceType.crypto_aesni_mb,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha1_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ digest_sz=20,
+ total_ops=10_000_000,
+ burst_sz=32,
+ buffer_sz=self.buffer_sizes["aesni_mb_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "aesni_mb_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def aesni_gcm_vdev(self):
+ """aesni_gcm virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_aesni_gcm and supplied buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aesni_gcm_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ vdevs=[VirtualDevice("aesni_gcm")],
+ ptest=TestType.throughput,
+ devtype=DeviceType.crypto_aesni_gcm,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_gcm,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=12,
+ auth_algo=AuthenticationAlgorithm.aes_gmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ digest_sz=4,
+ total_ops=10_000_000,
+ burst_sz=32,
+ buffer_sz=self.buffer_sizes["aesni_gcm_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "aesni_gcm_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def kasumi_vdev(self) -> None:
+ """Kasmumi virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_kasumi and supplied buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "kasumi_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ vdevs=[VirtualDevice("crypto_kasumi")],
+ ptest=TestType.throughput,
+ devtype=DeviceType.crypto_kasumi,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.kasumi_f8,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=8,
+ auth_algo=AuthenticationAlgorithm.kasumi_f9,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ digest_sz=4,
+ total_ops=10_000_000,
+ burst_sz=32,
+ buffer_sz=self.buffer_sizes["kasumi_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "kasumi_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def snow3g_vdev(self) -> None:
+ """snow3g virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_snow3g and supplied buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "snow3g_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ vdevs=[VirtualDevice("crypto_snow3g")],
+ ptest=TestType.throughput,
+ devtype=DeviceType.crypto_snow3g,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.snow3g_uea2,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.snow3g_uia2,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ digest_sz=4,
+ total_ops=10_000_000,
+ burst_sz=32,
+ buffer_sz=self.buffer_sizes["snow3g_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "snow3g_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(
+ result["passed"] == "PASS",
+ f"Gbps and MOps were {result["gbps delta"]} below baseline",
+ )
+
+ @func_test
+ def zuc_vdev(self) -> None:
+ """Zuc virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_zuc and supplied buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "zuc_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ vdevs=[VirtualDevice("crypto_zuc")],
+ ptest=TestType.throughput,
+ devtype=DeviceType.crypto_zuc,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.zuc_eea3,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.zuc_eia3,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ digest_sz=4,
+ total_ops=10_000_000,
+ burst_sz=32,
+ buffer_sz=self.buffer_sizes["zuc_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "zuc_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gpbs fell below delta tolerance")
+
+ @func_test
+ def open_ssl_vdev(self) -> None:
+ """open_ssl virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "open_ssl_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ vdevs=[VirtualDevice("crypto_openssl")],
+ ptest=TestType.throughput,
+ devtype=DeviceType.crypto_openssl,
+ optype=OperationType.aead,
+ aead_algo=AeadAlgName.aes_gcm,
+ aead_op=EncryptDecryptSwitch.encrypt,
+ aead_aad_sz=16,
+ aead_key_sz=16,
+ aead_iv_sz=16,
+ digest_sz=16,
+ total_ops=100_000,
+ buffer_sz=self.buffer_sizes["open_ssl_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "open_ssl_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(
+ result["passed"] == "PASS",
+ "gbps fell below delta tolerance",
+ )
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v2 4/5] dts: add crypto test decorator
2026-02-10 19:34 [PATCH v2 0/5] dts: add cryptodev testing support Andrew Bailey
` (2 preceding siblings ...)
2026-02-10 19:34 ` [PATCH v2 3/5] dts: add cryptodev throughput test suite Andrew Bailey
@ 2026-02-10 19:34 ` Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 5/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
` (3 subsequent siblings)
7 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-02-10 19:34 UTC (permalink / raw)
To: luca.vizzarro, probb, dev; +Cc: dmarx, Andrew Bailey
Currently, a test case is decorated to signify whether to use Scapy or
TREX. This change allows test suites that do not use a traffic generator
to avoid the overhead of initializing either one.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/configurations/test_run.example.yaml | 1 +
dts/framework/config/test_run.py | 3 +++
dts/framework/test_run.py | 2 +-
dts/framework/test_suite.py | 6 +++++
dts/tests/TestSuite_cryptodev_throughput.py | 30 +++++++++++----------
5 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/dts/configurations/test_run.example.yaml b/dts/configurations/test_run.example.yaml
index c8035fccf0..3eaffc2aa4 100644
--- a/dts/configurations/test_run.example.yaml
+++ b/dts/configurations/test_run.example.yaml
@@ -31,6 +31,7 @@ func_traffic_generator:
# config: "/opt/trex_config/trex_config.yaml" # Additional configuration files. (Leave blank if not required)
perf: false # disable performance testing
func: true # enable functional testing
+crypto: false # disable cryptographic testing
use_virtual_functions: false # use virtual functions (VFs) instead of physical functions
skip_smoke_tests: true # optional
# by removing the `test_suites` field, this test run will run every test suite available
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 6c292a3675..39f2c7cdf6 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -481,6 +481,8 @@ class TestRunConfiguration(FrozenModel):
perf: bool
#: Whether to run functional tests.
func: bool
+ #: Whether to run cryptography tests.
+ crypto: bool
#: Whether to run the testing with virtual functions instead of physical functions
use_virtual_functions: bool
#: Whether to skip smoke tests.
@@ -522,6 +524,7 @@ def filter_tests(
for tt in t.test_cases
if (tt.test_type is TestCaseType.FUNCTIONAL and self.func)
or (tt.test_type is TestCaseType.PERFORMANCE and self.perf)
+ or (tt.test_type is TestCaseType.CRYPTO and self.crypto)
),
)
for t in test_suites
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 36e6c5a44c..ada628c59e 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -349,7 +349,7 @@ def next(self) -> State | None:
if test_run.config.use_virtual_functions:
test_run.ctx.topology.instantiate_vf_ports()
- if test_run.ctx.sut_node.cryptodevs:
+ if test_run.ctx.sut_node.cryptodevs and test_run.config.crypto:
test_run.ctx.topology.instantiate_crypto_vf_ports()
test_run.ctx.topology.configure_cryptodevs("dpdk")
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 9c57e343ac..e86096cefe 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -174,6 +174,8 @@ def filter_test_cases(
perf_test_cases.add(test_case)
case TestCaseType.FUNCTIONAL:
func_test_cases.add(test_case)
+ case TestCaseType.CRYPTO:
+ pass
if test_case_sublist_copy:
raise ConfigurationError(
@@ -279,6 +281,8 @@ class TestCaseType(Enum):
FUNCTIONAL = auto()
#:
PERFORMANCE = auto()
+ #:
+ CRYPTO = auto()
class TestCase(TestProtocol, Protocol[TestSuiteMethodType]):
@@ -331,6 +335,8 @@ def _decorator(func: TestSuiteMethodType) -> type[TestCase]:
func_test: Callable = TestCase.make_decorator(TestCaseType.FUNCTIONAL)
#: The decorator for performance test cases.
perf_test: Callable = TestCase.make_decorator(TestCaseType.PERFORMANCE)
+#: The decorator for cryptography test cases.
+crypto_test: Callable = TestCase.make_decorator(TestCaseType.CRYPTO)
@dataclass
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index ef8fb026c2..10e3c7ff5e 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -32,7 +32,7 @@
from api.test import verify
from framework.context import get_ctx
from framework.exception import SkippedTestException
-from framework.test_suite import BaseConfig, TestSuite, func_test
+from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
config_list: list[dict[str, int | float | str]] = [
@@ -142,7 +142,7 @@ def _verify_throughput(
result_list.append(result_dict)
return result_list
- @func_test
+ @crypto_test
def aes_cbc(self) -> None:
"""aes_cbc test.
@@ -172,6 +172,7 @@ def aes_cbc(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+ @crypto_test
def aes_cbc_sha1(self) -> None:
"""aes_cbc_sha1 test.
@@ -204,6 +205,7 @@ def aes_cbc_sha1(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+ @crypto_test
def aes_cbc_sha2(self) -> None:
"""aes_cbc_sha2 test.
@@ -237,7 +239,7 @@ def aes_cbc_sha2(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aes_cbc_sha2_digest_16(self) -> None:
"""aes_cbc_sha2_digest_16 test.
@@ -271,7 +273,7 @@ def aes_cbc_sha2_digest_16(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aead_aes_gcm(self) -> None:
"""aead_aes_gcm test.
@@ -303,7 +305,7 @@ def aead_aes_gcm(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aes_docsisbpi(self) -> None:
"""aes_docsiscpi test.
@@ -363,7 +365,7 @@ def aes_sha1(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def snow3g_uea2_snow3g_uia2(self) -> None:
"""snow3g_uea2_snow3g_uia2 test.
@@ -398,7 +400,7 @@ def snow3g_uea2_snow3g_uia2(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def zuc_eea3_zuc_eia3(self) -> None:
"""zuc_eea3_zuc_eia3 test.
@@ -433,7 +435,7 @@ def zuc_eea3_zuc_eia3(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def kasumi_f8_kasumi_f9(self) -> None:
"""kasumi_f8 kasumi_f9 test.
@@ -469,7 +471,7 @@ def kasumi_f8_kasumi_f9(self) -> None:
# BEGIN VDEV TESTS
-
+ @crypto_test
def aesni_mb_vdev(self) -> None:
"""aesni_mb virtual device test.
@@ -505,7 +507,7 @@ def aesni_mb_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aesni_gcm_vdev(self):
"""aesni_gcm virtual device test.
@@ -541,7 +543,7 @@ def aesni_gcm_vdev(self):
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def kasumi_vdev(self) -> None:
"""Kasmumi virtual device test.
@@ -577,7 +579,7 @@ def kasumi_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def snow3g_vdev(self) -> None:
"""snow3g virtual device test.
@@ -616,7 +618,7 @@ def snow3g_vdev(self) -> None:
f"Gbps and MOps were {result["gbps delta"]} below baseline",
)
- @func_test
+ @crypto_test
def zuc_vdev(self) -> None:
"""Zuc virtual device test.
@@ -652,7 +654,7 @@ def zuc_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gpbs fell below delta tolerance")
- @func_test
+ @crypto_test
def open_ssl_vdev(self) -> None:
"""open_ssl virtual device test.
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v2 5/5] dts: automate VFIO-PCI modprobe in node setup
2026-02-10 19:34 [PATCH v2 0/5] dts: add cryptodev testing support Andrew Bailey
` (3 preceding siblings ...)
2026-02-10 19:34 ` [PATCH v2 4/5] dts: add crypto test decorator Andrew Bailey
@ 2026-02-10 19:34 ` Andrew Bailey
2026-02-13 19:35 ` [PATCH v3 0/5] dts: add cryptodev testing support Andrew Bailey
` (2 subsequent siblings)
7 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-02-10 19:34 UTC (permalink / raw)
To: luca.vizzarro, probb, dev; +Cc: dmarx, Andrew Bailey
Currently, users must modprobe VFIO-PCI before running DTS when using a
non-mellanox NIC. This patch automates the process on test run start up.
In addition, if the SUT is expected to use a subset of a few QAT devices,
VFIO-PCI must be loaded in a separate manner. This patch adds this
distinction.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/framework/testbed_model/linux_session.py | 24 ++++++++++++++++++++
dts/framework/testbed_model/os_session.py | 4 ++++
2 files changed, 28 insertions(+)
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index a9eb8b693d..c41155e23b 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -224,6 +224,30 @@ def devbind_script_path(self) -> PurePath:
"""
raise InternalError("Accessed devbind script path before setup.")
+ def load_vfio(self, pf_port: Port) -> None:
+ """Overrides :meth:`~os_session.OSSession,load_vfio`."""
+ cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
+ device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
+ if device and device.group(1) in ["37c8", "0435", "19e2"]:
+ self.send_command(
+ "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe -r vfio_virqfd; modprobe -r vfio",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
+ )
+ self.send_command(
+ "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
+ privileged=True,
+ )
+ else:
+ self.send_command("modprobe vfio-pci")
+ self.refresh_lshw()
+
def create_crypto_vfs(self, pf_port: list[Port]) -> None:
"""Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index 2eeeea6967..4a4fc1f34a 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -615,6 +615,10 @@ def configure_port_mtu(self, mtu: int, port: Port) -> None:
port: Port to set `mtu` on.
"""
+ @abstractmethod
+ def load_vfio(self, pf_port: Port) -> None:
+ """Load the vfio module according to the device type of the given port."""
+
@abstractmethod
def create_crypto_vfs(self, pf_ports: list[Port]) -> None:
"""Creatues virtual functions for each port in 'pf_ports'.
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v3 0/5] dts: add cryptodev testing support
2026-02-10 19:34 [PATCH v2 0/5] dts: add cryptodev testing support Andrew Bailey
` (4 preceding siblings ...)
2026-02-10 19:34 ` [PATCH v2 5/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
@ 2026-02-13 19:35 ` Andrew Bailey
2026-02-13 19:35 ` [PATCH v3 1/5] dts: add find float method to text parser Andrew Bailey
` (4 more replies)
2026-03-02 18:54 ` [PATCH v4 0/5] dts: add cryptodev testing support Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 0/5] dts: add cryptodev testing support Andrew Bailey
7 siblings, 5 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-02-13 19:35 UTC (permalink / raw)
To: luca.vizzarro, probb; +Cc: dmarx, dev, Andrew Bailey
This patch series adds support to use the dpdk-test-crypto-perf
application. This application utilizes crypto devices that must be
listed in the format of a port under the `cryptodevs:` label. Along
with this, the application does not utilize a traffic generator and
therefore a new test suite decorator has been made to signify this.
Crypto tests must be enabled in test_run.yaml as `crypto: true`.
It is not supported to enable crypto testing along with functional
or performance testing. Specific capabilities of a crypto device
cannot be gathered at setup and therefore, non-relevant tests skip at
runtime. Finally, the application must be run with virtual functions
and the process of creating and binding them has been automated within
this series.
---
v2:
* Updated example yaml files for test_run and tests_config.
* Expanded test suite coverage.
* Resolved git problems where patches were removing code added by the
previous commit.
* Updated test suite to only run tests that are configured in the
tests_config yaml.
v3:
* Fixed some tests that were being run with incorrect arguments.
* Updated crypto-device pci paths for driver binding and VF creation.
* Updated example yaml files for tests_config and nodes files.
Andrew Bailey (5):
dts: add find float method to text parser
dts: add cryptodev package to DTS
dts: add cryptodev throughput test suite
dts: add crypto test decorator
dts: automate VFIO-PCI modprobe in node setup
.../tests.TestSuite_cryptodev_throughput.rst | 8 +
dts/api/cryptodev/__init__.py | 134 ++++
dts/api/cryptodev/config.py | 499 +++++++++++++
dts/api/cryptodev/types.py | 185 +++++
dts/configurations/nodes.example.yaml | 6 +
dts/configurations/test_run.example.yaml | 1 +
dts/configurations/tests_config.example.yaml | 8 +
dts/framework/config/node.py | 4 +
dts/framework/config/test_run.py | 3 +
dts/framework/params/types.py | 65 ++
dts/framework/parser.py | 28 +
dts/framework/remote_session/dpdk_shell.py | 5 +-
dts/framework/test_run.py | 5 +
dts/framework/test_suite.py | 6 +
dts/framework/testbed_model/linux_session.py | 78 ++
dts/framework/testbed_model/node.py | 10 +
dts/framework/testbed_model/os_session.py | 34 +
dts/framework/testbed_model/topology.py | 89 ++-
dts/tests/TestSuite_cryptodev_throughput.py | 692 ++++++++++++++++++
19 files changed, 1857 insertions(+), 3 deletions(-)
create mode 100644 doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
create mode 100644 dts/api/cryptodev/__init__.py
create mode 100644 dts/api/cryptodev/config.py
create mode 100644 dts/api/cryptodev/types.py
create mode 100644 dts/tests/TestSuite_cryptodev_throughput.py
--
2.50.1
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v3 1/5] dts: add find float method to text parser
2026-02-13 19:35 ` [PATCH v3 0/5] dts: add cryptodev testing support Andrew Bailey
@ 2026-02-13 19:35 ` Andrew Bailey
2026-02-25 21:05 ` Patrick Robb
2026-02-13 19:35 ` [PATCH v3 2/5] dts: add cryptodev package to DTS Andrew Bailey
` (3 subsequent siblings)
4 siblings, 1 reply; 41+ messages in thread
From: Andrew Bailey @ 2026-02-13 19:35 UTC (permalink / raw)
To: luca.vizzarro, probb; +Cc: dmarx, dev, Andrew Bailey
Currently, there is no way to gather floats from text using the parser.
Adding a new method to find floats will allow testsuites to utilize
valuable float values that are output from applications.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/framework/parser.py | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 4170cdb1dd..3075c36857 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -220,6 +220,34 @@ def find_int(
return TextParser.wrap(TextParser.find(pattern), partial(int, base=int_base))
+ @staticmethod
+ def find_float(
+ pattern: str | re.Pattern[str],
+ flags: re.RegexFlag = re.RegexFlag(0),
+ ) -> ParserFn:
+ """Makes a parser function that converts the match of :meth:`~find` to float.
+
+ This function is compatible only with a pattern containing one capturing group.
+
+ Args:
+ pattern: The regular expression pattern.
+ flags: The regular expression flags. Ignored if the given pattern is already compiled.
+
+ Raises:
+ InternalError: If the pattern does not have exactly one capturing group.
+
+ Returns:
+ ParserFn: A dictionary for the `dataclasses.field` metadata argument containing the
+ :meth:`~find` parser function wrapped by the float built-in.
+ """
+ if isinstance(pattern, str):
+ pattern = re.compile(pattern, flags)
+
+ if pattern.groups != 1:
+ raise InternalError("only one capturing group is allowed with this parser function")
+
+ return TextParser.wrap(TextParser.find(pattern), partial(float))
+
"""============ END PARSER FUNCTIONS ============"""
@classmethod
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v3 2/5] dts: add cryptodev package to DTS
2026-02-13 19:35 ` [PATCH v3 0/5] dts: add cryptodev testing support Andrew Bailey
2026-02-13 19:35 ` [PATCH v3 1/5] dts: add find float method to text parser Andrew Bailey
@ 2026-02-13 19:35 ` Andrew Bailey
2026-02-25 21:06 ` Patrick Robb
2026-02-27 16:15 ` Patrick Robb
2026-02-13 19:35 ` [PATCH v3 3/5] dts: add cryptodev throughput test suite Andrew Bailey
` (2 subsequent siblings)
4 siblings, 2 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-02-13 19:35 UTC (permalink / raw)
To: luca.vizzarro, probb; +Cc: dmarx, dev, Andrew Bailey
Running the DPDK test crypto performance application is essential for
testing cryptodev performance in DTS. This application takes numerous
arguments and can be run in four modes; Throughput, Latency,
PMD-cyclecount, and Verify. The package to add in this commit allows for
this application to be run in any of these modes with user supplied
arguments.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/api/cryptodev/__init__.py | 134 +++++
dts/api/cryptodev/config.py | 499 +++++++++++++++++++
dts/api/cryptodev/types.py | 185 +++++++
dts/configurations/nodes.example.yaml | 6 +
dts/framework/config/node.py | 4 +
dts/framework/params/types.py | 65 +++
dts/framework/remote_session/dpdk_shell.py | 5 +-
dts/framework/test_run.py | 5 +
dts/framework/testbed_model/linux_session.py | 54 ++
dts/framework/testbed_model/node.py | 10 +
dts/framework/testbed_model/os_session.py | 30 ++
dts/framework/testbed_model/topology.py | 89 +++-
12 files changed, 1083 insertions(+), 3 deletions(-)
create mode 100644 dts/api/cryptodev/__init__.py
create mode 100644 dts/api/cryptodev/config.py
create mode 100644 dts/api/cryptodev/types.py
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
new file mode 100644
index 0000000000..9955657398
--- /dev/null
+++ b/dts/api/cryptodev/__init__.py
@@ -0,0 +1,134 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""Cryptodev-pmd non-interactive shell.
+
+Typical usage example in a TestSuite::
+
+ cryptodev = CryptodevPmd(CryptoPmdParams)
+ stats = cryptodev.run_app()
+"""
+
+import re
+from typing import TYPE_CHECKING, Any
+
+from typing_extensions import Unpack
+
+from api.cryptodev.config import CryptoPmdParams, TestType
+from api.cryptodev.types import (
+ CryptodevResults,
+ LatencyResults,
+ PmdCyclecountResults,
+ ThroughputResults,
+ VerifyResults,
+)
+from framework.context import get_ctx
+from framework.exception import RemoteCommandExecutionError, SkippedTestException
+from framework.remote_session.dpdk_shell import compute_eal_params
+
+if TYPE_CHECKING:
+ from framework.params.types import CryptoPmdParamsDict
+from pathlib import PurePath
+
+
+class Cryptodev:
+ """non-interactive cryptodev application.
+
+ Attributes:
+ _app_params: app parameters to pass to dpdk-test-crypto-perf application
+ """
+
+ _app_params: dict[str, Any]
+
+ def __init__(self, **app_params: Unpack["CryptoPmdParamsDict"]) -> None:
+ """Initialize the cryptodev application.
+
+ Args:
+ app_params: The application parameters as keyword arguments.
+
+ Raises:
+ ValueError: if the build environment is `None`
+ """
+ self._app_params = {}
+ for k, v in app_params.items():
+ if v is not None:
+ self._app_params[k] = (
+ self.vector_directory.joinpath(str(v)) if k == "test_file" else v
+ )
+ dpdk = get_ctx().dpdk.build
+ if dpdk is None:
+ raise ValueError("No DPDK build environment exists.")
+ self._path = dpdk.get_app("test-crypto-perf")
+
+ @property
+ def path(self) -> PurePath:
+ """Get the path to the cryptodev application.
+
+ Returns:
+ The path to the cryptodev application.
+ """
+ return PurePath(self._path)
+
+ @property
+ def vector_directory(self) -> PurePath:
+ """Get the path to the cryptodev vector files.
+
+ Returns:
+ The path to the cryptodev vector files.
+ """
+ return get_ctx().dpdk_build.remote_dpdk_tree_path.joinpath("app/test-crypto-perf/data/")
+
+ def run_app(self, numvfs: int | None = 0) -> list[CryptodevResults]:
+ """Run the cryptodev application with the given app parameters.
+
+ Raises:
+ SkippedTestException: If the device type is not supported on the main session.
+ RemoteCommandExecutionError: If there is an error running the command.
+
+ Returns:
+ list[CryptodevResults]: The list of parsed results for the cryptodev application.
+ """
+ try:
+ result = get_ctx().dpdk.run_dpdk_app(
+ self.path,
+ compute_eal_params(
+ CryptoPmdParams(
+ allowed_ports=get_ctx().topology.get_crypto_vfs(numvfs),
+ memory_channels=get_ctx().dpdk.config.memory_channels,
+ **self._app_params,
+ ),
+ ),
+ timeout=120,
+ )
+ except RemoteCommandExecutionError as e:
+ if "Crypto device type does not support capabilities requested" in e._command_stderr:
+ raise SkippedTestException(
+ f"{self._app_params['devtype']} does not support the requested capabilities"
+ )
+ elif "Failed to initialise requested crypto device type" in e._command_stderr:
+ raise SkippedTestException(
+ f"could not run application with devtype {self._app_params['devtype']}"
+ )
+ elif "failed to parse device" in e._command_stderr:
+ raise SkippedTestException(
+ f"dependencies missing for virtual device {self._app_params['vdevs'][0].name}"
+ )
+ raise e
+
+ regex = r"^\s+\d+.*$"
+ parser_options = re.MULTILINE
+ parser: type[CryptodevResults]
+
+ match self._app_params["ptest"]:
+ case TestType.throughput:
+ parser = ThroughputResults
+ case TestType.latency:
+ regex = r"total operations:.*time[^\n]*"
+ parser_options |= re.DOTALL
+ parser = LatencyResults
+ case TestType.pmd_cyclecount:
+ parser = PmdCyclecountResults
+ case TestType.verify:
+ parser = VerifyResults
+
+ return [parser.parse(line) for line in re.findall(regex, result.stdout, parser_options)]
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
new file mode 100644
index 0000000000..c6ec083a43
--- /dev/null
+++ b/dts/api/cryptodev/config.py
@@ -0,0 +1,499 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+"""Module containing types and parameter classes for cryptodev-pmd application."""
+
+from dataclasses import dataclass, field
+from enum import auto
+from typing import Literal
+
+from framework.params import Params, Switch
+from framework.params.eal import EalParams
+from framework.utils import StrEnum
+
+Silent = Literal[""]
+
+
+class DeviceType(StrEnum):
+ """Enum for cryptodev device types.
+
+ Attributes:
+ crypto_aesni_gcm: AES-NI GCM device type.
+ crypto_aesni_mb: AES-NI MB device type.
+ crypto_armv8: ARMv8 device type.
+ crypto_cn10k: CN10K device type.
+ crypto_cn9k: CN9K device type.
+ crypto_dpaa_sec: DPAA SEC device type.
+ crypto_dpaa2_sec: DPAA2 SEC device type.
+ crypto_kasumi: KASUMI device type.
+ crypto_mvsam: MVSAM device type.
+ crypto_null: NULL device type.
+ crypto_octeontx: OCTEONTX device type.
+ crypto_openssl: OpenSSL device type.
+ crypto_qat: QAT device type.
+ crypto_scheduler: Scheduler device type.
+ crypto_snow3g: SNOW3G device type.
+ crypto_zuc: ZUC device type.
+ """
+
+ crypto_aesni_gcm = auto()
+ crypto_aesni_mb = auto()
+ crypto_armv8 = auto()
+ crypto_cn10k = auto()
+ crypto_cn9k = auto()
+ crypto_dpaa_sec = auto()
+ crypto_dpaa2_sec = auto()
+ crypto_kasumi = auto()
+ crypto_mvsam = auto()
+ crypto_null = auto()
+ crypto_octeontx = auto()
+ crypto_openssl = auto()
+ crypto_qat = auto()
+ crypto_scheduler = auto()
+ crypto_snow3g = auto()
+ crypto_zuc = auto()
+
+
+def get_device_from_str(device: str) -> DeviceType | None:
+ """Get a device type from the given string.
+
+ Args:
+ device: the string representation of the desired device.
+
+ Returns:
+ the device as a DeviceType object.
+ """
+ for item in DeviceType:
+ if device in item.value:
+ return item
+ return None
+
+
+class OperationType(StrEnum):
+ """Enum for cryptodev operation types.
+
+ Attributes:
+ aead: AEAD operation type.
+ auth_only: Authentication only operation type.
+ auth_then_cipher: Authentication then cipher operation type.
+ cipher_only: Cipher only operation type.
+ cipher_then_auth: Cipher then authentication operation type.
+ docsis: DOCSIS operation type.
+ ecdsa_p192r1 = ECDSA P-192R1 operation type.
+ ecdsa_p224r1 = ECDSA P-224R1 operation type.
+ ecdsa_p256r1: ECDSA P-256R1 operation type.
+ ecdsa_p384r1 = ECDSA P-384R1 operation type.
+ ecdsa_p521r1 = ECDSA P-521R1 operation type.
+ eddsa_25519: EdDSA 25519 operation type.
+ modex: Modex operation type.
+ ipsec: IPsec operation type.
+ pdcp: PDCP operation type.
+ rsa: RSA operation type.
+ sm2: SM2 operation type.
+ tls_record: TLS record operation type.
+ """
+
+ aead = auto()
+ auth_only = "auth-only"
+ auth_then_cipher = "auth-then-cipher"
+ cipher_only = "cipher-only"
+ cipher_then_auth = "cipher-then-auth"
+ docsis = auto()
+ ecdsa_p192r1 = auto()
+ ecdsa_p224r1 = auto()
+ ecdsa_p256r1 = auto()
+ ecdsa_p384r1 = auto()
+ ecdsa_p521r1 = auto()
+ eddsa_25519 = auto()
+ modex = auto()
+ ipsec = auto()
+ pdcp = auto()
+ rsa = auto()
+ sm2 = auto()
+ tls_record = "tls-record"
+
+ def __str__(self) -> str:
+ """Override str to return the value."""
+ return self.value
+
+
+class CipherAlgorithm(StrEnum):
+ """Enum for cryptodev cipher algorithms.
+
+ Attributes:
+ aes_cbc: AES CBC cipher algorithm.
+ aes_ctr: AES CTR cipher algorithm.
+ aes_docsisbpi: AES DOCSIS BPI cipher algorithm.
+ aes_ecb: AES ECB cipher algorithm.
+ aes_f8: AES F8 cipher algorithm.
+ aes_xts: AES XTS cipher algorithm.
+ arc4: ARC4 cipher algorithm.
+ null: NULL cipher algorithm.
+ three_des_cbc: 3DES CBC cipher algorithm.
+ three_des_ctr: 3DES CTR cipher algorithm.
+ three_des_ecb: 3DES ECB cipher algorithm.
+ kasumi_f8: KASUMI F8 cipher algorithm.
+ snow3g_uea2: SNOW3G UEA2 cipher algorithm.
+ zuc_eea3: ZUC EEA3 cipher algorithm.
+ """
+
+ aes_cbc = "aes-cbc"
+ aes_ctr = "aes-ctr"
+ aes_docsisbpi = "aes-docsisbpi"
+ aes_ecb = "aes-ecb"
+ aes_f8 = "aes-f8"
+ aes_gcm = "aes-gcm"
+ aes_xts = "aes-xts"
+ arc4 = auto()
+ null = auto()
+ three_des_cbc = "3des-cbc"
+ three_des_ctr = "3des-ctr"
+ three_des_ecb = "3des-ecb"
+ kasumi_f8 = "kasumi-f8"
+ snow3g_uea2 = "snow3g-uea2"
+ zuc_eea3 = "zuc-eea3"
+
+ def __str__(self):
+ """Override str to return the value."""
+ return self.value
+
+
+class AuthenticationAlgorithm(StrEnum):
+ """Enum for cryptodev authentication algorithms.
+
+ Attributes:
+ aes_cbc_mac: AES CBC MAC authentication algorithm.
+ aes_cmac: AES CMAC authentication algorithm.
+ aes_gmac: AES GMAC authentication algorithm.
+ aes_xcbc_mac: AES XCBC MAC authentication algorithm.
+ kasumi_f9: KASUMI F9 authentication algorithm.
+ md5: MD5 authentication algorithm.
+ md5_hmac: MD5 HMAC authentication algorithm.
+ sha1: SHA1 authentication algorithm.
+ sha1_hmac: SHA1 HMAC authentication algorithm.
+ sha2_224: SHA2-224 authentication algorithm.
+ sha2_224_hmac: SHA2-224 HMAC authentication algorithm.
+ sha2_256: SHA2-256 authentication algorithm.
+ sha2_256_hmac: SHA2-256 HMAC authentication algorithm.
+ sha2_384: SHA2-384 authentication algorithm.
+ sha2_384_hmac: SHA2-384 HMAC authentication algorithm.
+ sha2_512: SHA2-512 authentication algorithm.
+ sha2_512_hmac: SHA2-512 HMAC authentication algorithm.
+ snow3g_uia2: SNOW3G UIA2 authentication algorithm.
+ zuc_eia3: ZUC EIA3 authentication algorithm.
+ """
+
+ aes_cbc_mac = "aes-cbc-mac"
+ aes_cmac = "aes-cmac"
+ aes_gmac = "aes-gmac"
+ aes_xcbc_mac = "aes-xcbc-mac"
+ kasumi_f9 = "kasumi-f9"
+ md5 = auto()
+ md5_hmac = "md5-hmac"
+ sha1 = auto()
+ sha1_hmac = "sha1-hmac"
+ sha2_224 = "sha2-224"
+ sha2_224_hmac = "sha2-224-hmac"
+ sha2_256 = "sha2-256"
+ sha2_256_hmac = "sha2-256-hmac"
+ sha2_384 = "sha2-384"
+ sha2_384_hmac = "sha2-384-hmac"
+ sha2_512 = "sha2-512"
+ sha2_512_hmac = "sha2-512-hmac"
+ snow3g_uia2 = "snow3g-uia2"
+ zuc_eia3 = "zuc-eia3"
+
+ def __str__(self) -> str:
+ """Override str to return the value."""
+ return self.value
+
+
+class TestType(StrEnum):
+ """Enum for cryptodev test types.
+
+ Attributes:
+ latency: Latency test type.
+ pmd_cyclecount: PMD cyclecount test type.
+ throughput: Throughput test type.
+ verify: Verify test type.
+ """
+
+ latency = auto()
+ pmd_cyclecount = "pmd-cyclecount"
+ throughput = auto()
+ verify = auto()
+
+ def __str__(self) -> str:
+ """Override str to return the value."""
+ return self.value
+
+
+class EncryptDecryptSwitch(StrEnum):
+ """Enum for cryptodev encrypt/decrypt operations.
+
+ Attributes:
+ decrypt: Decrypt operation.
+ encrypt: Encrypt operation.
+ """
+
+ decrypt = auto()
+ encrypt = auto()
+
+
+class AuthenticationOpMode(StrEnum):
+ """Enum for cryptodev authentication operation modes.
+
+ Attributes:
+ generate: Generate operation mode.
+ verify: Verify operation mode.
+ """
+
+ generate = auto()
+ verify = auto()
+
+
+class AeadAlgName(StrEnum):
+ """Enum for cryptodev AEAD algorithms.
+
+ Attributes:
+ aes_ccm: AES CCM algorithm.
+ aes_gcm: AES GCM algorithm.
+ """
+
+ aes_ccm = "aes-ccm"
+ aes_gcm = "aes-gcm"
+
+ def __str__(self):
+ """Override str to return the value."""
+ return self.value
+
+
+class AsymOpMode(StrEnum):
+ """Enum for cryptodev asymmetric operation modes.
+
+ Attributes:
+ decrypt: Decrypt operation mode.
+ encrypt: Encrypt operation mode.
+ sign: Sign operation mode.
+ verify: Verify operation mode.
+ """
+
+ decrypt = auto()
+ encrypt = auto()
+ sign = auto()
+ verify = auto()
+
+
+class PDCPDomain(StrEnum):
+ """Enum for cryptodev PDCP domains.
+
+ Attributes:
+ control: Control domain.
+ user: User domain.
+ """
+
+ control = auto()
+ user = auto()
+
+
+class RSAPrivKeyType(StrEnum):
+ """Enum for cryptodev RSA private key types.
+
+ Attributes:
+ exp: Exponent key type.
+ qt: QT key type.
+ """
+
+ exp = auto()
+ qt = auto()
+
+
+class TLSVersion(StrEnum):
+ """Enum for cryptodev TLS versions.
+
+ Attributes:
+ DTLS1_2: DTLS 1.2 version.
+ TLS1_2: TLS 1.2 version.
+ TLS1_3: TLS 1.3 version.
+ """
+
+ DTLS1_2 = "DTLS1.2"
+ TLS1_2 = "TLS1.2"
+ TLS1_3 = "TLS1.3"
+
+ def __str__(self):
+ """Override str to return the value."""
+ return self.value
+
+
+class RSAPrivKeytype(StrEnum):
+ """Enum for cryptodev RSA private key types.
+
+ Attributes:
+ exp: Exponent key type.
+ qt: QT key type.
+ """
+
+ exp = auto()
+ qt = auto()
+
+
+class BurstSizeRange:
+ """Class for burst size parameter.
+
+ Attributes:
+ burst_size: The burst size range, this list must be less than 32 elements.
+ """
+
+ burst_size: int | list[int]
+
+ def __init__(self, min: int, inc: int, max: int) -> None:
+ """Initialize the burst size range.
+
+ Raises:
+ ValueError: If the burst size range is more than 32 elements.
+ """
+ if (max - min) % inc > 32:
+ raise ValueError("Burst size range must be less than 32 elements.")
+ self.burst_size = list(range(min, max + 1, inc))
+
+
+class ListWrapper:
+ """Class for wrapping a list of integers.
+
+ One of the arguments for the cryptodev application is a list of integers. However, when
+ passing a list directly, it causes a syntax error in the command line. This class wraps
+ a list of integers and converts it to a comma-separated string for a proper command.
+ """
+
+ def __init__(self, value: list[int]) -> None:
+ """Initialize the list wrapper."""
+ self.value = value
+
+ def __str__(self) -> str:
+ """Convert the list to a comma-separated string."""
+ return ",".join(str(v) for v in self.value)
+
+
+@dataclass(slots=True, kw_only=True)
+class CryptoPmdParams(EalParams):
+ """Parameters for cryptodev-pmd application.
+
+ Attributes:
+ aead_aad_sz: set the size of AEAD AAD.
+ aead_algo: set AEAD algorithm name from class `AeadAlgName`.
+ aead_iv_sz: set the size of AEAD iv.
+ aead_key_sz: set the size of AEAD key.
+ aead_op: set AEAD operation mode from class `EncryptDecryptSwitch`.
+ asym_op: set asymmetric operation mode from class `AsymOpMode`.
+ auth_algo: set authentication algorithm name.
+ auth_aad_sz: set the size of authentication AAD.
+ auth_iv_sz: set the size of authentication iv.
+ auth_key_sz: set the size of authentication key.
+ auth_op: set authentication operation mode from class `AuthenticationOpMode`.
+ buffer_sz: Set the size of a single packet (plaintext or ciphertext in it).
+ burst_sz: Set the number of packets per burst. This can be set as a single value or
+ range of values defined by class `BurstSizeRange`. Default is 16.
+ burst_sz: Set the number of packets per burst. This can be set as a single value or
+ range of values defined by class `BurstSizeRange`. Default is 16.
+ cipher_algo: Set cipher algorithm name from class `CipherAlgorithm`.
+ cipher_iv_sz: set the size of cipher iv.
+ cipher_key_sz: set the size of cipher key.
+ cipher_op: set cipher operation mode from class `EncryptDecryptSwitch`.
+ csv_friendly: Enable test result output CSV friendly rather than human friendly.
+ desc_nb: set the number of descriptors for each crypto device.
+ devtype: Set the device name from class `DeviceType`.
+ digest_sz: set the size of digest.
+ docsis_hdr_sz: set DOCSIS header size(n) in bytes.
+ enable_sdap: enable service data adaptation protocol.
+ imix: Set the distribution of packet sizes. A list of weights must be passed, containing the
+ same number of items than buffer-sz, so each item in this list will be the weight of the
+ packet size on the same position in the buffer-sz parameter (a list has to be passed in
+ that parameter).
+ low_prio_qp_mask: set low priority queue pairs set in the hexadecimal mask. This is an
+ optional parameter, if not set all queue pairs will be on the same high priority.
+ modex_len: set modex length for asymmetric crypto perf test. Supported lengths are 60,
+ 128, 255, 448. Default length is 128.
+ optype: Set operation type from class `OpType`.
+ out_of_place: Enable out-of-place crypto operations mode.
+ pdcp_sn_sz: set PDCP sequebce number size(n) in bits. Valid values of n are 5/7/12/15/18.
+ pdcp_domain: Set PDCP domain to specify short_mac/control/user plane from class
+ `PDCPDomain`.
+ pdcp_ses_hfn_en: enable fixed session based HFN instead of per packet HFN.
+ pmd_cyclecount_delay_pmd: Add a delay (in milliseconds) between enqueue and dequeue in
+ pmd-cyclecount benchmarking mode (useful when benchmarking hardware acceleration).
+ pool_sz: Set the number if mbufs to be allocated in the mbuf pool.
+ ptest: Set performance throughput test type from class `TestType`.
+ rsa_modlen: Set RSA modulus length (in bits) for asymmetric crypto perf test.
+ To be used with RSA asymmetric crypto ops.Supported lengths are 1024, 2048, 4096, 8192.
+ Default length is 1024.
+ rsa_priv_keytype: set RSA private key type from class `RSAPrivKeytype`. To be used with RSA
+ asymmetric crypto ops.
+ segment_sz: Set the size of the segment to use, for Scatter Gather List testing. Use list of
+ values in buffer-sz in descending order if segment-sz is used. By default, it is set to
+ the size of the maximum buffer size, including the digest size, so a single segment is
+ created.
+ sessionless: Enable session-less crypto operations mode.
+ shared_session: Enable sharing sessions between all queue pairs on a single crypto PMD. This
+ can be useful for benchmarking this setup, or finding and debugging concurrency errors
+ that can occur while using sessions on multiple lcores simultaneously.
+ silent: Disable options dump.
+ test_file: Set test vector file path. See the Test Vector File chapter.
+ test_name: Set specific test name section in the test vector file.
+ tls_version: Set TLS/DTLS protocol version for perf test from class `TLSVersion`.
+ Default is TLS1.2.
+ total_ops: Set the number of total operations performed.
+ """
+
+ aead_aad_sz: int | None = field(default=None, metadata=Params.long("aead-aad-sz"))
+ aead_algo: AeadAlgName | None = field(default=None, metadata=Params.long("aead-algo"))
+ aead_iv_sz: int | None = field(default=None, metadata=Params.long("aead-iv-sz"))
+ aead_key_sz: int | None = field(default=None, metadata=Params.long("aead-key-sz"))
+ aead_op: EncryptDecryptSwitch | None = field(default=None, metadata=Params.long("aead-op"))
+ asym_op: AsymOpMode | None = field(default=None, metadata=Params.long("asym-op"))
+ auth_algo: AuthenticationAlgorithm | None = field(
+ default=None, metadata=Params.long("auth-algo")
+ )
+ auth_iv_sz: int | None = field(default=None, metadata=Params.long("auth-iv-sz"))
+ auth_key_sz: int | None = field(default=None, metadata=Params.long("auth-key-sz"))
+ auth_op: AuthenticationOpMode | None = field(default=None, metadata=Params.long("auth-op"))
+ buffer_sz: BurstSizeRange | ListWrapper | int | None = field(
+ default=None, metadata=Params.long("buffer-sz")
+ )
+ burst_sz: BurstSizeRange | ListWrapper | int | None = field(
+ default=None, metadata=Params.long("burst-sz")
+ )
+ cipher_algo: CipherAlgorithm | None = field(default=None, metadata=Params.long("cipher-algo"))
+ cipher_iv_sz: int | None = field(default=None, metadata=Params.long("cipher-iv-sz"))
+ cipher_key_sz: int | None = field(default=None, metadata=Params.long("cipher-key-sz"))
+ cipher_op: EncryptDecryptSwitch | None = field(default=None, metadata=Params.long("cipher-op"))
+ csv_friendly: Switch = field(default=None, metadata=Params.long("csv-friendly"))
+ desc_nb: int | None = field(default=None, metadata=Params.long("desc-nb"))
+ devtype: DeviceType = field(metadata=Params.long("devtype"))
+ digest_sz: int | None = field(default=None, metadata=Params.long("digest-sz"))
+ docsis_hdr_sz: int | None = field(default=None, metadata=Params.long("docsis-hdr-sz"))
+ enable_sdap: Switch = None
+ imix: int | None = field(default=None, metadata=Params.long("imix"))
+ low_prio_qp_mask: int | None = field(default=None, metadata=Params.convert_value(hex))
+ modex_len: int | None = field(default=None, metadata=Params.long("modex-len"))
+ optype: OperationType | None = field(default=None, metadata=Params.long("optype"))
+ out_of_place: Switch = None
+ pdcp_sn_sz: int | None = None
+ pdcp_domain: PDCPDomain | None = field(default=None, metadata=Params.long("pdcp-domain"))
+ pdcp_ses_hfn_en: Switch | None = field(default=None, metadata=Params.long("pdcp-ses-hfn-en"))
+ pmd_cyclecount_delay_pmd: int | None = field(
+ default=None, metadata=Params.long("pmd-cyclecount-delay-pmd")
+ )
+ pool_sz: int | None = field(default=None, metadata=Params.long("pool-sz"))
+ ptest: TestType = field(default=TestType.throughput, metadata=Params.long("ptest"))
+ rsa_modlen: int | None = field(default=None, metadata=Params.long("rsa-modlen"))
+ rsa_priv_keytype: RSAPrivKeytype | None = field(
+ default=None, metadata=Params.long("rsa-priv-keytype")
+ )
+ segment_sz: int | None = field(default=None, metadata=Params.long("segment-sz"))
+ sessionless: Switch = None
+ shared_session: Switch = None
+ silent: Silent | None = field(default="", metadata=Params.long("silent"))
+ test_file: str | None = field(default=None, metadata=Params.long("test-file"))
+ test_name: str | None = field(default=None, metadata=Params.long("test-name"))
+ tls_version: TLSVersion | None = field(default=None, metadata=Params.long("tls-version"))
+ total_ops: int | None = field(default=100000, metadata=Params.long("total-ops"))
diff --git a/dts/api/cryptodev/types.py b/dts/api/cryptodev/types.py
new file mode 100644
index 0000000000..df73a86fa4
--- /dev/null
+++ b/dts/api/cryptodev/types.py
@@ -0,0 +1,185 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""Cryptodev types module.
+
+Exposes types used in the Cryptodev API.
+"""
+
+from dataclasses import dataclass, field
+
+from framework.parser import TextParser
+
+
+@dataclass
+class CryptodevResults(TextParser):
+ """Base class for all cryptodev results."""
+
+ buffer_size: int
+
+ def __iter__(self):
+ """Iteration method to parse result objects.
+
+ Yields:
+ tuple[str, int | float]: a field name and its value.
+ """
+ for field_name in self.__dataclass_fields__:
+ yield field_name, getattr(self, field_name)
+
+
+@dataclass
+class ThroughputResults(CryptodevResults):
+ """A parser for throughput test output."""
+
+ #:
+ lcore_id: int = field(metadata=TextParser.find_int(r"\s*(\d+)"))
+ #: buffer size used in the run
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"\s+(?:\d+\s+)(\d+)"),
+ )
+ #: burst size used in the run
+ burst_size: int = field(
+ metadata=TextParser.find_int(r"\s+(?:\d+\s+){2}(\d+)"),
+ )
+ #: total packets enqueued
+ enqueued: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){3}(\d+)"))
+ #: total packets dequeued
+ dequeued: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){4}(\d+)"))
+ #: packets that failed enqueue
+ failed_enqueue: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){5}(\d+)"))
+ #: packets that failed dequeue
+ failed_dequeue: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){6}(\d+)"))
+ #: mega operations per second
+ mops: float = field(metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}([\d.]+)"))
+ #: gigabits per second
+ gbps: float = field(metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}(?:[\d.]+\s+)([\d.]+)"))
+ #: cpu cycles per buffer
+ cycles_per_buffer: float = field(
+ metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}(?:[\d.]+\s+){2}([\d.]+)")
+ )
+
+
+@dataclass
+class LatencyResults(CryptodevResults):
+ """A parser for latency test output."""
+
+ #: buffer size ran with app
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"Buf(?:.*\n\s+\d+\s+)?(?:fer size:\s+)?(\d+)"),
+ )
+ #: burst size ran with app
+ burst_size: int = field(
+ metadata=TextParser.find_int(rf"Burst(?:.*\n\s+\d+\s+){2}?(?: size:\s+)?(\d+)"),
+ )
+ #: total operations ran
+ total_ops: int = field(metadata=TextParser.find_int(r"total operations:\s+(\d+)"))
+ #: number of bursts
+ num_of_bursts: int = field(metadata=TextParser.find_int(r"Number of bursts:\s+(\d+)"))
+ #: minimum enqueued packets
+ min_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+){2}(\d+)"))
+ #: maximum enqueued packets
+ max_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+){3}(\d+)"))
+ #: average enqueued packets
+ avg_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+)(\d+)"))
+ #: total enqueued packets
+ total_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(\d+)"))
+ #: minimum dequeued packets
+ min_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+){2}(\d+)"))
+ #: maximum dequeued packets
+ max_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+){3}(\d+)"))
+ #: average dequeued packets
+ avg_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+)(\d+)"))
+ #: total dequeued packets
+ total_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(\d+)"))
+ #: minimum cycles per buffer
+ min_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+){3}([\d.]+)"))
+ #: maximum cycles per buffer
+ max_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+){2}([\d.]+)"))
+ #: average cycles per buffer
+ avg_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+)([\d.]+)"))
+ #: total cycles per buffer
+ total_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+([\d.]+)"))
+ #: mimum time in microseconds
+ min_time_us: float = field(
+ metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+){3}([\d.]+)")
+ )
+ #: maximum time in microseconds
+ max_time_us: float = field(
+ metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+){2}([\d.]+)")
+ )
+ #: average time in microseconds
+ avg_time_us: float = field(
+ metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+)([\d.]+)")
+ )
+ #: total time in microseconds
+ total_time_us: float = field(metadata=TextParser.find_float(r"time \[us\]\s+([\d.]+)"))
+
+
+@dataclass
+class PmdCyclecountResults(CryptodevResults):
+ """A parser for PMD cycle count test output."""
+
+ #:
+ lcore_id: int = field(metadata=TextParser.find_int(r"lcore\s+(?:id.*\n\s+)?(\d+)"))
+ #: buffer size used with app run
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"Buf(?:.*\n\s+(?:\d+\s+))?(?:fer size:\s+)?(\d+)"),
+ )
+ #: burst size used with app run
+ burst_size: int = field(
+ metadata=TextParser.find_int(r"Burst(?:.*\n\s+(?:\d+\s+){2})?(?: size:\s+)?(\d+)"),
+ )
+ #: packets enqueued
+ enqueued: int = field(metadata=TextParser.find_int(r"Enqueued.*\n\s+(?:\d+\s+){3}(\d+)"))
+ #: packets dequeued
+ dequeued: int = field(metadata=TextParser.find_int(r"Dequeued.*\n\s+(?:\d+\s+){4}(\d+)"))
+ #: number of enqueue packet retries
+ enqueue_retries: int = field(
+ metadata=TextParser.find_int(r"Enq Retries.*\n\s+(?:\d+\s+){5}(\d+)")
+ )
+ #: number of dequeue packet retries
+ dequeue_retries: int = field(
+ metadata=TextParser.find_int(r"Deq Retries.*\n\s+(?:\d+\s+){6}(\d+)")
+ )
+ #: number of cycles per operation
+ cycles_per_operation: float = field(
+ metadata=TextParser.find_float(r"Cycles/Op.*\n\s+(?:\d+\s+){7}([\d.]+)")
+ )
+ #: number of cycles per enqueue
+ cycles_per_enqueue: float = field(
+ metadata=TextParser.find_float(r"Cycles/Enq.*\n\s+(?:\d+\s+){7}(?:[\d.]+\s+)([\d.]+)")
+ )
+ #: number of cycles per dequeue
+ cycles_per_dequeue: float = field(
+ metadata=TextParser.find_float(r"Cycles/Deq.*\n\s+(?:\d+\s+){7}(?:[\d.]+\s+){2}([\d.]+)"),
+ )
+
+
+@dataclass
+class VerifyResults(CryptodevResults):
+ """A parser for verify test output."""
+
+ #:
+ lcore_id: int = field(metadata=TextParser.find_int(r"lcore\s+(?:id.*\n\s+)?(\d+)"))
+ #: buffer size ran with app
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"Buf(?:.*\n\s+(?:\d+\s+))?(?:fer size:\s+)?(\d+)"),
+ )
+ #: burst size ran with app
+ burst_size: int = field(
+ metadata=TextParser.find_int(r"Burst(?:.*\n\s+(?:\d+\s+){2})?(?: size:\s+)?(\d+)"),
+ )
+ #: number of packets enqueued
+ enqueued: int = field(metadata=TextParser.find_int(r"Enqueued.*\n\s+(?:\d+\s+){3}(\d+)"))
+ #: number of packets dequeued
+ dequeued: int = field(metadata=TextParser.find_int(r"Dequeued.*\n\s+(?:\d+\s+){4}(\d+)"))
+ #: number of packets enqueue failed
+ failed_enqueued: int = field(
+ metadata=TextParser.find_int(r"Failed Enq.*\n\s+(?:\d+\s+){5}(\d+)")
+ )
+ #: number of packets dequeue failed
+ failed_dequeued: int = field(
+ metadata=TextParser.find_int(r"Failed Deq.*\n\s+(?:\d+\s+){6}(\d+)")
+ )
+ #: total number of failed operations
+ failed_ops: int = field(metadata=TextParser.find_int(r"Failed Ops.*\n\s+(?:\d+\s+){7}(\d+)"))
diff --git a/dts/configurations/nodes.example.yaml b/dts/configurations/nodes.example.yaml
index 3f23df11ec..1fc0a38bf5 100644
--- a/dts/configurations/nodes.example.yaml
+++ b/dts/configurations/nodes.example.yaml
@@ -20,6 +20,12 @@
hugepages_2mb: # optional; if removed, will use system hugepage configuration
number_of: 256
force_first_numa: false
+ # cryptodevs: # optional; add each physical bdf as its own element in the list
+ # - name: cryptodev-0
+ # pci: "ffff:ff:ff.f"
+ # os_driver_for_dpdk: vfio-pci # OS driver that DPDK will use
+ # os_driver: _ # This is unused but a value is necessary to run dts
+ # crypto_driver: # the device type of the DUT (crypto_qat, crypto_zuc, crypto_aesni_mb)
# Define a Scapy traffic generator node, having two network ports
# physically connected to the corresponding ports in SUT 1 (the peer node).
- name: "TG 1"
diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
index 438a1bdc8f..36068c1ef8 100644
--- a/dts/framework/config/node.py
+++ b/dts/framework/config/node.py
@@ -70,6 +70,10 @@ class NodeConfiguration(FrozenModel):
hugepages: HugepageConfiguration | None = Field(None, alias="hugepages_2mb")
#: The ports that can be used in testing.
ports: list[PortConfig] = Field(min_length=1)
+ #: The pci info used by crypto devices
+ cryptodevs: list[PortConfig] = Field(default=[], min_length=0)
+ #: The crypto driver used by crypto devices
+ crypto_driver: str | None = None
@model_validator(mode="after")
def verify_unique_port_names(self) -> Self:
diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py
index 5bc4bd37d9..3c7650474c 100644
--- a/dts/framework/params/types.py
+++ b/dts/framework/params/types.py
@@ -15,6 +15,23 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
from pathlib import PurePath
from typing import TypedDict
+from api.cryptodev.config import (
+ AeadAlgName,
+ AsymOpMode,
+ AuthenticationAlgorithm,
+ AuthenticationOpMode,
+ BurstSizeRange,
+ CipherAlgorithm,
+ DeviceType,
+ EncryptDecryptSwitch,
+ ListWrapper,
+ OperationType,
+ PDCPDomain,
+ RSAPrivKeytype,
+ Silent,
+ TestType,
+ TLSVersion,
+)
from api.testpmd.config import (
AnonMempoolAllocationMode,
EthPeer,
@@ -55,6 +72,54 @@ class EalParamsDict(TypedDict, total=False):
other_eal_param: Params | None
+class CryptoPmdParamsDict(EalParamsDict, total=False):
+ """:class:`TypedDict` equivalent of :class:`~.cryptodev.CryptoPmdParams`."""
+
+ aead_aad_sz: int | None
+ aead_algo: AeadAlgName | None
+ aead_iv_sz: int | None
+ aead_key_sz: int | None
+ aead_op: EncryptDecryptSwitch | None
+ asym_op: AsymOpMode | None
+ auth_algo: AuthenticationAlgorithm | None
+ auth_iv_sz: int | None
+ auth_key_sz: int | None
+ auth_op: AuthenticationOpMode | None
+ buffer_sz: BurstSizeRange | ListWrapper | int | None
+ burst_sz: BurstSizeRange | ListWrapper | int | None
+ cipher_algo: CipherAlgorithm | None
+ cipher_iv_sz: int | None
+ cipher_key_sz: int | None
+ cipher_op: EncryptDecryptSwitch | None
+ csv_friendly: Switch
+ desc_nb: int | None
+ devtype: DeviceType | None
+ digest_sz: int | None
+ docsis_hdr_sz: int | None
+ enable_sdap: Switch
+ imix: int | None
+ low_prio_qp_mask: int | None
+ modex_len: int | None
+ optype: OperationType | None
+ out_of_place: Switch
+ pdcp_sn_sz: int | None
+ pdcp_domain: PDCPDomain | None
+ pdcp_ses_hfn_en: Switch | None
+ pmd_cyclecount_delay_pmd: int | None
+ pool_sz: int | None
+ ptest: TestType
+ rsa_modlen: int | None
+ rsa_priv_keytype: RSAPrivKeytype | None
+ segment_sz: int | None
+ sessionless: Switch
+ shared_session: Switch
+ silent: Silent | None
+ test_file: str | None
+ test_name: str | None
+ tls_version: TLSVersion | None
+ total_ops: int | None
+
+
class TestPmdParamsDict(EalParamsDict, total=False):
""":class:`TypedDict` equivalent of :class:`~.testpmd.TestPmdParams`."""
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index 51b97d4ff6..b94d336d4e 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -46,7 +46,10 @@ def compute_eal_params(
params.prefix = prefix
if params.allowed_ports is None:
- params.allowed_ports = ctx.topology.sut_dpdk_ports
+ if ctx.topology.crypto_vf_ports:
+ params.allowed_ports = [ctx.topology.crypto_vf_ports[0]]
+ else:
+ params.allowed_ports = ctx.topology.sut_dpdk_ports
return params
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index ff0a12c9ce..36e6c5a44c 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -349,6 +349,9 @@ def next(self) -> State | None:
if test_run.config.use_virtual_functions:
test_run.ctx.topology.instantiate_vf_ports()
+ if test_run.ctx.sut_node.cryptodevs:
+ test_run.ctx.topology.instantiate_crypto_vf_ports()
+ test_run.ctx.topology.configure_cryptodevs("dpdk")
test_run.ctx.topology.configure_ports("sut", "dpdk")
if test_run.ctx.func_tg:
@@ -442,6 +445,8 @@ def next(self) -> State | None:
"""Next state."""
if self.test_run.config.use_virtual_functions:
self.test_run.ctx.topology.delete_vf_ports()
+ if self.test_run.ctx.sut_node.cryptodevs:
+ self.test_run.ctx.topology.delete_crypto_vf_ports()
self.test_run.ctx.shell_pool.terminate_current_pool()
if self.test_run.ctx.func_tg and self.test_run.ctx.func_tg.is_setup:
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 711d4d97c3..27a5f48ecf 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -10,6 +10,7 @@
"""
import json
+import re
from collections.abc import Iterable
from functools import cached_property
from pathlib import PurePath
@@ -223,6 +224,30 @@ def devbind_script_path(self) -> PurePath:
"""
raise InternalError("Accessed devbind script path before setup.")
+ def create_crypto_vfs(self, pf_port: list[Port]) -> None:
+ """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ for port in pf_port:
+ self.delete_crypto_vfs(port)
+ for port in pf_port:
+ sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
+ )
+ self.send_command(
+ f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
+ )
+
+ self.refresh_lshw()
+
def create_vfs(self, pf_port: Port) -> None:
"""Overrides :meth:`~.os_session.OSSession.create_vfs`.
@@ -239,6 +264,23 @@ def create_vfs(self, pf_port: Port) -> None:
self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
self.refresh_lshw()
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if curr_num_vfs == 0:
+ return self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
+ self.send_command(
+ f"dpdk-devbind.py -u {pf_port.pci}".replace(":", "\\:"), privileged=True, timeout=30
+ )
+ self.send_command(
+ f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
+ privileged=True,
+ )
+ self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
+
def delete_vfs(self, pf_port: Port) -> None:
"""Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
@@ -250,6 +292,18 @@ def delete_vfs(self, pf_port: Port) -> None:
else:
self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ f"readlink {sys_bus_path}/virtfn*",
+ privileged=True,
+ )
+ return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
+ return []
+
def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
"""Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
index 6c3e63a2a1..5c3e02a320 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -54,6 +54,8 @@ class Node:
arch: Architecture
lcores: list[LogicalCore]
ports: list[Port]
+ cryptodevs: list[Port]
+ crypto_driver: str | None
_logger: DTSLogger
_other_sessions: list[OSSession]
_node_info: OSSessionInfo | None
@@ -82,6 +84,14 @@ def __init__(self, node_config: NodeConfiguration) -> None:
self._other_sessions = []
self._setup = False
self.ports = [Port(self, port_config) for port_config in self.config.ports]
+ self.cryptodevs = [
+ Port(self, cryptodev_config) for cryptodev_config in self.config.cryptodevs
+ ]
+ self.crypto_driver = self.config.crypto_driver
+ if self.cryptodevs:
+ self.main_session.load_vfio(self.cryptodevs[0])
+ elif self.ports and self.ports[0].config.os_driver_for_dpdk == "vfio-pci":
+ self.main_session.load_vfio(self.ports[0])
self._logger.info(f"Created node: {self.name}")
def setup(self) -> None:
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index b94c3e527b..2eeeea6967 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -615,6 +615,14 @@ def configure_port_mtu(self, mtu: int, port: Port) -> None:
port: Port to set `mtu` on.
"""
+ @abstractmethod
+ def create_crypto_vfs(self, pf_ports: list[Port]) -> None:
+ """Creatues virtual functions for each port in 'pf_ports'.
+
+ Checks how many virtual functions each crypto device in 'pf_ports' supports, and creates
+ that number of VFs on the port.
+ """
+
@abstractmethod
def create_vfs(self, pf_port: Port) -> None:
"""Creates virtual functions for `pf_port`.
@@ -630,6 +638,16 @@ def create_vfs(self, pf_port: Port) -> None:
maximum for `pf_port`.
"""
+ @abstractmethod
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Deletes virtual functions for crypto device 'pf_port'.
+
+ Checks how many virtual functions are currently active and removes all if any exist.
+
+ Args:
+ pf_port: The crypto device port to delete virtual functions on.
+ """
+
@abstractmethod
def delete_vfs(self, pf_port: Port) -> None:
"""Deletes virtual functions for `pf_port`.
@@ -656,3 +674,15 @@ def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
A list containing all of the PCI addresses of the VFs on the port. If the port has no
VFs then the list will be empty.
"""
+
+ @abstractmethod
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Find the PCI addresses of all virtual functions (VFs) of a crypto device on `pf_port`.
+
+ Args:
+ pf_port: The port to find the VFs on.
+
+ Returns:
+ A list containing all of the PCI addresses of the VFs on the port. If the port has no
+ VFs then the list will be empty.
+ """
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
index 13b4b58a74..d9af67552a 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -8,6 +8,7 @@
The link information then implies what type of topology is available.
"""
+import re
from collections import defaultdict
from collections.abc import Iterator
from dataclasses import dataclass
@@ -58,6 +59,8 @@ class Topology:
tg_ports: list[Port]
pf_ports: list[Port]
vf_ports: list[Port]
+ crypto_pf_ports: list[Port]
+ crypto_vf_ports: list[Port]
@classmethod
def from_port_links(cls, port_links: Iterator[PortLink]) -> Self:
@@ -85,7 +88,7 @@ def from_port_links(cls, port_links: Iterator[PortLink]) -> Self:
msg = "More than two links in a topology are not supported."
raise ConfigurationError(msg)
- return cls(type, sut_ports, tg_ports, [], [])
+ return cls(type, sut_ports, tg_ports, [], [], [], [])
def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node, list[Port]]:
"""Retrieve node and its ports for the current topology.
@@ -105,6 +108,39 @@ def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node,
msg = f"Invalid node `{node_identifier}` given."
raise InternalError(msg)
+ def get_crypto_vfs(self, numvfs: int | None = None) -> list[Port]:
+ """Retrieve virtual functions from active crypto vfs.
+
+ If numvfs is `None`, returns all crypto virtual functions. Otherwise, returns
+ `numvfs` number of virtual functions per physical function rounded down. If a number of
+ `numvfs` is less than the number of physical functions, one virtual function is returned.
+
+ Args:
+ numvfs: Number of virtual functions to return if provided.
+
+ Returns:
+ List containing the requested number of vfs, evenly distributed among
+ physcal functions rounded down.
+ """
+ return_list = []
+ device = 1
+ if len(self.crypto_pf_ports) == 0:
+ return []
+ if numvfs is None:
+ return_list.extend(self.crypto_vf_ports)
+ return return_list
+ mod = numvfs // len(self.crypto_pf_ports)
+ if numvfs < len(self.crypto_pf_ports):
+ return_list.extend([self.crypto_vf_ports[0]])
+ return return_list
+ for i in range(mod):
+ if i % 8 == 0 and i != 0:
+ device += 1
+ return_list.extend(
+ filter(lambda x: re.search(rf"0000:\d+:0{device}.{i}", x.pci), self.crypto_vf_ports)
+ )
+ return return_list
+
def setup(self) -> None:
"""Setup topology ports.
@@ -145,6 +181,34 @@ def _setup_ports(self, node_identifier: NodeIdentifier) -> None:
f"for port {port.name} in node {node.name}."
)
+ def instantiate_crypto_vf_ports(self) -> None:
+ """Create max number of virtual functions allowed on the SUT node.
+
+ Raises:
+ InternalError: If crypto virtual functions could not be created on a port.
+ """
+ from framework.context import get_ctx
+
+ ctx = get_ctx()
+
+ for port in ctx.sut_node.cryptodevs:
+ self.crypto_pf_ports.append(port)
+ self.delete_crypto_vf_ports()
+
+ ctx.sut_node.main_session.create_crypto_vfs(self.crypto_pf_ports)
+ for port in self.crypto_pf_ports:
+ addr_list = ctx.sut_node.main_session.get_pci_addr_of_crypto_vfs(port)
+ if addr_list == []:
+ raise InternalError(f"Failed to create crypto virtual function on port {port.pci}")
+ for addr in addr_list:
+ vf_config = PortConfig(
+ name=f"{port.name}-crypto-vf-{addr}",
+ pci=addr,
+ os_driver_for_dpdk=port.config.os_driver_for_dpdk,
+ os_driver=port.config.os_driver,
+ )
+ self.crypto_vf_ports.append(Port(node=port.node, config=vf_config))
+
def instantiate_vf_ports(self) -> None:
"""Create, setup, and add virtual functions to the list of ports on the SUT node.
@@ -189,6 +253,27 @@ def delete_vf_ports(self) -> None:
self.sut_ports.clear()
self.sut_ports.extend(self.pf_ports)
+ def delete_crypto_vf_ports(self) -> None:
+ """Delete crypto virtual functions from the SUT node during test run teardown."""
+ from framework.context import get_ctx
+
+ ctx = get_ctx()
+
+ for port in self.crypto_pf_ports:
+ ctx.sut_node.main_session.delete_crypto_vfs(port)
+ self.sut_ports.clear()
+ self.sut_ports.extend(self.pf_ports)
+
+ def configure_cryptodevs(self, driver: DriverKind):
+ """Configure the crypto device virtual functoins on the sut and bind to specified driver.
+
+ Args:
+ driver: The driver to bind the cryptofunctions
+ """
+ from framework.context import get_ctx
+
+ self._bind_ports_to_drivers(get_ctx().sut_node, self.crypto_vf_ports, driver)
+
def configure_ports(
self, node_identifier: NodeIdentifier, drivers: DriverKind | tuple[DriverKind, ...]
) -> None:
@@ -227,7 +312,7 @@ def _bind_ports_to_drivers(
for port_id, port in enumerate(ports):
driver_kind = drivers[port_id] if isinstance(drivers, tuple) else drivers
desired_driver = port.driver_by_kind(driver_kind)
- if port.current_driver != desired_driver:
+ if port in self.crypto_vf_ports or port.current_driver != desired_driver:
driver_to_ports[desired_driver].append(port)
for driver_name, ports in driver_to_ports.items():
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v3 3/5] dts: add cryptodev throughput test suite
2026-02-13 19:35 ` [PATCH v3 0/5] dts: add cryptodev testing support Andrew Bailey
2026-02-13 19:35 ` [PATCH v3 1/5] dts: add find float method to text parser Andrew Bailey
2026-02-13 19:35 ` [PATCH v3 2/5] dts: add cryptodev package to DTS Andrew Bailey
@ 2026-02-13 19:35 ` Andrew Bailey
2026-02-27 18:24 ` Patrick Robb
2026-02-13 19:35 ` [PATCH v3 4/5] dts: add crypto test decorator Andrew Bailey
2026-02-13 19:35 ` [PATCH v3 5/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
4 siblings, 1 reply; 41+ messages in thread
From: Andrew Bailey @ 2026-02-13 19:35 UTC (permalink / raw)
To: luca.vizzarro, probb; +Cc: dmarx, dev, Andrew Bailey
Add cryptodev performance test suite. This test covers throughput
performance metrics of supplied crypto devices using the
dpdk-test-crypto-perf application. The results of this application will
be compared against user supplied performance metrics and fail if signs
of regression are apparent.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
.../tests.TestSuite_cryptodev_throughput.rst | 8 +
dts/configurations/tests_config.example.yaml | 8 +
dts/tests/TestSuite_cryptodev_throughput.py | 690 ++++++++++++++++++
3 files changed, 706 insertions(+)
create mode 100644 doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
create mode 100644 dts/tests/TestSuite_cryptodev_throughput.py
diff --git a/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst b/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
new file mode 100644
index 0000000000..5df5e7547e
--- /dev/null
+++ b/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+cryptodev_throughput Test Suite
+===============================
+
+.. automodule:: tests.TestSuite_cryptodev_throughput
+ :members:
+ :show-inheritance:
diff --git a/dts/configurations/tests_config.example.yaml b/dts/configurations/tests_config.example.yaml
index 64fa630aa0..c9beff9c99 100644
--- a/dts/configurations/tests_config.example.yaml
+++ b/dts/configurations/tests_config.example.yaml
@@ -15,3 +15,11 @@ hello_world:
# num_descriptors: 1024
# expected_mpps: 1.0
# delta_tolerance: 0.05
+# cryptodev_throughput:
+# delta_tolerance: 0.05
+# throughput_test_parameters:
+# snow3g_uea2_snow3g_uia2: # Add the name of the test suite you would like to run
+# - buff_size: 64
+# gbps: 2.0 # Set gigabits per second according to your devices throughput for this given buffer size
+# - buff_size: 512
+# gbps: 20.0
\ No newline at end of file
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
new file mode 100644
index 0000000000..871e47ae63
--- /dev/null
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -0,0 +1,690 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""DPDK cryptodev performance test suite.
+
+The main goal of this testsuite is to utilize the dpdk-test-cryptodev application to gather
+performance metrics for various cryptographic operations supported by DPDK cryptodev-pmd.
+It will then compare the results against predefined baseline given in the test_config file to
+ensure performance standards are met.
+"""
+
+from api.capabilities import (
+ LinkTopology,
+ requires_link_topology,
+)
+from api.cryptodev import Cryptodev
+from api.cryptodev.config import (
+ AeadAlgName,
+ AuthenticationAlgorithm,
+ AuthenticationOpMode,
+ CipherAlgorithm,
+ DeviceType,
+ EncryptDecryptSwitch,
+ ListWrapper,
+ OperationType,
+ TestType,
+ get_device_from_str,
+)
+from api.cryptodev.types import (
+ CryptodevResults,
+)
+from api.test import verify
+from framework.context import get_ctx
+from framework.exception import SkippedTestException
+from framework.test_suite import BaseConfig, TestSuite, func_test
+from framework.testbed_model.virtual_device import VirtualDevice
+
+config_list: list[dict[str, int | float | str]] = [
+ {"buff_size": 64, "gbps": 1.00},
+ {"buff_size": 512, "gbps": 1.00},
+ {"buff_size": 2048, "gbps": 1.00},
+]
+
+
+class Config(BaseConfig):
+ """Performance test metrics.
+
+ Attributes:
+ delta_tolerance: The allowed tolerance below a given baseline.
+ throughput_test_parameters: The test parameters to use in the test suite.
+ """
+
+ delta_tolerance: float = 0.05
+
+ throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = {
+ "aes_cbc": config_list,
+ "aes_cbc_sha1": config_list,
+ "aes_cbc_sha2": config_list,
+ "aes_cbc_sha2_digest_16": config_list,
+ "aead_aes_gcm": config_list,
+ "aes_docsisbpi": config_list,
+ "sha1_hmac": config_list,
+ "snow3g_uea2_snow3g_uia2": config_list,
+ "zuc_eea3_zuc_eia3": config_list,
+ "kasumi_f8_kasumi_f9": config_list,
+ "open_ssl_vdev": config_list,
+ "aesni_mb_vdev": config_list,
+ "aesni_gcm_vdev": config_list,
+ "kasumi_vdev": config_list,
+ "zuc_vdev": config_list,
+ "snow3g_vdev": config_list,
+ }
+
+
+@requires_link_topology(LinkTopology.NO_LINK)
+class TestCryptodevThroughput(TestSuite):
+ """DPDK Crypto Device Testing Suite."""
+
+ config: Config
+
+ def set_up_suite(self) -> None:
+ """Set up the test suite."""
+ self.throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = (
+ self.config.throughput_test_parameters
+ )
+ self.delta_tolerance: float = self.config.delta_tolerance
+ device: DeviceType | None = get_device_from_str(str(get_ctx().sut_node.crypto_driver))
+ self.driver: DeviceType = device if device else DeviceType.crypto_qat
+ self.buffer_sizes = {}
+
+ for k, v in self.throughput_test_parameters.items():
+ self.buffer_sizes[k] = ListWrapper([int(run["buff_size"]) for run in v])
+
+ def _print_stats(self, test_vals: list[dict[str, int | float | str]]) -> None:
+ element_len = len("Delta Tolerance")
+ border_len = (element_len + 1) * (len(test_vals[0]))
+
+ 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}")
+
+ for test_val in test_vals:
+ for k, v in test_val.items():
+ print(f"|{v:<{element_len}}", end="")
+ print(f"|\n{'='*border_len}")
+
+ def _verify_throughput(
+ self,
+ results: list[CryptodevResults],
+ key: str,
+ ) -> list[dict[str, int | float | str]]:
+ result_list: list[dict[str, int | float | str]] = []
+
+ for result in results:
+ result_dict = {}
+ parameters: dict[str, int | float | str] = list(
+ filter(
+ lambda x: x["buff_size"] == result.buffer_size,
+ self.throughput_test_parameters[key],
+ )
+ )[0]
+ test_result = True
+ for arg, target_val in parameters.items():
+ match arg:
+ case "buff_size":
+ result_dict["Buffer Size"] = target_val
+ continue
+ case "gbps":
+ # result did not meet the given gbps parameter, check if within delta.
+ if target_val > getattr(result, "gbps"):
+ delta = round((1 - (getattr(result, "gbps") / target_val)), 5)
+ if delta > self.delta_tolerance:
+ test_result = False
+ else:
+ delta = round((1 - target_val / getattr(result, arg)), 5)
+ result_dict["gbps delta"] = delta
+ result_dict["delta tolerance"] = self.delta_tolerance
+ result_dict["gbps"] = getattr(result, "gbps")
+ result_dict["gbps target"] = target_val
+ result_dict["passed"] = "PASS" if test_result else "FAIL"
+ result_list.append(result_dict)
+ return result_list
+
+ @func_test
+ def aes_cbc(self) -> None:
+ """aes_cbc test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_only,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aes_cbc"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ def aes_cbc_sha1(self) -> None:
+ """aes_cbc_sha1 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc_sha1" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha1_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ auth_iv_sz=20,
+ digest_sz=12,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aes_cbc_sha1"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc_sha1")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ def aes_cbc_sha2(self) -> None:
+ """aes_cbc_sha2 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc_sha2" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha2_256_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ digest_sz=32,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aes_cbc_sha2"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc_sha2")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def aes_cbc_sha2_digest_16(self) -> None:
+ """aes_cbc_sha2_digest_16 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc_sha2_digest_16" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha2_256_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ digest_sz=16,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aes_cbc_sha2_digest_16"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc_sha2_digest_16")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def aead_aes_gcm(self) -> None:
+ """aead_aes_gcm test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aead_aes_gcm" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.aead,
+ aead_algo=AeadAlgName.aes_gcm,
+ aead_op=EncryptDecryptSwitch.encrypt,
+ aead_key_sz=16,
+ aead_iv_sz=12,
+ aead_aad_sz=16,
+ digest_sz=16,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aead_aes_gcm"],
+ )
+ results = self._verify_throughput(app.run_app(), "aead_aes_gcm")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def aes_docsisbpi(self) -> None:
+ """aes_docsiscpi test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_docsisbpi" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_only,
+ cipher_algo=CipherAlgorithm.aes_docsisbpi,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=32,
+ cipher_iv_sz=16,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aes_docsisbpi"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_docsisbpi")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @crypto_test
+ def sha1_hmac(self) -> None:
+ """sha1_hmac test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "sha1_hmac" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.auth_only,
+ auth_algo=AuthenticationAlgorithm.sha1_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ auth_iv_sz=16,
+ digest_sz=12,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["sha1_hmac"],
+ )
+ results = self._verify_throughput(app.run_app(), "sha1_hmac")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def snow3g_uea2_snow3g_uia2(self) -> None:
+ """snow3g_uea2_snow3g_uia2 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "snow3g_uea2_snow3g_uia2" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.snow3g_uea2,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.snow3g_uia2,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["snow3g_uea2_snow3g_uia2"],
+ )
+ results = self._verify_throughput(app.run_app(), "snow3g_uea2_snow3g_uia2")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def zuc_eea3_zuc_eia3(self) -> None:
+ """zuc_eea3_zuc_eia3 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "zuc_eea3_zuc_eia3" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.zuc_eea3,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.zuc_eia3,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["zuc_eea3_zuc_eia3"],
+ )
+ results = self._verify_throughput(app.run_app(), "zuc_eea3_zuc_eia3")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def kasumi_f8_kasumi_f9(self) -> None:
+ """kasumi_f8 kasumi_f9 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "kasumi_f8_kasumi_f9" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.kasumi_f8,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=8,
+ auth_algo=AuthenticationAlgorithm.kasumi_f9,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["kasumi_f8_kasumi_f9"],
+ )
+ results = self._verify_throughput(app.run_app(), "kasumi_f8_kasumi_f9")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ # BEGIN VDEV TESTS
+
+
+ def aesni_mb_vdev(self) -> None:
+ """aesni_mb virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_aesni_mb and supplied buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aesni_mb_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_aesni_mb0")],
+ devtype=DeviceType.crypto_aesni_mb,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=32,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha1_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aesni_mb_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "aesni_mb_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def aesni_gcm_vdev(self):
+ """aesni_gcm virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_aesni_gcm and supplied buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aesni_gcm_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_aesni_gcm0")],
+ devtype=DeviceType.crypto_aesni_gcm,
+ optype=OperationType.aead,
+ aead_algo=AeadAlgName.aes_gcm,
+ aead_op=EncryptDecryptSwitch.encrypt,
+ aead_key_sz=16,
+ aead_iv_sz=12,
+ aead_aad_sz=16,
+ digest_sz=16,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["aesni_gcm_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "aesni_gcm_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def kasumi_vdev(self) -> None:
+ """Kasmumi virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_kasumi and supplied buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "kasumi_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ vdevs=[VirtualDevice("crypto_kasumi0")],
+ ptest=TestType.throughput,
+ devtype=DeviceType.crypto_qat,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.kasumi_f8,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=8,
+ auth_algo=AuthenticationAlgorithm.kasumi_f9,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["kasumi_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "kasumi_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+
+ @func_test
+ def snow3g_vdev(self) -> None:
+ """snow3g virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_snow3g and supplied buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "snow3g_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_snow3g0")],
+ devtype=DeviceType.crypto_snow3g,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.snow3g_uea2,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.snow3g_uia2,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["snow3g_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "snow3g_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gpbs fell below delta tolerance")
+
+ @func_test
+ def zuc_vdev(self) -> None:
+ """Zuc virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_zuc and supplied buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "zuc_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_zuc0")],
+ devtype=DeviceType.crypto_zuc,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.zuc_eea3,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.zuc_eia3,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=10_000_000,
+ buffer_sz=self.buffer_sizes["zuc_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "zuc_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "gpbs fell below delta tolerance")
+
+ @func_test
+ def open_ssl_vdev(self) -> None:
+ """open_ssl virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "open_ssl_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_openssl0")],
+ devtype=DeviceType.crypto_openssl,
+ optype=OperationType.aead,
+ aead_algo=AeadAlgName.aes_gcm,
+ aead_op=EncryptDecryptSwitch.encrypt,
+ aead_key_sz=16,
+ aead_iv_sz=16,
+ aead_aad_sz=16,
+ digest_sz=16,
+ total_ops=100_000,
+ buffer_sz=self.buffer_sizes["open_ssl_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "open_ssl_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(
+ result["passed"] == "PASS",
+ "gbps fell below delta tolerance",
+ )
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v3 4/5] dts: add crypto test decorator
2026-02-13 19:35 ` [PATCH v3 0/5] dts: add cryptodev testing support Andrew Bailey
` (2 preceding siblings ...)
2026-02-13 19:35 ` [PATCH v3 3/5] dts: add cryptodev throughput test suite Andrew Bailey
@ 2026-02-13 19:35 ` Andrew Bailey
2026-02-27 18:28 ` Patrick Robb
2026-02-13 19:35 ` [PATCH v3 5/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
4 siblings, 1 reply; 41+ messages in thread
From: Andrew Bailey @ 2026-02-13 19:35 UTC (permalink / raw)
To: luca.vizzarro, probb; +Cc: dmarx, dev, Andrew Bailey
Currently, a test case is decorated to signify whether to use Scapy or
TREX. This change allows test suites that do not use a traffic generator
to avoid the overhead of initializing either one.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/configurations/test_run.example.yaml | 1 +
dts/framework/config/test_run.py | 3 +++
dts/framework/test_run.py | 2 +-
dts/framework/test_suite.py | 6 +++++
dts/tests/TestSuite_cryptodev_throughput.py | 30 +++++++++++----------
5 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/dts/configurations/test_run.example.yaml b/dts/configurations/test_run.example.yaml
index c8035fccf0..3eaffc2aa4 100644
--- a/dts/configurations/test_run.example.yaml
+++ b/dts/configurations/test_run.example.yaml
@@ -31,6 +31,7 @@ func_traffic_generator:
# config: "/opt/trex_config/trex_config.yaml" # Additional configuration files. (Leave blank if not required)
perf: false # disable performance testing
func: true # enable functional testing
+crypto: false # disable cryptographic testing
use_virtual_functions: false # use virtual functions (VFs) instead of physical functions
skip_smoke_tests: true # optional
# by removing the `test_suites` field, this test run will run every test suite available
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 6c292a3675..39f2c7cdf6 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -481,6 +481,8 @@ class TestRunConfiguration(FrozenModel):
perf: bool
#: Whether to run functional tests.
func: bool
+ #: Whether to run cryptography tests.
+ crypto: bool
#: Whether to run the testing with virtual functions instead of physical functions
use_virtual_functions: bool
#: Whether to skip smoke tests.
@@ -522,6 +524,7 @@ def filter_tests(
for tt in t.test_cases
if (tt.test_type is TestCaseType.FUNCTIONAL and self.func)
or (tt.test_type is TestCaseType.PERFORMANCE and self.perf)
+ or (tt.test_type is TestCaseType.CRYPTO and self.crypto)
),
)
for t in test_suites
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 36e6c5a44c..ada628c59e 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -349,7 +349,7 @@ def next(self) -> State | None:
if test_run.config.use_virtual_functions:
test_run.ctx.topology.instantiate_vf_ports()
- if test_run.ctx.sut_node.cryptodevs:
+ if test_run.ctx.sut_node.cryptodevs and test_run.config.crypto:
test_run.ctx.topology.instantiate_crypto_vf_ports()
test_run.ctx.topology.configure_cryptodevs("dpdk")
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 9c57e343ac..e86096cefe 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -174,6 +174,8 @@ def filter_test_cases(
perf_test_cases.add(test_case)
case TestCaseType.FUNCTIONAL:
func_test_cases.add(test_case)
+ case TestCaseType.CRYPTO:
+ pass
if test_case_sublist_copy:
raise ConfigurationError(
@@ -279,6 +281,8 @@ class TestCaseType(Enum):
FUNCTIONAL = auto()
#:
PERFORMANCE = auto()
+ #:
+ CRYPTO = auto()
class TestCase(TestProtocol, Protocol[TestSuiteMethodType]):
@@ -331,6 +335,8 @@ def _decorator(func: TestSuiteMethodType) -> type[TestCase]:
func_test: Callable = TestCase.make_decorator(TestCaseType.FUNCTIONAL)
#: The decorator for performance test cases.
perf_test: Callable = TestCase.make_decorator(TestCaseType.PERFORMANCE)
+#: The decorator for cryptography test cases.
+crypto_test: Callable = TestCase.make_decorator(TestCaseType.CRYPTO)
@dataclass
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index 871e47ae63..387afe8bcd 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -32,7 +32,7 @@
from api.test import verify
from framework.context import get_ctx
from framework.exception import SkippedTestException
-from framework.test_suite import BaseConfig, TestSuite, func_test
+from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
config_list: list[dict[str, int | float | str]] = [
@@ -142,7 +142,7 @@ def _verify_throughput(
result_list.append(result_dict)
return result_list
- @func_test
+ @crypto_test
def aes_cbc(self) -> None:
"""aes_cbc test.
@@ -172,6 +172,7 @@ def aes_cbc(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+ @crypto_test
def aes_cbc_sha1(self) -> None:
"""aes_cbc_sha1 test.
@@ -206,6 +207,7 @@ def aes_cbc_sha1(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
+ @crypto_test
def aes_cbc_sha2(self) -> None:
"""aes_cbc_sha2 test.
@@ -239,7 +241,7 @@ def aes_cbc_sha2(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aes_cbc_sha2_digest_16(self) -> None:
"""aes_cbc_sha2_digest_16 test.
@@ -273,7 +275,7 @@ def aes_cbc_sha2_digest_16(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aead_aes_gcm(self) -> None:
"""aead_aes_gcm test.
@@ -305,7 +307,7 @@ def aead_aes_gcm(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aes_docsisbpi(self) -> None:
"""aes_docsiscpi test.
@@ -366,7 +368,7 @@ def sha1_hmac(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def snow3g_uea2_snow3g_uia2(self) -> None:
"""snow3g_uea2_snow3g_uia2 test.
@@ -401,7 +403,7 @@ def snow3g_uea2_snow3g_uia2(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def zuc_eea3_zuc_eia3(self) -> None:
"""zuc_eea3_zuc_eia3 test.
@@ -436,7 +438,7 @@ def zuc_eea3_zuc_eia3(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def kasumi_f8_kasumi_f9(self) -> None:
"""kasumi_f8 kasumi_f9 test.
@@ -473,7 +475,7 @@ def kasumi_f8_kasumi_f9(self) -> None:
# BEGIN VDEV TESTS
-
+ @crypto_test
def aesni_mb_vdev(self) -> None:
"""aesni_mb virtual device test.
@@ -510,7 +512,7 @@ def aesni_mb_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aesni_gcm_vdev(self):
"""aesni_gcm virtual device test.
@@ -543,7 +545,7 @@ def aesni_gcm_vdev(self):
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def kasumi_vdev(self) -> None:
"""Kasmumi virtual device test.
@@ -579,7 +581,7 @@ def kasumi_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def snow3g_vdev(self) -> None:
"""snow3g virtual device test.
@@ -616,7 +618,7 @@ def snow3g_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gpbs fell below delta tolerance")
- @func_test
+ @crypto_test
def zuc_vdev(self) -> None:
"""Zuc virtual device test.
@@ -653,7 +655,7 @@ def zuc_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "gpbs fell below delta tolerance")
- @func_test
+ @crypto_test
def open_ssl_vdev(self) -> None:
"""open_ssl virtual device test.
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v3 5/5] dts: automate VFIO-PCI modprobe in node setup
2026-02-13 19:35 ` [PATCH v3 0/5] dts: add cryptodev testing support Andrew Bailey
` (3 preceding siblings ...)
2026-02-13 19:35 ` [PATCH v3 4/5] dts: add crypto test decorator Andrew Bailey
@ 2026-02-13 19:35 ` Andrew Bailey
2026-02-27 18:33 ` Patrick Robb
4 siblings, 1 reply; 41+ messages in thread
From: Andrew Bailey @ 2026-02-13 19:35 UTC (permalink / raw)
To: luca.vizzarro, probb; +Cc: dmarx, dev, Andrew Bailey
Currently, users must modprobe VFIO-PCI before running DTS when using a
non-mellanox NIC. This patch automates the process on test run start up.
In addition, if the SUT is expected to use a subset of a few QAT devices,
VFIO-PCI must be loaded in a separate manner. This patch adds this
distinction.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/framework/testbed_model/linux_session.py | 24 ++++++++++++++++++++
dts/framework/testbed_model/os_session.py | 4 ++++
2 files changed, 28 insertions(+)
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 27a5f48ecf..a761ca11fe 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -224,6 +224,30 @@ def devbind_script_path(self) -> PurePath:
"""
raise InternalError("Accessed devbind script path before setup.")
+ def load_vfio(self, pf_port: Port) -> None:
+ """Overrides :meth:`~os_session.OSSession,load_vfio`."""
+ cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
+ device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
+ if device and device.group(1) in ["37c8", "0435", "19e2"]:
+ self.send_command(
+ "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe -r vfio_virqfd; modprobe -r vfio",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
+ )
+ self.send_command(
+ "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
+ privileged=True,
+ )
+ else:
+ self.send_command("modprobe vfio-pci")
+ self.refresh_lshw()
+
def create_crypto_vfs(self, pf_port: list[Port]) -> None:
"""Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index 2eeeea6967..4a4fc1f34a 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -615,6 +615,10 @@ def configure_port_mtu(self, mtu: int, port: Port) -> None:
port: Port to set `mtu` on.
"""
+ @abstractmethod
+ def load_vfio(self, pf_port: Port) -> None:
+ """Load the vfio module according to the device type of the given port."""
+
@abstractmethod
def create_crypto_vfs(self, pf_ports: list[Port]) -> None:
"""Creatues virtual functions for each port in 'pf_ports'.
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [PATCH v3 1/5] dts: add find float method to text parser
2026-02-13 19:35 ` [PATCH v3 1/5] dts: add find float method to text parser Andrew Bailey
@ 2026-02-25 21:05 ` Patrick Robb
0 siblings, 0 replies; 41+ messages in thread
From: Patrick Robb @ 2026-02-25 21:05 UTC (permalink / raw)
To: Andrew Bailey; +Cc: luca.vizzarro, dmarx, dev
Reviewed-by: Patrick Robb <probb@iol.unh.edu>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v3 2/5] dts: add cryptodev package to DTS
2026-02-13 19:35 ` [PATCH v3 2/5] dts: add cryptodev package to DTS Andrew Bailey
@ 2026-02-25 21:06 ` Patrick Robb
2026-02-26 19:13 ` Andrew Bailey
2026-02-27 16:15 ` Patrick Robb
1 sibling, 1 reply; 41+ messages in thread
From: Patrick Robb @ 2026-02-25 21:06 UTC (permalink / raw)
To: Andrew Bailey; +Cc: luca.vizzarro, dmarx, dev
On Fri, Feb 13, 2026 at 2:36 PM Andrew Bailey <abailey@iol.unh.edu> wrote:
>
> Running the DPDK test crypto performance application is essential for
> testing cryptodev performance in DTS. This application takes numerous
> arguments and can be run in four modes; Throughput, Latency,
> PMD-cyclecount, and Verify. The package to add in this commit allows for
> this application to be run in any of these modes with user supplied
> arguments.
>
> Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
> ---
> dts/api/cryptodev/__init__.py | 134 +++++
> dts/api/cryptodev/config.py | 499 +++++++++++++++++++
> dts/api/cryptodev/types.py | 185 +++++++
> dts/configurations/nodes.example.yaml | 6 +
> dts/framework/config/node.py | 4 +
> dts/framework/params/types.py | 65 +++
> dts/framework/remote_session/dpdk_shell.py | 5 +-
> dts/framework/test_run.py | 5 +
> dts/framework/testbed_model/linux_session.py | 54 ++
> dts/framework/testbed_model/node.py | 10 +
> dts/framework/testbed_model/os_session.py | 30 ++
> dts/framework/testbed_model/topology.py | 89 +++-
> 12 files changed, 1083 insertions(+), 3 deletions(-)
> create mode 100644 dts/api/cryptodev/__init__.py
> create mode 100644 dts/api/cryptodev/config.py
> create mode 100644 dts/api/cryptodev/types.py
>
> diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
> new file mode 100644
> index 0000000000..9955657398
> --- /dev/null
> +++ b/dts/api/cryptodev/__init__.py
> @@ -0,0 +1,134 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2025 University of New Hampshire
> +
> +"""Cryptodev-pmd non-interactive shell.
> +
> +Typical usage example in a TestSuite::
> +
> + cryptodev = CryptodevPmd(CryptoPmdParams)
> + stats = cryptodev.run_app()
> +"""
> +
> +import re
> +from typing import TYPE_CHECKING, Any
> +
> +from typing_extensions import Unpack
> +
> +from api.cryptodev.config import CryptoPmdParams, TestType
> +from api.cryptodev.types import (
> + CryptodevResults,
> + LatencyResults,
> + PmdCyclecountResults,
> + ThroughputResults,
> + VerifyResults,
> +)
> +from framework.context import get_ctx
> +from framework.exception import RemoteCommandExecutionError, SkippedTestException
> +from framework.remote_session.dpdk_shell import compute_eal_params
> +
> +if TYPE_CHECKING:
> + from framework.params.types import CryptoPmdParamsDict
> +from pathlib import PurePath
> +
> +
> +class Cryptodev:
> + """non-interactive cryptodev application.
> +
> + Attributes:
> + _app_params: app parameters to pass to dpdk-test-crypto-perf application
> + """
> +
> + _app_params: dict[str, Any]
> +
> + def __init__(self, **app_params: Unpack["CryptoPmdParamsDict"]) -> None:
> + """Initialize the cryptodev application.
> +
> + Args:
> + app_params: The application parameters as keyword arguments.
> +
> + Raises:
> + ValueError: if the build environment is `None`
> + """
> + self._app_params = {}
> + for k, v in app_params.items():
> + if v is not None:
> + self._app_params[k] = (
> + self.vector_directory.joinpath(str(v)) if k == "test_file" else v
> + )
> + dpdk = get_ctx().dpdk.build
> + if dpdk is None:
> + raise ValueError("No DPDK build environment exists.")
> + self._path = dpdk.get_app("test-crypto-perf")
> +
> + @property
> + def path(self) -> PurePath:
> + """Get the path to the cryptodev application.
> +
> + Returns:
> + The path to the cryptodev application.
> + """
> + return PurePath(self._path)
> +
> + @property
> + def vector_directory(self) -> PurePath:
> + """Get the path to the cryptodev vector files.
> +
> + Returns:
> + The path to the cryptodev vector files.
> + """
> + return get_ctx().dpdk_build.remote_dpdk_tree_path.joinpath("app/test-crypto-perf/data/")
> +
> + def run_app(self, numvfs: int | None = 0) -> list[CryptodevResults]:
> + """Run the cryptodev application with the given app parameters.
> +
> + Raises:
> + SkippedTestException: If the device type is not supported on the main session.
> + RemoteCommandExecutionError: If there is an error running the command.
> +
> + Returns:
> + list[CryptodevResults]: The list of parsed results for the cryptodev application.
Missing the args docstring. Speaking of which, What is the theory
behind passing a vf count from the testsuites to the app? In terms of
what the testsuites require, and whether that aspect should be
configurable from the user side, why the default is zero, etc.
> + """
> + try:
> + result = get_ctx().dpdk.run_dpdk_app(
> + self.path,
> + compute_eal_params(
> + CryptoPmdParams(
> + allowed_ports=get_ctx().topology.get_crypto_vfs(numvfs),
> + memory_channels=get_ctx().dpdk.config.memory_channels,
> + **self._app_params,
> + ),
> + ),
> + timeout=120,
> + )
> + except RemoteCommandExecutionError as e:
> + if "Crypto device type does not support capabilities requested" in e._command_stderr:
> + raise SkippedTestException(
> + f"{self._app_params['devtype']} does not support the requested capabilities"
> + )
> + elif "Failed to initialise requested crypto device type" in e._command_stderr:
> + raise SkippedTestException(
> + f"could not run application with devtype {self._app_params['devtype']}"
> + )
> + elif "failed to parse device" in e._command_stderr:
> + raise SkippedTestException(
> + f"dependencies missing for virtual device {self._app_params['vdevs'][0].name}"
> + )
> + raise e
> +
> + regex = r"^\s+\d+.*$"
> + parser_options = re.MULTILINE
> + parser: type[CryptodevResults]
> +
> + match self._app_params["ptest"]:
> + case TestType.throughput:
> + parser = ThroughputResults
> + case TestType.latency:
> + regex = r"total operations:.*time[^\n]*"
> + parser_options |= re.DOTALL
> + parser = LatencyResults
> + case TestType.pmd_cyclecount:
> + parser = PmdCyclecountResults
> + case TestType.verify:
> + parser = VerifyResults
> +
> + return [parser.parse(line) for line in re.findall(regex, result.stdout, parser_options)]
> diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
> new file mode 100644
> index 0000000000..c6ec083a43
> --- /dev/null
> +++ b/dts/api/cryptodev/config.py
> @@ -0,0 +1,499 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2025 University of New Hampshire
> +"""Module containing types and parameter classes for cryptodev-pmd application."""
> +
> +from dataclasses import dataclass, field
> +from enum import auto
> +from typing import Literal
> +
> +from framework.params import Params, Switch
> +from framework.params.eal import EalParams
> +from framework.utils import StrEnum
> +
> +Silent = Literal[""]
> +
> +
> +class DeviceType(StrEnum):
> + """Enum for cryptodev device types.
> +
> + Attributes:
> + crypto_aesni_gcm: AES-NI GCM device type.
> + crypto_aesni_mb: AES-NI MB device type.
> + crypto_armv8: ARMv8 device type.
> + crypto_cn10k: CN10K device type.
> + crypto_cn9k: CN9K device type.
> + crypto_dpaa_sec: DPAA SEC device type.
> + crypto_dpaa2_sec: DPAA2 SEC device type.
> + crypto_kasumi: KASUMI device type.
> + crypto_mvsam: MVSAM device type.
> + crypto_null: NULL device type.
> + crypto_octeontx: OCTEONTX device type.
> + crypto_openssl: OpenSSL device type.
> + crypto_qat: QAT device type.
> + crypto_scheduler: Scheduler device type.
> + crypto_snow3g: SNOW3G device type.
> + crypto_zuc: ZUC device type.
> + """
> +
> + crypto_aesni_gcm = auto()
> + crypto_aesni_mb = auto()
> + crypto_armv8 = auto()
> + crypto_cn10k = auto()
> + crypto_cn9k = auto()
> + crypto_dpaa_sec = auto()
> + crypto_dpaa2_sec = auto()
> + crypto_kasumi = auto()
> + crypto_mvsam = auto()
> + crypto_null = auto()
> + crypto_octeontx = auto()
> + crypto_openssl = auto()
> + crypto_qat = auto()
> + crypto_scheduler = auto()
> + crypto_snow3g = auto()
> + crypto_zuc = auto()
> +
> +
> +def get_device_from_str(device: str) -> DeviceType | None:
> + """Get a device type from the given string.
> +
> + Args:
> + device: the string representation of the desired device.
> +
> + Returns:
> + the device as a DeviceType object.
> + """
> + for item in DeviceType:
> + if device in item.value:
> + return item
> + return None
> +
> +
> +class OperationType(StrEnum):
> + """Enum for cryptodev operation types.
> +
> + Attributes:
> + aead: AEAD operation type.
> + auth_only: Authentication only operation type.
> + auth_then_cipher: Authentication then cipher operation type.
> + cipher_only: Cipher only operation type.
> + cipher_then_auth: Cipher then authentication operation type.
> + docsis: DOCSIS operation type.
> + ecdsa_p192r1 = ECDSA P-192R1 operation type.
> + ecdsa_p224r1 = ECDSA P-224R1 operation type.
> + ecdsa_p256r1: ECDSA P-256R1 operation type.
> + ecdsa_p384r1 = ECDSA P-384R1 operation type.
> + ecdsa_p521r1 = ECDSA P-521R1 operation type.
> + eddsa_25519: EdDSA 25519 operation type.
> + modex: Modex operation type.
> + ipsec: IPsec operation type.
> + pdcp: PDCP operation type.
> + rsa: RSA operation type.
> + sm2: SM2 operation type.
> + tls_record: TLS record operation type.
> + """
> +
> + aead = auto()
> + auth_only = "auth-only"
> + auth_then_cipher = "auth-then-cipher"
> + cipher_only = "cipher-only"
> + cipher_then_auth = "cipher-then-auth"
> + docsis = auto()
> + ecdsa_p192r1 = auto()
> + ecdsa_p224r1 = auto()
> + ecdsa_p256r1 = auto()
> + ecdsa_p384r1 = auto()
> + ecdsa_p521r1 = auto()
> + eddsa_25519 = auto()
> + modex = auto()
> + ipsec = auto()
> + pdcp = auto()
> + rsa = auto()
> + sm2 = auto()
> + tls_record = "tls-record"
> +
> + def __str__(self) -> str:
> + """Override str to return the value."""
> + return self.value
> +
> +
> +class CipherAlgorithm(StrEnum):
> + """Enum for cryptodev cipher algorithms.
> +
> + Attributes:
> + aes_cbc: AES CBC cipher algorithm.
> + aes_ctr: AES CTR cipher algorithm.
> + aes_docsisbpi: AES DOCSIS BPI cipher algorithm.
> + aes_ecb: AES ECB cipher algorithm.
> + aes_f8: AES F8 cipher algorithm.
> + aes_xts: AES XTS cipher algorithm.
> + arc4: ARC4 cipher algorithm.
> + null: NULL cipher algorithm.
> + three_des_cbc: 3DES CBC cipher algorithm.
> + three_des_ctr: 3DES CTR cipher algorithm.
> + three_des_ecb: 3DES ECB cipher algorithm.
> + kasumi_f8: KASUMI F8 cipher algorithm.
> + snow3g_uea2: SNOW3G UEA2 cipher algorithm.
> + zuc_eea3: ZUC EEA3 cipher algorithm.
> + """
> +
> + aes_cbc = "aes-cbc"
> + aes_ctr = "aes-ctr"
> + aes_docsisbpi = "aes-docsisbpi"
> + aes_ecb = "aes-ecb"
> + aes_f8 = "aes-f8"
> + aes_gcm = "aes-gcm"
> + aes_xts = "aes-xts"
> + arc4 = auto()
> + null = auto()
> + three_des_cbc = "3des-cbc"
> + three_des_ctr = "3des-ctr"
> + three_des_ecb = "3des-ecb"
> + kasumi_f8 = "kasumi-f8"
> + snow3g_uea2 = "snow3g-uea2"
> + zuc_eea3 = "zuc-eea3"
> +
> + def __str__(self):
> + """Override str to return the value."""
> + return self.value
> +
> +
> +class AuthenticationAlgorithm(StrEnum):
> + """Enum for cryptodev authentication algorithms.
> +
> + Attributes:
> + aes_cbc_mac: AES CBC MAC authentication algorithm.
> + aes_cmac: AES CMAC authentication algorithm.
> + aes_gmac: AES GMAC authentication algorithm.
> + aes_xcbc_mac: AES XCBC MAC authentication algorithm.
> + kasumi_f9: KASUMI F9 authentication algorithm.
> + md5: MD5 authentication algorithm.
> + md5_hmac: MD5 HMAC authentication algorithm.
> + sha1: SHA1 authentication algorithm.
> + sha1_hmac: SHA1 HMAC authentication algorithm.
> + sha2_224: SHA2-224 authentication algorithm.
> + sha2_224_hmac: SHA2-224 HMAC authentication algorithm.
> + sha2_256: SHA2-256 authentication algorithm.
> + sha2_256_hmac: SHA2-256 HMAC authentication algorithm.
> + sha2_384: SHA2-384 authentication algorithm.
> + sha2_384_hmac: SHA2-384 HMAC authentication algorithm.
> + sha2_512: SHA2-512 authentication algorithm.
> + sha2_512_hmac: SHA2-512 HMAC authentication algorithm.
> + snow3g_uia2: SNOW3G UIA2 authentication algorithm.
> + zuc_eia3: ZUC EIA3 authentication algorithm.
> + """
> +
> + aes_cbc_mac = "aes-cbc-mac"
> + aes_cmac = "aes-cmac"
> + aes_gmac = "aes-gmac"
> + aes_xcbc_mac = "aes-xcbc-mac"
> + kasumi_f9 = "kasumi-f9"
> + md5 = auto()
> + md5_hmac = "md5-hmac"
> + sha1 = auto()
> + sha1_hmac = "sha1-hmac"
> + sha2_224 = "sha2-224"
> + sha2_224_hmac = "sha2-224-hmac"
> + sha2_256 = "sha2-256"
> + sha2_256_hmac = "sha2-256-hmac"
> + sha2_384 = "sha2-384"
> + sha2_384_hmac = "sha2-384-hmac"
> + sha2_512 = "sha2-512"
> + sha2_512_hmac = "sha2-512-hmac"
> + snow3g_uia2 = "snow3g-uia2"
> + zuc_eia3 = "zuc-eia3"
> +
> + def __str__(self) -> str:
> + """Override str to return the value."""
> + return self.value
> +
> +
> +class TestType(StrEnum):
> + """Enum for cryptodev test types.
> +
> + Attributes:
> + latency: Latency test type.
> + pmd_cyclecount: PMD cyclecount test type.
> + throughput: Throughput test type.
> + verify: Verify test type.
> + """
> +
> + latency = auto()
> + pmd_cyclecount = "pmd-cyclecount"
> + throughput = auto()
> + verify = auto()
> +
> + def __str__(self) -> str:
> + """Override str to return the value."""
> + return self.value
> +
> +
> +class EncryptDecryptSwitch(StrEnum):
> + """Enum for cryptodev encrypt/decrypt operations.
> +
> + Attributes:
> + decrypt: Decrypt operation.
> + encrypt: Encrypt operation.
> + """
> +
> + decrypt = auto()
> + encrypt = auto()
> +
> +
> +class AuthenticationOpMode(StrEnum):
> + """Enum for cryptodev authentication operation modes.
> +
> + Attributes:
> + generate: Generate operation mode.
> + verify: Verify operation mode.
> + """
> +
> + generate = auto()
> + verify = auto()
> +
> +
> +class AeadAlgName(StrEnum):
> + """Enum for cryptodev AEAD algorithms.
> +
> + Attributes:
> + aes_ccm: AES CCM algorithm.
> + aes_gcm: AES GCM algorithm.
> + """
> +
> + aes_ccm = "aes-ccm"
> + aes_gcm = "aes-gcm"
> +
> + def __str__(self):
> + """Override str to return the value."""
> + return self.value
> +
> +
> +class AsymOpMode(StrEnum):
> + """Enum for cryptodev asymmetric operation modes.
> +
> + Attributes:
> + decrypt: Decrypt operation mode.
> + encrypt: Encrypt operation mode.
> + sign: Sign operation mode.
> + verify: Verify operation mode.
> + """
> +
> + decrypt = auto()
> + encrypt = auto()
> + sign = auto()
> + verify = auto()
> +
> +
> +class PDCPDomain(StrEnum):
> + """Enum for cryptodev PDCP domains.
> +
> + Attributes:
> + control: Control domain.
> + user: User domain.
> + """
> +
> + control = auto()
> + user = auto()
> +
> +
> +class RSAPrivKeyType(StrEnum):
> + """Enum for cryptodev RSA private key types.
> +
> + Attributes:
> + exp: Exponent key type.
> + qt: QT key type.
> + """
> +
> + exp = auto()
> + qt = auto()
> +
> +
> +class TLSVersion(StrEnum):
> + """Enum for cryptodev TLS versions.
> +
> + Attributes:
> + DTLS1_2: DTLS 1.2 version.
> + TLS1_2: TLS 1.2 version.
> + TLS1_3: TLS 1.3 version.
> + """
> +
> + DTLS1_2 = "DTLS1.2"
> + TLS1_2 = "TLS1.2"
> + TLS1_3 = "TLS1.3"
> +
> + def __str__(self):
> + """Override str to return the value."""
> + return self.value
> +
> +
> +class RSAPrivKeytype(StrEnum):
> + """Enum for cryptodev RSA private key types.
> +
> + Attributes:
> + exp: Exponent key type.
> + qt: QT key type.
> + """
> +
> + exp = auto()
> + qt = auto()
> +
> +
> +class BurstSizeRange:
> + """Class for burst size parameter.
> +
> + Attributes:
> + burst_size: The burst size range, this list must be less than 32 elements.
> + """
> +
> + burst_size: int | list[int]
> +
> + def __init__(self, min: int, inc: int, max: int) -> None:
> + """Initialize the burst size range.
> +
> + Raises:
> + ValueError: If the burst size range is more than 32 elements.
> + """
Can you include an args section for the docstring on this init
function? The same goes for other init functions in your patchseries.
> + if (max - min) % inc > 32:
> + raise ValueError("Burst size range must be less than 32 elements.")
> + self.burst_size = list(range(min, max + 1, inc))
> +
> +
> +class ListWrapper:
> + """Class for wrapping a list of integers.
> +
> + One of the arguments for the cryptodev application is a list of integers. However, when
> + passing a list directly, it causes a syntax error in the command line. This class wraps
> + a list of integers and converts it to a comma-separated string for a proper command.
> + """
> +
> + def __init__(self, value: list[int]) -> None:
> + """Initialize the list wrapper."""
> + self.value = value
> +
> + def __str__(self) -> str:
> + """Convert the list to a comma-separated string."""
> + return ",".join(str(v) for v in self.value)
> +
> +
> +@dataclass(slots=True, kw_only=True)
> +class CryptoPmdParams(EalParams):
> + """Parameters for cryptodev-pmd application.
> +
> + Attributes:
> + aead_aad_sz: set the size of AEAD AAD.
> + aead_algo: set AEAD algorithm name from class `AeadAlgName`.
> + aead_iv_sz: set the size of AEAD iv.
> + aead_key_sz: set the size of AEAD key.
> + aead_op: set AEAD operation mode from class `EncryptDecryptSwitch`.
> + asym_op: set asymmetric operation mode from class `AsymOpMode`.
> + auth_algo: set authentication algorithm name.
> + auth_aad_sz: set the size of authentication AAD.
> + auth_iv_sz: set the size of authentication iv.
> + auth_key_sz: set the size of authentication key.
> + auth_op: set authentication operation mode from class `AuthenticationOpMode`.
> + buffer_sz: Set the size of a single packet (plaintext or ciphertext in it).
> + burst_sz: Set the number of packets per burst. This can be set as a single value or
> + range of values defined by class `BurstSizeRange`. Default is 16.
> + burst_sz: Set the number of packets per burst. This can be set as a single value or
> + range of values defined by class `BurstSizeRange`. Default is 16.
> + cipher_algo: Set cipher algorithm name from class `CipherAlgorithm`.
> + cipher_iv_sz: set the size of cipher iv.
> + cipher_key_sz: set the size of cipher key.
> + cipher_op: set cipher operation mode from class `EncryptDecryptSwitch`.
> + csv_friendly: Enable test result output CSV friendly rather than human friendly.
> + desc_nb: set the number of descriptors for each crypto device.
> + devtype: Set the device name from class `DeviceType`.
> + digest_sz: set the size of digest.
> + docsis_hdr_sz: set DOCSIS header size(n) in bytes.
> + enable_sdap: enable service data adaptation protocol.
> + imix: Set the distribution of packet sizes. A list of weights must be passed, containing the
> + same number of items than buffer-sz, so each item in this list will be the weight of the
> + packet size on the same position in the buffer-sz parameter (a list has to be passed in
> + that parameter).
> + low_prio_qp_mask: set low priority queue pairs set in the hexadecimal mask. This is an
> + optional parameter, if not set all queue pairs will be on the same high priority.
> + modex_len: set modex length for asymmetric crypto perf test. Supported lengths are 60,
> + 128, 255, 448. Default length is 128.
> + optype: Set operation type from class `OpType`.
> + out_of_place: Enable out-of-place crypto operations mode.
> + pdcp_sn_sz: set PDCP sequebce number size(n) in bits. Valid values of n are 5/7/12/15/18.
> + pdcp_domain: Set PDCP domain to specify short_mac/control/user plane from class
> + `PDCPDomain`.
> + pdcp_ses_hfn_en: enable fixed session based HFN instead of per packet HFN.
> + pmd_cyclecount_delay_pmd: Add a delay (in milliseconds) between enqueue and dequeue in
> + pmd-cyclecount benchmarking mode (useful when benchmarking hardware acceleration).
> + pool_sz: Set the number if mbufs to be allocated in the mbuf pool.
> + ptest: Set performance throughput test type from class `TestType`.
> + rsa_modlen: Set RSA modulus length (in bits) for asymmetric crypto perf test.
> + To be used with RSA asymmetric crypto ops.Supported lengths are 1024, 2048, 4096, 8192.
> + Default length is 1024.
> + rsa_priv_keytype: set RSA private key type from class `RSAPrivKeytype`. To be used with RSA
> + asymmetric crypto ops.
> + segment_sz: Set the size of the segment to use, for Scatter Gather List testing. Use list of
> + values in buffer-sz in descending order if segment-sz is used. By default, it is set to
> + the size of the maximum buffer size, including the digest size, so a single segment is
> + created.
> + sessionless: Enable session-less crypto operations mode.
> + shared_session: Enable sharing sessions between all queue pairs on a single crypto PMD. This
> + can be useful for benchmarking this setup, or finding and debugging concurrency errors
> + that can occur while using sessions on multiple lcores simultaneously.
> + silent: Disable options dump.
> + test_file: Set test vector file path. See the Test Vector File chapter.
> + test_name: Set specific test name section in the test vector file.
> + tls_version: Set TLS/DTLS protocol version for perf test from class `TLSVersion`.
> + Default is TLS1.2.
> + total_ops: Set the number of total operations performed.
> + """
> +
> + aead_aad_sz: int | None = field(default=None, metadata=Params.long("aead-aad-sz"))
> + aead_algo: AeadAlgName | None = field(default=None, metadata=Params.long("aead-algo"))
> + aead_iv_sz: int | None = field(default=None, metadata=Params.long("aead-iv-sz"))
> + aead_key_sz: int | None = field(default=None, metadata=Params.long("aead-key-sz"))
> + aead_op: EncryptDecryptSwitch | None = field(default=None, metadata=Params.long("aead-op"))
> + asym_op: AsymOpMode | None = field(default=None, metadata=Params.long("asym-op"))
> + auth_algo: AuthenticationAlgorithm | None = field(
> + default=None, metadata=Params.long("auth-algo")
> + )
> + auth_iv_sz: int | None = field(default=None, metadata=Params.long("auth-iv-sz"))
> + auth_key_sz: int | None = field(default=None, metadata=Params.long("auth-key-sz"))
> + auth_op: AuthenticationOpMode | None = field(default=None, metadata=Params.long("auth-op"))
> + buffer_sz: BurstSizeRange | ListWrapper | int | None = field(
> + default=None, metadata=Params.long("buffer-sz")
> + )
> + burst_sz: BurstSizeRange | ListWrapper | int | None = field(
> + default=None, metadata=Params.long("burst-sz")
> + )
> + cipher_algo: CipherAlgorithm | None = field(default=None, metadata=Params.long("cipher-algo"))
> + cipher_iv_sz: int | None = field(default=None, metadata=Params.long("cipher-iv-sz"))
> + cipher_key_sz: int | None = field(default=None, metadata=Params.long("cipher-key-sz"))
> + cipher_op: EncryptDecryptSwitch | None = field(default=None, metadata=Params.long("cipher-op"))
> + csv_friendly: Switch = field(default=None, metadata=Params.long("csv-friendly"))
> + desc_nb: int | None = field(default=None, metadata=Params.long("desc-nb"))
> + devtype: DeviceType = field(metadata=Params.long("devtype"))
> + digest_sz: int | None = field(default=None, metadata=Params.long("digest-sz"))
> + docsis_hdr_sz: int | None = field(default=None, metadata=Params.long("docsis-hdr-sz"))
> + enable_sdap: Switch = None
> + imix: int | None = field(default=None, metadata=Params.long("imix"))
> + low_prio_qp_mask: int | None = field(default=None, metadata=Params.convert_value(hex))
> + modex_len: int | None = field(default=None, metadata=Params.long("modex-len"))
> + optype: OperationType | None = field(default=None, metadata=Params.long("optype"))
> + out_of_place: Switch = None
> + pdcp_sn_sz: int | None = None
> + pdcp_domain: PDCPDomain | None = field(default=None, metadata=Params.long("pdcp-domain"))
> + pdcp_ses_hfn_en: Switch | None = field(default=None, metadata=Params.long("pdcp-ses-hfn-en"))
> + pmd_cyclecount_delay_pmd: int | None = field(
> + default=None, metadata=Params.long("pmd-cyclecount-delay-pmd")
> + )
> + pool_sz: int | None = field(default=None, metadata=Params.long("pool-sz"))
> + ptest: TestType = field(default=TestType.throughput, metadata=Params.long("ptest"))
> + rsa_modlen: int | None = field(default=None, metadata=Params.long("rsa-modlen"))
> + rsa_priv_keytype: RSAPrivKeytype | None = field(
> + default=None, metadata=Params.long("rsa-priv-keytype")
> + )
> + segment_sz: int | None = field(default=None, metadata=Params.long("segment-sz"))
> + sessionless: Switch = None
> + shared_session: Switch = None
> + silent: Silent | None = field(default="", metadata=Params.long("silent"))
> + test_file: str | None = field(default=None, metadata=Params.long("test-file"))
> + test_name: str | None = field(default=None, metadata=Params.long("test-name"))
> + tls_version: TLSVersion | None = field(default=None, metadata=Params.long("tls-version"))
> + total_ops: int | None = field(default=100000, metadata=Params.long("total-ops"))
> diff --git a/dts/api/cryptodev/types.py b/dts/api/cryptodev/types.py
> new file mode 100644
> index 0000000000..df73a86fa4
> --- /dev/null
> +++ b/dts/api/cryptodev/types.py
> @@ -0,0 +1,185 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2025 University of New Hampshire
> +
> +"""Cryptodev types module.
> +
> +Exposes types used in the Cryptodev API.
> +"""
> +
> +from dataclasses import dataclass, field
> +
> +from framework.parser import TextParser
> +
> +
> +@dataclass
> +class CryptodevResults(TextParser):
> + """Base class for all cryptodev results."""
> +
> + buffer_size: int
> +
> + def __iter__(self):
> + """Iteration method to parse result objects.
> +
> + Yields:
> + tuple[str, int | float]: a field name and its value.
> + """
> + for field_name in self.__dataclass_fields__:
> + yield field_name, getattr(self, field_name)
> +
> +
> +@dataclass
> +class ThroughputResults(CryptodevResults):
> + """A parser for throughput test output."""
> +
> + #:
> + lcore_id: int = field(metadata=TextParser.find_int(r"\s*(\d+)"))
> + #: buffer size used in the run
> + buffer_size: int = field(
> + metadata=TextParser.find_int(r"\s+(?:\d+\s+)(\d+)"),
> + )
> + #: burst size used in the run
> + burst_size: int = field(
> + metadata=TextParser.find_int(r"\s+(?:\d+\s+){2}(\d+)"),
> + )
> + #: total packets enqueued
> + enqueued: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){3}(\d+)"))
> + #: total packets dequeued
> + dequeued: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){4}(\d+)"))
> + #: packets that failed enqueue
> + failed_enqueue: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){5}(\d+)"))
> + #: packets that failed dequeue
> + failed_dequeue: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){6}(\d+)"))
> + #: mega operations per second
> + mops: float = field(metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}([\d.]+)"))
> + #: gigabits per second
> + gbps: float = field(metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}(?:[\d.]+\s+)([\d.]+)"))
> + #: cpu cycles per buffer
> + cycles_per_buffer: float = field(
> + metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}(?:[\d.]+\s+){2}([\d.]+)")
> + )
> +
> +
> +@dataclass
> +class LatencyResults(CryptodevResults):
> + """A parser for latency test output."""
> +
> + #: buffer size ran with app
> + buffer_size: int = field(
> + metadata=TextParser.find_int(r"Buf(?:.*\n\s+\d+\s+)?(?:fer size:\s+)?(\d+)"),
> + )
> + #: burst size ran with app
> + burst_size: int = field(
> + metadata=TextParser.find_int(rf"Burst(?:.*\n\s+\d+\s+){2}?(?: size:\s+)?(\d+)"),
> + )
> + #: total operations ran
> + total_ops: int = field(metadata=TextParser.find_int(r"total operations:\s+(\d+)"))
> + #: number of bursts
> + num_of_bursts: int = field(metadata=TextParser.find_int(r"Number of bursts:\s+(\d+)"))
> + #: minimum enqueued packets
> + min_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+){2}(\d+)"))
> + #: maximum enqueued packets
> + max_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+){3}(\d+)"))
> + #: average enqueued packets
> + avg_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+)(\d+)"))
> + #: total enqueued packets
> + total_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(\d+)"))
> + #: minimum dequeued packets
> + min_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+){2}(\d+)"))
> + #: maximum dequeued packets
> + max_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+){3}(\d+)"))
> + #: average dequeued packets
> + avg_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+)(\d+)"))
> + #: total dequeued packets
> + total_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(\d+)"))
> + #: minimum cycles per buffer
> + min_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+){3}([\d.]+)"))
> + #: maximum cycles per buffer
> + max_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+){2}([\d.]+)"))
> + #: average cycles per buffer
> + avg_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+)([\d.]+)"))
> + #: total cycles per buffer
> + total_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+([\d.]+)"))
> + #: mimum time in microseconds
> + min_time_us: float = field(
> + metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+){3}([\d.]+)")
> + )
> + #: maximum time in microseconds
> + max_time_us: float = field(
> + metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+){2}([\d.]+)")
> + )
> + #: average time in microseconds
> + avg_time_us: float = field(
> + metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+)([\d.]+)")
> + )
> + #: total time in microseconds
> + total_time_us: float = field(metadata=TextParser.find_float(r"time \[us\]\s+([\d.]+)"))
> +
> +
> +@dataclass
> +class PmdCyclecountResults(CryptodevResults):
> + """A parser for PMD cycle count test output."""
> +
> + #:
> + lcore_id: int = field(metadata=TextParser.find_int(r"lcore\s+(?:id.*\n\s+)?(\d+)"))
> + #: buffer size used with app run
> + buffer_size: int = field(
> + metadata=TextParser.find_int(r"Buf(?:.*\n\s+(?:\d+\s+))?(?:fer size:\s+)?(\d+)"),
> + )
> + #: burst size used with app run
> + burst_size: int = field(
> + metadata=TextParser.find_int(r"Burst(?:.*\n\s+(?:\d+\s+){2})?(?: size:\s+)?(\d+)"),
> + )
> + #: packets enqueued
> + enqueued: int = field(metadata=TextParser.find_int(r"Enqueued.*\n\s+(?:\d+\s+){3}(\d+)"))
> + #: packets dequeued
> + dequeued: int = field(metadata=TextParser.find_int(r"Dequeued.*\n\s+(?:\d+\s+){4}(\d+)"))
> + #: number of enqueue packet retries
> + enqueue_retries: int = field(
> + metadata=TextParser.find_int(r"Enq Retries.*\n\s+(?:\d+\s+){5}(\d+)")
> + )
> + #: number of dequeue packet retries
> + dequeue_retries: int = field(
> + metadata=TextParser.find_int(r"Deq Retries.*\n\s+(?:\d+\s+){6}(\d+)")
> + )
> + #: number of cycles per operation
> + cycles_per_operation: float = field(
> + metadata=TextParser.find_float(r"Cycles/Op.*\n\s+(?:\d+\s+){7}([\d.]+)")
> + )
> + #: number of cycles per enqueue
> + cycles_per_enqueue: float = field(
> + metadata=TextParser.find_float(r"Cycles/Enq.*\n\s+(?:\d+\s+){7}(?:[\d.]+\s+)([\d.]+)")
> + )
> + #: number of cycles per dequeue
> + cycles_per_dequeue: float = field(
> + metadata=TextParser.find_float(r"Cycles/Deq.*\n\s+(?:\d+\s+){7}(?:[\d.]+\s+){2}([\d.]+)"),
> + )
> +
> +
> +@dataclass
> +class VerifyResults(CryptodevResults):
> + """A parser for verify test output."""
> +
> + #:
> + lcore_id: int = field(metadata=TextParser.find_int(r"lcore\s+(?:id.*\n\s+)?(\d+)"))
> + #: buffer size ran with app
> + buffer_size: int = field(
> + metadata=TextParser.find_int(r"Buf(?:.*\n\s+(?:\d+\s+))?(?:fer size:\s+)?(\d+)"),
> + )
> + #: burst size ran with app
> + burst_size: int = field(
> + metadata=TextParser.find_int(r"Burst(?:.*\n\s+(?:\d+\s+){2})?(?: size:\s+)?(\d+)"),
> + )
> + #: number of packets enqueued
> + enqueued: int = field(metadata=TextParser.find_int(r"Enqueued.*\n\s+(?:\d+\s+){3}(\d+)"))
> + #: number of packets dequeued
> + dequeued: int = field(metadata=TextParser.find_int(r"Dequeued.*\n\s+(?:\d+\s+){4}(\d+)"))
> + #: number of packets enqueue failed
> + failed_enqueued: int = field(
> + metadata=TextParser.find_int(r"Failed Enq.*\n\s+(?:\d+\s+){5}(\d+)")
> + )
> + #: number of packets dequeue failed
> + failed_dequeued: int = field(
> + metadata=TextParser.find_int(r"Failed Deq.*\n\s+(?:\d+\s+){6}(\d+)")
> + )
> + #: total number of failed operations
> + failed_ops: int = field(metadata=TextParser.find_int(r"Failed Ops.*\n\s+(?:\d+\s+){7}(\d+)"))
> diff --git a/dts/configurations/nodes.example.yaml b/dts/configurations/nodes.example.yaml
> index 3f23df11ec..1fc0a38bf5 100644
> --- a/dts/configurations/nodes.example.yaml
> +++ b/dts/configurations/nodes.example.yaml
> @@ -20,6 +20,12 @@
> hugepages_2mb: # optional; if removed, will use system hugepage configuration
> number_of: 256
> force_first_numa: false
> + # cryptodevs: # optional; add each physical bdf as its own element in the list
I would rephrase to clarify:
"Uncomment to run cryptodev tests; add physical cryptodev devices by their BDF"
I also think that we are at a place now where we have a number of
optional fields in the config files that can be commented or
uncommented, and there may be value in explicitly calling this out,
perhaps with a one liner above all of the YAML fields. I think you are
going in the right direction with your comment, but then we have other
fields which are optional that don't have the same explanation, or
it's phrased differently, resulting in documentation which is
inconsistent and potentially confusing to new people who just want to
fill out their config files. What do you think agree/disagree? If you
agree can you come up with something? Thanks.
> + # - name: cryptodev-0
> + # pci: "ffff:ff:ff.f"
> + # os_driver_for_dpdk: vfio-pci # OS driver that DPDK will use
will use for the cryptodev VFs.
> + # os_driver: _ # This is unused but a value is necessary to run dts
If there is really no point in populating the os_driver field, is it
possible to remove the field and force the config parser step in the
framework be tolerant to this?
> + # crypto_driver: # the device type of the DUT (crypto_qat, crypto_zuc, crypto_aesni_mb)
Put e.g. ahead of the list.
> # Define a Scapy traffic generator node, having two network ports
> # physically connected to the corresponding ports in SUT 1 (the peer node).
> - name: "TG 1"
> diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
> index 438a1bdc8f..36068c1ef8 100644
> --- a/dts/framework/config/node.py
> +++ b/dts/framework/config/node.py
> @@ -70,6 +70,10 @@ class NodeConfiguration(FrozenModel):
> hugepages: HugepageConfiguration | None = Field(None, alias="hugepages_2mb")
> #: The ports that can be used in testing.
> ports: list[PortConfig] = Field(min_length=1)
> + #: The pci info used by crypto devices
> + cryptodevs: list[PortConfig] = Field(default=[], min_length=0)
> + #: The crypto driver used by crypto devices
> + crypto_driver: str | None = None
>
> @model_validator(mode="after")
> def verify_unique_port_names(self) -> Self:
> diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py
> index 5bc4bd37d9..3c7650474c 100644
> --- a/dts/framework/params/types.py
> +++ b/dts/framework/params/types.py
> @@ -15,6 +15,23 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
> from pathlib import PurePath
> from typing import TypedDict
>
> +from api.cryptodev.config import (
> + AeadAlgName,
> + AsymOpMode,
> + AuthenticationAlgorithm,
> + AuthenticationOpMode,
> + BurstSizeRange,
> + CipherAlgorithm,
> + DeviceType,
> + EncryptDecryptSwitch,
> + ListWrapper,
> + OperationType,
> + PDCPDomain,
> + RSAPrivKeytype,
> + Silent,
> + TestType,
> + TLSVersion,
> +)
> from api.testpmd.config import (
> AnonMempoolAllocationMode,
> EthPeer,
> @@ -55,6 +72,54 @@ class EalParamsDict(TypedDict, total=False):
> other_eal_param: Params | None
>
>
> +class CryptoPmdParamsDict(EalParamsDict, total=False):
> + """:class:`TypedDict` equivalent of :class:`~.cryptodev.CryptoPmdParams`."""
> +
> + aead_aad_sz: int | None
> + aead_algo: AeadAlgName | None
> + aead_iv_sz: int | None
> + aead_key_sz: int | None
> + aead_op: EncryptDecryptSwitch | None
> + asym_op: AsymOpMode | None
> + auth_algo: AuthenticationAlgorithm | None
> + auth_iv_sz: int | None
> + auth_key_sz: int | None
> + auth_op: AuthenticationOpMode | None
> + buffer_sz: BurstSizeRange | ListWrapper | int | None
> + burst_sz: BurstSizeRange | ListWrapper | int | None
> + cipher_algo: CipherAlgorithm | None
> + cipher_iv_sz: int | None
> + cipher_key_sz: int | None
> + cipher_op: EncryptDecryptSwitch | None
> + csv_friendly: Switch
> + desc_nb: int | None
> + devtype: DeviceType | None
> + digest_sz: int | None
> + docsis_hdr_sz: int | None
> + enable_sdap: Switch
> + imix: int | None
> + low_prio_qp_mask: int | None
> + modex_len: int | None
> + optype: OperationType | None
> + out_of_place: Switch
> + pdcp_sn_sz: int | None
> + pdcp_domain: PDCPDomain | None
> + pdcp_ses_hfn_en: Switch | None
> + pmd_cyclecount_delay_pmd: int | None
> + pool_sz: int | None
> + ptest: TestType
> + rsa_modlen: int | None
> + rsa_priv_keytype: RSAPrivKeytype | None
> + segment_sz: int | None
> + sessionless: Switch
> + shared_session: Switch
> + silent: Silent | None
> + test_file: str | None
> + test_name: str | None
> + tls_version: TLSVersion | None
> + total_ops: int | None
> +
> +
> class TestPmdParamsDict(EalParamsDict, total=False):
> """:class:`TypedDict` equivalent of :class:`~.testpmd.TestPmdParams`."""
>
> diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
> index 51b97d4ff6..b94d336d4e 100644
> --- a/dts/framework/remote_session/dpdk_shell.py
> +++ b/dts/framework/remote_session/dpdk_shell.py
> @@ -46,7 +46,10 @@ def compute_eal_params(
> params.prefix = prefix
>
> if params.allowed_ports is None:
> - params.allowed_ports = ctx.topology.sut_dpdk_ports
> + if ctx.topology.crypto_vf_ports:
> + params.allowed_ports = [ctx.topology.crypto_vf_ports[0]]
> + else:
> + params.allowed_ports = ctx.topology.sut_dpdk_ports
>
> return params
>
> diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
> index ff0a12c9ce..36e6c5a44c 100644
> --- a/dts/framework/test_run.py
> +++ b/dts/framework/test_run.py
> @@ -349,6 +349,9 @@ def next(self) -> State | None:
>
> if test_run.config.use_virtual_functions:
> test_run.ctx.topology.instantiate_vf_ports()
> + if test_run.ctx.sut_node.cryptodevs:
> + test_run.ctx.topology.instantiate_crypto_vf_ports()
> + test_run.ctx.topology.configure_cryptodevs("dpdk")
>
> test_run.ctx.topology.configure_ports("sut", "dpdk")
> if test_run.ctx.func_tg:
> @@ -442,6 +445,8 @@ def next(self) -> State | None:
> """Next state."""
> if self.test_run.config.use_virtual_functions:
> self.test_run.ctx.topology.delete_vf_ports()
> + if self.test_run.ctx.sut_node.cryptodevs:
> + self.test_run.ctx.topology.delete_crypto_vf_ports()
>
> self.test_run.ctx.shell_pool.terminate_current_pool()
> if self.test_run.ctx.func_tg and self.test_run.ctx.func_tg.is_setup:
> diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
> index 711d4d97c3..27a5f48ecf 100644
> --- a/dts/framework/testbed_model/linux_session.py
> +++ b/dts/framework/testbed_model/linux_session.py
> @@ -10,6 +10,7 @@
> """
>
> import json
> +import re
> from collections.abc import Iterable
> from functools import cached_property
> from pathlib import PurePath
> @@ -223,6 +224,30 @@ def devbind_script_path(self) -> PurePath:
> """
> raise InternalError("Accessed devbind script path before setup.")
>
> + def create_crypto_vfs(self, pf_port: list[Port]) -> None:
> + """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
> +
> + Raises:
> + InternalError: If there are existing VFs which have to be deleted.
> + """
> + for port in pf_port:
> + self.delete_crypto_vfs(port)
> + for port in pf_port:
> + sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
> + curr_num_vfs = int(
> + self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
> + )
> + if 0 < curr_num_vfs:
> + raise InternalError("There are existing VFs on the port which must be deleted.")
> + num_vfs = int(
> + self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
> + )
> + self.send_command(
> + f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
> + )
> +
> + self.refresh_lshw()
> +
> def create_vfs(self, pf_port: Port) -> None:
> """Overrides :meth:`~.os_session.OSSession.create_vfs`.
>
> @@ -239,6 +264,23 @@ def create_vfs(self, pf_port: Port) -> None:
> self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
> self.refresh_lshw()
>
> + def delete_crypto_vfs(self, pf_port: Port) -> None:
> + """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
> + sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
> + curr_num_vfs = int(
> + self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
> + )
> + if curr_num_vfs == 0:
> + return self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
> + self.send_command(
> + f"dpdk-devbind.py -u {pf_port.pci}".replace(":", "\\:"), privileged=True, timeout=30
> + )
> + self.send_command(
> + f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
> + privileged=True,
> + )
> + self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
> +
> def delete_vfs(self, pf_port: Port) -> None:
> """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
> sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
> @@ -250,6 +292,18 @@ def delete_vfs(self, pf_port: Port) -> None:
> else:
> self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
>
> + def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
> + """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
> + sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
> + curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
> + if curr_num_vfs > 0:
> + pci_addrs = self.send_command(
> + f"readlink {sys_bus_path}/virtfn*",
> + privileged=True,
> + )
> + return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
> + return []
> +
> def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
> """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
> sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
> diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
> index 6c3e63a2a1..5c3e02a320 100644
> --- a/dts/framework/testbed_model/node.py
> +++ b/dts/framework/testbed_model/node.py
> @@ -54,6 +54,8 @@ class Node:
> arch: Architecture
> lcores: list[LogicalCore]
> ports: list[Port]
> + cryptodevs: list[Port]
> + crypto_driver: str | None
> _logger: DTSLogger
> _other_sessions: list[OSSession]
> _node_info: OSSessionInfo | None
> @@ -82,6 +84,14 @@ def __init__(self, node_config: NodeConfiguration) -> None:
> self._other_sessions = []
> self._setup = False
> self.ports = [Port(self, port_config) for port_config in self.config.ports]
> + self.cryptodevs = [
> + Port(self, cryptodev_config) for cryptodev_config in self.config.cryptodevs
> + ]
> + self.crypto_driver = self.config.crypto_driver
> + if self.cryptodevs:
> + self.main_session.load_vfio(self.cryptodevs[0])
> + elif self.ports and self.ports[0].config.os_driver_for_dpdk == "vfio-pci":
> + self.main_session.load_vfio(self.ports[0])
> self._logger.info(f"Created node: {self.name}")
>
> def setup(self) -> None:
> diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
> index b94c3e527b..2eeeea6967 100644
> --- a/dts/framework/testbed_model/os_session.py
> +++ b/dts/framework/testbed_model/os_session.py
> @@ -615,6 +615,14 @@ def configure_port_mtu(self, mtu: int, port: Port) -> None:
> port: Port to set `mtu` on.
> """
>
> + @abstractmethod
> + def create_crypto_vfs(self, pf_ports: list[Port]) -> None:
> + """Creatues virtual functions for each port in 'pf_ports'.
typo
> +
> + Checks how many virtual functions each crypto device in 'pf_ports' supports, and creates
> + that number of VFs on the port.
> + """
> +
> @abstractmethod
> def create_vfs(self, pf_port: Port) -> None:
> """Creates virtual functions for `pf_port`.
> @@ -630,6 +638,16 @@ def create_vfs(self, pf_port: Port) -> None:
> maximum for `pf_port`.
> """
>
> + @abstractmethod
> + def delete_crypto_vfs(self, pf_port: Port) -> None:
> + """Deletes virtual functions for crypto device 'pf_port'.
> +
> + Checks how many virtual functions are currently active and removes all if any exist.
> +
> + Args:
> + pf_port: The crypto device port to delete virtual functions on.
> + """
> +
> @abstractmethod
> def delete_vfs(self, pf_port: Port) -> None:
> """Deletes virtual functions for `pf_port`.
> @@ -656,3 +674,15 @@ def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
> A list containing all of the PCI addresses of the VFs on the port. If the port has no
> VFs then the list will be empty.
> """
> +
> + @abstractmethod
> + def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
> + """Find the PCI addresses of all virtual functions (VFs) of a crypto device on `pf_port`.
> +
> + Args:
> + pf_port: The port to find the VFs on.
> +
> + Returns:
> + A list containing all of the PCI addresses of the VFs on the port. If the port has no
> + VFs then the list will be empty.
> + """
> diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
> index 13b4b58a74..d9af67552a 100644
> --- a/dts/framework/testbed_model/topology.py
> +++ b/dts/framework/testbed_model/topology.py
> @@ -8,6 +8,7 @@
> The link information then implies what type of topology is available.
> """
>
> +import re
> from collections import defaultdict
> from collections.abc import Iterator
> from dataclasses import dataclass
> @@ -58,6 +59,8 @@ class Topology:
> tg_ports: list[Port]
> pf_ports: list[Port]
> vf_ports: list[Port]
> + crypto_pf_ports: list[Port]
> + crypto_vf_ports: list[Port]
>
> @classmethod
> def from_port_links(cls, port_links: Iterator[PortLink]) -> Self:
> @@ -85,7 +88,7 @@ def from_port_links(cls, port_links: Iterator[PortLink]) -> Self:
> msg = "More than two links in a topology are not supported."
> raise ConfigurationError(msg)
>
> - return cls(type, sut_ports, tg_ports, [], [])
> + return cls(type, sut_ports, tg_ports, [], [], [], [])
>
> def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node, list[Port]]:
> """Retrieve node and its ports for the current topology.
> @@ -105,6 +108,39 @@ def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node,
> msg = f"Invalid node `{node_identifier}` given."
> raise InternalError(msg)
>
> + def get_crypto_vfs(self, numvfs: int | None = None) -> list[Port]:
numvfs -> num_vfs
> + """Retrieve virtual functions from active crypto vfs.
> +
> + If numvfs is `None`, returns all crypto virtual functions. Otherwise, returns
> + `numvfs` number of virtual functions per physical function rounded down. If a number of
> + `numvfs` is less than the number of physical functions, one virtual function is returned.
I am wondering about this design. So, the form of the return value is
unpredictable? What do the testsuites actually require?
> +
> + Args:
> + numvfs: Number of virtual functions to return if provided.
> +
> + Returns:
> + List containing the requested number of vfs, evenly distributed among
vfs -> VFs
> + physcal functions rounded down.
physical typo
> + """
> + return_list = []
> + device = 1
> + if len(self.crypto_pf_ports) == 0:
> + return []
> + if numvfs is None:
> + return_list.extend(self.crypto_vf_ports)
> + return return_list
> + mod = numvfs // len(self.crypto_pf_ports)
mod as in modulo? Perhaps you mean integer division instead of mod?
> + if numvfs < len(self.crypto_pf_ports):
> + return_list.extend([self.crypto_vf_ports[0]])
> + return return_list
> + for i in range(mod):
> + if i % 8 == 0 and i != 0:
> + device += 1
> + return_list.extend(
> + filter(lambda x: re.search(rf"0000:\d+:0{device}.{i}", x.pci), self.crypto_vf_ports)
> + )
> + return return_list
> +
> def setup(self) -> None:
> """Setup topology ports.
>
> @@ -145,6 +181,34 @@ def _setup_ports(self, node_identifier: NodeIdentifier) -> None:
> f"for port {port.name} in node {node.name}."
> )
>
> + def instantiate_crypto_vf_ports(self) -> None:
> + """Create max number of virtual functions allowed on the SUT node.
> +
> + Raises:
> + InternalError: If crypto virtual functions could not be created on a port.
> + """
> + from framework.context import get_ctx
> +
> + ctx = get_ctx()
> +
> + for port in ctx.sut_node.cryptodevs:
> + self.crypto_pf_ports.append(port)
> + self.delete_crypto_vf_ports()
populating crypto_pf_ports in the instantiate_crypto_vf_ports method?
Possible single responsibility principle violation but not a huge deal
if it cannot be cleanly populated "earlier."
> +
> + ctx.sut_node.main_session.create_crypto_vfs(self.crypto_pf_ports)
> + for port in self.crypto_pf_ports:
> + addr_list = ctx.sut_node.main_session.get_pci_addr_of_crypto_vfs(port)
> + if addr_list == []:
> + raise InternalError(f"Failed to create crypto virtual function on port {port.pci}")
> + for addr in addr_list:
> + vf_config = PortConfig(
> + name=f"{port.name}-crypto-vf-{addr}",
> + pci=addr,
> + os_driver_for_dpdk=port.config.os_driver_for_dpdk,
> + os_driver=port.config.os_driver,
> + )
> + self.crypto_vf_ports.append(Port(node=port.node, config=vf_config))
> +
> def instantiate_vf_ports(self) -> None:
> """Create, setup, and add virtual functions to the list of ports on the SUT node.
>
> @@ -189,6 +253,27 @@ def delete_vf_ports(self) -> None:
> self.sut_ports.clear()
> self.sut_ports.extend(self.pf_ports)
>
> + def delete_crypto_vf_ports(self) -> None:
> + """Delete crypto virtual functions from the SUT node during test run teardown."""
> + from framework.context import get_ctx
> +
> + ctx = get_ctx()
> +
> + for port in self.crypto_pf_ports:
> + ctx.sut_node.main_session.delete_crypto_vfs(port)
> + self.sut_ports.clear()
> + self.sut_ports.extend(self.pf_ports)
What list needs to be cleared here? sut_ports or crypto_vf_ports?
Unless I am misundersatnding, your implementation leaves sut_ports to
the ethdev devices and runs everything for cryptodev ports through
crypto_pf_ports and crypto_vf_ports.
> +
> + def configure_cryptodevs(self, driver: DriverKind):
> + """Configure the crypto device virtual functoins on the sut and bind to specified driver.
functions typo.
Also, is this configuring, or just binding?
> +
> + Args:
> + driver: The driver to bind the cryptofunctions
typo. Try to check on the spelling before your next version. :)
> + """
> + from framework.context import get_ctx
> +
> + self._bind_ports_to_drivers(get_ctx().sut_node, self.crypto_vf_ports, driver)
> +
> def configure_ports(
> self, node_identifier: NodeIdentifier, drivers: DriverKind | tuple[DriverKind, ...]
> ) -> None:
> @@ -227,7 +312,7 @@ def _bind_ports_to_drivers(
> for port_id, port in enumerate(ports):
> driver_kind = drivers[port_id] if isinstance(drivers, tuple) else drivers
> desired_driver = port.driver_by_kind(driver_kind)
> - if port.current_driver != desired_driver:
> + if port in self.crypto_vf_ports or port.current_driver != desired_driver:
> driver_to_ports[desired_driver].append(port)
>
> for driver_name, ports in driver_to_ports.items():
> --
> 2.50.1
>
Reviewed-by: Patrick Robb <probb@iol.unh.edu>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v3 2/5] dts: add cryptodev package to DTS
2026-02-25 21:06 ` Patrick Robb
@ 2026-02-26 19:13 ` Andrew Bailey
0 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-02-26 19:13 UTC (permalink / raw)
To: Patrick Robb; +Cc: luca.vizzarro, dmarx, dev
[-- Attachment #1: Type: text/plain, Size: 5426 bytes --]
>
>
>
> > + def run_app(self, numvfs: int | None = 0) -> list[CryptodevResults]:
> > + """Run the cryptodev application with the given app parameters.
> > +
> > + Raises:
> > + SkippedTestException: If the device type is not supported
> on the main session.
> > + RemoteCommandExecutionError: If there is an error running
> the command.
> > +
> > + Returns:
> > + list[CryptodevResults]: The list of parsed results for the
> cryptodev application.
>
> Missing the args docstring. Speaking of which, What is the theory
> behind passing a vf count from the testsuites to the app? In terms of
> what the testsuites require, and whether that aspect should be
> configurable from the user side, why the default is zero, etc.
>
The theory behind this is that some test suites in the future that use
scheduling will require to use multiple VFs.
I think that if this varies on a per test case basis, then it should be
configurable within a test case. In regard to the
default, it should be assigned to one, not zero. As the application
requires at least one VF.
>
> > diff --git a/dts/configurations/nodes.example.yaml
> b/dts/configurations/nodes.example.yaml
> > index 3f23df11ec..1fc0a38bf5 100644
> > --- a/dts/configurations/nodes.example.yaml
> > +++ b/dts/configurations/nodes.example.yaml
> > @@ -20,6 +20,12 @@
> > hugepages_2mb: # optional; if removed, will use system hugepage
> configuration
> > number_of: 256
> > force_first_numa: false
> > + # cryptodevs: # optional; add each physical bdf as its own element in
> the list
>
> I would rephrase to clarify:
>
> "Uncomment to run cryptodev tests; add physical cryptodev devices by their
> BDF"
>
> I also think that we are at a place now where we have a number of
> optional fields in the config files that can be commented or
> uncommented, and there may be value in explicitly calling this out,
> perhaps with a one liner above all of the YAML fields. I think you are
> going in the right direction with your comment, but then we have other
> fields which are optional that don't have the same explanation, or
> it's phrased differently, resulting in documentation which is
> inconsistent and potentially confusing to new people who just want to
> fill out their config files. What do you think agree/disagree? If you
> agree can you come up with something? Thanks.
>
I think this is a reasonable request, maybe we can have a disclaimer at the
top of the file
that states which variables are optional with a short description of each
one and some optional
tag each time it is used within the file. For example,
# Optional Arguments:
# cryptodevs: uncomment to run cryptodev tests; add physical cryptodev
devices by their BDF
# crypto_driver: the device type of the DUT e.g. (crypto_qat, crypto_zuc,
crypto_aesni_mb)
# hugepages_2mb: optional; add each physical bdf as its own element in
the list,
# if removed, will use system hugepage configuration
cryptodevs: # see 'Optional Arguments'
- name: cryptodev-0
pci: "ffff:ff:ff.f"
os_driver_for_dpdk: vfio-pci # OS driver that DPDK will use for the
cryptodev VFs
os_driver: _ # This is unused but a value is necessary to run dts
crypto_driver: # see 'Optional Arguments'
Let me know what you think. :)
>
> > + # os_driver: _ # This is unused but a value is necessary to run
> dts
>
> If there is really no point in populating the os_driver field, is it
> possible to remove the field and force the config parser step in the
> framework be tolerant to this?
>
This is kind of a shortcut, A crypto device is identical to the already
implemented port class; except that it
does not require an os driver. This field cannot be made optional with this
approach but its value has no
effect on test runs.
>
>
> > + """Retrieve virtual functions from active crypto vfs.
> > +
> > + If numvfs is `None`, returns all crypto virtual functions.
> Otherwise, returns
> > + `numvfs` number of virtual functions per physical function
> rounded down. If a number of
> > + `numvfs` is less than the number of physical functions, one
> virtual function is returned.
>
> I am wondering about this design. So, the form of the return value is
> unpredictable? What do the testsuites actually require?
>
>
This will be redesigned in my v4, It should return the exact number of vfs
requested or raise an exception
if an invalid number of vfs are called.
>
> > + def instantiate_crypto_vf_ports(self) -> None:
> > + """Create max number of virtual functions allowed on the SUT
> node.
> > +
> > + Raises:
> > + InternalError: If crypto virtual functions could not be
> created on a port.
> > + """
> > + from framework.context import get_ctx
> > +
> > + ctx = get_ctx()
> > +
> > + for port in ctx.sut_node.cryptodevs:
> > + self.crypto_pf_ports.append(port)
> > + self.delete_crypto_vf_ports()
>
> populating crypto_pf_ports in the instantiate_crypto_vf_ports method?
> Possible single responsibility principle violation but not a huge deal
> if it cannot be cleanly populated "earlier."
>
> This follows the behavior of the 'instantiate_vf_ports' method. The idea
here is that we will never want to instantiate
the crypto PF ports without the VFs. For this reason I thought it was
appropriate to do it in one method.
[-- Attachment #2: Type: text/html, Size: 6883 bytes --]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v3 2/5] dts: add cryptodev package to DTS
2026-02-13 19:35 ` [PATCH v3 2/5] dts: add cryptodev package to DTS Andrew Bailey
2026-02-25 21:06 ` Patrick Robb
@ 2026-02-27 16:15 ` Patrick Robb
1 sibling, 0 replies; 41+ messages in thread
From: Patrick Robb @ 2026-02-27 16:15 UTC (permalink / raw)
To: Andrew Bailey; +Cc: luca.vizzarro, dmarx, dev
> + # cryptodevs: # optional; add each physical bdf as its own element in the list
> + # - name: cryptodev-0
> + # pci: "ffff:ff:ff.f"
> + # os_driver_for_dpdk: vfio-pci # OS driver that DPDK will use
> + # os_driver: _ # This is unused but a value is necessary to run dts
> + # crypto_driver: # the device type of the DUT (crypto_qat, crypto_zuc, crypto_aesni_mb)
This naming is confusing. Can you switch to crypto_device_type? And
push that naming update downwards i.e. into node.py etc. Also, how can
users know where to find the full list of valid crypto_device_type
fields?
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v3 3/5] dts: add cryptodev throughput test suite
2026-02-13 19:35 ` [PATCH v3 3/5] dts: add cryptodev throughput test suite Andrew Bailey
@ 2026-02-27 18:24 ` Patrick Robb
2026-02-27 19:43 ` Andrew Bailey
0 siblings, 1 reply; 41+ messages in thread
From: Patrick Robb @ 2026-02-27 18:24 UTC (permalink / raw)
To: Andrew Bailey; +Cc: luca.vizzarro, dmarx, dev
On Fri, Feb 13, 2026 at 2:36 PM Andrew Bailey <abailey@iol.unh.edu> wrote:
>
> Add cryptodev performance test suite. This test covers throughput
> performance metrics of supplied crypto devices using the
> dpdk-test-crypto-perf application. The results of this application will
> be compared against user supplied performance metrics and fail if signs
> of regression are apparent.
>
> Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
> ---
> .../tests.TestSuite_cryptodev_throughput.rst | 8 +
> dts/configurations/tests_config.example.yaml | 8 +
> dts/tests/TestSuite_cryptodev_throughput.py | 690 ++++++++++++++++++
> 3 files changed, 706 insertions(+)
> create mode 100644 doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
> create mode 100644 dts/tests/TestSuite_cryptodev_throughput.py
>
> diff --git a/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst b/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
> new file mode 100644
> index 0000000000..5df5e7547e
> --- /dev/null
> +++ b/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
> @@ -0,0 +1,8 @@
> +.. SPDX-License-Identifier: BSD-3-Clause
> +
> +cryptodev_throughput Test Suite
> +===============================
> +
> +.. automodule:: tests.TestSuite_cryptodev_throughput
> + :members:
> + :show-inheritance:
> diff --git a/dts/configurations/tests_config.example.yaml b/dts/configurations/tests_config.example.yaml
> index 64fa630aa0..c9beff9c99 100644
> --- a/dts/configurations/tests_config.example.yaml
> +++ b/dts/configurations/tests_config.example.yaml
> @@ -15,3 +15,11 @@ hello_world:
> # num_descriptors: 1024
> # expected_mpps: 1.0
> # delta_tolerance: 0.05
> +# cryptodev_throughput:
> +# delta_tolerance: 0.05
> +# throughput_test_parameters:
> +# snow3g_uea2_snow3g_uia2: # Add the name of the test suite you would like to run
> +# - buff_size: 64
> +# gbps: 2.0 # Set gigabits per second according to your devices throughput for this given buffer size
> +# - buff_size: 512
> +# gbps: 20.0
> \ No newline at end of file
> diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
> new file mode 100644
> index 0000000000..871e47ae63
> --- /dev/null
> +++ b/dts/tests/TestSuite_cryptodev_throughput.py
> @@ -0,0 +1,690 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2025 University of New Hampshire
> +
> +"""DPDK cryptodev performance test suite.
> +
> +The main goal of this testsuite is to utilize the dpdk-test-cryptodev application to gather
> +performance metrics for various cryptographic operations supported by DPDK cryptodev-pmd.
> +It will then compare the results against predefined baseline given in the test_config file to
> +ensure performance standards are met.
> +"""
> +
> +from api.capabilities import (
> + LinkTopology,
> + requires_link_topology,
> +)
> +from api.cryptodev import Cryptodev
> +from api.cryptodev.config import (
> + AeadAlgName,
> + AuthenticationAlgorithm,
> + AuthenticationOpMode,
> + CipherAlgorithm,
> + DeviceType,
> + EncryptDecryptSwitch,
> + ListWrapper,
> + OperationType,
> + TestType,
> + get_device_from_str,
> +)
> +from api.cryptodev.types import (
> + CryptodevResults,
> +)
> +from api.test import verify
> +from framework.context import get_ctx
> +from framework.exception import SkippedTestException
> +from framework.test_suite import BaseConfig, TestSuite, func_test
> +from framework.testbed_model.virtual_device import VirtualDevice
> +
> +config_list: list[dict[str, int | float | str]] = [
> + {"buff_size": 64, "gbps": 1.00},
> + {"buff_size": 512, "gbps": 1.00},
> + {"buff_size": 2048, "gbps": 1.00},
> +]
> +
> +
> +class Config(BaseConfig):
> + """Performance test metrics.
> +
> + Attributes:
> + delta_tolerance: The allowed tolerance below a given baseline.
> + throughput_test_parameters: The test parameters to use in the test suite.
> + """
> +
> + delta_tolerance: float = 0.05
> +
> + throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = {
> + "aes_cbc": config_list,
> + "aes_cbc_sha1": config_list,
> + "aes_cbc_sha2": config_list,
> + "aes_cbc_sha2_digest_16": config_list,
> + "aead_aes_gcm": config_list,
> + "aes_docsisbpi": config_list,
> + "sha1_hmac": config_list,
> + "snow3g_uea2_snow3g_uia2": config_list,
> + "zuc_eea3_zuc_eia3": config_list,
> + "kasumi_f8_kasumi_f9": config_list,
> + "open_ssl_vdev": config_list,
> + "aesni_mb_vdev": config_list,
> + "aesni_gcm_vdev": config_list,
> + "kasumi_vdev": config_list,
> + "zuc_vdev": config_list,
> + "snow3g_vdev": config_list,
> + }
> +
> +
> +@requires_link_topology(LinkTopology.NO_LINK)
> +class TestCryptodevThroughput(TestSuite):
> + """DPDK Crypto Device Testing Suite."""
> +
> + config: Config
> +
> + def set_up_suite(self) -> None:
> + """Set up the test suite."""
> + self.throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = (
> + self.config.throughput_test_parameters
> + )
> + self.delta_tolerance: float = self.config.delta_tolerance
> + device: DeviceType | None = get_device_from_str(str(get_ctx().sut_node.crypto_driver))
> + self.driver: DeviceType = device if device else DeviceType.crypto_qat
Why the "else DeviceType.crypto_qat"? Shouldn't the device type be a
required field from the config if we are running a crypto test?
> + self.buffer_sizes = {}
> +
> + for k, v in self.throughput_test_parameters.items():
> + self.buffer_sizes[k] = ListWrapper([int(run["buff_size"]) for run in v])
> +
> + def _print_stats(self, test_vals: list[dict[str, int | float | str]]) -> None:
> + element_len = len("Delta Tolerance")
> + border_len = (element_len + 1) * (len(test_vals[0]))
> +
> + 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}")
> +
> + for test_val in test_vals:
> + for k, v in test_val.items():
> + print(f"|{v:<{element_len}}", end="")
> + print(f"|\n{'='*border_len}")
> +
> + def _verify_throughput(
> + self,
> + results: list[CryptodevResults],
> + key: str,
> + ) -> list[dict[str, int | float | str]]:
> + result_list: list[dict[str, int | float | str]] = []
> +
> + for result in results:
> + result_dict = {}
> + parameters: dict[str, int | float | str] = list(
> + filter(
> + lambda x: x["buff_size"] == result.buffer_size,
> + self.throughput_test_parameters[key],
> + )
> + )[0]
> + test_result = True
> + for arg, target_val in parameters.items():
Can you just you a typical "for k, v in parameters.items()"? the name
target_val doesn't really represent both values and it's just
confusing to read in my opinion.
> + match arg:
> + case "buff_size":
> + result_dict["Buffer Size"] = target_val
> + continue
> + case "gbps":
> + # result did not meet the given gbps parameter, check if within delta.
> + if target_val > getattr(result, "gbps"):
> + delta = round((1 - (getattr(result, "gbps") / target_val)), 5)
> + if delta > self.delta_tolerance:
> + test_result = False
> + else:
> + delta = round((1 - target_val / getattr(result, arg)), 5)
Don't calculate delta once (and in two different ways for that
matter). Start by calculating the delta, and set it to a variable.
Then, you can have your followup business logic do the necessary steps
based on what the delta is. But the first step is to calculate the
delta one time and set it so it can be read for the subsequent steps.
> + result_dict["gbps delta"] = delta
> + result_dict["delta tolerance"] = self.delta_tolerance
> + result_dict["gbps"] = getattr(result, "gbps")
> + result_dict["gbps target"] = target_val
> + result_dict["passed"] = "PASS" if test_result else "FAIL"
> + result_list.append(result_dict)
> + return result_list
Overall I think the structure of the logic in this method is a little
confusing but I think it will be okay if you make the change
recommended in my above comment.
> +
> + @func_test
> + def aes_cbc(self) -> None:
> + """aes_cbc test.
> +
> + Steps:
> + * Create a cryptodev instance with provided driver and buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "aes_cbc" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + devtype=self.driver,
> + optype=OperationType.cipher_only,
> + cipher_algo=CipherAlgorithm.aes_cbc,
> + cipher_op=EncryptDecryptSwitch.encrypt,
> + cipher_key_sz=16,
> + cipher_iv_sz=16,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["aes_cbc"],
> + )
> + results = self._verify_throughput(app.run_app(), "aes_cbc")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + def aes_cbc_sha1(self) -> None:
> + """aes_cbc_sha1 test.
> +
> + Steps:
> + * Create a cryptodev instance with provided driver and buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "aes_cbc_sha1" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + devtype=self.driver,
> + optype=OperationType.cipher_then_auth,
> + cipher_algo=CipherAlgorithm.aes_cbc,
> + cipher_op=EncryptDecryptSwitch.encrypt,
> + cipher_key_sz=16,
> + cipher_iv_sz=16,
> + auth_algo=AuthenticationAlgorithm.sha1_hmac,
> + auth_op=AuthenticationOpMode.generate,
> + auth_key_sz=64,
> + auth_iv_sz=20,
> + digest_sz=12,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["aes_cbc_sha1"],
> + )
> + results = self._verify_throughput(app.run_app(), "aes_cbc_sha1")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + def aes_cbc_sha2(self) -> None:
> + """aes_cbc_sha2 test.
> +
> + Steps:
> + * Create a cryptodev instance with provided driver and buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "aes_cbc_sha2" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + devtype=self.driver,
> + optype=OperationType.cipher_then_auth,
> + cipher_algo=CipherAlgorithm.aes_cbc,
> + cipher_op=EncryptDecryptSwitch.encrypt,
> + cipher_key_sz=16,
> + cipher_iv_sz=16,
> + auth_algo=AuthenticationAlgorithm.sha2_256_hmac,
> + auth_op=AuthenticationOpMode.generate,
> + auth_key_sz=64,
> + digest_sz=32,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["aes_cbc_sha2"],
> + )
> + results = self._verify_throughput(app.run_app(), "aes_cbc_sha2")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + @func_test
> + def aes_cbc_sha2_digest_16(self) -> None:
> + """aes_cbc_sha2_digest_16 test.
> +
> + Steps:
> + * Create a cryptodev instance with provided driver and buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "aes_cbc_sha2_digest_16" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + devtype=self.driver,
> + optype=OperationType.cipher_then_auth,
> + cipher_algo=CipherAlgorithm.aes_cbc,
> + cipher_op=EncryptDecryptSwitch.encrypt,
> + cipher_key_sz=16,
> + cipher_iv_sz=16,
> + auth_algo=AuthenticationAlgorithm.sha2_256_hmac,
> + auth_op=AuthenticationOpMode.generate,
> + auth_key_sz=64,
> + digest_sz=16,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["aes_cbc_sha2_digest_16"],
> + )
> + results = self._verify_throughput(app.run_app(), "aes_cbc_sha2_digest_16")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + @func_test
> + def aead_aes_gcm(self) -> None:
> + """aead_aes_gcm test.
> +
> + Steps:
> + * Create a cryptodev instance with provided driver and buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "aead_aes_gcm" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + devtype=self.driver,
> + optype=OperationType.aead,
> + aead_algo=AeadAlgName.aes_gcm,
> + aead_op=EncryptDecryptSwitch.encrypt,
> + aead_key_sz=16,
> + aead_iv_sz=12,
> + aead_aad_sz=16,
> + digest_sz=16,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["aead_aes_gcm"],
> + )
> + results = self._verify_throughput(app.run_app(), "aead_aes_gcm")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + @func_test
> + def aes_docsisbpi(self) -> None:
> + """aes_docsiscpi test.
docsisbpi
> +
> + Steps:
> + * Create a cryptodev instance with provided driver and buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "aes_docsisbpi" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + devtype=self.driver,
> + optype=OperationType.cipher_only,
> + cipher_algo=CipherAlgorithm.aes_docsisbpi,
> + cipher_op=EncryptDecryptSwitch.encrypt,
> + cipher_key_sz=32,
> + cipher_iv_sz=16,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["aes_docsisbpi"],
> + )
> + results = self._verify_throughput(app.run_app(), "aes_docsisbpi")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + @crypto_test
> + def sha1_hmac(self) -> None:
> + """sha1_hmac test.
> +
> + Steps:
> + * Create a cryptodev instance with provided driver and buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "sha1_hmac" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + devtype=self.driver,
> + optype=OperationType.auth_only,
> + auth_algo=AuthenticationAlgorithm.sha1_hmac,
> + auth_op=AuthenticationOpMode.generate,
> + auth_key_sz=64,
> + auth_iv_sz=16,
> + digest_sz=12,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["sha1_hmac"],
> + )
> + results = self._verify_throughput(app.run_app(), "sha1_hmac")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + @func_test
> + def snow3g_uea2_snow3g_uia2(self) -> None:
> + """snow3g_uea2_snow3g_uia2 test.
> +
> + Steps:
> + * Create a cryptodev instance with provided driver and buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "snow3g_uea2_snow3g_uia2" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + devtype=self.driver,
> + optype=OperationType.cipher_then_auth,
> + cipher_algo=CipherAlgorithm.snow3g_uea2,
> + cipher_op=EncryptDecryptSwitch.encrypt,
> + cipher_key_sz=16,
> + cipher_iv_sz=16,
> + auth_algo=AuthenticationAlgorithm.snow3g_uia2,
> + auth_op=AuthenticationOpMode.generate,
> + auth_key_sz=16,
> + auth_iv_sz=16,
> + digest_sz=4,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["snow3g_uea2_snow3g_uia2"],
> + )
> + results = self._verify_throughput(app.run_app(), "snow3g_uea2_snow3g_uia2")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + @func_test
> + def zuc_eea3_zuc_eia3(self) -> None:
> + """zuc_eea3_zuc_eia3 test.
> +
> + Steps:
> + * Create a cryptodev instance with provided driver and buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "zuc_eea3_zuc_eia3" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + devtype=self.driver,
> + optype=OperationType.cipher_then_auth,
> + cipher_algo=CipherAlgorithm.zuc_eea3,
> + cipher_op=EncryptDecryptSwitch.encrypt,
> + cipher_key_sz=16,
> + cipher_iv_sz=16,
> + auth_algo=AuthenticationAlgorithm.zuc_eia3,
> + auth_op=AuthenticationOpMode.generate,
> + auth_key_sz=16,
> + auth_iv_sz=16,
> + digest_sz=4,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["zuc_eea3_zuc_eia3"],
> + )
> + results = self._verify_throughput(app.run_app(), "zuc_eea3_zuc_eia3")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + @func_test
> + def kasumi_f8_kasumi_f9(self) -> None:
> + """kasumi_f8 kasumi_f9 test.
> +
> + Steps:
> + * Create a cryptodev instance with provided driver and buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "kasumi_f8_kasumi_f9" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + devtype=self.driver,
> + optype=OperationType.cipher_then_auth,
> + cipher_algo=CipherAlgorithm.kasumi_f8,
> + cipher_op=EncryptDecryptSwitch.encrypt,
> + cipher_key_sz=16,
> + cipher_iv_sz=8,
> + auth_algo=AuthenticationAlgorithm.kasumi_f9,
> + auth_op=AuthenticationOpMode.generate,
> + auth_key_sz=16,
> + digest_sz=4,
> + burst_sz=32,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["kasumi_f8_kasumi_f9"],
> + )
> + results = self._verify_throughput(app.run_app(), "kasumi_f8_kasumi_f9")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + # BEGIN VDEV TESTS
> +
> +
> + def aesni_mb_vdev(self) -> None:
> + """aesni_mb virtual device test.
> +
> + Steps:
> + * Create a cryptodev instance with crypto_aesni_mb and supplied buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "aesni_mb_vdev" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + vdevs=[VirtualDevice("crypto_aesni_mb0")],
> + devtype=DeviceType.crypto_aesni_mb,
> + optype=OperationType.cipher_then_auth,
> + cipher_algo=CipherAlgorithm.aes_cbc,
> + cipher_op=EncryptDecryptSwitch.encrypt,
> + cipher_key_sz=32,
> + cipher_iv_sz=16,
> + auth_algo=AuthenticationAlgorithm.sha1_hmac,
> + auth_op=AuthenticationOpMode.generate,
> + auth_key_sz=16,
> + auth_iv_sz=16,
> + digest_sz=4,
> + burst_sz=32,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["aesni_mb_vdev"],
> + )
> + results = self._verify_throughput(app.run_app(), "aesni_mb_vdev")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + @func_test
> + def aesni_gcm_vdev(self):
> + """aesni_gcm virtual device test.
> +
> + Steps:
> + * Create a cryptodev instance with crypto_aesni_gcm and supplied buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "aesni_gcm_vdev" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + vdevs=[VirtualDevice("crypto_aesni_gcm0")],
> + devtype=DeviceType.crypto_aesni_gcm,
> + optype=OperationType.aead,
> + aead_algo=AeadAlgName.aes_gcm,
> + aead_op=EncryptDecryptSwitch.encrypt,
> + aead_key_sz=16,
> + aead_iv_sz=12,
> + aead_aad_sz=16,
> + digest_sz=16,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["aesni_gcm_vdev"],
> + )
> + results = self._verify_throughput(app.run_app(), "aesni_gcm_vdev")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + @func_test
> + def kasumi_vdev(self) -> None:
> + """Kasmumi virtual device test.
> +
> + Steps:
> + * Create a cryptodev instance with crypto_kasumi and supplied buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "kasumi_vdev" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + vdevs=[VirtualDevice("crypto_kasumi0")],
> + ptest=TestType.throughput,
> + devtype=DeviceType.crypto_qat,
Should this be crypto_kasumi?
> + optype=OperationType.cipher_then_auth,
> + cipher_algo=CipherAlgorithm.kasumi_f8,
> + cipher_op=EncryptDecryptSwitch.encrypt,
> + cipher_key_sz=16,
> + cipher_iv_sz=8,
> + auth_algo=AuthenticationAlgorithm.kasumi_f9,
> + auth_op=AuthenticationOpMode.generate,
> + auth_key_sz=16,
> + digest_sz=4,
> + burst_sz=32,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["kasumi_vdev"],
> + )
> + results = self._verify_throughput(app.run_app(), "kasumi_vdev")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
> +
> + @func_test
> + def snow3g_vdev(self) -> None:
> + """snow3g virtual device test.
> +
> + Steps:
> + * Create a cryptodev instance with crypto_snow3g and supplied buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "snow3g_vdev" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + vdevs=[VirtualDevice("crypto_snow3g0")],
> + devtype=DeviceType.crypto_snow3g,
> + optype=OperationType.cipher_then_auth,
> + cipher_algo=CipherAlgorithm.snow3g_uea2,
> + cipher_op=EncryptDecryptSwitch.encrypt,
> + cipher_key_sz=16,
> + cipher_iv_sz=16,
> + auth_algo=AuthenticationAlgorithm.snow3g_uia2,
> + auth_op=AuthenticationOpMode.generate,
> + auth_key_sz=16,
> + auth_iv_sz=16,
> + digest_sz=4,
> + burst_sz=32,
> + total_ops=10_000_000,
> + buffer_sz=self.buffer_sizes["snow3g_vdev"],
> + )
> + results = self._verify_throughput(app.run_app(), "snow3g_vdev")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gpbs fell below delta tolerance")
gpbs -> gbps
Also switch to Gbps instead of gbps throughout the suite for error
strings and docstrings (code is okay to be lowercase).
> +
> + @func_test
> + def zuc_vdev(self) -> None:
> + """Zuc virtual device test.
> +
> + Steps:
> + * Create a cryptodev instance with crypto_zuc and supplied buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "zuc_vdev" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + vdevs=[VirtualDevice("crypto_zuc0")],
> + devtype=DeviceType.crypto_zuc,
> + optype=OperationType.cipher_then_auth,
> + cipher_algo=CipherAlgorithm.zuc_eea3,
> + cipher_op=EncryptDecryptSwitch.encrypt,
> + cipher_key_sz=16,
> + cipher_iv_sz=16,
> + auth_algo=AuthenticationAlgorithm.zuc_eia3,
> + auth_op=AuthenticationOpMode.generate,
> + auth_key_sz=16,
> + auth_iv_sz=16,
> + digest_sz=4,
> + burst_sz=32,
> + total_ops=10_000_000,
Maybe set 10_000_00 to a variable which can be referenced in each testsuite?
> + buffer_sz=self.buffer_sizes["zuc_vdev"],
> + )
> + results = self._verify_throughput(app.run_app(), "zuc_vdev")
> + self._print_stats(results)
> + for result in results:
> + verify(result["passed"] == "PASS", "gpbs fell below delta tolerance")
Gbps
> +
> + @func_test
> + def open_ssl_vdev(self) -> None:
> + """open_ssl virtual device test.
> +
> + Steps:
> + * Create a cryptodev instance with provided driver and buffer sizes.
> + Verify:
> + * The resulting gbps is greater than expected_gbps*(1-delta_tolerance).
> +
> + Raises:
> + SkippedTestException: When configuration is not provided.
> + """
> + if "open_ssl_vdev" not in self.throughput_test_parameters:
> + raise SkippedTestException("test not configured")
> + app = Cryptodev(
> + ptest=TestType.throughput,
> + vdevs=[VirtualDevice("crypto_openssl0")],
> + devtype=DeviceType.crypto_openssl,
> + optype=OperationType.aead,
> + aead_algo=AeadAlgName.aes_gcm,
> + aead_op=EncryptDecryptSwitch.encrypt,
> + aead_key_sz=16,
> + aead_iv_sz=16,
> + aead_aad_sz=16,
> + digest_sz=16,
> + total_ops=100_000,
I'm guessing there is a reason but why fewer ops for this test? Just curious.
> + buffer_sz=self.buffer_sizes["open_ssl_vdev"],
> + )
> + results = self._verify_throughput(app.run_app(), "open_ssl_vdev")
> + self._print_stats(results)
> + for result in results:
> + verify(
> + result["passed"] == "PASS",
> + "gbps fell below delta tolerance",
> + )
> --
> 2.50.1
>
I noticed that for the VF creation, the crypto interface is being read
3x. Are there 1-2 additional checks happening, or are we just hitting
these code paths multiple times for good reason? Maybe not, I'm just
checking if there are any inadvertent calls. I guess this is a matter
for the 2/5 patch though:
2026/02/26 04:44:46 - test_run_setup - dts.SUT 1 - INFO - Sending:
'sudo -- sh -c 'cat /sys/bus/pci/devices/0000\:76\:00.0/sriov_numvfs''
2026/02/26 04:44:46 - test_run_setup - dts.SUT 1 - INFO - Sending:
'sudo -- sh -c 'cat /sys/bus/pci/devices/0000\:76\:00.0/sriov_numvfs''
2026/02/26 04:44:47 - test_run_setup - dts.SUT 1 - INFO - Sending:
'sudo -- sh -c 'cat /sys/bus/pci/devices/0000\:76\:00.0/sriov_numvfs''
Reviewed-by: Patrick Robb <probb@iol.unh.edu>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v3 4/5] dts: add crypto test decorator
2026-02-13 19:35 ` [PATCH v3 4/5] dts: add crypto test decorator Andrew Bailey
@ 2026-02-27 18:28 ` Patrick Robb
2026-02-27 19:18 ` Andrew Bailey
0 siblings, 1 reply; 41+ messages in thread
From: Patrick Robb @ 2026-02-27 18:28 UTC (permalink / raw)
To: Andrew Bailey; +Cc: luca.vizzarro, dmarx, dev
On Fri, Feb 13, 2026 at 2:36 PM Andrew Bailey <abailey@iol.unh.edu> wrote:
>
> Currently, a test case is decorated to signify whether to use Scapy or
> TREX. This change allows test suites that do not use a traffic generator
> to avoid the overhead of initializing either one.
>
> Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
> ---
> dts/configurations/test_run.example.yaml | 1 +
> dts/framework/config/test_run.py | 3 +++
> dts/framework/test_run.py | 2 +-
> dts/framework/test_suite.py | 6 +++++
> dts/tests/TestSuite_cryptodev_throughput.py | 30 +++++++++++----------
> 5 files changed, 27 insertions(+), 15 deletions(-)
>
> diff --git a/dts/configurations/test_run.example.yaml b/dts/configurations/test_run.example.yaml
> index c8035fccf0..3eaffc2aa4 100644
> --- a/dts/configurations/test_run.example.yaml
> +++ b/dts/configurations/test_run.example.yaml
> @@ -31,6 +31,7 @@ func_traffic_generator:
> # config: "/opt/trex_config/trex_config.yaml" # Additional configuration files. (Leave blank if not required)
> perf: false # disable performance testing
> func: true # enable functional testing
> +crypto: false # disable cryptographic testing
> use_virtual_functions: false # use virtual functions (VFs) instead of physical functions
> skip_smoke_tests: true # optional
> # by removing the `test_suites` field, this test run will run every test suite available
> diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
> index 6c292a3675..39f2c7cdf6 100644
> --- a/dts/framework/config/test_run.py
> +++ b/dts/framework/config/test_run.py
> @@ -481,6 +481,8 @@ class TestRunConfiguration(FrozenModel):
> perf: bool
> #: Whether to run functional tests.
> func: bool
> + #: Whether to run cryptography tests.
> + crypto: bool
I think this is good for now, but long term we need to discuss if it
makes sense to keep func, perf, and crypto, or if we want something
like ethdev_func, ethdev_perf, crypto? Can you ticket this on Bugzilla
so others can comment?
> #: Whether to run the testing with virtual functions instead of physical functions
> use_virtual_functions: bool
> #: Whether to skip smoke tests.
> @@ -522,6 +524,7 @@ def filter_tests(
> for tt in t.test_cases
> if (tt.test_type is TestCaseType.FUNCTIONAL and self.func)
> or (tt.test_type is TestCaseType.PERFORMANCE and self.perf)
> + or (tt.test_type is TestCaseType.CRYPTO and self.crypto)
> ),
> )
> for t in test_suites
> diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
> index 36e6c5a44c..ada628c59e 100644
> --- a/dts/framework/test_run.py
> +++ b/dts/framework/test_run.py
> @@ -349,7 +349,7 @@ def next(self) -> State | None:
>
> if test_run.config.use_virtual_functions:
> test_run.ctx.topology.instantiate_vf_ports()
> - if test_run.ctx.sut_node.cryptodevs:
> + if test_run.ctx.sut_node.cryptodevs and test_run.config.crypto:
> test_run.ctx.topology.instantiate_crypto_vf_ports()
> test_run.ctx.topology.configure_cryptodevs("dpdk")
>
> diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
> index 9c57e343ac..e86096cefe 100644
> --- a/dts/framework/test_suite.py
> +++ b/dts/framework/test_suite.py
> @@ -174,6 +174,8 @@ def filter_test_cases(
> perf_test_cases.add(test_case)
> case TestCaseType.FUNCTIONAL:
> func_test_cases.add(test_case)
> + case TestCaseType.CRYPTO:
> + pass
>
> if test_case_sublist_copy:
> raise ConfigurationError(
> @@ -279,6 +281,8 @@ class TestCaseType(Enum):
> FUNCTIONAL = auto()
> #:
> PERFORMANCE = auto()
> + #:
> + CRYPTO = auto()
>
>
> class TestCase(TestProtocol, Protocol[TestSuiteMethodType]):
> @@ -331,6 +335,8 @@ def _decorator(func: TestSuiteMethodType) -> type[TestCase]:
> func_test: Callable = TestCase.make_decorator(TestCaseType.FUNCTIONAL)
> #: The decorator for performance test cases.
> perf_test: Callable = TestCase.make_decorator(TestCaseType.PERFORMANCE)
> +#: The decorator for cryptography test cases.
> +crypto_test: Callable = TestCase.make_decorator(TestCaseType.CRYPTO)
>
>
> @dataclass
> diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
> index 871e47ae63..387afe8bcd 100644
> --- a/dts/tests/TestSuite_cryptodev_throughput.py
> +++ b/dts/tests/TestSuite_cryptodev_throughput.py
> @@ -32,7 +32,7 @@
> from api.test import verify
> from framework.context import get_ctx
> from framework.exception import SkippedTestException
> -from framework.test_suite import BaseConfig, TestSuite, func_test
> +from framework.test_suite import BaseConfig, TestSuite, crypto_test
> from framework.testbed_model.virtual_device import VirtualDevice
>
> config_list: list[dict[str, int | float | str]] = [
> @@ -142,7 +142,7 @@ def _verify_throughput(
> result_list.append(result_dict)
> return result_list
>
> - @func_test
> + @crypto_test
> def aes_cbc(self) -> None:
> """aes_cbc test.
>
> @@ -172,6 +172,7 @@ def aes_cbc(self) -> None:
> for result in results:
> verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
>
> + @crypto_test
> def aes_cbc_sha1(self) -> None:
> """aes_cbc_sha1 test.
>
> @@ -206,6 +207,7 @@ def aes_cbc_sha1(self) -> None:
> for result in results:
> verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
>
> + @crypto_test
> def aes_cbc_sha2(self) -> None:
> """aes_cbc_sha2 test.
>
> @@ -239,7 +241,7 @@ def aes_cbc_sha2(self) -> None:
> for result in results:
> verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
>
> - @func_test
> + @crypto_test
> def aes_cbc_sha2_digest_16(self) -> None:
> """aes_cbc_sha2_digest_16 test.
>
> @@ -273,7 +275,7 @@ def aes_cbc_sha2_digest_16(self) -> None:
> for result in results:
> verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
>
> - @func_test
> + @crypto_test
> def aead_aes_gcm(self) -> None:
> """aead_aes_gcm test.
>
> @@ -305,7 +307,7 @@ def aead_aes_gcm(self) -> None:
> for result in results:
> verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
>
> - @func_test
> + @crypto_test
> def aes_docsisbpi(self) -> None:
> """aes_docsiscpi test.
>
> @@ -366,7 +368,7 @@ def sha1_hmac(self) -> None:
> for result in results:
> verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
>
> - @func_test
> + @crypto_test
> def snow3g_uea2_snow3g_uia2(self) -> None:
> """snow3g_uea2_snow3g_uia2 test.
>
> @@ -401,7 +403,7 @@ def snow3g_uea2_snow3g_uia2(self) -> None:
> for result in results:
> verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
>
> - @func_test
> + @crypto_test
> def zuc_eea3_zuc_eia3(self) -> None:
> """zuc_eea3_zuc_eia3 test.
>
> @@ -436,7 +438,7 @@ def zuc_eea3_zuc_eia3(self) -> None:
> for result in results:
> verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
>
> - @func_test
> + @crypto_test
> def kasumi_f8_kasumi_f9(self) -> None:
> """kasumi_f8 kasumi_f9 test.
>
> @@ -473,7 +475,7 @@ def kasumi_f8_kasumi_f9(self) -> None:
>
> # BEGIN VDEV TESTS
>
> -
> + @crypto_test
> def aesni_mb_vdev(self) -> None:
> """aesni_mb virtual device test.
>
> @@ -510,7 +512,7 @@ def aesni_mb_vdev(self) -> None:
> for result in results:
> verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
>
> - @func_test
> + @crypto_test
> def aesni_gcm_vdev(self):
> """aesni_gcm virtual device test.
>
> @@ -543,7 +545,7 @@ def aesni_gcm_vdev(self):
> for result in results:
> verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
>
> - @func_test
> + @crypto_test
> def kasumi_vdev(self) -> None:
> """Kasmumi virtual device test.
>
> @@ -579,7 +581,7 @@ def kasumi_vdev(self) -> None:
> for result in results:
> verify(result["passed"] == "PASS", "gbps fell below delta tolerance")
>
> - @func_test
> + @crypto_test
> def snow3g_vdev(self) -> None:
> """snow3g virtual device test.
>
> @@ -616,7 +618,7 @@ def snow3g_vdev(self) -> None:
> for result in results:
> verify(result["passed"] == "PASS", "gpbs fell below delta tolerance")
>
> - @func_test
> + @crypto_test
> def zuc_vdev(self) -> None:
> """Zuc virtual device test.
>
> @@ -653,7 +655,7 @@ def zuc_vdev(self) -> None:
> for result in results:
> verify(result["passed"] == "PASS", "gpbs fell below delta tolerance")
>
> - @func_test
> + @crypto_test
> def open_ssl_vdev(self) -> None:
> """open_ssl virtual device test.
>
> --
> 2.50.1
>
Have you tested this by running an execution that includes both
cryptodev tests and ethdev tests (both func and perf)?
Reviewed-by: Patrick Robb <probb@iol.unh.edu>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v3 5/5] dts: automate VFIO-PCI modprobe in node setup
2026-02-13 19:35 ` [PATCH v3 5/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
@ 2026-02-27 18:33 ` Patrick Robb
0 siblings, 0 replies; 41+ messages in thread
From: Patrick Robb @ 2026-02-27 18:33 UTC (permalink / raw)
To: Andrew Bailey
Cc: luca.vizzarro, dmarx, dev, Ji, Kai, Dharmik Jayesh Thakkar,
David Marchand
On Fri, Feb 13, 2026 at 2:36 PM Andrew Bailey <abailey@iol.unh.edu> wrote:
>
> Currently, users must modprobe VFIO-PCI before running DTS when using a
> non-mellanox NIC. This patch automates the process on test run start up.
> In addition, if the SUT is expected to use a subset of a few QAT devices,
> VFIO-PCI must be loaded in a separate manner. This patch adds this
> distinction.
>
> Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
> ---
> dts/framework/testbed_model/linux_session.py | 24 ++++++++++++++++++++
> dts/framework/testbed_model/os_session.py | 4 ++++
> 2 files changed, 28 insertions(+)
>
> diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
> index 27a5f48ecf..a761ca11fe 100644
> --- a/dts/framework/testbed_model/linux_session.py
> +++ b/dts/framework/testbed_model/linux_session.py
> @@ -224,6 +224,30 @@ def devbind_script_path(self) -> PurePath:
> """
> raise InternalError("Accessed devbind script path before setup.")
>
> + def load_vfio(self, pf_port: Port) -> None:
> + """Overrides :meth:`~os_session.OSSession,load_vfio`."""
> + cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
> + device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
> + if device and device.group(1) in ["37c8", "0435", "19e2"]:
> + self.send_command(
> + "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
> + privileged=True,
> + )
> + self.send_command(
> + "modprobe -r vfio_virqfd; modprobe -r vfio",
> + privileged=True,
> + )
> + self.send_command(
> + "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
> + )
> + self.send_command(
> + "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
> + privileged=True,
> + )
> + else:
> + self.send_command("modprobe vfio-pci")
> + self.refresh_lshw()
> +
> def create_crypto_vfs(self, pf_port: list[Port]) -> None:
> """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
>
> diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
> index 2eeeea6967..4a4fc1f34a 100644
> --- a/dts/framework/testbed_model/os_session.py
> +++ b/dts/framework/testbed_model/os_session.py
> @@ -615,6 +615,10 @@ def configure_port_mtu(self, mtu: int, port: Port) -> None:
> port: Port to set `mtu` on.
> """
>
> + @abstractmethod
> + def load_vfio(self, pf_port: Port) -> None:
> + """Load the vfio module according to the device type of the given port."""
> +
> @abstractmethod
> def create_crypto_vfs(self, pf_ports: list[Port]) -> None:
> """Creatues virtual functions for each port in 'pf_ports'.
typo
> --
> 2.50.1
>
Adding Dharmik and David since they were the ones interested in this
custom vfio-pci loading way back when I added the support to legacy
DTS a couple years ago. And also Kai Ji since he is QAT maintainer and
must be familiar with the custom vfio-pci loading requirement for QAT
8970 and other cards.
In DPDK, your individual patches need to be "non-breaking" if they are
applied in chronological order. I.e. if only patch 1 is applied
nothing breaks, if patches 1 and 2 are applied nothing breaks etc.
It's challenging but let's make a best effort to comply. In the case
of this patch, it breaks the rule since load_vfio() is called in the
2/5 patch but does not exist until this 5/5 patch. So, I think the
easiest fix would be to move this 5/5 patch to be in between the
current 1/5 and current 2/5 patch.
I think the content of the patch looks good otherwise. Let's make sure
we run the testsuites on the 8970 card to validate the correct
"custom" vfio-pci loading.
Reviewed-by: Patrick Robb <probb@iol.unh.edu>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v3 4/5] dts: add crypto test decorator
2026-02-27 18:28 ` Patrick Robb
@ 2026-02-27 19:18 ` Andrew Bailey
0 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-02-27 19:18 UTC (permalink / raw)
To: Patrick Robb; +Cc: luca.vizzarro, dmarx, dev
[-- Attachment #1: Type: text/plain, Size: 273 bytes --]
>
>
> Have you tested this by running an execution that includes both
> cryptodev tests and ethdev tests (both func and perf)?
>
> Yes, this has been tested and runs cryptodev tests alongside perf/func
tests
with no issues. I will submit a ticket documenting this concern.
[-- Attachment #2: Type: text/html, Size: 515 bytes --]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v3 3/5] dts: add cryptodev throughput test suite
2026-02-27 18:24 ` Patrick Robb
@ 2026-02-27 19:43 ` Andrew Bailey
0 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-02-27 19:43 UTC (permalink / raw)
To: Patrick Robb; +Cc: luca.vizzarro, dmarx, dev
[-- Attachment #1: Type: text/plain, Size: 794 bytes --]
>
> I noticed that for the VF creation, the crypto interface is being read
> 3x. Are there 1-2 additional checks happening, or are we just hitting
> these code paths multiple times for good reason? Maybe not, I'm just
> checking if there are any inadvertent calls. I guess this is a matter
> for the 2/5 patch though:
>
> 2026/02/26 04:44:46 - test_run_setup - dts.SUT 1 - INFO - Sending:
> 'sudo -- sh -c 'cat /sys/bus/pci/devices/0000\:76\:00.0/sriov_numvfs''
> 2026/02/26 04:44:46 - test_run_setup - dts.SUT 1 - INFO - Sending:
> 'sudo -- sh -c 'cat /sys/bus/pci/devices/0000\:76\:00.0/sriov_numvfs''
> 2026/02/26 04:44:47 - test_run_setup - dts.SUT 1 - INFO - Sending:
> 'sudo -- sh -c 'cat /sys/bus/pci/devices/0000\:76\:00.0/sriov_numvfs''
>
> This is not normal and will be fixed in v4.
[-- Attachment #2: Type: text/html, Size: 1118 bytes --]
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v4 0/5] dts: add cryptodev testing support
2026-02-10 19:34 [PATCH v2 0/5] dts: add cryptodev testing support Andrew Bailey
` (5 preceding siblings ...)
2026-02-13 19:35 ` [PATCH v3 0/5] dts: add cryptodev testing support Andrew Bailey
@ 2026-03-02 18:54 ` Andrew Bailey
2026-03-02 18:54 ` [PATCH v4 1/5] dts: add find float method to text parser Andrew Bailey
` (4 more replies)
2026-03-06 16:40 ` [PATCH v5 0/5] dts: add cryptodev testing support Andrew Bailey
7 siblings, 5 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-03-02 18:54 UTC (permalink / raw)
To: probb
Cc: luca.vizzarro, dmarx, dev, gakhil, kai.ji, dharmikjayesh,
david.marchand, Andrew Bailey
This patch series adds support to use the dpdk-test-crypto-perf
application. This application utilizes crypto devices that must be
listed in the format of a port under the `cryptodevs:` label. Along
with this, the application does not utilize a traffic generator and
therefore a new test suite decorator has been made to signify this.
Crypto tests must be enabled in test_run.yaml as `crypto: true`.
It is not supported to enable crypto testing along with functional
or performance testing. Specific capabilities of a crypto device
cannot be gathered at setup and therefore, non-relevant tests skip at
runtime. Finally, the application must be run with virtual functions
and the process of creating and binding them has been automated within
this series.
---
v2:
* Updated example yaml files for test_run and tests_config.
* Expanded test suite coverage.
* Resolved git problems where patches were removing code added by the
previous commit.
* Updated test suite to only run tests that are configured in the
tests_config yaml.
v3:
* Fixed some tests that were being run with incorrect arguments.
* Updated crypto-device pci paths for driver binding and VF creation.
* Updated example yaml files for tests_config and nodes files.
v4:
* Updated how virtual functions are allocated to a run of the cryptodev
application.
* Updated config files to be more readable.
* Reduced total commands sent to SUT node for crypto VF creation.
* Fixed various typos and docstring issues.
* Updated a vdev test case to use the correct devtype.
Andrew Bailey (5):
dts: add find float method to text parser
dts: automate VFIO-PCI modprobe in node setup
dts: add cryptodev package to DTS
dts: add cryptodev throughput test suite
dts: add crypto test decorator
.../tests.TestSuite_cryptodev_throughput.rst | 8 +
dts/api/cryptodev/__init__.py | 137 ++++
dts/api/cryptodev/config.py | 508 +++++++++++++
dts/api/cryptodev/types.py | 185 +++++
dts/configurations/nodes.example.yaml | 14 +
dts/configurations/test_run.example.yaml | 1 +
dts/configurations/tests_config.example.yaml | 8 +
dts/framework/config/node.py | 4 +
dts/framework/config/test_run.py | 3 +
dts/framework/params/types.py | 65 ++
dts/framework/parser.py | 28 +
dts/framework/remote_session/dpdk_shell.py | 5 +-
dts/framework/test_run.py | 5 +
dts/framework/test_suite.py | 6 +
dts/framework/testbed_model/linux_session.py | 69 ++
dts/framework/testbed_model/node.py | 10 +
dts/framework/testbed_model/os_session.py | 34 +
dts/framework/testbed_model/topology.py | 78 +-
dts/tests/TestSuite_cryptodev_throughput.py | 695 ++++++++++++++++++
19 files changed, 1860 insertions(+), 3 deletions(-)
create mode 100644 doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
create mode 100644 dts/api/cryptodev/__init__.py
create mode 100644 dts/api/cryptodev/config.py
create mode 100644 dts/api/cryptodev/types.py
create mode 100644 dts/tests/TestSuite_cryptodev_throughput.py
--
2.50.1
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v4 1/5] dts: add find float method to text parser
2026-03-02 18:54 ` [PATCH v4 0/5] dts: add cryptodev testing support Andrew Bailey
@ 2026-03-02 18:54 ` Andrew Bailey
2026-03-02 18:54 ` [PATCH v4 2/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
` (3 subsequent siblings)
4 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-03-02 18:54 UTC (permalink / raw)
To: probb
Cc: luca.vizzarro, dmarx, dev, gakhil, kai.ji, dharmikjayesh,
david.marchand, Andrew Bailey
Currently, there is no way to gather floats from text using the parser.
Adding a new method to find floats will allow testsuites to utilize
valuable float values that are output from applications.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/framework/parser.py | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 4170cdb1dd..3075c36857 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -220,6 +220,34 @@ def find_int(
return TextParser.wrap(TextParser.find(pattern), partial(int, base=int_base))
+ @staticmethod
+ def find_float(
+ pattern: str | re.Pattern[str],
+ flags: re.RegexFlag = re.RegexFlag(0),
+ ) -> ParserFn:
+ """Makes a parser function that converts the match of :meth:`~find` to float.
+
+ This function is compatible only with a pattern containing one capturing group.
+
+ Args:
+ pattern: The regular expression pattern.
+ flags: The regular expression flags. Ignored if the given pattern is already compiled.
+
+ Raises:
+ InternalError: If the pattern does not have exactly one capturing group.
+
+ Returns:
+ ParserFn: A dictionary for the `dataclasses.field` metadata argument containing the
+ :meth:`~find` parser function wrapped by the float built-in.
+ """
+ if isinstance(pattern, str):
+ pattern = re.compile(pattern, flags)
+
+ if pattern.groups != 1:
+ raise InternalError("only one capturing group is allowed with this parser function")
+
+ return TextParser.wrap(TextParser.find(pattern), partial(float))
+
"""============ END PARSER FUNCTIONS ============"""
@classmethod
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 2/5] dts: automate VFIO-PCI modprobe in node setup
2026-03-02 18:54 ` [PATCH v4 0/5] dts: add cryptodev testing support Andrew Bailey
2026-03-02 18:54 ` [PATCH v4 1/5] dts: add find float method to text parser Andrew Bailey
@ 2026-03-02 18:54 ` Andrew Bailey
2026-03-02 18:54 ` [PATCH v4 3/5] dts: add cryptodev package to DTS Andrew Bailey
` (2 subsequent siblings)
4 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-03-02 18:54 UTC (permalink / raw)
To: probb
Cc: luca.vizzarro, dmarx, dev, gakhil, kai.ji, dharmikjayesh,
david.marchand, Andrew Bailey
Currently, users must modprobe VFIO-PCI before running DTS when using a
non-mellanox NIC. This patch automates the process on test run start up.
In addition, if the SUT is expected to use a subset of a few QAT devices,
VFIO-PCI must be loaded in a separate manner. This patch adds this
distinction.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/framework/testbed_model/linux_session.py | 24 ++++++++++++++++++++
dts/framework/testbed_model/os_session.py | 4 ++++
2 files changed, 28 insertions(+)
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 711d4d97c3..2f4aef1460 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -223,6 +223,30 @@ def devbind_script_path(self) -> PurePath:
"""
raise InternalError("Accessed devbind script path before setup.")
+ def load_vfio(self, pf_port: Port) -> None:
+ """Overrides :meth:`~os_session.OSSession,load_vfio`."""
+ cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
+ device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
+ if device and device.group(1) in ["37c8", "0435", "19e2"]:
+ self.send_command(
+ "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe -r vfio_virqfd; modprobe -r vfio",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
+ )
+ self.send_command(
+ "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
+ privileged=True,
+ )
+ else:
+ self.send_command("modprobe vfio-pci")
+ self.refresh_lshw()
+
def create_vfs(self, pf_port: Port) -> None:
"""Overrides :meth:`~.os_session.OSSession.create_vfs`.
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index b94c3e527b..f3064feeaa 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -615,6 +615,10 @@ def configure_port_mtu(self, mtu: int, port: Port) -> None:
port: Port to set `mtu` on.
"""
+ @abstractmethod
+ def load_vfio(self, pf_port: Port) -> None:
+ """Load the vfio module according to the device type of the given port."""
+
@abstractmethod
def create_vfs(self, pf_port: Port) -> None:
"""Creates virtual functions for `pf_port`.
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 3/5] dts: add cryptodev package to DTS
2026-03-02 18:54 ` [PATCH v4 0/5] dts: add cryptodev testing support Andrew Bailey
2026-03-02 18:54 ` [PATCH v4 1/5] dts: add find float method to text parser Andrew Bailey
2026-03-02 18:54 ` [PATCH v4 2/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
@ 2026-03-02 18:54 ` Andrew Bailey
2026-03-02 18:54 ` [PATCH v4 4/5] dts: add cryptodev throughput test suite Andrew Bailey
2026-03-02 18:54 ` [PATCH v4 5/5] dts: add crypto test decorator Andrew Bailey
4 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-03-02 18:54 UTC (permalink / raw)
To: probb
Cc: luca.vizzarro, dmarx, dev, gakhil, kai.ji, dharmikjayesh,
david.marchand, Andrew Bailey
Running the DPDK test crypto performance application is essential for
testing cryptodev performance in DTS. This application takes numerous
arguments and can be run in four modes; Throughput, Latency,
PMD-cyclecount, and Verify. The package to add in this commit allows for
this application to be run in any of these modes with user supplied
arguments.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/api/cryptodev/__init__.py | 137 +++++
dts/api/cryptodev/config.py | 508 +++++++++++++++++++
dts/api/cryptodev/types.py | 185 +++++++
dts/configurations/nodes.example.yaml | 14 +
dts/framework/config/node.py | 4 +
dts/framework/params/types.py | 65 +++
dts/framework/remote_session/dpdk_shell.py | 5 +-
dts/framework/test_run.py | 5 +
dts/framework/testbed_model/linux_session.py | 45 ++
dts/framework/testbed_model/node.py | 10 +
dts/framework/testbed_model/os_session.py | 30 ++
dts/framework/testbed_model/topology.py | 78 ++-
12 files changed, 1083 insertions(+), 3 deletions(-)
create mode 100644 dts/api/cryptodev/__init__.py
create mode 100644 dts/api/cryptodev/config.py
create mode 100644 dts/api/cryptodev/types.py
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
new file mode 100644
index 0000000000..a4fafc3713
--- /dev/null
+++ b/dts/api/cryptodev/__init__.py
@@ -0,0 +1,137 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""Cryptodev-pmd non-interactive shell.
+
+Typical usage example in a TestSuite::
+
+ cryptodev = CryptodevPmd(CryptoPmdParams)
+ stats = cryptodev.run_app()
+"""
+
+import re
+from typing import TYPE_CHECKING, Any
+
+from typing_extensions import Unpack
+
+from api.cryptodev.config import CryptoPmdParams, TestType
+from api.cryptodev.types import (
+ CryptodevResults,
+ LatencyResults,
+ PmdCyclecountResults,
+ ThroughputResults,
+ VerifyResults,
+)
+from framework.context import get_ctx
+from framework.exception import RemoteCommandExecutionError, SkippedTestException
+from framework.remote_session.dpdk_shell import compute_eal_params
+
+if TYPE_CHECKING:
+ from framework.params.types import CryptoPmdParamsDict
+from pathlib import PurePath
+
+
+class Cryptodev:
+ """non-interactive cryptodev application.
+
+ Attributes:
+ _app_params: app parameters to pass to dpdk-test-crypto-perf application
+ """
+
+ _app_params: dict[str, Any]
+
+ def __init__(self, **app_params: Unpack["CryptoPmdParamsDict"]) -> None:
+ """Initialize the cryptodev application.
+
+ Args:
+ app_params: The application parameters as keyword arguments.
+
+ Raises:
+ ValueError: if the build environment is `None`
+ """
+ self._app_params = {}
+ for k, v in app_params.items():
+ if v is not None:
+ self._app_params[k] = (
+ self.vector_directory.joinpath(str(v)) if k == "test_file" else v
+ )
+ dpdk = get_ctx().dpdk.build
+ if dpdk is None:
+ raise ValueError("No DPDK build environment exists.")
+ self._path = dpdk.get_app("test-crypto-perf")
+
+ @property
+ def path(self) -> PurePath:
+ """Get the path to the cryptodev application.
+
+ Returns:
+ The path to the cryptodev application.
+ """
+ return PurePath(self._path)
+
+ @property
+ def vector_directory(self) -> PurePath:
+ """Get the path to the cryptodev vector files.
+
+ Returns:
+ The path to the cryptodev vector files.
+ """
+ return get_ctx().dpdk_build.remote_dpdk_tree_path.joinpath("app/test-crypto-perf/data/")
+
+ def run_app(self, num_vfs: int = 1) -> list[CryptodevResults]:
+ """Run the cryptodev application with the given app parameters.
+
+ Args:
+ num_vfs: number of virtual functions to run the crypto-dev application with.
+
+ Raises:
+ SkippedTestException: If the device type is not supported on the main session.
+ RemoteCommandExecutionError: If there is an error running the command.
+
+ Returns:
+ list[CryptodevResults]: The list of parsed results for the cryptodev application.
+ """
+ try:
+ result = get_ctx().dpdk.run_dpdk_app(
+ self.path,
+ compute_eal_params(
+ CryptoPmdParams(
+ allowed_ports=get_ctx().topology.get_crypto_vfs(num_vfs),
+ memory_channels=get_ctx().dpdk.config.memory_channels,
+ **self._app_params,
+ ),
+ ),
+ timeout=120,
+ )
+ except RemoteCommandExecutionError as e:
+ if "Crypto device type does not support capabilities requested" in e._command_stderr:
+ raise SkippedTestException(
+ f"{self._app_params['devtype']} does not support the requested capabilities"
+ )
+ elif "Failed to initialise requested crypto device type" in e._command_stderr:
+ raise SkippedTestException(
+ f"could not run application with devtype {self._app_params['devtype']}"
+ )
+ elif "failed to parse device" in e._command_stderr:
+ raise SkippedTestException(
+ f"dependencies missing for virtual device {self._app_params['vdevs'][0].name}"
+ )
+ raise e
+
+ regex = r"^\s+\d+.*$"
+ parser_options = re.MULTILINE
+ parser: type[CryptodevResults]
+
+ match self._app_params["ptest"]:
+ case TestType.throughput:
+ parser = ThroughputResults
+ case TestType.latency:
+ regex = r"total operations:.*time[^\n]*"
+ parser_options |= re.DOTALL
+ parser = LatencyResults
+ case TestType.pmd_cyclecount:
+ parser = PmdCyclecountResults
+ case TestType.verify:
+ parser = VerifyResults
+
+ return [parser.parse(line) for line in re.findall(regex, result.stdout, parser_options)]
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
new file mode 100644
index 0000000000..ff4ce25c93
--- /dev/null
+++ b/dts/api/cryptodev/config.py
@@ -0,0 +1,508 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+"""Module containing types and parameter classes for cryptodev-pmd application."""
+
+from dataclasses import dataclass, field
+from enum import auto
+from typing import Literal
+
+from framework.params import Params, Switch
+from framework.params.eal import EalParams
+from framework.utils import StrEnum
+
+Silent = Literal[""]
+
+
+class DeviceType(StrEnum):
+ """Enum for cryptodev device types.
+
+ Attributes:
+ crypto_aesni_gcm: AES-NI GCM device type.
+ crypto_aesni_mb: AES-NI MB device type.
+ crypto_armv8: ARMv8 device type.
+ crypto_cn10k: CN10K device type.
+ crypto_cn9k: CN9K device type.
+ crypto_dpaa_sec: DPAA SEC device type.
+ crypto_dpaa2_sec: DPAA2 SEC device type.
+ crypto_kasumi: KASUMI device type.
+ crypto_mvsam: MVSAM device type.
+ crypto_null: NULL device type.
+ crypto_octeontx: OCTEONTX device type.
+ crypto_openssl: OpenSSL device type.
+ crypto_qat: QAT device type.
+ crypto_scheduler: Scheduler device type.
+ crypto_snow3g: SNOW3G device type.
+ crypto_zuc: ZUC device type.
+ """
+
+ crypto_aesni_gcm = auto()
+ crypto_aesni_mb = auto()
+ crypto_armv8 = auto()
+ crypto_cn10k = auto()
+ crypto_cn9k = auto()
+ crypto_dpaa_sec = auto()
+ crypto_dpaa2_sec = auto()
+ crypto_kasumi = auto()
+ crypto_mvsam = auto()
+ crypto_null = auto()
+ crypto_octeontx = auto()
+ crypto_openssl = auto()
+ crypto_qat = auto()
+ crypto_scheduler = auto()
+ crypto_snow3g = auto()
+ crypto_zuc = auto()
+
+
+def get_device_from_str(device: str) -> DeviceType | None:
+ """Get a device type from the given string.
+
+ Args:
+ device: the string representation of the desired device.
+
+ Returns:
+ the device as a DeviceType object.
+ """
+ for item in DeviceType:
+ if device in item.value:
+ return item
+ return None
+
+
+class OperationType(StrEnum):
+ """Enum for cryptodev operation types.
+
+ Attributes:
+ aead: AEAD operation type.
+ auth_only: Authentication only operation type.
+ auth_then_cipher: Authentication then cipher operation type.
+ cipher_only: Cipher only operation type.
+ cipher_then_auth: Cipher then authentication operation type.
+ docsis: DOCSIS operation type.
+ ecdsa_p192r1 = ECDSA P-192R1 operation type.
+ ecdsa_p224r1 = ECDSA P-224R1 operation type.
+ ecdsa_p256r1: ECDSA P-256R1 operation type.
+ ecdsa_p384r1 = ECDSA P-384R1 operation type.
+ ecdsa_p521r1 = ECDSA P-521R1 operation type.
+ eddsa_25519: EdDSA 25519 operation type.
+ modex: Modex operation type.
+ ipsec: IPsec operation type.
+ pdcp: PDCP operation type.
+ rsa: RSA operation type.
+ sm2: SM2 operation type.
+ tls_record: TLS record operation type.
+ """
+
+ aead = auto()
+ auth_only = "auth-only"
+ auth_then_cipher = "auth-then-cipher"
+ cipher_only = "cipher-only"
+ cipher_then_auth = "cipher-then-auth"
+ docsis = auto()
+ ecdsa_p192r1 = auto()
+ ecdsa_p224r1 = auto()
+ ecdsa_p256r1 = auto()
+ ecdsa_p384r1 = auto()
+ ecdsa_p521r1 = auto()
+ eddsa_25519 = auto()
+ modex = auto()
+ ipsec = auto()
+ pdcp = auto()
+ rsa = auto()
+ sm2 = auto()
+ tls_record = "tls-record"
+
+ def __str__(self) -> str:
+ """Override str to return the value."""
+ return self.value
+
+
+class CipherAlgorithm(StrEnum):
+ """Enum for cryptodev cipher algorithms.
+
+ Attributes:
+ aes_cbc: AES CBC cipher algorithm.
+ aes_ctr: AES CTR cipher algorithm.
+ aes_docsisbpi: AES DOCSIS BPI cipher algorithm.
+ aes_ecb: AES ECB cipher algorithm.
+ aes_f8: AES F8 cipher algorithm.
+ aes_xts: AES XTS cipher algorithm.
+ arc4: ARC4 cipher algorithm.
+ null: NULL cipher algorithm.
+ three_des_cbc: 3DES CBC cipher algorithm.
+ three_des_ctr: 3DES CTR cipher algorithm.
+ three_des_ecb: 3DES ECB cipher algorithm.
+ kasumi_f8: KASUMI F8 cipher algorithm.
+ snow3g_uea2: SNOW3G UEA2 cipher algorithm.
+ zuc_eea3: ZUC EEA3 cipher algorithm.
+ """
+
+ aes_cbc = "aes-cbc"
+ aes_ctr = "aes-ctr"
+ aes_docsisbpi = "aes-docsisbpi"
+ aes_ecb = "aes-ecb"
+ aes_f8 = "aes-f8"
+ aes_gcm = "aes-gcm"
+ aes_xts = "aes-xts"
+ arc4 = auto()
+ null = auto()
+ three_des_cbc = "3des-cbc"
+ three_des_ctr = "3des-ctr"
+ three_des_ecb = "3des-ecb"
+ kasumi_f8 = "kasumi-f8"
+ snow3g_uea2 = "snow3g-uea2"
+ zuc_eea3 = "zuc-eea3"
+
+ def __str__(self):
+ """Override str to return the value."""
+ return self.value
+
+
+class AuthenticationAlgorithm(StrEnum):
+ """Enum for cryptodev authentication algorithms.
+
+ Attributes:
+ aes_cbc_mac: AES CBC MAC authentication algorithm.
+ aes_cmac: AES CMAC authentication algorithm.
+ aes_gmac: AES GMAC authentication algorithm.
+ aes_xcbc_mac: AES XCBC MAC authentication algorithm.
+ kasumi_f9: KASUMI F9 authentication algorithm.
+ md5: MD5 authentication algorithm.
+ md5_hmac: MD5 HMAC authentication algorithm.
+ sha1: SHA1 authentication algorithm.
+ sha1_hmac: SHA1 HMAC authentication algorithm.
+ sha2_224: SHA2-224 authentication algorithm.
+ sha2_224_hmac: SHA2-224 HMAC authentication algorithm.
+ sha2_256: SHA2-256 authentication algorithm.
+ sha2_256_hmac: SHA2-256 HMAC authentication algorithm.
+ sha2_384: SHA2-384 authentication algorithm.
+ sha2_384_hmac: SHA2-384 HMAC authentication algorithm.
+ sha2_512: SHA2-512 authentication algorithm.
+ sha2_512_hmac: SHA2-512 HMAC authentication algorithm.
+ snow3g_uia2: SNOW3G UIA2 authentication algorithm.
+ zuc_eia3: ZUC EIA3 authentication algorithm.
+ """
+
+ aes_cbc_mac = "aes-cbc-mac"
+ aes_cmac = "aes-cmac"
+ aes_gmac = "aes-gmac"
+ aes_xcbc_mac = "aes-xcbc-mac"
+ kasumi_f9 = "kasumi-f9"
+ md5 = auto()
+ md5_hmac = "md5-hmac"
+ sha1 = auto()
+ sha1_hmac = "sha1-hmac"
+ sha2_224 = "sha2-224"
+ sha2_224_hmac = "sha2-224-hmac"
+ sha2_256 = "sha2-256"
+ sha2_256_hmac = "sha2-256-hmac"
+ sha2_384 = "sha2-384"
+ sha2_384_hmac = "sha2-384-hmac"
+ sha2_512 = "sha2-512"
+ sha2_512_hmac = "sha2-512-hmac"
+ snow3g_uia2 = "snow3g-uia2"
+ zuc_eia3 = "zuc-eia3"
+
+ def __str__(self) -> str:
+ """Override str to return the value."""
+ return self.value
+
+
+class TestType(StrEnum):
+ """Enum for cryptodev test types.
+
+ Attributes:
+ latency: Latency test type.
+ pmd_cyclecount: PMD cyclecount test type.
+ throughput: Throughput test type.
+ verify: Verify test type.
+ """
+
+ latency = auto()
+ pmd_cyclecount = "pmd-cyclecount"
+ throughput = auto()
+ verify = auto()
+
+ def __str__(self) -> str:
+ """Override str to return the value."""
+ return self.value
+
+
+class EncryptDecryptSwitch(StrEnum):
+ """Enum for cryptodev encrypt/decrypt operations.
+
+ Attributes:
+ decrypt: Decrypt operation.
+ encrypt: Encrypt operation.
+ """
+
+ decrypt = auto()
+ encrypt = auto()
+
+
+class AuthenticationOpMode(StrEnum):
+ """Enum for cryptodev authentication operation modes.
+
+ Attributes:
+ generate: Generate operation mode.
+ verify: Verify operation mode.
+ """
+
+ generate = auto()
+ verify = auto()
+
+
+class AeadAlgName(StrEnum):
+ """Enum for cryptodev AEAD algorithms.
+
+ Attributes:
+ aes_ccm: AES CCM algorithm.
+ aes_gcm: AES GCM algorithm.
+ """
+
+ aes_ccm = "aes-ccm"
+ aes_gcm = "aes-gcm"
+
+ def __str__(self):
+ """Override str to return the value."""
+ return self.value
+
+
+class AsymOpMode(StrEnum):
+ """Enum for cryptodev asymmetric operation modes.
+
+ Attributes:
+ decrypt: Decrypt operation mode.
+ encrypt: Encrypt operation mode.
+ sign: Sign operation mode.
+ verify: Verify operation mode.
+ """
+
+ decrypt = auto()
+ encrypt = auto()
+ sign = auto()
+ verify = auto()
+
+
+class PDCPDomain(StrEnum):
+ """Enum for cryptodev PDCP domains.
+
+ Attributes:
+ control: Control domain.
+ user: User domain.
+ """
+
+ control = auto()
+ user = auto()
+
+
+class RSAPrivKeyType(StrEnum):
+ """Enum for cryptodev RSA private key types.
+
+ Attributes:
+ exp: Exponent key type.
+ qt: QT key type.
+ """
+
+ exp = auto()
+ qt = auto()
+
+
+class TLSVersion(StrEnum):
+ """Enum for cryptodev TLS versions.
+
+ Attributes:
+ DTLS1_2: DTLS 1.2 version.
+ TLS1_2: TLS 1.2 version.
+ TLS1_3: TLS 1.3 version.
+ """
+
+ DTLS1_2 = "DTLS1.2"
+ TLS1_2 = "TLS1.2"
+ TLS1_3 = "TLS1.3"
+
+ def __str__(self):
+ """Override str to return the value."""
+ return self.value
+
+
+class RSAPrivKeytype(StrEnum):
+ """Enum for cryptodev RSA private key types.
+
+ Attributes:
+ exp: Exponent key type.
+ qt: QT key type.
+ """
+
+ exp = auto()
+ qt = auto()
+
+
+class BurstSizeRange:
+ """Class for burst size parameter.
+
+ Attributes:
+ burst_size: The burst size range, this list must be less than 32 elements.
+ """
+
+ burst_size: int | list[int]
+
+ def __init__(self, min: int, inc: int, max: int) -> None:
+ """Initialize the burst size range.
+
+ Args:
+ min: minimum value in range (inclusive)
+ inc: space in between each element in the range
+ max: maximum value in range (inclusive)
+
+ Raises:
+ ValueError: If the burst size range is more than 32 elements.
+ """
+ if (max - min) % inc > 32:
+ raise ValueError("Burst size range must be less than 32 elements.")
+ self.burst_size = list(range(min, max + 1, inc))
+
+
+class ListWrapper:
+ """Class for wrapping a list of integers.
+
+ One of the arguments for the cryptodev application is a list of integers. However, when
+ passing a list directly, it causes a syntax error in the command line. This class wraps
+ a list of integers and converts it to a comma-separated string for a proper command.
+ """
+
+ def __init__(self, values: list[int]) -> None:
+ """Initialize the list wrapper.
+
+ Args:
+ values: list of values to wrap
+ """
+ self.values = values
+
+ def __str__(self) -> str:
+ """Convert the list to a comma-separated string."""
+ return ",".join(str(v) for v in self.values)
+
+
+@dataclass(slots=True, kw_only=True)
+class CryptoPmdParams(EalParams):
+ """Parameters for cryptodev-pmd application.
+
+ Attributes:
+ aead_aad_sz: set the size of AEAD AAD.
+ aead_algo: set AEAD algorithm name from class `AeadAlgName`.
+ aead_iv_sz: set the size of AEAD iv.
+ aead_key_sz: set the size of AEAD key.
+ aead_op: set AEAD operation mode from class `EncryptDecryptSwitch`.
+ asym_op: set asymmetric operation mode from class `AsymOpMode`.
+ auth_algo: set authentication algorithm name.
+ auth_aad_sz: set the size of authentication AAD.
+ auth_iv_sz: set the size of authentication iv.
+ auth_key_sz: set the size of authentication key.
+ auth_op: set authentication operation mode from class `AuthenticationOpMode`.
+ buffer_sz: Set the size of a single packet (plaintext or ciphertext in it).
+ burst_sz: Set the number of packets per burst. This can be set as a single value or
+ range of values defined by class `BurstSizeRange`. Default is 16.
+ burst_sz: Set the number of packets per burst. This can be set as a single value or
+ range of values defined by class `BurstSizeRange`. Default is 16.
+ cipher_algo: Set cipher algorithm name from class `CipherAlgorithm`.
+ cipher_iv_sz: set the size of cipher iv.
+ cipher_key_sz: set the size of cipher key.
+ cipher_op: set cipher operation mode from class `EncryptDecryptSwitch`.
+ csv_friendly: Enable test result output CSV friendly rather than human friendly.
+ desc_nb: set the number of descriptors for each crypto device.
+ devtype: Set the device name from class `DeviceType`.
+ digest_sz: set the size of digest.
+ docsis_hdr_sz: set DOCSIS header size(n) in bytes.
+ enable_sdap: enable service data adaptation protocol.
+ imix: Set the distribution of packet sizes. A list of weights must be passed, containing the
+ same number of items than buffer-sz, so each item in this list will be the weight of the
+ packet size on the same position in the buffer-sz parameter (a list has to be passed in
+ that parameter).
+ low_prio_qp_mask: set low priority queue pairs set in the hexadecimal mask. This is an
+ optional parameter, if not set all queue pairs will be on the same high priority.
+ modex_len: set modex length for asymmetric crypto perf test. Supported lengths are 60,
+ 128, 255, 448. Default length is 128.
+ optype: Set operation type from class `OpType`.
+ out_of_place: Enable out-of-place crypto operations mode.
+ pdcp_sn_sz: set PDCP sequebce number size(n) in bits. Valid values of n are 5/7/12/15/18.
+ pdcp_domain: Set PDCP domain to specify short_mac/control/user plane from class
+ `PDCPDomain`.
+ pdcp_ses_hfn_en: enable fixed session based HFN instead of per packet HFN.
+ pmd_cyclecount_delay_pmd: Add a delay (in milliseconds) between enqueue and dequeue in
+ pmd-cyclecount benchmarking mode (useful when benchmarking hardware acceleration).
+ pool_sz: Set the number if mbufs to be allocated in the mbuf pool.
+ ptest: Set performance throughput test type from class `TestType`.
+ rsa_modlen: Set RSA modulus length (in bits) for asymmetric crypto perf test.
+ To be used with RSA asymmetric crypto ops.Supported lengths are 1024, 2048, 4096, 8192.
+ Default length is 1024.
+ rsa_priv_keytype: set RSA private key type from class `RSAPrivKeytype`. To be used with RSA
+ asymmetric crypto ops.
+ segment_sz: Set the size of the segment to use, for Scatter Gather List testing. Use list of
+ values in buffer-sz in descending order if segment-sz is used. By default, it is set to
+ the size of the maximum buffer size, including the digest size, so a single segment is
+ created.
+ sessionless: Enable session-less crypto operations mode.
+ shared_session: Enable sharing sessions between all queue pairs on a single crypto PMD. This
+ can be useful for benchmarking this setup, or finding and debugging concurrency errors
+ that can occur while using sessions on multiple lcores simultaneously.
+ silent: Disable options dump.
+ test_file: Set test vector file path. See the Test Vector File chapter.
+ test_name: Set specific test name section in the test vector file.
+ tls_version: Set TLS/DTLS protocol version for perf test from class `TLSVersion`.
+ Default is TLS1.2.
+ total_ops: Set the number of total operations performed.
+ """
+
+ aead_aad_sz: int | None = field(default=None, metadata=Params.long("aead-aad-sz"))
+ aead_algo: AeadAlgName | None = field(default=None, metadata=Params.long("aead-algo"))
+ aead_iv_sz: int | None = field(default=None, metadata=Params.long("aead-iv-sz"))
+ aead_key_sz: int | None = field(default=None, metadata=Params.long("aead-key-sz"))
+ aead_op: EncryptDecryptSwitch | None = field(default=None, metadata=Params.long("aead-op"))
+ asym_op: AsymOpMode | None = field(default=None, metadata=Params.long("asym-op"))
+ auth_algo: AuthenticationAlgorithm | None = field(
+ default=None, metadata=Params.long("auth-algo")
+ )
+ auth_iv_sz: int | None = field(default=None, metadata=Params.long("auth-iv-sz"))
+ auth_key_sz: int | None = field(default=None, metadata=Params.long("auth-key-sz"))
+ auth_op: AuthenticationOpMode | None = field(default=None, metadata=Params.long("auth-op"))
+ buffer_sz: BurstSizeRange | ListWrapper | int | None = field(
+ default=None, metadata=Params.long("buffer-sz")
+ )
+ burst_sz: BurstSizeRange | ListWrapper | int | None = field(
+ default=None, metadata=Params.long("burst-sz")
+ )
+ cipher_algo: CipherAlgorithm | None = field(default=None, metadata=Params.long("cipher-algo"))
+ cipher_iv_sz: int | None = field(default=None, metadata=Params.long("cipher-iv-sz"))
+ cipher_key_sz: int | None = field(default=None, metadata=Params.long("cipher-key-sz"))
+ cipher_op: EncryptDecryptSwitch | None = field(default=None, metadata=Params.long("cipher-op"))
+ csv_friendly: Switch = field(default=None, metadata=Params.long("csv-friendly"))
+ desc_nb: int | None = field(default=None, metadata=Params.long("desc-nb"))
+ devtype: DeviceType = field(metadata=Params.long("devtype"))
+ digest_sz: int | None = field(default=None, metadata=Params.long("digest-sz"))
+ docsis_hdr_sz: int | None = field(default=None, metadata=Params.long("docsis-hdr-sz"))
+ enable_sdap: Switch = None
+ imix: int | None = field(default=None, metadata=Params.long("imix"))
+ low_prio_qp_mask: int | None = field(default=None, metadata=Params.convert_value(hex))
+ modex_len: int | None = field(default=None, metadata=Params.long("modex-len"))
+ optype: OperationType | None = field(default=None, metadata=Params.long("optype"))
+ out_of_place: Switch = None
+ pdcp_sn_sz: int | None = None
+ pdcp_domain: PDCPDomain | None = field(default=None, metadata=Params.long("pdcp-domain"))
+ pdcp_ses_hfn_en: Switch | None = field(default=None, metadata=Params.long("pdcp-ses-hfn-en"))
+ pmd_cyclecount_delay_pmd: int | None = field(
+ default=None, metadata=Params.long("pmd-cyclecount-delay-pmd")
+ )
+ pool_sz: int | None = field(default=None, metadata=Params.long("pool-sz"))
+ ptest: TestType = field(default=TestType.throughput, metadata=Params.long("ptest"))
+ rsa_modlen: int | None = field(default=None, metadata=Params.long("rsa-modlen"))
+ rsa_priv_keytype: RSAPrivKeytype | None = field(
+ default=None, metadata=Params.long("rsa-priv-keytype")
+ )
+ segment_sz: int | None = field(default=None, metadata=Params.long("segment-sz"))
+ sessionless: Switch = None
+ shared_session: Switch = None
+ silent: Silent | None = field(default="", metadata=Params.long("silent"))
+ test_file: str | None = field(default=None, metadata=Params.long("test-file"))
+ test_name: str | None = field(default=None, metadata=Params.long("test-name"))
+ tls_version: TLSVersion | None = field(default=None, metadata=Params.long("tls-version"))
+ total_ops: int | None = field(default=100000, metadata=Params.long("total-ops"))
diff --git a/dts/api/cryptodev/types.py b/dts/api/cryptodev/types.py
new file mode 100644
index 0000000000..df73a86fa4
--- /dev/null
+++ b/dts/api/cryptodev/types.py
@@ -0,0 +1,185 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""Cryptodev types module.
+
+Exposes types used in the Cryptodev API.
+"""
+
+from dataclasses import dataclass, field
+
+from framework.parser import TextParser
+
+
+@dataclass
+class CryptodevResults(TextParser):
+ """Base class for all cryptodev results."""
+
+ buffer_size: int
+
+ def __iter__(self):
+ """Iteration method to parse result objects.
+
+ Yields:
+ tuple[str, int | float]: a field name and its value.
+ """
+ for field_name in self.__dataclass_fields__:
+ yield field_name, getattr(self, field_name)
+
+
+@dataclass
+class ThroughputResults(CryptodevResults):
+ """A parser for throughput test output."""
+
+ #:
+ lcore_id: int = field(metadata=TextParser.find_int(r"\s*(\d+)"))
+ #: buffer size used in the run
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"\s+(?:\d+\s+)(\d+)"),
+ )
+ #: burst size used in the run
+ burst_size: int = field(
+ metadata=TextParser.find_int(r"\s+(?:\d+\s+){2}(\d+)"),
+ )
+ #: total packets enqueued
+ enqueued: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){3}(\d+)"))
+ #: total packets dequeued
+ dequeued: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){4}(\d+)"))
+ #: packets that failed enqueue
+ failed_enqueue: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){5}(\d+)"))
+ #: packets that failed dequeue
+ failed_dequeue: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){6}(\d+)"))
+ #: mega operations per second
+ mops: float = field(metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}([\d.]+)"))
+ #: gigabits per second
+ gbps: float = field(metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}(?:[\d.]+\s+)([\d.]+)"))
+ #: cpu cycles per buffer
+ cycles_per_buffer: float = field(
+ metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}(?:[\d.]+\s+){2}([\d.]+)")
+ )
+
+
+@dataclass
+class LatencyResults(CryptodevResults):
+ """A parser for latency test output."""
+
+ #: buffer size ran with app
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"Buf(?:.*\n\s+\d+\s+)?(?:fer size:\s+)?(\d+)"),
+ )
+ #: burst size ran with app
+ burst_size: int = field(
+ metadata=TextParser.find_int(rf"Burst(?:.*\n\s+\d+\s+){2}?(?: size:\s+)?(\d+)"),
+ )
+ #: total operations ran
+ total_ops: int = field(metadata=TextParser.find_int(r"total operations:\s+(\d+)"))
+ #: number of bursts
+ num_of_bursts: int = field(metadata=TextParser.find_int(r"Number of bursts:\s+(\d+)"))
+ #: minimum enqueued packets
+ min_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+){2}(\d+)"))
+ #: maximum enqueued packets
+ max_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+){3}(\d+)"))
+ #: average enqueued packets
+ avg_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+)(\d+)"))
+ #: total enqueued packets
+ total_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(\d+)"))
+ #: minimum dequeued packets
+ min_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+){2}(\d+)"))
+ #: maximum dequeued packets
+ max_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+){3}(\d+)"))
+ #: average dequeued packets
+ avg_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+)(\d+)"))
+ #: total dequeued packets
+ total_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(\d+)"))
+ #: minimum cycles per buffer
+ min_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+){3}([\d.]+)"))
+ #: maximum cycles per buffer
+ max_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+){2}([\d.]+)"))
+ #: average cycles per buffer
+ avg_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+)([\d.]+)"))
+ #: total cycles per buffer
+ total_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+([\d.]+)"))
+ #: mimum time in microseconds
+ min_time_us: float = field(
+ metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+){3}([\d.]+)")
+ )
+ #: maximum time in microseconds
+ max_time_us: float = field(
+ metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+){2}([\d.]+)")
+ )
+ #: average time in microseconds
+ avg_time_us: float = field(
+ metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+)([\d.]+)")
+ )
+ #: total time in microseconds
+ total_time_us: float = field(metadata=TextParser.find_float(r"time \[us\]\s+([\d.]+)"))
+
+
+@dataclass
+class PmdCyclecountResults(CryptodevResults):
+ """A parser for PMD cycle count test output."""
+
+ #:
+ lcore_id: int = field(metadata=TextParser.find_int(r"lcore\s+(?:id.*\n\s+)?(\d+)"))
+ #: buffer size used with app run
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"Buf(?:.*\n\s+(?:\d+\s+))?(?:fer size:\s+)?(\d+)"),
+ )
+ #: burst size used with app run
+ burst_size: int = field(
+ metadata=TextParser.find_int(r"Burst(?:.*\n\s+(?:\d+\s+){2})?(?: size:\s+)?(\d+)"),
+ )
+ #: packets enqueued
+ enqueued: int = field(metadata=TextParser.find_int(r"Enqueued.*\n\s+(?:\d+\s+){3}(\d+)"))
+ #: packets dequeued
+ dequeued: int = field(metadata=TextParser.find_int(r"Dequeued.*\n\s+(?:\d+\s+){4}(\d+)"))
+ #: number of enqueue packet retries
+ enqueue_retries: int = field(
+ metadata=TextParser.find_int(r"Enq Retries.*\n\s+(?:\d+\s+){5}(\d+)")
+ )
+ #: number of dequeue packet retries
+ dequeue_retries: int = field(
+ metadata=TextParser.find_int(r"Deq Retries.*\n\s+(?:\d+\s+){6}(\d+)")
+ )
+ #: number of cycles per operation
+ cycles_per_operation: float = field(
+ metadata=TextParser.find_float(r"Cycles/Op.*\n\s+(?:\d+\s+){7}([\d.]+)")
+ )
+ #: number of cycles per enqueue
+ cycles_per_enqueue: float = field(
+ metadata=TextParser.find_float(r"Cycles/Enq.*\n\s+(?:\d+\s+){7}(?:[\d.]+\s+)([\d.]+)")
+ )
+ #: number of cycles per dequeue
+ cycles_per_dequeue: float = field(
+ metadata=TextParser.find_float(r"Cycles/Deq.*\n\s+(?:\d+\s+){7}(?:[\d.]+\s+){2}([\d.]+)"),
+ )
+
+
+@dataclass
+class VerifyResults(CryptodevResults):
+ """A parser for verify test output."""
+
+ #:
+ lcore_id: int = field(metadata=TextParser.find_int(r"lcore\s+(?:id.*\n\s+)?(\d+)"))
+ #: buffer size ran with app
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"Buf(?:.*\n\s+(?:\d+\s+))?(?:fer size:\s+)?(\d+)"),
+ )
+ #: burst size ran with app
+ burst_size: int = field(
+ metadata=TextParser.find_int(r"Burst(?:.*\n\s+(?:\d+\s+){2})?(?: size:\s+)?(\d+)"),
+ )
+ #: number of packets enqueued
+ enqueued: int = field(metadata=TextParser.find_int(r"Enqueued.*\n\s+(?:\d+\s+){3}(\d+)"))
+ #: number of packets dequeued
+ dequeued: int = field(metadata=TextParser.find_int(r"Dequeued.*\n\s+(?:\d+\s+){4}(\d+)"))
+ #: number of packets enqueue failed
+ failed_enqueued: int = field(
+ metadata=TextParser.find_int(r"Failed Enq.*\n\s+(?:\d+\s+){5}(\d+)")
+ )
+ #: number of packets dequeue failed
+ failed_dequeued: int = field(
+ metadata=TextParser.find_int(r"Failed Deq.*\n\s+(?:\d+\s+){6}(\d+)")
+ )
+ #: total number of failed operations
+ failed_ops: int = field(metadata=TextParser.find_int(r"Failed Ops.*\n\s+(?:\d+\s+){7}(\d+)"))
diff --git a/dts/configurations/nodes.example.yaml b/dts/configurations/nodes.example.yaml
index 3f23df11ec..d48020a1d5 100644
--- a/dts/configurations/nodes.example.yaml
+++ b/dts/configurations/nodes.example.yaml
@@ -2,6 +2,14 @@
# Copyright 2022-2023 The DPDK contributors
# Copyright 2023 Arm Limited
+# Optional Fields
+# cryptodevs:
+# Uncomment to run cryptodev tests; add physical cryptodev devices by their BDF
+# crypto_device_type:
+# Uncomment to run cryptodev tests; the device type of the DUT e.g. (crypto_qat, crypto_zuc,
+# crypto_aesni_mb) the complete list of device types can be found here
+# https://doc.dpdk.org/guides-24.07/cryptodevs/overview.html
+
# Define a system under test node, having two network ports physically
# connected to the corresponding ports in TG 1 (the peer node)
- name: "SUT 1"
@@ -20,6 +28,12 @@
hugepages_2mb: # optional; if removed, will use system hugepage configuration
number_of: 256
force_first_numa: false
+ # cryptodevs: # see `Optional Fields`
+ # - name: cryptodev-0
+ # pci: "ffff:ff:ff.9"
+ # os_driver_for_dpdk: vfio-pci # OS driver that DPDK will use for cryptodev VFs
+ # os_driver: _ # This is unused but a value is necessary to run dts
+ # crypto_device_type: # see `Optional Fields`
# Define a Scapy traffic generator node, having two network ports
# physically connected to the corresponding ports in SUT 1 (the peer node).
- name: "TG 1"
diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
index 438a1bdc8f..792290f11f 100644
--- a/dts/framework/config/node.py
+++ b/dts/framework/config/node.py
@@ -70,6 +70,10 @@ class NodeConfiguration(FrozenModel):
hugepages: HugepageConfiguration | None = Field(None, alias="hugepages_2mb")
#: The ports that can be used in testing.
ports: list[PortConfig] = Field(min_length=1)
+ #: The pci info used by crypto devices
+ cryptodevs: list[PortConfig] = Field(default=[], min_length=0)
+ #: The crypto driver used by crypto devices
+ crypto_device_type: str | None = None
@model_validator(mode="after")
def verify_unique_port_names(self) -> Self:
diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py
index 5bc4bd37d9..3c7650474c 100644
--- a/dts/framework/params/types.py
+++ b/dts/framework/params/types.py
@@ -15,6 +15,23 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
from pathlib import PurePath
from typing import TypedDict
+from api.cryptodev.config import (
+ AeadAlgName,
+ AsymOpMode,
+ AuthenticationAlgorithm,
+ AuthenticationOpMode,
+ BurstSizeRange,
+ CipherAlgorithm,
+ DeviceType,
+ EncryptDecryptSwitch,
+ ListWrapper,
+ OperationType,
+ PDCPDomain,
+ RSAPrivKeytype,
+ Silent,
+ TestType,
+ TLSVersion,
+)
from api.testpmd.config import (
AnonMempoolAllocationMode,
EthPeer,
@@ -55,6 +72,54 @@ class EalParamsDict(TypedDict, total=False):
other_eal_param: Params | None
+class CryptoPmdParamsDict(EalParamsDict, total=False):
+ """:class:`TypedDict` equivalent of :class:`~.cryptodev.CryptoPmdParams`."""
+
+ aead_aad_sz: int | None
+ aead_algo: AeadAlgName | None
+ aead_iv_sz: int | None
+ aead_key_sz: int | None
+ aead_op: EncryptDecryptSwitch | None
+ asym_op: AsymOpMode | None
+ auth_algo: AuthenticationAlgorithm | None
+ auth_iv_sz: int | None
+ auth_key_sz: int | None
+ auth_op: AuthenticationOpMode | None
+ buffer_sz: BurstSizeRange | ListWrapper | int | None
+ burst_sz: BurstSizeRange | ListWrapper | int | None
+ cipher_algo: CipherAlgorithm | None
+ cipher_iv_sz: int | None
+ cipher_key_sz: int | None
+ cipher_op: EncryptDecryptSwitch | None
+ csv_friendly: Switch
+ desc_nb: int | None
+ devtype: DeviceType | None
+ digest_sz: int | None
+ docsis_hdr_sz: int | None
+ enable_sdap: Switch
+ imix: int | None
+ low_prio_qp_mask: int | None
+ modex_len: int | None
+ optype: OperationType | None
+ out_of_place: Switch
+ pdcp_sn_sz: int | None
+ pdcp_domain: PDCPDomain | None
+ pdcp_ses_hfn_en: Switch | None
+ pmd_cyclecount_delay_pmd: int | None
+ pool_sz: int | None
+ ptest: TestType
+ rsa_modlen: int | None
+ rsa_priv_keytype: RSAPrivKeytype | None
+ segment_sz: int | None
+ sessionless: Switch
+ shared_session: Switch
+ silent: Silent | None
+ test_file: str | None
+ test_name: str | None
+ tls_version: TLSVersion | None
+ total_ops: int | None
+
+
class TestPmdParamsDict(EalParamsDict, total=False):
""":class:`TypedDict` equivalent of :class:`~.testpmd.TestPmdParams`."""
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index 51b97d4ff6..b94d336d4e 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -46,7 +46,10 @@ def compute_eal_params(
params.prefix = prefix
if params.allowed_ports is None:
- params.allowed_ports = ctx.topology.sut_dpdk_ports
+ if ctx.topology.crypto_vf_ports:
+ params.allowed_ports = [ctx.topology.crypto_vf_ports[0]]
+ else:
+ params.allowed_ports = ctx.topology.sut_dpdk_ports
return params
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index ff0a12c9ce..494eb40046 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -349,6 +349,9 @@ def next(self) -> State | None:
if test_run.config.use_virtual_functions:
test_run.ctx.topology.instantiate_vf_ports()
+ if test_run.ctx.sut_node.cryptodevs:
+ test_run.ctx.topology.instantiate_crypto_ports()
+ test_run.ctx.topology.bind_cryptodevs("dpdk")
test_run.ctx.topology.configure_ports("sut", "dpdk")
if test_run.ctx.func_tg:
@@ -442,6 +445,8 @@ def next(self) -> State | None:
"""Next state."""
if self.test_run.config.use_virtual_functions:
self.test_run.ctx.topology.delete_vf_ports()
+ if self.test_run.ctx.sut_node.cryptodevs:
+ self.test_run.ctx.topology.delete_crypto_vf_ports()
self.test_run.ctx.shell_pool.terminate_current_pool()
if self.test_run.ctx.func_tg and self.test_run.ctx.func_tg.is_setup:
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 2f4aef1460..ee943462c2 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -10,6 +10,7 @@
"""
import json
+import re
from collections.abc import Iterable
from functools import cached_property
from pathlib import PurePath
@@ -247,6 +248,30 @@ def load_vfio(self, pf_port: Port) -> None:
self.send_command("modprobe vfio-pci")
self.refresh_lshw()
+ def create_crypto_vfs(self, pf_port: list[Port]) -> None:
+ """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ for port in pf_port:
+ self.delete_crypto_vfs(port)
+ for port in pf_port:
+ sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
+ )
+ self.send_command(
+ f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
+ )
+
+ self.refresh_lshw()
+
def create_vfs(self, pf_port: Port) -> None:
"""Overrides :meth:`~.os_session.OSSession.create_vfs`.
@@ -263,6 +288,14 @@ def create_vfs(self, pf_port: Port) -> None:
self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
self.refresh_lshw()
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
+ self.send_command(
+ f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
+ privileged=True,
+ )
+ self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
+
def delete_vfs(self, pf_port: Port) -> None:
"""Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
@@ -274,6 +307,18 @@ def delete_vfs(self, pf_port: Port) -> None:
else:
self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ f"readlink {sys_bus_path}/virtfn*",
+ privileged=True,
+ )
+ return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
+ return []
+
def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
"""Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
index 6c3e63a2a1..67a96ef4e5 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -54,6 +54,8 @@ class Node:
arch: Architecture
lcores: list[LogicalCore]
ports: list[Port]
+ cryptodevs: list[Port]
+ crypto_device_type: str | None
_logger: DTSLogger
_other_sessions: list[OSSession]
_node_info: OSSessionInfo | None
@@ -82,6 +84,14 @@ def __init__(self, node_config: NodeConfiguration) -> None:
self._other_sessions = []
self._setup = False
self.ports = [Port(self, port_config) for port_config in self.config.ports]
+ self.cryptodevs = [
+ Port(self, cryptodev_config) for cryptodev_config in self.config.cryptodevs
+ ]
+ self.crypto_device_type = self.config.crypto_device_type
+ if self.cryptodevs:
+ self.main_session.load_vfio(self.cryptodevs[0])
+ elif self.ports and self.ports[0].config.os_driver_for_dpdk == "vfio-pci":
+ self.main_session.load_vfio(self.ports[0])
self._logger.info(f"Created node: {self.name}")
def setup(self) -> None:
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index f3064feeaa..2c267afed1 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -619,6 +619,14 @@ def configure_port_mtu(self, mtu: int, port: Port) -> None:
def load_vfio(self, pf_port: Port) -> None:
"""Load the vfio module according to the device type of the given port."""
+ @abstractmethod
+ def create_crypto_vfs(self, pf_ports: list[Port]) -> None:
+ """Creates virtual functions for each port in 'pf_ports'.
+
+ Checks how many virtual functions each crypto device in 'pf_ports' supports, and creates
+ that number of VFs on the port.
+ """
+
@abstractmethod
def create_vfs(self, pf_port: Port) -> None:
"""Creates virtual functions for `pf_port`.
@@ -634,6 +642,16 @@ def create_vfs(self, pf_port: Port) -> None:
maximum for `pf_port`.
"""
+ @abstractmethod
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Deletes virtual functions for crypto device 'pf_port'.
+
+ Checks how many virtual functions are currently active and removes all if any exist.
+
+ Args:
+ pf_port: The crypto device port to delete virtual functions on.
+ """
+
@abstractmethod
def delete_vfs(self, pf_port: Port) -> None:
"""Deletes virtual functions for `pf_port`.
@@ -660,3 +678,15 @@ def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
A list containing all of the PCI addresses of the VFs on the port. If the port has no
VFs then the list will be empty.
"""
+
+ @abstractmethod
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Find the PCI addresses of all virtual functions (VFs) of a crypto device on `pf_port`.
+
+ Args:
+ pf_port: The port to find the VFs on.
+
+ Returns:
+ A list containing all of the PCI addresses of the VFs on the port. If the port has no
+ VFs then the list will be empty.
+ """
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
index 13b4b58a74..34862c4d2e 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -8,6 +8,7 @@
The link information then implies what type of topology is available.
"""
+import re
from collections import defaultdict
from collections.abc import Iterator
from dataclasses import dataclass
@@ -58,6 +59,8 @@ class Topology:
tg_ports: list[Port]
pf_ports: list[Port]
vf_ports: list[Port]
+ crypto_pf_ports: list[Port]
+ crypto_vf_ports: list[Port]
@classmethod
def from_port_links(cls, port_links: Iterator[PortLink]) -> Self:
@@ -85,7 +88,7 @@ def from_port_links(cls, port_links: Iterator[PortLink]) -> Self:
msg = "More than two links in a topology are not supported."
raise ConfigurationError(msg)
- return cls(type, sut_ports, tg_ports, [], [])
+ return cls(type, sut_ports, tg_ports, [], [], [], [])
def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node, list[Port]]:
"""Retrieve node and its ports for the current topology.
@@ -105,6 +108,32 @@ def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node,
msg = f"Invalid node `{node_identifier}` given."
raise InternalError(msg)
+ def get_crypto_vfs(self, num_vfs: int) -> list[Port]:
+ """Retrieve virtual functions from active crypto vfs.
+
+ Args:
+ num_vfs: Number of virtual functions to return.
+
+ Returns:
+ List containing the requested number of VFs
+
+ Raises:
+ ValueError: If there are not `num_vfs` available VFs
+ """
+ return_list: list[Port] = []
+ device = 1
+ if len(self.crypto_vf_ports) < num_vfs or num_vfs < 0:
+ raise ValueError
+ for i in range(num_vfs):
+ if i % 8 == 0 and i != 0:
+ device += 1
+ for port in filter(
+ lambda x: re.search(rf"0000:\d+:0{device}.{i}", x.pci), self.crypto_vf_ports
+ ):
+ if len(return_list) < num_vfs:
+ return_list.append(port)
+ return return_list
+
def setup(self) -> None:
"""Setup topology ports.
@@ -145,6 +174,32 @@ def _setup_ports(self, node_identifier: NodeIdentifier) -> None:
f"for port {port.name} in node {node.name}."
)
+ def instantiate_crypto_ports(self) -> None:
+ """Create max number of virtual functions allowed on the SUT node from physical BDFs.
+
+ Raises:
+ InternalError: If crypto virtual functions could not be created on a port.
+ """
+ from framework.context import get_ctx
+
+ ctx = get_ctx()
+ for port in ctx.sut_node.cryptodevs:
+ self.crypto_pf_ports.append(port)
+
+ ctx.sut_node.main_session.create_crypto_vfs(self.crypto_pf_ports)
+ for port in self.crypto_pf_ports:
+ addr_list = ctx.sut_node.main_session.get_pci_addr_of_crypto_vfs(port)
+ if addr_list == []:
+ raise InternalError(f"Failed to create crypto virtual function on port {port.pci}")
+ for addr in addr_list:
+ vf_config = PortConfig(
+ name=f"{port.name}-crypto-vf-{addr}",
+ pci=addr,
+ os_driver_for_dpdk=port.config.os_driver_for_dpdk,
+ os_driver=port.config.os_driver,
+ )
+ self.crypto_vf_ports.append(Port(node=port.node, config=vf_config))
+
def instantiate_vf_ports(self) -> None:
"""Create, setup, and add virtual functions to the list of ports on the SUT node.
@@ -189,6 +244,25 @@ def delete_vf_ports(self) -> None:
self.sut_ports.clear()
self.sut_ports.extend(self.pf_ports)
+ def delete_crypto_vf_ports(self) -> None:
+ """Delete crypto virtual functions from the SUT node during test run teardown."""
+ from framework.context import get_ctx
+
+ ctx = get_ctx()
+
+ for port in self.crypto_pf_ports:
+ ctx.sut_node.main_session.delete_crypto_vfs(port)
+
+ def bind_cryptodevs(self, driver: DriverKind):
+ """Bind the crypto device virtual functions on the sut to the specified driver.
+
+ Args:
+ driver: The driver to bind the crypto functions
+ """
+ from framework.context import get_ctx
+
+ self._bind_ports_to_drivers(get_ctx().sut_node, self.crypto_vf_ports, driver)
+
def configure_ports(
self, node_identifier: NodeIdentifier, drivers: DriverKind | tuple[DriverKind, ...]
) -> None:
@@ -227,7 +301,7 @@ def _bind_ports_to_drivers(
for port_id, port in enumerate(ports):
driver_kind = drivers[port_id] if isinstance(drivers, tuple) else drivers
desired_driver = port.driver_by_kind(driver_kind)
- if port.current_driver != desired_driver:
+ if port in self.crypto_vf_ports or port.current_driver != desired_driver:
driver_to_ports[desired_driver].append(port)
for driver_name, ports in driver_to_ports.items():
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 4/5] dts: add cryptodev throughput test suite
2026-03-02 18:54 ` [PATCH v4 0/5] dts: add cryptodev testing support Andrew Bailey
` (2 preceding siblings ...)
2026-03-02 18:54 ` [PATCH v4 3/5] dts: add cryptodev package to DTS Andrew Bailey
@ 2026-03-02 18:54 ` Andrew Bailey
2026-03-05 19:26 ` Patrick Robb
2026-03-05 20:20 ` Patrick Robb
2026-03-02 18:54 ` [PATCH v4 5/5] dts: add crypto test decorator Andrew Bailey
4 siblings, 2 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-03-02 18:54 UTC (permalink / raw)
To: probb
Cc: luca.vizzarro, dmarx, dev, gakhil, kai.ji, dharmikjayesh,
david.marchand, Andrew Bailey
Add cryptodev performance test suite. This test covers throughput
performance metrics of supplied crypto devices using the
dpdk-test-crypto-perf application. The results of this application will
be compared against user supplied performance metrics and fail if signs
of regression are apparent.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
.../tests.TestSuite_cryptodev_throughput.rst | 8 +
dts/configurations/tests_config.example.yaml | 8 +
dts/tests/TestSuite_cryptodev_throughput.py | 693 ++++++++++++++++++
3 files changed, 709 insertions(+)
create mode 100644 doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
create mode 100644 dts/tests/TestSuite_cryptodev_throughput.py
diff --git a/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst b/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
new file mode 100644
index 0000000000..5df5e7547e
--- /dev/null
+++ b/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+cryptodev_throughput Test Suite
+===============================
+
+.. automodule:: tests.TestSuite_cryptodev_throughput
+ :members:
+ :show-inheritance:
diff --git a/dts/configurations/tests_config.example.yaml b/dts/configurations/tests_config.example.yaml
index 64fa630aa0..c9beff9c99 100644
--- a/dts/configurations/tests_config.example.yaml
+++ b/dts/configurations/tests_config.example.yaml
@@ -15,3 +15,11 @@ hello_world:
# num_descriptors: 1024
# expected_mpps: 1.0
# delta_tolerance: 0.05
+# cryptodev_throughput:
+# delta_tolerance: 0.05
+# throughput_test_parameters:
+# snow3g_uea2_snow3g_uia2: # Add the name of the test suite you would like to run
+# - buff_size: 64
+# gbps: 2.0 # Set gigabits per second according to your devices throughput for this given buffer size
+# - buff_size: 512
+# gbps: 20.0
\ No newline at end of file
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
new file mode 100644
index 0000000000..d32879b9aa
--- /dev/null
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -0,0 +1,693 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""DPDK cryptodev performance test suite.
+
+The main goal of this test suite is to utilize the dpdk-test-cryptodev application to gather
+performance metrics for various cryptographic operations supported by DPDK cryptodev-pmd.
+It will then compare the results against predefined baseline given in the test_config file to
+ensure performance standards are met.
+"""
+
+from api.capabilities import (
+ LinkTopology,
+ requires_link_topology,
+)
+from api.cryptodev import Cryptodev
+from api.cryptodev.config import (
+ AeadAlgName,
+ AuthenticationAlgorithm,
+ AuthenticationOpMode,
+ CipherAlgorithm,
+ DeviceType,
+ EncryptDecryptSwitch,
+ ListWrapper,
+ OperationType,
+ TestType,
+ get_device_from_str,
+)
+from api.cryptodev.types import (
+ CryptodevResults,
+)
+from api.test import verify
+from framework.context import get_ctx
+from framework.exception import SkippedTestException
+from framework.test_suite import BaseConfig, TestSuite, func_test
+from framework.testbed_model.virtual_device import VirtualDevice
+
+config_list: list[dict[str, int | float | str]] = [
+ {"buff_size": 64, "Gbps": 1.00},
+ {"buff_size": 512, "Gbps": 1.00},
+ {"buff_size": 2048, "Gbps": 1.00},
+]
+
+TOTAL_OPS = 10_000_000
+
+
+class Config(BaseConfig):
+ """Performance test metrics.
+
+ Attributes:
+ delta_tolerance: The allowed tolerance below a given baseline.
+ throughput_test_parameters: The test parameters to use in the test suite.
+ """
+
+ delta_tolerance: float = 0.05
+
+ throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = {
+ "aes_cbc": config_list,
+ "aes_cbc_sha1": config_list,
+ "aes_cbc_sha2": config_list,
+ "aes_cbc_sha2_digest_16": config_list,
+ "aead_aes_gcm": config_list,
+ "aes_docsisbpi": config_list,
+ "sha1_hmac": config_list,
+ "snow3g_uea2_snow3g_uia2": config_list,
+ "zuc_eea3_zuc_eia3": config_list,
+ "kasumi_f8_kasumi_f9": config_list,
+ "open_ssl_vdev": config_list,
+ "aesni_mb_vdev": config_list,
+ "aesni_gcm_vdev": config_list,
+ "kasumi_vdev": config_list,
+ "zuc_vdev": config_list,
+ "snow3g_vdev": config_list,
+ }
+
+
+@requires_link_topology(LinkTopology.NO_LINK)
+class TestCryptodevThroughput(TestSuite):
+ """DPDK Crypto Device Testing Suite."""
+
+ config: Config
+
+ def set_up_suite(self) -> None:
+ """Set up the test suite."""
+ self.throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = (
+ self.config.throughput_test_parameters
+ )
+ self.delta_tolerance: float = self.config.delta_tolerance
+ self.driver: DeviceType | None = get_device_from_str(
+ str(get_ctx().sut_node.crypto_device_type)
+ )
+ self.buffer_sizes = {}
+
+ for k, v in self.throughput_test_parameters.items():
+ self.buffer_sizes[k] = ListWrapper([int(run["buff_size"]) for run in v])
+
+ def _print_stats(self, test_vals: list[dict[str, int | float | str]]) -> None:
+ element_len = len("Delta Tolerance")
+ border_len = (element_len + 1) * (len(test_vals[0]))
+
+ 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}")
+
+ for test_val in test_vals:
+ for k, v in test_val.items():
+ print(f"|{v:<{element_len}}", end="")
+ print(f"|\n{'='*border_len}")
+
+ def _verify_throughput(
+ self,
+ results: list[CryptodevResults],
+ key: str,
+ ) -> list[dict[str, int | float | str]]:
+ result_list: list[dict[str, int | float | str]] = []
+
+ for result in results:
+ # get the corresponding baseline for the current buffer size
+ parameters: dict[str, int | float | str] = list(
+ filter(
+ lambda x: x["buff_size"] == result.buffer_size,
+ self.throughput_test_parameters[key],
+ )
+ )[0]
+ test_result = True
+ expected_gbps = parameters["Gbps"]
+ measured_delta = abs(
+ round((getattr(result, "gbps") - expected_gbps) / expected_gbps, 5)
+ )
+
+ # result did not meet the given Gbps parameter, check if within delta.
+ if getattr(result, "gbps") < expected_gbps:
+ if self.delta_tolerance < measured_delta:
+ test_result = False
+ result_list.append(
+ {
+ "Buffer Size": parameters["buff_size"],
+ "Gbps delta": measured_delta,
+ "delta tolerance": self.delta_tolerance,
+ "Gbps": getattr(result, "gbps"),
+ "Gbps target": expected_gbps,
+ "passed": "PASS" if test_result else "FAIL",
+ }
+ )
+ return result_list
+
+ @func_test
+ def aes_cbc(self) -> None:
+ """aes_cbc test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_only,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aes_cbc"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ def aes_cbc_sha1(self) -> None:
+ """aes_cbc_sha1 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc_sha1" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha1_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ auth_iv_sz=20,
+ digest_sz=12,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aes_cbc_sha1"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc_sha1")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ def aes_cbc_sha2(self) -> None:
+ """aes_cbc_sha2 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc_sha2" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha2_256_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ digest_sz=32,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aes_cbc_sha2"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc_sha2")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def aes_cbc_sha2_digest_16(self) -> None:
+ """aes_cbc_sha2_digest_16 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc_sha2_digest_16" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha2_256_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ digest_sz=16,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aes_cbc_sha2_digest_16"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc_sha2_digest_16")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def aead_aes_gcm(self) -> None:
+ """aead_aes_gcm test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aead_aes_gcm" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.aead,
+ aead_algo=AeadAlgName.aes_gcm,
+ aead_op=EncryptDecryptSwitch.encrypt,
+ aead_key_sz=16,
+ aead_iv_sz=12,
+ aead_aad_sz=16,
+ digest_sz=16,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aead_aes_gcm"],
+ )
+ results = self._verify_throughput(app.run_app(), "aead_aes_gcm")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def aes_docsisbpi(self) -> None:
+ """aes_docsisbpi test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_docsisbpi" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_only,
+ cipher_algo=CipherAlgorithm.aes_docsisbpi,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=32,
+ cipher_iv_sz=16,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aes_docsisbpi"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_docsisbpi")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @crypto_test
+ def sha1_hmac(self) -> None:
+ """sha1_hmac test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "sha1_hmac" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.auth_only,
+ auth_algo=AuthenticationAlgorithm.sha1_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ auth_iv_sz=16,
+ digest_sz=12,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["sha1_hmac"],
+ )
+ results = self._verify_throughput(app.run_app(), "sha1_hmac")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def snow3g_uea2_snow3g_uia2(self) -> None:
+ """snow3g_uea2_snow3g_uia2 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "snow3g_uea2_snow3g_uia2" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.snow3g_uea2,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.snow3g_uia2,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["snow3g_uea2_snow3g_uia2"],
+ )
+ results = self._verify_throughput(app.run_app(), "snow3g_uea2_snow3g_uia2")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def zuc_eea3_zuc_eia3(self) -> None:
+ """zuc_eea3_zuc_eia3 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "zuc_eea3_zuc_eia3" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.zuc_eea3,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.zuc_eia3,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["zuc_eea3_zuc_eia3"],
+ )
+ results = self._verify_throughput(app.run_app(), "zuc_eea3_zuc_eia3")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def kasumi_f8_kasumi_f9(self) -> None:
+ """kasumi_f8 kasumi_f9 test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "kasumi_f8_kasumi_f9" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.driver,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.kasumi_f8,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=8,
+ auth_algo=AuthenticationAlgorithm.kasumi_f9,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["kasumi_f8_kasumi_f9"],
+ )
+ results = self._verify_throughput(app.run_app(), "kasumi_f8_kasumi_f9")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ # BEGIN VDEV TESTS
+
+
+ def aesni_mb_vdev(self) -> None:
+ """aesni_mb virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_aesni_mb and supplied buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aesni_mb_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_aesni_mb0")],
+ devtype=DeviceType.crypto_aesni_mb,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=32,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha1_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aesni_mb_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "aesni_mb_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def aesni_gcm_vdev(self):
+ """aesni_gcm virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_aesni_gcm and supplied buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aesni_gcm_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_aesni_gcm0")],
+ devtype=DeviceType.crypto_aesni_gcm,
+ optype=OperationType.aead,
+ aead_algo=AeadAlgName.aes_gcm,
+ aead_op=EncryptDecryptSwitch.encrypt,
+ aead_key_sz=16,
+ aead_iv_sz=12,
+ aead_aad_sz=16,
+ digest_sz=16,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aesni_gcm_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "aesni_gcm_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def kasumi_vdev(self) -> None:
+ """Kasmumi virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_kasumi and supplied buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "kasumi_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ vdevs=[VirtualDevice("crypto_kasumi0")],
+ ptest=TestType.throughput,
+ devtype=DeviceType.crypto_kasumi,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.kasumi_f8,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=8,
+ auth_algo=AuthenticationAlgorithm.kasumi_f9,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["kasumi_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "kasumi_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def snow3g_vdev(self) -> None:
+ """snow3g virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_snow3g and supplied buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "snow3g_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_snow3g0")],
+ devtype=DeviceType.crypto_snow3g,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.snow3g_uea2,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.snow3g_uia2,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["snow3g_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "snow3g_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def zuc_vdev(self) -> None:
+ """Zuc virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_zuc and supplied buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "zuc_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_zuc0")],
+ devtype=DeviceType.crypto_zuc,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.zuc_eea3,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.zuc_eia3,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["zuc_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "zuc_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def open_ssl_vdev(self) -> None:
+ """open_ssl virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with provided driver and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "open_ssl_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_openssl0")],
+ devtype=DeviceType.crypto_openssl,
+ optype=OperationType.aead,
+ aead_algo=AeadAlgName.aes_gcm,
+ aead_op=EncryptDecryptSwitch.encrypt,
+ aead_key_sz=16,
+ aead_iv_sz=16,
+ aead_aad_sz=16,
+ digest_sz=16,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["open_ssl_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(), "open_ssl_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(
+ result["passed"] == "PASS",
+ "Gbps fell below delta tolerance",
+ )
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 5/5] dts: add crypto test decorator
2026-03-02 18:54 ` [PATCH v4 0/5] dts: add cryptodev testing support Andrew Bailey
` (3 preceding siblings ...)
2026-03-02 18:54 ` [PATCH v4 4/5] dts: add cryptodev throughput test suite Andrew Bailey
@ 2026-03-02 18:54 ` Andrew Bailey
4 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-03-02 18:54 UTC (permalink / raw)
To: probb
Cc: luca.vizzarro, dmarx, dev, gakhil, kai.ji, dharmikjayesh,
david.marchand, Andrew Bailey
Currently, a test case is decorated to signify whether to use Scapy or
TREX. This change allows test suites that do not use a traffic generator
to avoid the overhead of initializing either one.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/configurations/test_run.example.yaml | 1 +
dts/framework/config/test_run.py | 3 +++
dts/framework/test_run.py | 2 +-
dts/framework/test_suite.py | 6 +++++
dts/tests/TestSuite_cryptodev_throughput.py | 30 +++++++++++----------
5 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/dts/configurations/test_run.example.yaml b/dts/configurations/test_run.example.yaml
index c8035fccf0..3eaffc2aa4 100644
--- a/dts/configurations/test_run.example.yaml
+++ b/dts/configurations/test_run.example.yaml
@@ -31,6 +31,7 @@ func_traffic_generator:
# config: "/opt/trex_config/trex_config.yaml" # Additional configuration files. (Leave blank if not required)
perf: false # disable performance testing
func: true # enable functional testing
+crypto: false # disable cryptographic testing
use_virtual_functions: false # use virtual functions (VFs) instead of physical functions
skip_smoke_tests: true # optional
# by removing the `test_suites` field, this test run will run every test suite available
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 6c292a3675..39f2c7cdf6 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -481,6 +481,8 @@ class TestRunConfiguration(FrozenModel):
perf: bool
#: Whether to run functional tests.
func: bool
+ #: Whether to run cryptography tests.
+ crypto: bool
#: Whether to run the testing with virtual functions instead of physical functions
use_virtual_functions: bool
#: Whether to skip smoke tests.
@@ -522,6 +524,7 @@ def filter_tests(
for tt in t.test_cases
if (tt.test_type is TestCaseType.FUNCTIONAL and self.func)
or (tt.test_type is TestCaseType.PERFORMANCE and self.perf)
+ or (tt.test_type is TestCaseType.CRYPTO and self.crypto)
),
)
for t in test_suites
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 494eb40046..94dc6023a7 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -349,7 +349,7 @@ def next(self) -> State | None:
if test_run.config.use_virtual_functions:
test_run.ctx.topology.instantiate_vf_ports()
- if test_run.ctx.sut_node.cryptodevs:
+ if test_run.ctx.sut_node.cryptodevs and test_run.config.crypto:
test_run.ctx.topology.instantiate_crypto_ports()
test_run.ctx.topology.bind_cryptodevs("dpdk")
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 9c57e343ac..e86096cefe 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -174,6 +174,8 @@ def filter_test_cases(
perf_test_cases.add(test_case)
case TestCaseType.FUNCTIONAL:
func_test_cases.add(test_case)
+ case TestCaseType.CRYPTO:
+ pass
if test_case_sublist_copy:
raise ConfigurationError(
@@ -279,6 +281,8 @@ class TestCaseType(Enum):
FUNCTIONAL = auto()
#:
PERFORMANCE = auto()
+ #:
+ CRYPTO = auto()
class TestCase(TestProtocol, Protocol[TestSuiteMethodType]):
@@ -331,6 +335,8 @@ def _decorator(func: TestSuiteMethodType) -> type[TestCase]:
func_test: Callable = TestCase.make_decorator(TestCaseType.FUNCTIONAL)
#: The decorator for performance test cases.
perf_test: Callable = TestCase.make_decorator(TestCaseType.PERFORMANCE)
+#: The decorator for cryptography test cases.
+crypto_test: Callable = TestCase.make_decorator(TestCaseType.CRYPTO)
@dataclass
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index d32879b9aa..1680a6b328 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -32,7 +32,7 @@
from api.test import verify
from framework.context import get_ctx
from framework.exception import SkippedTestException
-from framework.test_suite import BaseConfig, TestSuite, func_test
+from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
config_list: list[dict[str, int | float | str]] = [
@@ -145,7 +145,7 @@ def _verify_throughput(
)
return result_list
- @func_test
+ @crypto_test
def aes_cbc(self) -> None:
"""aes_cbc test.
@@ -175,6 +175,7 @@ def aes_cbc(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+ @crypto_test
def aes_cbc_sha1(self) -> None:
"""aes_cbc_sha1 test.
@@ -209,6 +210,7 @@ def aes_cbc_sha1(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+ @crypto_test
def aes_cbc_sha2(self) -> None:
"""aes_cbc_sha2 test.
@@ -242,7 +244,7 @@ def aes_cbc_sha2(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aes_cbc_sha2_digest_16(self) -> None:
"""aes_cbc_sha2_digest_16 test.
@@ -276,7 +278,7 @@ def aes_cbc_sha2_digest_16(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aead_aes_gcm(self) -> None:
"""aead_aes_gcm test.
@@ -308,7 +310,7 @@ def aead_aes_gcm(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aes_docsisbpi(self) -> None:
"""aes_docsisbpi test.
@@ -369,7 +371,7 @@ def sha1_hmac(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def snow3g_uea2_snow3g_uia2(self) -> None:
"""snow3g_uea2_snow3g_uia2 test.
@@ -404,7 +406,7 @@ def snow3g_uea2_snow3g_uia2(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def zuc_eea3_zuc_eia3(self) -> None:
"""zuc_eea3_zuc_eia3 test.
@@ -439,7 +441,7 @@ def zuc_eea3_zuc_eia3(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def kasumi_f8_kasumi_f9(self) -> None:
"""kasumi_f8 kasumi_f9 test.
@@ -476,7 +478,7 @@ def kasumi_f8_kasumi_f9(self) -> None:
# BEGIN VDEV TESTS
-
+ @crypto_test
def aesni_mb_vdev(self) -> None:
"""aesni_mb virtual device test.
@@ -513,7 +515,7 @@ def aesni_mb_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aesni_gcm_vdev(self):
"""aesni_gcm virtual device test.
@@ -546,7 +548,7 @@ def aesni_gcm_vdev(self):
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def kasumi_vdev(self) -> None:
"""Kasmumi virtual device test.
@@ -582,7 +584,7 @@ def kasumi_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def snow3g_vdev(self) -> None:
"""snow3g virtual device test.
@@ -619,7 +621,7 @@ def snow3g_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def zuc_vdev(self) -> None:
"""Zuc virtual device test.
@@ -656,7 +658,7 @@ def zuc_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def open_ssl_vdev(self) -> None:
"""open_ssl virtual device test.
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [PATCH v4 4/5] dts: add cryptodev throughput test suite
2026-03-02 18:54 ` [PATCH v4 4/5] dts: add cryptodev throughput test suite Andrew Bailey
@ 2026-03-05 19:26 ` Patrick Robb
2026-03-05 20:20 ` Patrick Robb
1 sibling, 0 replies; 41+ messages in thread
From: Patrick Robb @ 2026-03-05 19:26 UTC (permalink / raw)
To: Andrew Bailey
Cc: luca.vizzarro, dmarx, dev, gakhil, kai.ji, dharmikjayesh,
david.marchand
On Mon, Mar 2, 2026 at 1:55 PM Andrew Bailey <abailey@iol.unh.edu> wrote:
>
>
> +config_list: list[dict[str, int | float | str]] = [
> + {"buff_size": 64, "Gbps": 1.00},
> + {"buff_size": 512, "Gbps": 1.00},
> + {"buff_size": 2048, "Gbps": 1.00},
Either Gbps needs to become gbps or the gbps field from
tests_config.example.yaml needs to be updated.
> +]
> +
> +TOTAL_OPS = 10_000_000
> +
> +
> +class Config(BaseConfig):
> + """Performance test metrics.
> +
> + Attributes:
> + delta_tolerance: The allowed tolerance below a given baseline.
> + throughput_test_parameters: The test parameters to use in the test suite.
> + """
> +
> + delta_tolerance: float = 0.05
> +
> + throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = {
> + "aes_cbc": config_list,
> + "aes_cbc_sha1": config_list,
> + "aes_cbc_sha2": config_list,
> + "aes_cbc_sha2_digest_16": config_list,
> + "aead_aes_gcm": config_list,
> + "aes_docsisbpi": config_list,
> + "sha1_hmac": config_list,
> + "snow3g_uea2_snow3g_uia2": config_list,
> + "zuc_eea3_zuc_eia3": config_list,
> + "kasumi_f8_kasumi_f9": config_list,
> + "open_ssl_vdev": config_list,
> + "aesni_mb_vdev": config_list,
> + "aesni_gcm_vdev": config_list,
> + "kasumi_vdev": config_list,
> + "zuc_vdev": config_list,
> + "snow3g_vdev": config_list,
> + }
> +
> +
> +@requires_link_topology(LinkTopology.NO_LINK)
> +class TestCryptodevThroughput(TestSuite):
> + """DPDK Crypto Device Testing Suite."""
> +
> + config: Config
> +
> + def set_up_suite(self) -> None:
> + """Set up the test suite."""
> + self.throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = (
> + self.config.throughput_test_parameters
> + )
> + self.delta_tolerance: float = self.config.delta_tolerance
> + self.driver: DeviceType | None = get_device_from_str(
> + str(get_ctx().sut_node.crypto_device_type)
> + )
> + self.buffer_sizes = {}
> +
> + for k, v in self.throughput_test_parameters.items():
> + self.buffer_sizes[k] = ListWrapper([int(run["buff_size"]) for run in v])
> +
> + def _print_stats(self, test_vals: list[dict[str, int | float | str]]) -> None:
> + element_len = len("Delta Tolerance")
> + border_len = (element_len + 1) * (len(test_vals[0]))
> +
> + 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}")
> +
> + for test_val in test_vals:
> + for k, v in test_val.items():
> + print(f"|{v:<{element_len}}", end="")
> + print(f"|\n{'='*border_len}")
> +
> + def _verify_throughput(
> + self,
> + results: list[CryptodevResults],
> + key: str,
> + ) -> list[dict[str, int | float | str]]:
> + result_list: list[dict[str, int | float | str]] = []
> +
> + for result in results:
> + # get the corresponding baseline for the current buffer size
> + parameters: dict[str, int | float | str] = list(
> + filter(
> + lambda x: x["buff_size"] == result.buffer_size,
> + self.throughput_test_parameters[key],
> + )
> + )[0]
> + test_result = True
> + expected_gbps = parameters["Gbps"]
Gbps <-> gbps misalignment with tests_config.example.yaml
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 4/5] dts: add cryptodev throughput test suite
2026-03-02 18:54 ` [PATCH v4 4/5] dts: add cryptodev throughput test suite Andrew Bailey
2026-03-05 19:26 ` Patrick Robb
@ 2026-03-05 20:20 ` Patrick Robb
1 sibling, 0 replies; 41+ messages in thread
From: Patrick Robb @ 2026-03-05 20:20 UTC (permalink / raw)
To: Andrew Bailey
Cc: luca.vizzarro, dmarx, dev, gakhil, kai.ji, dharmikjayesh,
david.marchand
On Mon, Mar 2, 2026 at 1:55 PM Andrew Bailey <abailey@iol.unh.edu> wrote:
>
> +
> +@requires_link_topology(LinkTopology.NO_LINK)
> +class TestCryptodevThroughput(TestSuite):
> + """DPDK Crypto Device Testing Suite."""
> +
> + config: Config
> +
> + def set_up_suite(self) -> None:
> + """Set up the test suite."""
> + self.throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = (
> + self.config.throughput_test_parameters
> + )
> + self.delta_tolerance: float = self.config.delta_tolerance
> + self.driver: DeviceType | None = get_device_from_str(
does it make sense to change this to self.device_type to keep the
naming aligned?
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v5 0/5] dts: add cryptodev testing support
2026-02-10 19:34 [PATCH v2 0/5] dts: add cryptodev testing support Andrew Bailey
` (6 preceding siblings ...)
2026-03-02 18:54 ` [PATCH v4 0/5] dts: add cryptodev testing support Andrew Bailey
@ 2026-03-06 16:40 ` Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 1/5] dts: add find float method to text parser Andrew Bailey
` (4 more replies)
7 siblings, 5 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-03-06 16:40 UTC (permalink / raw)
To: probb
Cc: dev, luca.vizzarro, dmarx, kai.ji, dharmikjayesh.thakkar,
david.marchand, gakhil, Andrew Bailey
This patch series adds support to use the dpdk-test-crypto-perf
application. This application utilizes crypto devices that must be
listed in the format of a port under the `cryptodevs:` label. Along
with this, the application does not utilize a traffic generator and
therefore a new test suite decorator has been made to signify this.
Crypto tests must be enabled in test_run.yaml as `crypto: true`.
It is not supported to enable crypto testing along with functional
or performance testing. Specific capabilities of a crypto device
cannot be gathered at setup and therefore, non-relevant tests skip at
runtime. Finally, the application must be run with virtual functions
and the process of creating and binding them has been automated within
this series.
---
v2:
* Updated example yaml files for test_run and tests_config.
* Expanded test suite coverage.
* Resolved git problems where patches were removing code added by the
previous commit.
* Updated test suite to only run tests that are configured in the
tests_config yaml.
v3:
* Fixed some tests that were being run with incorrect arguments.
* Updated crypto-device pci paths for driver binding and VF creation.
* Updated example yaml files for tests_config and nodes files.
v4:
* Updated how virtual functions are allocated to a run of the cryptodev
application.
* Updated config files to be more readable.
* Reduced total commands sent to SUT node for crypto VF creation.
* Fixed various typos and docstring issues.
* Updated a vdev test case to use the correct devtype.
v5:
* Updated test suites to use zero VFs when running a vdev test.
* Updated variable naming and docstrings to be consistent.
* Resolved rebase conflicts with main.
Andrew Bailey (5):
dts: add find float method to text parser
dts: automate VFIO-PCI modprobe in node setup
dts: add cryptodev package to DTS
dts: add cryptodev throughput test suite
dts: add crypto test decorator
.../tests.TestSuite_cryptodev_throughput.rst | 8 +
dts/api/cryptodev/__init__.py | 137 ++++
dts/api/cryptodev/config.py | 508 +++++++++++++
dts/api/cryptodev/types.py | 185 +++++
dts/configurations/nodes.example.yaml | 12 +
dts/configurations/test_run.example.yaml | 1 +
dts/configurations/tests_config.example.yaml | 8 +
dts/framework/config/node.py | 4 +
dts/framework/config/test_run.py | 3 +
dts/framework/params/types.py | 65 ++
dts/framework/parser.py | 28 +
dts/framework/remote_session/dpdk_shell.py | 5 +-
dts/framework/test_run.py | 5 +
dts/framework/test_suite.py | 6 +
dts/framework/testbed_model/linux_session.py | 69 ++
dts/framework/testbed_model/node.py | 10 +
dts/framework/testbed_model/os_session.py | 34 +
dts/framework/testbed_model/topology.py | 78 +-
dts/tests/TestSuite_cryptodev_throughput.py | 695 ++++++++++++++++++
19 files changed, 1858 insertions(+), 3 deletions(-)
create mode 100644 doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
create mode 100644 dts/api/cryptodev/__init__.py
create mode 100644 dts/api/cryptodev/config.py
create mode 100644 dts/api/cryptodev/types.py
create mode 100644 dts/tests/TestSuite_cryptodev_throughput.py
--
2.50.1
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v5 1/5] dts: add find float method to text parser
2026-03-06 16:40 ` [PATCH v5 0/5] dts: add cryptodev testing support Andrew Bailey
@ 2026-03-06 16:40 ` Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 2/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
` (3 subsequent siblings)
4 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-03-06 16:40 UTC (permalink / raw)
To: probb
Cc: dev, luca.vizzarro, dmarx, kai.ji, dharmikjayesh.thakkar,
david.marchand, gakhil, Andrew Bailey
Currently, there is no way to gather floats from text using the parser.
Adding a new method to find floats will allow testsuites to utilize
valuable float values that are output from applications.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/framework/parser.py | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 4170cdb1dd..3075c36857 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -220,6 +220,34 @@ def find_int(
return TextParser.wrap(TextParser.find(pattern), partial(int, base=int_base))
+ @staticmethod
+ def find_float(
+ pattern: str | re.Pattern[str],
+ flags: re.RegexFlag = re.RegexFlag(0),
+ ) -> ParserFn:
+ """Makes a parser function that converts the match of :meth:`~find` to float.
+
+ This function is compatible only with a pattern containing one capturing group.
+
+ Args:
+ pattern: The regular expression pattern.
+ flags: The regular expression flags. Ignored if the given pattern is already compiled.
+
+ Raises:
+ InternalError: If the pattern does not have exactly one capturing group.
+
+ Returns:
+ ParserFn: A dictionary for the `dataclasses.field` metadata argument containing the
+ :meth:`~find` parser function wrapped by the float built-in.
+ """
+ if isinstance(pattern, str):
+ pattern = re.compile(pattern, flags)
+
+ if pattern.groups != 1:
+ raise InternalError("only one capturing group is allowed with this parser function")
+
+ return TextParser.wrap(TextParser.find(pattern), partial(float))
+
"""============ END PARSER FUNCTIONS ============"""
@classmethod
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v5 2/5] dts: automate VFIO-PCI modprobe in node setup
2026-03-06 16:40 ` [PATCH v5 0/5] dts: add cryptodev testing support Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 1/5] dts: add find float method to text parser Andrew Bailey
@ 2026-03-06 16:40 ` Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 3/5] dts: add cryptodev package to DTS Andrew Bailey
` (2 subsequent siblings)
4 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-03-06 16:40 UTC (permalink / raw)
To: probb
Cc: dev, luca.vizzarro, dmarx, kai.ji, dharmikjayesh.thakkar,
david.marchand, gakhil, Andrew Bailey
Currently, users must modprobe VFIO-PCI before running DTS when using a
non-mellanox NIC. This patch automates the process on test run start up.
In addition, if the SUT is expected to use a subset of a few QAT devices,
VFIO-PCI must be loaded in a separate manner. This patch adds this
distinction.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/framework/testbed_model/linux_session.py | 24 ++++++++++++++++++++
dts/framework/testbed_model/os_session.py | 4 ++++
2 files changed, 28 insertions(+)
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 711d4d97c3..2f4aef1460 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -223,6 +223,30 @@ def devbind_script_path(self) -> PurePath:
"""
raise InternalError("Accessed devbind script path before setup.")
+ def load_vfio(self, pf_port: Port) -> None:
+ """Overrides :meth:`~os_session.OSSession,load_vfio`."""
+ cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
+ device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
+ if device and device.group(1) in ["37c8", "0435", "19e2"]:
+ self.send_command(
+ "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe -r vfio_virqfd; modprobe -r vfio",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
+ )
+ self.send_command(
+ "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
+ privileged=True,
+ )
+ else:
+ self.send_command("modprobe vfio-pci")
+ self.refresh_lshw()
+
def create_vfs(self, pf_port: Port) -> None:
"""Overrides :meth:`~.os_session.OSSession.create_vfs`.
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index b94c3e527b..f3064feeaa 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -615,6 +615,10 @@ def configure_port_mtu(self, mtu: int, port: Port) -> None:
port: Port to set `mtu` on.
"""
+ @abstractmethod
+ def load_vfio(self, pf_port: Port) -> None:
+ """Load the vfio module according to the device type of the given port."""
+
@abstractmethod
def create_vfs(self, pf_port: Port) -> None:
"""Creates virtual functions for `pf_port`.
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v5 3/5] dts: add cryptodev package to DTS
2026-03-06 16:40 ` [PATCH v5 0/5] dts: add cryptodev testing support Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 1/5] dts: add find float method to text parser Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 2/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
@ 2026-03-06 16:40 ` Andrew Bailey
2026-03-09 1:26 ` Patrick Robb
2026-03-06 16:40 ` [PATCH v5 4/5] dts: add cryptodev throughput test suite Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 5/5] dts: add crypto test decorator Andrew Bailey
4 siblings, 1 reply; 41+ messages in thread
From: Andrew Bailey @ 2026-03-06 16:40 UTC (permalink / raw)
To: probb
Cc: dev, luca.vizzarro, dmarx, kai.ji, dharmikjayesh.thakkar,
david.marchand, gakhil, Andrew Bailey
Running the DPDK test crypto performance application is essential for
testing cryptodev performance in DTS. This application takes numerous
arguments and can be run in four modes; Throughput, Latency,
PMD-cyclecount, and Verify. The package to add in this commit allows for
this application to be run in any of these modes with user supplied
arguments.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/api/cryptodev/__init__.py | 137 +++++
dts/api/cryptodev/config.py | 508 +++++++++++++++++++
dts/api/cryptodev/types.py | 185 +++++++
dts/configurations/nodes.example.yaml | 12 +
dts/framework/config/node.py | 4 +
dts/framework/params/types.py | 65 +++
dts/framework/remote_session/dpdk_shell.py | 5 +-
dts/framework/test_run.py | 5 +
dts/framework/testbed_model/linux_session.py | 45 ++
dts/framework/testbed_model/node.py | 10 +
dts/framework/testbed_model/os_session.py | 30 ++
dts/framework/testbed_model/topology.py | 78 ++-
12 files changed, 1081 insertions(+), 3 deletions(-)
create mode 100644 dts/api/cryptodev/__init__.py
create mode 100644 dts/api/cryptodev/config.py
create mode 100644 dts/api/cryptodev/types.py
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
new file mode 100644
index 0000000000..a4fafc3713
--- /dev/null
+++ b/dts/api/cryptodev/__init__.py
@@ -0,0 +1,137 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""Cryptodev-pmd non-interactive shell.
+
+Typical usage example in a TestSuite::
+
+ cryptodev = CryptodevPmd(CryptoPmdParams)
+ stats = cryptodev.run_app()
+"""
+
+import re
+from typing import TYPE_CHECKING, Any
+
+from typing_extensions import Unpack
+
+from api.cryptodev.config import CryptoPmdParams, TestType
+from api.cryptodev.types import (
+ CryptodevResults,
+ LatencyResults,
+ PmdCyclecountResults,
+ ThroughputResults,
+ VerifyResults,
+)
+from framework.context import get_ctx
+from framework.exception import RemoteCommandExecutionError, SkippedTestException
+from framework.remote_session.dpdk_shell import compute_eal_params
+
+if TYPE_CHECKING:
+ from framework.params.types import CryptoPmdParamsDict
+from pathlib import PurePath
+
+
+class Cryptodev:
+ """non-interactive cryptodev application.
+
+ Attributes:
+ _app_params: app parameters to pass to dpdk-test-crypto-perf application
+ """
+
+ _app_params: dict[str, Any]
+
+ def __init__(self, **app_params: Unpack["CryptoPmdParamsDict"]) -> None:
+ """Initialize the cryptodev application.
+
+ Args:
+ app_params: The application parameters as keyword arguments.
+
+ Raises:
+ ValueError: if the build environment is `None`
+ """
+ self._app_params = {}
+ for k, v in app_params.items():
+ if v is not None:
+ self._app_params[k] = (
+ self.vector_directory.joinpath(str(v)) if k == "test_file" else v
+ )
+ dpdk = get_ctx().dpdk.build
+ if dpdk is None:
+ raise ValueError("No DPDK build environment exists.")
+ self._path = dpdk.get_app("test-crypto-perf")
+
+ @property
+ def path(self) -> PurePath:
+ """Get the path to the cryptodev application.
+
+ Returns:
+ The path to the cryptodev application.
+ """
+ return PurePath(self._path)
+
+ @property
+ def vector_directory(self) -> PurePath:
+ """Get the path to the cryptodev vector files.
+
+ Returns:
+ The path to the cryptodev vector files.
+ """
+ return get_ctx().dpdk_build.remote_dpdk_tree_path.joinpath("app/test-crypto-perf/data/")
+
+ def run_app(self, num_vfs: int = 1) -> list[CryptodevResults]:
+ """Run the cryptodev application with the given app parameters.
+
+ Args:
+ num_vfs: number of virtual functions to run the crypto-dev application with.
+
+ Raises:
+ SkippedTestException: If the device type is not supported on the main session.
+ RemoteCommandExecutionError: If there is an error running the command.
+
+ Returns:
+ list[CryptodevResults]: The list of parsed results for the cryptodev application.
+ """
+ try:
+ result = get_ctx().dpdk.run_dpdk_app(
+ self.path,
+ compute_eal_params(
+ CryptoPmdParams(
+ allowed_ports=get_ctx().topology.get_crypto_vfs(num_vfs),
+ memory_channels=get_ctx().dpdk.config.memory_channels,
+ **self._app_params,
+ ),
+ ),
+ timeout=120,
+ )
+ except RemoteCommandExecutionError as e:
+ if "Crypto device type does not support capabilities requested" in e._command_stderr:
+ raise SkippedTestException(
+ f"{self._app_params['devtype']} does not support the requested capabilities"
+ )
+ elif "Failed to initialise requested crypto device type" in e._command_stderr:
+ raise SkippedTestException(
+ f"could not run application with devtype {self._app_params['devtype']}"
+ )
+ elif "failed to parse device" in e._command_stderr:
+ raise SkippedTestException(
+ f"dependencies missing for virtual device {self._app_params['vdevs'][0].name}"
+ )
+ raise e
+
+ regex = r"^\s+\d+.*$"
+ parser_options = re.MULTILINE
+ parser: type[CryptodevResults]
+
+ match self._app_params["ptest"]:
+ case TestType.throughput:
+ parser = ThroughputResults
+ case TestType.latency:
+ regex = r"total operations:.*time[^\n]*"
+ parser_options |= re.DOTALL
+ parser = LatencyResults
+ case TestType.pmd_cyclecount:
+ parser = PmdCyclecountResults
+ case TestType.verify:
+ parser = VerifyResults
+
+ return [parser.parse(line) for line in re.findall(regex, result.stdout, parser_options)]
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
new file mode 100644
index 0000000000..ff4ce25c93
--- /dev/null
+++ b/dts/api/cryptodev/config.py
@@ -0,0 +1,508 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+"""Module containing types and parameter classes for cryptodev-pmd application."""
+
+from dataclasses import dataclass, field
+from enum import auto
+from typing import Literal
+
+from framework.params import Params, Switch
+from framework.params.eal import EalParams
+from framework.utils import StrEnum
+
+Silent = Literal[""]
+
+
+class DeviceType(StrEnum):
+ """Enum for cryptodev device types.
+
+ Attributes:
+ crypto_aesni_gcm: AES-NI GCM device type.
+ crypto_aesni_mb: AES-NI MB device type.
+ crypto_armv8: ARMv8 device type.
+ crypto_cn10k: CN10K device type.
+ crypto_cn9k: CN9K device type.
+ crypto_dpaa_sec: DPAA SEC device type.
+ crypto_dpaa2_sec: DPAA2 SEC device type.
+ crypto_kasumi: KASUMI device type.
+ crypto_mvsam: MVSAM device type.
+ crypto_null: NULL device type.
+ crypto_octeontx: OCTEONTX device type.
+ crypto_openssl: OpenSSL device type.
+ crypto_qat: QAT device type.
+ crypto_scheduler: Scheduler device type.
+ crypto_snow3g: SNOW3G device type.
+ crypto_zuc: ZUC device type.
+ """
+
+ crypto_aesni_gcm = auto()
+ crypto_aesni_mb = auto()
+ crypto_armv8 = auto()
+ crypto_cn10k = auto()
+ crypto_cn9k = auto()
+ crypto_dpaa_sec = auto()
+ crypto_dpaa2_sec = auto()
+ crypto_kasumi = auto()
+ crypto_mvsam = auto()
+ crypto_null = auto()
+ crypto_octeontx = auto()
+ crypto_openssl = auto()
+ crypto_qat = auto()
+ crypto_scheduler = auto()
+ crypto_snow3g = auto()
+ crypto_zuc = auto()
+
+
+def get_device_from_str(device: str) -> DeviceType | None:
+ """Get a device type from the given string.
+
+ Args:
+ device: the string representation of the desired device.
+
+ Returns:
+ the device as a DeviceType object.
+ """
+ for item in DeviceType:
+ if device in item.value:
+ return item
+ return None
+
+
+class OperationType(StrEnum):
+ """Enum for cryptodev operation types.
+
+ Attributes:
+ aead: AEAD operation type.
+ auth_only: Authentication only operation type.
+ auth_then_cipher: Authentication then cipher operation type.
+ cipher_only: Cipher only operation type.
+ cipher_then_auth: Cipher then authentication operation type.
+ docsis: DOCSIS operation type.
+ ecdsa_p192r1 = ECDSA P-192R1 operation type.
+ ecdsa_p224r1 = ECDSA P-224R1 operation type.
+ ecdsa_p256r1: ECDSA P-256R1 operation type.
+ ecdsa_p384r1 = ECDSA P-384R1 operation type.
+ ecdsa_p521r1 = ECDSA P-521R1 operation type.
+ eddsa_25519: EdDSA 25519 operation type.
+ modex: Modex operation type.
+ ipsec: IPsec operation type.
+ pdcp: PDCP operation type.
+ rsa: RSA operation type.
+ sm2: SM2 operation type.
+ tls_record: TLS record operation type.
+ """
+
+ aead = auto()
+ auth_only = "auth-only"
+ auth_then_cipher = "auth-then-cipher"
+ cipher_only = "cipher-only"
+ cipher_then_auth = "cipher-then-auth"
+ docsis = auto()
+ ecdsa_p192r1 = auto()
+ ecdsa_p224r1 = auto()
+ ecdsa_p256r1 = auto()
+ ecdsa_p384r1 = auto()
+ ecdsa_p521r1 = auto()
+ eddsa_25519 = auto()
+ modex = auto()
+ ipsec = auto()
+ pdcp = auto()
+ rsa = auto()
+ sm2 = auto()
+ tls_record = "tls-record"
+
+ def __str__(self) -> str:
+ """Override str to return the value."""
+ return self.value
+
+
+class CipherAlgorithm(StrEnum):
+ """Enum for cryptodev cipher algorithms.
+
+ Attributes:
+ aes_cbc: AES CBC cipher algorithm.
+ aes_ctr: AES CTR cipher algorithm.
+ aes_docsisbpi: AES DOCSIS BPI cipher algorithm.
+ aes_ecb: AES ECB cipher algorithm.
+ aes_f8: AES F8 cipher algorithm.
+ aes_xts: AES XTS cipher algorithm.
+ arc4: ARC4 cipher algorithm.
+ null: NULL cipher algorithm.
+ three_des_cbc: 3DES CBC cipher algorithm.
+ three_des_ctr: 3DES CTR cipher algorithm.
+ three_des_ecb: 3DES ECB cipher algorithm.
+ kasumi_f8: KASUMI F8 cipher algorithm.
+ snow3g_uea2: SNOW3G UEA2 cipher algorithm.
+ zuc_eea3: ZUC EEA3 cipher algorithm.
+ """
+
+ aes_cbc = "aes-cbc"
+ aes_ctr = "aes-ctr"
+ aes_docsisbpi = "aes-docsisbpi"
+ aes_ecb = "aes-ecb"
+ aes_f8 = "aes-f8"
+ aes_gcm = "aes-gcm"
+ aes_xts = "aes-xts"
+ arc4 = auto()
+ null = auto()
+ three_des_cbc = "3des-cbc"
+ three_des_ctr = "3des-ctr"
+ three_des_ecb = "3des-ecb"
+ kasumi_f8 = "kasumi-f8"
+ snow3g_uea2 = "snow3g-uea2"
+ zuc_eea3 = "zuc-eea3"
+
+ def __str__(self):
+ """Override str to return the value."""
+ return self.value
+
+
+class AuthenticationAlgorithm(StrEnum):
+ """Enum for cryptodev authentication algorithms.
+
+ Attributes:
+ aes_cbc_mac: AES CBC MAC authentication algorithm.
+ aes_cmac: AES CMAC authentication algorithm.
+ aes_gmac: AES GMAC authentication algorithm.
+ aes_xcbc_mac: AES XCBC MAC authentication algorithm.
+ kasumi_f9: KASUMI F9 authentication algorithm.
+ md5: MD5 authentication algorithm.
+ md5_hmac: MD5 HMAC authentication algorithm.
+ sha1: SHA1 authentication algorithm.
+ sha1_hmac: SHA1 HMAC authentication algorithm.
+ sha2_224: SHA2-224 authentication algorithm.
+ sha2_224_hmac: SHA2-224 HMAC authentication algorithm.
+ sha2_256: SHA2-256 authentication algorithm.
+ sha2_256_hmac: SHA2-256 HMAC authentication algorithm.
+ sha2_384: SHA2-384 authentication algorithm.
+ sha2_384_hmac: SHA2-384 HMAC authentication algorithm.
+ sha2_512: SHA2-512 authentication algorithm.
+ sha2_512_hmac: SHA2-512 HMAC authentication algorithm.
+ snow3g_uia2: SNOW3G UIA2 authentication algorithm.
+ zuc_eia3: ZUC EIA3 authentication algorithm.
+ """
+
+ aes_cbc_mac = "aes-cbc-mac"
+ aes_cmac = "aes-cmac"
+ aes_gmac = "aes-gmac"
+ aes_xcbc_mac = "aes-xcbc-mac"
+ kasumi_f9 = "kasumi-f9"
+ md5 = auto()
+ md5_hmac = "md5-hmac"
+ sha1 = auto()
+ sha1_hmac = "sha1-hmac"
+ sha2_224 = "sha2-224"
+ sha2_224_hmac = "sha2-224-hmac"
+ sha2_256 = "sha2-256"
+ sha2_256_hmac = "sha2-256-hmac"
+ sha2_384 = "sha2-384"
+ sha2_384_hmac = "sha2-384-hmac"
+ sha2_512 = "sha2-512"
+ sha2_512_hmac = "sha2-512-hmac"
+ snow3g_uia2 = "snow3g-uia2"
+ zuc_eia3 = "zuc-eia3"
+
+ def __str__(self) -> str:
+ """Override str to return the value."""
+ return self.value
+
+
+class TestType(StrEnum):
+ """Enum for cryptodev test types.
+
+ Attributes:
+ latency: Latency test type.
+ pmd_cyclecount: PMD cyclecount test type.
+ throughput: Throughput test type.
+ verify: Verify test type.
+ """
+
+ latency = auto()
+ pmd_cyclecount = "pmd-cyclecount"
+ throughput = auto()
+ verify = auto()
+
+ def __str__(self) -> str:
+ """Override str to return the value."""
+ return self.value
+
+
+class EncryptDecryptSwitch(StrEnum):
+ """Enum for cryptodev encrypt/decrypt operations.
+
+ Attributes:
+ decrypt: Decrypt operation.
+ encrypt: Encrypt operation.
+ """
+
+ decrypt = auto()
+ encrypt = auto()
+
+
+class AuthenticationOpMode(StrEnum):
+ """Enum for cryptodev authentication operation modes.
+
+ Attributes:
+ generate: Generate operation mode.
+ verify: Verify operation mode.
+ """
+
+ generate = auto()
+ verify = auto()
+
+
+class AeadAlgName(StrEnum):
+ """Enum for cryptodev AEAD algorithms.
+
+ Attributes:
+ aes_ccm: AES CCM algorithm.
+ aes_gcm: AES GCM algorithm.
+ """
+
+ aes_ccm = "aes-ccm"
+ aes_gcm = "aes-gcm"
+
+ def __str__(self):
+ """Override str to return the value."""
+ return self.value
+
+
+class AsymOpMode(StrEnum):
+ """Enum for cryptodev asymmetric operation modes.
+
+ Attributes:
+ decrypt: Decrypt operation mode.
+ encrypt: Encrypt operation mode.
+ sign: Sign operation mode.
+ verify: Verify operation mode.
+ """
+
+ decrypt = auto()
+ encrypt = auto()
+ sign = auto()
+ verify = auto()
+
+
+class PDCPDomain(StrEnum):
+ """Enum for cryptodev PDCP domains.
+
+ Attributes:
+ control: Control domain.
+ user: User domain.
+ """
+
+ control = auto()
+ user = auto()
+
+
+class RSAPrivKeyType(StrEnum):
+ """Enum for cryptodev RSA private key types.
+
+ Attributes:
+ exp: Exponent key type.
+ qt: QT key type.
+ """
+
+ exp = auto()
+ qt = auto()
+
+
+class TLSVersion(StrEnum):
+ """Enum for cryptodev TLS versions.
+
+ Attributes:
+ DTLS1_2: DTLS 1.2 version.
+ TLS1_2: TLS 1.2 version.
+ TLS1_3: TLS 1.3 version.
+ """
+
+ DTLS1_2 = "DTLS1.2"
+ TLS1_2 = "TLS1.2"
+ TLS1_3 = "TLS1.3"
+
+ def __str__(self):
+ """Override str to return the value."""
+ return self.value
+
+
+class RSAPrivKeytype(StrEnum):
+ """Enum for cryptodev RSA private key types.
+
+ Attributes:
+ exp: Exponent key type.
+ qt: QT key type.
+ """
+
+ exp = auto()
+ qt = auto()
+
+
+class BurstSizeRange:
+ """Class for burst size parameter.
+
+ Attributes:
+ burst_size: The burst size range, this list must be less than 32 elements.
+ """
+
+ burst_size: int | list[int]
+
+ def __init__(self, min: int, inc: int, max: int) -> None:
+ """Initialize the burst size range.
+
+ Args:
+ min: minimum value in range (inclusive)
+ inc: space in between each element in the range
+ max: maximum value in range (inclusive)
+
+ Raises:
+ ValueError: If the burst size range is more than 32 elements.
+ """
+ if (max - min) % inc > 32:
+ raise ValueError("Burst size range must be less than 32 elements.")
+ self.burst_size = list(range(min, max + 1, inc))
+
+
+class ListWrapper:
+ """Class for wrapping a list of integers.
+
+ One of the arguments for the cryptodev application is a list of integers. However, when
+ passing a list directly, it causes a syntax error in the command line. This class wraps
+ a list of integers and converts it to a comma-separated string for a proper command.
+ """
+
+ def __init__(self, values: list[int]) -> None:
+ """Initialize the list wrapper.
+
+ Args:
+ values: list of values to wrap
+ """
+ self.values = values
+
+ def __str__(self) -> str:
+ """Convert the list to a comma-separated string."""
+ return ",".join(str(v) for v in self.values)
+
+
+@dataclass(slots=True, kw_only=True)
+class CryptoPmdParams(EalParams):
+ """Parameters for cryptodev-pmd application.
+
+ Attributes:
+ aead_aad_sz: set the size of AEAD AAD.
+ aead_algo: set AEAD algorithm name from class `AeadAlgName`.
+ aead_iv_sz: set the size of AEAD iv.
+ aead_key_sz: set the size of AEAD key.
+ aead_op: set AEAD operation mode from class `EncryptDecryptSwitch`.
+ asym_op: set asymmetric operation mode from class `AsymOpMode`.
+ auth_algo: set authentication algorithm name.
+ auth_aad_sz: set the size of authentication AAD.
+ auth_iv_sz: set the size of authentication iv.
+ auth_key_sz: set the size of authentication key.
+ auth_op: set authentication operation mode from class `AuthenticationOpMode`.
+ buffer_sz: Set the size of a single packet (plaintext or ciphertext in it).
+ burst_sz: Set the number of packets per burst. This can be set as a single value or
+ range of values defined by class `BurstSizeRange`. Default is 16.
+ burst_sz: Set the number of packets per burst. This can be set as a single value or
+ range of values defined by class `BurstSizeRange`. Default is 16.
+ cipher_algo: Set cipher algorithm name from class `CipherAlgorithm`.
+ cipher_iv_sz: set the size of cipher iv.
+ cipher_key_sz: set the size of cipher key.
+ cipher_op: set cipher operation mode from class `EncryptDecryptSwitch`.
+ csv_friendly: Enable test result output CSV friendly rather than human friendly.
+ desc_nb: set the number of descriptors for each crypto device.
+ devtype: Set the device name from class `DeviceType`.
+ digest_sz: set the size of digest.
+ docsis_hdr_sz: set DOCSIS header size(n) in bytes.
+ enable_sdap: enable service data adaptation protocol.
+ imix: Set the distribution of packet sizes. A list of weights must be passed, containing the
+ same number of items than buffer-sz, so each item in this list will be the weight of the
+ packet size on the same position in the buffer-sz parameter (a list has to be passed in
+ that parameter).
+ low_prio_qp_mask: set low priority queue pairs set in the hexadecimal mask. This is an
+ optional parameter, if not set all queue pairs will be on the same high priority.
+ modex_len: set modex length for asymmetric crypto perf test. Supported lengths are 60,
+ 128, 255, 448. Default length is 128.
+ optype: Set operation type from class `OpType`.
+ out_of_place: Enable out-of-place crypto operations mode.
+ pdcp_sn_sz: set PDCP sequebce number size(n) in bits. Valid values of n are 5/7/12/15/18.
+ pdcp_domain: Set PDCP domain to specify short_mac/control/user plane from class
+ `PDCPDomain`.
+ pdcp_ses_hfn_en: enable fixed session based HFN instead of per packet HFN.
+ pmd_cyclecount_delay_pmd: Add a delay (in milliseconds) between enqueue and dequeue in
+ pmd-cyclecount benchmarking mode (useful when benchmarking hardware acceleration).
+ pool_sz: Set the number if mbufs to be allocated in the mbuf pool.
+ ptest: Set performance throughput test type from class `TestType`.
+ rsa_modlen: Set RSA modulus length (in bits) for asymmetric crypto perf test.
+ To be used with RSA asymmetric crypto ops.Supported lengths are 1024, 2048, 4096, 8192.
+ Default length is 1024.
+ rsa_priv_keytype: set RSA private key type from class `RSAPrivKeytype`. To be used with RSA
+ asymmetric crypto ops.
+ segment_sz: Set the size of the segment to use, for Scatter Gather List testing. Use list of
+ values in buffer-sz in descending order if segment-sz is used. By default, it is set to
+ the size of the maximum buffer size, including the digest size, so a single segment is
+ created.
+ sessionless: Enable session-less crypto operations mode.
+ shared_session: Enable sharing sessions between all queue pairs on a single crypto PMD. This
+ can be useful for benchmarking this setup, or finding and debugging concurrency errors
+ that can occur while using sessions on multiple lcores simultaneously.
+ silent: Disable options dump.
+ test_file: Set test vector file path. See the Test Vector File chapter.
+ test_name: Set specific test name section in the test vector file.
+ tls_version: Set TLS/DTLS protocol version for perf test from class `TLSVersion`.
+ Default is TLS1.2.
+ total_ops: Set the number of total operations performed.
+ """
+
+ aead_aad_sz: int | None = field(default=None, metadata=Params.long("aead-aad-sz"))
+ aead_algo: AeadAlgName | None = field(default=None, metadata=Params.long("aead-algo"))
+ aead_iv_sz: int | None = field(default=None, metadata=Params.long("aead-iv-sz"))
+ aead_key_sz: int | None = field(default=None, metadata=Params.long("aead-key-sz"))
+ aead_op: EncryptDecryptSwitch | None = field(default=None, metadata=Params.long("aead-op"))
+ asym_op: AsymOpMode | None = field(default=None, metadata=Params.long("asym-op"))
+ auth_algo: AuthenticationAlgorithm | None = field(
+ default=None, metadata=Params.long("auth-algo")
+ )
+ auth_iv_sz: int | None = field(default=None, metadata=Params.long("auth-iv-sz"))
+ auth_key_sz: int | None = field(default=None, metadata=Params.long("auth-key-sz"))
+ auth_op: AuthenticationOpMode | None = field(default=None, metadata=Params.long("auth-op"))
+ buffer_sz: BurstSizeRange | ListWrapper | int | None = field(
+ default=None, metadata=Params.long("buffer-sz")
+ )
+ burst_sz: BurstSizeRange | ListWrapper | int | None = field(
+ default=None, metadata=Params.long("burst-sz")
+ )
+ cipher_algo: CipherAlgorithm | None = field(default=None, metadata=Params.long("cipher-algo"))
+ cipher_iv_sz: int | None = field(default=None, metadata=Params.long("cipher-iv-sz"))
+ cipher_key_sz: int | None = field(default=None, metadata=Params.long("cipher-key-sz"))
+ cipher_op: EncryptDecryptSwitch | None = field(default=None, metadata=Params.long("cipher-op"))
+ csv_friendly: Switch = field(default=None, metadata=Params.long("csv-friendly"))
+ desc_nb: int | None = field(default=None, metadata=Params.long("desc-nb"))
+ devtype: DeviceType = field(metadata=Params.long("devtype"))
+ digest_sz: int | None = field(default=None, metadata=Params.long("digest-sz"))
+ docsis_hdr_sz: int | None = field(default=None, metadata=Params.long("docsis-hdr-sz"))
+ enable_sdap: Switch = None
+ imix: int | None = field(default=None, metadata=Params.long("imix"))
+ low_prio_qp_mask: int | None = field(default=None, metadata=Params.convert_value(hex))
+ modex_len: int | None = field(default=None, metadata=Params.long("modex-len"))
+ optype: OperationType | None = field(default=None, metadata=Params.long("optype"))
+ out_of_place: Switch = None
+ pdcp_sn_sz: int | None = None
+ pdcp_domain: PDCPDomain | None = field(default=None, metadata=Params.long("pdcp-domain"))
+ pdcp_ses_hfn_en: Switch | None = field(default=None, metadata=Params.long("pdcp-ses-hfn-en"))
+ pmd_cyclecount_delay_pmd: int | None = field(
+ default=None, metadata=Params.long("pmd-cyclecount-delay-pmd")
+ )
+ pool_sz: int | None = field(default=None, metadata=Params.long("pool-sz"))
+ ptest: TestType = field(default=TestType.throughput, metadata=Params.long("ptest"))
+ rsa_modlen: int | None = field(default=None, metadata=Params.long("rsa-modlen"))
+ rsa_priv_keytype: RSAPrivKeytype | None = field(
+ default=None, metadata=Params.long("rsa-priv-keytype")
+ )
+ segment_sz: int | None = field(default=None, metadata=Params.long("segment-sz"))
+ sessionless: Switch = None
+ shared_session: Switch = None
+ silent: Silent | None = field(default="", metadata=Params.long("silent"))
+ test_file: str | None = field(default=None, metadata=Params.long("test-file"))
+ test_name: str | None = field(default=None, metadata=Params.long("test-name"))
+ tls_version: TLSVersion | None = field(default=None, metadata=Params.long("tls-version"))
+ total_ops: int | None = field(default=100000, metadata=Params.long("total-ops"))
diff --git a/dts/api/cryptodev/types.py b/dts/api/cryptodev/types.py
new file mode 100644
index 0000000000..df73a86fa4
--- /dev/null
+++ b/dts/api/cryptodev/types.py
@@ -0,0 +1,185 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""Cryptodev types module.
+
+Exposes types used in the Cryptodev API.
+"""
+
+from dataclasses import dataclass, field
+
+from framework.parser import TextParser
+
+
+@dataclass
+class CryptodevResults(TextParser):
+ """Base class for all cryptodev results."""
+
+ buffer_size: int
+
+ def __iter__(self):
+ """Iteration method to parse result objects.
+
+ Yields:
+ tuple[str, int | float]: a field name and its value.
+ """
+ for field_name in self.__dataclass_fields__:
+ yield field_name, getattr(self, field_name)
+
+
+@dataclass
+class ThroughputResults(CryptodevResults):
+ """A parser for throughput test output."""
+
+ #:
+ lcore_id: int = field(metadata=TextParser.find_int(r"\s*(\d+)"))
+ #: buffer size used in the run
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"\s+(?:\d+\s+)(\d+)"),
+ )
+ #: burst size used in the run
+ burst_size: int = field(
+ metadata=TextParser.find_int(r"\s+(?:\d+\s+){2}(\d+)"),
+ )
+ #: total packets enqueued
+ enqueued: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){3}(\d+)"))
+ #: total packets dequeued
+ dequeued: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){4}(\d+)"))
+ #: packets that failed enqueue
+ failed_enqueue: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){5}(\d+)"))
+ #: packets that failed dequeue
+ failed_dequeue: int = field(metadata=TextParser.find_int(r"\s+(?:\d+\s+){6}(\d+)"))
+ #: mega operations per second
+ mops: float = field(metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}([\d.]+)"))
+ #: gigabits per second
+ gbps: float = field(metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}(?:[\d.]+\s+)([\d.]+)"))
+ #: cpu cycles per buffer
+ cycles_per_buffer: float = field(
+ metadata=TextParser.find_float(r"\s+(?:\d+\s+){7}(?:[\d.]+\s+){2}([\d.]+)")
+ )
+
+
+@dataclass
+class LatencyResults(CryptodevResults):
+ """A parser for latency test output."""
+
+ #: buffer size ran with app
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"Buf(?:.*\n\s+\d+\s+)?(?:fer size:\s+)?(\d+)"),
+ )
+ #: burst size ran with app
+ burst_size: int = field(
+ metadata=TextParser.find_int(rf"Burst(?:.*\n\s+\d+\s+){2}?(?: size:\s+)?(\d+)"),
+ )
+ #: total operations ran
+ total_ops: int = field(metadata=TextParser.find_int(r"total operations:\s+(\d+)"))
+ #: number of bursts
+ num_of_bursts: int = field(metadata=TextParser.find_int(r"Number of bursts:\s+(\d+)"))
+ #: minimum enqueued packets
+ min_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+){2}(\d+)"))
+ #: maximum enqueued packets
+ max_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+){3}(\d+)"))
+ #: average enqueued packets
+ avg_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(?:\d+\s+)(\d+)"))
+ #: total enqueued packets
+ total_enqueued: int = field(metadata=TextParser.find_int(r"enqueued\s+(\d+)"))
+ #: minimum dequeued packets
+ min_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+){2}(\d+)"))
+ #: maximum dequeued packets
+ max_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+){3}(\d+)"))
+ #: average dequeued packets
+ avg_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(?:\d+\s+)(\d+)"))
+ #: total dequeued packets
+ total_dequeued: int = field(metadata=TextParser.find_int(r"dequeued\s+(\d+)"))
+ #: minimum cycles per buffer
+ min_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+){3}([\d.]+)"))
+ #: maximum cycles per buffer
+ max_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+){2}([\d.]+)"))
+ #: average cycles per buffer
+ avg_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+(?:[\d.]+\s+)([\d.]+)"))
+ #: total cycles per buffer
+ total_cycles: float = field(metadata=TextParser.find_float(r"cycles\s+([\d.]+)"))
+ #: mimum time in microseconds
+ min_time_us: float = field(
+ metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+){3}([\d.]+)")
+ )
+ #: maximum time in microseconds
+ max_time_us: float = field(
+ metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+){2}([\d.]+)")
+ )
+ #: average time in microseconds
+ avg_time_us: float = field(
+ metadata=TextParser.find_float(r"time \[us\]\s+(?:[\d.]+\s+)([\d.]+)")
+ )
+ #: total time in microseconds
+ total_time_us: float = field(metadata=TextParser.find_float(r"time \[us\]\s+([\d.]+)"))
+
+
+@dataclass
+class PmdCyclecountResults(CryptodevResults):
+ """A parser for PMD cycle count test output."""
+
+ #:
+ lcore_id: int = field(metadata=TextParser.find_int(r"lcore\s+(?:id.*\n\s+)?(\d+)"))
+ #: buffer size used with app run
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"Buf(?:.*\n\s+(?:\d+\s+))?(?:fer size:\s+)?(\d+)"),
+ )
+ #: burst size used with app run
+ burst_size: int = field(
+ metadata=TextParser.find_int(r"Burst(?:.*\n\s+(?:\d+\s+){2})?(?: size:\s+)?(\d+)"),
+ )
+ #: packets enqueued
+ enqueued: int = field(metadata=TextParser.find_int(r"Enqueued.*\n\s+(?:\d+\s+){3}(\d+)"))
+ #: packets dequeued
+ dequeued: int = field(metadata=TextParser.find_int(r"Dequeued.*\n\s+(?:\d+\s+){4}(\d+)"))
+ #: number of enqueue packet retries
+ enqueue_retries: int = field(
+ metadata=TextParser.find_int(r"Enq Retries.*\n\s+(?:\d+\s+){5}(\d+)")
+ )
+ #: number of dequeue packet retries
+ dequeue_retries: int = field(
+ metadata=TextParser.find_int(r"Deq Retries.*\n\s+(?:\d+\s+){6}(\d+)")
+ )
+ #: number of cycles per operation
+ cycles_per_operation: float = field(
+ metadata=TextParser.find_float(r"Cycles/Op.*\n\s+(?:\d+\s+){7}([\d.]+)")
+ )
+ #: number of cycles per enqueue
+ cycles_per_enqueue: float = field(
+ metadata=TextParser.find_float(r"Cycles/Enq.*\n\s+(?:\d+\s+){7}(?:[\d.]+\s+)([\d.]+)")
+ )
+ #: number of cycles per dequeue
+ cycles_per_dequeue: float = field(
+ metadata=TextParser.find_float(r"Cycles/Deq.*\n\s+(?:\d+\s+){7}(?:[\d.]+\s+){2}([\d.]+)"),
+ )
+
+
+@dataclass
+class VerifyResults(CryptodevResults):
+ """A parser for verify test output."""
+
+ #:
+ lcore_id: int = field(metadata=TextParser.find_int(r"lcore\s+(?:id.*\n\s+)?(\d+)"))
+ #: buffer size ran with app
+ buffer_size: int = field(
+ metadata=TextParser.find_int(r"Buf(?:.*\n\s+(?:\d+\s+))?(?:fer size:\s+)?(\d+)"),
+ )
+ #: burst size ran with app
+ burst_size: int = field(
+ metadata=TextParser.find_int(r"Burst(?:.*\n\s+(?:\d+\s+){2})?(?: size:\s+)?(\d+)"),
+ )
+ #: number of packets enqueued
+ enqueued: int = field(metadata=TextParser.find_int(r"Enqueued.*\n\s+(?:\d+\s+){3}(\d+)"))
+ #: number of packets dequeued
+ dequeued: int = field(metadata=TextParser.find_int(r"Dequeued.*\n\s+(?:\d+\s+){4}(\d+)"))
+ #: number of packets enqueue failed
+ failed_enqueued: int = field(
+ metadata=TextParser.find_int(r"Failed Enq.*\n\s+(?:\d+\s+){5}(\d+)")
+ )
+ #: number of packets dequeue failed
+ failed_dequeued: int = field(
+ metadata=TextParser.find_int(r"Failed Deq.*\n\s+(?:\d+\s+){6}(\d+)")
+ )
+ #: total number of failed operations
+ failed_ops: int = field(metadata=TextParser.find_int(r"Failed Ops.*\n\s+(?:\d+\s+){7}(\d+)"))
diff --git a/dts/configurations/nodes.example.yaml b/dts/configurations/nodes.example.yaml
index 4da5585318..b9e567b442 100644
--- a/dts/configurations/nodes.example.yaml
+++ b/dts/configurations/nodes.example.yaml
@@ -4,6 +4,12 @@
# Optional Fields:
# `hugepages_2mb`: if removed, will use system hugepage configuration
+# `cryptodevs`:
+# Uncomment to run cryptodev tests; add physical cryptodev devices by their BDF
+# `crypto_device_type`:
+# Uncomment to run cryptodev tests; the device type of the DUT e.g. (crypto_qat, crypto_zuc,
+# crypto_aesni_mb) the complete list of device types can be found here
+# https://doc.dpdk.org/guides-24.07/cryptodevs/overview.html
# Define a system under test node, having two network ports physically
# connected to the corresponding ports in TG 1 (the peer node)
@@ -23,6 +29,12 @@
hugepages_2mb: # see 'Optional Fields'
number_of: 256
force_first_numa: false
+ # cryptodevs: # see `Optional Fields`
+ # - name: cryptodev-0
+ # pci: "ffff:ff:ff.9"
+ # os_driver_for_dpdk: vfio-pci # OS driver that DPDK will use for cryptodev VFs
+ # os_driver: _ # This is unused but a value is necessary to run dts
+ # crypto_device_type: # see `Optional Fields`
# Define a Scapy traffic generator node, having two network ports
# physically connected to the corresponding ports in SUT 1 (the peer node).
- name: "TG 1"
diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
index 438a1bdc8f..792290f11f 100644
--- a/dts/framework/config/node.py
+++ b/dts/framework/config/node.py
@@ -70,6 +70,10 @@ class NodeConfiguration(FrozenModel):
hugepages: HugepageConfiguration | None = Field(None, alias="hugepages_2mb")
#: The ports that can be used in testing.
ports: list[PortConfig] = Field(min_length=1)
+ #: The pci info used by crypto devices
+ cryptodevs: list[PortConfig] = Field(default=[], min_length=0)
+ #: The crypto driver used by crypto devices
+ crypto_device_type: str | None = None
@model_validator(mode="after")
def verify_unique_port_names(self) -> Self:
diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py
index 5bc4bd37d9..3c7650474c 100644
--- a/dts/framework/params/types.py
+++ b/dts/framework/params/types.py
@@ -15,6 +15,23 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
from pathlib import PurePath
from typing import TypedDict
+from api.cryptodev.config import (
+ AeadAlgName,
+ AsymOpMode,
+ AuthenticationAlgorithm,
+ AuthenticationOpMode,
+ BurstSizeRange,
+ CipherAlgorithm,
+ DeviceType,
+ EncryptDecryptSwitch,
+ ListWrapper,
+ OperationType,
+ PDCPDomain,
+ RSAPrivKeytype,
+ Silent,
+ TestType,
+ TLSVersion,
+)
from api.testpmd.config import (
AnonMempoolAllocationMode,
EthPeer,
@@ -55,6 +72,54 @@ class EalParamsDict(TypedDict, total=False):
other_eal_param: Params | None
+class CryptoPmdParamsDict(EalParamsDict, total=False):
+ """:class:`TypedDict` equivalent of :class:`~.cryptodev.CryptoPmdParams`."""
+
+ aead_aad_sz: int | None
+ aead_algo: AeadAlgName | None
+ aead_iv_sz: int | None
+ aead_key_sz: int | None
+ aead_op: EncryptDecryptSwitch | None
+ asym_op: AsymOpMode | None
+ auth_algo: AuthenticationAlgorithm | None
+ auth_iv_sz: int | None
+ auth_key_sz: int | None
+ auth_op: AuthenticationOpMode | None
+ buffer_sz: BurstSizeRange | ListWrapper | int | None
+ burst_sz: BurstSizeRange | ListWrapper | int | None
+ cipher_algo: CipherAlgorithm | None
+ cipher_iv_sz: int | None
+ cipher_key_sz: int | None
+ cipher_op: EncryptDecryptSwitch | None
+ csv_friendly: Switch
+ desc_nb: int | None
+ devtype: DeviceType | None
+ digest_sz: int | None
+ docsis_hdr_sz: int | None
+ enable_sdap: Switch
+ imix: int | None
+ low_prio_qp_mask: int | None
+ modex_len: int | None
+ optype: OperationType | None
+ out_of_place: Switch
+ pdcp_sn_sz: int | None
+ pdcp_domain: PDCPDomain | None
+ pdcp_ses_hfn_en: Switch | None
+ pmd_cyclecount_delay_pmd: int | None
+ pool_sz: int | None
+ ptest: TestType
+ rsa_modlen: int | None
+ rsa_priv_keytype: RSAPrivKeytype | None
+ segment_sz: int | None
+ sessionless: Switch
+ shared_session: Switch
+ silent: Silent | None
+ test_file: str | None
+ test_name: str | None
+ tls_version: TLSVersion | None
+ total_ops: int | None
+
+
class TestPmdParamsDict(EalParamsDict, total=False):
""":class:`TypedDict` equivalent of :class:`~.testpmd.TestPmdParams`."""
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index 51b97d4ff6..b94d336d4e 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -46,7 +46,10 @@ def compute_eal_params(
params.prefix = prefix
if params.allowed_ports is None:
- params.allowed_ports = ctx.topology.sut_dpdk_ports
+ if ctx.topology.crypto_vf_ports:
+ params.allowed_ports = [ctx.topology.crypto_vf_ports[0]]
+ else:
+ params.allowed_ports = ctx.topology.sut_dpdk_ports
return params
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index ff0a12c9ce..494eb40046 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -349,6 +349,9 @@ def next(self) -> State | None:
if test_run.config.use_virtual_functions:
test_run.ctx.topology.instantiate_vf_ports()
+ if test_run.ctx.sut_node.cryptodevs:
+ test_run.ctx.topology.instantiate_crypto_ports()
+ test_run.ctx.topology.bind_cryptodevs("dpdk")
test_run.ctx.topology.configure_ports("sut", "dpdk")
if test_run.ctx.func_tg:
@@ -442,6 +445,8 @@ def next(self) -> State | None:
"""Next state."""
if self.test_run.config.use_virtual_functions:
self.test_run.ctx.topology.delete_vf_ports()
+ if self.test_run.ctx.sut_node.cryptodevs:
+ self.test_run.ctx.topology.delete_crypto_vf_ports()
self.test_run.ctx.shell_pool.terminate_current_pool()
if self.test_run.ctx.func_tg and self.test_run.ctx.func_tg.is_setup:
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 2f4aef1460..ee943462c2 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -10,6 +10,7 @@
"""
import json
+import re
from collections.abc import Iterable
from functools import cached_property
from pathlib import PurePath
@@ -247,6 +248,30 @@ def load_vfio(self, pf_port: Port) -> None:
self.send_command("modprobe vfio-pci")
self.refresh_lshw()
+ def create_crypto_vfs(self, pf_port: list[Port]) -> None:
+ """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ for port in pf_port:
+ self.delete_crypto_vfs(port)
+ for port in pf_port:
+ sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
+ )
+ self.send_command(
+ f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
+ )
+
+ self.refresh_lshw()
+
def create_vfs(self, pf_port: Port) -> None:
"""Overrides :meth:`~.os_session.OSSession.create_vfs`.
@@ -263,6 +288,14 @@ def create_vfs(self, pf_port: Port) -> None:
self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
self.refresh_lshw()
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
+ self.send_command(
+ f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
+ privileged=True,
+ )
+ self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
+
def delete_vfs(self, pf_port: Port) -> None:
"""Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
@@ -274,6 +307,18 @@ def delete_vfs(self, pf_port: Port) -> None:
else:
self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ f"readlink {sys_bus_path}/virtfn*",
+ privileged=True,
+ )
+ return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
+ return []
+
def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
"""Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
index 6c3e63a2a1..67a96ef4e5 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -54,6 +54,8 @@ class Node:
arch: Architecture
lcores: list[LogicalCore]
ports: list[Port]
+ cryptodevs: list[Port]
+ crypto_device_type: str | None
_logger: DTSLogger
_other_sessions: list[OSSession]
_node_info: OSSessionInfo | None
@@ -82,6 +84,14 @@ def __init__(self, node_config: NodeConfiguration) -> None:
self._other_sessions = []
self._setup = False
self.ports = [Port(self, port_config) for port_config in self.config.ports]
+ self.cryptodevs = [
+ Port(self, cryptodev_config) for cryptodev_config in self.config.cryptodevs
+ ]
+ self.crypto_device_type = self.config.crypto_device_type
+ if self.cryptodevs:
+ self.main_session.load_vfio(self.cryptodevs[0])
+ elif self.ports and self.ports[0].config.os_driver_for_dpdk == "vfio-pci":
+ self.main_session.load_vfio(self.ports[0])
self._logger.info(f"Created node: {self.name}")
def setup(self) -> None:
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index f3064feeaa..2c267afed1 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -619,6 +619,14 @@ def configure_port_mtu(self, mtu: int, port: Port) -> None:
def load_vfio(self, pf_port: Port) -> None:
"""Load the vfio module according to the device type of the given port."""
+ @abstractmethod
+ def create_crypto_vfs(self, pf_ports: list[Port]) -> None:
+ """Creates virtual functions for each port in 'pf_ports'.
+
+ Checks how many virtual functions each crypto device in 'pf_ports' supports, and creates
+ that number of VFs on the port.
+ """
+
@abstractmethod
def create_vfs(self, pf_port: Port) -> None:
"""Creates virtual functions for `pf_port`.
@@ -634,6 +642,16 @@ def create_vfs(self, pf_port: Port) -> None:
maximum for `pf_port`.
"""
+ @abstractmethod
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Deletes virtual functions for crypto device 'pf_port'.
+
+ Checks how many virtual functions are currently active and removes all if any exist.
+
+ Args:
+ pf_port: The crypto device port to delete virtual functions on.
+ """
+
@abstractmethod
def delete_vfs(self, pf_port: Port) -> None:
"""Deletes virtual functions for `pf_port`.
@@ -660,3 +678,15 @@ def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
A list containing all of the PCI addresses of the VFs on the port. If the port has no
VFs then the list will be empty.
"""
+
+ @abstractmethod
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Find the PCI addresses of all virtual functions (VFs) of a crypto device on `pf_port`.
+
+ Args:
+ pf_port: The port to find the VFs on.
+
+ Returns:
+ A list containing all of the PCI addresses of the VFs on the port. If the port has no
+ VFs then the list will be empty.
+ """
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
index 13b4b58a74..34862c4d2e 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -8,6 +8,7 @@
The link information then implies what type of topology is available.
"""
+import re
from collections import defaultdict
from collections.abc import Iterator
from dataclasses import dataclass
@@ -58,6 +59,8 @@ class Topology:
tg_ports: list[Port]
pf_ports: list[Port]
vf_ports: list[Port]
+ crypto_pf_ports: list[Port]
+ crypto_vf_ports: list[Port]
@classmethod
def from_port_links(cls, port_links: Iterator[PortLink]) -> Self:
@@ -85,7 +88,7 @@ def from_port_links(cls, port_links: Iterator[PortLink]) -> Self:
msg = "More than two links in a topology are not supported."
raise ConfigurationError(msg)
- return cls(type, sut_ports, tg_ports, [], [])
+ return cls(type, sut_ports, tg_ports, [], [], [], [])
def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node, list[Port]]:
"""Retrieve node and its ports for the current topology.
@@ -105,6 +108,32 @@ def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node,
msg = f"Invalid node `{node_identifier}` given."
raise InternalError(msg)
+ def get_crypto_vfs(self, num_vfs: int) -> list[Port]:
+ """Retrieve virtual functions from active crypto vfs.
+
+ Args:
+ num_vfs: Number of virtual functions to return.
+
+ Returns:
+ List containing the requested number of VFs
+
+ Raises:
+ ValueError: If there are not `num_vfs` available VFs
+ """
+ return_list: list[Port] = []
+ device = 1
+ if len(self.crypto_vf_ports) < num_vfs or num_vfs < 0:
+ raise ValueError
+ for i in range(num_vfs):
+ if i % 8 == 0 and i != 0:
+ device += 1
+ for port in filter(
+ lambda x: re.search(rf"0000:\d+:0{device}.{i}", x.pci), self.crypto_vf_ports
+ ):
+ if len(return_list) < num_vfs:
+ return_list.append(port)
+ return return_list
+
def setup(self) -> None:
"""Setup topology ports.
@@ -145,6 +174,32 @@ def _setup_ports(self, node_identifier: NodeIdentifier) -> None:
f"for port {port.name} in node {node.name}."
)
+ def instantiate_crypto_ports(self) -> None:
+ """Create max number of virtual functions allowed on the SUT node from physical BDFs.
+
+ Raises:
+ InternalError: If crypto virtual functions could not be created on a port.
+ """
+ from framework.context import get_ctx
+
+ ctx = get_ctx()
+ for port in ctx.sut_node.cryptodevs:
+ self.crypto_pf_ports.append(port)
+
+ ctx.sut_node.main_session.create_crypto_vfs(self.crypto_pf_ports)
+ for port in self.crypto_pf_ports:
+ addr_list = ctx.sut_node.main_session.get_pci_addr_of_crypto_vfs(port)
+ if addr_list == []:
+ raise InternalError(f"Failed to create crypto virtual function on port {port.pci}")
+ for addr in addr_list:
+ vf_config = PortConfig(
+ name=f"{port.name}-crypto-vf-{addr}",
+ pci=addr,
+ os_driver_for_dpdk=port.config.os_driver_for_dpdk,
+ os_driver=port.config.os_driver,
+ )
+ self.crypto_vf_ports.append(Port(node=port.node, config=vf_config))
+
def instantiate_vf_ports(self) -> None:
"""Create, setup, and add virtual functions to the list of ports on the SUT node.
@@ -189,6 +244,25 @@ def delete_vf_ports(self) -> None:
self.sut_ports.clear()
self.sut_ports.extend(self.pf_ports)
+ def delete_crypto_vf_ports(self) -> None:
+ """Delete crypto virtual functions from the SUT node during test run teardown."""
+ from framework.context import get_ctx
+
+ ctx = get_ctx()
+
+ for port in self.crypto_pf_ports:
+ ctx.sut_node.main_session.delete_crypto_vfs(port)
+
+ def bind_cryptodevs(self, driver: DriverKind):
+ """Bind the crypto device virtual functions on the sut to the specified driver.
+
+ Args:
+ driver: The driver to bind the crypto functions
+ """
+ from framework.context import get_ctx
+
+ self._bind_ports_to_drivers(get_ctx().sut_node, self.crypto_vf_ports, driver)
+
def configure_ports(
self, node_identifier: NodeIdentifier, drivers: DriverKind | tuple[DriverKind, ...]
) -> None:
@@ -227,7 +301,7 @@ def _bind_ports_to_drivers(
for port_id, port in enumerate(ports):
driver_kind = drivers[port_id] if isinstance(drivers, tuple) else drivers
desired_driver = port.driver_by_kind(driver_kind)
- if port.current_driver != desired_driver:
+ if port in self.crypto_vf_ports or port.current_driver != desired_driver:
driver_to_ports[desired_driver].append(port)
for driver_name, ports in driver_to_ports.items():
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v5 4/5] dts: add cryptodev throughput test suite
2026-03-06 16:40 ` [PATCH v5 0/5] dts: add cryptodev testing support Andrew Bailey
` (2 preceding siblings ...)
2026-03-06 16:40 ` [PATCH v5 3/5] dts: add cryptodev package to DTS Andrew Bailey
@ 2026-03-06 16:40 ` Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 5/5] dts: add crypto test decorator Andrew Bailey
4 siblings, 0 replies; 41+ messages in thread
From: Andrew Bailey @ 2026-03-06 16:40 UTC (permalink / raw)
To: probb
Cc: dev, luca.vizzarro, dmarx, kai.ji, dharmikjayesh.thakkar,
david.marchand, gakhil, Andrew Bailey
Add cryptodev performance test suite. This test covers throughput
performance metrics of supplied crypto devices using the
dpdk-test-crypto-perf application. The results of this application will
be compared against user supplied performance metrics and fail if signs
of regression are apparent.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
.../tests.TestSuite_cryptodev_throughput.rst | 8 +
dts/configurations/tests_config.example.yaml | 8 +
dts/tests/TestSuite_cryptodev_throughput.py | 693 ++++++++++++++++++
3 files changed, 709 insertions(+)
create mode 100644 doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
create mode 100644 dts/tests/TestSuite_cryptodev_throughput.py
diff --git a/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst b/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
new file mode 100644
index 0000000000..5df5e7547e
--- /dev/null
+++ b/doc/api/dts/tests.TestSuite_cryptodev_throughput.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+cryptodev_throughput Test Suite
+===============================
+
+.. automodule:: tests.TestSuite_cryptodev_throughput
+ :members:
+ :show-inheritance:
diff --git a/dts/configurations/tests_config.example.yaml b/dts/configurations/tests_config.example.yaml
index 39e96660a5..209d7dd393 100644
--- a/dts/configurations/tests_config.example.yaml
+++ b/dts/configurations/tests_config.example.yaml
@@ -22,3 +22,11 @@ hello_world:
# NUM_QUEUES: 4
# ACTUAL_KEY_SIZE: 52
# ACTUAL_RETA_SIZE: 512
+# cryptodev_throughput:
+# delta_tolerance: 0.05
+# throughput_test_parameters:
+# snow3g_uea2_snow3g_uia2: # Add the name of the test suite you would like to run
+# - buff_size: 64
+# Gbps: 2.0 # Set gigabits per second according to your devices throughput for this given buffer size
+# - buff_size: 512
+# Gbps: 20.0
\ No newline at end of file
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
new file mode 100644
index 0000000000..638a52ac74
--- /dev/null
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -0,0 +1,693 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""DPDK cryptodev performance test suite.
+
+The main goal of this test suite is to utilize the dpdk-test-cryptodev application to gather
+performance metrics for various cryptographic operations supported by DPDK cryptodev-pmd.
+It will then compare the results against predefined baseline given in the test_config file to
+ensure performance standards are met.
+"""
+
+from api.capabilities import (
+ LinkTopology,
+ requires_link_topology,
+)
+from api.cryptodev import Cryptodev
+from api.cryptodev.config import (
+ AeadAlgName,
+ AuthenticationAlgorithm,
+ AuthenticationOpMode,
+ CipherAlgorithm,
+ DeviceType,
+ EncryptDecryptSwitch,
+ ListWrapper,
+ OperationType,
+ TestType,
+ get_device_from_str,
+)
+from api.cryptodev.types import (
+ CryptodevResults,
+)
+from api.test import verify
+from framework.context import get_ctx
+from framework.exception import SkippedTestException
+from framework.test_suite import BaseConfig, TestSuite, func_test
+from framework.testbed_model.virtual_device import VirtualDevice
+
+config_list: list[dict[str, int | float | str]] = [
+ {"buff_size": 64, "Gbps": 1.00},
+ {"buff_size": 512, "Gbps": 1.00},
+ {"buff_size": 2048, "Gbps": 1.00},
+]
+
+TOTAL_OPS = 10_000_000
+
+
+class Config(BaseConfig):
+ """Performance test metrics.
+
+ Attributes:
+ delta_tolerance: The allowed tolerance below a given baseline.
+ throughput_test_parameters: The test parameters to use in the test suite.
+ """
+
+ delta_tolerance: float = 0.05
+
+ throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = {
+ "aes_cbc": config_list,
+ "aes_cbc_sha1": config_list,
+ "aes_cbc_sha2": config_list,
+ "aes_cbc_sha2_digest_16": config_list,
+ "aead_aes_gcm": config_list,
+ "aes_docsisbpi": config_list,
+ "sha1_hmac": config_list,
+ "snow3g_uea2_snow3g_uia2": config_list,
+ "zuc_eea3_zuc_eia3": config_list,
+ "kasumi_f8_kasumi_f9": config_list,
+ "open_ssl_vdev": config_list,
+ "aesni_mb_vdev": config_list,
+ "aesni_gcm_vdev": config_list,
+ "kasumi_vdev": config_list,
+ "zuc_vdev": config_list,
+ "snow3g_vdev": config_list,
+ }
+
+
+@requires_link_topology(LinkTopology.NO_LINK)
+class TestCryptodevThroughput(TestSuite):
+ """DPDK Crypto Device Testing Suite."""
+
+ config: Config
+
+ def set_up_suite(self) -> None:
+ """Set up the test suite."""
+ self.throughput_test_parameters: dict[str, list[dict[str, int | float | str]]] = (
+ self.config.throughput_test_parameters
+ )
+ self.delta_tolerance: float = self.config.delta_tolerance
+ self.device_type: DeviceType | None = get_device_from_str(
+ str(get_ctx().sut_node.crypto_device_type)
+ )
+ self.buffer_sizes = {}
+
+ for k, v in self.throughput_test_parameters.items():
+ self.buffer_sizes[k] = ListWrapper([int(run["buff_size"]) for run in v])
+
+ def _print_stats(self, test_vals: list[dict[str, int | float | str]]) -> None:
+ element_len = len("Delta Tolerance")
+ border_len = (element_len + 1) * (len(test_vals[0]))
+
+ 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}")
+
+ for test_val in test_vals:
+ for k, v in test_val.items():
+ print(f"|{v:<{element_len}}", end="")
+ print(f"|\n{'='*border_len}")
+
+ def _verify_throughput(
+ self,
+ results: list[CryptodevResults],
+ key: str,
+ ) -> list[dict[str, int | float | str]]:
+ result_list: list[dict[str, int | float | str]] = []
+
+ for result in results:
+ # get the corresponding baseline for the current buffer size
+ parameters: dict[str, int | float | str] = list(
+ filter(
+ lambda x: x["buff_size"] == result.buffer_size,
+ self.throughput_test_parameters[key],
+ )
+ )[0]
+ test_result = True
+ expected_gbps = parameters["Gbps"]
+ measured_delta = abs(
+ round((getattr(result, "gbps") - expected_gbps) / expected_gbps, 5)
+ )
+
+ # result did not meet the given Gbps parameter, check if within delta.
+ if getattr(result, "gbps") < expected_gbps:
+ if self.delta_tolerance < measured_delta:
+ test_result = False
+ result_list.append(
+ {
+ "Buffer Size": parameters["buff_size"],
+ "Gbps delta": measured_delta,
+ "delta tolerance": self.delta_tolerance,
+ "Gbps": getattr(result, "gbps"),
+ "Gbps target": expected_gbps,
+ "passed": "PASS" if test_result else "FAIL",
+ }
+ )
+ return result_list
+
+ @func_test
+ def aes_cbc(self) -> None:
+ """aes_cbc test.
+
+ Steps:
+ * Create a cryptodev instance with provided device type and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.device_type,
+ optype=OperationType.cipher_only,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aes_cbc"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ def aes_cbc_sha1(self) -> None:
+ """aes_cbc_sha1 test.
+
+ Steps:
+ * Create a cryptodev instance with provided device type and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc_sha1" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.device_type,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha1_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ auth_iv_sz=20,
+ digest_sz=12,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aes_cbc_sha1"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc_sha1")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ def aes_cbc_sha2(self) -> None:
+ """aes_cbc_sha2 test.
+
+ Steps:
+ * Create a cryptodev instance with provided device type and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc_sha2" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.device_type,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha2_256_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ digest_sz=32,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aes_cbc_sha2"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc_sha2")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def aes_cbc_sha2_digest_16(self) -> None:
+ """aes_cbc_sha2_digest_16 test.
+
+ Steps:
+ * Create a cryptodev instance with provided device type and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_cbc_sha2_digest_16" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.device_type,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha2_256_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ digest_sz=16,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aes_cbc_sha2_digest_16"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_cbc_sha2_digest_16")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def aead_aes_gcm(self) -> None:
+ """aead_aes_gcm test.
+
+ Steps:
+ * Create a cryptodev instance with provided device type and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aead_aes_gcm" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.device_type,
+ optype=OperationType.aead,
+ aead_algo=AeadAlgName.aes_gcm,
+ aead_op=EncryptDecryptSwitch.encrypt,
+ aead_key_sz=16,
+ aead_iv_sz=12,
+ aead_aad_sz=16,
+ digest_sz=16,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aead_aes_gcm"],
+ )
+ results = self._verify_throughput(app.run_app(), "aead_aes_gcm")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def aes_docsisbpi(self) -> None:
+ """aes_docsisbpi test.
+
+ Steps:
+ * Create a cryptodev instance with provided device type and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aes_docsisbpi" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.device_type,
+ optype=OperationType.cipher_only,
+ cipher_algo=CipherAlgorithm.aes_docsisbpi,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=32,
+ cipher_iv_sz=16,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aes_docsisbpi"],
+ )
+ results = self._verify_throughput(app.run_app(), "aes_docsisbpi")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @crypto_test
+ def sha1_hmac(self) -> None:
+ """sha1_hmac test.
+
+ Steps:
+ * Create a cryptodev instance with provided device type and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "sha1_hmac" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.device_type,
+ optype=OperationType.auth_only,
+ auth_algo=AuthenticationAlgorithm.sha1_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=64,
+ auth_iv_sz=16,
+ digest_sz=12,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["sha1_hmac"],
+ )
+ results = self._verify_throughput(app.run_app(), "sha1_hmac")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def snow3g_uea2_snow3g_uia2(self) -> None:
+ """snow3g_uea2_snow3g_uia2 test.
+
+ Steps:
+ * Create a cryptodev instance with provided device type and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "snow3g_uea2_snow3g_uia2" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.device_type,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.snow3g_uea2,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.snow3g_uia2,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["snow3g_uea2_snow3g_uia2"],
+ )
+ results = self._verify_throughput(app.run_app(), "snow3g_uea2_snow3g_uia2")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def zuc_eea3_zuc_eia3(self) -> None:
+ """zuc_eea3_zuc_eia3 test.
+
+ Steps:
+ * Create a cryptodev instance with provided device type and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "zuc_eea3_zuc_eia3" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.device_type,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.zuc_eea3,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.zuc_eia3,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["zuc_eea3_zuc_eia3"],
+ )
+ results = self._verify_throughput(app.run_app(), "zuc_eea3_zuc_eia3")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def kasumi_f8_kasumi_f9(self) -> None:
+ """kasumi_f8 kasumi_f9 test.
+
+ Steps:
+ * Create a cryptodev instance with provided device type and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "kasumi_f8_kasumi_f9" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ devtype=self.device_type,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.kasumi_f8,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=8,
+ auth_algo=AuthenticationAlgorithm.kasumi_f9,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["kasumi_f8_kasumi_f9"],
+ )
+ results = self._verify_throughput(app.run_app(), "kasumi_f8_kasumi_f9")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ # BEGIN VDEV TESTS
+
+
+ def aesni_mb_vdev(self) -> None:
+ """aesni_mb virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_aesni_mb and supplied buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aesni_mb_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_aesni_mb0")],
+ devtype=DeviceType.crypto_aesni_mb,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.aes_cbc,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=32,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.sha1_hmac,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aesni_mb_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(num_vfs=0), "aesni_mb_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def aesni_gcm_vdev(self):
+ """aesni_gcm virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_aesni_gcm and supplied buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "aesni_gcm_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_aesni_gcm0")],
+ devtype=DeviceType.crypto_aesni_gcm,
+ optype=OperationType.aead,
+ aead_algo=AeadAlgName.aes_gcm,
+ aead_op=EncryptDecryptSwitch.encrypt,
+ aead_key_sz=16,
+ aead_iv_sz=12,
+ aead_aad_sz=16,
+ digest_sz=16,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["aesni_gcm_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(num_vfs=0), "aesni_gcm_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def kasumi_vdev(self) -> None:
+ """Kasmumi virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_kasumi and supplied buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "kasumi_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ vdevs=[VirtualDevice("crypto_kasumi0")],
+ ptest=TestType.throughput,
+ devtype=DeviceType.crypto_kasumi,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.kasumi_f8,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=8,
+ auth_algo=AuthenticationAlgorithm.kasumi_f9,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["kasumi_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(num_vfs=0), "kasumi_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def snow3g_vdev(self) -> None:
+ """snow3g virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_snow3g and supplied buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "snow3g_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_snow3g0")],
+ devtype=DeviceType.crypto_snow3g,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.snow3g_uea2,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.snow3g_uia2,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["snow3g_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(num_vfs=0), "snow3g_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def zuc_vdev(self) -> None:
+ """Zuc virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with crypto_zuc and supplied buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "zuc_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_zuc0")],
+ devtype=DeviceType.crypto_zuc,
+ optype=OperationType.cipher_then_auth,
+ cipher_algo=CipherAlgorithm.zuc_eea3,
+ cipher_op=EncryptDecryptSwitch.encrypt,
+ cipher_key_sz=16,
+ cipher_iv_sz=16,
+ auth_algo=AuthenticationAlgorithm.zuc_eia3,
+ auth_op=AuthenticationOpMode.generate,
+ auth_key_sz=16,
+ auth_iv_sz=16,
+ digest_sz=4,
+ burst_sz=32,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["zuc_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(num_vfs=0), "zuc_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+ @func_test
+ def open_ssl_vdev(self) -> None:
+ """open_ssl virtual device test.
+
+ Steps:
+ * Create a cryptodev instance with provided device type and buffer sizes.
+ Verify:
+ * The resulting Gbps is greater than expected_gbps*(1-delta_tolerance).
+
+ Raises:
+ SkippedTestException: When configuration is not provided.
+ """
+ if "open_ssl_vdev" not in self.throughput_test_parameters:
+ raise SkippedTestException("test not configured")
+ app = Cryptodev(
+ ptest=TestType.throughput,
+ vdevs=[VirtualDevice("crypto_openssl0")],
+ devtype=DeviceType.crypto_openssl,
+ optype=OperationType.aead,
+ aead_algo=AeadAlgName.aes_gcm,
+ aead_op=EncryptDecryptSwitch.encrypt,
+ aead_key_sz=16,
+ aead_iv_sz=16,
+ aead_aad_sz=16,
+ digest_sz=16,
+ total_ops=TOTAL_OPS,
+ buffer_sz=self.buffer_sizes["open_ssl_vdev"],
+ )
+ results = self._verify_throughput(app.run_app(num_vfs=0), "open_ssl_vdev")
+ self._print_stats(results)
+ for result in results:
+ verify(
+ result["passed"] == "PASS",
+ "Gbps fell below delta tolerance",
+ )
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v5 5/5] dts: add crypto test decorator
2026-03-06 16:40 ` [PATCH v5 0/5] dts: add cryptodev testing support Andrew Bailey
` (3 preceding siblings ...)
2026-03-06 16:40 ` [PATCH v5 4/5] dts: add cryptodev throughput test suite Andrew Bailey
@ 2026-03-06 16:40 ` Andrew Bailey
2026-03-12 18:08 ` Thomas Monjalon
4 siblings, 1 reply; 41+ messages in thread
From: Andrew Bailey @ 2026-03-06 16:40 UTC (permalink / raw)
To: probb
Cc: dev, luca.vizzarro, dmarx, kai.ji, dharmikjayesh.thakkar,
david.marchand, gakhil, Andrew Bailey
Currently, a test case is decorated to signify whether to use Scapy or
TREX. This change allows test suites that do not use a traffic generator
to avoid the overhead of initializing either one.
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/configurations/test_run.example.yaml | 1 +
dts/framework/config/test_run.py | 3 +++
dts/framework/test_run.py | 2 +-
dts/framework/test_suite.py | 6 +++++
dts/tests/TestSuite_cryptodev_throughput.py | 30 +++++++++++----------
5 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/dts/configurations/test_run.example.yaml b/dts/configurations/test_run.example.yaml
index 707a3104ed..ee641f5dce 100644
--- a/dts/configurations/test_run.example.yaml
+++ b/dts/configurations/test_run.example.yaml
@@ -48,6 +48,7 @@ func_traffic_generator:
# config: "/opt/trex_config/trex_config.yaml" # Additional configuration files. (Leave blank if not required)
perf: false # disable performance testing
func: true # enable functional testing
+crypto: false # disable cryptographic testing
use_virtual_functions: false # use virtual functions (VFs) instead of physical functions
skip_smoke_tests: true # see `Optional Fields`
test_suites: # see `Optional Fields`; the following test suites will be run in their entirety
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 6c292a3675..39f2c7cdf6 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -481,6 +481,8 @@ class TestRunConfiguration(FrozenModel):
perf: bool
#: Whether to run functional tests.
func: bool
+ #: Whether to run cryptography tests.
+ crypto: bool
#: Whether to run the testing with virtual functions instead of physical functions
use_virtual_functions: bool
#: Whether to skip smoke tests.
@@ -522,6 +524,7 @@ def filter_tests(
for tt in t.test_cases
if (tt.test_type is TestCaseType.FUNCTIONAL and self.func)
or (tt.test_type is TestCaseType.PERFORMANCE and self.perf)
+ or (tt.test_type is TestCaseType.CRYPTO and self.crypto)
),
)
for t in test_suites
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 494eb40046..94dc6023a7 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -349,7 +349,7 @@ def next(self) -> State | None:
if test_run.config.use_virtual_functions:
test_run.ctx.topology.instantiate_vf_ports()
- if test_run.ctx.sut_node.cryptodevs:
+ if test_run.ctx.sut_node.cryptodevs and test_run.config.crypto:
test_run.ctx.topology.instantiate_crypto_ports()
test_run.ctx.topology.bind_cryptodevs("dpdk")
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 9c57e343ac..e86096cefe 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -174,6 +174,8 @@ def filter_test_cases(
perf_test_cases.add(test_case)
case TestCaseType.FUNCTIONAL:
func_test_cases.add(test_case)
+ case TestCaseType.CRYPTO:
+ pass
if test_case_sublist_copy:
raise ConfigurationError(
@@ -279,6 +281,8 @@ class TestCaseType(Enum):
FUNCTIONAL = auto()
#:
PERFORMANCE = auto()
+ #:
+ CRYPTO = auto()
class TestCase(TestProtocol, Protocol[TestSuiteMethodType]):
@@ -331,6 +335,8 @@ def _decorator(func: TestSuiteMethodType) -> type[TestCase]:
func_test: Callable = TestCase.make_decorator(TestCaseType.FUNCTIONAL)
#: The decorator for performance test cases.
perf_test: Callable = TestCase.make_decorator(TestCaseType.PERFORMANCE)
+#: The decorator for cryptography test cases.
+crypto_test: Callable = TestCase.make_decorator(TestCaseType.CRYPTO)
@dataclass
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index 638a52ac74..af0a5680ab 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -32,7 +32,7 @@
from api.test import verify
from framework.context import get_ctx
from framework.exception import SkippedTestException
-from framework.test_suite import BaseConfig, TestSuite, func_test
+from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
config_list: list[dict[str, int | float | str]] = [
@@ -145,7 +145,7 @@ def _verify_throughput(
)
return result_list
- @func_test
+ @crypto_test
def aes_cbc(self) -> None:
"""aes_cbc test.
@@ -175,6 +175,7 @@ def aes_cbc(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+ @crypto_test
def aes_cbc_sha1(self) -> None:
"""aes_cbc_sha1 test.
@@ -209,6 +210,7 @@ def aes_cbc_sha1(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+ @crypto_test
def aes_cbc_sha2(self) -> None:
"""aes_cbc_sha2 test.
@@ -242,7 +244,7 @@ def aes_cbc_sha2(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aes_cbc_sha2_digest_16(self) -> None:
"""aes_cbc_sha2_digest_16 test.
@@ -276,7 +278,7 @@ def aes_cbc_sha2_digest_16(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aead_aes_gcm(self) -> None:
"""aead_aes_gcm test.
@@ -308,7 +310,7 @@ def aead_aes_gcm(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aes_docsisbpi(self) -> None:
"""aes_docsisbpi test.
@@ -369,7 +371,7 @@ def sha1_hmac(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def snow3g_uea2_snow3g_uia2(self) -> None:
"""snow3g_uea2_snow3g_uia2 test.
@@ -404,7 +406,7 @@ def snow3g_uea2_snow3g_uia2(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def zuc_eea3_zuc_eia3(self) -> None:
"""zuc_eea3_zuc_eia3 test.
@@ -439,7 +441,7 @@ def zuc_eea3_zuc_eia3(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def kasumi_f8_kasumi_f9(self) -> None:
"""kasumi_f8 kasumi_f9 test.
@@ -476,7 +478,7 @@ def kasumi_f8_kasumi_f9(self) -> None:
# BEGIN VDEV TESTS
-
+ @crypto_test
def aesni_mb_vdev(self) -> None:
"""aesni_mb virtual device test.
@@ -513,7 +515,7 @@ def aesni_mb_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def aesni_gcm_vdev(self):
"""aesni_gcm virtual device test.
@@ -546,7 +548,7 @@ def aesni_gcm_vdev(self):
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def kasumi_vdev(self) -> None:
"""Kasmumi virtual device test.
@@ -582,7 +584,7 @@ def kasumi_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def snow3g_vdev(self) -> None:
"""snow3g virtual device test.
@@ -619,7 +621,7 @@ def snow3g_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def zuc_vdev(self) -> None:
"""Zuc virtual device test.
@@ -656,7 +658,7 @@ def zuc_vdev(self) -> None:
for result in results:
verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
- @func_test
+ @crypto_test
def open_ssl_vdev(self) -> None:
"""open_ssl virtual device test.
--
2.50.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [PATCH v5 3/5] dts: add cryptodev package to DTS
2026-03-06 16:40 ` [PATCH v5 3/5] dts: add cryptodev package to DTS Andrew Bailey
@ 2026-03-09 1:26 ` Patrick Robb
0 siblings, 0 replies; 41+ messages in thread
From: Patrick Robb @ 2026-03-09 1:26 UTC (permalink / raw)
To: Andrew Bailey
Cc: dev, luca.vizzarro, dmarx, kai.ji, dharmikjayesh.thakkar,
david.marchand, gakhil
> # Optional Fields:
> # `hugepages_2mb`: if removed, will use system hugepage configuration
> +# `cryptodevs`:
> +# Uncomment to run cryptodev tests; add physical cryptodev devices by their BDF
> +# `crypto_device_type`:
> +# Uncomment to run cryptodev tests; the device type of the DUT e.g. (crypto_qat, crypto_zuc,
> +# crypto_aesni_mb) the complete list of device types can be found here
> +# https://doc.dpdk.org/guides-24.07/cryptodevs/overview.html
>
Applied to next-dts with a git edit adding an additional semi colon to
the crypto_device_type to separate the 2nd and 3rd sentences.
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v5 5/5] dts: add crypto test decorator
2026-03-06 16:40 ` [PATCH v5 5/5] dts: add crypto test decorator Andrew Bailey
@ 2026-03-12 18:08 ` Thomas Monjalon
2026-03-12 18:48 ` Andrew Bailey
2026-03-12 19:54 ` Patrick Robb
0 siblings, 2 replies; 41+ messages in thread
From: Thomas Monjalon @ 2026-03-12 18:08 UTC (permalink / raw)
To: probb, Andrew Bailey
Cc: dev, luca.vizzarro, dmarx, kai.ji, dharmikjayesh.thakkar,
david.marchand, gakhil
06/03/2026 17:40, Andrew Bailey:
> Currently, a test case is decorated to signify whether to use Scapy or
> TREX. This change allows test suites that do not use a traffic generator
> to avoid the overhead of initializing either one.
[...]
> if (tt.test_type is TestCaseType.FUNCTIONAL and self.func)
> or (tt.test_type is TestCaseType.PERFORMANCE and self.perf)
> + or (tt.test_type is TestCaseType.CRYPTO and self.crypto)
That's a bit strange to read,
because a crypto test can be functional or performance.
I suppose we can re-discuss the classification of the tests.
If the only need here is about the traffic generator,
we could make it "raw input" or something like that?
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v5 5/5] dts: add crypto test decorator
2026-03-12 18:08 ` Thomas Monjalon
@ 2026-03-12 18:48 ` Andrew Bailey
2026-03-12 18:55 ` Thomas Monjalon
2026-03-12 19:54 ` Patrick Robb
1 sibling, 1 reply; 41+ messages in thread
From: Andrew Bailey @ 2026-03-12 18:48 UTC (permalink / raw)
To: Thomas Monjalon
Cc: probb, dev, luca.vizzarro, dmarx, kai.ji, dharmikjayesh.thakkar,
david.marchand, gakhil
[-- Attachment #1: Type: text/plain, Size: 819 bytes --]
On Thu, Mar 12, 2026 at 2:08 PM Thomas Monjalon <thomas@monjalon.net> wrote:
> That's a bit strange to read,
> because a crypto test can be functional or performance.
> I suppose we can re-discuss the classification of the tests.
> If the only need here is about the traffic generator,
> we could make it "raw input" or something like that?
>
I agree that "crypto" is not exactly a true description of the
purpose for this test type. It is probably better for the test type
to explicitly describe it's purpose. maybe NO_GEN/NO_GENERATOR
or NO_TG. It just so happens with this series that crypto tests are the
only cases with this type, which was the cause for the initial name
but should change in the future. Furthermore, we could divide this
into performance and functional versions of this flag.
[-- Attachment #2: Type: text/html, Size: 1208 bytes --]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v5 5/5] dts: add crypto test decorator
2026-03-12 18:48 ` Andrew Bailey
@ 2026-03-12 18:55 ` Thomas Monjalon
0 siblings, 0 replies; 41+ messages in thread
From: Thomas Monjalon @ 2026-03-12 18:55 UTC (permalink / raw)
To: Andrew Bailey
Cc: probb, dev, luca.vizzarro, dmarx, kai.ji, dharmikjayesh.thakkar,
david.marchand, gakhil
12/03/2026 19:48, Andrew Bailey:
> On Thu, Mar 12, 2026 at 2:08 PM Thomas Monjalon <thomas@monjalon.net> wrote:
>
> > That's a bit strange to read,
> > because a crypto test can be functional or performance.
> > I suppose we can re-discuss the classification of the tests.
> > If the only need here is about the traffic generator,
> > we could make it "raw input" or something like that?
> >
>
> I agree that "crypto" is not exactly a true description of the
> purpose for this test type. It is probably better for the test type
> to explicitly describe it's purpose. maybe NO_GEN/NO_GENERATOR
> or NO_TG. It just so happens with this series that crypto tests are the
> only cases with this type, which was the cause for the initial name
> but should change in the future. Furthermore, we could divide this
> into performance and functional versions of this flag.
Yes I think we need to consider the level of abstraction required here,
and the cases we want to cover.
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v5 5/5] dts: add crypto test decorator
2026-03-12 18:08 ` Thomas Monjalon
2026-03-12 18:48 ` Andrew Bailey
@ 2026-03-12 19:54 ` Patrick Robb
2026-03-12 20:29 ` Thomas Monjalon
1 sibling, 1 reply; 41+ messages in thread
From: Patrick Robb @ 2026-03-12 19:54 UTC (permalink / raw)
To: Thomas Monjalon
Cc: Andrew Bailey, dev, luca.vizzarro, dmarx, kai.ji,
dharmikjayesh.thakkar, david.marchand, gakhil
On Thu, Mar 12, 2026 at 2:08 PM Thomas Monjalon <thomas@monjalon.net> wrote:
>
> 06/03/2026 17:40, Andrew Bailey:
> > Currently, a test case is decorated to signify whether to use Scapy or
> > TREX. This change allows test suites that do not use a traffic generator
> > to avoid the overhead of initializing either one.
> [...]
> > if (tt.test_type is TestCaseType.FUNCTIONAL and self.func)
> > or (tt.test_type is TestCaseType.PERFORMANCE and self.perf)
> > + or (tt.test_type is TestCaseType.CRYPTO and self.crypto)
>
> That's a bit strange to read,
> because a crypto test can be functional or performance.
> I suppose we can re-discuss the classification of the tests.
> If the only need here is about the traffic generator,
In my opinion this is not quite true. There is another (and more
important) need associated with the test categories, which is allowing
users to run only a sub-category of tests according to their interest
or hardware setup. I.e. we can expect some people to show up and
desire only to run functional testcases (perhaps they have a minimal
hardware setup which is not good for performance, or they are only
working on resolving functional issues) and the test type categories
allow the users this flexibility with regards to what kinds of DTS
tests are run.
> we could make it "raw input" or something like that?
>
>
In any case I agree with Thomas and Andrew that the situation with
regards to test type classification/organization could use a review
and a tweak in the 26.07 release. Right now every single crypto_test
is a performance test (i.e. it runs a crypto workload, compares the
Gbps output against the Gbps baseline the user set in
tests_config.yaml for each testcast). But, some open questions for the
future:
1. Is there a need for users to select only functional crypto cases vs
only perf crypto cases? Or we can just leave all crypto cases in 1
bucket?
2. If the above is true, should we switch the testcase decorators over to:
a. ethdev_func
b. ethdev_perf
c. cryptodev_func
d. cryptodev_perf
and every testcase gets one of these decorators
OR
We apply multiple decorators to each testcase (i.e. add both
crypto_test and func_test to a testcase) and have the framework code
read the whole set of testcase type decorators and setup accordingly.
Thomas, is what we have right now in terms of testcase classification
okay for the 26.03 release, and we can tweak it in 26.07, or do you
want us to do something in the immediate term?
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v5 5/5] dts: add crypto test decorator
2026-03-12 19:54 ` Patrick Robb
@ 2026-03-12 20:29 ` Thomas Monjalon
0 siblings, 0 replies; 41+ messages in thread
From: Thomas Monjalon @ 2026-03-12 20:29 UTC (permalink / raw)
To: Patrick Robb
Cc: Andrew Bailey, dev, luca.vizzarro, dmarx, kai.ji,
dharmikjayesh.thakkar, david.marchand, gakhil
12/03/2026 20:54, Patrick Robb:
> On Thu, Mar 12, 2026 at 2:08 PM Thomas Monjalon <thomas@monjalon.net> wrote:
> >
> > 06/03/2026 17:40, Andrew Bailey:
> > > Currently, a test case is decorated to signify whether to use Scapy or
> > > TREX. This change allows test suites that do not use a traffic generator
> > > to avoid the overhead of initializing either one.
> > [...]
> > > if (tt.test_type is TestCaseType.FUNCTIONAL and self.func)
> > > or (tt.test_type is TestCaseType.PERFORMANCE and self.perf)
> > > + or (tt.test_type is TestCaseType.CRYPTO and self.crypto)
> >
> > That's a bit strange to read,
> > because a crypto test can be functional or performance.
> > I suppose we can re-discuss the classification of the tests.
> > If the only need here is about the traffic generator,
>
> In my opinion this is not quite true. There is another (and more
> important) need associated with the test categories, which is allowing
> users to run only a sub-category of tests according to their interest
> or hardware setup. I.e. we can expect some people to show up and
> desire only to run functional testcases (perhaps they have a minimal
> hardware setup which is not good for performance, or they are only
> working on resolving functional issues) and the test type categories
> allow the users this flexibility with regards to what kinds of DTS
> tests are run.
>
> > we could make it "raw input" or something like that?
> >
> >
>
> In any case I agree with Thomas and Andrew that the situation with
> regards to test type classification/organization could use a review
> and a tweak in the 26.07 release. Right now every single crypto_test
> is a performance test (i.e. it runs a crypto workload, compares the
> Gbps output against the Gbps baseline the user set in
> tests_config.yaml for each testcast). But, some open questions for the
> future:
>
> 1. Is there a need for users to select only functional crypto cases vs
> only perf crypto cases? Or we can just leave all crypto cases in 1
> bucket?
> 2. If the above is true, should we switch the testcase decorators over to:
>
> a. ethdev_func
> b. ethdev_perf
> c. cryptodev_func
> d. cryptodev_perf
>
> and every testcase gets one of these decorators
>
> OR
>
> We apply multiple decorators to each testcase (i.e. add both
> crypto_test and func_test to a testcase) and have the framework code
> read the whole set of testcase type decorators and setup accordingly.
>
> Thomas, is what we have right now in terms of testcase classification
> okay for the 26.03 release, and we can tweak it in 26.07, or do you
> want us to do something in the immediate term?
There is no urgency.
I'm just opening the discussion for future.
^ permalink raw reply [flat|nested] 41+ messages in thread
end of thread, other threads:[~2026-03-12 20:29 UTC | newest]
Thread overview: 41+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-10 19:34 [PATCH v2 0/5] dts: add cryptodev testing support Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 1/5] dts: add find float method to text parser Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 2/5] dts: add cryptodev package to DTS Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 3/5] dts: add cryptodev throughput test suite Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 4/5] dts: add crypto test decorator Andrew Bailey
2026-02-10 19:34 ` [PATCH v2 5/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
2026-02-13 19:35 ` [PATCH v3 0/5] dts: add cryptodev testing support Andrew Bailey
2026-02-13 19:35 ` [PATCH v3 1/5] dts: add find float method to text parser Andrew Bailey
2026-02-25 21:05 ` Patrick Robb
2026-02-13 19:35 ` [PATCH v3 2/5] dts: add cryptodev package to DTS Andrew Bailey
2026-02-25 21:06 ` Patrick Robb
2026-02-26 19:13 ` Andrew Bailey
2026-02-27 16:15 ` Patrick Robb
2026-02-13 19:35 ` [PATCH v3 3/5] dts: add cryptodev throughput test suite Andrew Bailey
2026-02-27 18:24 ` Patrick Robb
2026-02-27 19:43 ` Andrew Bailey
2026-02-13 19:35 ` [PATCH v3 4/5] dts: add crypto test decorator Andrew Bailey
2026-02-27 18:28 ` Patrick Robb
2026-02-27 19:18 ` Andrew Bailey
2026-02-13 19:35 ` [PATCH v3 5/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
2026-02-27 18:33 ` Patrick Robb
2026-03-02 18:54 ` [PATCH v4 0/5] dts: add cryptodev testing support Andrew Bailey
2026-03-02 18:54 ` [PATCH v4 1/5] dts: add find float method to text parser Andrew Bailey
2026-03-02 18:54 ` [PATCH v4 2/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
2026-03-02 18:54 ` [PATCH v4 3/5] dts: add cryptodev package to DTS Andrew Bailey
2026-03-02 18:54 ` [PATCH v4 4/5] dts: add cryptodev throughput test suite Andrew Bailey
2026-03-05 19:26 ` Patrick Robb
2026-03-05 20:20 ` Patrick Robb
2026-03-02 18:54 ` [PATCH v4 5/5] dts: add crypto test decorator Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 0/5] dts: add cryptodev testing support Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 1/5] dts: add find float method to text parser Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 2/5] dts: automate VFIO-PCI modprobe in node setup Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 3/5] dts: add cryptodev package to DTS Andrew Bailey
2026-03-09 1:26 ` Patrick Robb
2026-03-06 16:40 ` [PATCH v5 4/5] dts: add cryptodev throughput test suite Andrew Bailey
2026-03-06 16:40 ` [PATCH v5 5/5] dts: add crypto test decorator Andrew Bailey
2026-03-12 18:08 ` Thomas Monjalon
2026-03-12 18:48 ` Andrew Bailey
2026-03-12 18:55 ` Thomas Monjalon
2026-03-12 19:54 ` Patrick Robb
2026-03-12 20:29 ` Thomas Monjalon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox