diff mbox

[6/6] drm: Add four ioctls for managing drm mode object leases [v3]

Message ID 20170705222406.28124-7-keithp@keithp.com
State New, archived
Headers show

Commit Message

Keith Packard July 5, 2017, 10:24 p.m. UTC
drm_mode_create_lease

	Creates a lease for a list of drm mode objects, returning an
	fd for the new drm_master and a 64-bit identifier for the lessee

drm_mode_list_lesees

	List the identifiers of the lessees for a master file

drm_mode_get_lease

	List the leased objects for a master file

drm_mode_revoke_lease

	Erase the set of objects managed by a lease.

This should suffice to at least create and query leases.

Changes for v2 as suggested by Daniel Vetter <daniel.vetter@ffwll.ch>:

 * query ioctls only query the master associated with
   the provided file.

 * 'mask_lease' value has been removed

 * change ioctl has been removed.

Changes for v3 suggested in part by Dave Airlie <airlied@gmail.com>

 * Add revoke ioctl.

Signed-off-by: Keith Packard <keithp@keithp.com>
---
 drivers/gpu/drm/drm_ioctl.c |   4 +
 drivers/gpu/drm/drm_lease.c | 270 ++++++++++++++++++++++++++++++++++++++++++++
 include/drm/drm_lease.h     |  12 ++
 include/uapi/drm/drm.h      |   5 +
 include/uapi/drm/drm_mode.h |  64 +++++++++++
 5 files changed, 355 insertions(+)

Comments

Dave Airlie July 6, 2017, 12:42 a.m. UTC | #1
> +/**
> + * Lease mode resources, creating another drm_master.
> + */
> +struct drm_mode_create_lease {
> +       /** Pointer to array of object ids (__u32) */
> +       __u64 object_ids;
> +       /** Number of object ids */
> +       __u32 object_count;
> +       /** flags for new FD (O_CLOEXEC, etc) */
> +       __u32 flags;
> +
> +       /** Return: unique identifier for lessee. */
> +       __u32 lessee_id;
> +       /** Return: file descriptor to new drm_master file */
> +       __u32 fd;
> +};
> +
> +/**
> + * List lesses from a drm_master
> + */
> +struct drm_mode_list_lessees {
> +       /** Number of lessees.
> +        * On input, provides length of the array.
> +        * On output, provides total number. No
> +        * more than the input number will be written
> +        * back, so two calls can be used to get
> +        * the size and then the data.
> +        */
> +       __u32 count_lessees;
> +
> +       /** Pointer to lessees.
> +        * pointer to __u64 array of lessee ids
> +        */
> +       __u64 lessees_ptr;
> +};

I think this needs a pad ^.

