[2/4] drm: Add drm_object lease infrastructure
diff mbox

Message ID 20170401170841.2643-3-keithp@keithp.com
State New
Headers show

Commit Message

Keith Packard April 1, 2017, 5:08 p.m. UTC
This provides new data structures to hold "lease" information about
drm mode setting objects, and provides for creating new drm_masters
which have access to a subset of the available drm resources.

An 'owner' is a drm_master which is not leasing the objects from
another drm_master, and hence 'owns' them. This sits at the top of a
tree of drm_masters.

A 'lessee' is a drm_master which is leasing objects from some other
drm_master. Each lessee holds the set of objects which it is leasing
from the lessor.

A 'lessor' is a drm_master which is leasing objects to another
drm_master.

The set of objects any drm_master 'controls' is limited to the set of
objects it leases (for lessees) or all objects (for owners),
optionally minus the set of objects it has leased to other
drm_masters.

Objects not controlled by a drm_master cannot be modified through the
various state manipulating ioctls, and any state reported back to user
space will be edited to make them appear idle and/or unusable. For
instance, connectors always report 'disconnected', while encoders
report no possible crtcs or clones.

The full list of lessees leasing objects from an owner (either
directly, or indirectly through another lessee), can be searched from
an idr in the drm_master of the owner.

Signed-off-by: Keith Packard <keithp@keithp.com>
---
 drivers/gpu/drm/Makefile    |   3 +-
 drivers/gpu/drm/drm_auth.c  |  22 +-
 drivers/gpu/drm/drm_lease.c | 485 ++++++++++++++++++++++++++++++++++++++++++++
 include/drm/drmP.h          |   1 +
 include/drm/drm_auth.h      |  28 +++
 include/drm/drm_lease.h     |  51 +++++
 6 files changed, 588 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_lease.c
 create mode 100644 include/drm/drm_lease.h

Comments

Daniel Vetter April 2, 2017, 1:38 p.m. UTC | #1
On Sat, Apr 01, 2017 at 10:08:39AM -0700, Keith Packard wrote:
> This provides new data structures to hold "lease" information about
> drm mode setting objects, and provides for creating new drm_masters
> which have access to a subset of the available drm resources.
> 
> An 'owner' is a drm_master which is not leasing the objects from
> another drm_master, and hence 'owns' them. This sits at the top of a
> tree of drm_masters.
> 
> A 'lessee' is a drm_master which is leasing objects from some other
> drm_master. Each lessee holds the set of objects which it is leasing
> from the lessor.
> 
> A 'lessor' is a drm_master which is leasing objects to another
> drm_master.
> 
> The set of objects any drm_master 'controls' is limited to the set of
> objects it leases (for lessees) or all objects (for owners),
> optionally minus the set of objects it has leased to other
> drm_masters.

Still not sure we want to restrict objects on the lessor side. Feels like
unecessary complexity (i.e. more bugs in kernel, that's never good), and
at best only needed for lessors who can't keep track of stuff. I'm also
not sure whether we really want sub-leases in v1, that's easy to add later
on, but for now just complicates stuff. Main compositor should be a full
master, VR can be the first lease level, we don't need more I think for
now?

> Objects not controlled by a drm_master cannot be modified through the
> various state manipulating ioctls, and any state reported back to user
> space will be edited to make them appear idle and/or unusable. For
> instance, connectors always report 'disconnected', while encoders
> report no possible crtcs or clones.
> 
> The full list of lessees leasing objects from an owner (either
> directly, or indirectly through another lessee), can be searched from
> an idr in the drm_master of the owner.
> 
> Signed-off-by: Keith Packard <keithp@keithp.com>

[snip]

> diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h
> index 610223b0481b..e0e2af09d3af 100644
> --- a/include/drm/drm_auth.h
> +++ b/include/drm/drm_auth.h
> @@ -50,10 +50,38 @@ struct drm_master {
>  	struct idr magic_map;
>  	struct drm_lock_data lock;
>  	void *driver_priv;
> +
> +	/* Tree of display resource leases, each of which is a drm_master struct
> +	 * All of these get activated simultaneously, so drm_device master points

&drm_device.master to do a reference in kernel-doc. Please feed this to
kernel-doc in general and make sure the links all point at the right
stuff, and it's all parsed.

Cheers, Daniel


> +	 * at the top of the tree (for which lessor is NULL)
> +	 */
> +
> +	/** Lease holder */

	/** @lessor: Lease holder. */

> +	struct drm_master *lessor;
> +
> +	/** id for lessees. Owners always have id 0 */
> +	int	lessee_id;
> +
> +	/** other lessees of the same master */
> +	struct list_head lessee_list;
> +
> +	/** drm_masters leasing from this one */
> +	struct list_head lessees;
> +
> +	/** Objects leased to this drm_master. */
> +	struct idr leases;
> +
> +	/** All lessees under this owner (only used where lessor == NULL) */
> +	struct idr lessee_idr;
> +
> +	/** Indicates that the leased objects should be hidden from the lessor */
> +	bool mask_lease;
>  };
>  
>  struct drm_master *drm_master_get(struct drm_master *master);
>  void drm_master_put(struct drm_master **master);
>  bool drm_is_current_master(struct drm_file *fpriv);
>  
> +struct drm_master *drm_master_create(struct drm_device *dev);
> +
>  #endif
> diff --git a/include/drm/drm_lease.h b/include/drm/drm_lease.h
> new file mode 100644
> index 000000000000..e02adf3e42fd
> --- /dev/null
> +++ b/include/drm/drm_lease.h
> @@ -0,0 +1,51 @@
> +/*
> + * Copyright © 2017 Keith Packard <keithp@keithp.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + */
> +
> +#ifndef _DRM_LEASE_H_
> +#define _DRM_LEASE_H_
> +
> +struct drm_file;
> +struct drm_device;
> +
> +struct drm_master *drm_lease_owner(struct drm_master *master);
> +
> +struct drm_master *drm_lessee_find(struct drm_master *top, int lessee_id);
> +
> +void drm_lease_destroy(struct drm_master *lessee);
> +
> +struct drm_mode_object *drm_lease_find(struct drm_master *master, int id);
> +
> +/**
> + * drm_lease_check - check drm_mode_object lease status
> + * @master: the drm_master
> + * @id: the object id
> + *
> + * Checks if the specified master holds a lease on the object
> + * and also whether it has been leased to some lessee of the
> + * specified master. Return value:
> + *
> + *	0		'master' holds a lease (or owns) and has not leased
> + *	-EACCESS	Some other master holds the lease
> + *	-ENOENT		'id' is not a valid DRM object for this device
> + *	-EBUSY		'master' holds lease, but has sub-leased
> + */
> +
> +static inline int drm_lease_check(struct drm_master *master, int id) {
> +	struct drm_mode_object *object = drm_lease_find(master, id);
> +	if (IS_ERR(object))
> +		return PTR_ERR(object);
> +	return 0;
> +}
> +
> +#endif /* _DRM_LEASE_H_ */
> -- 
> 2.11.0
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Keith Packard April 2, 2017, 4:31 p.m. UTC | #2
Daniel Vetter <daniel@ffwll.ch> writes:

> Still not sure we want to restrict objects on the lessor side. Feels like
> unecessary complexity (i.e. more bugs in kernel, that's never good), and
> at best only needed for lessors who can't keep track of stuff.

It's been useful when hacking existing code, and will help catch
application bugs. Limiting access to what you actually need always seems
like good practice to me.

> I'm also not sure whether we really want sub-leases in v1, that's easy
> to add later on, but for now just complicates stuff. Main compositor
> should be a full master, VR can be the first lease level, we don't
> need more I think for now?

We've discussed how leases might be used to implement multi-user
support, so offering sub-leases means that environment could also
support leasing resources out from the users session.

We also just don't know how useful it might be until we explore the
space a bit more. Given that it takes years to get new features into
distributions, I tend to error on the side of generality.

I think a key requirement for acceptance would be a set of robust tests,
something I haven't started writing yet.

>> +	/* Tree of display resource leases, each of which is a drm_master struct
>> +	 * All of these get activated simultaneously, so drm_device master points
>
> &drm_device.master to do a reference in kernel-doc. Please feed this to
> kernel-doc in general and make sure the links all point at the right
> stuff, and it's all parsed.

Thanks, will do.
Daniel Vetter April 2, 2017, 5:51 p.m. UTC | #3
On Sat, Apr 01, 2017 at 10:08:39AM -0700, Keith Packard wrote:
> +	BUG_ON(__mutex_owner(&master->dev->mode_config.idr_mutex) != current);

Forgot to reply on this:

lockdep_assert_held + enable lockdep.

Cheers, Daniel
Keith Packard April 2, 2017, 7:59 p.m. UTC | #4
Daniel Vetter <daniel@ffwll.ch> writes:

> On Sat, Apr 01, 2017 at 10:08:39AM -0700, Keith Packard wrote:
>> +	BUG_ON(__mutex_owner(&master->dev->mode_config.idr_mutex) != current);
>
> Forgot to reply on this:
>
> lockdep_assert_held + enable lockdep.

Thanks. Will fix.
Michel Dänzer April 10, 2017, 5:16 a.m. UTC | #5
On 03/04/17 01:31 AM, Keith Packard wrote:
> Daniel Vetter <daniel@ffwll.ch> writes:
> 
>> I'm also not sure whether we really want sub-leases in v1, that's easy
>> to add later on, but for now just complicates stuff. Main compositor
>> should be a full master, VR can be the first lease level, we don't
>> need more I think for now?
> 
> We've discussed how leases might be used to implement multi-user
> support, so offering sub-leases means that environment could also
> support leasing resources out from the users session.
> 
> We also just don't know how useful it might be until we explore the
> space a bit more.

It should only be added upstream once it's clear that it's useful.


> Given that it takes years to get new features into distributions, I
> tend to error on the side of generality.

Why would it take years? Distros seem to generally use the latest
upstream kernel release available at release/freeze.

Patch
diff mbox

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index b9ae4280de9d..c2c6d61d30cf 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -16,7 +16,8 @@  drm-y       :=	drm_auth.o drm_bufs.o drm_cache.o \
 		drm_framebuffer.o drm_connector.o drm_blend.o \
 		drm_encoder.o drm_mode_object.o drm_property.o \
 		drm_plane.o drm_color_mgmt.o drm_print.o \
-		drm_dumb_buffers.o drm_mode_config.o
+		drm_dumb_buffers.o drm_mode_config.o \
+		drm_lease.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
index 6b143514a566..1db4f63860d1 100644
--- a/drivers/gpu/drm/drm_auth.c
+++ b/drivers/gpu/drm/drm_auth.c
@@ -31,6 +31,7 @@ 
 #include <drm/drmP.h>
 #include "drm_internal.h"
 #include "drm_legacy.h"
+#include <drm/drm_lease.h>
 
 /**
  * DOC: master and authentication
@@ -93,7 +94,7 @@  int drm_authmagic(struct drm_device *dev, void *data,
 	return file ? 0 : -EINVAL;
 }
 
-static struct drm_master *drm_master_create(struct drm_device *dev)
+struct drm_master *drm_master_create(struct drm_device *dev)
 {
 	struct drm_master *master;
 
@@ -107,6 +108,14 @@  static struct drm_master *drm_master_create(struct drm_device *dev)
 	idr_init(&master->magic_map);
 	master->dev = dev;
 
+	/* initialize the tree of output resource lessees */
+	master->lessor = NULL;
+	master->lessee_id = 0;
+	INIT_LIST_HEAD(&master->lessees);
+	INIT_LIST_HEAD(&master->lessee_list);
+	idr_init(&master->leases);
+	idr_init(&master->lessee_idr);
+
 	return master;
 }
 
@@ -189,6 +198,12 @@  int drm_setmaster_ioctl(struct drm_device *dev, void *data,
 		goto out_unlock;
 	}
 
+	if (file_priv->master->lessor != NULL) {
+		DRM_DEBUG_LEASE("Attempt to set lessee %d as master\n", file_priv->master->lessee_id);
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
 	ret = drm_set_master(dev, file_priv, false);
 out_unlock:
 	mutex_unlock(&dev->master_mutex);
@@ -310,12 +325,17 @@  static void drm_master_destroy(struct kref *kref)
 	struct drm_master *master = container_of(kref, struct drm_master, refcount);
 	struct drm_device *dev = master->dev;
 
+	drm_lease_destroy(master);
+
 	if (dev->driver->master_destroy)
 		dev->driver->master_destroy(dev, master);
 
 	drm_legacy_master_rmmaps(dev, master);
 
 	idr_destroy(&master->magic_map);
+
+	idr_destroy(&master->lessee_idr);
+
 	kfree(master->unique);
 	kfree(master);
 }
diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c
new file mode 100644
index 000000000000..782005c7706d
--- /dev/null
+++ b/drivers/gpu/drm/drm_lease.c
@@ -0,0 +1,485 @@ 
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include "drm_internal.h"
+#include "drm_legacy.h"
+#include "drm_crtc_internal.h"
+#include <drm/drm_lease.h>
+#include <drm/drm_auth.h>
+
+#define drm_for_each_lessee(lessee, lessor) \
+	list_for_each_entry((lessee), &(lessor)->lessees, lessee_list)
+
+/*
+ * drm_lease_owner - return ancestor owner drm_master
+ * @master: drm_master somewhere within tree of lessees and lessors
+ *
+ * RETURN:
+ *
+ * drm_master at the top of the tree (i.e, with lessor NULL
+ */
+struct drm_master *drm_lease_owner(struct drm_master *master) {
+	while (master->lessor != NULL)
+		master = master->lessor;
+	return master;
+}
+EXPORT_SYMBOL(drm_lease_owner);
+
+/**
+ * _drm_lease_find: Look up an object and check who holds the lease
+ * @master: the master to look in
+ * @id: the id to check
+ *
+ * Check if 'master' holds a lease (or owns) 'id' and has not
+ * sub-leased it to another master. Return value:
+ *
+ *	object		'master' holds lease and has not sub-leased
+ *	-EACCESS	Some other master holds the lease
+ *	-ENOENT		'id' is not a valid DRM object for this device
+ *	-EBUSY		'master' holds lease, but has sub-leased
+ *
+ * Must be called with mode_config.idr_mutex held
+ */
+static struct drm_mode_object *_drm_lease_find(struct drm_master *master, int id)
+{
+	struct drm_master *lessee;
+	struct drm_mode_object *object;
+
+	DRM_DEBUG_LEASE("master %d object %d\n", master->lessee_id, id);
+	BUG_ON(__mutex_owner(&master->dev->mode_config.idr_mutex) != current);
+
+	object = idr_find(&master->dev->mode_config.crtc_idr, id);
+	if (IS_ERR_OR_NULL(object)) {
+		DRM_DEBUG_LEASE("no such object %d\n", id);
+		return ERR_PTR(-ENOENT);
+	}
+
+	if (master->lessor) {
+		/*
+		 * If the lessor is not the owner, then make sure they
+		 * are the current lease holder for the object
+		 */
+		if (idr_find(&master->leases, id) == NULL) {
+			DRM_DEBUG_LEASE("%d is not lessee for %d\n", master->lessee_id, id);
+			return ERR_PTR(-EACCES);
+		}
+	}
+
+	/* Now make sure no other lessee already holds a lease which masks this object */
+	drm_for_each_lessee(lessee, master) {
+		if (lessee->mask_lease && idr_find(&lessee->leases, id) != NULL) {
+			DRM_DEBUG_LEASE("id %d is held by %d\n", id, lessee->lessee_id);
+			return ERR_PTR(-EBUSY);
+		}
+	}
+
+	DRM_DEBUG_LEASE("id %d is held by us\n", id);
+	return object;
+}
+
+struct drm_mode_object *drm_lease_find(struct drm_master *master, int id)
+{
+	struct drm_mode_object *object;
+
+	BUG_ON(__mutex_owner(&master->dev->mode_config.idr_mutex) == current);
+	mutex_lock(&master->dev->mode_config.idr_mutex);
+	object = _drm_lease_find(master, id);
+	mutex_unlock(&master->dev->mode_config.idr_mutex);
+	if (IS_ERR(object))
+		DRM_DEBUG_LEASE("find %d from master %d failed %ld\n", id, master->lessee_id, PTR_ERR(object));
+	else
+		DRM_DEBUG_LEASE("find %d from master %d %p\n", id, master->lessee_id, object);
+	return object;
+}
+EXPORT_SYMBOL(drm_lease_find);
+
+static inline int _drm_lease_check(struct drm_master *master, int id) {
+	struct drm_mode_object *object = _drm_lease_find(master, id);
+	if (IS_ERR(object))
+		return PTR_ERR(object);
+	return 0;
+}
+
+/**
+ * drm_lease_hoder - find current lease holder for a resource
+ * @master: Some drm_master for the device
+ * @id: the resource id
+ *
+ * Walks down the lease holder tree looking for the drm_master who holds
+ * a lease and is not sub-leasing to another drm_master.
+ *
+ * Returns the owner if no lessee holds the resource
+ */
+static struct drm_master *_drm_lease_holder(struct drm_master *master, int id)
+{
+	struct drm_master *lessor = drm_lease_owner(master);
+
+	for (;;) {
+		struct drm_master *lessee;
+		struct drm_master *found;
+
+		found = NULL;
+		drm_for_each_lessee(lessee, lessor) {
+			if (idr_find(&lessee->leases, id) != NULL) {
+				found = lessee;
+				break;
+			}
+		}
+		if (found == NULL)
+			return lessor;
+		lessor = found;
+	}
+}
+
+/**
+ * drm_lessee_is_descendant - check hierarchy structure
+ * @lessee: lessee
+ * @lessor: lessor
+ *
+ * Checks whether 'lessor' is in control of 'lessee' resources, which
+ * happens when lessee is lessor or lessee is a lessee of lessor or
+ * one of lessors lessees (recursively)
+ */
+static bool
+drm_lessee_is_descendant(struct drm_master *lessee, struct drm_master *lessor)
+{
+	/* Walk up the chain to the owner */
+	while (lessee) {
+
+		/* return true if we found lessor on the way up */
+		if (lessee == lessor)
+			return true;
+		lessee = lessee->lessor;
+	}
+
+	/* return false if lessor is not 'above' lessee */
+	return false;
+}
+
+/**
+ * drm_lessee_get - Get a reference on a lessee_id in a lease sub-tree
+ * @top: the top of the sub-tree to search in
+ * @id: the lessee_id to look for
+ */
+static struct drm_master *
+drm_lessee_get(struct drm_master *top, int lessee_id)
+{
+	struct drm_master *owner;
+	struct drm_master *lessee;
+
+	BUG_ON(__mutex_owner(&top->dev->master_mutex) != current);
+
+	DRM_DEBUG_LEASE("%d\n", lessee_id);
+
+	owner = drm_lease_owner(top);
+	if (lessee_id == 0)
+		lessee = owner;
+	else
+		lessee = idr_find(&owner->lessee_idr, lessee_id);
+
+	DRM_DEBUG_LEASE("%d -> %p (top %p)\n", lessee_id, lessee, top);
+	if (!lessee || !drm_lessee_is_descendant(lessee, top)) {
+		DRM_DEBUG_LEASE("null lessee or not descendant\n");
+		return NULL;
+	}
+
+	return drm_master_get(lessee);
+}
+
+enum drm_lease_walk {
+	drm_lease_walk_stop,
+	drm_lease_walk_dont_descend,
+	drm_lease_walk_descend
+};
+
+/**
+ * drm_lease_walk - walk a subtree of leases
+ * @master: the top of the sub-tree to walk
+ * @func: the function to call on each lease
+ * @closure: data to pass to the function
+ */
+static struct drm_master *
+drm_lease_walk(struct drm_master *top,
+	       enum drm_lease_walk (*func)(struct drm_master *lessee, void *closure),
+	       void *closure)
+{
+	struct drm_master *master = top;
+	enum drm_lease_walk result;
+
+	for (;;) {
+		result = (*func)(master, closure);
+		if (result == drm_lease_walk_stop)
+			return master;
+		if (result == drm_lease_walk_descend && !list_empty(&master->lessees)) {
+			master = list_first_entry(&master->lessees, struct drm_master, lessee_list);
+		} else {
+			while (master != top && list_is_last(&master->lessee_list,
+							     &master->lessor->lessees))
+				master = master->lessor;
+			if (master == top)
+				return NULL;
+			master = list_next_entry(master, lessee_list);
+		}
+	}
+}
+
+/**
+ * drm_lease_filter_crtcs - remove unleased crtcs from set
+ * @master: drm_master of requestor
+ * @crtcs: bitmask of crtcs to check
+ */
+uint32_t drm_lease_filter_crtcs(struct drm_master *master, uint32_t crtcs)
+{
+	struct drm_device *dev = master->dev;
+	struct drm_crtc *crtc;
+	int count;
+
+	BUG_ON(__mutex_owner(&master->dev->mode_config.idr_mutex) == current);
+	mutex_lock(&master->dev->mode_config.idr_mutex);
+	count = -1;
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+		uint32_t	mask = 1ul << count;
+		count++;
+
+		if ((crtcs & mask) != 0)
+			if (IS_ERR(_drm_lease_find(master, crtc->base.id)))
+				crtcs &= ~mask;
+	}
+	mutex_unlock(&master->dev->mode_config.idr_mutex);
+	return crtcs;
+}
+EXPORT_SYMBOL(drm_lease_filter_crtcs);
+
+/**
+ * drm_lease_filter_encoders - remove unleased encoders from set
+ * @master: drm_master of requestor
+ * @encoders: bitmask of encoders to check
+ */
+uint32_t drm_lease_filter_encoders(struct drm_master *master, uint32_t encoders)
+{
+	struct drm_device *dev = master->dev;
+	struct drm_encoder *encoder;
+	int count;
+
+	BUG_ON(__mutex_owner(&master->dev->mode_config.idr_mutex) == current);
+	mutex_lock(&master->dev->mode_config.idr_mutex);
+	count = -1;
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		uint32_t	mask = 1ul << count;
+		count++;
+
+		if ((encoders & mask) != 0)
+			if (IS_ERR(_drm_lease_find(master, encoder->base.id)))
+				encoders &= ~mask;
+	}
+	mutex_unlock(&master->dev->mode_config.idr_mutex);
+	return encoders;
+}
+EXPORT_SYMBOL(drm_lease_filter_encoders);
+
+/*
+ * drm_lease_create - create a new drm_master with leased objects
+ * @lessor: lease holder (or owner) of objects
+ * @leases: objects to lease to the new drm_master
+ *
+ * Uses drm_master_create to allocate a new drm_master, then checks to
+ * make sure all of the desired objects can be leased, atomically
+ * leasing them to the new drmmaster.
+ *
+ * 	ERR_PTR(-EACCESS)	some other master holds the title to any object
+ * 	ERR_PTR(-ENOENT)	some object is not a valid DRM object for this device
+ * 	ERR_PTR(-EBUSY)		some other lessee holds title to this object
+ *	ERR_PTR(-EEXIST)	same object specified more than once in the provided list
+ *	ERR_PTR(-ENOMEM)	allocation failed
+ */
+static struct drm_master *drm_lease_create(struct drm_master *lessor, bool mask_lease, struct idr *leases)
+{
+	int error;
+	struct drm_master *lessee;
+	int object;
+	int id;
+	void *entry;
+
+	DRM_DEBUG_LEASE("lessor %d\n", lessor->lessee_id);
+
+	BUG_ON(__mutex_owner(&lessor->dev->master_mutex) != current);
+
+	lessee = drm_master_create(lessor->dev);
+	if (!lessee) {
+		DRM_DEBUG_LEASE("drm_master_create failed\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	/* Insert the new lessee into the tree */
+
+	id = idr_alloc(&(drm_lease_owner(lessor)->lessee_idr), lessee, 1, 0, GFP_KERNEL);
+	if (id < 0) {
+		DRM_DEBUG_LEASE("lessee_id allocation failed %d\n", id);
+		drm_master_put(&lessee);
+		return ERR_PTR(id);
+	}
+
+	lessee->lessee_id = id;
+	lessee->lessor = drm_master_get(lessor);
+	list_add_tail(&lessee->lessee_list, &lessor->lessees);
+
+	/* Lock the mode object mutex to make the check and allocation atomic */
+
+	mutex_lock(&lessor->dev->mode_config.idr_mutex);
+	idr_for_each_entry(leases, entry, object) {
+		error = _drm_lease_check(lessor, object);
+		if (error) {
+			mutex_unlock(&lessor->dev->mode_config.idr_mutex);
+			drm_master_put(&lessee);
+			DRM_DEBUG_LEASE("_drm_lease_check %d failed %d\n", object, error);
+			return ERR_PTR(error);
+		}
+	}
+
+	/* Move the leases over */
+	lessee->leases = *leases;
+	lessee->mask_lease = mask_lease;
+	idr_init(leases);
+	mutex_unlock(&lessor->dev->mode_config.idr_mutex);
+
+	DRM_DEBUG_LEASE("new lessee %d %p, lessor %d %p\n", lessee->lessee_id, lessee, lessor->lessee_id, lessor);
+
+	return lessee;
+}
+
+static enum drm_lease_walk drm_lease_validate(struct drm_master *lessee, void *v_lessor)
+{
+	struct drm_master *lessor = v_lessor;
+	struct drm_mode_object *entry;
+	int object;
+	int to_remove;
+	bool any_removed = false;
+
+	if (lessee == lessor)
+		return drm_lease_walk_descend;
+
+	for (;;) {
+		to_remove = 0;
+
+		/* it's not safe to call idr_remove inside the idr_for_each_entry loop,
+		 * so find the first invalid object, remove that, and then try again
+		 */
+		idr_for_each_entry(&lessee->leases, entry, object) {
+			if (idr_find(&lessor->leases, object) == NULL) {
+				to_remove = object;
+				break;
+			}
+		}
+		if (to_remove == 0)
+			break;
+		idr_remove(&lessee->leases, to_remove);
+		any_removed = true;
+	}
+	if (any_removed)
+		return drm_lease_walk_descend;
+	else
+		return drm_lease_walk_dont_descend;
+}
+
+/**
+ * drm_lease_change - change the resources held in a specific lease
+ * @lessor: lessor of the resources
+ * @lessee_id: unique id of the lessee
+ * @leases: new set of resources to be held in the lease
+ */
+int
+drm_lease_change(struct drm_master *lessor, int lessee_id, bool mask_lease, struct idr *leases)
+{
+	struct drm_master *lessee;
+	int ret;
+	struct drm_mode_object *entry;
+	int object;
+
+	/* Find the lessee and hold a reference while we mess around */
+	lessee = drm_lessee_get(lessor, lessee_id);
+	if (!lessee)
+		return -ENOENT;
+	if (lessee == lessor)
+		return -EINVAL;
+
+	/*
+	 * Check to make sure the updated lease terms are valid, which
+	 * means that each resource is leased by the lessor and either
+	 * not sub-leased, or leased to the lessee
+	 */
+
+	mutex_lock(&lessor->dev->mode_config.idr_mutex);
+	idr_for_each_entry(leases, entry, object) {
+		entry = _drm_lease_find(lessor, object);
+		if (IS_ERR(entry)) {
+			ret = PTR_ERR(entry);
+			if (ret != -EBUSY ||
+			    _drm_lease_holder(lessor, object) != lessee)
+			{
+				mutex_unlock(&lessor->dev->mode_config.idr_mutex);
+				drm_master_put(&lessee);
+				return ret;
+			}
+		}
+	}
+
+	/* Free the existing leases and move the new ones in */
+	idr_destroy(&lessee->leases);
+	lessee->leases = *leases;
+	lessee->mask_lease = mask_lease;
+	idr_init(leases);
+
+	drm_lease_walk(lessee, drm_lease_validate, lessee);
+
+	mutex_unlock(&lessor->dev->mode_config.idr_mutex);
+
+	drm_master_put(&lessee);
+	return 0;
+}
+
+/**
+ * drm_lease_destroy - a master is going away
+ * @master: the drm_master being destroyed
+ *
+ * Revoke all leases held by this master from it and all lessees
+ * Orphan all lessees. They'll go away when closed.
+ */
+void drm_lease_destroy(struct drm_master *master)
+{
+	struct drm_device *dev = master->dev;
+
+	BUG_ON(__mutex_owner(&dev->master_mutex) != current);
+
+	DRM_DEBUG_LEASE("drm_lease_destroy %d\n", master->lessee_id);
+
+	/* This master is referenced by all lessees, hence it cannot be destroyed
+	 * until all of them have been
+	 */
+
+	BUG_ON(!list_empty(&master->lessees));
+
+	/* Remove this master from the lessee idr in the owner */
+	if (master->lessee_id != 0) {
+		DRM_DEBUG_LEASE("remove master %d from device list of lessees\n", master->lessee_id);
+		idr_remove(&(drm_lease_owner(master)->lessee_idr), master->lessee_id);
+	}
+
+	/* Remove this master from any lessee list it may be on */
+	list_del(&master->lessee_list);
+	if (master->lessor)
+		drm_master_put(&master->lessor);
+
+	DRM_DEBUG_LEASE("drm_lease_destroy done %d\n", master->lessee_id);
+}
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 304a22c87999..b468bc9cf318 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -340,6 +340,7 @@  typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd,
 #define DRM_CONTROL_ALLOW 0x8
 #define DRM_UNLOCKED	0x10
 #define DRM_RENDER_ALLOW 0x20
+#define DRM_ANY_MASTER	0x40
 
 struct drm_ioctl_desc {
 	unsigned int cmd;
diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h
index 610223b0481b..e0e2af09d3af 100644
--- a/include/drm/drm_auth.h
+++ b/include/drm/drm_auth.h
@@ -50,10 +50,38 @@  struct drm_master {
 	struct idr magic_map;
 	struct drm_lock_data lock;
 	void *driver_priv;
+
+	/* Tree of display resource leases, each of which is a drm_master struct
+	 * All of these get activated simultaneously, so drm_device master points
+	 * at the top of the tree (for which lessor is NULL)
+	 */
+
+	/** Lease holder */
+	struct drm_master *lessor;
+
+	/** id for lessees. Owners always have id 0 */
+	int	lessee_id;
+
+	/** other lessees of the same master */
+	struct list_head lessee_list;
+
+	/** drm_masters leasing from this one */
+	struct list_head lessees;
+
+	/** Objects leased to this drm_master. */
+	struct idr leases;
+
+	/** All lessees under this owner (only used where lessor == NULL) */
+	struct idr lessee_idr;
+
+	/** Indicates that the leased objects should be hidden from the lessor */
+	bool mask_lease;
 };
 
 struct drm_master *drm_master_get(struct drm_master *master);
 void drm_master_put(struct drm_master **master);
 bool drm_is_current_master(struct drm_file *fpriv);
 
+struct drm_master *drm_master_create(struct drm_device *dev);
+
 #endif
diff --git a/include/drm/drm_lease.h b/include/drm/drm_lease.h
new file mode 100644
index 000000000000..e02adf3e42fd
--- /dev/null
+++ b/include/drm/drm_lease.h
@@ -0,0 +1,51 @@ 
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _DRM_LEASE_H_
+#define _DRM_LEASE_H_
+
+struct drm_file;
+struct drm_device;
+
+struct drm_master *drm_lease_owner(struct drm_master *master);
+
+struct drm_master *drm_lessee_find(struct drm_master *top, int lessee_id);
+
+void drm_lease_destroy(struct drm_master *lessee);
+
+struct drm_mode_object *drm_lease_find(struct drm_master *master, int id);
+
+/**
+ * drm_lease_check - check drm_mode_object lease status
+ * @master: the drm_master
+ * @id: the object id
+ *
+ * Checks if the specified master holds a lease on the object
+ * and also whether it has been leased to some lessee of the
+ * specified master. Return value:
+ *
+ *	0		'master' holds a lease (or owns) and has not leased
+ *	-EACCESS	Some other master holds the lease
+ *	-ENOENT		'id' is not a valid DRM object for this device
+ *	-EBUSY		'master' holds lease, but has sub-leased
+ */
+
+static inline int drm_lease_check(struct drm_master *master, int id) {
+	struct drm_mode_object *object = drm_lease_find(master, id);
+	if (IS_ERR(object))
+		return PTR_ERR(object);
+	return 0;
+}
+
+#endif /* _DRM_LEASE_H_ */