public inbox for igt-dev@lists.freedesktop.org
 help / color / mirror / Atom feed
From: Adam Miszczak <adam.miszczak@linux.intel.com>
To: igt-dev@lists.freedesktop.org
Cc: marcin.bernatowicz@linux.intel.com, kamil.konieczny@linux.intel.com
Subject: [PATCH i-g-t 07/10] tools/vmtb: Introduce VirtualDevice class
Date: Tue, 24 Feb 2026 08:50:24 +0100	[thread overview]
Message-ID: <20260224075027.2409675-8-adam.miszczak@linux.intel.com> (raw)
In-Reply-To: <20260224075027.2409675-1-adam.miszczak@linux.intel.com>

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 []
+
     def link_exists(self, path: str) -> bool:
         pid = self.execute(f'/bin/sh -c "[ -h {path} ]"')
         status = self.execute_wait(pid)
-- 
2.39.1


  parent reply	other threads:[~2026-02-24  8:24 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 ` Adam Miszczak [this message]
2026-03-10 10:45   ` [PATCH i-g-t 07/10] tools/vmtb: Introduce VirtualDevice class Bernatowicz, Marcin
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=20260224075027.2409675-8-adam.miszczak@linux.intel.com \
    --to=adam.miszczak@linux.intel.com \
    --cc=igt-dev@lists.freedesktop.org \
    --cc=kamil.konieczny@linux.intel.com \
    --cc=marcin.bernatowicz@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