> +
> +/**
> + * Get leased objects
> + */
> +struct drm_mode_get_lease {
> +       /** Number of leased objects.
> +        * On input, provides length of the array.
> +        * On output, provides total number. No
> +        * more than the input number will be written
> +        * back, so two calls can be used to get
> +        * the size and then the data.
> +        */
> +       __u32 count_objects;
> +
> +       /** Pointer to objects.
> +        * pointer to __u32 array of object ids
> +        */
> +       __u64 objects_ptr;

And this.

> +};
> +
> +/**
> + * Revoke lease
> + */
> +struct drm_mode_revoke_lease {
> +       /** Unique ID of lessee
> +        */
> +       __u32 lessee_id;

And this.

Dave.
Keith Packard July 6, 2017, 3:38 a.m. UTC | #2
Dave Airlie <airlied@gmail.com> writes:

> I think this needs a pad ^.

> And this.

Yup. Thanks!

>> +struct drm_mode_revoke_lease {
>> +       /** Unique ID of lessee
>> +        */
>> +       __u32 lessee_id;
>
> And this.

None of the other bare 32-bit ioctl structures are padded; I think
that's probably fine as they'll be 4 bytes on both 64-bit and 32-bit
machines.
Dave Airlie Oct. 5, 2017, 3:17 a.m. UTC | #3
> ---
>  drivers/gpu/drm/drm_ioctl.c |   4 +
>  drivers/gpu/drm/drm_lease.c | 270 ++++++++++++++++++++++++++++++++++++++++++++
>  include/drm/drm_lease.h     |  12 ++
>  include/uapi/drm/drm.h      |   5 +
>  include/uapi/drm/drm_mode.h |  64 +++++++++++
>  5 files changed, 355 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
> index a5a259964c7d..0a43e82d3f06 100644
> --- a/drivers/gpu/drm/drm_ioctl.c
> +++ b/drivers/gpu/drm/drm_ioctl.c
> @@ -657,6 +657,10 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
>                       DRM_UNLOCKED|DRM_RENDER_ALLOW),
>         DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, drm_syncobj_fd_to_handle_ioctl,
>                       DRM_UNLOCKED|DRM_RENDER_ALLOW),
> +       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_LEASE, drm_mode_create_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
> +       DRM_IOCTL_DEF(DRM_IOCTL_MODE_LIST_LESSEES, drm_mode_list_lessees_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
> +       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GET_LEASE, drm_mode_get_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
> +       DRM_IOCTL_DEF(DRM_IOCTL_MODE_REVOKE_LEASE, drm_mode_revoke_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
>  };
>
>  #define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
> diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c
> index a8bd4bdd2977..f233d8b488f2 100644
> --- a/drivers/gpu/drm/drm_lease.c
> +++ b/drivers/gpu/drm/drm_lease.c
> @@ -23,6 +23,8 @@
>  #define drm_for_each_lessee(lessee, lessor) \
>         list_for_each_entry((lessee), &(lessor)->lessees, lessee_list)
>
> +static uint64_t drm_lease_idr_object;

What is this for? ^^

> +               ret = idr_alloc(&leases, &drm_lease_idr_object , object_id, object_id + 1, GFP_KERNEL);

You can just pass NULL here.

Dave.
Dave Airlie Oct. 5, 2017, 3:24 a.m. UTC | #4
On 5 October 2017 at 13:17, Dave Airlie <airlied@gmail.com> wrote:
>> ---
>>  drivers/gpu/drm/drm_ioctl.c |   4 +
>>  drivers/gpu/drm/drm_lease.c | 270 ++++++++++++++++++++++++++++++++++++++++++++
>>  include/drm/drm_lease.h     |  12 ++
>>  include/uapi/drm/drm.h      |   5 +
>>  include/uapi/drm/drm_mode.h |  64 +++++++++++
>>  5 files changed, 355 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
>> index a5a259964c7d..0a43e82d3f06 100644
>> --- a/drivers/gpu/drm/drm_ioctl.c
>> +++ b/drivers/gpu/drm/drm_ioctl.c
>> @@ -657,6 +657,10 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
>>                       DRM_UNLOCKED|DRM_RENDER_ALLOW),
>>         DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, drm_syncobj_fd_to_handle_ioctl,
>>                       DRM_UNLOCKED|DRM_RENDER_ALLOW),
>> +       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_LEASE, drm_mode_create_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
>> +       DRM_IOCTL_DEF(DRM_IOCTL_MODE_LIST_LESSEES, drm_mode_list_lessees_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
>> +       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GET_LEASE, drm_mode_get_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
>> +       DRM_IOCTL_DEF(DRM_IOCTL_MODE_REVOKE_LEASE, drm_mode_revoke_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
>>  };
>>
>>  #define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
>> diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c
>> index a8bd4bdd2977..f233d8b488f2 100644
>> --- a/drivers/gpu/drm/drm_lease.c
>> +++ b/drivers/gpu/drm/drm_lease.c
>> @@ -23,6 +23,8 @@
>>  #define drm_for_each_lessee(lessee, lessor) \
>>         list_for_each_entry((lessee), &(lessor)->lessees, lessee_list)
>>
>> +static uint64_t drm_lease_idr_object;
>
> What is this for? ^^
>
>> +               ret = idr_alloc(&leases, &drm_lease_idr_object , object_id, object_id + 1, GFP_KERNEL);
>
> You can just pass NULL here.
>

Just read the comment, this smells a bit of black magic, I'll spend
some time staring at it :-)

