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 04/10] tools/vmtb: Extend IGT and WSIM abstractions
Date: Tue, 24 Feb 2026 08:50:21 +0100	[thread overview]
Message-ID: <20260224075027.2409675-5-adam.miszczak@linux.intel.com> (raw)
In-Reply-To: <20260224075027.2409675-1-adam.miszczak@linux.intel.com>

Introduce few improvements to IGT and gem_wsim executors:

1. Add gem_wsim workload descriptor resources config
In order to place workload files in a common resources directory,
extend VMTB config with a relevant entry and read the path in a conftest.

Implement capability for device specific workload files in wsim resources
directory (as different GPU devices may have various sets of engines).
Wsim device specific path format looks as follows:
[vmtb_root_path]/vmm_flows/resources/wsim/[device_name_abbr]/[wl_desc].wsim
e.g.:
- resources/wsim/ptl/wl.wsim
- resources/wsim/bmg/wl.wsim

2. Support gem_wsim workload files on VM
So far GemWsim was initialized with a simple string workload descriptor
passed with '-w <workload>' option.
However, multiple complex workloads are expected for a busy VF migration
scenarios - therefore it would be handy to define it as workload files.
Gem_wsim input files will be deployed as VMTB resources on a host,
whereas we want to execute it on VM as well.
Hence, content of the wsim workload file is copied to /tmp on a guest/host
and executed from that location.
In case of the host, original (resources) location will work
as a wsim file input, however temp file is still used for simplicity
and consistency (to avoid instance type checks).

3. Add IGT and WSIM results check function
Extend IgtExecutor and GemWsim with new check_results() method to allow
object-oriented approach for checking IGT/WSIM results and unify interfaces.
Existing helper - igt_check(IgtExecutor) is still supported,
but IgtExecutor.check_results() is now preferred.
Also, implement IgtExecutor's is_running() (similarly to GemWsim)
and keep the process state (ProcessResult).

Introduced change simplifies supporting both IGT and gem_wsim workloads
in busy migration tests.

4. Support IGT tests repeated execution
Few planned VF busy migration scenarios require executing short IGT tests
in a longer time slot (in a loop).
To achieve it, allow IgtExecutor to accept expected number of IGT test
repeats and reflect it in a testlist passed to the igt_runner
(multiply requested test entries).

By default, IgtExecutor's 'num_repeats' input parameter is set to 1,
so in the usual case (single IGT execution) it doesn't need to be
explicitly provided.

Signed-off-by: Adam Miszczak <adam.miszczak@linux.intel.com>
---
 tools/vmtb/bench/configurators/vmtb_config.py |  4 +-
 tools/vmtb/bench/executors/gem_wsim.py        | 45 +++++++++++++++----
 tools/vmtb/bench/executors/igt.py             | 36 ++++++++++++---
 tools/vmtb/bench/machines/physical/device.py  |  2 +-
 tools/vmtb/vmm_flows/conftest.py              |  8 +++-
 tools/vmtb/vmtb_config.json                   |  1 +
 6 files changed, 78 insertions(+), 18 deletions(-)

diff --git a/tools/vmtb/bench/configurators/vmtb_config.py b/tools/vmtb/bench/configurators/vmtb_config.py
index 49dde4589..8fd2b87d5 100644
--- a/tools/vmtb/bench/configurators/vmtb_config.py
+++ b/tools/vmtb/bench/configurators/vmtb_config.py
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: MIT
-# Copyright © 2024 Intel Corporation
+# Copyright © 2024-2026 Intel Corporation
 
 import json
 import logging
@@ -40,6 +40,7 @@ class VmtbConfig:
     host_config: VmtbHostConfig
     guest_config: VmtbGuestConfig
     vgpu_profiles_path: str
+    wsim_wl_path: str
     guc_ver_path: str
     ci_host_dmesg_file: str
 
@@ -104,6 +105,7 @@ class VmtbConfigJsonReader:
             host_config=vmtb_host_config,
             guest_config=vmtb_guest_config,
             vgpu_profiles_path=config_json['resources']['vgpu_profiles_path'],
+            wsim_wl_path=config_json['resources']['wsim_wl_path'],
             guc_ver_path=config_json['resources']['guc_ver_path'],
             ci_host_dmesg_file=config_json['ci']['host_dmesg_file'])
 
diff --git a/tools/vmtb/bench/executors/gem_wsim.py b/tools/vmtb/bench/executors/gem_wsim.py
index 46fa2291c..0cdfcc7e4 100644
--- a/tools/vmtb/bench/executors/gem_wsim.py
+++ b/tools/vmtb/bench/executors/gem_wsim.py
@@ -1,9 +1,10 @@
 # SPDX-License-Identifier: MIT
-# Copyright © 2024 Intel Corporation
+# Copyright © 2024-2026 Intel Corporation
 
 import logging
 import re
 import typing
+from pathlib import Path
 
 from bench import exceptions
 from bench.executors.shell import ShellExecutor
