panfrost: Fix GEM handle refcount leak in panfrost_bo_import

panfrost_bo_import() calls drmPrimeFDToHandle() then pan_kmod_bo_import(),
which also calls drmPrimeFDToHandle() internally. This double import causes
GEM handle refcount leaks because each drmPrimeFDToHandle() increments the
kernel's GEM handle refcount, but only one drmCloseBufferHandle() is called
during cleanup by panfrost_kmod_bo_free(or panthor_kmod_bo_free).

Fix by removing the redundant drmPrimeFDToHandle() and using
pan_kmod_bo_import() directly. On re-import of existing buffers, properly
release the extra pan_kmod_bo reference with pan_kmod_bo_put().

This ensures GEM handle refcount, pan_kmod_bo refcount, and panfrost_bo
refcount are all properly balanced.

Fixes: 5089a758df ("panfrost: Back panfrost_bo with pan_kmod_bo object")

Signed-off-by: Xianzhong Li <xianzhong.li@nxp.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Eric R. Smith <eric.smith@collabora.com>
(cherry picked from commit 248b0b47b7)

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40979>
This commit is contained in:
Xianzhong Li 2026-04-03 14:17:07 +08:00 committed by Eric Engestrom
parent e8955066a7
commit 871a933cac
2 changed files with 17 additions and 5 deletions

View file

@ -3744,7 +3744,7 @@
"description": "panfrost: Fix GEM handle refcount leak in panfrost_bo_import",
"nominated": true,
"nomination_type": 2,
"resolution": 0,
"resolution": 1,
"main_sha": null,
"because_sha": "5089a758dfb1760a0dc397b0f491c4a6dc2160af",
"notes": null

View file

@ -471,18 +471,24 @@ struct panfrost_bo *
panfrost_bo_import(struct panfrost_device *dev, int fd)
{
struct panfrost_bo *bo;
ASSERTED int ret;
struct pan_kmod_bo *kmod_bo;
unsigned gem_handle;
pthread_mutex_lock(&dev->bo_map_lock);
ret = drmPrimeFDToHandle(dev->kmod.dev->fd, fd, &gem_handle);
assert(!ret);
/* Import via pan_kmod_bo_import - it handles drmPrimeFDToHandle internally */
kmod_bo = pan_kmod_bo_import(dev->kmod.dev, fd, 0);
if (!kmod_bo) {
pthread_mutex_unlock(&dev->bo_map_lock);
return NULL;
}
/* Look up panfrost_bo by the GEM handle from kmod_bo */
gem_handle = pan_kmod_bo_handle(kmod_bo);
bo = pan_lookup_bo(dev, gem_handle);
if (!bo->dev) {
bo->dev = dev;
bo->kmod_bo = pan_kmod_bo_import(dev->kmod.dev, fd, 0);
bo->kmod_bo = kmod_bo; /* Take ownership of kmod_bo reference */
struct pan_kmod_vm_op vm_op = {
.type = PAN_KMOD_VM_OP_TYPE_MAP,
@ -510,6 +516,12 @@ panfrost_bo_import(struct panfrost_device *dev, int fd)
if ((dev->debug & PAN_DBG_DUMP) && panfrost_bo_mmap(bo))
mesa_loge("failed to mmap");
} else {
/* pan_kmod_bo_import already incremented kmod_bo->refcnt,
* but we already have a reference in bo->kmod_bo,
* so release this extra kmod_bo reference.
*/
pan_kmod_bo_put(kmod_bo);
/* bo->refcnt == 0 can happen if the BO
* was being released but panfrost_bo_import() acquired the
* lock before panfrost_bo_unreference(). In that case, refcnt