Dave.
Dave Airlie Oct. 5, 2017, 3:37 a.m. UTC | #5
On 5 October 2017 at 13:24, Dave Airlie <airlied@gmail.com> wrote:
> On 5 October 2017 at 13:17, Dave Airlie <airlied@gmail.com> wrote:
>>> ---
>>>  drivers/gpu/drm/drm_ioctl.c |   4 +
>>>  drivers/gpu/drm/drm_lease.c | 270 ++++++++++++++++++++++++++++++++++++++++++++
>>>  include/drm/drm_lease.h     |  12 ++
>>>  include/uapi/drm/drm.h      |   5 +
>>>  include/uapi/drm/drm_mode.h |  64 +++++++++++
>>>  5 files changed, 355 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
>>> index a5a259964c7d..0a43e82d3f06 100644
>>> --- a/drivers/gpu/drm/drm_ioctl.c
>>> +++ b/drivers/gpu/drm/drm_ioctl.c
>>> @@ -657,6 +657,10 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
>>>                       DRM_UNLOCKED|DRM_RENDER_ALLOW),
>>>         DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, drm_syncobj_fd_to_handle_ioctl,
>>>                       DRM_UNLOCKED|DRM_RENDER_ALLOW),
>>> +       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_LEASE, drm_mode_create_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
>>> +       DRM_IOCTL_DEF(DRM_IOCTL_MODE_LIST_LESSEES, drm_mode_list_lessees_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
>>> +       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GET_LEASE, drm_mode_get_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
>>> +       DRM_IOCTL_DEF(DRM_IOCTL_MODE_REVOKE_LEASE, drm_mode_revoke_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
>>>  };
>>>
>>>  #define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
>>> diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c
>>> index a8bd4bdd2977..f233d8b488f2 100644
>>> --- a/drivers/gpu/drm/drm_lease.c
>>> +++ b/drivers/gpu/drm/drm_lease.c
>>> @@ -23,6 +23,8 @@
>>>  #define drm_for_each_lessee(lessee, lessor) \
>>>         list_for_each_entry((lessee), &(lessor)->lessees, lessee_list)
>>>
>>> +static uint64_t drm_lease_idr_object;
>>
>> What is this for? ^^
>>
>>> +               ret = idr_alloc(&leases, &drm_lease_idr_object , object_id, object_id + 1, GFP_KERNEL);
>>
>> You can just pass NULL here.
>>
>
> Just read the comment, this smells a bit of black magic, I'll spend
> some time staring at it :-)

I think a comment in here is definitely warranted with what you expect
from the idr interface here.

You seem to be storing the object ids in it from the user, and failing
if you get a duplicate, then later dequeuing the idr.

I'm not sure this an intended use of idr :-), my brain is saying a
bitmap would work just as well, or even why we want/need
deduplication here. If userspace does something stupid does it matter?

Because otherwise you could just memdup_user the array of ids, and
pass that in without the idr stuff.

Dave.
Keith Packard Oct. 5, 2017, 6:23 a.m. UTC | #6
Dave Airlie <airlied@gmail.com> writes:

> I think a comment in here is definitely warranted with what you expect
> from the idr interface here.

I've added a longer comment where this value is used; I tried replacing
the IDR with a sorted array and the result wasn't any easier to use or
understand. I'm pretty sure it would be more efficient, but as leases
contain only a few objects in the cases we're considering, it's hard to
get worked up about performance when the code is larger and mostly not
any easier to understand. Feel free to take a look at that:

        git://people.freedesktop.org/~keithp/linux drm-lease-array

> Because otherwise you could just memdup_user the array of ids, and
> pass that in without the idr stuff.

Yeah, that part was more efficient. The rest of the places using this
data were longer. I guess I could bail on sorting the array, but I fear
someone would come up with a place needing a huge lease list, and then
we'd find performance issues.

