public inbox for igt-dev@lists.freedesktop.org
 help / color / mirror / Atom feed
From: "Bernatowicz, Marcin" <marcin.bernatowicz@linux.intel.com>
To: Adam Miszczak <adam.miszczak@linux.intel.com>,
	igt-dev@lists.freedesktop.org
Cc: kamil.konieczny@linux.intel.com
Subject: Re: [PATCH i-g-t 07/10] tools/vmtb: Introduce VirtualDevice class
Date: Tue, 10 Mar 2026 11:45:07 +0100	[thread overview]
Message-ID: <265e75e3-0f4d-469d-af04-b3dffb8788d3@linux.intel.com> (raw)
In-Reply-To: <20260224075027.2409675-8-adam.miszczak@linux.intel.com>


On 2/24/2026 8:50 AM, Adam Miszczak wrote:
> VirtualDevice represents GPU device enumerated on VM/Guest OS
> (composited into VirtualMachine class).
> Implement methods common of all devices like bind/unbind driver,
> number of GTs as well as VF specific getter of assigned resources
> read from debugfs self_config file.
> Similarly to physical Device, PciInfo instance with PCI BDF,
> Device ID and minor device is embedded as inner class.
>
> VirtualDevice needs to keep a reference to the VirtualMachine
> (and pass it to VfDriver) to access Guest OS via QEMU Guest-Agent
> running on VM level.
>
> Additionally:
> - add dir_list function to a common MachineInterface
>    (used currently by VirtualDevice)
> - remove physical device specific functions:
>    create/remove VF from a base DeviceInterface class.
> - adjust debugfs/self_config reader
>
> Signed-off-by: Adam Miszczak <adam.miszczak@linux.intel.com>
> ---
>   tools/vmtb/bench/machines/device_interface.py |  14 +-
>   tools/vmtb/bench/machines/host.py             |   5 +
>   .../vmtb/bench/machines/machine_interface.py  |   6 +-
>   tools/vmtb/bench/machines/virtual/device.py   | 179 ++++++++++++++++++
>   tools/vmtb/bench/machines/virtual/vm.py       |   5 +
>   5 files changed, 198 insertions(+), 11 deletions(-)
>   create mode 100644 tools/vmtb/bench/machines/virtual/device.py
>
> diff --git a/tools/vmtb/bench/machines/device_interface.py b/tools/vmtb/bench/machines/device_interface.py
> index e8d4068e8..0eff493f0 100644
> --- a/tools/vmtb/bench/machines/device_interface.py
> +++ b/tools/vmtb/bench/machines/device_interface.py
> @@ -1,19 +1,13 @@
>   # SPDX-License-Identifier: MIT
> -# Copyright © 2024 Intel Corporation
> +# Copyright © 2024-2026 Intel Corporation
>   
>   import abc
>   
>   
>   class DeviceInterface(abc.ABC):
> -
> -    @abc.abstractmethod
> -    def create_vf(self, num: int) -> int:
> -        raise NotImplementedError
> -
> -    @abc.abstractmethod
> -    def remove_vfs(self) -> int:
> -        raise NotImplementedError
> -
> +    """Base class for devices (Physical and Virtual).
> +    Provide common operations for all devices like bind/unbind driver.
> +    """
>       @abc.abstractmethod
>       def bind_driver(self) -> None:
>           raise NotImplementedError
> diff --git a/tools/vmtb/bench/machines/host.py b/tools/vmtb/bench/machines/host.py
> index aecc7709a..b6cfb62ef 100644
> --- a/tools/vmtb/bench/machines/host.py
> +++ b/tools/vmtb/bench/machines/host.py
> @@ -99,6 +99,11 @@ class Host(MachineInterface):
>       def dir_exists(self, path: str) -> bool:
>           return Path(path).is_dir()
>   
> +    def dir_list(self, path: str) -> typing.List[str]:
> +        # TODO: implement, currently no-op to fulfill MachineInterface requirement
> +        logger.warning("Host.dir_list() is not implemented yet!")
> +        return []
> +
>       def get_drm_driver_name(self) -> str:
>           # Used as a part of MachineInterface for helpers
>           return self.drm_driver_name
> diff --git a/tools/vmtb/bench/machines/machine_interface.py b/tools/vmtb/bench/machines/machine_interface.py
> index 8daa2cda3..d67c3db74 100644
> --- a/tools/vmtb/bench/machines/machine_interface.py
> +++ b/tools/vmtb/bench/machines/machine_interface.py
> @@ -1,5 +1,5 @@
>   # SPDX-License-Identifier: MIT
> -# Copyright © 2024 Intel Corporation
> +# Copyright © 2024-2026 Intel Corporation
>   
>   import abc
>   import enum
> @@ -56,6 +56,10 @@ class MachineInterface(metaclass=abc.ABCMeta):
>       def dir_exists(self, path: str) -> bool:
>           raise NotImplementedError
>   
> +    @abc.abstractmethod
> +    def dir_list(self, path: str) -> typing.List[str]:
> +        raise NotImplementedError
> +
>       @abc.abstractmethod
>       def get_drm_driver_name(self) -> str:
>           raise NotImplementedError
> diff --git a/tools/vmtb/bench/machines/virtual/device.py b/tools/vmtb/bench/machines/virtual/device.py
> new file mode 100644
> index 000000000..c838db59d
> --- /dev/null
> +++ b/tools/vmtb/bench/machines/virtual/device.py
> @@ -0,0 +1,179 @@
> +# SPDX-License-Identifier: MIT
> +# Copyright © 2025-2026 Intel Corporation
> +
> +import logging
> +import re
> +import typing
> +
> +from bench import exceptions
> +from bench.configurators import pci
> +from bench.drivers.driver_interface import DriverInterface, VfDriver
> +from bench.machines.device_interface import DeviceInterface
> +from bench.machines.machine_interface import MachineInterface
> +
> +logger = logging.getLogger('VirtualDevice')
> +
> +
> +class VirtualDevice(DeviceInterface):
> +    class PciInfo:
> +        def __init__(self, bdf: str, vm: MachineInterface) -> None:
> +            self.bdf: str = bdf
> +            self.vm: MachineInterface = vm
> +            self.devid: str = self.get_device_id(self.bdf)
> +            self.minor_number: int = self.get_device_minor_number(self.bdf)
> +
> +        def get_device_minor_number(self, bdf: str) -> int:
> +            drm_dir = f'/sys/bus/pci/devices/{bdf}/drm'
> +
> +            for dir_name in self.vm.dir_list(drm_dir):
> +                if 'card' in dir_name:
> +                    index_match = re.match(r'card(?P<card_index>\d+)', dir_name)
> +                    if index_match:
> +                        return int(index_match.group('card_index'))
> +
> +            logger.error("Could not determine minor number (card index) for device %s", bdf)
> +            raise exceptions.HostError(f'Could not determine minor number (card index) for device {bdf}')
> +
> +        def get_device_id(self, bdf: str) -> str:
> +            device_file = f'/sys/bus/pci/devices/{bdf}/device'
> +            devid = self.vm.read_file_content(device_file)
> +
> +            return devid.strip()[2:] # Strip whitespaces and 0x
> +
> +    def __init__(self, bdf: str, vm: MachineInterface) -> None:
> +        self.pci_info = self.PciInfo(bdf, vm)
> +        self.gpu_model = pci.get_gpu_model(self.pci_info.devid)
> +        # Reference to VirtualMachine - required for Guest-Agent use (e.g. guest file read/write):
> +        self.vm: MachineInterface = vm
> +        self.driver: DriverInterface = VfDriver(self.pci_info.bdf, self.vm)
> +
> +        # Resources provisioned to the VF/VM:
> +        self._lmem_size: typing.Optional[int] = None
> +        self._ggtt_size: typing.Optional[int] = None
> +        self._contexts: typing.Optional[int] = None
> +        self._doorbells: typing.Optional[int] = None
> +        # Tile mask is relevant for multi-tile devices:
> +        self._tile_mask: typing.Optional[int] = None
> +
> +    def __str__(self) -> str:
> +        return f'VirtualDevice-{self.pci_info.bdf}'
> +
> +    def bind_driver(self) -> None:
> +        logger.debug("Bind %s driver to virtual device - PCI BDF: %s",
> +                     self.vm.get_drm_driver_name(), self.pci_info.bdf)
> +        self.driver.bind()
> +
> +    def unbind_driver(self) -> None:
> +        logger.debug("Unbind %s driver from virtual device - PCI BDF: %s",
> +                     self.vm.get_drm_driver_name(), self.pci_info.bdf)
> +        self.driver.unbind()
> +
> +    @property
> +    def lmem_size(self) -> typing.Optional[int]:
> +        if self._lmem_size is None:
> +            self.get_debugfs_selfconfig()
> +
> +        return self._lmem_size
> +
> +    @property
> +    def ggtt_size(self) -> typing.Optional[int]:
> +        if self._ggtt_size is None:
> +            self.get_debugfs_selfconfig()
> +
> +        return self._ggtt_size
> +
> +    @property
> +    def contexts(self) -> typing.Optional[int]:
> +        if self._contexts is None:
> +            self.get_debugfs_selfconfig()
> +
> +        return self._contexts
> +
> +    @property
> +    def doorbells(self) -> typing.Optional[int]:
> +        if self._doorbells is None:
> +            self.get_debugfs_selfconfig()
> +
> +        return self._doorbells
> +
> +    @property
> +    def tile_mask(self) -> typing.Optional[int]:
> +        if self._tile_mask is None:
> +            self.get_debugfs_selfconfig()
> +
> +        return self._tile_mask
> +
> +    def get_num_gts(self) -> int:
> +        """Get number of GTs for a device based on exposed sysfs gt nodes."""
> +        gt_num = 0
> +        # Fixme: tile0 only at the moment, add support for multiple tiles if needed
> +        path = self.driver.sysfs_device_path / 'tile0'
> +
> +        if self.vm.dir_exists(str(path / 'gt')):
> +            gt_num = 1
> +        else:
> +            while self.vm.dir_exists(str(path / f'gt{gt_num}')):
> +                gt_num += 1
> +
> +        return gt_num
> +
> +    # helper_convert_units_to_bytes - convert size with units to bytes
> +    # @size_str: multiple-byte unit size with suffix (K/M/G)
> +    # Returns: size in bytes
> +    # TODO: function perhaps could be moved to some new utils module
> +    # improve - consider regex to handle various formats eg. both M and MB
> +    def helper_convert_units_to_bytes(self, size_str: str) -> int:
> +        size_str = size_str.upper()
> +        size_int = 0
> +
> +        if size_str.endswith('B'):
> +            size_int = int(size_str[0:-1])
> +        elif size_str.endswith('K'):
> +            size_int = int(size_str[0:-1]) * 1024
> +        elif size_str.endswith('M'):
> +            size_int = int(size_str[0:-1]) * 1024**2
> +        elif size_str.endswith('G'):
> +            size_int = int(size_str[0:-1]) * 1024**3
> +
> +        return size_int
> +
> +    # __parse_selfconfig_size - parses size string from debugfs/self_config
> +    # GGTT/LMEM size on xe has the following format:
> +    # GGTT size:      536870912 (512 MiB)
> +    # whereas on i915 (unit suffix):
> +    # GGTT size:     524288K
> +    # @size_str: size string read from self_config file
> +    # (with unit suffix (B/K/M/G) or in bytes (without suffix))
> +    # Returns: size in bytes
> +    def __parse_selfconfig_size(self, size_str: str) -> int:
> +        retval = 0
> +        size_match = re.search(r'(?P<size_units>^\d+[BKMG])|(?P<size_bytes>^\d+)', size_str.strip())
> +        if size_match:
> +            if size_match.group('size_units'):
> +                # i915 specific format (in B/K/M/G units)
> +                retval = self.helper_convert_units_to_bytes(size_match.group('size_units'))
> +            elif size_match.group('size_bytes'):
> +                # Xe specific format (in Bytes)
> +                retval = int(size_match.group('size_bytes'))
> +            else:
> +                logger.warning("Unexpected size pattern match!")
> +
> +        return retval
> +
> +    def get_debugfs_selfconfig(self, gt_num: int = 0) -> None:
> +        """Read hard resources allocated to VF from debugfs selfconfig file."""
> +        out = self.driver.read_debugfs(f'gt{gt_num}/vf/self_config')
> +
> +        for line in out.splitlines():
> +            param, value = line.split(':')
> +
> +            if param == 'GGTT size':
> +                self._ggtt_size = self.__parse_selfconfig_size(value)
> +            elif param == 'LMEM size':
> +                self._lmem_size = self.__parse_selfconfig_size(value)
> +            elif param.find('contexts') != -1:
> +                self._contexts = int(value)
> +            elif param.find('doorbells') != -1:
> +                self._doorbells = int(value)
> +            elif param == 'tile mask':
> +                self._tile_mask = int(value, base=16)
> diff --git a/tools/vmtb/bench/machines/virtual/vm.py b/tools/vmtb/bench/machines/virtual/vm.py
> index 9f4ca1de7..312e87e4b 100644
> --- a/tools/vmtb/bench/machines/virtual/vm.py
> +++ b/tools/vmtb/bench/machines/virtual/vm.py
> @@ -518,6 +518,11 @@ class VirtualMachine(MachineInterface):
>               return False
>           return True
>   
> +    def dir_list(self, path: str) -> typing.List[str]:
> +        # TODO: implement, currently no-op to fulfill MachineInterface requirement
> +        logger.warning("VirtualMachine.dir_list() is not implemented yet!")
> +        return []
> +
LGTM,
Reviewed-by: Marcin Bernatowicz <marcin.bernatowicz@linux.intel.com>
>       def link_exists(self, path: str) -> bool:
>           pid = self.execute(f'/bin/sh -c "[ -h {path} ]"')
>           status = self.execute_wait(pid)

  reply	other threads:[~2026-03-10 10:45 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-24  7:50 [PATCH i-g-t 00/10] vmtb: Modernize SR-IOV VM Test Bench core Adam Miszczak
2026-02-24  7:50 ` [PATCH i-g-t 01/10] tools/vmtb: Update QEMU parameters Adam Miszczak
2026-03-10 10:22   ` Bernatowicz, Marcin
2026-02-24  7:50 ` [PATCH i-g-t 02/10] tools/vmtb: Fix DUT selection based on card index Adam Miszczak
2026-03-10 10:26   ` Bernatowicz, Marcin
2026-02-24  7:50 ` [PATCH i-g-t 03/10] tools/vmtb: Fix VM snapshot query handling Adam Miszczak
2026-03-10 10:29   ` Bernatowicz, Marcin
2026-02-24  7:50 ` [PATCH i-g-t 04/10] tools/vmtb: Extend IGT and WSIM abstractions Adam Miszczak
2026-03-10 10:36   ` Bernatowicz, Marcin
2026-02-24  7:50 ` [PATCH i-g-t 05/10] tools/vmtb: VF auto/fair provisioning support Adam Miszczak
2026-03-10 10:38   ` Bernatowicz, Marcin
2026-02-24  7:50 ` [PATCH i-g-t 06/10] tools/vmtb: Refactor driver interfaces Adam Miszczak
2026-03-10 10:43   ` Bernatowicz, Marcin
2026-02-24  7:50 ` [PATCH i-g-t 07/10] tools/vmtb: Introduce VirtualDevice class Adam Miszczak
2026-03-10 10:45   ` Bernatowicz, Marcin [this message]
2026-02-24  7:50 ` [PATCH i-g-t 08/10] tools/vmtb: Redesign VirtualMachine class Adam Miszczak
2026-03-10 10:47   ` Bernatowicz, Marcin
2026-02-24  7:50 ` [PATCH i-g-t 09/10] tools/vmtb: Support max VFs configuration Adam Miszczak
2026-03-10 10:52   ` Bernatowicz, Marcin
2026-02-24  7:50 ` [PATCH i-g-t 10/10] tools/vmtb: Platform enabling: PTL and BMG support Adam Miszczak
2026-03-10 10:52   ` Bernatowicz, Marcin
2026-02-24 11:49 ` ✓ Xe.CI.BAT: success for vmtb: Modernize SR-IOV VM Test Bench core Patchwork
2026-02-24 12:43 ` ✓ i915.CI.BAT: " Patchwork
2026-02-24 16:27 ` ✗ i915.CI.Full: failure " Patchwork
2026-02-24 20:21 ` ✗ Xe.CI.FULL: " Patchwork

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=265e75e3-0f4d-469d-af04-b3dffb8788d3@linux.intel.com \
    --to=marcin.bernatowicz@linux.intel.com \
    --cc=adam.miszczak@linux.intel.com \
    --cc=igt-dev@lists.freedesktop.org \
    --cc=kamil.konieczny@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox