From: Iago Toral <itoral@igalia.com>
To: Maíra Canal <mcanal@igalia.com>, Melissa Wen <mwen@igalia.com>,
Tvrtko Ursulin <tvrtko.ursulin@igalia.com>,
David Airlie <airlied@gmail.com>, Simona Vetter <simona@ffwll.ch>
Cc: kernel-dev@igalia.com, dri-devel@lists.freedesktop.org
Subject: Re: [PATCH v2 3/4] drm/v3d: Serialize jobs across queues when a perfmon is attached
Date: Tue, 02 Jun 2026 09:35:15 +0200 [thread overview]
Message-ID: <7a9d503de81b403585962f1c25dd0b506a6eb3f2.camel@igalia.com> (raw)
In-Reply-To: <20260531-v3d-perfmon-lifetime-v2-3-60ed4485a203@igalia.com>
Hi Maíra, this pactch looks good to me but I am wondering if we should
do anything to clean up the perfmon state when a gpu reset happens:
i.e. what would happen if a perfmon job resets? Would we continue to
schedule after-reset jobs to wait on the fence of the reset job?
Iago
El dom, 31-05-2026 a las 17:18 -0300, Maíra Canal escribió:
> A non-global perfmon is meant to count events generated by a specific
> submission, but the scheduler can run jobs from different queues
> concurrently on the same V3D core. Without explicit serialization, an
> unrelated job running in parallel with a perfmon-carrying job
> pollutes
> the counters and generates unusable results.
>
> To address such issue, we must enforce cross-queue serialization when
> we
> detect a perfmon-carrying submission. It's possible to implement
> serialization by enforcing two rules:
>
> 1. A job that carries a non-global perfmon must wait for every job
> currently in-flight across all HW queues to finish.
>
> 2. While a perfmon-carrying job is still in-flight, all
> subsequently
> submitted jobs must wait for it.
>
> Note that serialization is not needed in the global perfmon case, as
> the
> global perfmon tracks activity from all jobs, so concurrency is
> desirable.
>
> Therefore, check if serialization is needed during job submission and
> if
> so, attach fence dependences to enforce cross-queue serialization.
>
> Signed-off-by: Maíra Canal <mcanal@igalia.com>
> ---
> drivers/gpu/drm/v3d/v3d_drv.h | 35 ++++++++++++++++++----
> drivers/gpu/drm/v3d/v3d_gem.c | 3 ++
> drivers/gpu/drm/v3d/v3d_perfmon.c | 6 ++++
> drivers/gpu/drm/v3d/v3d_submit.c | 63
> +++++++++++++++++++++++++++++++++++++++
> 4 files changed, 101 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/gpu/drm/v3d/v3d_drv.h
> b/drivers/gpu/drm/v3d/v3d_drv.h
> index 51486af68cf4..cdf4926d51f2 100644
> --- a/drivers/gpu/drm/v3d/v3d_drv.h
> +++ b/drivers/gpu/drm/v3d/v3d_drv.h
> @@ -74,11 +74,13 @@ struct v3d_queue_state {
> spinlock_t queue_lock;
> };
>
> -/* Performance monitor object. The perform lifetime is controlled by
> userspace
> - * using perfmon related ioctls. A perfmon can be attached to a
> submit_cl
> - * request, and when this is the case, HW perf counters will be
> activated just
> - * before the submit_cl is submitted to the GPU and disabled when
> the job is
> - * done. This way, only events related to a specific job will be
> counted.
> +/* Performance monitor object
> + *
> + * The performance monitor (perfmon) lifetime is controlled by
> userspace using
> + * perfmon related ioctls. A perfmon can be attached to a CL or CSD
> submission
> + * request, and when it is, HW performance counters will be
> activated just
> + * before the job is submitted to the GPU and disabled when the job
> is done.
> + * This way, only events related to a specific submission will be
> counted.
> */
> struct v3d_perfmon {
> /* Tracks the number of users of the perfmon, when this
> counter reaches
> @@ -167,13 +169,31 @@ struct v3d_dev {
>
> struct v3d_queue_state queue[V3D_MAX_QUEUES];
>
> - /* Tracks the performance monitor state. */
> + /*
> + * Tracks the performance monitor state and consistency.
> + *
> + * When a non-global perfmon is attached to a job, the
> scheduler must
> + * not run any other job on the HW concurrently (otherwise,
> the
> + * counters would be polluted by unrelated work).
> + */
> struct {
> /* Protects @active. */
> spinlock_t lock;
>
> /* Perfmon currently programmed in HW (or NULL if
> none). */
> struct v3d_perfmon *active;
> +
> + /* Finished fence of the most recently submitted job
> that
> + * opened a serialization window (i.e. a job with a
> non-global
> + * perfmon attached).
> + */
> + struct dma_fence *fence;
> +
> + /* Finished fence of the most recently submitted job
> on each HW
> + * queue. Used so that a new perfmon-carrying job
> can depend on
> + * every job currently in-flight across all queues.
> + */
> + struct dma_fence *last_hw_fence[V3D_MAX_QUEUES];
> } perfmon_state;
>
> /* Protects bo_stats */
> @@ -319,6 +339,9 @@ struct v3d_job {
>
> struct v3d_dev *v3d;
>
> + /* The queue that the job was submitted on. */
> + enum v3d_queue queue;
> +
> /* This is the array of BOs that were looked up at the start
> * of submission.
> */
> diff --git a/drivers/gpu/drm/v3d/v3d_gem.c
> b/drivers/gpu/drm/v3d/v3d_gem.c
> index 9487ab7acd03..0b415486f5b4 100644
> --- a/drivers/gpu/drm/v3d/v3d_gem.c
> +++ b/drivers/gpu/drm/v3d/v3d_gem.c
> @@ -363,8 +363,11 @@ v3d_gem_destroy(struct drm_device *dev)
> for (q = 0; q < V3D_MAX_QUEUES; q++) {
> WARN_ON(v3d->queue[q].active_job);
> v3d_stats_put(v3d->queue[q].stats);
> + dma_fence_put(v3d->perfmon_state.last_hw_fence[q]);
> }
>
> + dma_fence_put(v3d->perfmon_state.fence);
> +
> drm_mm_takedown(&v3d->mm);
>
> dma_free_coherent(v3d->drm.dev, 4096 * 1024, (void *)v3d-
> >pt,
> diff --git a/drivers/gpu/drm/v3d/v3d_perfmon.c
> b/drivers/gpu/drm/v3d/v3d_perfmon.c
> index 3ad0f022753c..07dab7fb3060 100644
> --- a/drivers/gpu/drm/v3d/v3d_perfmon.c
> +++ b/drivers/gpu/drm/v3d/v3d_perfmon.c
> @@ -275,6 +275,12 @@ void v3d_perfmon_start(struct v3d_dev *v3d,
> struct v3d_perfmon *perfmon)
> if (!perfmon || v3d->global_perfmon)
> return;
>
> + /* Cross-queue serialization should have drained any
> previous perfmon
> + * job before this one runs.
> + */
> + if (WARN_ON_ONCE(v3d->perfmon_state.active))
> + return;
> +
> if (!pm_runtime_get_if_active(v3d->drm.dev))
> return;
>
> diff --git a/drivers/gpu/drm/v3d/v3d_submit.c
> b/drivers/gpu/drm/v3d/v3d_submit.c
> index 4c526aafc4e0..46fc88bacc95 100644
> --- a/drivers/gpu/drm/v3d/v3d_submit.c
> +++ b/drivers/gpu/drm/v3d/v3d_submit.c
> @@ -234,6 +234,7 @@ v3d_submit_add_job(struct v3d_submit *submit,
> void **container, size_t size,
> job->v3d = v3d;
> job->free = free;
> job->file_priv = v3d_priv;
> + job->queue = queue;
>
> ret = drm_sched_job_init(&job->base, &v3d_priv-
> >sched_entity[queue],
> 1, v3d_priv, submit->file_priv-
> >client_id);
> @@ -293,6 +294,62 @@ v3d_attach_perfmon_to_jobs(struct v3d_submit
> *submit, u32 perfmon_id)
> return 0;
> }
>
> +/*
> + * Prepare fences to enforce job serialization when a perfmon is
> active. A job
> + * that carries a non-global perfmon must wait for every job
> currently in-flight
> + * across all HW queues to finish, otherwise concurrent unrelated
> work on the
> + * same core would pollute the performance counters. Symmetrically,
> while such a
> + * job is still in-flight, all subsequently submitted jobs must wait
> for it.
> + *
> + * We don't serialize the jobs when using a global perfmon as it's
> expected to
> + * track concurrent activity from all jobs.
> + */
> +static int
> +v3d_serialize_for_perfmon(struct v3d_job *job)
> +{
> + struct v3d_dev *v3d = job->v3d;
> + bool is_global_perfmon;
> + int ret;
> +
> + lockdep_assert_held(&v3d->sched_lock);
> +
> + scoped_guard(spinlock_irqsave, &v3d->perfmon_state.lock)
> + is_global_perfmon = !!v3d->global_perfmon;
> +
> + if (is_global_perfmon)
> + goto publish;
> +
> + if (job->perfmon) {
> + for (enum v3d_queue q = 0; q < V3D_MAX_QUEUES; q++)
> {
> + struct dma_fence *f = v3d-
> >perfmon_state.last_hw_fence[q];
> +
> + if (!f || dma_fence_is_signaled(f))
> + continue;
> +
> + ret = drm_sched_job_add_dependency(&job-
> >base, dma_fence_get(f));
> + if (ret)
> + return ret;
> + }
> + } else if (v3d->perfmon_state.fence &&
> + !dma_fence_is_signaled(v3d->perfmon_state.fence))
> {
> + ret = drm_sched_job_add_dependency(&job->base,
> +
> dma_fence_get(v3d->perfmon_state.fence));
> + if (ret)
> + return ret;
> + }
> +
> +publish:
> + dma_fence_put(v3d->perfmon_state.last_hw_fence[job->queue]);
> + v3d->perfmon_state.last_hw_fence[job->queue] =
> dma_fence_get(job->done_fence);
> +
> + if (job->perfmon && !is_global_perfmon) {
> + dma_fence_put(v3d->perfmon_state.fence);
> + v3d->perfmon_state.fence = dma_fence_get(job-
> >done_fence);
> + }
> +
> + return 0;
> +}
> +
> static void
> v3d_submit_attach_object_fences(struct v3d_submit *submit)
> {
> @@ -337,6 +394,12 @@ v3d_submit_jobs(struct v3d_submit *submit)
> goto err;
> }
>
> + for (int i = 0; i < submit->job_count; i++) {
> + ret = v3d_serialize_for_perfmon(submit->jobs[i]);
> + if (ret)
> + goto err;
> + }
> +
> for (int i = 0; i < submit->job_count; i++)
> drm_sched_entity_push_job(&submit->jobs[i]->base);
>
>
next prev parent reply other threads:[~2026-06-02 7:35 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-31 20:18 [PATCH v2 0/4] drm/v3d: Fix perfmon locking and cross-queue isolation Maíra Canal
2026-05-31 20:18 ` [PATCH v2 1/4] drm/v3d: Fix global performance monitor reference counting Maíra Canal
2026-06-04 4:44 ` Claude review: " Claude Code Review Bot
2026-05-31 20:18 ` [PATCH v2 2/4] drm/v3d: Refactor perfmon locking Maíra Canal
2026-06-01 11:52 ` Iago Toral
2026-06-01 12:03 ` Maíra Canal
2026-06-04 4:44 ` Claude review: " Claude Code Review Bot
2026-05-31 20:18 ` [PATCH v2 3/4] drm/v3d: Serialize jobs across queues when a perfmon is attached Maíra Canal
2026-06-02 7:35 ` Iago Toral [this message]
2026-06-02 10:49 ` Maíra Canal
2026-06-02 11:10 ` Iago Toral
2026-06-04 4:44 ` Claude review: " Claude Code Review Bot
2026-05-31 20:18 ` [PATCH v2 4/4] drm/v3d: Drop the queue argument from v3d_job_add_syncobjs() Maíra Canal
2026-06-04 4:44 ` Claude review: " Claude Code Review Bot
2026-06-04 4:44 ` Claude review: drm/v3d: Fix perfmon locking and cross-queue isolation Claude Code Review Bot
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=7a9d503de81b403585962f1c25dd0b506a6eb3f2.camel@igalia.com \
--to=itoral@igalia.com \
--cc=airlied@gmail.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=kernel-dev@igalia.com \
--cc=mcanal@igalia.com \
--cc=mwen@igalia.com \
--cc=simona@ffwll.ch \
--cc=tvrtko.ursulin@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