Skip to content

Commit b04ce1e

Browse files
brendan-kingMTCoster
authored andcommitted
drm/imagination: Break an object reference loop
When remaining resources are being cleaned up on driver close, outstanding VM mappings may result in resources being leaked, due to an object reference loop, as shown below, with each object (or set of objects) referencing the object below it: PVR GEM Object GPU scheduler "finished" fence GPU scheduler “scheduled” fence PVR driver “done” fence PVR Context PVR VM Context PVR VM Mappings PVR GEM Object The reference that the PVR VM Context has on the VM mappings is a soft one, in the sense that the freeing of outstanding VM mappings is done as part of VM context destruction; no reference counts are involved, as is the case for all the other references in the loop. To break the reference loop during cleanup, free the outstanding VM mappings before destroying the PVR Context associated with the VM context. Signed-off-by: Brendan King <[email protected]> Signed-off-by: Matt Coster <[email protected]> Reviewed-by: Frank Binns <[email protected]> Cc: [email protected] Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent b0ef514 commit b04ce1e

File tree

4 files changed

+56
-4
lines changed

4 files changed

+56
-4
lines changed

drivers/gpu/drm/imagination/pvr_context.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,11 +450,30 @@ pvr_context_destroy(struct pvr_file *pvr_file, u32 handle)
450450
*/
451451
void pvr_destroy_contexts_for_file(struct pvr_file *pvr_file)
452452
{
453+
struct pvr_device *pvr_dev = pvr_file->pvr_dev;
453454
struct pvr_context *ctx;
454455
unsigned long handle;
455456

456457
xa_for_each(&pvr_file->ctx_handles, handle, ctx)
457458
pvr_context_destroy(pvr_file, handle);
459+
460+
spin_lock(&pvr_dev->ctx_list_lock);
461+
ctx = list_first_entry(&pvr_file->contexts, struct pvr_context, file_link);
462+
463+
while (!list_entry_is_head(ctx, &pvr_file->contexts, file_link)) {
464+
list_del_init(&ctx->file_link);
465+
466+
if (pvr_context_get_if_referenced(ctx)) {
467+
spin_unlock(&pvr_dev->ctx_list_lock);
468+
469+
pvr_vm_unmap_all(ctx->vm_ctx);
470+
471+
pvr_context_put(ctx);
472+
spin_lock(&pvr_dev->ctx_list_lock);
473+
}
474+
ctx = list_first_entry(&pvr_file->contexts, struct pvr_context, file_link);
475+
}
476+
spin_unlock(&pvr_dev->ctx_list_lock);
458477
}
459478

460479
/**

drivers/gpu/drm/imagination/pvr_context.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,24 @@ pvr_context_get(struct pvr_context *ctx)
126126
return ctx;
127127
}
128128

129+
/**
130+
* pvr_context_get_if_referenced() - Take an additional reference on a still
131+
* referenced context.
132+
* @ctx: Context pointer.
133+
*
134+
* Call pvr_context_put() to release.
135+
*
136+
* Returns:
137+
* * True on success, or
138+
* * false if no context pointer passed, or the context wasn't still
139+
* * referenced.
140+
*/
141+
static __always_inline bool
142+
pvr_context_get_if_referenced(struct pvr_context *ctx)
143+
{
144+
return ctx != NULL && kref_get_unless_zero(&ctx->ref_count) != 0;
145+
}
146+
129147
/**
130148
* pvr_context_lookup() - Lookup context pointer from handle and file.
131149
* @pvr_file: Pointer to pvr_file structure.

drivers/gpu/drm/imagination/pvr_vm.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <drm/drm_gem.h>
1515
#include <drm/drm_gpuvm.h>
1616

17+
#include <linux/bug.h>
1718
#include <linux/container_of.h>
1819
#include <linux/err.h>
1920
#include <linux/errno.h>
@@ -597,12 +598,26 @@ pvr_vm_create_context(struct pvr_device *pvr_dev, bool is_userspace_context)
597598
}
598599

599600
/**
600-
* pvr_vm_context_release() - Teardown a VM context.
601-
* @ref_count: Pointer to reference counter of the VM context.
601+
* pvr_vm_unmap_all() - Unmap all mappings associated with a VM context.
602+
* @vm_ctx: Target VM context.
602603
*
603604
* This function ensures that no mappings are left dangling by unmapping them
604605
* all in order of ascending device-virtual address.
605606
*/
607+
void
608+
pvr_vm_unmap_all(struct pvr_vm_context *vm_ctx)
609+
{
610+
WARN_ON(pvr_vm_unmap(vm_ctx, vm_ctx->gpuvm_mgr.mm_start,
611+
vm_ctx->gpuvm_mgr.mm_range));
612+
}
613+
614+
/**
615+
* pvr_vm_context_release() - Teardown a VM context.
616+
* @ref_count: Pointer to reference counter of the VM context.
617+
*
618+
* This function also ensures that no mappings are left dangling by calling
619+
* pvr_vm_unmap_all.
620+
*/
606621
static void
607622
pvr_vm_context_release(struct kref *ref_count)
608623
{
@@ -612,8 +627,7 @@ pvr_vm_context_release(struct kref *ref_count)
612627
if (vm_ctx->fw_mem_ctx_obj)
613628
pvr_fw_object_destroy(vm_ctx->fw_mem_ctx_obj);
614629

615-
WARN_ON(pvr_vm_unmap(vm_ctx, vm_ctx->gpuvm_mgr.mm_start,
616-
vm_ctx->gpuvm_mgr.mm_range));
630+
pvr_vm_unmap_all(vm_ctx);
617631

618632
pvr_mmu_context_destroy(vm_ctx->mmu_ctx);
619633
drm_gem_private_object_fini(&vm_ctx->dummy_gem);

drivers/gpu/drm/imagination/pvr_vm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ int pvr_vm_map(struct pvr_vm_context *vm_ctx,
3939
struct pvr_gem_object *pvr_obj, u64 pvr_obj_offset,
4040
u64 device_addr, u64 size);
4141
int pvr_vm_unmap(struct pvr_vm_context *vm_ctx, u64 device_addr, u64 size);
42+
void pvr_vm_unmap_all(struct pvr_vm_context *vm_ctx);
4243

4344
dma_addr_t pvr_vm_get_page_table_root_addr(struct pvr_vm_context *vm_ctx);
4445
struct dma_resv *pvr_vm_get_dma_resv(struct pvr_vm_context *vm_ctx);

0 commit comments

Comments
 (0)