drm: Add DRM prime interface to reassign GEM handle

CRIU restore of drm buffer objects requires the ability to create
or import a buffer object with a specific gem handle.

Add new drm ioctl DRM_IOCTL_GEM_CHANGE_HANDLE, which takes
the gem handle of an object and moves that object to a
specified new gem handle.

This ioctl needs to call drm_prime_remove_buf_handle,
but that function acquires the prime lock, which the ioctl
needs to hold for other purposes.

Make drm_prime_remove_buf_handle not acquire the prime lock,
and change its other caller to reflect this.

The rest of the kernel patches required to enable CRIU can be
found at
https://lore.kernel.org/dri-devel/20250617194536.538681-1-David.Francis@amd.com/

v2 - Move documentation to UAPI headers
v3 - Always return 0 on success

Signed-off-by: David Francis <David.Francis@amd.com>
Acked-by: Felix Kuehling <felix.kuehling@amd.com>
Reviewed-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Christian König <christian.koenig@amd.com>
Link: https://lore.kernel.org/r/20250717143556.857893-2-David.Francis@amd.com
This commit is contained in:
David Francis
2025-07-17 10:35:55 -04:00
committed by Christian König
parent b9a572f471
commit 53096728b8
5 changed files with 85 additions and 5 deletions

View File

@@ -283,7 +283,12 @@ drm_gem_object_release_handle(int id, void *ptr, void *data)
if (obj->funcs->close)
obj->funcs->close(obj, file_priv);
mutex_lock(&file_priv->prime.lock);
drm_prime_remove_buf_handle(&file_priv->prime, id);
mutex_unlock(&file_priv->prime.lock);
drm_vma_node_revoke(&obj->vma_node, file_priv);
drm_gem_object_handle_put_unlocked(obj);
@@ -934,6 +939,57 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data,
return ret;
}
int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_gem_change_handle *args = data;
struct drm_gem_object *obj;
int ret;
if (!drm_core_check_feature(dev, DRIVER_GEM))
return -EOPNOTSUPP;
obj = drm_gem_object_lookup(file_priv, args->handle);
if (!obj)
return -ENOENT;
if (args->handle == args->new_handle)
return 0;
mutex_lock(&file_priv->prime.lock);
spin_lock(&file_priv->table_lock);
ret = idr_alloc(&file_priv->object_idr, obj,
args->new_handle, args->new_handle + 1, GFP_NOWAIT);
spin_unlock(&file_priv->table_lock);
if (ret < 0)
goto out_unlock;
if (obj->dma_buf) {
ret = drm_prime_add_buf_handle(&file_priv->prime, obj->dma_buf, args->new_handle);
if (ret < 0) {
spin_lock(&file_priv->table_lock);
idr_remove(&file_priv->object_idr, args->new_handle);
spin_unlock(&file_priv->table_lock);
goto out_unlock;
}
drm_prime_remove_buf_handle(&file_priv->prime, args->handle);
}
ret = 0;
spin_lock(&file_priv->table_lock);
idr_remove(&file_priv->object_idr, args->handle);
spin_unlock(&file_priv->table_lock);
out_unlock:
mutex_unlock(&file_priv->prime.lock);
return ret;
}
/**
* drm_gem_open - initializes GEM file-private structures at devnode open time
* @dev: drm_device which is being opened by userspace

View File

@@ -85,6 +85,8 @@ int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data,
void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv);
void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv);
int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv,
struct dma_buf *dma_buf, uint32_t handle);
void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv,
uint32_t handle);
@@ -168,6 +170,8 @@ int drm_gem_close_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_gem_flink_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_gem_open_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
void drm_gem_open(struct drm_device *dev, struct drm_file *file_private);

View File

@@ -653,6 +653,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_CHANGE_HANDLE, drm_gem_change_handle_ioctl, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, 0),

View File

@@ -93,7 +93,7 @@ struct drm_prime_member {
struct rb_node handle_rb;
};
static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv,
int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv,
struct dma_buf *dma_buf, uint32_t handle)
{
struct drm_prime_member *member;
@@ -190,8 +190,6 @@ void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv,
{
struct rb_node *rb;
mutex_lock(&prime_fpriv->lock);
rb = prime_fpriv->handles.rb_node;
while (rb) {
struct drm_prime_member *member;
@@ -210,8 +208,6 @@ void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv,
rb = rb->rb_left;
}
}
mutex_unlock(&prime_fpriv->lock);
}
void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv)

View File

@@ -625,6 +625,21 @@ struct drm_gem_open {
__u64 size;
};
/**
* struct drm_gem_change_handle - Argument for &DRM_IOCTL_GEM_CHANGE_HANDLE ioctl.
* @handle: The handle of a gem object.
* @new_handle: An available gem handle.
*
* This ioctl changes the handle of a GEM object to the specified one.
* The new handle must be unused. On success the old handle is closed
* and all further IOCTL should refer to the new handle only.
* Calls to DRM_IOCTL_PRIME_FD_TO_HANDLE will return the new handle.
*/
struct drm_gem_change_handle {
__u32 handle;
__u32 new_handle;
};
/**
* DRM_CAP_DUMB_BUFFER
*
@@ -1309,6 +1324,14 @@ extern "C" {
*/
#define DRM_IOCTL_SET_CLIENT_NAME DRM_IOWR(0xD1, struct drm_set_client_name)
/**
* DRM_IOCTL_GEM_CHANGE_HANDLE - Move an object to a different handle
*
* Some applications (notably CRIU) need objects to have specific gem handles.
* This ioctl changes the object at one gem handle to use a new gem handle.
*/
#define DRM_IOCTL_GEM_CHANGE_HANDLE DRM_IOWR(0xD2, struct drm_gem_change_handle)
/*
* Device specific ioctls should only be in their respective headers
* The device specific ioctl range is from 0x40 to 0x9f.