RE: [PATCH] tests/intel/xe_gt_debugfs: Add GT-level Xe debugfs coverage

"Balasubramanyam, Smitha" <[email protected]> Tue, 28 Jul 2026 10:20:48 +0000
Newsgroups org.freedesktop.lists.igt-dev
Message-ID <PH7PR11MB5863AC353F5D6296328B684785CB2@PH7PR11MB5863.namprd11.prod.outlook.com>

-----Original Message-----
From: Kamil Konieczny <[email protected]> 
Sent: Friday, July 24, 2026 2:39 PM
To: Balasubramanyam, Smitha <[email protected]>
Cc: [email protected]; Thomas, Sobin <[email protected]>
Subject: Re: [PATCH] tests/intel/xe_gt_debugfs: Add GT-level Xe debugfs coverage

Hi Smitha,
On 2026-07-17 at 14:03:35 +0530, Smitha Balasubramanyam wrote:
> Add a new test exercising the GT-level debugfs attributes exposed by 
> the Xe driver.

Why new test? imho this should be in xe_debugfs.c

Hi Kamil,

Thanks for the review.

Agreed, I have now extended the existing xe_debugfs to handle gt specific attrs too. 
https://patchwork.freedesktop.org/patch/742672/
I have closed the current one. 

> 
> The following kernel show/store handlers are covered:
> 
>  - hw_engines, steering (PF-only)
>  - register-save-restore, hwconfig, default_lrc_{rcs,ccs,bcs,vcs,vecs}
>    (VF-safe)

Please split these into more patches, add SRIOV developers to Cc for any VF/PF changes.

>  - stats store handler: valid boolean input accepted, invalid input
>    rejected
> 
> force_reset and force_reset_sync are present in the VF-safe node list 
> but their show handlers trigger live GT resets and are intentionally 
> not read. Presence is verified via openat-only check in gt-dir-vf-safe.
> 
> Subtests:
>  - gt-dir-vf-safe: presence check for unconditional VF-safe nodes
>  - gt-dir-pf-only: presence check for PF-only nodes (skipped on VF)
>  - gt-hw-engines: read hw_engines (PF only)
>  - gt-steering: read steering (PF only)
>  - gt-register-save-restore: read register-save-restore
>  - gt-default-lrc: read per engine class, skips absent engines
>  - gt-hwconfig: read hwconfig
>  - gt-stats-write: verify stats accepts valid input and rejects
>    malformed input

Can you reuse existing infra in xe_debugfs? +cc Sobin
Cc: Sobin Thomas <[email protected]>


Regards,
Kamil

