From: "Maíra Canal" <mcanal@igalia.com>
To: Iago Toral <itoral@igalia.com>, Melissa Wen <mwen@igalia.com>,
Kamil Konieczny <kamil.konieczny@linux.intel.com>,
Juha-Pekka Heikkila <juhapekka.heikkila@gmail.com>,
Bhanuprakash Modem <bhanuprakash.modem@gmail.com>,
Ashutosh Dixit <ashutosh.dixit@intel.com>,
Karthik B S <karthik.b.s@intel.com>
Cc: igt-dev@lists.freedesktop.org, kernel-dev@igalia.com,
"Maíra Canal" <mcanal@igalia.com>
Subject: [PATCH i-g-t 5/5] tests/v3d_perfmon: Add global perfmon and counter isolation tests
Date: Fri, 8 May 2026 09:42:54 -0300 [thread overview]
Message-ID: <20260508124446.1260672-7-mcanal@igalia.com> (raw)
In-Reply-To: <20260508124446.1260672-2-mcanal@igalia.com>
Exercise the DRM_IOCTL_V3D_PERFMON_SET_GLOBAL ioctl with a set of
subtests covering:
- Input validation: invalid flags, invalid perfmon id, and clearing a
perfmon that was never set as global all return errors.
- Single-perfmon semantics: setting a second global perfmon while one is
already active returns EBUSY.
- Implicit clear on destroy: destroying a global perfmon releases the
slot, allowing a new one to be set without EBUSY.
- Precedence over per-job perfmon: submitting a CL job with a per-job
perfmon_id while a global perfmon is active is rejected with EAGAIN.
- Cross-fd tracking: a global perfmon set on one file descriptor
accumulates cycles from CL jobs submitted on a second file descriptor,
and that second fd cannot install its own global perfmon while the
first is active.
- fd close: closing a file descriptor that owns an active global
perfmon clears it cleanly without crashing the driver.
Add two counter isolation subtests:
- perfmon-counter-isolation: blocks 100 CSD jobs behind a sw_sync fence,
then submits a CL job with a perfmon. The driver's queue serialization
must drain all pending CSD work before opening the perfmon window, so
compute-active-cycles must read zero after the CL job completes.
- perfmon-counter-isolation-subsequent-cl: a non-perfmon CL job submitted
after a perfmon CL job must be serialized behind it and must not
contribute to its counter values.
Also add get-values-check-monotonically, which submits ten CL jobs in a
loop and asserts that each get-values call returns strictly larger values
than the previous one.
Signed-off-by: Maíra Canal <mcanal@igalia.com>
---
tests/v3d/v3d_perfmon.c | 529 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 528 insertions(+), 1 deletion(-)
diff --git a/tests/v3d/v3d_perfmon.c b/tests/v3d/v3d_perfmon.c
index f6f2a2774..1e854b8cc 100644
--- a/tests/v3d/v3d_perfmon.c
+++ b/tests/v3d/v3d_perfmon.c
@@ -1,19 +1,25 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2022 Igalia S.L.
+ * Copyright © 2026 Raspberry Pi Ltd
*/
+#include <sys/ioctl.h>
+
#include "igt.h"
+#include "igt_syncobj.h"
#include "igt_v3d.h"
+#include "sw_sync.h"
IGT_TEST_DESCRIPTION("Tests for the V3D's performance monitors");
int igt_main()
{
- int fd;
+ int fd, ver;
igt_fixture() {
fd = drm_open_driver(DRIVER_V3D);
+ ver = igt_v3d_get_version(fd);
igt_require(igt_v3d_get_param(fd, DRM_V3D_PARAM_SUPPORTS_PERFMON));
}
@@ -124,6 +130,65 @@ int igt_main()
igt_v3d_perfmon_destroy(fd, id);
}
+ igt_describe("Make sure the values returned by the perfmon are increasing monotonically.");
+ igt_subtest("get-values-check-monotonically") {
+ struct v3d_cl_job *job = igt_v3d_noop_job(fd);
+ uint32_t id, out_sync;
+ uint64_t *prev, *curr;
+ uint8_t counters[4];
+
+ /*
+ * Different V3D versions have different performance counters
+ * IDs for the same counters.
+ */
+ if (ver >= 71) {
+ counters[0] = 3; /* CLE-bin-thread-active-cycles */
+ counters[1] = 64; /* PTB-memory-writes */
+ counters[2] = 66; /* core-memory-reads */
+ counters[3] = 71; /* PTB-memory-words-writes */
+ } else if (ver >= 42) {
+ counters[0] = V3D_PERFCNT_CLE_ACTIVE;
+ counters[1] = V3D_PERFCNT_PTB_MEM_WRITES;
+ counters[2] = V3D_PERFCNT_CORE_MEM_READS;
+ counters[3] = V3D_PERFCNT_PTB_W_MEM_WORDS;
+ } else
+ igt_abort_on_f(true, "This V3D version is not supported.");
+
+ id = igt_v3d_perfmon_create(fd, ARRAY_SIZE(counters), counters);
+
+ prev = calloc(ARRAY_SIZE(counters), sizeof(*prev));
+ curr = calloc(ARRAY_SIZE(counters), sizeof(*curr));
+
+ igt_v3d_perfmon_get_values(fd, id, prev);
+
+ for (int i = 0; i < ARRAY_SIZE(counters); i++)
+ igt_assert_eq(prev[i], 0);
+
+ out_sync = syncobj_create(fd, 0);
+ job->submit->out_sync = out_sync;
+ job->submit->perfmon_id = id;
+
+ /* Check if the counters are increasing monotonically. */
+ for (int i = 0; i < 10; i++) {
+ do_ioctl(fd, DRM_IOCTL_V3D_SUBMIT_CL, job->submit);
+ igt_assert(syncobj_wait(fd, &out_sync, 1, INT64_MAX, 0, NULL));
+
+ igt_v3d_perfmon_get_values(fd, id, curr);
+
+ for (int j = 0; j < ARRAY_SIZE(counters); j++)
+ igt_assert_lt(prev[j], curr[j]);
+
+ igt_swap(prev, curr);
+ }
+
+ syncobj_destroy(fd, out_sync);
+ igt_v3d_free_cl_job(fd, job);
+ igt_v3d_perfmon_destroy(fd, id);
+
+ free(prev);
+ free(curr);
+ }
+
igt_describe("Make sure that destroying a non-existent perfmon fails.");
igt_subtest("destroy-invalid-perfmon") {
struct drm_v3d_perfmon_destroy destroy = {
@@ -151,6 +216,468 @@ int igt_main()
do_ioctl_err(fd, DRM_IOCTL_V3D_PERFMON_GET_VALUES, &get, EINVAL);
}
+ igt_describe("Make sure that setting a global perfmon fails for an invalid flag.");
+ igt_subtest("global-perfmon-invalid-flag") {
+ struct drm_v3d_perfmon_set_global set_global = {
+ .flags = 0xdeadbeef,
+ };
+ do_ioctl_err(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global, EINVAL);
+ }
+
+ igt_describe("Make sure that setting a global perfmon fails for an invalid identifier.");
+ igt_subtest("global-perfmon-invalid-perfmon") {
+ struct drm_v3d_perfmon_set_global set_global = {
+ .id = 0xdeadbeef,
+ };
+ do_ioctl_err(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global, EINVAL);
+ }
+
+ igt_describe("Make sure that clearing a valid identifier that it's not a global perfmon fails.");
+ igt_subtest("global-perfmon-clear-unset-perfmon") {
+ uint8_t counters[] = { V3D_PERFCNT_AXI_WRITE_STALLS_WATCH_1,
+ V3D_PERFCNT_TMU_CONFIG_ACCESSES,
+ V3D_PERFCNT_TLB_PARTIAL_QUADS,
+ V3D_PERFCNT_L2T_SLC0_READS };
+ uint32_t id = igt_v3d_perfmon_create(fd, ARRAY_SIZE(counters), counters);
+ struct drm_v3d_perfmon_set_global set_global = {
+ .id = id,
+ .flags = DRM_V3D_PERFMON_CLEAR_GLOBAL,
+ };
+
+ do_ioctl_err(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global, EINVAL);
+
+ igt_v3d_perfmon_destroy(fd, id);
+ }
+
+ igt_describe("Make sure that the IOCTL fails when one sets a global perfmon, but one is already set.");
+ igt_subtest("global-perfmon-set-valid-perfmon-twice") {
+ uint8_t counters[] = { V3D_PERFCNT_L2T_HITS, V3D_PERFCNT_L2T_MISSES };
+ uint32_t id1 = igt_v3d_perfmon_create(fd, ARRAY_SIZE(counters), counters);
+ uint32_t id2 = igt_v3d_perfmon_create(fd, ARRAY_SIZE(counters), counters);
+
+ struct drm_v3d_perfmon_set_global set_global = {
+ .id = id1,
+ };
+ do_ioctl(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global);
+
+ set_global.id = id2;
+ do_ioctl_err(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global, EBUSY);
+
+ igt_v3d_perfmon_destroy(fd, id1);
+ igt_v3d_perfmon_destroy(fd, id2);
+ }
+
+ igt_describe("Make sure that destroying a global perfmon clears it even without "
+ "DRM_V3D_PERFMON_CLEAR_GLOBAL.");
+ igt_subtest("global-perfmon-destroy") {
+ uint8_t counters[] = { V3D_PERFCNT_AXI_WRITE_STALLS_WATCH_1,
+ V3D_PERFCNT_L2T_SLC0_READS };
+ uint32_t id1 = igt_v3d_perfmon_create(fd, ARRAY_SIZE(counters), counters);
+ uint32_t id2 = igt_v3d_perfmon_create(fd, ARRAY_SIZE(counters), counters);
+ struct drm_v3d_perfmon_set_global set_global = { .id = id1 };
+
+ do_ioctl(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global);
+
+ igt_v3d_perfmon_destroy(fd, id1);
+
+ /* Returns success as global perfmon was cleared during destroy. */
+ set_global.id = id2;
+ do_ioctl(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global);
+
+ igt_v3d_perfmon_destroy(fd, id2);
+ }
+
+ igt_describe("Make sure that submitting a per-job perfmon is rejected when a "
+ "global perfmon is active, and that the global perfmon keeps tracking.");
+ igt_subtest("global-perfmon-precedes-per-job") {
+ struct drm_v3d_perfmon_set_global set_global = { 0 };
+ struct v3d_cl_job *job = igt_v3d_noop_job(fd);
+ uint32_t id_global, id_job, out_sync;
+ uint64_t values;
+ uint8_t counter;
+ int ret;
+
+ /*
+ * Different V3D versions have different performance counters
+ * IDs for the same counters.
+ */
+ if (ver >= 71)
+ counter = 0; /* cycle-count */
+ else if (ver >= 42)
+ counter = V3D_PERFCNT_CYCLE_COUNT;
+ else
+ igt_abort_on_f(true, "This V3D version is not supported.");
+
+ id_global = igt_v3d_perfmon_create(fd, 1, &counter);
+ id_job = igt_v3d_perfmon_create(fd, 1, &counter);
+
+ set_global.id = id_global;
+ do_ioctl(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global);
+
+ /* Submitting a per-job perfmon while a global one is active must
+ * be rejected: the global perfmon takes precedence.
+ */
+ job->submit->perfmon_id = id_job;
+
+ /* Don't use drmIoctl(), as it restarts the ioctl if errno is EAGAIN. */
+ ret = ioctl(fd, DRM_IOCTL_V3D_SUBMIT_CL, job->submit);
+ igt_assert_eq(ret, -1);
+ igt_assert_eq(errno, EAGAIN);
+
+ /* Submit a plain job (no per-job perfmon). */
+ out_sync = syncobj_create(fd, 0);
+ job->submit->perfmon_id = 0;
+ job->submit->out_sync = out_sync;
+ do_ioctl(fd, DRM_IOCTL_V3D_SUBMIT_CL, job->submit);
+ igt_assert(syncobj_wait(fd, &out_sync, 1, INT64_MAX, 0, NULL));
+
+ /* The global perfmon kept tracking. */
+ igt_v3d_perfmon_get_values(fd, id_global, &values);
+ igt_assert_lt(0, values);
+
+ igt_v3d_perfmon_destroy(fd, id_global);
+ igt_v3d_perfmon_destroy(fd, id_job);
+
+ syncobj_destroy(fd, out_sync);
+ igt_v3d_free_cl_job(fd, job);
+ }
+
+ igt_describe("Make sure that the global perfmon is tracking all jobs consistently.");
+ igt_subtest("global-perfmon-valid-perfmon") {
+ struct drm_v3d_perfmon_set_global set_global = { 0 };
+ struct v3d_cl_job *job = igt_v3d_noop_job(fd);
+ uint64_t *values1, *values2;
+ uint32_t id, out_sync;
+ uint8_t counters[2];
+
+ /*
+ * Different V3D versions have different performance counters
+ * IDs for the same counters.
+ */
+ if (ver >= 71) {
+ counters[0] = 0; /* cycle-count */
+ counters[1] = 1; /* core-active */
+ } else if (ver >= 42) {
+ counters[0] = V3D_PERFCNT_CYCLE_COUNT;
+ counters[1] = V3D_PERFCNT_CLE_ACTIVE;
+ } else
+ igt_abort_on_f(true, "This V3D version is not supported.");
+
+ id = igt_v3d_perfmon_create(fd, ARRAY_SIZE(counters), counters);
+
+ values1 = calloc(ARRAY_SIZE(counters), sizeof(*values1));
+ values2 = calloc(ARRAY_SIZE(counters), sizeof(*values2));
+
+ set_global.id = id;
+ do_ioctl(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global);
+
+ igt_v3d_perfmon_get_values(fd, id, values1);
+
+ out_sync = syncobj_create(fd, 0);
+ job->submit->out_sync = out_sync;
+
+ /* Submit an arbitrary number of CL jobs to stress the perfcnts. */
+ for (int i = 0; i < 10; i++)
+ do_ioctl(fd, DRM_IOCTL_V3D_SUBMIT_CL, job->submit);
+
+ /* Wait for the last job to complete so counters are captured. */
+ igt_assert(syncobj_wait(fd, &out_sync, 1, INT64_MAX, 0, NULL));
+
+ igt_v3d_perfmon_get_values(fd, id, values2);
+
+ /* As the global perfmon is active, the cycle-count must have
+ * increased with time.
+ */
+ for (int i = 0; i < ARRAY_SIZE(counters); i++)
+ igt_assert_lt(values1[i], values2[i]);
+
+ set_global.flags = DRM_V3D_PERFMON_CLEAR_GLOBAL;
+ do_ioctl(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global);
+
+ igt_v3d_perfmon_get_values(fd, id, values1);
+
+ /* Submit an arbitrary number of CL jobs to stress the perfcnts. */
+ for (int i = 0; i < 10; i++)
+ do_ioctl(fd, DRM_IOCTL_V3D_SUBMIT_CL, job->submit);
+
+ igt_v3d_perfmon_get_values(fd, id, values2);
+
+ /* As the global perfmon was cleared, the perfmon is inactive
+ * and it must preserve its values.
+ */
+ for (int i = 0; i < ARRAY_SIZE(counters); i++)
+ igt_assert_eq(values1[i], values2[i]);
+
+ igt_v3d_perfmon_destroy(fd, id);
+
+ syncobj_destroy(fd, out_sync);
+ igt_v3d_free_cl_job(fd, job);
+
+ free(values1);
+ free(values2);
+ }
+
+ igt_describe("Make sure that a global perfmon tracks jobs from other file descriptors.");
+ igt_subtest("global-perfmon-multifd") {
+ struct drm_v3d_perfmon_set_global set_global = { 0 },
+ set_global2 = { 0 };
+ int fd2 = drm_open_driver_render(DRIVER_V3D);
+ struct v3d_cl_job *job = igt_v3d_noop_job(fd2);
+ uint32_t id1, id2, out_sync;
+ uint64_t values1, values2;
+ uint8_t counter;
+
+ /*
+ * Different V3D versions have different performance counters
+ * IDs for the same counters.
+ */
+ if (ver >= 71)
+ counter = 0; /* cycle-count */
+ else if (ver >= 42)
+ counter = V3D_PERFCNT_CYCLE_COUNT;
+ else
+ igt_abort_on_f(true, "This V3D version is not supported.");
+
+ /* Create and set global perfmon from fd. */
+ id1 = igt_v3d_perfmon_create(fd, 1, &counter);
+ set_global.id = id1;
+ do_ioctl(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global);
+
+ igt_v3d_perfmon_get_values(fd, id1, &values1);
+
+ /* Submit jobs from the second fd; the global perfmon should track them. */
+ out_sync = syncobj_create(fd2, 0);
+ job->submit->out_sync = out_sync;
+ for (int i = 0; i < 10; i++)
+ do_ioctl(fd2, DRM_IOCTL_V3D_SUBMIT_CL, job->submit);
+ igt_assert(syncobj_wait(fd2, &out_sync, 1, INT64_MAX, 0, NULL));
+
+ igt_v3d_perfmon_get_values(fd, id1, &values2);
+ igt_assert_lt(values1, values2);
+
+ /* fd2 cannot set its own global perfmon while fd's is active. */
+ id2 = igt_v3d_perfmon_create(fd2, 1, &counter);
+ set_global2.id = id2;
+ do_ioctl_err(fd2, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global2, EBUSY);
+
+ igt_v3d_perfmon_destroy(fd2, id2);
+ igt_v3d_perfmon_destroy(fd, id1);
+
+ syncobj_destroy(fd2, out_sync);
+ igt_v3d_free_cl_job(fd2, job);
+
+ drm_close_driver(fd2);
+ }
+
+ igt_describe("Make sure closing a file descriptor with an active global "
+ "perfmon does not crash the driver.");
+ igt_subtest("global-perfmon-destroy-multifd") {
+ struct drm_v3d_perfmon_set_global set_global = { 0 };
+ struct v3d_cl_job *job = igt_v3d_noop_job(fd);
+ int fd2 = drm_open_driver(DRIVER_V3D);
+ uint32_t id, new_id, out_sync;
+ uint8_t counter = 0;
+
+ /* Create and set global perfmon from fd2. */
+ id = igt_v3d_perfmon_create(fd2, 1, &counter);
+ set_global.id = id;
+ do_ioctl(fd2, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global);
+
+ /* Keep HW busy from fd while fd2 is closed. */
+ out_sync = syncobj_create(fd, 0);
+ job->submit->out_sync = out_sync;
+ for (int i = 0; i < 10; i++)
+ do_ioctl(fd, DRM_IOCTL_V3D_SUBMIT_CL, job->submit);
+
+ /* Closing fd2 must cleanly stop the active global perfmon and
+ * clear the global perfmon without crashing.
+ */
+ drm_close_driver(fd2);
+
+ igt_assert(syncobj_wait(fd, &out_sync, 1, INT64_MAX, 0, NULL));
+
+ /* fd can now set a new global perfmon without EBUSY. */
+ new_id = igt_v3d_perfmon_create(fd, 1, &counter);
+
+ set_global.id = new_id;
+ do_ioctl(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global);
+
+ set_global.flags = DRM_V3D_PERFMON_CLEAR_GLOBAL;
+ do_ioctl(fd, DRM_IOCTL_V3D_PERFMON_SET_GLOBAL, &set_global);
+
+ igt_v3d_perfmon_destroy(fd, new_id);
+
+ syncobj_destroy(fd, out_sync);
+ igt_v3d_free_cl_job(fd, job);
+ }
+
+ igt_describe("Make sure a perfmon CL job is not contaminated by concurrent CSD "
+ "work on a different queue.");
+ igt_subtest("perfmon-counter-isolation") {
+ struct v3d_csd_job *csd_job = igt_v3d_empty_shader(fd);
+ struct v3d_cl_job *cl_job = igt_v3d_noop_job(fd);
+ uint32_t id, blocker, cl_out;
+ int timeline, fence_fd;
+ uint8_t counter;
+ uint64_t values;
+
+ igt_require_sw_sync();
+
+ /*
+ * compute-active-cycles is zero during pure CL work and non-zero
+ * while a CSD job is executing. Submitting CSD jobs alongside a
+ * perfmon CL job lets us distinguish "CSD ran while the perfmon
+ * window was open" (isolation broken) from "CSD finished before
+ * the perfmon window opened" (isolation correct).
+ */
+ if (ver >= 71)
+ counter = 4; /* compute-active-cycles */
+ else if (ver >= 42)
+ counter = V3D_PERFCNT_COMPUTE_ACTIVE;
+ else
+ igt_abort_on_f(true, "This V3D version is not supported.");
+
+ id = igt_v3d_perfmon_create(fd, 1, &counter);
+
+ /*
+ * Create an unsignaled fence via sw_sync. All CSD jobs will
+ * wait on this fence and stay queued until we increment the
+ * sw_sync timeline.
+ */
+ timeline = sw_sync_timeline_create();
+ fence_fd = sw_sync_timeline_create_fence(timeline, 1);
+
+ blocker = syncobj_create(fd, 0);
+ syncobj_import_sync_file(fd, blocker, fence_fd);
+ close(fence_fd);
+
+ cl_out = syncobj_create(fd, 0);
+
+ /*
+ * Block all CSD jobs with the unsignaled SW fence so they are
+ * still queued when the CL perfmon job is submitted.
+ */
+ csd_job->submit->in_sync = blocker;
+ for (int i = 0; i < 100; i++)
+ do_ioctl(fd, DRM_IOCTL_V3D_SUBMIT_CSD, csd_job->submit);
+
+ /*
+ * Submit the CL job with the perfmon. v3d perfmon serialization
+ * must add a fence dependency on the CSD done fences so the CL
+ * job is forced to wait for all previous jobs to complete before
+ * its perfmon window opens.
+ */
+ cl_job->submit->out_sync = cl_out;
+ cl_job->submit->perfmon_id = id;
+ do_ioctl(fd, DRM_IOCTL_V3D_SUBMIT_CL, cl_job->submit);
+
+ /*
+ * With serialization, the CL job waits for all CSD work to finish
+ * first, so the perfmon sees only pure CL activity. Without it,
+ * the two queues would race and CSD cycles would pollute the counter.
+ */
+ sw_sync_timeline_inc(timeline, 1);
+ igt_assert(syncobj_wait(fd, &cl_out, 1, INT64_MAX, 0, NULL));
+
+ igt_v3d_perfmon_get_values(fd, id, &values);
+
+ /* All CSD activity finished before the perfmon window opened,
+ * so no compute cycles must have been counted during the CL job.
+ */
+ igt_assert_eq(values, 0);
+
+ close(timeline);
+ igt_v3d_perfmon_destroy(fd, id);
+
+ syncobj_destroy(fd, blocker);
+ syncobj_destroy(fd, cl_out);
+ igt_v3d_free_cl_job(fd, cl_job);
+ igt_v3d_free_csd_job(fd, csd_job);
+ }
+
+ igt_describe("Make sure a non-perfmon CL job submitted after a perfmon CL job "
+ "is serialized behind it and does not contaminate its counter values.");
+ igt_subtest("perfmon-counter-isolation-subsequent-cl") {
+ struct v3d_cl_job *job1 = igt_v3d_noop_job(fd);
+ struct v3d_cl_job *job2 = igt_v3d_noop_job(fd);
+ uint64_t id, values, values_after_j2;
+ uint32_t blocker, out1, out2;
+ int timeline, fence_fd;
+ uint8_t counter;
+
+ igt_require_sw_sync();
+
+ /*
+ * Different V3D versions have different performance counters IDs
+ * for the same counters.
+ */
+ if (ver >= 71)
+ counter = 0; /* cycle-count */
+ else if (ver >= 42)
+ counter = V3D_PERFCNT_CYCLE_COUNT;
+ else
+ igt_abort_on_f(true, "This V3D version is not supported.");
+
+ id = igt_v3d_perfmon_create(fd, 1, &counter);
+
+ /*
+ * Create an unsignaled fence via sw_sync. job1 will wait on
+ * this fence and stay blocked until we increment the timeline.
+ */
+ timeline = sw_sync_timeline_create();
+ fence_fd = sw_sync_timeline_create_fence(timeline, 1);
+
+ blocker = syncobj_create(fd, 0);
+ syncobj_import_sync_file(fd, blocker, fence_fd);
+ close(fence_fd);
+
+ out1 = syncobj_create(fd, 0);
+ out2 = syncobj_create(fd, 0);
+
+ /* job1: BIN job is blocked until we signal the blocker. */
+ job1->submit->in_sync_bcl = blocker;
+ job1->submit->perfmon_id = id;
+ job1->submit->out_sync = out1;
+ do_ioctl(fd, DRM_IOCTL_V3D_SUBMIT_CL, job1->submit);
+
+ /* job2: no perfmon, must wait for job1 due to job serialization. */
+ job2->submit->out_sync = out2;
+ do_ioctl(fd, DRM_IOCTL_V3D_SUBMIT_CL, job2->submit);
+
+ /* job2 is serialized behind job1, so it cannot have completed yet. */
+ igt_assert_eq(syncobj_wait_err(fd, &out2, 1, 0, 0), -ETIME);
+
+ /* While job1 has not started, the perfmon must still read zero. */
+ igt_v3d_perfmon_get_values(fd, id, &values);
+ igt_assert_eq(values, 0);
+
+ /* Release the blocker. */
+ sw_sync_timeline_inc(timeline, 1);
+
+ igt_assert(syncobj_wait(fd, &out1, 1, INT64_MAX, 0, NULL));
+ igt_assert(syncobj_wait(fd, &out2, 1, INT64_MAX, 0, NULL));
+
+ /* job1 must have contributed cycles to the perfmon. */
+ igt_v3d_perfmon_get_values(fd, id, &values);
+ igt_assert_lt(0, values);
+
+ /* job2 doesn't have a perfmon attached, so a second read must
+ * return the same value.
+ */
+ igt_v3d_perfmon_get_values(fd, id, &values_after_j2);
+ igt_assert_eq(values, values_after_j2);
+
+ close(timeline);
+ syncobj_destroy(fd, blocker);
+ syncobj_destroy(fd, out1);
+ syncobj_destroy(fd, out2);
+
+ igt_v3d_perfmon_destroy(fd, id);
+
+ igt_v3d_free_cl_job(fd, job1);
+ igt_v3d_free_cl_job(fd, job2);
+ }
+
igt_fixture()
drm_close_driver(fd);
}
--
2.54.0
next prev parent reply other threads:[~2026-05-08 12:47 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-08 12:42 [PATCH i-g-t 0/5] tests/v3d_perfmon: Fix V3D 7.1 regression and extend perfmon testing Maíra Canal
2026-05-08 12:42 ` [PATCH i-g-t 1/5] tests/v3d_perfmon: Don't use the deprecated V3D_PERFCNT_NUM Maíra Canal
2026-05-08 12:42 ` [PATCH i-g-t 2/5] drm-uapi/v3d: Sync v3d UAPI Maíra Canal
2026-05-08 12:42 ` [PATCH i-g-t 3/5] lib/v3d: Allow callers to retrieve perfmon counter values Maíra Canal
2026-05-08 12:42 ` [PATCH i-g-t 4/5] lib/v3d: Add helper to query the V3D hardware version Maíra Canal
2026-05-08 12:42 ` Maíra Canal [this message]
2026-05-12 6:50 ` [PATCH i-g-t 5/5] tests/v3d_perfmon: Add global perfmon and counter isolation tests Iago Toral
2026-05-09 0:21 ` ✓ i915.CI.BAT: success for tests/v3d_perfmon: Fix V3D 7.1 regression and extend perfmon testing Patchwork
2026-05-09 0:28 ` ✓ Xe.CI.BAT: " Patchwork
2026-05-09 13:32 ` ✗ Xe.CI.FULL: failure " Patchwork
2026-05-10 1:38 ` ✓ i915.CI.Full: success " 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=20260508124446.1260672-7-mcanal@igalia.com \
--to=mcanal@igalia.com \
--cc=ashutosh.dixit@intel.com \
--cc=bhanuprakash.modem@gmail.com \
--cc=igt-dev@lists.freedesktop.org \
--cc=itoral@igalia.com \
--cc=juhapekka.heikkila@gmail.com \
--cc=kamil.konieczny@linux.intel.com \
--cc=karthik.b.s@intel.com \
--cc=kernel-dev@igalia.com \
--cc=mwen@igalia.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