Re: [v2,1/4] lib/xe: cache engine class masks from debugfs info

Daniel Charles
2026-04-06T22:25:38Z

On 4/3/2026 12:13 AM, Xin Wang wrote:
> Read the multi_lrc_engine_classes and multi_queue_engine_classes lines
> from the xe debugfs info node and cache them in struct xe_device as
> bitmasks indexed by DRM_XE_ENGINE_CLASS_* values.
>
> The new fields are set to UINT16_MAX when the kernel does not yet expose
> the information (i.e. when the key is not present in the info node), so
> callers can distinguish "not available" from "empty set".
>
> Signed-off-by: Xin Wang <x.wang@intel.com>
> ---
> lib/xe/xe_query.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++
> lib/xe/xe_query.h | 12 +++++++
> 2 files changed, 98 insertions(+)
>
> diff --git a/lib/xe/xe_query.c b/lib/xe/xe_query.c
> index 00331c628..441e95794 100644
> --- a/lib/xe/xe_query.c
> +++ b/lib/xe/xe_query.c
> @@ -6,6 +6,7 @@
> * Matthew Brost <matthew.brost@intel.com>
> */
>
> +#include <fcntl.h>
> #include <stdlib.h>
> #include <pthread.h>
>
> @@ -19,6 +20,7 @@
> #endif
>
> #include "drmtest.h"
> +#include "igt_debugfs.h"
> #include "ioctl_wrappers.h"
> #include "igt_map.h"
> #include "intel_pat.h"
> @@ -134,6 +136,85 @@ static uint32_t __mem_default_alignment(struct drm_xe_query_mem_regions *mem_reg
> return alignment;
> }
>
> +/*
> + * parse_engine_class_mask - parse a space-separated list of engine class names
> + * into a bitmask indexed by DRM_XE_ENGINE_CLASS_* values.
> + *
> + * @names: the engine class names string, e.g. " vcs vecs" or "bcs ccs"
> + *
> + * The kernel debugfs "info" output for multi_lrc_engine_classes and
> + * multi_queue_engine_classes uses the same short names as
> + * xe_engine_class_short_string(): "rcs", "bcs", "vcs", "vecs", "ccs".
> + *
> + * Returns the bitmask, or 0 if no known class names were found.
> + */
> +static uint16_t parse_engine_class_mask(const char *names)
> +{
> + static const struct {
> + const char *name;
> + uint32_t engine_class;
> + } class_map[] = {
> + { "rcs", DRM_XE_ENGINE_CLASS_RENDER },
> + { "bcs", DRM_XE_ENGINE_CLASS_COPY },
> + { "vcs", DRM_XE_ENGINE_CLASS_VIDEO_DECODE },
> + { "vecs", DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE },
> + { "ccs", DRM_XE_ENGINE_CLASS_COMPUTE },
> + };
> + uint16_t mask = 0;
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(class_map); i++) {
> + if (strstr(names, class_map[i].name))
> + mask |= BIT(class_map[i].engine_class);
> + }
> +
> + return mask;
> +}
> +
> +/*
> + * Read the debugfs "info" file and OR together the engine class bitmasks from
> + * every line that contains @key. Returns UINT16_MAX if @key is not found
> + * (kernel too old to expose this information).
> + *
> + * multi_lrc_engine_classes is printed once at device level; multi_queue_engine_classes
> + * is printed once per GT, so the OR handles the multi-GT case transparently.
> + */
> +static uint16_t xe_device_query_engine_class_mask(int fd, const char *key)
> +{
> + char *line = NULL;
> + size_t line_len = 0;
> + uint16_t mask = UINT16_MAX;
> + size_t key_len = strlen(key);
> + int dbgfs_fd;
> + FILE *dbgfs_file;
> +
> + dbgfs_fd = igt_debugfs_open(fd, "info", O_RDONLY);
> + if (dbgfs_fd < 0)
> + return mask;
> +
> + dbgfs_file = fdopen(dbgfs_fd, "r");
> + if (!dbgfs_file) {
> + close(dbgfs_fd);
> + return mask;
> + }
> +
> + while (getline(&line, &line_len, dbgfs_file) != -1) {
> + const char *p = strstr(line, key);
> +
> + if (!p)
> + continue;
> +
> + if (mask == UINT16_MAX)
> + mask = 0;
> + mask |= parse_engine_class_mask(p + key_len);
> + }
> +
> + free(line);
> + fclose(dbgfs_file);
> +
> + return mask;
> +}
> +
> /**
> * xe_engine_class_supports_multi_queue:
> * @engine_class: engine class
> @@ -330,6 +411,11 @@ struct xe_device *xe_device_get(int fd)
> }
> pthread_mutex_unlock(&cache.cache_mutex);
>
> + xe_dev->multi_lrc_mask =
> + xe_device_query_engine_class_mask(fd, "multi_lrc_engine_classes");
> + xe_dev->multi_queue_engine_class_mask =
> + xe_device_query_engine_class_mask(fd, "multi_queue_engine_classes");
> +
> return xe_dev;
> }
>
> diff --git a/lib/xe/xe_query.h b/lib/xe/xe_query.h
> index 05e2ad84f..f33562bda 100644
> --- a/lib/xe/xe_query.h
> +++ b/lib/xe/xe_query.h
> @@ -79,6 +79,18 @@ struct xe_device {
>
> /** @pat_cache: cached PAT index configuration, NULL if not yet populated */
> struct intel_pat_cache *pat_cache;
> +
> + /**
> + * @multi_lrc_mask: bitmask of engine classes supporting multi-LRC.
> + * UINT16_MAX if not available (older kernel).
> + */
> + uint16_t multi_lrc_mask;
> +
> + /**
> + * @multi_queue_engine_class_mask: bitmask of engine classes supporting
> + * multi-queue. UINT16_MAX if not available (older kernel).
> + */
> + uint16_t multi_queue_engine_class_mask;
> };
>
> #define xe_for_each_engine(__fd, __hwe) \
>
> From patchwork Fri Apr 3 07:13:20 2026
> Content-Type: text/plain; charset="utf-8"
> MIME-Version: 1.0
> Content-Transfer-Encoding: 7bit
> Subject: [v2,2/4] lib/xe: add xe_engine_class_supports_multi_lrc()
> From: Xin Wang <x.wang@intel.com>
> X-Patchwork-Id: 716341
> Message-Id: <20260403071322.366766-3-x.wang@intel.com>
> To: igt-dev@lists.freedesktop.org
> Cc: Xin Wang <x.wang@intel.com>
> Date: Fri, 3 Apr 2026 00:13:20 -0700
>
> Add xe_engine_class_supports_multi_lrc() to query whether an engine
> class supports multi-LRC submission. When the kernel exposes the
> information via the debugfs info node it is used directly; otherwise
> fall back to a hardcoded default to keep compatibility with older KMD.
>
> Signed-off-by: Xin Wang <x.wang@intel.com>
> ---
> lib/xe/xe_query.c | 30 ++++++++++++++++++++++++++++++
> lib/xe/xe_query.h | 1 +
> 2 files changed, 31 insertions(+)
>
> diff --git a/lib/xe/xe_query.c b/lib/xe/xe_query.c
> index 441e95794..e13d5e143 100644
> --- a/lib/xe/xe_query.c
> +++ b/lib/xe/xe_query.c
> @@ -308,6 +308,36 @@ static struct xe_device *find_in_cache(int fd)
> return xe_dev;
> }
>
> +/**
> + * xe_engine_class_supports_multi_lrc:
> + * @fd: xe device fd
> + * @engine_class: engine class
> + *
> + * Returns true if multi LRC supported by engine class or false.
> + * Uses the kernel-reported bitmask from debugfs when available, otherwise
> + * falls back to the hardcoded per-class default.
> + */
> +bool xe_engine_class_supports_multi_lrc(int fd, uint32_t engine_class)
> +{
> + struct xe_device *xe_dev = find_in_cache(fd);
> +
> + if (xe_dev && xe_dev->multi_lrc_mask != UINT16_MAX)
> + return !!(xe_dev->multi_lrc_mask & BIT(engine_class));
> +
> + switch (engine_class) {
> + case DRM_XE_ENGINE_CLASS_COPY:
> + case DRM_XE_ENGINE_CLASS_COMPUTE:
> + case DRM_XE_ENGINE_CLASS_RENDER:
> + return false;
> + case DRM_XE_ENGINE_CLASS_VIDEO_DECODE:
> + case DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE:
> + return true;
> + default:
> + igt_warn("Engine class 0x%x unknown\n", engine_class);
> + return false;
> + }
> +}
> +
> static void xe_device_free(struct xe_device *xe_dev)
> {
> free(xe_dev->config);
> diff --git a/lib/xe/xe_query.h b/lib/xe/xe_query.h
> index f33562bda..5c26a3e88 100644
> --- a/lib/xe/xe_query.h
> +++ b/lib/xe/xe_query.h
> @@ -162,6 +162,7 @@ uint32_t xe_va_bits(int fd);
> uint16_t xe_dev_id(int fd);
> int xe_supports_faults(int fd);
> bool xe_engine_class_supports_multi_queue(uint32_t engine_class);
> +bool xe_engine_class_supports_multi_lrc(int fd, uint32_t engine_class);
> const char *xe_engine_class_string(uint32_t engine_class);
> const char *xe_engine_class_short_string(uint32_t engine_class);
> bool xe_has_engine_class(int fd, uint16_t engine_class);
>
> From patchwork Fri Apr 3 07:13:21 2026
> Content-Type: text/plain; charset="utf-8"
> MIME-Version: 1.0
> Content-Transfer-Encoding: 7bit
> Subject: [v2,3/4] lib/xe: use debugfs info to implement
> xe_engine_class_supports_multi_queue()
> From: Xin Wang <x.wang@intel.com>
> X-Patchwork-Id: 716343
> Message-Id: <20260403071322.366766-4-x.wang@intel.com>
> To: igt-dev@lists.freedesktop.org
> Cc: Xin Wang <x.wang@intel.com>
> Date: Fri, 3 Apr 2026 00:13:21 -0700
>
> Update xe_engine_class_supports_multi_queue() to take a device fd and
> use the kernel-reported multi_queue_engine_classes bitmask from debugfs
> when available, with a fallback to the hardcoded default for older KMD.
>
> Propagate the new fd argument through the xe_for_each_multi_queue_engine
> and xe_for_each_multi_queue_engine_class macros and all their call sites.
>
> Signed-off-by: Xin Wang <x.wang@intel.com>
> ---
> lib/xe/xe_query.c | 52 ++++++++++++++++++-------------
> lib/xe/xe_query.h | 8 ++---
> tests/intel/xe_exec_multi_queue.c | 4 +--
> tests/intel/xe_exec_threads.c | 4 +--
> 4 files changed, 38 insertions(+), 30 deletions(-)
>
> diff --git a/lib/xe/xe_query.c b/lib/xe/xe_query.c
> index e13d5e143..3286a3b37 100644
> --- a/lib/xe/xe_query.c
> +++ b/lib/xe/xe_query.c
> @@ -215,28 +215,6 @@ static uint16_t xe_device_query_engine_class_mask(int fd, const char *key)
> return mask;
> }
>
> -/**
> - * xe_engine_class_supports_multi_queue:
> - * @engine_class: engine class
> - *
> - * Returns true if multi queue supported by engine class or false.
> - */
> -bool xe_engine_class_supports_multi_queue(uint32_t engine_class)
> -{
> - switch (engine_class) {
> - case DRM_XE_ENGINE_CLASS_COPY:
> - case DRM_XE_ENGINE_CLASS_COMPUTE:
> - return true;
> - case DRM_XE_ENGINE_CLASS_RENDER:
> - case DRM_XE_ENGINE_CLASS_VIDEO_DECODE:
> - case DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE:
> - return false;
> - default:
> - igt_warn("Engine class 0x%x unknown\n", engine_class);
> - return false;
> - }
> -}
> -
> /**
> * xe_engine_class_string:
> * @engine_class: engine class
> @@ -338,6 +316,36 @@ bool xe_engine_class_supports_multi_lrc(int fd, uint32_t engine_class)
> }
> }
>
> +/**
> + * xe_engine_class_supports_multi_queue:
> + * @fd: xe device fd
> + * @engine_class: engine class
> + *
> + * Returns true if multi queue supported by engine class or false.
> + * Uses the kernel-reported bitmask from debugfs when available, otherwise
> + * falls back to the hardcoded per-class default.
> + */
> +bool xe_engine_class_supports_multi_queue(int fd, uint32_t engine_class)
> +{
> + struct xe_device *xe_dev = find_in_cache(fd);
> +
> + if (xe_dev && xe_dev->multi_queue_engine_class_mask != UINT16_MAX)
> + return !!(xe_dev->multi_queue_engine_class_mask & BIT(engine_class));
> +
> + switch (engine_class) {
> + case DRM_XE_ENGINE_CLASS_COPY:
> + case DRM_XE_ENGINE_CLASS_COMPUTE:
> + return true;
> + case DRM_XE_ENGINE_CLASS_RENDER:
> + case DRM_XE_ENGINE_CLASS_VIDEO_DECODE:
> + case DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE:
> + return false;
> + default:
> + igt_warn("Engine class 0x%x unknown\n", engine_class);
> + return false;
> + }
> +}
> +
> static void xe_device_free(struct xe_device *xe_dev)
> {
> free(xe_dev->config);
> diff --git a/lib/xe/xe_query.h b/lib/xe/xe_query.h
> index 5c26a3e88..8815c6c66 100644
> --- a/lib/xe/xe_query.h
> +++ b/lib/xe/xe_query.h
> @@ -113,10 +113,10 @@ struct xe_device {
>
> #define xe_for_each_multi_queue_engine(__fd, __hwe) \
> xe_for_each_engine(__fd, __hwe) \
> - for_if(xe_engine_class_supports_multi_queue((__hwe)->engine_class))
> -#define xe_for_each_multi_queue_engine_class(__class) \
> + for_if(xe_engine_class_supports_multi_queue(__fd, (__hwe)->engine_class))
> +#define xe_for_each_multi_queue_engine_class(__fd, __class) \
> xe_for_each_engine_class(__class) \
> - for_if(xe_engine_class_supports_multi_queue(__class))
> + for_if(xe_engine_class_supports_multi_queue(__fd, __class))
>
> #define XE_IS_CLASS_SYSMEM(__region) ((__region)->mem_class == DRM_XE_MEM_REGION_CLASS_SYSMEM)
> #define XE_IS_CLASS_VRAM(__region) ((__region)->mem_class == DRM_XE_MEM_REGION_CLASS_VRAM)
> @@ -161,7 +161,7 @@ uint32_t xe_get_default_alignment(int fd);
> uint32_t xe_va_bits(int fd);
> uint16_t xe_dev_id(int fd);
> int xe_supports_faults(int fd);
> -bool xe_engine_class_supports_multi_queue(uint32_t engine_class);
> +bool xe_engine_class_supports_multi_queue(int fd, uint32_t engine_class);
> bool xe_engine_class_supports_multi_lrc(int fd, uint32_t engine_class);
> const char *xe_engine_class_string(uint32_t engine_class);
> const char *xe_engine_class_short_string(uint32_t engine_class);
> diff --git a/tests/intel/xe_exec_multi_queue.c b/tests/intel/xe_exec_multi_queue.c
> index f987f8d6a..b5ded0633 100644
> --- a/tests/intel/xe_exec_multi_queue.c
> +++ b/tests/intel/xe_exec_multi_queue.c
> @@ -1052,7 +1052,7 @@ int igt_main()
>
> igt_subtest_f("sanity")
> xe_for_each_gt(fd, gt)
> - xe_for_each_multi_queue_engine_class(class)
> + xe_for_each_multi_queue_engine_class(fd, class)
> test_sanity(fd, gt, class);
>
> igt_subtest_f("exec-sanity")
> @@ -1061,7 +1061,7 @@ int igt_main()
>
> igt_subtest_f("virtual")
> xe_for_each_gt(fd, gt)
> - xe_for_each_multi_queue_engine_class(class)
> + xe_for_each_multi_queue_engine_class(fd, class)
> test_exec_virtual(fd, gt, class);
>
> igt_subtest_f("priority")
> diff --git a/tests/intel/xe_exec_threads.c b/tests/intel/xe_exec_threads.c
> index f082a0eda..ab9565beb 100644
> --- a/tests/intel/xe_exec_threads.c
> +++ b/tests/intel/xe_exec_threads.c
> @@ -1141,7 +1141,7 @@ static void threads(int fd, int flags)
> int gt;
>
> xe_for_each_engine(fd, hwe) {
> - if ((flags & MULTI_QUEUE) && !xe_engine_class_supports_multi_queue(hwe->engine_class))
> + if ((flags & MULTI_QUEUE) && !xe_engine_class_supports_multi_queue(fd, hwe->engine_class))
> continue;
> ++n_engines;
> }
> @@ -1170,7 +1170,7 @@ static void threads(int fd, int flags)
> }
>
> xe_for_each_engine(fd, hwe) {
> - if ((flags & MULTI_QUEUE) && !xe_engine_class_supports_multi_queue(hwe->engine_class))
> + if ((flags & MULTI_QUEUE) && !xe_engine_class_supports_multi_queue(fd, hwe->engine_class))
> continue;
> threads_data[i].mutex = &mutex;
> threads_data[i].cond = &cond;
>
> From patchwork Fri Apr 3 07:13:22 2026
> Content-Type: text/plain; charset="utf-8"
> MIME-Version: 1.0
> Content-Transfer-Encoding: 7bit
> Subject: [v2,4/4] tests/intel: skip or adjust tests for non-multi-LRC engine
> classes
> From: Xin Wang <x.wang@intel.com>
> X-Patchwork-Id: 716344
> Message-Id: <20260403071322.366766-5-x.wang@intel.com>
> To: igt-dev@lists.freedesktop.org
> Cc: Xin Wang <x.wang@intel.com>,
> Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com>,
> Matthew Brost <matthew.brost@intel.com>
> Date: Fri, 3 Apr 2026 00:13:22 -0700
>
> Use xe_engine_class_supports_multi_lrc() to guard multi-LRC test paths:
>
> - xe_exec_balancer: skip tests requiring multi-LRC if the engine class
> does not support it
> - xe_exec_reset: same guard for the balancer subtest
> - xe_drm_fdinfo: skip utilization_multi() for non-multi-LRC classes
> - xe_exec_threads: assert multi-LRC support before test_balancer()
> - xe_exec_multi_queue: only validate parallel-queue rejection (-EINVAL)
> for engine classes that support multi-LRC; non-multi-LRC classes do
> not accept the MULTI_GROUP flag at all so the check is irrelevant
>
> Cc: Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com>
> Cc: Matthew Brost <matthew.brost@intel.com>
> Signed-off-by: Xin Wang <x.wang@intel.com>
> ---
> tests/intel/xe_drm_fdinfo.c | 2 +-
> tests/intel/xe_exec_balancer.c | 6 +++---
> tests/intel/xe_exec_multi_queue.c | 2 +-
> tests/intel/xe_exec_reset.c | 2 +-
> tests/intel/xe_exec_threads.c | 1 +
> 5 files changed, 7 insertions(+), 6 deletions(-)
>
> diff --git a/tests/intel/xe_drm_fdinfo.c b/tests/intel/xe_drm_fdinfo.c
> index 411ca6ec4..3c113ed5d 100644
> --- a/tests/intel/xe_drm_fdinfo.c
> +++ b/tests/intel/xe_drm_fdinfo.c
> @@ -673,7 +673,7 @@ utilization_multi(int fd, int gt, int class, unsigned int flags)
> igt_assert(virtual ^ parallel);
>
> num_placements = xe_gt_fill_engines_by_class(fd, gt, class, eci);
> - if (num_placements < 2)
> + if (num_placements < 2 || !xe_engine_class_supports_multi_lrc(fd, class))
> return;
>
> igt_debug("Target class: %s\n", engine_map[class]);
> diff --git a/tests/intel/xe_exec_balancer.c b/tests/intel/xe_exec_balancer.c
> index 9cd641b4e..3c6ec45f1 100644
> --- a/tests/intel/xe_exec_balancer.c
> +++ b/tests/intel/xe_exec_balancer.c
> @@ -57,7 +57,7 @@ static bool test_all_active(int fd, int gt, int class)
> int i, num_placements;
>
> num_placements = xe_gt_fill_engines_by_class(fd, gt, class, eci);
> - if (num_placements < 2)
> + if (num_placements < 2 || !xe_engine_class_supports_multi_lrc(fd, class))
> return false;
>
> vm = xe_vm_create(fd, 0, 0);
> @@ -187,7 +187,7 @@ test_exec(int fd, int gt, int class, int n_exec_queues, int n_execs,
> igt_assert_lte(n_exec_queues, MAX_N_EXEC_QUEUES);
>
> num_placements = xe_gt_fill_engines_by_class(fd, gt, class, eci);
> - if (num_placements < 2)
> + if (num_placements < 2 || !xe_engine_class_supports_multi_lrc(fd, class))
> return false;
>
> vm = xe_vm_create(fd, 0, 0);
> @@ -402,7 +402,7 @@ test_cm(int fd, int gt, int class, int n_exec_queues, int n_execs,
> igt_assert_lte(n_exec_queues, MAX_N_EXEC_QUEUES);
>
> num_placements = xe_gt_fill_engines_by_class(fd, gt, class, eci);
> - if (num_placements < 2)
> + if (num_placements < 2 || !xe_engine_class_supports_multi_lrc(fd, class))
> return false;
>
> vm = xe_vm_create(fd, DRM_XE_VM_CREATE_FLAG_LR_MODE, 0);
> diff --git a/tests/intel/xe_exec_multi_queue.c b/tests/intel/xe_exec_multi_queue.c
> index b5ded0633..ca96099d3 100644
> --- a/tests/intel/xe_exec_multi_queue.c
> +++ b/tests/intel/xe_exec_multi_queue.c
> @@ -112,7 +112,7 @@ __test_sanity(int fd, int gt, int class, bool preempt_mode)
>
> /* Multi-Queue can't be a parallel queue */
> multi_queue.value = DRM_XE_MULTI_GROUP_CREATE;
> - if (n > 1)
> + if (n > 1 && xe_engine_class_supports_multi_lrc(fd, class))
> igt_assert_eq(__xe_exec_queue_create(fd, vm, 2, 1, eci, ext, &val), -EINVAL);
>
> /* Specifying multiple MULTI_GROUP property is invalid */
> diff --git a/tests/intel/xe_exec_reset.c b/tests/intel/xe_exec_reset.c
> index 7aaee31dd..95191139d 100644
> --- a/tests/intel/xe_exec_reset.c
> +++ b/tests/intel/xe_exec_reset.c
> @@ -184,7 +184,7 @@ test_balancer(int fd, int gt, int class, int n_exec_queues, int n_execs,
> fd = drm_open_driver(DRIVER_XE);
>
> num_placements = xe_gt_fill_engines_by_class(fd, gt, class, eci);
> - if (num_placements < 2)
> + if (num_placements < 2 || !xe_engine_class_supports_multi_lrc(fd, class))
> return;
>
> vm = xe_vm_create(fd, 0, 0);
> diff --git a/tests/intel/xe_exec_threads.c b/tests/intel/xe_exec_threads.c
> index ab9565beb..7b8100c5b 100644
> --- a/tests/intel/xe_exec_threads.c
> +++ b/tests/intel/xe_exec_threads.c
> @@ -85,6 +85,7 @@ test_balancer(int fd, int gt, uint32_t vm, uint64_t addr, uint64_t userptr,
>
> num_placements = xe_gt_fill_engines_by_class(fd, gt, class, eci);
> igt_assert_lt(1, num_placements);
> + igt_assert(xe_engine_class_supports_multi_lrc(fd, class));

The series looks good to me

Reviewed-by: Daniel Charles <daniel.charles@intel.com>

>
> bo_size = sizeof(*data) * n_execs;
> bo_size = xe_bb_size(fd, bo_size);