Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ interfaces = owner.get_interfaces()
```
## Exceptions raised by MFD-Network-Adapter module
- related to module: `NetworkAdapterModuleException`
- related to Network Interface: `InterfaceNameNotFound`, `IPException`, `IPAddressesNotFound`, `NetworkQueuesException`, `RDMADeviceNotFound`, `NumaNodeException`, `DriverInfoNotFound`, `FirmwareVersionNotFound`
- related to Network Interface: `InterfaceNameNotFound`, `IPException`, `IPAddressesNotFound`, `NetworkQueuesException`, `RDMADeviceNotFound`, `NumaNodeException`, `DriverInfoNotFound`, `FirmwareVersionNotFound`, `VirtualFunctionNotFoundException`, `HypervisorNotSupportedException`, `NetworkAdapterConfigurationException`, `NetworkInterfaceNotSupported`
- related to NetworkInterface's features: `VirtualizationFeatureException`
-
## Classes
Expand Down Expand Up @@ -650,6 +650,11 @@ Value of param will be prepared for update for all interfaces using <driver_name
prepare_values_sharing_same_driver(*, driver_name: str, param: str, value: int) -> str
```

[Linux] Enable or disable SRIOV drivers auto probe.
```python
set_sriov_driver_autoprobe(self, state: bool) -> None
```

[Windows]

`change_state_family_interfaces(*, driver_filename: str, enable: State.ENABLED) -> None`
Expand Down Expand Up @@ -927,6 +932,16 @@ verify_vmdq(interface: "NetworkInterface", desired_value: int) -> None
get_vm_vf_ids(self, vm_name: str, interface: "ESXiNetworkInterface") -> list[int]
```

[Linux] Set number of MSI-X vectors for PF interface.
```python
set_msix_vectors_count(self, count: int, method: MethodType = MethodType.DEVLINK) -> None
```

[Linux] Get number of MSI-X vectors of PF interface.
```python
get_msix_vectors_count(self, method: MethodType = MethodType.DEVLINK) -> int
```

### Queue

[L] Get number of queues from proc interrupts
Expand Down Expand Up @@ -1259,6 +1274,8 @@ class NetworkInterface(ABC):

- `get_number_of_ports() -> int'` - Get number of ports in tested adapter.

- `reload_adapter_devlink() -> None` - Reload adapter via devlink.

- `restart() -> None` - Restart interface.

