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)
next prev parent 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