diff options
| author | Jerome Glisse <jglisse@redhat.com> | 2009-12-08 09:33:32 -0500 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2009-12-10 00:09:02 -0500 |
| commit | ca262a9998d46196750bb19a9dc4bd465b170ff7 (patch) | |
| tree | b128691e5c57f6305c5752ac5c1b09e6aedfb650 /drivers/gpu | |
| parent | a2e68e92d384d37c8cc6bb7206d43b1eb9bc3f08 (diff) | |
drm/ttm: Rework validation & memory space allocation (V3)
This change allow driver to pass sorted memory placement,
from most prefered placement to least prefered placement.
In order to avoid long function prototype a structure is
used to gather memory placement informations such as range
restriction (if you need a buffer to be in given range).
Range restriction is determined by fpfn & lpfn which are
the first page and last page number btw which allocation
can happen. If those fields are set to 0 ttm will assume
buffer can be put anywhere in the address space (thus it
avoids putting a burden on the driver to always properly
set those fields).
This patch also factor few functions like evicting first
entry of lru list or getting a memory space. This avoid
code duplication.
V2: Change API to use placement flags and array instead
of packing placement order into a quadword.
V3: Make sure we set the appropriate mem.placement flag
when validating or allocation memory space.
[Pending Thomas Hellstrom further review but okay
from preliminary review so far].
Signed-off-by: Jerome Glisse <jglisse@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu')
| -rw-r--r-- | drivers/gpu/drm/ttm/ttm_bo.c | 463 |
1 files changed, 221 insertions, 242 deletions
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index e13fd23f3334..60d8179a8bcd 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c | |||
| @@ -27,6 +27,14 @@ | |||
| 27 | /* | 27 | /* |
| 28 | * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> | 28 | * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> |
| 29 | */ | 29 | */ |
| 30 | /* Notes: | ||
| 31 | * | ||
| 32 | * We store bo pointer in drm_mm_node struct so we know which bo own a | ||
| 33 | * specific node. There is no protection on the pointer, thus to make | ||
| 34 | * sure things don't go berserk you have to access this pointer while | ||
| 35 | * holding the global lru lock and make sure anytime you free a node you | ||
| 36 | * reset the pointer to NULL. | ||
| 37 | */ | ||
| 30 | 38 | ||
| 31 | #include "ttm/ttm_module.h" | 39 | #include "ttm/ttm_module.h" |
| 32 | #include "ttm/ttm_bo_driver.h" | 40 | #include "ttm/ttm_bo_driver.h" |
| @@ -247,7 +255,6 @@ EXPORT_SYMBOL(ttm_bo_unreserve); | |||
| 247 | /* | 255 | /* |
| 248 | * Call bo->mutex locked. | 256 | * Call bo->mutex locked. |
| 249 | */ | 257 | */ |
| 250 | |||
| 251 | static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc) | 258 | static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc) |
| 252 | { | 259 | { |
| 253 | struct ttm_bo_device *bdev = bo->bdev; | 260 | struct ttm_bo_device *bdev = bo->bdev; |
| @@ -329,14 +336,8 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, | |||
| 329 | } | 336 | } |
| 330 | 337 | ||
| 331 | if (bo->mem.mem_type == TTM_PL_SYSTEM) { | 338 | if (bo->mem.mem_type == TTM_PL_SYSTEM) { |
| 332 | 339 | bo->mem = *mem; | |
| 333 | struct ttm_mem_reg *old_mem = &bo->mem; | ||
| 334 | uint32_t save_flags = old_mem->placement; | ||
| 335 | |||
| 336 | *old_mem = *mem; | ||
| 337 | mem->mm_node = NULL; | 340 | mem->mm_node = NULL; |
| 338 | ttm_flag_masked(&save_flags, mem->placement, | ||
| 339 | TTM_PL_MASK_MEMTYPE); | ||
| 340 | goto moved; | 341 | goto moved; |
| 341 | } | 342 | } |
| 342 | 343 | ||
| @@ -419,6 +420,7 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, bool remove_all) | |||
| 419 | kref_put(&bo->list_kref, ttm_bo_ref_bug); | 420 | kref_put(&bo->list_kref, ttm_bo_ref_bug); |
| 420 | } | 421 | } |
| 421 | if (bo->mem.mm_node) { | 422 | if (bo->mem.mm_node) { |
| 423 | bo->mem.mm_node->private = NULL; | ||
| 422 | drm_mm_put_block(bo->mem.mm_node); | 424 | drm_mm_put_block(bo->mem.mm_node); |
| 423 | bo->mem.mm_node = NULL; | 425 | bo->mem.mm_node = NULL; |
| 424 | } | 426 | } |
| @@ -555,17 +557,14 @@ void ttm_bo_unref(struct ttm_buffer_object **p_bo) | |||
| 555 | } | 557 | } |
| 556 | EXPORT_SYMBOL(ttm_bo_unref); | 558 | EXPORT_SYMBOL(ttm_bo_unref); |
| 557 | 559 | ||
| 558 | static int ttm_bo_evict(struct ttm_buffer_object *bo, unsigned mem_type, | 560 | static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible, |
| 559 | bool interruptible, bool no_wait) | 561 | bool no_wait) |
| 560 | { | 562 | { |
| 561 | int ret = 0; | ||
| 562 | struct ttm_bo_device *bdev = bo->bdev; | 563 | struct ttm_bo_device *bdev = bo->bdev; |
| 563 | struct ttm_bo_global *glob = bo->glob; | 564 | struct ttm_bo_global *glob = bo->glob; |
| 564 | struct ttm_mem_reg evict_mem; | 565 | struct ttm_mem_reg evict_mem; |
| 565 | uint32_t proposed_placement; | 566 | struct ttm_placement placement; |
| 566 | 567 | int ret = 0; | |
| 567 | if (bo->mem.mem_type != mem_type) | ||
| 568 | goto out; | ||
| 569 | 568 | ||
| 570 | spin_lock(&bo->lock); | 569 | spin_lock(&bo->lock); |
| 571 | ret = ttm_bo_wait(bo, false, interruptible, no_wait); | 570 | ret = ttm_bo_wait(bo, false, interruptible, no_wait); |
| @@ -585,14 +584,9 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, unsigned mem_type, | |||
| 585 | evict_mem = bo->mem; | 584 | evict_mem = bo->mem; |
| 586 | evict_mem.mm_node = NULL; | 585 | evict_mem.mm_node = NULL; |
| 587 | 586 | ||
| 588 | proposed_placement = bdev->driver->evict_flags(bo); | 587 | bdev->driver->evict_flags(bo, &placement); |
| 589 | 588 | ret = ttm_bo_mem_space(bo, &placement, &evict_mem, interruptible, | |
| 590 | ret = ttm_bo_mem_space(bo, proposed_placement, | 589 | no_wait); |
| 591 | &evict_mem, interruptible, no_wait); | ||
| 592 | if (unlikely(ret != 0 && ret != -ERESTART)) | ||
| 593 | ret = ttm_bo_mem_space(bo, TTM_PL_FLAG_SYSTEM, | ||
| 594 | &evict_mem, interruptible, no_wait); | ||
| 595 | |||
| 596 | if (ret) { | 590 | if (ret) { |
| 597 | if (ret != -ERESTART) | 591 | if (ret != -ERESTART) |
| 598 | printk(KERN_ERR TTM_PFX | 592 | printk(KERN_ERR TTM_PFX |
| @@ -606,95 +600,117 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, unsigned mem_type, | |||
| 606 | if (ret) { | 600 | if (ret) { |
| 607 | if (ret != -ERESTART) | 601 | if (ret != -ERESTART) |
| 608 | printk(KERN_ERR TTM_PFX "Buffer eviction failed\n"); | 602 | printk(KERN_ERR TTM_PFX "Buffer eviction failed\n"); |
| 603 | spin_lock(&glob->lru_lock); | ||
| 604 | if (evict_mem.mm_node) { | ||
| 605 | evict_mem.mm_node->private = NULL; | ||
| 606 | drm_mm_put_block(evict_mem.mm_node); | ||
| 607 | evict_mem.mm_node = NULL; | ||
| 608 | } | ||
| 609 | spin_unlock(&glob->lru_lock); | ||
| 609 | goto out; | 610 | goto out; |
| 610 | } | 611 | } |
| 612 | bo->evicted = true; | ||
| 613 | out: | ||
| 614 | return ret; | ||
| 615 | } | ||
| 616 | |||
| 617 | static int ttm_mem_evict_first(struct ttm_bo_device *bdev, | ||
| 618 | uint32_t mem_type, | ||
| 619 | bool interruptible, bool no_wait) | ||
| 620 | { | ||
| 621 | struct ttm_bo_global *glob = bdev->glob; | ||
| 622 | struct ttm_mem_type_manager *man = &bdev->man[mem_type]; | ||
| 623 | struct ttm_buffer_object *bo; | ||
| 624 | int ret, put_count = 0; | ||
| 611 | 625 | ||
| 612 | spin_lock(&glob->lru_lock); | 626 | spin_lock(&glob->lru_lock); |
| 613 | if (evict_mem.mm_node) { | 627 | bo = list_first_entry(&man->lru, struct ttm_buffer_object, lru); |
| 614 | drm_mm_put_block(evict_mem.mm_node); | 628 | kref_get(&bo->list_kref); |
| 615 | evict_mem.mm_node = NULL; | 629 | ret = ttm_bo_reserve_locked(bo, interruptible, no_wait, false, 0); |
| 616 | } | 630 | if (likely(ret == 0)) |
| 631 | put_count = ttm_bo_del_from_lru(bo); | ||
| 617 | spin_unlock(&glob->lru_lock); | 632 | spin_unlock(&glob->lru_lock); |
| 618 | bo->evicted = true; | 633 | if (unlikely(ret != 0)) |
| 619 | out: | 634 | return ret; |
| 635 | while (put_count--) | ||
| 636 | kref_put(&bo->list_kref, ttm_bo_ref_bug); | ||
| 637 | ret = ttm_bo_evict(bo, interruptible, no_wait); | ||
| 638 | ttm_bo_unreserve(bo); | ||
| 639 | kref_put(&bo->list_kref, ttm_bo_release_list); | ||
| 620 | return ret; | 640 | return ret; |
| 621 | } | 641 | } |
| 622 | 642 | ||
| 643 | static int ttm_bo_man_get_node(struct ttm_buffer_object *bo, | ||
| 644 | struct ttm_mem_type_manager *man, | ||
| 645 | struct ttm_placement *placement, | ||
| 646 | struct ttm_mem_reg *mem, | ||
| 647 | struct drm_mm_node **node) | ||
| 648 | { | ||
| 649 | struct ttm_bo_global *glob = bo->glob; | ||
| 650 | unsigned long lpfn; | ||
| 651 | int ret; | ||
| 652 | |||
| 653 | lpfn = placement->lpfn; | ||
| 654 | if (!lpfn) | ||
| 655 | lpfn = man->size; | ||
| 656 | *node = NULL; | ||
| 657 | do { | ||
| 658 | ret = drm_mm_pre_get(&man->manager); | ||
| 659 | if (unlikely(ret)) | ||
| 660 | return ret; | ||
| 661 | |||
| 662 | spin_lock(&glob->lru_lock); | ||
| 663 | *node = drm_mm_search_free_in_range(&man->manager, | ||
| 664 | mem->num_pages, mem->page_alignment, | ||
| 665 | placement->fpfn, lpfn, 1); | ||
| 666 | if (unlikely(*node == NULL)) { | ||
| 667 | spin_unlock(&glob->lru_lock); | ||
| 668 | return 0; | ||
| 669 | } | ||
| 670 | *node = drm_mm_get_block_atomic_range(*node, mem->num_pages, | ||
| 671 | mem->page_alignment, | ||
| 672 | placement->fpfn, | ||
| 673 | lpfn); | ||
| 674 | spin_unlock(&glob->lru_lock); | ||
| 675 | } while (*node == NULL); | ||
| 676 | return 0; | ||
| 677 | } | ||
| 678 | |||
| 623 | /** | 679 | /** |
| 624 | * Repeatedly evict memory from the LRU for @mem_type until we create enough | 680 | * Repeatedly evict memory from the LRU for @mem_type until we create enough |
| 625 | * space, or we've evicted everything and there isn't enough space. | 681 | * space, or we've evicted everything and there isn't enough space. |
| 626 | */ | 682 | */ |
| 627 | static int ttm_bo_mem_force_space(struct ttm_bo_device *bdev, | 683 | static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, |
| 628 | struct ttm_mem_reg *mem, | 684 | uint32_t mem_type, |
| 629 | uint32_t mem_type, | 685 | struct ttm_placement *placement, |
| 630 | bool interruptible, bool no_wait) | 686 | struct ttm_mem_reg *mem, |
| 687 | bool interruptible, bool no_wait) | ||
| 631 | { | 688 | { |
| 689 | struct ttm_bo_device *bdev = bo->bdev; | ||
| 632 | struct ttm_bo_global *glob = bdev->glob; | 690 | struct ttm_bo_global *glob = bdev->glob; |
| 633 | struct drm_mm_node *node; | ||
| 634 | struct ttm_buffer_object *entry; | ||
| 635 | struct ttm_mem_type_manager *man = &bdev->man[mem_type]; | 691 | struct ttm_mem_type_manager *man = &bdev->man[mem_type]; |
| 636 | struct list_head *lru; | 692 | struct drm_mm_node *node; |
| 637 | unsigned long num_pages = mem->num_pages; | ||
| 638 | int put_count = 0; | ||
| 639 | int ret; | 693 | int ret; |
| 640 | 694 | ||
| 641 | retry_pre_get: | ||
| 642 | ret = drm_mm_pre_get(&man->manager); | ||
| 643 | if (unlikely(ret != 0)) | ||
| 644 | return ret; | ||
| 645 | |||
| 646 | spin_lock(&glob->lru_lock); | ||
| 647 | do { | 695 | do { |
| 648 | node = drm_mm_search_free(&man->manager, num_pages, | 696 | ret = ttm_bo_man_get_node(bo, man, placement, mem, &node); |
| 649 | mem->page_alignment, 1); | 697 | if (unlikely(ret != 0)) |
| 698 | return ret; | ||
| 650 | if (node) | 699 | if (node) |
| 651 | break; | 700 | break; |
| 652 | 701 | spin_lock(&glob->lru_lock); | |
| 653 | lru = &man->lru; | 702 | if (list_empty(&man->lru)) { |
| 654 | if (list_empty(lru)) | 703 | spin_unlock(&glob->lru_lock); |
| 655 | break; | 704 | break; |
| 656 | 705 | } | |
| 657 | entry = list_first_entry(lru, struct ttm_buffer_object, lru); | ||
| 658 | kref_get(&entry->list_kref); | ||
| 659 | |||
| 660 | ret = | ||
| 661 | ttm_bo_reserve_locked(entry, interruptible, no_wait, | ||
| 662 | false, 0); | ||
| 663 | |||
| 664 | if (likely(ret == 0)) | ||
| 665 | put_count = ttm_bo_del_from_lru(entry); | ||
| 666 | |||
| 667 | spin_unlock(&glob->lru_lock); | 706 | spin_unlock(&glob->lru_lock); |
| 668 | 707 | ret = ttm_mem_evict_first(bdev, mem_type, interruptible, | |
| 708 | no_wait); | ||
| 669 | if (unlikely(ret != 0)) | 709 | if (unlikely(ret != 0)) |
| 670 | return ret; | 710 | return ret; |
| 671 | |||
| 672 | while (put_count--) | ||
| 673 | kref_put(&entry->list_kref, ttm_bo_ref_bug); | ||
| 674 | |||
| 675 | ret = ttm_bo_evict(entry, mem_type, interruptible, no_wait); | ||
| 676 | |||
| 677 | ttm_bo_unreserve(entry); | ||
| 678 | |||
| 679 | kref_put(&entry->list_kref, ttm_bo_release_list); | ||
| 680 | if (ret) | ||
| 681 | return ret; | ||
| 682 | |||
| 683 | spin_lock(&glob->lru_lock); | ||
| 684 | } while (1); | 711 | } while (1); |
| 685 | 712 | if (node == NULL) | |
| 686 | if (!node) { | ||
| 687 | spin_unlock(&glob->lru_lock); | ||
| 688 | return -ENOMEM; | 713 | return -ENOMEM; |
| 689 | } | ||
| 690 | |||
| 691 | node = drm_mm_get_block_atomic(node, num_pages, mem->page_alignment); | ||
| 692 | if (unlikely(!node)) { | ||
| 693 | spin_unlock(&glob->lru_lock); | ||
| 694 | goto retry_pre_get; | ||
| 695 | } | ||
| 696 | |||
| 697 | spin_unlock(&glob->lru_lock); | ||
| 698 | mem->mm_node = node; | 714 | mem->mm_node = node; |
| 699 | mem->mem_type = mem_type; | 715 | mem->mem_type = mem_type; |
| 700 | return 0; | 716 | return 0; |
| @@ -725,7 +741,6 @@ static uint32_t ttm_bo_select_caching(struct ttm_mem_type_manager *man, | |||
| 725 | return result; | 741 | return result; |
| 726 | } | 742 | } |
| 727 | 743 | ||
| 728 | |||
| 729 | static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man, | 744 | static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man, |
| 730 | bool disallow_fixed, | 745 | bool disallow_fixed, |
| 731 | uint32_t mem_type, | 746 | uint32_t mem_type, |
| @@ -749,6 +764,18 @@ static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man, | |||
| 749 | return true; | 764 | return true; |
| 750 | } | 765 | } |
| 751 | 766 | ||
| 767 | static inline int ttm_mem_type_from_flags(uint32_t flags, uint32_t *mem_type) | ||
| 768 | { | ||
| 769 | int i; | ||
| 770 | |||
| 771 | for (i = 0; i <= TTM_PL_PRIV5; i++) | ||
| 772 | if (flags & (1 << i)) { | ||
| 773 | *mem_type = i; | ||
| 774 | return 0; | ||
| 775 | } | ||
| 776 | return -EINVAL; | ||
| 777 | } | ||
| 778 | |||
| 752 | /** | 779 | /** |
| 753 | * Creates space for memory region @mem according to its type. | 780 | * Creates space for memory region @mem according to its type. |
| 754 | * | 781 | * |
| @@ -758,66 +785,55 @@ static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man, | |||
| 758 | * space. | 785 | * space. |
| 759 | */ | 786 | */ |
| 760 | int ttm_bo_mem_space(struct ttm_buffer_object *bo, | 787 | int ttm_bo_mem_space(struct ttm_buffer_object *bo, |
| 761 | uint32_t proposed_placement, | 788 | struct ttm_placement *placement, |
| 762 | struct ttm_mem_reg *mem, | 789 | struct ttm_mem_reg *mem, |
| 763 | bool interruptible, bool no_wait) | 790 | bool interruptible, bool no_wait) |
| 764 | { | 791 | { |
| 765 | struct ttm_bo_device *bdev = bo->bdev; | 792 | struct ttm_bo_device *bdev = bo->bdev; |
| 766 | struct ttm_bo_global *glob = bo->glob; | ||
| 767 | struct ttm_mem_type_manager *man; | 793 | struct ttm_mem_type_manager *man; |
| 768 | |||
| 769 | uint32_t num_prios = bdev->driver->num_mem_type_prio; | ||
| 770 | const uint32_t *prios = bdev->driver->mem_type_prio; | ||
| 771 | uint32_t i; | ||
| 772 | uint32_t mem_type = TTM_PL_SYSTEM; | 794 | uint32_t mem_type = TTM_PL_SYSTEM; |
| 773 | uint32_t cur_flags = 0; | 795 | uint32_t cur_flags = 0; |
| 774 | bool type_found = false; | 796 | bool type_found = false; |
| 775 | bool type_ok = false; | 797 | bool type_ok = false; |
| 776 | bool has_eagain = false; | 798 | bool has_eagain = false; |
| 777 | struct drm_mm_node *node = NULL; | 799 | struct drm_mm_node *node = NULL; |
| 778 | int ret; | 800 | int i, ret; |
| 779 | 801 | ||
| 780 | mem->mm_node = NULL; | 802 | mem->mm_node = NULL; |
| 781 | for (i = 0; i < num_prios; ++i) { | 803 | for (i = 0; i <= placement->num_placement; ++i) { |
| 782 | mem_type = prios[i]; | 804 | ret = ttm_mem_type_from_flags(placement->placement[i], |
| 805 | &mem_type); | ||
| 806 | if (ret) | ||
| 807 | return ret; | ||
| 783 | man = &bdev->man[mem_type]; | 808 | man = &bdev->man[mem_type]; |
| 784 | 809 | ||
| 785 | type_ok = ttm_bo_mt_compatible(man, | 810 | type_ok = ttm_bo_mt_compatible(man, |
| 786 | bo->type == ttm_bo_type_user, | 811 | bo->type == ttm_bo_type_user, |
| 787 | mem_type, proposed_placement, | 812 | mem_type, |
| 788 | &cur_flags); | 813 | placement->placement[i], |
| 814 | &cur_flags); | ||
| 789 | 815 | ||
| 790 | if (!type_ok) | 816 | if (!type_ok) |
| 791 | continue; | 817 | continue; |
| 792 | 818 | ||
| 793 | cur_flags = ttm_bo_select_caching(man, bo->mem.placement, | 819 | cur_flags = ttm_bo_select_caching(man, bo->mem.placement, |
| 794 | cur_flags); | 820 | cur_flags); |
| 821 | /* | ||
| 822 | * Use the access and other non-mapping-related flag bits from | ||
| 823 | * the memory placement flags to the current flags | ||
| 824 | */ | ||
| 825 | ttm_flag_masked(&cur_flags, placement->placement[i], | ||
| 826 | ~TTM_PL_MASK_MEMTYPE); | ||
| 795 | 827 | ||
| 796 | if (mem_type == TTM_PL_SYSTEM) | 828 | if (mem_type == TTM_PL_SYSTEM) |
| 797 | break; | 829 | break; |
| 798 | 830 | ||
| 799 | if (man->has_type && man->use_type) { | 831 | if (man->has_type && man->use_type) { |
| 800 | type_found = true; | 832 | type_found = true; |
| 801 | do { | 833 | ret = ttm_bo_man_get_node(bo, man, placement, mem, |
| 802 | ret = drm_mm_pre_get(&man->manager); | 834 | &node); |
| 803 | if (unlikely(ret)) | 835 | if (unlikely(ret)) |
| 804 | return ret; | 836 | return ret; |
| 805 | |||
| 806 | spin_lock(&glob->lru_lock); | ||
| 807 | node = drm_mm_search_free(&man->manager, | ||
| 808 | mem->num_pages, | ||
| 809 | mem->page_alignment, | ||
| 810 | 1); | ||
| 811 | if (unlikely(!node)) { | ||
| 812 | spin_unlock(&glob->lru_lock); | ||
| 813 | break; | ||
| 814 | } | ||
| 815 | node = drm_mm_get_block_atomic(node, | ||
| 816 | mem->num_pages, | ||
| 817 | mem-> | ||
| 818 | page_alignment); | ||
| 819 | spin_unlock(&glob->lru_lock); | ||
| 820 | } while (!node); | ||
| 821 | } | 837 | } |
| 822 | if (node) | 838 | if (node) |
| 823 | break; | 839 | break; |
| @@ -827,43 +843,48 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, | |||
| 827 | mem->mm_node = node; | 843 | mem->mm_node = node; |
| 828 | mem->mem_type = mem_type; | 844 | mem->mem_type = mem_type; |
| 829 | mem->placement = cur_flags; | 845 | mem->placement = cur_flags; |
| 846 | if (node) | ||
| 847 | node->private = bo; | ||
| 830 | return 0; | 848 | return 0; |
| 831 | } | 849 | } |
| 832 | 850 | ||
| 833 | if (!type_found) | 851 | if (!type_found) |
| 834 | return -EINVAL; | 852 | return -EINVAL; |
| 835 | 853 | ||
| 836 | num_prios = bdev->driver->num_mem_busy_prio; | 854 | for (i = 0; i <= placement->num_busy_placement; ++i) { |
| 837 | prios = bdev->driver->mem_busy_prio; | 855 | ret = ttm_mem_type_from_flags(placement->placement[i], |
| 838 | 856 | &mem_type); | |
| 839 | for (i = 0; i < num_prios; ++i) { | 857 | if (ret) |
| 840 | mem_type = prios[i]; | 858 | return ret; |
| 841 | man = &bdev->man[mem_type]; | 859 | man = &bdev->man[mem_type]; |
| 842 | |||
| 843 | if (!man->has_type) | 860 | if (!man->has_type) |
| 844 | continue; | 861 | continue; |
| 845 | |||
| 846 | if (!ttm_bo_mt_compatible(man, | 862 | if (!ttm_bo_mt_compatible(man, |
| 847 | bo->type == ttm_bo_type_user, | 863 | bo->type == ttm_bo_type_user, |
| 848 | mem_type, | 864 | mem_type, |
| 849 | proposed_placement, &cur_flags)) | 865 | placement->placement[i], |
| 866 | &cur_flags)) | ||
| 850 | continue; | 867 | continue; |
| 851 | 868 | ||
| 852 | cur_flags = ttm_bo_select_caching(man, bo->mem.placement, | 869 | cur_flags = ttm_bo_select_caching(man, bo->mem.placement, |
| 853 | cur_flags); | 870 | cur_flags); |
| 871 | /* | ||
| 872 | * Use the access and other non-mapping-related flag bits from | ||
| 873 | * the memory placement flags to the current flags | ||
| 874 | */ | ||
| 875 | ttm_flag_masked(&cur_flags, placement->placement[i], | ||
| 876 | ~TTM_PL_MASK_MEMTYPE); | ||
| 854 | 877 | ||
| 855 | ret = ttm_bo_mem_force_space(bdev, mem, mem_type, | 878 | ret = ttm_bo_mem_force_space(bo, mem_type, placement, mem, |
| 856 | interruptible, no_wait); | 879 | interruptible, no_wait); |
| 857 | |||
| 858 | if (ret == 0 && mem->mm_node) { | 880 | if (ret == 0 && mem->mm_node) { |
| 859 | mem->placement = cur_flags; | 881 | mem->placement = cur_flags; |
| 882 | mem->mm_node->private = bo; | ||
| 860 | return 0; | 883 | return 0; |
| 861 | } | 884 | } |
| 862 | |||
| 863 | if (ret == -ERESTART) | 885 | if (ret == -ERESTART) |
| 864 | has_eagain = true; | 886 | has_eagain = true; |
| 865 | } | 887 | } |
| 866 | |||
| 867 | ret = (has_eagain) ? -ERESTART : -ENOMEM; | 888 | ret = (has_eagain) ? -ERESTART : -ENOMEM; |
| 868 | return ret; | 889 | return ret; |
| 869 | } | 890 | } |
| @@ -886,8 +907,8 @@ int ttm_bo_wait_cpu(struct ttm_buffer_object *bo, bool no_wait) | |||
| 886 | } | 907 | } |
| 887 | 908 | ||
| 888 | int ttm_bo_move_buffer(struct ttm_buffer_object *bo, | 909 | int ttm_bo_move_buffer(struct ttm_buffer_object *bo, |
| 889 | uint32_t proposed_placement, | 910 | struct ttm_placement *placement, |
| 890 | bool interruptible, bool no_wait) | 911 | bool interruptible, bool no_wait) |
| 891 | { | 912 | { |
| 892 | struct ttm_bo_global *glob = bo->glob; | 913 | struct ttm_bo_global *glob = bo->glob; |
| 893 | int ret = 0; | 914 | int ret = 0; |
| @@ -900,101 +921,82 @@ int ttm_bo_move_buffer(struct ttm_buffer_object *bo, | |||
| 900 | * Have the driver move function wait for idle when necessary, | 921 | * Have the driver move function wait for idle when necessary, |
| 901 | * instead of doing it here. | 922 | * instead of doing it here. |
| 902 | */ | 923 | */ |
| 903 | |||
| 904 | spin_lock(&bo->lock); | 924 | spin_lock(&bo->lock); |
| 905 | ret = ttm_bo_wait(bo, false, interruptible, no_wait); | 925 | ret = ttm_bo_wait(bo, false, interruptible, no_wait); |
| 906 | spin_unlock(&bo->lock); | 926 | spin_unlock(&bo->lock); |
| 907 | |||
| 908 | if (ret) | 927 | if (ret) |
| 909 | return ret; | 928 | return ret; |
| 910 | |||
| 911 | mem.num_pages = bo->num_pages; | 929 | mem.num_pages = bo->num_pages; |
| 912 | mem.size = mem.num_pages << PAGE_SHIFT; | 930 | mem.size = mem.num_pages << PAGE_SHIFT; |
| 913 | mem.page_alignment = bo->mem.page_alignment; | 931 | mem.page_alignment = bo->mem.page_alignment; |
| 914 | |||
| 915 | /* | 932 | /* |
| 916 | * Determine where to move the buffer. | 933 | * Determine where to move the buffer. |
| 917 | */ | 934 | */ |
| 918 | 935 | ret = ttm_bo_mem_space(bo, placement, &mem, interruptible, no_wait); | |
| 919 | ret = ttm_bo_mem_space(bo, proposed_placement, &mem, | ||
| 920 | interruptible, no_wait); | ||
| 921 | if (ret) | 936 | if (ret) |
| 922 | goto out_unlock; | 937 | goto out_unlock; |
| 923 | |||
| 924 | ret = ttm_bo_handle_move_mem(bo, &mem, false, interruptible, no_wait); | 938 | ret = ttm_bo_handle_move_mem(bo, &mem, false, interruptible, no_wait); |
| 925 | |||
| 926 | out_unlock: | 939 | out_unlock: |
| 927 | if (ret && mem.mm_node) { | 940 | if (ret && mem.mm_node) { |
| 928 | spin_lock(&glob->lru_lock); | 941 | spin_lock(&glob->lru_lock); |
| 942 | mem.mm_node->private = NULL; | ||
| 929 | drm_mm_put_block(mem.mm_node); | 943 | drm_mm_put_block(mem.mm_node); |
| 930 | spin_unlock(&glob->lru_lock); | 944 | spin_unlock(&glob->lru_lock); |
| 931 | } | 945 | } |
| 932 | return ret; | 946 | return ret; |
| 933 | } | 947 | } |
| 934 | 948 | ||
| 935 | static int ttm_bo_mem_compat(uint32_t proposed_placement, | 949 | static int ttm_bo_mem_compat(struct ttm_placement *placement, |
| 936 | struct ttm_mem_reg *mem) | 950 | struct ttm_mem_reg *mem) |
| 937 | { | 951 | { |
| 938 | if ((proposed_placement & mem->placement & TTM_PL_MASK_MEM) == 0) | 952 | int i; |
| 939 | return 0; | 953 | |
| 940 | if ((proposed_placement & mem->placement & TTM_PL_MASK_CACHING) == 0) | 954 | for (i = 0; i < placement->num_placement; i++) { |
| 941 | return 0; | 955 | if ((placement->placement[i] & mem->placement & |
| 942 | 956 | TTM_PL_MASK_CACHING) && | |
| 943 | return 1; | 957 | (placement->placement[i] & mem->placement & |
| 958 | TTM_PL_MASK_MEM)) | ||
| 959 | return i; | ||
| 960 | } | ||
| 961 | return -1; | ||
| 944 | } | 962 | } |
| 945 | 963 | ||
| 946 | int ttm_buffer_object_validate(struct ttm_buffer_object *bo, | 964 | int ttm_buffer_object_validate(struct ttm_buffer_object *bo, |
| 947 | uint32_t proposed_placement, | 965 | struct ttm_placement *placement, |
| 948 | bool interruptible, bool no_wait) | 966 | bool interruptible, bool no_wait) |
| 949 | { | 967 | { |
| 950 | int ret; | 968 | int ret; |
| 951 | 969 | ||
| 952 | BUG_ON(!atomic_read(&bo->reserved)); | 970 | BUG_ON(!atomic_read(&bo->reserved)); |
| 953 | bo->proposed_placement = proposed_placement; | 971 | /* Check that range is valid */ |
| 954 | 972 | if (placement->lpfn || placement->fpfn) | |
| 955 | TTM_DEBUG("Proposed placement 0x%08lx, Old flags 0x%08lx\n", | 973 | if (placement->fpfn > placement->lpfn || |
| 956 | (unsigned long)proposed_placement, | 974 | (placement->lpfn - placement->fpfn) < bo->num_pages) |
| 957 | (unsigned long)bo->mem.placement); | 975 | return -EINVAL; |
| 958 | |||
| 959 | /* | 976 | /* |
| 960 | * Check whether we need to move buffer. | 977 | * Check whether we need to move buffer. |
| 961 | */ | 978 | */ |
| 962 | 979 | ret = ttm_bo_mem_compat(placement, &bo->mem); | |
| 963 | if (!ttm_bo_mem_compat(bo->proposed_placement, &bo->mem)) { | 980 | if (ret < 0) { |
| 964 | ret = ttm_bo_move_buffer(bo, bo->proposed_placement, | 981 | ret = ttm_bo_move_buffer(bo, placement, interruptible, no_wait); |
| 965 | interruptible, no_wait); | 982 | if (ret) |
| 966 | if (ret) { | ||
| 967 | if (ret != -ERESTART) | ||
| 968 | printk(KERN_ERR TTM_PFX | ||
| 969 | "Failed moving buffer. " | ||
| 970 | "Proposed placement 0x%08x\n", | ||
| 971 | bo->proposed_placement); | ||
| 972 | if (ret == -ENOMEM) | ||
| 973 | printk(KERN_ERR TTM_PFX | ||
| 974 | "Out of aperture space or " | ||
| 975 | "DRM memory quota.\n"); | ||
| 976 | return ret; | 983 | return ret; |
| 977 | } | 984 | } else { |
| 985 | /* | ||
| 986 | * Use the access and other non-mapping-related flag bits from | ||
| 987 | * the compatible memory placement flags to the active flags | ||
| 988 | */ | ||
| 989 | ttm_flag_masked(&bo->mem.placement, placement->placement[ret], | ||
| 990 | ~TTM_PL_MASK_MEMTYPE); | ||
| 978 | } | 991 | } |
| 979 | |||
| 980 | /* | 992 | /* |
| 981 | * We might need to add a TTM. | 993 | * We might need to add a TTM. |
| 982 | */ | 994 | */ |
| 983 | |||
| 984 | if (bo->mem.mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) { | 995 | if (bo->mem.mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) { |
| 985 | ret = ttm_bo_add_ttm(bo, true); | 996 | ret = ttm_bo_add_ttm(bo, true); |
| 986 | if (ret) | 997 | if (ret) |
| 987 | return ret; | 998 | return ret; |
| 988 | } | 999 | } |
| 989 | /* | ||
| 990 | * Validation has succeeded, move the access and other | ||
| 991 | * non-mapping-related flag bits from the proposed flags to | ||
| 992 | * the active flags | ||
| 993 | */ | ||
| 994 | |||
| 995 | ttm_flag_masked(&bo->mem.placement, bo->proposed_placement, | ||
| 996 | ~TTM_PL_MASK_MEMTYPE); | ||
| 997 | |||
| 998 | return 0; | 1000 | return 0; |
| 999 | } | 1001 | } |
| 1000 | EXPORT_SYMBOL(ttm_buffer_object_validate); | 1002 | EXPORT_SYMBOL(ttm_buffer_object_validate); |
| @@ -1042,8 +1044,10 @@ int ttm_buffer_object_init(struct ttm_bo_device *bdev, | |||
| 1042 | size_t acc_size, | 1044 | size_t acc_size, |
| 1043 | void (*destroy) (struct ttm_buffer_object *)) | 1045 | void (*destroy) (struct ttm_buffer_object *)) |
| 1044 | { | 1046 | { |
| 1045 | int ret = 0; | 1047 | int i, c, ret = 0; |
| 1046 | unsigned long num_pages; | 1048 | unsigned long num_pages; |
| 1049 | uint32_t placements[8]; | ||
| 1050 | struct ttm_placement placement; | ||
| 1047 | 1051 | ||
| 1048 | size += buffer_start & ~PAGE_MASK; | 1052 | size += buffer_start & ~PAGE_MASK; |
| 1049 | num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; | 1053 | num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; |
| @@ -1100,7 +1104,16 @@ int ttm_buffer_object_init(struct ttm_bo_device *bdev, | |||
| 1100 | goto out_err; | 1104 | goto out_err; |
| 1101 | } | 1105 | } |
| 1102 | 1106 | ||
| 1103 | ret = ttm_buffer_object_validate(bo, flags, interruptible, false); | 1107 | placement.fpfn = 0; |
| 1108 | placement.lpfn = 0; | ||
| 1109 | for (i = 0, c = 0; i <= TTM_PL_PRIV5; i++) | ||
| 1110 | if (flags & (1 << i)) | ||
| 1111 | placements[c++] = (flags & ~TTM_PL_MASK_MEM) | (1 << i); | ||
| 1112 | placement.placement = placements; | ||
| 1113 | placement.num_placement = c; | ||
| 1114 | placement.busy_placement = placements; | ||
| 1115 | placement.num_busy_placement = c; | ||
| 1116 | ret = ttm_buffer_object_validate(bo, &placement, interruptible, false); | ||
| 1104 | if (ret) | 1117 | if (ret) |
| 1105 | goto out_err; | 1118 | goto out_err; |
| 1106 | 1119 | ||
| @@ -1135,8 +1148,8 @@ int ttm_buffer_object_create(struct ttm_bo_device *bdev, | |||
| 1135 | struct ttm_buffer_object **p_bo) | 1148 | struct ttm_buffer_object **p_bo) |
| 1136 | { | 1149 | { |
| 1137 | struct ttm_buffer_object *bo; | 1150 | struct ttm_buffer_object *bo; |
| 1138 | int ret; | ||
| 1139 | struct ttm_mem_global *mem_glob = bdev->glob->mem_glob; | 1151 | struct ttm_mem_global *mem_glob = bdev->glob->mem_glob; |
| 1152 | int ret; | ||
| 1140 | 1153 | ||
| 1141 | size_t acc_size = | 1154 | size_t acc_size = |
| 1142 | ttm_bo_size(bdev->glob, (size + PAGE_SIZE - 1) >> PAGE_SHIFT); | 1155 | ttm_bo_size(bdev->glob, (size + PAGE_SIZE - 1) >> PAGE_SHIFT); |
| @@ -1161,66 +1174,32 @@ int ttm_buffer_object_create(struct ttm_bo_device *bdev, | |||
| 1161 | return ret; | 1174 | return ret; |
| 1162 | } | 1175 | } |
| 1163 | 1176 | ||
| 1164 | static int ttm_bo_leave_list(struct ttm_buffer_object *bo, | ||
| 1165 | uint32_t mem_type, bool allow_errors) | ||
| 1166 | { | ||
| 1167 | int ret; | ||
| 1168 | |||
| 1169 | spin_lock(&bo->lock); | ||
| 1170 | ret = ttm_bo_wait(bo, false, false, false); | ||
| 1171 | spin_unlock(&bo->lock); | ||
| 1172 | |||
| 1173 | if (ret && allow_errors) | ||
| 1174 | goto out; | ||
| 1175 | |||
| 1176 | if (bo->mem.mem_type == mem_type) | ||
| 1177 | ret = ttm_bo_evict(bo, mem_type, false, false); | ||
| 1178 | |||
| 1179 | if (ret) { | ||
| 1180 | if (allow_errors) { | ||
| 1181 | goto out; | ||
| 1182 | } else { | ||
| 1183 | ret = 0; | ||
| 1184 | printk(KERN_ERR TTM_PFX "Cleanup eviction failed\n"); | ||
| 1185 | } | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | out: | ||
| 1189 | return ret; | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, | 1177 | static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, |
| 1193 | struct list_head *head, | 1178 | unsigned mem_type, bool allow_errors) |
| 1194 | unsigned mem_type, bool allow_errors) | ||
| 1195 | { | 1179 | { |
| 1180 | struct ttm_mem_type_manager *man = &bdev->man[mem_type]; | ||
| 1196 | struct ttm_bo_global *glob = bdev->glob; | 1181 | struct ttm_bo_global *glob = bdev->glob; |
| 1197 | struct ttm_buffer_object *entry; | ||
| 1198 | int ret; | 1182 | int ret; |
| 1199 | int put_count; | ||
| 1200 | 1183 | ||
| 1201 | /* | 1184 | /* |
| 1202 | * Can't use standard list traversal since we're unlocking. | 1185 | * Can't use standard list traversal since we're unlocking. |
| 1203 | */ | 1186 | */ |
| 1204 | 1187 | ||
| 1205 | spin_lock(&glob->lru_lock); | 1188 | spin_lock(&glob->lru_lock); |
| 1206 | 1189 | while (!list_empty(&man->lru)) { | |
| 1207 | while (!list_empty(head)) { | ||
| 1208 | entry = list_first_entry(head, struct ttm_buffer_object, lru); | ||
| 1209 | kref_get(&entry->list_kref); | ||
| 1210 | ret = ttm_bo_reserve_locked(entry, false, false, false, 0); | ||
| 1211 | put_count = ttm_bo_del_from_lru(entry); | ||
| 1212 | spin_unlock(&glob->lru_lock); | 1190 | spin_unlock(&glob->lru_lock); |
| 1213 | while (put_count--) | 1191 | ret = ttm_mem_evict_first(bdev, mem_type, false, false); |
| 1214 | kref_put(&entry->list_kref, ttm_bo_ref_bug); | 1192 | if (ret) { |
| 1215 | BUG_ON(ret); | 1193 | if (allow_errors) { |
| 1216 | ret = ttm_bo_leave_list(entry, mem_type, allow_errors); | 1194 | return ret; |
| 1217 | ttm_bo_unreserve(entry); | 1195 | } else { |
| 1218 | kref_put(&entry->list_kref, ttm_bo_release_list); | 1196 | printk(KERN_ERR TTM_PFX |
| 1197 | "Cleanup eviction failed\n"); | ||
| 1198 | } | ||
| 1199 | } | ||
| 1219 | spin_lock(&glob->lru_lock); | 1200 | spin_lock(&glob->lru_lock); |
| 1220 | } | 1201 | } |
| 1221 | |||
| 1222 | spin_unlock(&glob->lru_lock); | 1202 | spin_unlock(&glob->lru_lock); |
| 1223 | |||
| 1224 | return 0; | 1203 | return 0; |
| 1225 | } | 1204 | } |
| 1226 | 1205 | ||
| @@ -1247,7 +1226,7 @@ int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type) | |||
| 1247 | 1226 | ||
| 1248 | ret = 0; | 1227 | ret = 0; |
| 1249 | if (mem_type > 0) { | 1228 | if (mem_type > 0) { |
| 1250 | ttm_bo_force_list_clean(bdev, &man->lru, mem_type, false); | 1229 | ttm_bo_force_list_clean(bdev, mem_type, false); |
| 1251 | 1230 | ||
| 1252 | spin_lock(&glob->lru_lock); | 1231 | spin_lock(&glob->lru_lock); |
| 1253 | if (drm_mm_clean(&man->manager)) | 1232 | if (drm_mm_clean(&man->manager)) |
| @@ -1280,12 +1259,12 @@ int ttm_bo_evict_mm(struct ttm_bo_device *bdev, unsigned mem_type) | |||
| 1280 | return 0; | 1259 | return 0; |
| 1281 | } | 1260 | } |
| 1282 | 1261 | ||
| 1283 | return ttm_bo_force_list_clean(bdev, &man->lru, mem_type, true); | 1262 | return ttm_bo_force_list_clean(bdev, mem_type, true); |
| 1284 | } | 1263 | } |
| 1285 | EXPORT_SYMBOL(ttm_bo_evict_mm); | 1264 | EXPORT_SYMBOL(ttm_bo_evict_mm); |
| 1286 | 1265 | ||
| 1287 | int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type, | 1266 | int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type, |
| 1288 | unsigned long p_offset, unsigned long p_size) | 1267 | unsigned long p_size) |
| 1289 | { | 1268 | { |
| 1290 | int ret = -EINVAL; | 1269 | int ret = -EINVAL; |
| 1291 | struct ttm_mem_type_manager *man; | 1270 | struct ttm_mem_type_manager *man; |
| @@ -1315,7 +1294,7 @@ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type, | |||
| 1315 | type); | 1294 | type); |
| 1316 | return ret; | 1295 | return ret; |
| 1317 | } | 1296 | } |
| 1318 | ret = drm_mm_init(&man->manager, p_offset, p_size); | 1297 | ret = drm_mm_init(&man->manager, 0, p_size); |
| 1319 | if (ret) | 1298 | if (ret) |
| 1320 | return ret; | 1299 | return ret; |
| 1321 | } | 1300 | } |
| @@ -1464,7 +1443,7 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev, | |||
| 1464 | * Initialize the system memory buffer type. | 1443 | * Initialize the system memory buffer type. |
| 1465 | * Other types need to be driver / IOCTL initialized. | 1444 | * Other types need to be driver / IOCTL initialized. |
| 1466 | */ | 1445 | */ |
| 1467 | ret = ttm_bo_init_mm(bdev, TTM_PL_SYSTEM, 0, 0); | 1446 | ret = ttm_bo_init_mm(bdev, TTM_PL_SYSTEM, 0); |
| 1468 | if (unlikely(ret != 0)) | 1447 | if (unlikely(ret != 0)) |
| 1469 | goto out_no_sys; | 1448 | goto out_no_sys; |
| 1470 | 1449 | ||
