AMD General ping
-----Original Message----- From: Zhu, Lingshan <[email protected]> Sent: Wednesday, June 17, 2026 2:55 PM To: Deucher, Alexander <[email protected]>; Koenig, Christian <[email protected]> Cc: [email protected]; Huang, Ray <[email protected]>; Zhu, Lingshan <[email protected]> Subject: [PATCH] drm/amdgpu: split amdgpu_bo_create_reserved into create and map helpers amdgpu_bo_create_reserved() only allocates a new BO when *bo_ptr (struct amdgpu_bo **bo_ptr as input parameter) is NULL, it simply skips creation when *bo_ptr is non-NULL. But it unconditionally reserves, pins, gart allocates and maps the BO afterwards. When the same non-NULL BO pointer is passed in again, for example firmware buffers that live in adev and are re-loaded on every resume / cp_resume / start under AMDGPU_FW_LOAD_DIRECT, amdgpu_bo_pin() just increases pin_count unconditionally, however the matching teardown only unpins once, so pin_count never drops to zero, so TTM is not able to move, swap or evict a BO, causing BO leaks. Ideally the BO should only be pinned once at creation. So this commit splits current amdgpu_bo_create_reserved() into two helpers: - amdgpu_bo_create_pinned(): create + reserve + pin + alloc_gart and return the BO unreserved. It requires *bo_ptr == NULL on entry, so only pins the BO once. - amdgpu_bo_get_access(): reserve + map gpu/cpu addr + unreserve. This function is idempotent and is safe to call multiple times on an existing pinned BO, for example, on every resume path. amdgpu_bo_create_reserved() now calls amdgpu_bo_create_pinned() only when *bo_ptr is NULL, then calls amdgpu_bo_get_access() for CPU/GPU address mapping, and reserves the BO before returning. Repeated calls no longer take additional pin references. Signed-off-by: Zhu Lingshan <[email protected]> --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 175 +++++++++++++++++---- 1 file changed, 143 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 4dd7c712b8c3..dd0a59137028 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -217,57 +217,53 @@ void amdgpu_bo_placement_from_domain(struct amdgpu_bo *abo, u32 domain) } /** - * amdgpu_bo_create_reserved - create reserved BO for kernel use + * amdgpu_bo_create_pinned - create and pin a BO for kernel use * * @adev: amdgpu device object * @size: size for the new BO * @align: alignment for the new BO * @domain: where to place it - * @bo_ptr: used to initialize BOs in structures - * @gpu_addr: GPU addr of the pinned BO - * @cpu_addr: optional CPU address mapping + * @bo_ptr: used to return the newly created BO, must point to NULL on + entry + * @cpu_access: true if the BO needs to be CPU accessible * - * Allocates and pins a BO for kernel internal use, and returns it still - * reserved. + * Allocates and pins a BO for kernel internal use, and returns it + unreserved * - * Note: For bo_ptr new BO is only created if bo_ptr points to NULL. + * Note: *bo_ptr must be NULL on entry, this helper always creates + * a new BO and never reuses an existing one, + * so it never pins the same BO twice. * * Returns: * 0 on success, negative error code otherwise. */ -int amdgpu_bo_create_reserved(struct amdgpu_device *adev, - unsigned long size, int align, - u32 domain, struct amdgpu_bo **bo_ptr, - u64 *gpu_addr, void **cpu_addr) +static int amdgpu_bo_create_pinned(struct amdgpu_device *adev, + unsigned long size, int align, + u32 domain, struct amdgpu_bo **bo_ptr, + bool cpu_access) { struct amdgpu_bo_param bp; - bool free = false; int r; - if (!size) { - amdgpu_bo_unref(bo_ptr); - return 0; - } + if (WARN_ON(!bo_ptr || *bo_ptr)) + return -EINVAL; + + if (WARN_ON(!size)) + return -EINVAL; memset(&bp, 0, sizeof(bp)); bp.size = size; bp.byte_align = align; bp.domain = domain; - bp.flags = cpu_addr ? AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED + bp.flags = cpu_access ? AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED : AMDGPU_GEM_CREATE_NO_CPU_ACCESS; bp.flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS; bp.type = ttm_bo_type_kernel; bp.resv = NULL; bp.bo_ptr_size = sizeof(struct amdgpu_bo); - if (!*bo_ptr) { - r = amdgpu_bo_create(adev, &bp, bo_ptr); - if (r) { - dev_err(adev->dev, "(%d) failed to allocate kernel bo\n", - r); - return r; - } - free = true; + r = amdgpu_bo_create(adev, &bp, bo_ptr); + if (r) { + dev_err(adev->dev, "(%d) failed to allocate kernel bo\n", r); + return r; } r = amdgpu_bo_reserve(*bo_ptr, false); @@ -288,27 +284,142 @@ int amdgpu_bo_create_reserved(struct amdgpu_device *adev, goto error_unpin; } + amdgpu_bo_unreserve(*bo_ptr); + return 0; + +error_unpin: + amdgpu_bo_unpin(*bo_ptr); +error_unreserve: + amdgpu_bo_unreserve(*bo_ptr); +error_free: + amdgpu_bo_unref(bo_ptr); + return r; +} + +/** + * amdgpu_bo_get_access - get CPU/GPU access to a BO + * + * @bo: the input BO, must be pinned + * @gpu_addr: optional, returns the GPU address of the BO + * @cpu_addr: optional, returns the CPU address of the BO + * + * Note: *bo must be already pinned! + * + * Returns: + * 0 on success, negative error code otherwise. + */ +static int amdgpu_bo_get_access(struct amdgpu_bo *bo, u64 *gpu_addr, + void **cpu_addr) +{ + struct amdgpu_device *adev; + int r; + + if (WARN_ON(!bo)) + return -EINVAL; + + adev = amdgpu_ttm_adev(bo->tbo.bdev); + + r = amdgpu_bo_reserve(bo, false); + if (r) { + dev_err(adev->dev, "(%d) failed to reserve kernel bo\n", r); + return r; + } + + /* + * The BO must already be pinned. A GPU or CPU address of an + * unpinned BO would become meaningless because TTM + * may then move or evict it at any time. + */ + if (WARN_ON_ONCE(!bo->tbo.pin_count)) { + r = -EINVAL; + goto error_unreserve; + } + if (gpu_addr) - *gpu_addr = amdgpu_bo_gpu_offset(*bo_ptr); + *gpu_addr = amdgpu_bo_gpu_offset(bo); if (cpu_addr) { - r = amdgpu_bo_kmap(*bo_ptr, cpu_addr); + r = amdgpu_bo_kmap(bo, cpu_addr); if (r) { dev_err(adev->dev, "(%d) kernel bo map failed\n", r); - goto error_unpin; + goto error_unreserve; } } + amdgpu_bo_unreserve(bo); return 0; -error_unpin: - amdgpu_bo_unpin(*bo_ptr); error_unreserve: - amdgpu_bo_unreserve(*bo_ptr); + amdgpu_bo_unreserve(bo); + return r; +} + +/** + * amdgpu_bo_create_reserved - create reserved BO for kernel use + * + * @adev: amdgpu device object + * @size: size for the new BO + * @align: alignment for the new BO + * @domain: where to place it + * @bo_ptr: used to initialize BOs in structures + * @gpu_addr: GPU addr of the pinned BO + * @cpu_addr: optional CPU address mapping + * + * Allocates and pins a BO for kernel internal use, and returns it +still + * reserved. + * + * Note: For bo_ptr new BO is only created if bo_ptr points to NULL. An + * existing BO is only re-accessed (get CPU/GPU mapping) and not pinned +again, + * so repeated calls with the same BO do not leak pin references. + * + * Returns: + * 0 on success, negative error code otherwise. + */ +int amdgpu_bo_create_reserved(struct amdgpu_device *adev, + unsigned long size, int align, + u32 domain, struct amdgpu_bo **bo_ptr, + u64 *gpu_addr, void **cpu_addr) { + bool created = false; + int r; + + if (WARN_ON(!bo_ptr)) + return -EINVAL; + + if (!size) { + amdgpu_bo_unref(bo_ptr); + return 0; + } + + if (!*bo_ptr) { + r = amdgpu_bo_create_pinned(adev, size, align, domain, bo_ptr, + !!cpu_addr); + if (r) + return r; + created = true; + } + + r = amdgpu_bo_get_access(*bo_ptr, gpu_addr, cpu_addr); + if (r) + goto error_free; + + r = amdgpu_bo_reserve(*bo_ptr, false); + if (r) { + dev_err(adev->dev, "(%d) failed to reserve kernel bo\n", r); + goto error_free; + } + + return 0; error_free: - if (free) + if (created) { + if (amdgpu_bo_reserve(*bo_ptr, true) == 0) { + amdgpu_bo_kunmap(*bo_ptr); + amdgpu_bo_unpin(*bo_ptr); + amdgpu_bo_unreserve(*bo_ptr); + } amdgpu_bo_unref(bo_ptr); + } return r; } -- 2.54.0
