public inbox for igt-dev@lists.freedesktop.org
 help / color / mirror / Atom feed
From: Soham Purkait <soham.purkait@intel.com>
To: igt-dev@lists.freedesktop.org, riana.tauro@intel.com,
	badal.nilawar@intel.com, kamil.konieczny@intel.com,
	vinay.belgaumkar@intel.com
Cc: anshuman.gupta@intel.com, soham.purkait@intel.com,
	tvrtko.ursulin@igalia.com, tursulin@ursulin.net,
	lucas.de.marchi@gmail.com
Subject: [PATCH i-g-t v10 3/5] tools/gputop.src/xe_gputop: Use direct ioctls with card_fd cleanup
Date: Mon,  6 Apr 2026 11:34:26 +0530	[thread overview]
Message-ID: <20260406060428.2734117-4-soham.purkait@intel.com> (raw)
In-Reply-To: <20260406060428.2734117-1-soham.purkait@intel.com>

During xe engine initialization, card_fd is needed to query engines
and obtain PMU busyness configuration, but keeping it open makes the
process appear as a DRM client, which conflicts with KMS test
requirements. To avoid this, close card_fd once engine discovery is
complete and replace the lib/xe/* dependency with direct ioctl calls,
simplifying the implementation and reducing IGT test library dependencies.

v1:
 - Initialize pmu_device_obj to null.
v2:
 - Remove dependency on lib/xe/* libraries. (Kamil)
v3:
 - Refactor ioctl failure check. (Vinay)
 - Check q.size only once. (Vinay)
 - Add num_bytes for asprintf return value. (Vinay)
v4:
 - Replace malloc/memset with calloc. (Vinay)
 - Refactor the commit message to make it concise. (Kamil)

Fixes: c8106465683f ("tools/gputop/xe_gputop: Add gputop support for xe specific devices")
Signed-off-by: Soham Purkait <soham.purkait@intel.com>

Reviewed-by: Vinay Belgaumkar <vinay.belgaumkar@intel.com>
---
 tools/gputop.src/xe_gputop.c | 150 ++++++++++++++++++++++++++---------
 tools/gputop.src/xe_gputop.h |   8 +-
 2 files changed, 115 insertions(+), 43 deletions(-)

diff --git a/tools/gputop.src/xe_gputop.c b/tools/gputop.src/xe_gputop.c
index bb2caa6ea..30f9c593f 100644
--- a/tools/gputop.src/xe_gputop.c
+++ b/tools/gputop.src/xe_gputop.c
@@ -1,8 +1,11 @@
 // SPDX-License-Identifier: MIT
 /*
- * Copyright © 2025 Intel Corporation
+ * Copyright © 2025-2026 Intel Corporation
  */
 
+#include <sys/ioctl.h>
+
+#include "igt_perf.h"
 #include "xe_gputop.h"
 
 #define engine_ptr(pmu_device, n) (&(pmu_device)->engine + (n))
@@ -65,7 +68,7 @@ void xe_clean_up(void *obj, int len)
 			}
 			if (gputop_dev[i].pmu_device_obj->device)
 				free(gputop_dev[i].pmu_device_obj->device);
-			free(gputop_dev->pmu_device_obj);
+			free(gputop_dev[i].pmu_device_obj);
 		}
 	}
 }
@@ -92,6 +95,7 @@ void xe_gputop_init(void *ptr, int index,
 
 	obj = ((struct xe_gputop *)ptr) + index;
 	obj->card = card;
+	obj->pmu_device_obj = NULL;
 }
 
 static int pmu_format_shift(int xe, const char *name)
@@ -121,20 +125,50 @@ static int engine_cmp(const void *__a, const void *__b)
 		return a->drm_xe_engine.engine_instance - b->drm_xe_engine.engine_instance;
 }
 
