Skip to content

Commit 9ce4aef

Browse files
DarksonnDanilo Krummrich
authored andcommitted
drm/gpuvm: take GEM lock inside drm_gpuvm_bo_obtain_prealloc()
When calling drm_gpuvm_bo_obtain_prealloc() and using immediate mode, this may result in a call to ops->vm_bo_free(vm_bo) while holding the GEMs gpuva mutex. This is a problem if ops->vm_bo_free(vm_bo) performs any operations that are not safe in the fence signalling critical path, and it turns out that Panthor (the only current user of the method) calls drm_gem_shmem_unpin() which takes a resv lock internally. This constitutes both a violation of signalling safety and lock inversion. To fix this, we modify the method to internally take the GEMs gpuva mutex so that the mutex can be unlocked before freeing the preallocated vm_bo. Note that this modification introduces a requirement that the driver uses immediate mode to call drm_gpuvm_bo_obtain_prealloc() as it would otherwise take the wrong lock. Fixes: 63e919a ("panthor: use drm_gpuva_unlink_defer()") Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com> Signed-off-by: Alice Ryhl <aliceryhl@google.com> Link: https://patch.msgid.link/20260108-gpuvm-rust-v2-1-dbd014005a0b@google.com Signed-off-by: Danilo Krummrich <dakr@kernel.org>
1 parent 7f6721b commit 9ce4aef

2 files changed

Lines changed: 48 additions & 31 deletions

File tree

drivers/gpu/drm/drm_gpuvm.c

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,14 +1602,48 @@ drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm,
16021602
}
16031603
EXPORT_SYMBOL_GPL(drm_gpuvm_bo_create);
16041604

1605+
/*
1606+
* drm_gpuvm_bo_destroy_not_in_lists() - final part of drm_gpuvm_bo cleanup
1607+
* @vm_bo: the &drm_gpuvm_bo to destroy
1608+
*
1609+
* It is illegal to call this method if the @vm_bo is present in the GEMs gpuva
1610+
* list, the extobj list, or the evicted list.
1611+
*
1612+
* Note that this puts a refcount on the GEM object, which may destroy the GEM
1613+
* object if the refcount reaches zero. It's illegal for this to happen if the
1614+
* caller holds the GEMs gpuva mutex because it would free the mutex.
1615+
*/
1616+
static void
1617+
drm_gpuvm_bo_destroy_not_in_lists(struct drm_gpuvm_bo *vm_bo)
1618+
{
1619+
struct drm_gpuvm *gpuvm = vm_bo->vm;
1620+
const struct drm_gpuvm_ops *ops = gpuvm->ops;
1621+
struct drm_gem_object *obj = vm_bo->obj;
1622+
1623+
if (ops && ops->vm_bo_free)
1624+
ops->vm_bo_free(vm_bo);
1625+
else
1626+
kfree(vm_bo);
1627+
1628+
drm_gpuvm_put(gpuvm);
1629+
drm_gem_object_put(obj);
1630+
}
1631+
1632+
static void
1633+
drm_gpuvm_bo_destroy_not_in_lists_kref(struct kref *kref)
1634+
{
1635+
struct drm_gpuvm_bo *vm_bo = container_of(kref, struct drm_gpuvm_bo,
1636+
kref);
1637+
1638+
drm_gpuvm_bo_destroy_not_in_lists(vm_bo);
1639+
}
1640+
16051641
static void
16061642
drm_gpuvm_bo_destroy(struct kref *kref)
16071643
{
16081644
struct drm_gpuvm_bo *vm_bo = container_of(kref, struct drm_gpuvm_bo,
16091645
kref);
16101646
struct drm_gpuvm *gpuvm = vm_bo->vm;
1611-
const struct drm_gpuvm_ops *ops = gpuvm->ops;
1612-
struct drm_gem_object *obj = vm_bo->obj;
16131647
bool lock = !drm_gpuvm_resv_protected(gpuvm);
16141648

16151649
if (!lock)
@@ -1618,16 +1652,10 @@ drm_gpuvm_bo_destroy(struct kref *kref)
16181652
drm_gpuvm_bo_list_del(vm_bo, extobj, lock);
16191653
drm_gpuvm_bo_list_del(vm_bo, evict, lock);
16201654

1621-
drm_gem_gpuva_assert_lock_held(gpuvm, obj);
1655+
drm_gem_gpuva_assert_lock_held(gpuvm, vm_bo->obj);
16221656
list_del(&vm_bo->list.entry.gem);
16231657

1624-
if (ops && ops->vm_bo_free)
1625-
ops->vm_bo_free(vm_bo);
1626-
else
1627-
kfree(vm_bo);
1628-
1629-
drm_gpuvm_put(gpuvm);
1630-
drm_gem_object_put(obj);
1658+
drm_gpuvm_bo_destroy_not_in_lists(vm_bo);
16311659
}
16321660

