Commit 53096728 authored by David Francis's avatar David Francis Committed by Christian König
Browse files

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: default avatarDavid Francis <David.Francis@amd.com>
Acked-by: default avatarFelix Kuehling <felix.kuehling@amd.com>
Reviewed-by: default avatarChristian König <christian.koenig@amd.com>
Signed-off-by: default avatarChristian König <christian.koenig@amd.com>
Link: https://lore.kernel.org/r/20250717143556.857893-2-David.Francis@amd.com
parent b9a572f4
Loading
Loading
Loading
Loading
+56 −0
Original line number Diff line number Diff line
@@ -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
+4 −0
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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),

+1 −5
Original line number Diff line number Diff line
@@ -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)
+23 −0
Original line number Diff line number Diff line
@@ -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.