> Signed-off-by: Smitha Balasubramanyam 
> <[email protected]>
> ---
>  tests/intel/xe_gt_debugfs.c | 359 ++++++++++++++++++++++++++++++++++++
>  tests/meson.build           |   1 +
>  2 files changed, 360 insertions(+)
>  create mode 100644 tests/intel/xe_gt_debugfs.c
> 
> diff --git a/tests/intel/xe_gt_debugfs.c b/tests/intel/xe_gt_debugfs.c 
> new file mode 100644 index 000000000..afb3ff4bd
> --- /dev/null
> +++ b/tests/intel/xe_gt_debugfs.c
> @@ -0,0 +1,359 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2025 Intel Corporation  */
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +
> +#include "igt.h"
> +#include "igt_debugfs.h"
> +#include "igt_sysfs.h"
> +#include "igt_sriov_device.h"
> +#include "xe/xe_query.h"
> +
> +IGT_TEST_DESCRIPTION("Validate GT-level Xe debugfs attributes and 
> +their content");
> +
> +/**
> + * TEST: Xe GT debugfs
> + * Category: Core
> + * Mega feature: General Core features
> + * Sub-category: uapi
> + * Functionality: debugfs
> + * Feature: core
> + * Test category: functionality test
> + * Description: Validate that GT-level Xe debugfs attributes are 
> +readable or
> + * writable and return valid content.  Covers both PF-only and 
> +VF-safe
> + * attributes, and the stats write path.
> + */
> +
> +/**
> + * SUBTEST: gt-dir-vf-safe
> + * Description: Check that unconditional GT debugfs attrs accessible 
> +from
> + * both PF and VF contexts are present for each GT.
> + *
> + * SUBTEST: gt-dir-pf-only
> + * Description: Check that PF-only GT debugfs attrs are present for each GT on PF.
> + *
> + * SUBTEST: gt-hw-engines
> + * Description: Read hw_engines debugfs attribute for each GT.
> + *
> + * SUBTEST: gt-steering
> + * Description: Read steering debugfs attribute for each GT.
> + *
> + * SUBTEST: gt-register-save-restore
> + * Description: Read register-save-restore debugfs attribute for each GT.
> + *
> + * SUBTEST: gt-default-lrc
> + * Description: Validate default LRC content for each engine class present on each GT.
> + * Engine classes absent on the GT are skipped.
> + *
> + * SUBTEST: gt-hwconfig
> + * Description: Read hwconfig debugfs attribute for each GT.
> + *
> + * SUBTEST: gt-stats-write
> + * Description: Write an invalid payload to the stats debugfs 
> +attribute for
> + * each GT to verify the kernel rejects malformed input without 
> +modifying
> + * counter state.
> + */
> +
> +static int *gt_dirs;
> +
> +static bool attr_exists(int dirfd, const char *name, int mode) {
> +	int fd = openat(dirfd, name, mode);
> +
> +	if (fd < 0)
> +		return false;
> +
> +	close(fd);
> +	return true;
> +}
> +
> +static void test_gt_dir_vf_safe(unsigned int gt) {
> +	/*
> +	 * Unconditionally registered on every GT for both PF and VF contexts.
> +	 */
> +	static const char * const attrs[] = {
> +		"register-save-restore",
> +		"hwconfig",
> +		"stats",
> +		"force_reset",
> +		"force_reset_sync",
> +	};
> +	/* Engine-conditional: absent when that engine class is not on this GT. */
> +	static const char * const lrc_attrs[] = {
> +		"default_lrc_rcs",
> +		"default_lrc_ccs",
> +		"default_lrc_bcs",
> +		"default_lrc_vcs",
> +		"default_lrc_vecs",
> +	};
> +	int dir = gt_dirs[gt];
> +	int missing_attr = 0;
> +	int lrc_present = 0;
> +
> +	for (size_t i = 0; i < ARRAY_SIZE(attrs); i++) {
> +		if (!attr_exists(dir, attrs[i], O_RDONLY)) {
> +			igt_warn("GT%u: missing attr: %s\n", gt, attrs[i]);
> +			missing_attr++;
> +		}
> +	}
> +
> +	for (size_t i = 0; i < ARRAY_SIZE(lrc_attrs); i++) {
> +		if (attr_exists(dir, lrc_attrs[i], O_RDONLY))
> +			lrc_present++;
> +		else
> +			igt_info("GT%u: %s absent (engine class not present on this GT)\n",
> +				 gt, lrc_attrs[i]);
> +	}
> +
> +	igt_assert_f(missing_attr == 0,
> +		     "GT%u: %d unconditional GT debugfs attrs missing (see warnings above)\n",
> +		     gt, missing_attr);
> +	igt_assert_f(lrc_present > 0,
> +		     "GT%u: no default_lrc_* entries found; expected at least one engine\n",
> +		     gt);
> +}
> +
> +static void test_gt_dir_pf_only(unsigned int gt) {
> +	static const char * const attrs[] = {
> +		"hw_engines",
> +		"steering",
> +	};
> +	int dir = gt_dirs[gt];
> +	int missing_attr = 0;
> +
> +	for (size_t i = 0; i < ARRAY_SIZE(attrs); i++) {
> +		if (!attr_exists(dir, attrs[i], O_RDONLY)) {
> +			igt_warn("GT%u: missing PF-only attr: %s\n",
> +				 gt, attrs[i]);
> +			missing_attr++;
> +		}
> +	}
> +
> +	igt_assert_f(missing_attr == 0,
> +		     "GT%u: %d missing PF-only GT debugfs attrs (see warnings above)\n",
> +		     gt, missing_attr);
> +}
> +
> +static void test_gt_hw_engines(unsigned int gt) {
> +	/*
> +	 * Read 4 bytes to confirm the attribute returns non-empty output.
> +	 * Content correctness is not currently validated.
> +	 */
> +	char buf[4];
> +
> +	igt_assert_f(igt_sysfs_read(gt_dirs[gt], "hw_engines", buf, sizeof(buf)) > 0,
> +		     "GT%u: hw_engines is empty or unreadable\n", gt); }
> +
> +static void test_gt_steering(unsigned int gt) {
> +	/*
> +	 * Read 4 bytes to confirm the attribute returns non-empty output.
> +	 * Content correctness is not currently validated.
> +	 */
> +	char buf[4];
> +
> +	igt_assert_f(igt_sysfs_read(gt_dirs[gt], "steering", buf, sizeof(buf)) > 0,
> +		     "GT%u: steering is empty or unreadable\n", gt); }
> +
> +static void test_gt_register_save_restore(unsigned int gt) {
> +	/*
> +	 * Read 4 bytes to confirm the attribute returns non-empty output.
> +	 * Content correctness is not currently validated.
> +	 */
> +	char buf[4];
> +
> +	igt_assert_f(igt_sysfs_read(gt_dirs[gt], "register-save-restore",
> +				    buf, sizeof(buf)) > 0,
> +		     "GT%u: register-save-restore is empty or unreadable\n", gt); }
> +
> +static void test_gt_default_lrc(unsigned int gt) {
> +	static const struct {
> +		const char *attr;
> +		const char *engine;
> +	} lrc_attrs[] = {
> +		{ "default_lrc_rcs",  "RCS"  },
> +		{ "default_lrc_ccs",  "CCS"  },
> +		{ "default_lrc_bcs",  "BCS"  },
> +		{ "default_lrc_vcs",  "VCS"  },
> +		{ "default_lrc_vecs", "VECS" },
> +	};
> +
> +	int engines_found = 0;
> +
> +	for (size_t i = 0; i < ARRAY_SIZE(lrc_attrs); i++) {
> +		/*
> +		 * Read 4 bytes to confirm the attribute returns non-empty output.
> +		 * Content correctness is not currently validated.
> +		 */
> +		char buf[4];
> +		int ret = igt_sysfs_read(gt_dirs[gt],
> +					 lrc_attrs[i].attr,
> +					 buf, sizeof(buf));
> +
> +		if (ret == -ENOENT) {
> +			igt_info("GT%u: %s absent (no %s engine)\n",
> +				 gt, lrc_attrs[i].attr, lrc_attrs[i].engine);
> +			continue;
> +		}
> +
> +		igt_assert_f(ret > 0,
> +			     "GT%u: %s is empty or unreadable (%d)\n",
> +			     gt, lrc_attrs[i].attr, ret);
> +		igt_debug("GT%u %s: readable\n", gt, lrc_attrs[i].attr);
> +		engines_found++;
> +	}
> +
> +	igt_assert_f(engines_found > 0,
> +		     "GT%u: no default_lrc_* attrs readable; expected at least one engine\n",
> +		     gt);
> +}
> +
> +static void test_gt_hwconfig(unsigned int gt) {
> +	/*
> +	 * Read 4 bytes to confirm the attribute returns non-empty output.
> +	 * Content correctness is not currently validated.
> +	 */
> +	char buf[4];
> +
> +	igt_assert_f(igt_sysfs_read(gt_dirs[gt], "hwconfig", buf, sizeof(buf)) > 0,
> +		     "GT%u: hwconfig is empty or unreadable\n", gt); }
> +
> +static void test_gt_stats_write(unsigned int gt) {
> +	char buf[4096];
> +	int len, wret;
> +
> +	len = igt_sysfs_read(gt_dirs[gt], "stats", buf, sizeof(buf) - 1);
> +	igt_assert_f(len > 0,
> +		     "GT%u: pre-write stats read failed or empty (%d)\n", gt, len);
> +	buf[len] = '\0';
> +	igt_debug("GT%u stats before write:\n%s\n", gt, buf);
> +
> +	/*
> +	 * Write "0" — a valid boolean input that does NOT trigger
> +	 * xe_gt_stats_clear. Confirms the
> +	 * write handler accepts well-formed input without modifying state.
> +	 */
> +	wret = igt_sysfs_write(gt_dirs[gt], "stats", "0", 1);
> +	igt_assert_f(wret > 0,
> +		     "GT%u: stats_write rejected valid input \"0\" (%d)\n",
> +		     gt, wret);
> +
> +	/*
> +	 * Write an invalid payload — the kernel returns -EINVAL for input
> +	 * that cannot be parsed as a boolean, without modifying counter state.
> +	 */
> +	wret = igt_sysfs_write(gt_dirs[gt], "stats", "invalid_key 0\n", 14);
> +	igt_assert_f(wret == -EINVAL,
> +		     "GT%u: stats_write did not reject invalid key with EINVAL (returned %d)\n",
> +		     gt, wret);
> +	igt_debug("GT%u stats_write correctly rejected invalid payload (%d)\n",
> +		  gt, wret);
> +
> +	len = igt_sysfs_read(gt_dirs[gt], "stats", buf, sizeof(buf) - 1);
> +	igt_assert_f(len > 0,
> +		     "GT%u: post-write stats read failed or empty (%d)\n",
> +		     gt, len);
> +	igt_debug("GT%u stats after write attempts: %d bytes\n", gt, len); }
> +
> +int igt_main()
> +{
> +	struct xe_device *xe_dev;
> +	unsigned int gt;
> +	bool is_vf;
> +	int fd = -1;
> +
> +	igt_fixture() {
> +		fd = drm_open_driver_master(DRIVER_XE);
> +		xe_dev = xe_device_get(fd);
> +		igt_assert_f(xe_dev, "Failed to get xe device\n");
> +		is_vf = intel_is_vf_device(fd);
> +
> +		gt_dirs = calloc(xe_dev_max_gt(fd) + 1, sizeof(*gt_dirs));
> +		igt_assert(gt_dirs);
> +		xe_for_each_gt(fd, gt) {
> +			gt_dirs[gt] = igt_debugfs_gt_dir(fd, gt);
> +			igt_assert_f(gt_dirs[gt] >= 0,
> +				     "GT%u: failed to open GT debugfs directory\n",
> +				     gt);
> +		}
> +	}
> +
> +	igt_describe("Check that unconditional GT debugfs attrs accessible from both PF and VF contexts are present for each GT.");
> +	igt_subtest_with_dynamic("gt-dir-vf-safe")
> +		xe_for_each_gt(fd, gt)
> +			igt_dynamic_f("gt-%u", gt)
> +				test_gt_dir_vf_safe(gt);
> +
> +	igt_describe("Check that PF-only GT debugfs attrs are present for each GT on PF.");
> +	igt_subtest_with_dynamic("gt-dir-pf-only") {
> +		igt_skip_on_f(is_vf, "PF-only attributes not present on VF\n");
> +		xe_for_each_gt(fd, gt)
> +			igt_dynamic_f("gt-%u", gt)
> +				test_gt_dir_pf_only(gt);
> +	}
> +
> +	igt_describe("Read hw_engines debugfs attribute for each GT.");
> +	igt_subtest_with_dynamic("gt-hw-engines") {
> +		igt_skip_on_f(is_vf, "hw_engines not present on VF\n");
> +		xe_for_each_gt(fd, gt)
> +			igt_dynamic_f("gt-%u", gt)
> +				test_gt_hw_engines(gt);
> +	}
> +
> +	igt_describe("Read steering debugfs attribute for each GT.");
> +	igt_subtest_with_dynamic("gt-steering") {
> +		igt_skip_on_f(is_vf, "steering not present on VF\n");
> +		xe_for_each_gt(fd, gt)
> +			igt_dynamic_f("gt-%u", gt)
> +				test_gt_steering(gt);
> +	}
> +
> +	igt_describe("Read register-save-restore debugfs attribute for each GT.");
> +	igt_subtest_with_dynamic("gt-register-save-restore")
> +		xe_for_each_gt(fd, gt)
> +			igt_dynamic_f("gt-%u", gt)
> +				test_gt_register_save_restore(gt);
> +
> +	igt_describe("Validate default LRC content for each engine class present on each GT.");
> +	igt_subtest_with_dynamic("gt-default-lrc")
> +		xe_for_each_gt(fd, gt)
> +			igt_dynamic_f("gt-%u", gt)
> +				test_gt_default_lrc(gt);
> +
> +	igt_describe("Read hwconfig debugfs attribute for each GT.");
> +	igt_subtest_with_dynamic("gt-hwconfig")
> +		xe_for_each_gt(fd, gt)
> +			igt_dynamic_f("gt-%u", gt)
> +				test_gt_hwconfig(gt);
> +
> +	igt_describe("Write an invalid payload to the stats debugfs attribute for each GT to verify the kernel rejects malformed input without modifying counter state.  Root only.");
> +	igt_subtest_with_dynamic("gt-stats-write") {
> +		igt_require(geteuid() == 0);
> +		xe_for_each_gt(fd, gt)
> +			igt_dynamic_f("gt-%u", gt)
> +				test_gt_stats_write(gt);
> +	}
> +
> +	igt_fixture() {
> +		xe_for_each_gt(fd, gt)
> +			close(gt_dirs[gt]);
> +		free(gt_dirs);
> +		drm_close_driver(fd);
> +	}
> +}
> diff --git a/tests/meson.build b/tests/meson.build index 
> d73aaecd9..984a88c21 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -312,6 +312,7 @@ intel_xe_progs = [
>  	'xe_exercise_blt',
>  	'xe_fault_injection',
>  	'xe_gpgpu_fill',
> +        'xe_gt_debugfs',
>  	'xe_gt_freq',
>  	'xe_huc_copy',
>  	'xe_intel_bb',
> --
> 2.43.0
>