16331661
/**
@@ -1745,9 +1773,7 @@ EXPORT_SYMBOL_GPL(drm_gpuvm_bo_put_deferred);
17451773
void
17461774
drm_gpuvm_bo_deferred_cleanup(struct drm_gpuvm *gpuvm)
17471775
{
1748-
const struct drm_gpuvm_ops *ops = gpuvm->ops;
17491776
struct drm_gpuvm_bo *vm_bo;
1750-
struct drm_gem_object *obj;
17511777
struct llist_node *bo_defer;
17521778

17531779
bo_defer = llist_del_all(&gpuvm->bo_defer);
@@ -1766,14 +1792,7 @@ drm_gpuvm_bo_deferred_cleanup(struct drm_gpuvm *gpuvm)
17661792
while (bo_defer) {
17671793
vm_bo = llist_entry(bo_defer, struct drm_gpuvm_bo, list.entry.bo_defer);
17681794
bo_defer = bo_defer->next;
1769-
obj = vm_bo->obj;
1770-
if (ops && ops->vm_bo_free)
1771-
ops->vm_bo_free(vm_bo);
1772-
else
1773-
kfree(vm_bo);
1774-
1775-
drm_gpuvm_put(gpuvm);
1776-
drm_gem_object_put(obj);
1795+
drm_gpuvm_bo_destroy_not_in_lists(vm_bo);
17771796
}
17781797
}
17791798
EXPORT_SYMBOL_GPL(drm_gpuvm_bo_deferred_cleanup);
@@ -1861,6 +1880,9 @@ EXPORT_SYMBOL_GPL(drm_gpuvm_bo_obtain);
18611880
* count is decreased. If not found @__vm_bo is returned without further
18621881
* increase of the reference count.
18631882
*
1883+
* The provided @__vm_bo must not already be in the gpuva, evict, or extobj
1884+
* lists prior to calling this method.
1885+
*
18641886
* A new &drm_gpuvm_bo is added to the GEMs gpuva list.
18651887
*
18661888
* Returns: a pointer to the found &drm_gpuvm_bo or @__vm_bo if no existing
@@ -1873,14 +1895,19 @@ drm_gpuvm_bo_obtain_prealloc(struct drm_gpuvm_bo *__vm_bo)
18731895
struct drm_gem_object *obj = __vm_bo->obj;
18741896
struct drm_gpuvm_bo *vm_bo;
18751897

1898+
drm_WARN_ON(gpuvm->drm, !drm_gpuvm_immediate_mode(gpuvm));
1899+
1900+
mutex_lock(&obj->gpuva.lock);
18761901
vm_bo = drm_gpuvm_bo_find(gpuvm, obj);
18771902
if (vm_bo) {
1878-
drm_gpuvm_bo_put(__vm_bo);
1903+
mutex_unlock(&obj->gpuva.lock);
1904+
kref_put(&__vm_bo->kref, drm_gpuvm_bo_destroy_not_in_lists_kref);
18791905
return vm_bo;
18801906
}
18811907

18821908
drm_gem_gpuva_assert_lock_held(gpuvm, obj);
18831909
list_add_tail(&__vm_bo->list.entry.gem, &obj->gpuva.list);
1910+
mutex_unlock(&obj->gpuva.lock);
18841911

18851912
return __vm_bo;
18861913
}

drivers/gpu/drm/panthor/panthor_mmu.c

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,17 +1252,7 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
12521252
goto err_cleanup;
12531253
}
12541254

1255-
/* drm_gpuvm_bo_obtain_prealloc() will call drm_gpuvm_bo_put() on our
1256-
* pre-allocated BO if the <BO,VM> association exists. Given we
1257-
* only have one ref on preallocated_vm_bo, drm_gpuvm_bo_destroy() will
1258-
* be called immediately, and we have to hold the VM resv lock when
1259-
* calling this function.
1260-
*/
1261-
dma_resv_lock(panthor_vm_resv(vm), NULL);
1262-
mutex_lock(&bo->base.base.gpuva.lock);
12631255
op_ctx->map.vm_bo = drm_gpuvm_bo_obtain_prealloc(preallocated_vm_bo);
1264-
mutex_unlock(&bo->base.base.gpuva.lock);
1265-
dma_resv_unlock(panthor_vm_resv(vm));
12661256

12671257
op_ctx->map.bo_offset = offset;
12681258

0 commit comments

Comments
 (0)