#### Additional methods - Linux
Expand Down Expand Up @@ -2032,6 +2049,18 @@ get_queues_for_rss_engine(self) -> dict[str, list[str]]
get_netq_defq_rss_queues(self, netq_rss: bool) -> list
```

[Linux] Set RSS queues count.

```python
set_rss_queues_count(self, count: int, vf_pci_address: PCIAddress | None = None) -> None
```

[Linux] Get RSS queues count.

```python
get_rss_queues_count(self, vf_pci_address: PCIAddress | None = None) -> int
```

#### Stats


Expand Down Expand Up @@ -2258,6 +2287,7 @@ Virtualization related functionalities.
- `set_link_for_vf(vf_id: int, link_state: LinkState) -> None` - Set link for a VF interface.
- `set_vlan_for_vf(vf_id: int, vlan_id: int, proto: VlanProto) -> None` - Set port VLAN for a VF interface
- `set_mac_for_vf(vf_id: int, mac: MACAddress) -> None` - Set MAC address for VF interface.
- `get_vf_id_by_pci(vf_pci_address: PCIAddress) -> int` - Get VF ID based on PCI Address.
- `get_max_vfs() -> int` - Get maximal number of VFs per interface based on either name or PCI Address (if name not set on the interface).
- `get_current_vfs() -> int` - Get current number of VFs per interface based on either name or PCI Address (if name not set on the interface).
- `get_designed_number_vfs() -> tuple[int, int]` - Get designed max number of VFs, total and per PF.
Expand Down
16 changes: 16 additions & 0 deletions mfd_network_adapter/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,19 @@ class NetworkInterfaceIncomparableObject(Exception):

class VirtualFunctionCreationException(Exception):
"""Exception raised when VF creation process fails."""


class VirtualFunctionNotFoundException(Exception):
"""Exception raised when VF is not found after creation."""


class HypervisorNotSupportedException(Exception):
"""Exception raised when the hypervisor is not supported."""


class NetworkAdapterConfigurationException(Exception):
"""Exception raised for errors in network adapter configuration."""


class NetworkInterfaceNotSupported(Exception):
"""Exception raised when the operation called on network interface is not supported."""
32 changes: 32 additions & 0 deletions mfd_network_adapter/network_interface/feature/driver/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
# SPDX-License-Identifier: MIT
"""Module for Driver feature for Linux."""

import logging
import re
from typing import Dict, TYPE_CHECKING

from mfd_common_libs import add_logging_level, log_levels
from mfd_const import Speed
from mfd_ethtool import Ethtool
from mfd_typing.network_interface import InterfaceType

from mfd_network_adapter.exceptions import NetworkInterfaceNotSupported, NetworkAdapterConfigurationException
from .base import BaseFeatureDriver
from ...exceptions import DriverInfoNotFound

Expand All @@ -16,6 +20,9 @@
from mfd_network_adapter import NetworkInterface
from mfd_typing.driver_info import DriverInfo

logger = logging.getLogger(__name__)
add_logging_level(level_name="MODULE_DEBUG", level_value=log_levels.MODULE_DEBUG)


class LinuxDriver(BaseFeatureDriver):
"""Linux class for Driver feature."""
Expand Down Expand Up @@ -67,3 +74,28 @@ def get_formatted_driver_version(self) -> Dict:

return ver_dict
raise DriverInfoNotFound(f"Driver version not available for {self._interface().name}")

def set_sriov_drivers_autoprobe(self, state: bool) -> None:
"""
Enable or disable SRIOV drivers auto probe.

:param state: State to set (True or False)
"""
interface = self._interface()
if interface.interface_type is not InterfaceType.PF:
raise NetworkInterfaceNotSupported(
"Enabling/disabling SRIOV drivers auto probe is only supported on PF interface."
)
status = "enabled" if state else "disabled"
logger.log(
level=log_levels.MFD_DEBUG,
msg=f"{status} sriov_drivers_autoprobe on interface {interface.name}",
)
self._connection.execute_command(
f"echo {int(state)} > /sys/class/net/{interface.name}/device/sriov_drivers_autoprobe",
custom_exception=NetworkAdapterConfigurationException,
)
logger.log(
level=log_levels.MFD_INFO,
msg=f"Successfully {status} sriov_drivers_autoprobe on interface {interface.name}",
)
55 changes: 55 additions & 0 deletions mfd_network_adapter/network_interface/feature/rss/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
from mfd_common_libs import add_logging_level, log_levels
from mfd_ethtool import Ethtool
from mfd_ethtool.exceptions import EthtoolExecutionError
from mfd_typing import PCIAddress
from mfd_typing.network_interface import InterfaceType

from mfd_network_adapter.data_structures import State
from mfd_network_adapter.exceptions import NetworkInterfaceNotSupported, NetworkAdapterConfigurationException
from mfd_network_adapter.stat_checker.base import Trend

from .base import BaseFeatureRSS
Expand Down Expand Up @@ -285,3 +289,54 @@ def validate_statistics(self, traffic_duration: int = 30) -> None:
# in ethtool then get_stats will throw a RuntimeError. That is expected behavior.
# If it does exist, it should be zero.
logger.log(level=log_levels.MODULE_DEBUG, msg=f"Not found {stat_name} statistic, it's expected.")

def set_rss_queues_count(self, count: int, vf_pci_address: PCIAddress | None = None) -> None:
"""
Set number of RSS queues for the given interface.

:param vf_pci_address: PCI address of VF to set RSS queues count for. If not provided, sets for PF.
:param count: Number of RSS queues to set
"""
interface = self._interface()
if interface.interface_type is not InterfaceType.PF:
raise NetworkInterfaceNotSupported("Setting RSS queues count on VF is only supported through PF interface.")
logger.log(
level=log_levels.MFD_DEBUG,
msg=f"Setting RSS queues count to {count} for {'VF' if vf_pci_address else interface.name} interface.",
)
vf_path = ""
if vf_pci_address:
vf_num = str(interface.virtualization.get_vf_id_by_pci(vf_pci_address))
vf_path = f"virtfn{vf_num}/"
self._connection.execute_command(
f"echo {count} > /sys/class/net/{interface.name}/device/{vf_path}rss_lut_pf_attr",
custom_exception=NetworkAdapterConfigurationException,
)
logger.log(
level=log_levels.MFD_INFO,
msg=f"Successfully set RSS queues count on interface {interface.name} to {count}",
)

def get_rss_queues_count(self, vf_pci_address: PCIAddress | None = None) -> int:
"""
Get number of RSS queues of the given interface.

:param vf_pci_address: PCI address of VF to get RSS queues count for. If not provided, gets for PF.
:return: Number of RSS queues
"""
interface = self._interface()
if interface.interface_type is not InterfaceType.PF:
raise NetworkInterfaceNotSupported("Getting RSS queues count on VF is only supported through PF interface.")
logger.log(
level=log_levels.MFD_DEBUG,
msg=f"Retrieving RSS queues count of {'VF' if vf_pci_address else interface.name} interface.",
)
vf_path = ""
if vf_pci_address:
vf_num = str(interface.virtualization.get_vf_id_by_pci(vf_pci_address))
vf_path = f"virtfn{vf_num}/"
out = self._connection.execute_command(
f"cat /sys/class/net/{interface.name}/device/{vf_path}rss_lut_pf_attr",
expected_return_codes={0},
).stdout
return int(out)
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,11 @@ class VFInfo:
vf_id: str
pci_address: PCIAddress
owner_world_id: str


@dataclass
class MethodType:
"""Class for method types."""

DEVLINK: str = "devlink"
SYSFS: str = "sysfs"
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@
from typing import List

from mfd_common_libs import add_logging_level, log_levels
from mfd_typing import MACAddress, DeviceID, SubDeviceID
from mfd_typing.network_interface import InterfaceType
from mfd_const.network import DESIGNED_NUMBER_VFS_BY_SPEED, Speed
from mfd_typing import MACAddress, DeviceID, SubDeviceID, PCIAddress
from mfd_typing.network_interface import InterfaceType

from mfd_network_adapter.data_structures import State
from mfd_network_adapter.exceptions import (
VirtualFunctionNotFoundException,
NetworkAdapterConfigurationException,
NetworkInterfaceNotSupported,
)
from .base import BaseFeatureVirtualization
from .data_structures import MethodType
from ...data_structures import VlanProto, VFDetail, LinkState
from ...exceptions import VirtualizationFeatureException, VirtualizationWrongInterfaceException, DeviceSetupException

Expand Down Expand Up @@ -338,3 +345,89 @@ def set_mac_for_vf(self, vf_id: int, mac: MACAddress) -> None:
self._raise_error_if_not_supported_type()
cmd = f"ip link set {self._interface().name} vf {vf_id} mac {mac}"
self._connection.execute_command(command=cmd, custom_exception=VirtualizationFeatureException)

def get_vf_id_by_pci(self, vf_pci_address: PCIAddress) -> int:
"""
Get ID of VF with the given PCI address on specific PF PCI address using /sys/bus/pci/devices/pci_address.

:param vf_pci_address: VF interface PCI address.
:return: ID of the VF.
"""
result = self._connection.execute_command(
f"ls /sys/bus/pci/devices/{self._interface().pci_address}/virtfn* -la",
shell=True,
expected_return_codes={0, 1},
)
if result.return_code != 0:
raise VirtualFunctionNotFoundException(
f"Failed to list VFs for PF PCI Address {self._interface().pci_address}: {result.stderr.strip()}"
)
vf_number_regex = rf"^.*devices/{self._interface().pci_address}/virtfn(?P<vf_number>\d+).*->.*{vf_pci_address}$"
match = re.search(vf_number_regex, result.stdout, re.M)
if match:
return int(match.group("vf_number"))
else:
raise VirtualFunctionNotFoundException(f"0 matched VFs for PF PCI Address {self._interface().pci_address}")

def get_msix_vectors_count(self, method: MethodType = MethodType.DEVLINK) -> int:
"""
Get number of MSI-X vectors for the given interface.

:param method: Method to use for setting MSI-X vectors count. Options are "devlink" or "sysfs".
:return: Number of MSI-X vectors available for the interface.
"""
interface = self._interface()
if interface.interface_type is not InterfaceType.PF:
raise NetworkInterfaceNotSupported("Getting MSI-X vector count is only supported on PF interface.")
logger.log(level=log_levels.MFD_DEBUG, msg=f"Getting MSI-X vectors count for interface {interface.name}")
if method == MethodType.DEVLINK:
out = self._connection.execute_command(f"devlink resource show pci/{interface.pci_address}").stdout
match = re.search(r"name msix_vf size (\d+) ", out)
if match:
logger.log(
level=log_levels.MFD_INFO,
msg=f"MSI-X vectors count for interface {interface.name}: {match.group(1)}",
)
return int(match.group(1))
else:
raise NetworkAdapterConfigurationException(
f"Could not find MSI-X vectors count for interface {interface.name}"
)

if method == MethodType.SYSFS:
out = self._connection.execute_command(
f"cat /sys/bus/pci/devices/{self._interface().pci_address}/sriov_vf_msix_count"
).stdout
if out:
logger.log(level=log_levels.MFD_INFO, msg=f"MSI-X vectors count for interface {interface.name}: {out}")
return int(out)
else:
raise NetworkAdapterConfigurationException(
f"Could not find MSI-X vectors count for interface {interface.name}"
)

raise ValueError(f"Unknown method {method} for getting MSI-X vectors count")

def set_msix_vectors_count(self, count: int, method: MethodType = MethodType.DEVLINK) -> None:
"""
Set number of MSI-X vectors for the given interface.

:param count: Number of MSI-X vectors to set
:param method: Method to use for setting MSI-X vectors count. Options are "devlink" or "sysfs".
"""
interface = self._interface()
if interface.interface_type is not InterfaceType.PF:
raise NetworkInterfaceNotSupported(
"Setting MSI-X vector count on VF is only supported through PF interface."
)
logger.log(
level=log_levels.MFD_DEBUG, msg=f"Setting MSI-X vectors count to {count} for interface {interface.name}"
)
if method == MethodType.DEVLINK:
command = f"devlink resource set pci/{interface.pci_address} path /msix/msix_vf/ size {count}"
elif method == MethodType.SYSFS:
command = f"echo {count} > /sys/bus/pci/devices/{interface.pci_address}/sriov_vf_msix_count"
else:
raise ValueError(f"Unknown method {method} for setting MSI-X vectors count")
self._connection.execute_command(command, custom_exception=NetworkAdapterConfigurationException)
logger.log(level=log_levels.MFD_INFO, msg=f"MSI-X vectors count set to {count} for interface {interface.name}")
12 changes: 12 additions & 0 deletions mfd_network_adapter/network_interface/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import Dict, Optional, TYPE_CHECKING, Union

from mfd_common_libs import add_logging_level, log_levels
from mfd_connect.base import ConnectionCompletedProcess
from mfd_connect.exceptions import ConnectionCalledProcessError
from mfd_kernel_namespace import add_namespace_call_command
from mfd_typing import MACAddress
Expand Down Expand Up @@ -317,6 +318,17 @@ def get_number_of_ports(self) -> int:
else:
raise DeviceSetupException("Can't find number of ports in tested adapter.")

def reload_adapter_devlink(self) -> ConnectionCompletedProcess:
"""
Reload adapter using devlink.

:return: ConnectionCompletedProcess
"""
logger.log(level=log_levels.MFD_DEBUG, msg=f"Reloading adapter {self.name} using devlink")
return self._connection.execute_command(
f"devlink dev reload pci/{self.pci_address}", expected_return_codes={0, 1}
)

def restart(self) -> None:
"""Restart interface."""
raise NotImplementedError
Loading