Re: [RFC 5/5] drm/amdgpu: Add driver managed buffer copy
On 27/05/2026 12:46, Christian König wrote:
On 5/26/26 18:06, Tvrtko Ursulin wrote:
Saving and restoring buffer object content poses a challenge for the
checkpoint and restore process for at least two reasons.
For example not all objects can be exported as dma-buf to enable copying
from a separate client context, neither can any objects be easily copied
from the same context since the injected CRIU code is unaware of the GPU
virtual memory free and allocated ranges.
Lets bypass both problems by simply exposing access to the alrady present
fast kernel copy via a new DRM_IOCTL_AMDGPU_GEM_COPY_BUFFER ioctl.
Oh nice, that was on the TODO list for KFD integration as well.
That's good to hear! I was worried this one could be shot down in flames.
By giving the kernel simply the source and destination handles it is able
to copy them without the need for objects to be mapped, or shared with a
separate client.
For now this is implemented fully synchronous but can easily be extended
for more parallelsim.
Looks reasonable of hand but I would rather like to have an output DMA-fence
(drm_syncobj) instead of the dma_fence_wait().
I opted for simplicity for the RFC but I can certainly add an output
fence. I guess that way userspace could implement parallel/pipelined
save/restore for maximum performance.
Marking as TODO for RFC v2.
Regards,
Tvrtko
Signed-off-by: Tvrtko Ursulin
---
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 1 +
drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 102
drivers/gpu/drm/amd/amdgpu/amdgpu_gem.h | 2 +
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 14 ++--
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h | 7 ++
include/uapi/drm/amdgpu_drm.h | 8 ++
6 files changed, 127 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 2b971de3c189..b927e3a3089e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -3066,6 +3066,7 @@ const struct drm_ioctl_desc amdgpu_ioctls_kms[] = {
DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_WAIT, amdgpu_userq_wait_ioctl,
DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(AMDGPU_GEM_LIST_HANDLES,
amdgpu_gem_list_handles_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(AMDGPU_GEM_LIST_CONTEXTS,
amdgpu_gem_list_contexts_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(AMDGPU_GEM_COPY_BUFFER, amdgpu_gem_copy_buffer_ioctl,
DRM_AUTH|DRM_RENDER_ALLOW),
};
static const struct drm_driver amdgpu_kms_driver = {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
index 5eb8433229b4..4e0440cd6ee2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
@@ -1216,6 +1216,108 @@ int amdgpu_gem_list_handles_ioctl(struct drm_device
*dev, void *data,
return ret;
}
+/**
+ * amdgpu_gem_copy_buffer_ioctl - copy buffer object content
+ *
+ * @dev: drm device pointer
+ * @data: drm_amdgpu_gem_copy_buffer
+ * @filp: drm file pointer
+ *
+ * Returns:
+ * 0 for success, -errno for errors.
+ */
+int amdgpu_gem_copy_buffer_ioctl(struct drm_device *dev, void *data,
+struct drm_file *filp)
+{
+ struct amdgpu_copy_mem src_mem = {}, dst_mem = {};
+ struct drm_amdgpu_gem_copy_buffer *args = data;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct drm_gem_object *src_gobj, *dst_gobj;
+ struct amdgpu_bo *src_bo, *dst_bo;
+ struct dma_fence *fence = NULL;
+ struct drm_exec exec;
+ unsigned int e;
+ long timeout;
+ int r;
+
+ if (args->flags)
+ return -EINVAL;
+
+ src_gobj = drm_gem_object_lookup(filp, args->src_handle);
+ if (!src_gobj)
+ return -ENOENT;
+
+ dst_gobj = drm_gem_object_lookup(filp, args->dst_handle);
+ if (!dst_gobj) {
+ r = -ENOENT;
+ goto err_dst;
+ }
+
+ src_bo = gem_to_amdgpu_bo(src_gobj);
+ dst_bo = gem_to_amdgpu_bo(dst_gobj);
+
+ if (amdgpu_bo_size(src_bo) < amdgpu_bo_size(dst_bo)) {
+ r = -E2BIG;
+ goto err_sizes;
+ }
+
+ drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
+
+ drm_exec_until_all_locked(&exec) {
+ r = drm_exec_prepare_obj(&exec, &src_bo->tbo.base, 1);
+ drm_exec_retry_on_contention(&exec);
+ if (r)
+ goto err_unlock;
+
+ r = drm_exec_prepare_obj(&exec, &dst_bo->tbo.base, 1);
+ drm_exec_retry_on_contention(&exec);
+ if (r)
+ goto err_unlock;
+ }
+
+ src_mem.bo = &src_bo->tbo;
+ src_mem.mem = src_bo->tbo.resource;
+ dst_mem.bo = &dst_bo->tbo;
+ dst_mem.mem = dst_bo->tbo.resource;
+ e = atomic_inc_return(&adev->mman.next_move_entity) %
+
Re: [RFC 5/5] drm/amdgpu: Add driver managed buffer copy
On 5/26/26 18:06, Tvrtko Ursulin wrote:
> Saving and restoring buffer object content poses a challenge for the
> checkpoint and restore process for at least two reasons.
>
> For example not all objects can be exported as dma-buf to enable copying
> from a separate client context, neither can any objects be easily copied
> from the same context since the injected CRIU code is unaware of the GPU
> virtual memory free and allocated ranges.
>
> Lets bypass both problems by simply exposing access to the alrady present
> fast kernel copy via a new DRM_IOCTL_AMDGPU_GEM_COPY_BUFFER ioctl.
Oh nice, that was on the TODO list for KFD integration as well.
> By giving the kernel simply the source and destination handles it is able
> to copy them without the need for objects to be mapped, or shared with a
> separate client.
>
> For now this is implemented fully synchronous but can easily be extended
> for more parallelsim.
Looks reasonable of hand but I would rather like to have an output DMA-fence
(drm_syncobj) instead of the dma_fence_wait().
Regards,
Christian.
>
> Signed-off-by: Tvrtko Ursulin
> ---
> drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 1 +
> drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 102
> drivers/gpu/drm/amd/amdgpu/amdgpu_gem.h | 2 +
> drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 14 ++--
> drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h | 7 ++
> include/uapi/drm/amdgpu_drm.h | 8 ++
> 6 files changed, 127 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> index 2b971de3c189..b927e3a3089e 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> @@ -3066,6 +3066,7 @@ const struct drm_ioctl_desc amdgpu_ioctls_kms[] = {
> DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_WAIT, amdgpu_userq_wait_ioctl,
> DRM_AUTH|DRM_RENDER_ALLOW),
> DRM_IOCTL_DEF_DRV(AMDGPU_GEM_LIST_HANDLES,
> amdgpu_gem_list_handles_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
> DRM_IOCTL_DEF_DRV(AMDGPU_GEM_LIST_CONTEXTS,
> amdgpu_gem_list_contexts_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
> + DRM_IOCTL_DEF_DRV(AMDGPU_GEM_COPY_BUFFER, amdgpu_gem_copy_buffer_ioctl,
> DRM_AUTH|DRM_RENDER_ALLOW),
> };
>
> static const struct drm_driver amdgpu_kms_driver = {
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
> index 5eb8433229b4..4e0440cd6ee2 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
> @@ -1216,6 +1216,108 @@ int amdgpu_gem_list_handles_ioctl(struct drm_device
> *dev, void *data,
> return ret;
> }
>
> +/**
> + * amdgpu_gem_copy_buffer_ioctl - copy buffer object content
> + *
> + * @dev: drm device pointer
> + * @data: drm_amdgpu_gem_copy_buffer
> + * @filp: drm file pointer
> + *
> + * Returns:
> + * 0 for success, -errno for errors.
> + */
> +int amdgpu_gem_copy_buffer_ioctl(struct drm_device *dev, void *data,
> + struct drm_file *filp)
> +{
> + struct amdgpu_copy_mem src_mem = {}, dst_mem = {};
> + struct drm_amdgpu_gem_copy_buffer *args = data;
> + struct amdgpu_device *adev = drm_to_adev(dev);
> + struct drm_gem_object *src_gobj, *dst_gobj;
> + struct amdgpu_bo *src_bo, *dst_bo;
> + struct dma_fence *fence = NULL;
> + struct drm_exec exec;
> + unsigned int e;
> + long timeout;
> + int r;
> +
> + if (args->flags)
> + return -EINVAL;
> +
> + src_gobj = drm_gem_object_lookup(filp, args->src_handle);
> + if (!src_gobj)
> + return -ENOENT;
> +
> + dst_gobj = drm_gem_object_lookup(filp, args->dst_handle);
> + if (!dst_gobj) {
> + r = -ENOENT;
> + goto err_dst;
> + }
> +
> + src_bo = gem_to_amdgpu_bo(src_gobj);
> + dst_bo = gem_to_amdgpu_bo(dst_gobj);
> +
> + if (amdgpu_bo_size(src_bo) < amdgpu_bo_size(dst_bo)) {
> + r = -E2BIG;
> + goto err_sizes;
> + }
> +
> + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
> +
> + drm_exec_until_all_locked(&exec) {
> + r = drm_exec_prepare_obj(&exec, &src_bo->tbo.base, 1);
> + drm_exec_retry_on_contention(&exec);
> + if (r)
> + goto err_unlock;
> +
> + r = drm_exec_prepare_obj(&exec, &dst_bo->tbo.base, 1);
> + drm_exec_retry_on_contention(&exec);
> + if (r)
> + goto err_unlock;
> + }
> +
> + src_mem.bo = &src_bo->tbo;
> + src_mem.mem = src_bo->tbo.resource;
> + dst_mem.bo = &dst_bo->tbo;
> + dst_mem.mem = dst_bo->tbo.resource;
> + e = atomic_inc_return(&adev->mman.next_move_entity) %
> + adev->mman.num_move_entities;
> + r = amdgpu_ttm_copy_mem_to_mem(adev, &adev->mman.move_entities[e],
> +&src_mem, &dst_mem,
> +
