Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: Add four ioctls for managing drm mode object leases [v7]

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.

Changes for v4 suggested by Dave Airlie <airlied@gmail.com>

* 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@gmail.com>

* Check for non-negative object_id in create_lease to avoid debug
output from the kernel.

Changes for v6 provided by Dave Airlie <airlied@gmail.com>

* 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.

Changes for v7 provided by Dave Airlie <airlied@gmail.com>

* Check pad args are 0
* Check create flags and object count are valid.
* Check return from fd allocation
* Refactor lease idr setup and add some simple validation
* Use idr_mutex uniformly (Keith)

Signed-off-by: Keith Packard <keithp@keithp.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>

authored by

Keith Packard and committed by
Dave Airlie
62884cd3 7de440db

+504 -2
+4
drivers/gpu/drm/drm_ioctl.c
··· 665 665 DRM_UNLOCKED|DRM_RENDER_ALLOW), 666 666 DRM_IOCTL_DEF(DRM_IOCTL_CRTC_GET_SEQUENCE, drm_crtc_get_sequence_ioctl, DRM_UNLOCKED), 667 667 DRM_IOCTL_DEF(DRM_IOCTL_CRTC_QUEUE_SEQUENCE, drm_crtc_queue_sequence_ioctl, DRM_UNLOCKED), 668 + DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_LEASE, drm_mode_create_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), 669 + DRM_IOCTL_DEF(DRM_IOCTL_MODE_LIST_LESSEES, drm_mode_list_lessees_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), 670 + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GET_LEASE, drm_mode_get_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), 671 + DRM_IOCTL_DEF(DRM_IOCTL_MODE_REVOKE_LEASE, drm_mode_revoke_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), 668 672 }; 669 673 670 674 #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )
+412
drivers/gpu/drm/drm_lease.c
··· 23 23 #define drm_for_each_lessee(lessee, lessor) \ 24 24 list_for_each_entry((lessee), &(lessor)->lessees, lessee_list) 25 25 26 + static uint64_t drm_lease_idr_object; 27 + 26 28 /** 27 29 * drm_lease_owner - return ancestor owner drm_master 28 30 * @master: drm_master somewhere within tree of lessees and lessors ··· 354 352 mutex_lock(&top->dev->mode_config.idr_mutex); 355 353 _drm_lease_revoke(top); 356 354 mutex_unlock(&top->dev->mode_config.idr_mutex); 355 + } 356 + 357 + static int validate_lease(struct drm_device *dev, 358 + struct drm_file *lessor_priv, 359 + int object_count, 360 + struct drm_mode_object **objects) 361 + { 362 + int o; 363 + int has_crtc = -1; 364 + int has_connector = -1; 365 + int has_plane = -1; 366 + 367 + /* we want to confirm that there is at least one crtc, plane 368 + connector object. */ 369 + 370 + for (o = 0; o < object_count; o++) { 371 + if (objects[o]->type == DRM_MODE_OBJECT_CRTC && has_crtc == -1) { 372 + has_crtc = o; 373 + } 374 + if (objects[o]->type == DRM_MODE_OBJECT_CONNECTOR && has_connector == -1) 375 + has_connector = o; 376 + 377 + if (lessor_priv->universal_planes) { 378 + if (objects[o]->type == DRM_MODE_OBJECT_PLANE && has_plane == -1) 379 + has_plane = o; 380 + } 381 + } 382 + if (has_crtc == -1 || has_connector == -1) 383 + return -EINVAL; 384 + if (lessor_priv->universal_planes && has_plane == -1) 385 + return -EINVAL; 386 + return 0; 387 + } 388 + 389 + static int fill_object_idr(struct drm_device *dev, 390 + struct drm_file *lessor_priv, 391 + struct idr *leases, 392 + int object_count, 393 + u32 *object_ids) 394 + { 395 + struct drm_mode_object **objects; 396 + u32 o; 397 + int ret; 398 + objects = kcalloc(object_count, sizeof(struct drm_mode_object *), 399 + GFP_KERNEL); 400 + if (!objects) 401 + return -ENOMEM; 402 + 403 + /* step one - get references to all the mode objects 404 + and check for validity. */ 405 + for (o = 0; o < object_count; o++) { 406 + if ((int) object_ids[o] < 0) { 407 + ret = -EINVAL; 408 + goto out_free_objects; 409 + } 410 + 411 + objects[o] = drm_mode_object_find(dev, lessor_priv, 412 + object_ids[o], 413 + DRM_MODE_OBJECT_ANY); 414 + if (!objects[o]) { 415 + ret = -ENOENT; 416 + goto out_free_objects; 417 + } 418 + 419 + if (!drm_mode_object_lease_required(objects[o]->type)) { 420 + ret = -EINVAL; 421 + goto out_free_objects; 422 + } 423 + } 424 + 425 + ret = validate_lease(dev, lessor_priv, object_count, objects); 426 + if (ret) 427 + goto out_free_objects; 428 + 429 + /* add their IDs to the lease request - taking into account 430 + universal planes */ 431 + for (o = 0; o < object_count; o++) { 432 + struct drm_mode_object *obj = objects[o]; 433 + u32 object_id = objects[o]->id; 434 + DRM_DEBUG_LEASE("Adding object %d to lease\n", object_id); 435 + 436 + /* 437 + * We're using an IDR to hold the set of leased 438 + * objects, but we don't need to point at the object's 439 + * data structure from the lease as the main crtc_idr 440 + * will be used to actually find that. Instead, all we 441 + * really want is a 'leased/not-leased' result, for 442 + * which any non-NULL pointer will work fine. 443 + */ 444 + ret = idr_alloc(leases, &drm_lease_idr_object , object_id, object_id + 1, GFP_KERNEL); 445 + if (ret < 0) { 446 + DRM_DEBUG_LEASE("Object %d cannot be inserted into leases (%d)\n", 447 + object_id, ret); 448 + goto out_free_objects; 449 + } 450 + if (obj->type == DRM_MODE_OBJECT_CRTC && !lessor_priv->universal_planes) { 451 + struct drm_crtc *crtc = obj_to_crtc(obj); 452 + ret = idr_alloc(leases, &drm_lease_idr_object, crtc->primary->base.id, crtc->primary->base.id + 1, GFP_KERNEL); 453 + if (ret < 0) { 454 + DRM_DEBUG_LEASE("Object primary plane %d cannot be inserted into leases (%d)\n", 455 + object_id, ret); 456 + goto out_free_objects; 457 + } 458 + if (crtc->cursor) { 459 + ret = idr_alloc(leases, &drm_lease_idr_object, crtc->cursor->base.id, crtc->cursor->base.id + 1, GFP_KERNEL); 460 + if (ret < 0) { 461 + DRM_DEBUG_LEASE("Object cursor plane %d cannot be inserted into leases (%d)\n", 462 + object_id, ret); 463 + goto out_free_objects; 464 + } 465 + } 466 + } 467 + } 468 + 469 + ret = 0; 470 + out_free_objects: 471 + for (o = 0; o < object_count; o++) { 472 + if (objects[o]) 473 + drm_mode_object_put(objects[o]); 474 + } 475 + kfree(objects); 476 + return ret; 477 + } 478 + 479 + /** 480 + * drm_mode_create_lease_ioctl - create a new lease 481 + * @dev: the drm device 482 + * @data: pointer to struct drm_mode_create_lease 483 + * @file_priv: the file being manipulated 484 + * 485 + * The master associated with the specified file will have a lease 486 + * created containing the objects specified in the ioctl structure. 487 + * A file descriptor will be allocated for that and returned to the 488 + * application. 489 + */ 490 + int drm_mode_create_lease_ioctl(struct drm_device *dev, 491 + void *data, struct drm_file *lessor_priv) 492 + { 493 + struct drm_mode_create_lease *cl = data; 494 + size_t object_count; 495 + int ret = 0; 496 + struct idr leases; 497 + struct drm_master *lessor = lessor_priv->master; 498 + struct drm_master *lessee = NULL; 499 + struct file *lessee_file = NULL; 500 + struct file *lessor_file = lessor_priv->filp; 501 + struct drm_file *lessee_priv; 502 + int fd = -1; 503 + uint32_t *object_ids; 504 + 505 + /* Can't lease without MODESET */ 506 + if (!drm_core_check_feature(dev, DRIVER_MODESET)) 507 + return -EINVAL; 508 + 509 + /* Do not allow sub-leases */ 510 + if (lessor->lessor) 511 + return -EINVAL; 512 + 513 + /* need some objects */ 514 + if (cl->object_count == 0) 515 + return -EINVAL; 516 + 517 + if (cl->flags && (cl->flags & ~(O_CLOEXEC | O_NONBLOCK))) 518 + return -EINVAL; 519 + 520 + object_count = cl->object_count; 521 + 522 + object_ids = memdup_user(u64_to_user_ptr(cl->object_ids), object_count * sizeof(__u32)); 523 + if (IS_ERR(object_ids)) 524 + return PTR_ERR(object_ids); 525 + 526 + idr_init(&leases); 527 + 528 + /* fill and validate the object idr */ 529 + ret = fill_object_idr(dev, lessor_priv, &leases, 530 + object_count, object_ids); 531 + kfree(object_ids); 532 + if (ret) { 533 + idr_destroy(&leases); 534 + return ret; 535 + } 536 + 537 + /* Allocate a file descriptor for the lease */ 538 + fd = get_unused_fd_flags(cl->flags & (O_CLOEXEC | O_NONBLOCK)); 539 + if (fd < 0) { 540 + idr_destroy(&leases); 541 + return fd; 542 + } 543 + 544 + DRM_DEBUG_LEASE("Creating lease\n"); 545 + lessee = drm_lease_create(lessor, &leases); 546 + 547 + if (IS_ERR(lessee)) { 548 + ret = PTR_ERR(lessee); 549 + goto out_leases; 550 + } 551 + 552 + /* Clone the lessor file to create a new file for us */ 553 + DRM_DEBUG_LEASE("Allocating lease file\n"); 554 + path_get(&lessor_file->f_path); 555 + lessee_file = alloc_file(&lessor_file->f_path, 556 + lessor_file->f_mode, 557 + fops_get(lessor_file->f_inode->i_fop)); 558 + 559 + if (IS_ERR(lessee_file)) { 560 + ret = PTR_ERR(lessee_file); 561 + goto out_lessee; 562 + } 563 + 564 + /* Initialize the new file for DRM */ 565 + DRM_DEBUG_LEASE("Initializing the file with %p\n", lessee_file->f_op->open); 566 + ret = lessee_file->f_op->open(lessee_file->f_inode, lessee_file); 567 + if (ret) 568 + goto out_lessee_file; 569 + 570 + lessee_priv = lessee_file->private_data; 571 + 572 + /* Change the file to a master one */ 573 + drm_master_put(&lessee_priv->master); 574 + lessee_priv->master = lessee; 575 + lessee_priv->is_master = 1; 576 + lessee_priv->authenticated = 1; 577 + 578 + /* Hook up the fd */ 579 + fd_install(fd, lessee_file); 580 + 581 + /* Pass fd back to userspace */ 582 + DRM_DEBUG_LEASE("Returning fd %d id %d\n", fd, lessee->lessee_id); 583 + cl->fd = fd; 584 + cl->lessee_id = lessee->lessee_id; 585 + 586 + DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl succeeded\n"); 587 + return 0; 588 + 589 + out_lessee_file: 590 + fput(lessee_file); 591 + 592 + out_lessee: 593 + drm_master_put(&lessee); 594 + 595 + out_leases: 596 + put_unused_fd(fd); 597 + idr_destroy(&leases); 598 + 599 + DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl failed: %d\n", ret); 600 + return ret; 601 + } 602 + 603 + /** 604 + * drm_mode_list_lessees_ioctl - list lessee ids 605 + * @dev: the drm device 606 + * @data: pointer to struct drm_mode_list_lessees 607 + * @lessor_priv: the file being manipulated 608 + * 609 + * Starting from the master associated with the specified file, 610 + * the master with the provided lessee_id is found, and then 611 + * an array of lessee ids associated with leases from that master 612 + * are returned. 613 + */ 614 + 615 + int drm_mode_list_lessees_ioctl(struct drm_device *dev, 616 + void *data, struct drm_file *lessor_priv) 617 + { 618 + struct drm_mode_list_lessees *arg = data; 619 + __u32 __user *lessee_ids = (__u32 __user *) (uintptr_t) (arg->lessees_ptr); 620 + __u32 count_lessees = arg->count_lessees; 621 + struct drm_master *lessor = lessor_priv->master, *lessee; 622 + int count; 623 + int ret = 0; 624 + 625 + if (arg->pad) 626 + return -EINVAL; 627 + 628 + /* Can't lease without MODESET */ 629 + if (!drm_core_check_feature(dev, DRIVER_MODESET)) 630 + return -EINVAL; 631 + 632 + DRM_DEBUG_LEASE("List lessees for %d\n", lessor->lessee_id); 633 + 634 + mutex_lock(&dev->mode_config.idr_mutex); 635 + 636 + count = 0; 637 + drm_for_each_lessee(lessee, lessor) { 638 + /* Only list un-revoked leases */ 639 + if (!idr_is_empty(&lessee->leases)) { 640 + if (count_lessees > count) { 641 + DRM_DEBUG_LEASE("Add lessee %d\n", lessee->lessee_id); 642 + ret = put_user(lessee->lessee_id, lessee_ids + count); 643 + if (ret) 644 + break; 645 + } 646 + count++; 647 + } 648 + } 649 + 650 + DRM_DEBUG_LEASE("Lessor leases to %d\n", count); 651 + if (ret == 0) 652 + arg->count_lessees = count; 653 + 654 + mutex_unlock(&dev->mode_config.idr_mutex); 655 + 656 + return ret; 657 + } 658 + 659 + /** 660 + * drm_mode_get_lease_ioctl - list leased objects 661 + * @dev: the drm device 662 + * @data: pointer to struct drm_mode_get_lease 663 + * @file_priv: the file being manipulated 664 + * 665 + * Return the list of leased objects for the specified lessee 666 + */ 667 + 668 + int drm_mode_get_lease_ioctl(struct drm_device *dev, 669 + void *data, struct drm_file *lessee_priv) 670 + { 671 + struct drm_mode_get_lease *arg = data; 672 + __u32 __user *object_ids = (__u32 __user *) (uintptr_t) (arg->objects_ptr); 673 + __u32 count_objects = arg->count_objects; 674 + struct drm_master *lessee = lessee_priv->master; 675 + struct idr *object_idr; 676 + int count; 677 + void *entry; 678 + int object; 679 + int ret = 0; 680 + 681 + if (arg->pad) 682 + return -EINVAL; 683 + 684 + /* Can't lease without MODESET */ 685 + if (!drm_core_check_feature(dev, DRIVER_MODESET)) 686 + return -EINVAL; 687 + 688 + DRM_DEBUG_LEASE("get lease for %d\n", lessee->lessee_id); 689 + 690 + mutex_lock(&dev->mode_config.idr_mutex); 691 + 692 + if (lessee->lessor == NULL) 693 + /* owner can use all objects */ 694 + object_idr = &lessee->dev->mode_config.crtc_idr; 695 + else 696 + /* lessee can only use allowed object */ 697 + object_idr = &lessee->leases; 698 + 699 + count = 0; 700 + idr_for_each_entry(object_idr, entry, object) { 701 + if (count_objects > count) { 702 + DRM_DEBUG_LEASE("adding object %d\n", object); 703 + ret = put_user(object, object_ids + count); 704 + if (ret) 705 + break; 706 + } 707 + count++; 708 + } 709 + 710 + DRM_DEBUG("lease holds %d objects\n", count); 711 + if (ret == 0) 712 + arg->count_objects = count; 713 + 714 + mutex_unlock(&dev->mode_config.idr_mutex); 715 + 716 + return ret; 717 + } 718 + 719 + /** 720 + * drm_mode_revoke_lease_ioctl - revoke lease 721 + * @dev: the drm device 722 + * @data: pointer to struct drm_mode_revoke_lease 723 + * @file_priv: the file being manipulated 724 + * 725 + * This removes all of the objects from the lease without 726 + * actually getting rid of the lease itself; that way all 727 + * references to it still work correctly 728 + */ 729 + int drm_mode_revoke_lease_ioctl(struct drm_device *dev, 730 + void *data, struct drm_file *lessor_priv) 731 + { 732 + struct drm_mode_revoke_lease *arg = data; 733 + struct drm_master *lessor = lessor_priv->master; 734 + struct drm_master *lessee; 735 + int ret = 0; 736 + 737 + DRM_DEBUG_LEASE("revoke lease for %d\n", arg->lessee_id); 738 + 739 + /* Can't lease without MODESET */ 740 + if (!drm_core_check_feature(dev, DRIVER_MODESET)) 741 + return -EINVAL; 742 + 743 + mutex_lock(&dev->mode_config.idr_mutex); 744 + 745 + lessee = _drm_find_lessee(lessor, arg->lessee_id); 746 + 747 + /* No such lessee */ 748 + if (!lessee) { 749 + ret = -ENOENT; 750 + goto fail; 751 + } 752 + 753 + /* Lease is not held by lessor */ 754 + if (lessee->lessor != lessor) { 755 + ret = -EACCES; 756 + goto fail; 757 + } 758 + 759 + _drm_lease_revoke(lessee); 760 + 761 + fail: 762 + mutex_unlock(&dev->mode_config.idr_mutex); 763 + 764 + return ret; 357 765 }
+3 -2
drivers/gpu/drm/drm_mode_object.c
··· 111 111 * Returns whether the provided type of drm_mode_object must 112 112 * be owned or leased to be used by a process. 113 113 */ 114 - static bool drm_lease_required(uint32_t type) 114 + bool drm_mode_object_lease_required(uint32_t type) 115 115 { 116 116 switch(type) { 117 117 case DRM_MODE_OBJECT_CRTC: ··· 136 136 if (obj && obj->id != id) 137 137 obj = NULL; 138 138 139 - if (obj && drm_lease_required(obj->type) && !_drm_lease_held(file_priv, obj->id)) 139 + if (obj && drm_mode_object_lease_required(obj->type) && 140 + !_drm_lease_held(file_priv, obj->id)) 140 141 obj = NULL; 141 142 142 143 if (obj && obj->free_cb) {
+12
include/drm/drm_lease.h
··· 31 31 32 32 uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs); 33 33 34 + int drm_mode_create_lease_ioctl(struct drm_device *dev, 35 + void *data, struct drm_file *file_priv); 36 + 37 + int drm_mode_list_lessees_ioctl(struct drm_device *dev, 38 + void *data, struct drm_file *file_priv); 39 + 40 + int drm_mode_get_lease_ioctl(struct drm_device *dev, 41 + void *data, struct drm_file *file_priv); 42 + 43 + int drm_mode_revoke_lease_ioctl(struct drm_device *dev, 44 + void *data, struct drm_file *file_priv); 45 + 34 46 #endif /* _DRM_LEASE_H_ */
+2
include/drm/drm_mode_object.h
··· 154 154 void drm_object_attach_property(struct drm_mode_object *obj, 155 155 struct drm_property *property, 156 156 uint64_t init_val); 157 + 158 + bool drm_mode_object_lease_required(uint32_t type); 157 159 #endif
+5
include/uapi/drm/drm.h
··· 888 888 #define DRM_IOCTL_SYNCOBJ_RESET DRM_IOWR(0xC4, struct drm_syncobj_array) 889 889 #define DRM_IOCTL_SYNCOBJ_SIGNAL DRM_IOWR(0xC5, struct drm_syncobj_array) 890 890 891 + #define DRM_IOCTL_MODE_CREATE_LEASE DRM_IOWR(0xC6, struct drm_mode_create_lease) 892 + #define DRM_IOCTL_MODE_LIST_LESSEES DRM_IOWR(0xC7, struct drm_mode_list_lessees) 893 + #define DRM_IOCTL_MODE_GET_LEASE DRM_IOWR(0xC8, struct drm_mode_get_lease) 894 + #define DRM_IOCTL_MODE_REVOKE_LEASE DRM_IOWR(0xC9, struct drm_mode_revoke_lease) 895 + 891 896 /** 892 897 * Device specific ioctls should only be in their respective headers 893 898 * The device specific ioctl range is from 0x40 to 0x9f.
+66
include/uapi/drm/drm_mode.h
··· 782 782 __u32 blob_id; 783 783 }; 784 784 785 + /** 786 + * Lease mode resources, creating another drm_master. 787 + */ 788 + struct drm_mode_create_lease { 789 + /** Pointer to array of object ids (__u32) */ 790 + __u64 object_ids; 791 + /** Number of object ids */ 792 + __u32 object_count; 793 + /** flags for new FD (O_CLOEXEC, etc) */ 794 + __u32 flags; 795 + 796 + /** Return: unique identifier for lessee. */ 797 + __u32 lessee_id; 798 + /** Return: file descriptor to new drm_master file */ 799 + __u32 fd; 800 + }; 801 + 802 + /** 803 + * List lesses from a drm_master 804 + */ 805 + struct drm_mode_list_lessees { 806 + /** Number of lessees. 807 + * On input, provides length of the array. 808 + * On output, provides total number. No 809 + * more than the input number will be written 810 + * back, so two calls can be used to get 811 + * the size and then the data. 812 + */ 813 + __u32 count_lessees; 814 + __u32 pad; 815 + 816 + /** Pointer to lessees. 817 + * pointer to __u64 array of lessee ids 818 + */ 819 + __u64 lessees_ptr; 820 + }; 821 + 822 + /** 823 + * Get leased objects 824 + */ 825 + struct drm_mode_get_lease { 826 + /** Number of leased objects. 827 + * On input, provides length of the array. 828 + * On output, provides total number. No 829 + * more than the input number will be written 830 + * back, so two calls can be used to get 831 + * the size and then the data. 832 + */ 833 + __u32 count_objects; 834 + __u32 pad; 835 + 836 + /** Pointer to objects. 837 + * pointer to __u32 array of object ids 838 + */ 839 + __u64 objects_ptr; 840 + }; 841 + 842 + /** 843 + * Revoke lease 844 + */ 845 + struct drm_mode_revoke_lease { 846 + /** Unique ID of lessee 847 + */ 848 + __u32 lessee_id; 849 + }; 850 + 785 851 #if defined(__cplusplus) 786 852 } 787 853 #endif