Re: [PATCH 5/5] drm: Add four ioctls for managing drm mode object leases [v6]

From: Sean Paul
Date: Mon Oct 16 2017 - 17:03:24 EST


On Thu, Oct 12, 2017 at 06:56:31PM -0700, Keith Packard wrote:
> 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@xxxxxxxx>:
>
> * 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@xxxxxxxxx>
>
> * Add revoke ioctl.
>
> Changes for v4 suggested by Dave Airlie <airlied@xxxxxxxxx>
>
> * Expand on the comment about the magic use of &drm_lease_idr_object
> * Pad lease ioctl structures to align on 64-bit boundaries
>
> Changes for v5 suggested by Dave Airlie <airlied@xxxxxxxxx>
>
> * Check for non-negative object_id in create_lease to avoid debug
> output from the kernel.
>
> Changes for v5 provided by Dave Airlie <airlied@xxxxxxxxx>
>
> * For non-universal planes add primary/cursor planes to lease
>
> If we aren't exposing universal planes to this userspace client,
> and it requests a lease on a crtc, we should implicitly export the
> primary and cursor planes for the crtc.
>
> If the lessee doesn't request universal planes, it will just see
> the crtc, but if it does request them it will then see the plane
> objects as well.
>
> This also moves the object look ups earlier as a side effect, so
> we'd exit the ioctl quicker for non-existant objects.
>
> * Restrict leases to crtc/connector/planes.
>
> This only allows leasing for objects we wish to allow.
>
> Signed-off-by: Keith Packard <keithp@xxxxxxxxxx>
> Signed-off-by: Dave Airlie <airlied@xxxxxxxxxx>
> ---
> drivers/gpu/drm/drm_ioctl.c | 4 +
> drivers/gpu/drm/drm_lease.c | 318 ++++++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/drm_mode_object.c | 5 +-
> include/drm/drm_lease.h | 12 ++
> include/drm/drm_mode_object.h | 2 +
> include/uapi/drm/drm.h | 5 +
> include/uapi/drm/drm_mode.h | 66 ++++++++
> 7 files changed, 410 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
> index 9c435a4c0c82..4aafe4802099 100644
> --- a/drivers/gpu/drm/drm_ioctl.c
> +++ b/drivers/gpu/drm/drm_ioctl.c
> @@ -665,6 +665,10 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
> DRM_UNLOCKED|DRM_RENDER_ALLOW),
> DRM_IOCTL_DEF(DRM_IOCTL_CRTC_GET_SEQUENCE, drm_crtc_get_sequence_ioctl, DRM_UNLOCKED),
> DRM_IOCTL_DEF(DRM_IOCTL_CRTC_QUEUE_SEQUENCE, drm_crtc_queue_sequence_ioctl, DRM_UNLOCKED),
> + 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 6c3f2445254c..88c213f9c4ab 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
> @@ -337,3 +339,319 @@ 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;
> + struct drm_mode_object *obj;
> +
> + 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);
> +
> + if ((int) object_id < 0) {
> + ret = -EINVAL;
> + goto out_leases;
> + }
> +
> + obj = drm_mode_object_find(dev, lessor_priv, object_id,
> + DRM_MODE_OBJECT_ANY);
> + if (!obj) {
> + ret = -ENOENT;
> + goto out_leases;
> + }
> +
> + /* only allow leasing on crtc/plane/connector objects */
> + if (!drm_mode_object_lease_required(obj->type)) {
> + ret = -EINVAL;
> + drm_mode_object_put(obj);
> + goto out_leases;
> + }
> +
> + /*
> + * We're using an IDR to hold the set of leased
> + * objects, but we don't need to point at the object's
> + * data structure from the lease as the main crtc_idr
> + * will be used to actually find that. Instead, all we
> + * really want is a 'leased/not-leased' result, for
> + * which any non-NULL pointer will work fine.
> + */
> + ret = idr_alloc(&leases, &drm_lease_idr_object , object_id, object_id + 1, GFP_KERNEL);

nit: space before ,