+static int engine_query(int fd, struct drm_xe_query_engines **engine_q)
+{
+	struct drm_xe_device_query q;
+	int ret = 0, num_eng;
+
+	memset(&q, 0, sizeof(q));
+	q.query = DRM_XE_DEVICE_QUERY_ENGINES;
+
+	ret = ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &q);
+	if (ret != 0 || q.size == 0)
+		return 0;
+
+	num_eng = (int)((q.size - sizeof(struct drm_xe_query_engines)) /
+			sizeof(struct drm_xe_engine_class_instance));
+
+	*engine_q = (struct drm_xe_query_engines *)calloc(1, q.size);
+	if (!*engine_q)
+		return -1;
+
+	q.data = (uintptr_t)(*engine_q);
+
+	ret = ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &q);
+	if (ret != 0) {
+		free(*engine_q);
+		return -1;
+	}
+
+	return num_eng;
+}
+
 void *xe_populate_engines(const void *obj, int index)
 {
 	struct xe_gputop *ptr = ((struct xe_gputop *)obj) + index;
 	struct igt_device_card *card = ptr->card;
 	uint64_t engine_active_config, engine_total_config;
 	uint64_t engine_class, engine_instance, gt_shift;
-	struct drm_xe_engine_class_instance *hwe;
-	struct xe_pmu_device *engines;
+	struct drm_xe_query_engines *engine_q = NULL;
+	struct xe_pmu_device *engines = NULL;
 	char device[30];
-	int ret = 0;
-	int card_fd;
+	int ret = 0, num_eng;
+	int card_fd = -1;
 
-	if (!card || !strlen(card->card) || !strlen(card->render))
-		return NULL;
+	if (!card || (!strlen(card->card) && !strlen(card->render)))
+		goto err;
 
 	if (strlen(card->card)) {
 		card_fd = igt_open_card(card);
@@ -142,39 +176,53 @@ void *xe_populate_engines(const void *obj, int index)
 		card_fd = igt_open_render(card);
 	} else {
 		fprintf(stderr, "Failed to detect device!\n");
-		return NULL;
+		goto err;
+	}
+
+	num_eng = engine_query(card_fd, &engine_q);
+	if (num_eng <= 0) {
+		fprintf(stderr, "Engine query failed!\n");
+		goto err;
 	}
-	xe_device_get(card_fd);
-	engines = malloc(sizeof(struct xe_pmu_device) +
-			 xe_number_engines(card_fd) * sizeof(struct xe_engine));
-	if (!engines)
-		return NULL;
 
-	memset(engines, 0, sizeof(struct xe_pmu_device) +
-	       xe_number_engines(card_fd) * sizeof(struct xe_engine));
+	engines = calloc(1, sizeof(struct xe_pmu_device) +
+			 num_eng * sizeof(struct xe_engine));
+	if (!engines)
+		goto err;
 
 	engines->num_engines = 0;
 	gt_shift = pmu_format_shift(card_fd, "gt");
 	engine_class = pmu_format_shift(card_fd, "engine_class");
 	engine_instance = pmu_format_shift(card_fd, "engine_instance");
 	xe_perf_device(card_fd, device, sizeof(device));
+	close(card_fd);
+	card_fd = -1;
+
 	engines->device = strdup(device);
+	if (!engines->device) {
+		errno = ENOMEM;
+		goto err;
+	}
 	ret = perf_event_config(device, "engine-active-ticks", &engine_active_config);
 	if (ret < 0)
-		return NULL;
+		goto err;
 
 	ret = perf_event_config(device, "engine-total-ticks", &engine_total_config);
 	if (ret < 0)
-		return NULL;
+		goto err;
 
-	xe_for_each_engine(card_fd, hwe) {
+	while (engines->num_engines < engine_q->num_engines) {
+		int num_bytes = 0;
 		uint64_t  param_config;
 		struct xe_engine *engine;
+		struct drm_xe_engine_class_instance hwe;
+
+		hwe = engine_q->engines[engines->num_engines].instance;
 
 		engine = engine_ptr(engines, engines->num_engines);
-		param_config = (uint64_t)hwe->gt_id << gt_shift | hwe->engine_class << engine_class
-			| hwe->engine_instance << engine_instance;
-		engine->drm_xe_engine = *hwe;
+		param_config = (uint64_t)hwe.gt_id << gt_shift | hwe.engine_class << engine_class
+			| hwe.engine_instance << engine_instance;
+		engine->drm_xe_engine = hwe;
 		engine->engine_active_ticks.config = engine_active_config | param_config;
 		engine->engine_total_ticks.config = engine_total_config | param_config;
 
@@ -184,12 +232,12 @@ void *xe_populate_engines(const void *obj, int index)
 			break;
 		}
 
-		ret = asprintf(&engine->display_name, "GT:%u %s/%u",
-			       hwe->gt_id,
-			       class_display_name(engine->drm_xe_engine.engine_class),
-			       engine->drm_xe_engine.engine_instance);
+		num_bytes = asprintf(&engine->display_name, "GT:%u %s/%u",
+				     hwe.gt_id,
+				     class_display_name(engine->drm_xe_engine.engine_class),
+				     engine->drm_xe_engine.engine_instance);
 
-		if (ret <= 0) {
+		if (num_bytes <= 0) {
 			ret = errno;
 			break;
 		}
@@ -197,9 +245,12 @@ void *xe_populate_engines(const void *obj, int index)
 		engines->num_engines++;
 	}
 
-	if (!ret) {
+	free(engine_q);
+	engine_q = NULL;
+
+	if (ret) {
 		errno = ret;
-		return NULL;
+		goto err;
 	}
 
 	qsort(engine_ptr(engines, 0), engines->num_engines,
@@ -208,9 +259,26 @@ void *xe_populate_engines(const void *obj, int index)
 	ptr->pmu_device_obj = engines;
 
 	return engines;
+
+err:
+	if (card_fd >= 0)
+		close(card_fd);
+	if (engines) {
+		for (unsigned int i = 0; i < engines->num_engines; i++) {
+			struct xe_engine *engine = engine_ptr(engines, i);
+
+			free(engine->display_name);
+		}
+		if (engines->device)
+			free(engines->device);
+		free(engines);
+	}
+	if (engine_q)
+		free(engine_q);
+	return NULL;
 }
 
-static uint64_t pmu_read_multi(int fd, unsigned int num, uint64_t *val)
+static int pmu_read_multi(int fd, unsigned int num, uint64_t *val)
 {
 	uint64_t buf[2 + num];
 	unsigned int i;
@@ -219,15 +287,16 @@ static uint64_t pmu_read_multi(int fd, unsigned int num, uint64_t *val)
 	memset(buf, 0, sizeof(buf));
 
 	len = read(fd, buf, sizeof(buf));
-	assert(len == sizeof(buf));
+	if (len != sizeof(buf))
+		return -1;
 
 	for (i = 0; i < num; i++)
 		val[i] = buf[2 + i];
 
-	return buf[1];
+	return 0;
 }
 
-void xe_pmu_sample(const void *obj, int index)
+int xe_pmu_sample(const void *obj, int index)
 {
 	struct xe_gputop *ptr = ((struct xe_gputop *)obj) + index;
 	struct xe_pmu_device *engines = ptr->pmu_device_obj;
@@ -235,7 +304,8 @@ void xe_pmu_sample(const void *obj, int index)
 	uint64_t val[2 + num_val];
 	unsigned int i;
 
-	pmu_read_multi(engines->fd, num_val, val);
+	if (pmu_read_multi(engines->fd, num_val, val) < 0)
+		return -1;
 
 	for (i = 0; i < engines->num_engines; i++) {
 		struct xe_engine *engine = engine_ptr(engines, i);
@@ -243,6 +313,8 @@ void xe_pmu_sample(const void *obj, int index)
 		update_sample(&engine->engine_active_ticks, val);
 		update_sample(&engine->engine_total_ticks, val);
 	}
+
+	return 0;
 }
 
 int xe_pmu_init(const void *obj, int index)
@@ -263,11 +335,11 @@ int xe_pmu_init(const void *obj, int index)
 		fd = _open_pmu(type, &engines->num_counters, &engine->engine_active_ticks,
 			       &engines->fd);
 		if (fd < 0)
-			return -1;
+			return fd;
 		fd = _open_pmu(type, &engines->num_counters, &engine->engine_total_ticks,
 			       &engines->fd);
 		if (fd < 0)
-			return -1;
+			return fd;
 	}
 	return 0;
 }
@@ -280,7 +352,10 @@ static double pmu_active_percentage(struct xe_engine *engine)
 				 engine->engine_total_ticks.val.prev;
 	double percentage;
 
-	percentage = (pmu_active_ticks * 100) / pmu_total_ticks;
+	if (pmu_total_ticks)
+		percentage = (pmu_active_ticks * 100) / pmu_total_ticks;
+	else
+		percentage = 0.0;
 
 	return clamp(percentage, 0., 100.);
 }
