[PATCH v2 2/2] drm/imagination: Break an object reference loop

Frank Binns Frank.Binns at imgtec.com
Wed Oct 16 13:31:59 UTC 2024


On Mon, 2024-10-14 at 14:23 +0000, Matt Coster wrote:
> From: Brendan King <brendan.king at imgtec.com>
> 
> 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.
> 

Reviewed-by: Frank Binns <frank.binns at imgtec.com>

> Signed-off-by: Brendan King <brendan.king at imgtec.com>
> Signed-off-by: Matt Coster <matt.coster at imgtec.com>
> ---
> Changes in v1 -> v2:
>  - None
> ---
>  drivers/gpu/drm/imagination/pvr_context.c | 19 +++++++++++++++++++
>  drivers/gpu/drm/imagination/pvr_context.h | 18 ++++++++++++++++++
>  drivers/gpu/drm/imagination/pvr_vm.c      | 22 ++++++++++++++++++----
>  drivers/gpu/drm/imagination/pvr_vm.h      |  1 +
>  4 files changed, 56 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/imagination/pvr_context.c b/drivers/gpu/drm/imagination/pvr_context.c
> index 255c93582734..4cb3494c0bb2 100644
> --- a/drivers/gpu/drm/imagination/pvr_context.c
> +++ b/drivers/gpu/drm/imagination/pvr_context.c
> @@ -450,11 +450,30 @@ pvr_context_destroy(struct pvr_file *pvr_file, u32 handle)
>   */
>  void pvr_destroy_contexts_for_file(struct pvr_file *pvr_file)
>  {
> +	struct pvr_device *pvr_dev = pvr_file->pvr_dev;
>  	struct pvr_context *ctx;
>  	unsigned long handle;
>  
>  	xa_for_each(&pvr_file->ctx_handles, handle, ctx)
>  		pvr_context_destroy(pvr_file, handle);
> +
> +	spin_lock(&pvr_dev->ctx_list_lock);
> +	ctx = list_first_entry(&pvr_file->contexts, struct pvr_context, file_link);
> +
> +	while (!list_entry_is_head(ctx, &pvr_file->contexts, file_link)) {
> +		list_del_init(&ctx->file_link);
> +
> +		if (pvr_context_get_if_referenced(ctx)) {
> +			spin_unlock(&pvr_dev->ctx_list_lock);
> +
> +			pvr_vm_unmap_all(ctx->vm_ctx);
> +
> +			pvr_context_put(ctx);
> +			spin_lock(&pvr_dev->ctx_list_lock);
> +		}
> +		ctx = list_first_entry(&pvr_file->contexts, struct pvr_context, file_link);
> +	}
> +	spin_unlock(&pvr_dev->ctx_list_lock);
>  }
>  
>  /**
> diff --git a/drivers/gpu/drm/imagination/pvr_context.h b/drivers/gpu/drm/imagination/pvr_context.h
> index a5b0a82a54a1..07afa179cdf4 100644
> --- a/drivers/gpu/drm/imagination/pvr_context.h
> +++ b/drivers/gpu/drm/imagination/pvr_context.h
> @@ -126,6 +126,24 @@ pvr_context_get(struct pvr_context *ctx)
>  	return ctx;
>  }
>  
> +/**
> + * pvr_context_get_if_referenced() - Take an additional reference on a still
> + * referenced context.
> + * @ctx: Context pointer.
> + *
> + * Call pvr_context_put() to release.
> + *
> + * Returns:
> + *  * True on success, or
> + *  * false if no context pointer passed, or the context wasn't still
> + *  * referenced.
> + */
> +static __always_inline bool
> +pvr_context_get_if_referenced(struct pvr_context *ctx)
> +{
> +	return ctx != NULL && kref_get_unless_zero(&ctx->ref_count) != 0;
> +}
> +
>  /**
>   * pvr_context_lookup() - Lookup context pointer from handle and file.
>   * @pvr_file: Pointer to pvr_file structure.
> diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c
> index 97c0f772ed65..7bd6ba4c6e8a 100644
> --- a/drivers/gpu/drm/imagination/pvr_vm.c
> +++ b/drivers/gpu/drm/imagination/pvr_vm.c
> @@ -14,6 +14,7 @@
>  #include <drm/drm_gem.h>
>  #include <drm/drm_gpuvm.h>
>  
> +#include <linux/bug.h>
>  #include <linux/container_of.h>
>  #include <linux/err.h>
>  #include <linux/errno.h>
> @@ -597,12 +598,26 @@ pvr_vm_create_context(struct pvr_device *pvr_dev, bool is_userspace_context)
>  }
>  
>  /**
> - * pvr_vm_context_release() - Teardown a VM context.
> - * @ref_count: Pointer to reference counter of the VM context.
> + * pvr_vm_unmap_all() - Unmap all mappings associated with a VM context.
> + * @vm_ctx: Target VM context.
>   *
>   * This function ensures that no mappings are left dangling by unmapping them
>   * all in order of ascending device-virtual address.
>   */
> +void
> +pvr_vm_unmap_all(struct pvr_vm_context *vm_ctx)
> +{
> +	WARN_ON(pvr_vm_unmap(vm_ctx, vm_ctx->gpuvm_mgr.mm_start,
> +			     vm_ctx->gpuvm_mgr.mm_range));
> +}
> +
> +/**
> + * pvr_vm_context_release() - Teardown a VM context.
> + * @ref_count: Pointer to reference counter of the VM context.
> + *
> + * This function also ensures that no mappings are left dangling by calling
> + * pvr_vm_unmap_all.
> + */
>  static void
>  pvr_vm_context_release(struct kref *ref_count)
>  {
> @@ -612,8 +627,7 @@ pvr_vm_context_release(struct kref *ref_count)
>  	if (vm_ctx->fw_mem_ctx_obj)
>  		pvr_fw_object_destroy(vm_ctx->fw_mem_ctx_obj);
>  
> -	WARN_ON(pvr_vm_unmap(vm_ctx, vm_ctx->gpuvm_mgr.mm_start,
> -			     vm_ctx->gpuvm_mgr.mm_range));
> +	pvr_vm_unmap_all(vm_ctx);
>  
>  	pvr_mmu_context_destroy(vm_ctx->mmu_ctx);
>  	drm_gem_private_object_fini(&vm_ctx->dummy_gem);
> diff --git a/drivers/gpu/drm/imagination/pvr_vm.h b/drivers/gpu/drm/imagination/pvr_vm.h
> index f2a6463f2b05..79406243617c 100644
> --- a/drivers/gpu/drm/imagination/pvr_vm.h
> +++ b/drivers/gpu/drm/imagination/pvr_vm.h
> @@ -39,6 +39,7 @@ int pvr_vm_map(struct pvr_vm_context *vm_ctx,
>  	       struct pvr_gem_object *pvr_obj, u64 pvr_obj_offset,
>  	       u64 device_addr, u64 size);
>  int pvr_vm_unmap(struct pvr_vm_context *vm_ctx, u64 device_addr, u64 size);
> +void pvr_vm_unmap_all(struct pvr_vm_context *vm_ctx);
>  
>  dma_addr_t pvr_vm_get_page_table_root_addr(struct pvr_vm_context *vm_ctx);
>  struct dma_resv *pvr_vm_get_dma_resv(struct pvr_vm_context *vm_ctx);


More information about the dri-devel mailing list