> + if (ret < 0) {
> + DRM_DEBUG_LEASE("Object %d cannot be inserted into leases (%d)\n",
> + object_id, ret);
> + drm_mode_object_put(obj);
> + goto out_leases;
> + }
> + if (obj->type == DRM_MODE_OBJECT_CRTC && !lessor_priv->universal_planes) {
> + struct drm_crtc *crtc = obj_to_crtc(obj);
> + ret = idr_alloc(&leases, &drm_lease_idr_object, crtc->primary->base.id, crtc->primary->base.id + 1, GFP_KERNEL);
> + if (ret < 0) {
> + DRM_DEBUG_LEASE("Object primary plane %d cannot be inserted into leases (%d)\n",
> + object_id, ret);
> + drm_mode_object_put(obj);
> + goto out_leases;
> + }
> + if (crtc->cursor) {
> + ret = idr_alloc(&leases, &drm_lease_idr_object, crtc->cursor->base.id, crtc->cursor->base.id + 1, GFP_KERNEL);
> + if (ret < 0) {
> + DRM_DEBUG_LEASE("Object cursor plane %d cannot be inserted into leases (%d)\n",
> + object_id, ret);
> + drm_mode_object_put(obj);
> + goto out_leases;
> + }
> + }
> + }
> + drm_mode_object_put(obj);
> + }
> +
> + 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);

Please forgive the stupid question, but where is this reference given up?

> + 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;

What about other types of objects?

> + 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/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c
> index d1599f36b605..7c8b2698c6a7 100644
> --- a/drivers/gpu/drm/drm_mode_object.c
> +++ b/drivers/gpu/drm/drm_mode_object.c
> @@ -111,7 +111,7 @@ void drm_mode_object_unregister(struct drm_device *dev,
> * Returns whether the provided type of drm_mode_object must
> * be owned or leased to be used by a process.
> */
> -static bool drm_lease_required(uint32_t type)
> +bool drm_mode_object_lease_required(uint32_t type)
> {
> switch(type) {
> case DRM_MODE_OBJECT_CRTC:
> @@ -136,7 +136,8 @@ struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
> if (obj && obj->id != id)
> obj = NULL;
>
> - if (obj && drm_lease_required(obj->type) && !_drm_lease_held(file_priv, obj->id))
> + if (obj && drm_mode_object_lease_required(obj->type) &&
> + !_drm_lease_held(file_priv, obj->id))
> obj = NULL;
>
> if (obj && obj->free_cb) {
> diff --git a/include/drm/drm_lease.h b/include/drm/drm_lease.h
> index 0981631b9aed..d2bcd1ea6cdc 100644
> --- a/include/drm/drm_lease.h
> +++ b/include/drm/drm_lease.h
> @@ -31,4 +31,16 @@ void _drm_lease_revoke(struct drm_master *master);
>
> uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs);
>
> +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/drm/drm_mode_object.h b/include/drm/drm_mode_object.h
> index c8155cb5a932..7ba3913f30b5 100644
> --- a/include/drm/drm_mode_object.h
> +++ b/include/drm/drm_mode_object.h
> @@ -154,4 +154,6 @@ int drm_object_property_get_value(struct drm_mode_object *obj,
> void drm_object_attach_property(struct drm_mode_object *obj,
> struct drm_property *property,
> uint64_t init_val);
> +
> +bool drm_mode_object_lease_required(uint32_t type);
> #endif
> diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
> index a106f6a7a0f9..9c02d2125d07 100644
> --- a/include/uapi/drm/drm.h
> +++ b/include/uapi/drm/drm.h
> @@ -888,6 +888,11 @@ extern "C" {
> #define DRM_IOCTL_SYNCOBJ_RESET DRM_IOWR(0xC4, struct drm_syncobj_array)
> #define DRM_IOCTL_SYNCOBJ_SIGNAL DRM_IOWR(0xC5, struct drm_syncobj_array)
>
> +#define DRM_IOCTL_MODE_CREATE_LEASE DRM_IOWR(0xC6, struct drm_mode_create_lease)
> +#define DRM_IOCTL_MODE_LIST_LESSEES DRM_IOWR(0xC7, struct drm_mode_list_lessees)
> +#define DRM_IOCTL_MODE_GET_LEASE DRM_IOWR(0xC8, struct drm_mode_get_lease)
> +#define DRM_IOCTL_MODE_REVOKE_LEASE DRM_IOWR(0xC9, 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 34b6bb34b002..5597a87154e5 100644
> --- a/include/uapi/drm/drm_mode.h
> +++ b/include/uapi/drm/drm_mode.h
> @@ -782,6 +782,72 @@ 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;
> + __u32 pad;
> +
> + /** 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;
> + __u32 pad;
> +
> + /** 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
> --
> 2.15.0.rc0
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@xxxxxxxxxxxxxxxxxxxxx
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

--
Sean Paul, Software Engineer, Google / Chromium OS