From: Maíra Canal Date: Sun, 31 May 2026 20:18:55 +0000 (-0300) Subject: drm/v3d: Fix global performance monitor reference counting X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=6bf7e2affc6e62da7add393d7f352d4040f5bc27;p=thirdparty%2Fkernel%2Flinux.git 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: c6eabbab359c ("drm/v3d: Add DRM_IOCTL_V3D_PERFMON_SET_GLOBAL") Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260531-v3d-perfmon-lifetime-v2-1-60ed4485a203@igalia.com Signed-off-by: Maíra Canal --- diff --git a/drivers/gpu/drm/v3d/v3d_perfmon.c b/drivers/gpu/drm/v3d/v3d_perfmon.c index 8e0249580bba..ecfd446ff75f 100644 --- a/drivers/gpu/drm/v3d/v3d_perfmon.c +++ b/drivers/gpu/drm/v3d/v3d_perfmon.c @@ -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; }