@@ -359,4 +434,3 @@ int xe_print_engines(const void *obj, int index, int lines, int w, int h)
 
 	return lines;
 }
-
diff --git a/tools/gputop.src/xe_gputop.h b/tools/gputop.src/xe_gputop.h
index 1e3856071..d89db400c 100644
--- a/tools/gputop.src/xe_gputop.h
+++ b/tools/gputop.src/xe_gputop.h
@@ -1,17 +1,15 @@
 /* SPDX-License-Identifier: MIT */
 /*
- * Copyright © 2025 Intel Corporation
+ * Copyright © 2025-2026 Intel Corporation
  */
 
 #ifndef __XE_GPUTOP_H__
 #define __XE_GPUTOP_H__
 
 #include <dirent.h>
+#include <xe_drm.h>
 
-#include "igt_device_scan.h"
-#include "igt_perf.h"
 #include "utils.h"
-#include "xe/xe_query.h"
 
 struct xe_pmu_pair {
 	uint64_t cur;
@@ -52,7 +50,7 @@ struct xe_gputop {
 void xe_gputop_init(void *ptr, int index, struct igt_device_card *card);
 void *xe_populate_engines(const void *obj, int index);
 int xe_pmu_init(const void *obj, int index);
-void xe_pmu_sample(const void *obj, int index);
+int xe_pmu_sample(const void *obj, int index);
 int xe_print_engines(const void *obj, int index, int lines, int w, int h);
 void xe_clean_up(void *obj, int len);
 
-- 
2.34.1


  parent reply	other threads:[~2026-04-06  6:12 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-06  6:04 [PATCH i-g-t v10 0/5] Close any open drm device after engine initialization in GPUTOP Soham Purkait
2026-04-06  6:04 ` [PATCH i-g-t v10 1/5] tools: Rename tools/gputop to tools/gputop.src Soham Purkait
2026-04-07  7:45   ` Jani Nikula
2026-04-08  5:50     ` Purkait, Soham
2026-04-06  6:04 ` [PATCH i-g-t v10 2/5] tools/gputop.src/utils: Add clamp macro to remove dependency on lib/xe/* Soham Purkait
2026-04-06  6:04 ` Soham Purkait [this message]
2026-04-06  6:04 ` [PATCH i-g-t v10 4/5] tools/gputop.src/gputop: Add support for per-engine busyness monitoring Soham Purkait
2026-04-06  9:04   ` Tvrtko Ursulin
2026-04-08  7:50     ` Purkait, Soham
2026-04-09  8:52       ` Tvrtko Ursulin
2026-04-06  6:04 ` [PATCH i-g-t v10 5/5] tools/gputop.src/gputop: Add command line option for device filter Soham Purkait
2026-04-06  8:25 ` ✓ Xe.CI.BAT: success for Close any open drm device after engine initialization in GPUTOP (rev11) Patchwork
2026-04-06  8:42 ` ✓ i915.CI.BAT: " Patchwork
2026-04-06 11:21 ` ✗ Xe.CI.FULL: failure " Patchwork
2026-04-06 12:32 ` ✗ i915.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=20260406060428.2734117-4-soham.purkait@intel.com \
    --to=soham.purkait@intel.com \
    --cc=anshuman.gupta@intel.com \
    --cc=badal.nilawar@intel.com \
    --cc=igt-dev@lists.freedesktop.org \
    --cc=kamil.konieczny@intel.com \
    --cc=lucas.de.marchi@gmail.com \
    --cc=riana.tauro@intel.com \
    --cc=tursulin@ursulin.net \
    --cc=tvrtko.ursulin@igalia.com \
    --cc=vinay.belgaumkar@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