Thanks for your review; I've sent out a new series and pushed it to my
repo in the drm-lease-v4 branch
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index a5a259964c7d..0a43e82d3f06 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -657,6 +657,10 @@  static const struct drm_ioctl_desc drm_ioctls[] = {
 		      DRM_UNLOCKED|DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, drm_syncobj_fd_to_handle_ioctl,
 		      DRM_UNLOCKED|DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_LEASE, drm_mode_create_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+	DRM_IOCTL_DEF(DRM_IOCTL_MODE_LIST_LESSEES, drm_mode_list_lessees_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GET_LEASE, drm_mode_get_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+	DRM_IOCTL_DEF(DRM_IOCTL_MODE_REVOKE_LEASE, drm_mode_revoke_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
 };
 
 #define DRM_CORE_IOCTL_COUNT	ARRAY_SIZE( drm_ioctls )
diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c
index a8bd4bdd2977..f233d8b488f2 100644
--- a/drivers/gpu/drm/drm_lease.c
+++ b/drivers/gpu/drm/drm_lease.c
@@ -23,6 +23,8 @@ 
 #define drm_for_each_lessee(lessee, lessor) \
 	list_for_each_entry((lessee), &(lessor)->lessees, lessee_list)
 
+static uint64_t drm_lease_idr_object;
+
 /**
  * drm_lease_owner - return ancestor owner drm_master
  * @master: drm_master somewhere within tree of lessees and lessors
@@ -374,3 +376,271 @@  void _drm_lease_revoke(struct drm_master *top)
 		}
 	}
 }
+
+/**
+ * drm_mode_create_lease_ioctl - create a new lease
+ * @dev: the drm device
+ * @data: pointer to struct drm_mode_create_lease
+ * @file_priv: the file being manipulated
+ *
+ * The master associated with the specified file will have a lease
+ * created containing the objects specified in the ioctl structure.
+ * A file descriptor will be allocated for that and returned to the
+ * application.
+ */
+int drm_mode_create_lease_ioctl(struct drm_device *dev,
+				void *data, struct drm_file *lessor_priv)
+{
+	struct drm_mode_create_lease *cl = data;
+	size_t object_count;
+	size_t o;
+	int ret = 0;
+	struct idr leases;
+	struct drm_master *lessor = lessor_priv->master;
+	struct drm_master *lessee = NULL;
+	struct file *lessee_file = NULL;
+	struct file *lessor_file = lessor_priv->filp;
+	struct drm_file *lessee_priv;
+	int fd = -1;
+
+	/* Do not allow sub-leases */
+	if (lessor->lessor)
+		return -EINVAL;
+
+	object_count = cl->object_count;
+	idr_init(&leases);
+
+	/* Allocate a file descriptor for the lease */
+	fd = get_unused_fd_flags(cl->flags & (O_CLOEXEC | O_NONBLOCK));
+
+	DRM_DEBUG_LEASE("Creating new lease\n");
+
+	/* Lookup the mode objects and add their IDs to the lease request */
+	for (o = 0; o < object_count; o++) {
+		__u32 object_id;
+
+		if (copy_from_user(&object_id,
+				   u64_to_user_ptr(cl->object_ids) + o * sizeof (__u32),
+				   sizeof (__u32))) {
+			ret = -EFAULT;
+			goto out_leases;
+		}
+		DRM_DEBUG_LEASE("Adding object %d to lease\n", object_id);
+
+		/* Just need to have something non-NULL here, it doesn't matter what */
+		ret = idr_alloc(&leases, &drm_lease_idr_object , object_id, object_id + 1, GFP_KERNEL);
+		if (ret < 0) {
+			DRM_DEBUG_LEASE("Object %d cannot be inserted into leases (%d)\n",
+					object_id, ret);
+			goto out_leases;
+		}
+	}
+
+	mutex_lock(&dev->master_mutex);
+
+	DRM_DEBUG_LEASE("Creating lease\n");
+	lessee = drm_lease_create(lessor, &leases);
+
+	if (IS_ERR(lessee)) {
+		ret = PTR_ERR(lessee);
+		mutex_unlock(&dev->master_mutex);
+		goto out_leases;
+	}
+
+	/* Clone the lessor file to create a new file for us */
+	DRM_DEBUG_LEASE("Allocating lease file\n");
+	path_get(&lessor_file->f_path);
+	lessee_file = alloc_file(&lessor_file->f_path,
+				 lessor_file->f_mode,
+				 fops_get(lessor_file->f_inode->i_fop));
+	mutex_unlock(&dev->master_mutex);
+
+	if (IS_ERR(lessee_file)) {
+		ret = PTR_ERR(lessee_file);
+		goto out_lessee;
+	}
+
+	/* Initialize the new file for DRM */
+	DRM_DEBUG_LEASE("Initializing the file with %p\n", lessee_file->f_op->open);
+	ret = lessee_file->f_op->open(lessee_file->f_inode, lessee_file);
+	if (ret)
+		goto out_lessee_file;
+
+	lessee_priv = lessee_file->private_data;
+
+	/* Change the file to a master one */
+	drm_master_put(&lessee_priv->master);
+	lessee_priv->master = lessee;
+	lessee_priv->is_master = 1;
+	lessee_priv->authenticated = 1;
+
+	/* Hook up the fd */
+	fd_install(fd, lessee_file);
+
+	/* Pass fd back to userspace */
+	DRM_DEBUG_LEASE("Returning fd %d id %d\n", fd, lessee->lessee_id);
+	cl->fd = fd;
+	cl->lessee_id = lessee->lessee_id;
+
+	DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl succeeded\n");
+	return 0;
+
+out_lessee_file:
+	fput(lessee_file);
+
+out_lessee:
+	drm_master_put(&lessee);
+
+out_leases:
+	idr_destroy(&leases);
+	put_unused_fd(fd);
+
+	DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl failed: %d\n", ret);
+	return ret;
+}
+
+/**
+ * drm_mode_list_lessees_ioctl - list lessee ids
+ * @dev: the drm device
+ * @data: pointer to struct drm_mode_list_lessees
+ * @lessor_priv: the file being manipulated
+ *
+ * Starting from the master associated with the specified file,
+ * the master with the provided lessee_id is found, and then
+ * an array of lessee ids associated with leases from that master
+ * are returned.
+ */
+
+int drm_mode_list_lessees_ioctl(struct drm_device *dev,
+			       void *data, struct drm_file *lessor_priv)
+{
+	struct drm_mode_list_lessees *arg = data;
+	__u32 __user *lessee_ids = (__u32 __user *) (uintptr_t) (arg->lessees_ptr);
+	__u32 count_lessees = arg->count_lessees;
+	struct drm_master *lessor = lessor_priv->master, *lessee;
+	int count;
+	int ret = 0;
+
+	DRM_DEBUG_LEASE("List lessees for %d\n", lessor->lessee_id);
+
+	mutex_lock(&dev->master_mutex);
+
+	count = 0;
+	drm_for_each_lessee(lessee, lessor) {
+		/* Only list un-revoked leases */
+		if (!idr_is_empty(&lessee->leases)) {
+			if (count_lessees > count) {
+				DRM_DEBUG_LEASE("Add lessee %d\n", lessee->lessee_id);
+				ret = put_user(lessee->lessee_id, lessee_ids + count);
+				if (ret)
+					break;
+			}
+			count++;
+		}
+	}
+
+	DRM_DEBUG_LEASE("Lessor leases to %d\n", count);
+	if (ret == 0)
+		arg->count_lessees = count;
+
+	mutex_unlock(&dev->master_mutex);
+
+	return ret;
+}
+
+/**
+ * drm_mode_get_lease_ioctl - list leased objects
+ * @dev: the drm device
+ * @data: pointer to struct drm_mode_get_lease
+ * @file_priv: the file being manipulated
+ *
+ * Return the list of leased objects for the specified lessee
+ */
+
+int drm_mode_get_lease_ioctl(struct drm_device *dev,
+			     void *data, struct drm_file *lessee_priv)
+{
+	struct drm_mode_get_lease *arg = data;
+	__u32 __user *object_ids = (__u32 __user *) (uintptr_t) (arg->objects_ptr);
+	__u32 count_objects = arg->count_objects;
+	struct drm_master *lessee = lessee_priv->master;
+	struct idr *object_idr;
+	int count;
+	void *entry;
+	int object;
+	int ret = 0;
+
+	DRM_DEBUG_LEASE("get lease for %d\n", lessee->lessee_id);
+
+	mutex_lock(&dev->master_mutex);
+
+	if (lessee->lessor == NULL)
+		/* owner can use all objects */
+		object_idr = &lessee->dev->mode_config.crtc_idr;
+	else
+		/* lessee can only use allowed object */
+		object_idr = &lessee->leases;
+
+	count = 0;
+	idr_for_each_entry(object_idr, entry, object) {
+		if (count_objects > count) {
+			DRM_DEBUG_LEASE("adding object %d\n", object);
+			ret = put_user(object, object_ids + count);
+			if (ret)
+				break;
+		}
+		count++;
+	}
+
+	DRM_DEBUG("lease holds %d objects\n", count);
+	if (ret == 0)
+		arg->count_objects = count;
+
+	mutex_unlock(&dev->master_mutex);
+
+	return ret;
+}
+
+/**
+ * drm_mode_revoke_lease_ioctl - revoke lease
+ * @dev: the drm device
+ * @data: pointer to struct drm_mode_revoke_lease
+ * @file_priv: the file being manipulated
+ *
+ * This removes all of the objects from the lease without
+ * actually getting rid of the lease itself; that way all
+ * references to it still work correctly
+ */
+int drm_mode_revoke_lease_ioctl(struct drm_device *dev,
+				void *data, struct drm_file *lessor_priv)
+{
+	struct drm_mode_revoke_lease *arg = data;
+	struct drm_master *lessor = lessor_priv->master;
+	struct drm_master *lessee;
+	int ret = 0;
+
+	DRM_DEBUG_LEASE("revoke lease for %d\n", arg->lessee_id);
+
+	mutex_lock(&dev->master_mutex);
+
+	lessee = _drm_find_lessee(lessor, arg->lessee_id);
+
+	/* No such lessee */
+	if (!lessee) {
+		ret = -ENOENT;
+		goto fail;
+	}
+
+	/* Lease is not held by lessor */
+	if (lessee->lessor != lessor) {
+		ret = -EACCES;
+		goto fail;
+	}
+
+	_drm_lease_revoke(lessee);
+
+fail:
+	mutex_unlock(&dev->master_mutex);
+
+	return ret;
+}
diff --git a/include/drm/drm_lease.h b/include/drm/drm_lease.h
index a49667db1d6d..53ffcee2617e 100644
--- a/include/drm/drm_lease.h
+++ b/include/drm/drm_lease.h
@@ -33,4 +33,16 @@  uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs);
 
 uint32_t drm_lease_filter_encoders(struct drm_file *file_priv, uint32_t encoders);
 
+int drm_mode_create_lease_ioctl(struct drm_device *dev,
+				void *data, struct drm_file *file_priv);
+
+int drm_mode_list_lessees_ioctl(struct drm_device *dev,
+				void *data, struct drm_file *file_priv);
+
+int drm_mode_get_lease_ioctl(struct drm_device *dev,
+			     void *data, struct drm_file *file_priv);
+
+int drm_mode_revoke_lease_ioctl(struct drm_device *dev,
+				void *data, struct drm_file *file_priv);
+
 #endif /* _DRM_LEASE_H_ */
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index 101593ab10ac..dd350bf0ad75 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -841,6 +841,11 @@  extern "C" {
 #define DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD	DRM_IOWR(0xC1, struct drm_syncobj_handle)
 #define DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE	DRM_IOWR(0xC2, struct drm_syncobj_handle)
 
+#define DRM_IOCTL_MODE_CREATE_LEASE	DRM_IOWR(0xC3, struct drm_mode_create_lease)
+#define DRM_IOCTL_MODE_LIST_LESSEES	DRM_IOWR(0xC4, struct drm_mode_list_lessees)
+#define DRM_IOCTL_MODE_GET_LEASE	DRM_IOWR(0xC5, struct drm_mode_get_lease)
+#define DRM_IOCTL_MODE_REVOKE_LEASE	DRM_IOWR(0xC6, struct drm_mode_revoke_lease)
+
 /**
  * Device specific ioctls should only be in their respective headers
  * The device specific ioctl range is from 0x40 to 0x9f.
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 403339f98a92..184948bdd4b4 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -732,6 +732,70 @@  struct drm_mode_destroy_blob {
 	__u32 blob_id;
 };
 
+/**
+ * Lease mode resources, creating another drm_master.
+ */
+struct drm_mode_create_lease {
+	/** Pointer to array of object ids (__u32) */
+	__u64 object_ids;
+	/** Number of object ids */
+	__u32 object_count;
+	/** flags for new FD (O_CLOEXEC, etc) */
+	__u32 flags;
+
+	/** Return: unique identifier for lessee. */
+	__u32 lessee_id;
+	/** Return: file descriptor to new drm_master file */
+	__u32 fd;
+};
+
+/**
+ * List lesses from a drm_master
+ */
+struct drm_mode_list_lessees {
+	/** Number of lessees.
+	 * On input, provides length of the array.
+	 * On output, provides total number. No
+	 * more than the input number will be written
+	 * back, so two calls can be used to get
+	 * the size and then the data.
+	 */
+	__u32 count_lessees;
+
+	/** Pointer to lessees.
+	 * pointer to __u64 array of lessee ids
+	 */
+	__u64 lessees_ptr;
+};
+
+/**
+ * Get leased objects
+ */
+struct drm_mode_get_lease {
+	/** Number of leased objects.
+	 * On input, provides length of the array.
+	 * On output, provides total number. No
+	 * more than the input number will be written
+	 * back, so two calls can be used to get
+	 * the size and then the data.
+	 */
+	__u32 count_objects;
+
+	/** Pointer to objects.
+	 * pointer to __u32 array of object ids
+	 */
+	__u64 objects_ptr;
+};
+
+/**
+ * Revoke lease
+ */
+struct drm_mode_revoke_lease {
+	/** Unique ID of lessee
+	 */
+	__u32 lessee_id;
+};
+
 #if defined(__cplusplus)
 }
 #endif