drm/v3d: Fix global performance monitor reference counting

In the SET_GLOBAL ioctl, v3d_perfmon_find() bumps the reference count on
the perfmon it returns, but v3d_perfmon_set_global_ioctl() and
v3d_perfmon_delete() fail to release that reference on several paths:

  1. v3d_perfmon_set_global_ioctl() leaks the reference on its error
     paths.

  2. CLEAR_GLOBAL leaks both the find reference and the reference
     previously stashed in v3d->global_perfmon by the SET_GLOBAL ioctl
     that configured it.

  3. Destroying a perfmon that is the current global perfmon leaks the
     reference stashed by the SET_GLOBAL ioctl.

Release each of these references explicitly.

Cc: stable@vger.kernel.org
Fixes: c6eabbab35 ("drm/v3d: Add DRM_IOCTL_V3D_PERFMON_SET_GLOBAL")
Reviewed-by: Iago Toral Quiroga <itoral@igalia.com>
Link: https://patch.msgid.link/20260531-v3d-perfmon-lifetime-v2-1-60ed4485a203@igalia.com
Signed-off-by: Maíra Canal <mcanal@igalia.com>
master
Maíra Canal 2026-05-31 17:18:55 -03:00
parent 30252e6f71
commit 6bf7e2affc
No known key found for this signature in database
GPG Key ID: 3FF30E8A7688FAAA
1 changed files with 19 additions and 5 deletions

View File

@ -309,8 +309,11 @@ static void v3d_perfmon_delete(struct v3d_file_priv *v3d_priv,
if (perfmon == v3d->active_perfmon)
v3d_perfmon_stop(v3d, perfmon, false);
/* If the global perfmon is being destroyed, set it to NULL */
cmpxchg(&v3d->global_perfmon, perfmon, NULL);
/* If the global perfmon is being destroyed, clean it and release
* the reference stashed in v3d_perfmon_set_global_ioctl().
*/
if (cmpxchg(&v3d->global_perfmon, perfmon, NULL) == perfmon)
v3d_perfmon_put(perfmon);
v3d_perfmon_put(perfmon);
}
@ -461,16 +464,27 @@ int v3d_perfmon_set_global_ioctl(struct drm_device *dev, void *data,
/* If the request is to clear the global performance monitor */
if (req->flags & DRM_V3D_PERFMON_CLEAR_GLOBAL) {
if (!v3d->global_perfmon)
struct v3d_perfmon *old;
/* DRM_V3D_PERFMON_CLEAR_GLOBAL doesn't check if
* v3d->global_perfmon == perfmon. Therefore, there
* is no need to keep perfmon's reference.
*/
v3d_perfmon_put(perfmon);
old = xchg(&v3d->global_perfmon, NULL);
if (!old)
return -EINVAL;
xchg(&v3d->global_perfmon, NULL);
v3d_perfmon_put(old);
return 0;
}
if (cmpxchg(&v3d->global_perfmon, NULL, perfmon))
if (cmpxchg(&v3d->global_perfmon, NULL, perfmon)) {
v3d_perfmon_put(perfmon);
return -EBUSY;
}
return 0;
}