@@ -23,13 +24,28 @@ PREEMPT_10MS_WORKLOAD = (f'1.DEFAULT.{int(ONE_CYCLE_DURATION_MS * 1000 / 2)}.0.0
 NON_PREEMPT_10MS_WORKLOAD = f'X.1.0,X.2.0,{PREEMPT_10MS_WORKLOAD}'
 
 class GemWsim(ShellExecutor):
-    def __init__(self, machine: MachineInterface, num_clients: int = 1, num_repeats: int = 1,
-                 workload: str = PREEMPT_10MS_WORKLOAD, timeout: int = DEFAULT_TIMEOUT) -> None:
-        super().__init__(
-            machine,
-            f'/usr/local/libexec/igt-gpu-tools/benchmarks/gem_wsim -w {workload} -c {num_clients} -r {num_repeats}',
-            timeout)
-        self.machine_id = str(machine)
+    def __init__(self, target: MachineInterface,
+                 num_clients: int = 1,
+                 num_repeats: int = 1,
+                 workload: str = PREEMPT_10MS_WORKLOAD,
+                 timeout: int = DEFAULT_TIMEOUT) -> None:
+
+        self.machine_id = str(target)
+        wl_file_path = Path(workload)
+
+        if wl_file_path.is_file():
+            # Copy workload descriptor from the source host file
+            # to the requested target machine (VM or host)
+            workload = '/tmp/igt_workload.wsim'
+            wl_desc = wl_file_path.read_text(encoding='utf-8')
+            target.write_file_content(workload, wl_desc)
+
+            logger.debug("IGT/wsim workload descriptor:\n%s", wl_desc)
+
+        wsim_path = Path(target.get_igt_config().test_dir) / 'benchmarks' / 'gem_wsim'
+        command = f'{wsim_path} -w {workload} -c {num_clients} -r {num_repeats}'
+
+        super().__init__(target, command, timeout)
 
     def __str__(self) -> str:
         return f'gem_wsim({self.machine_id}:{self.pid})'
@@ -49,6 +65,19 @@ class GemWsim(ShellExecutor):
         raise exceptions.GemWsimError(f'{self}: exit_code: {proc_result.exit_code}'
                                       f' stdout: {proc_result.stdout} stderr: {proc_result.stderr}')
 
+    def check_results(self) -> bool:
+        """Verify gem_wsim execution results. Return True for workload success, False on fail."""
+        # TODO: support also checking wsim workload: 'Verify Xe spinner batch completion'
+        try:
+            wl_results: GemWsimResult = self.wait_results()
+        except exceptions.GemWsimError as exc:
+            logger.error("[%s] WSIM failed: %s", self.target, exc)
+            return False
+
+        logger.debug("[%s] WSIM passed in %ss (%s WL/s)",
+                      self.target, wl_results.elapsed_sec, wl_results.workloads_per_sec)
+        return True
+
 
 def gem_wsim_parallel_exec_and_check(vms: typing.List[MachineInterface], workload: str, iterations: int,
                                      expected: typing.Optional[GemWsimResult] = None) -> GemWsimResult:
diff --git a/tools/vmtb/bench/executors/igt.py b/tools/vmtb/bench/executors/igt.py
index 4296464c2..a08f71d54 100644
--- a/tools/vmtb/bench/executors/igt.py
+++ b/tools/vmtb/bench/executors/igt.py
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: MIT
-# Copyright © 2024 Intel Corporation
+# Copyright © 2024-2026 Intel Corporation
 
 import enum
 import json
@@ -34,6 +34,7 @@ igt_tests: typing.Dict[IgtType, typing.Tuple[str, str]] = {
 class IgtExecutor(ExecutorInterface):
     def __init__(self, target: MachineInterface,
                  test: typing.Union[str, IgtType],
+                 num_repeats: int = 1,
                  timeout: int = DEFAULT_TIMEOUT) -> None:
         self.igt_config = target.get_igt_config()
 
@@ -46,18 +47,25 @@ class IgtExecutor(ExecutorInterface):
         self.results: typing.Dict[str, typing.Any] = {}
         self.target: MachineInterface = target
         self.igt: str = test if isinstance(test, str) else self.select_igt_variant(target.get_drm_driver_name(), test)
-        self.target.write_file_content(testlist, self.igt)
-        self.timeout: int = timeout
 
         logger.info("[%s] Execute IGT test: %s", target, self.igt)
+        if num_repeats > 1:
+            logger.debug("Repeat IGT execution %s times", num_repeats)
+            self.igt = (self.igt + '\n') * num_repeats
+
+        self.target.write_file_content(testlist, self.igt)
+        self.timeout: int = timeout
+        self.proc_result = ProcessResult()
         self.pid: int = self.target.execute(command)
 
     # Executor interface implementation
     def status(self) -> ProcessResult:
-        return self.target.execute_status(self.pid)
+        self.proc_result = self.target.execute_status(self.pid)
+        return self.proc_result
 
     def wait(self) -> ProcessResult:
-        return self.target.execute_wait(self.pid, self.timeout)
+        self.proc_result = self.target.execute_wait(self.pid, self.timeout)
+        return self.proc_result
 
     def sendsig(self, sig: signal.Signals) -> None:
         self.target.execute_signal(self.pid, sig)
@@ -69,9 +77,25 @@ class IgtExecutor(ExecutorInterface):
         self.sendsig(signal.SIGKILL)
 
     # IGT specific methods
+    def is_running(self) -> bool:
+        return not self.status().exited
+
+    def check_results(self) -> bool:
+        """Verify IGT test results. Return True for test success, False on fail."""
+        if not self.proc_result.exited:
+            self.proc_result = self.wait()
+
+        if self.proc_result.exit_code == 0 and self.did_pass():
+            logger.debug("[%s] IGT passed", self.target)
+            return True
+
+        logger.error("[%s] IGT failed: %s", self.target, self.proc_result)
+        return False
+
     def get_results_log(self) -> typing.Dict:
         # Results are cached
         if self.results:
+            logger.debug("Get available IGT results from cache")
             return self.results
         path = posixpath.join(self.igt_config.result_dir, 'results.json')
         result = self.target.read_file_content(path)
@@ -95,7 +119,7 @@ class IgtExecutor(ExecutorInterface):
                 continue
             fail_case = fail_case + aggregate[key]
 
-        logger.debug('Full IGT test results:\n%s', json.dumps(results, indent=4))
+        logger.debug("[%s] Full IGT test results:\n%s", self.target, json.dumps(results, indent=4))
 
         if fail_case > 0:
             logger.error('Test failed!')
diff --git a/tools/vmtb/bench/machines/physical/device.py b/tools/vmtb/bench/machines/physical/device.py
index 887f607e4..a487c0e5f 100644
--- a/tools/vmtb/bench/machines/physical/device.py
+++ b/tools/vmtb/bench/machines/physical/device.py
@@ -45,7 +45,7 @@ class Device(DeviceInterface):
 
     def __init__(self, bdf: str, driver: str) -> None:
         self.pci_info = self.PciInfo(bdf)
-        self.gpu_model: str = pci.get_gpu_model(self.pci_info.devid)
+        self.gpu_model = pci.get_gpu_model(self.pci_info.devid)
         self.driver: DriverInterface = self.instantiate_driver(driver, self.pci_info.minor_number)
 
     def __str__(self) -> str:
diff --git a/tools/vmtb/vmm_flows/conftest.py b/tools/vmtb/vmm_flows/conftest.py
index 3bfac01c4..28c0b5f00 100644
--- a/tools/vmtb/vmm_flows/conftest.py
+++ b/tools/vmtb/vmm_flows/conftest.py
@@ -79,8 +79,6 @@ class VmmTestingSetup:
         self.guest_os_image = vmtb_config.get_guest_config().os_image_path if cmdline_config['vm_image'] is None \
                          else cmdline_config['vm_image']
 
-        self.vgpu_profiles_dir = vmtb_config.vmtb_config_file.parent / vmtb_config.config.vgpu_profiles_path
-
         self.host.drm_driver_name = vmtb_config.get_host_config().driver
         self.host.igt_config = vmtb_config.get_host_config().igt_config
 
@@ -103,6 +101,12 @@ class VmmTestingSetup:
                     self.get_dut().driver.get_name(),
                     vf_migration_support)
 
+        vmtb_root_path = vmtb_config.vmtb_config_file.parent
+        self.vgpu_profiles_dir = vmtb_root_path / vmtb_config.config.vgpu_profiles_path
+        # Device specific wsim descriptors directory path, e.g.:
+        # [vmtb_root]/vmm_flows/resources/wsim/ptl (last subdir is lowercase key/name from pci.GpuModel class)
+        self.wsim_wl_dir = vmtb_root_path / vmtb_config.config.wsim_wl_path / self.get_dut().gpu_model.name.lower()
+
         self.vgpu_profile: VgpuProfile = self.get_vgpu_profile()
 
         # Start maximum requested number of VMs, but not more than VFs supported by the given vGPU profile
diff --git a/tools/vmtb/vmtb_config.json b/tools/vmtb/vmtb_config.json
index 640a64123..2b8fb43f3 100644
--- a/tools/vmtb/vmtb_config.json
+++ b/tools/vmtb/vmtb_config.json
@@ -23,6 +23,7 @@
     },
     "resources": {
         "vgpu_profiles_path": "vmm_flows/resources/vgpu_profiles",
+        "wsim_wl_path": "vmm_flows/resources/wsim",
         "guc_ver_path": "vmm_flows/resources/guc"
     },
     "ci": {
-- 
2.39.1


  parent reply	other threads:[~2026-02-24  8:23 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 ` Adam Miszczak [this message]
2026-03-10 10:36   ` [PATCH i-g-t 04/10] tools/vmtb: Extend IGT and WSIM abstractions 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
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-5-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