Commit 9902cb99 authored by Rob Clark's avatar Rob Clark
Browse files

drm/msm/gem: Add metadata



The EXT_external_objects extension is a bit awkward as it doesn't pass
explicit modifiers, leaving the importer to guess with incomplete
information.  In the case of vk (turnip) exporting and gl (freedreno)
importing, the "OPTIMAL_TILING_EXT" layout depends on VkImageCreateInfo
flags (among other things), which the importer does not know.  Which
unfortunately leaves us with the need for a metadata back-channel.

The contents of the metadata are defined by userspace.  The
EXT_external_objects extension is only required to work between
compatible versions of gl and vk drivers, as defined by device and
driver UUIDs.

v2: add missing metadata kfree
v3: Rework to move copy_from/to_user out from under gem obj lock
    to avoid angering lockdep about deadlocks against fs-reclaim

Signed-off-by: default avatarRob Clark <robdclark@chromium.org>
Patchwork: https://patchwork.freedesktop.org/patch/566157/
parent d1912f69
Loading
Loading
Loading
Loading
+91 −1
Original line number Diff line number Diff line
@@ -37,9 +37,10 @@
 * - 1.9.0 - Add MSM_SUBMIT_FENCE_SN_IN
 * - 1.10.0 - Add MSM_SUBMIT_BO_NO_IMPLICIT
 * - 1.11.0 - Add wait boost (MSM_WAIT_FENCE_BOOST, MSM_PREP_BOOST)
 * - 1.12.0 - Add MSM_INFO_SET_METADATA and MSM_INFO_GET_METADATA
 */
#define MSM_VERSION_MAJOR	1
#define MSM_VERSION_MINOR	11
#define MSM_VERSION_MINOR	12
#define MSM_VERSION_PATCHLEVEL	0

static void msm_deinit_vram(struct drm_device *ddev);
@@ -546,6 +547,85 @@ static int msm_ioctl_gem_info_set_iova(struct drm_device *dev,
	return msm_gem_set_iova(obj, ctx->aspace, iova);
}

static int msm_ioctl_gem_info_set_metadata(struct drm_gem_object *obj,
					   __user void *metadata,
					   u32 metadata_size)
{
	struct msm_gem_object *msm_obj = to_msm_bo(obj);
	void *buf;
	int ret;

	/* Impose a moderate upper bound on metadata size: */
	if (metadata_size > 128) {
		return -EOVERFLOW;
	}

	/* Use a temporary buf to keep copy_from_user() outside of gem obj lock: */
	buf = memdup_user(metadata, metadata_size);
	if (IS_ERR(buf))
		return PTR_ERR(buf);

	ret = msm_gem_lock_interruptible(obj);
	if (ret)
		goto out;

	msm_obj->metadata =
		krealloc(msm_obj->metadata, metadata_size, GFP_KERNEL);
	msm_obj->metadata_size = metadata_size;
	memcpy(msm_obj->metadata, buf, metadata_size);

	msm_gem_unlock(obj);

out:
	kfree(buf);

	return ret;
}

static int msm_ioctl_gem_info_get_metadata(struct drm_gem_object *obj,
					   __user void *metadata,
					   u32 *metadata_size)
{
	struct msm_gem_object *msm_obj = to_msm_bo(obj);
	void *buf;
	int ret, len;

	if (!metadata) {
		/*
		 * Querying the size is inherently racey, but
		 * EXT_external_objects expects the app to confirm
		 * via device and driver UUIDs that the exporter and
		 * importer versions match.  All we can do from the
		 * kernel side is check the length under obj lock
		 * when userspace tries to retrieve the metadata
		 */
		*metadata_size = msm_obj->metadata_size;
		return 0;
	}

	ret = msm_gem_lock_interruptible(obj);
	if (ret)
		return ret;

	/* Avoid copy_to_user() under gem obj lock: */
	len = msm_obj->metadata_size;
	buf = kmemdup(msm_obj->metadata, len, GFP_KERNEL);

	msm_gem_unlock(obj);

	if (*metadata_size < len) {
		ret = -ETOOSMALL;
	} else if (copy_to_user(metadata, buf, len)) {
		ret = -EFAULT;
	} else {
		*metadata_size = len;
	}

	kfree(buf);

	return 0;
}

static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
		struct drm_file *file)
{
@@ -568,6 +648,8 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
		break;
	case MSM_INFO_SET_NAME:
	case MSM_INFO_GET_NAME:
	case MSM_INFO_SET_METADATA:
	case MSM_INFO_GET_METADATA:
		break;
	default:
		return -EINVAL;
@@ -630,6 +712,14 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
				ret = -EFAULT;
		}
		break;
	case MSM_INFO_SET_METADATA:
		ret = msm_ioctl_gem_info_set_metadata(
			obj, u64_to_user_ptr(args->value), args->len);
		break;
	case MSM_INFO_GET_METADATA:
		ret = msm_ioctl_gem_info_get_metadata(
			obj, u64_to_user_ptr(args->value), &args->len);
		break;
	}

	drm_gem_object_put(obj);
+1 −0
Original line number Diff line number Diff line
@@ -1058,6 +1058,7 @@ static void msm_gem_free_object(struct drm_gem_object *obj)

	drm_gem_object_release(obj);

	kfree(msm_obj->metadata);
	kfree(msm_obj);
}

+4 −0
Original line number Diff line number Diff line
@@ -108,6 +108,10 @@ struct msm_gem_object {

	char name[32]; /* Identifier to print for the debugfs files */

	/* userspace metadata backchannel */
	void *metadata;
	u32 metadata_size;

	/**
	 * pin_count: Number of times the pages are pinned
	 *
+2 −0
Original line number Diff line number Diff line
@@ -139,6 +139,8 @@ struct drm_msm_gem_new {
#define MSM_INFO_GET_NAME	0x03   /* get debug name, returned by pointer */
#define MSM_INFO_SET_IOVA	0x04   /* set the iova, passed by value */
#define MSM_INFO_GET_FLAGS	0x05   /* get the MSM_BO_x flags */
#define MSM_INFO_SET_METADATA	0x06   /* set userspace metadata */
#define MSM_INFO_GET_METADATA	0x07   /* get userspace metadata */

struct drm_msm_gem_info {
	__u32 handle;         /* in */