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 <[email protected]> --- dts/api/cryptodev/__init__.py | 132 +++++ dts/api/cryptodev/config.py | 484 +++++++++++++++++++ dts/api/cryptodev/types.py | 185 +++++++ dts/framework/config/node.py | 2 + dts/framework/params/types.py | 65 +++ dts/framework/remote_session/dpdk_shell.py | 5 +- dts/framework/test_run.py | 5 + dts/framework/test_suite.py | 2 + dts/framework/testbed_model/linux_session.py | 91 +++- dts/framework/testbed_model/node.py | 4 + dts/framework/testbed_model/os_session.py | 34 ++ dts/framework/testbed_model/topology.py | 90 +++- 12 files changed, 1095 insertions(+), 4 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..d13fcc4a53 --- /dev/null +++ b/dts/api/cryptodev/__init__.py @@ -0,0 +1,132 @@ +# 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. + """ + allowed_ports = get_ctx().topology.get_crypto_vfs(numvfs) + print(f"ALLOWED PORTS: {allowed_ports}") + 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']}" + ) + 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..8b1a293018 --- /dev/null +++ b/dts/api/cryptodev/config.py @@ -0,0 +1,484 @@ +# 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() + + +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..cc1ba9b664 100644 --- a/dts/framework/config/node.py +++ b/dts/framework/config/node.py @@ -70,6 +70,8 @@ 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) @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..ada628c59e 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 and test_run.config.crypto: + 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/test_suite.py b/dts/framework/test_suite.py index 9c57e343ac..856dae0a85 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( diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py index 711d4d97c3..fb6081ab37 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 @@ -193,7 +194,8 @@ def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None: verify=True, ) - del self._lshw_net_info + if self._lshw_net_info: + del self._lshw_net_info def bring_up_link(self, ports: Iterable[Port]) -> None: """Overrides :meth:`~.os_session.OSSession.bring_up_link`.""" @@ -223,6 +225,57 @@ 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`. + + 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.send_command(f"modprobe {driver}", privileged=True) + self.refresh_lshw() + def create_vfs(self, pf_port: Port) -> None: """Overrides :meth:`~.os_session.OSSession.create_vfs`. @@ -239,6 +292,28 @@ 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( + ":", "\\:" + ) + try: + 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") + except ValueError: + pass + 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 +325,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..a4c1f8f22c 100644 --- a/dts/framework/testbed_model/node.py +++ b/dts/framework/testbed_model/node.py @@ -54,6 +54,7 @@ class Node: arch: Architecture lcores: list[LogicalCore] ports: list[Port] + cryptodevs: list[Port] _logger: DTSLogger _other_sessions: list[OSSession] _node_info: OSSessionInfo | None @@ -82,6 +83,9 @@ 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._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..4a4fc1f34a 100644 --- a/dts/framework/testbed_model/os_session.py +++ b/dts/framework/testbed_model/os_session.py @@ -615,6 +615,18 @@ 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'. + + 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 +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`. @@ -656,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..10805f8b48 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,40 @@ 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 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 +182,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 +254,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 +313,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

