diff options
author | Maarten Lankhorst <maarten.lankhorst@canonical.com> | 2012-11-29 06:36:54 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2012-12-10 05:21:03 -0500 |
commit | 85b144f860176ec18db927d6d9ecdfb24d9c6483 (patch) | |
tree | 22c7d066d82286c7638bd974089ef0a682d0c4dd /drivers/gpu/drm/ttm | |
parent | 6ed9ccb41209b93409c92eb8c130eada4e0832ef (diff) |
drm/ttm: call ttm_bo_cleanup_refs with reservation and lru lock held, v3
By removing the unlocking of lru and retaking it immediately, a race is
removed where the bo is taken off the swap list or the lru list between
the unlock and relock. As such the cleanup_refs code can be simplified,
it will attempt to call ttm_bo_wait non-blockingly, and if it fails
it will drop the locks and perform a blocking wait, or return an error
if no_wait_gpu was set.
The need for looping is also eliminated, since swapout and evict_mem_first
will always follow the destruction path, no new fence is allowed
to be attached. As far as I can see this may already have been the case,
but the unlocking / relocking required a complicated loop to deal with
re-reservation.
Changes since v1:
- Simplify no_wait_gpu case by folding it in with empty ddestroy.
- Hold a reservation while calling ttm_bo_cleanup_memtype_use again.
Changes since v2:
- Do not remove bo from lru list while waiting
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/ttm')
-rw-r--r-- | drivers/gpu/drm/ttm/ttm_bo.c | 123 |
1 files changed, 76 insertions, 47 deletions
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index b7781453bfd1..ef223d581a70 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c | |||
@@ -488,12 +488,16 @@ static void ttm_bo_cleanup_memtype_use(struct ttm_buffer_object *bo) | |||
488 | ttm_bo_mem_put(bo, &bo->mem); | 488 | ttm_bo_mem_put(bo, &bo->mem); |
489 | 489 | ||
490 | atomic_set(&bo->reserved, 0); | 490 | atomic_set(&bo->reserved, 0); |
491 | wake_up_all(&bo->event_queue); | ||
491 | 492 | ||
492 | /* | 493 | /* |
493 | * Make processes trying to reserve really pick it up. | 494 | * Since the final reference to this bo may not be dropped by |
495 | * the current task we have to put a memory barrier here to make | ||
496 | * sure the changes done in this function are always visible. | ||
497 | * | ||
498 | * This function only needs protection against the final kref_put. | ||
494 | */ | 499 | */ |
495 | smp_mb__after_atomic_dec(); | 500 | smp_mb__before_atomic_dec(); |
496 | wake_up_all(&bo->event_queue); | ||
497 | } | 501 | } |
498 | 502 | ||
499 | static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) | 503 | static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) |
@@ -543,68 +547,84 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) | |||
543 | } | 547 | } |
544 | 548 | ||
545 | /** | 549 | /** |
546 | * function ttm_bo_cleanup_refs | 550 | * function ttm_bo_cleanup_refs_and_unlock |
547 | * If bo idle, remove from delayed- and lru lists, and unref. | 551 | * If bo idle, remove from delayed- and lru lists, and unref. |
548 | * If not idle, do nothing. | 552 | * If not idle, do nothing. |
549 | * | 553 | * |
554 | * Must be called with lru_lock and reservation held, this function | ||
555 | * will drop both before returning. | ||
556 | * | ||
550 | * @interruptible Any sleeps should occur interruptibly. | 557 | * @interruptible Any sleeps should occur interruptibly. |
551 | * @no_wait_reserve Never wait for reserve. Return -EBUSY instead. | ||
552 | * @no_wait_gpu Never wait for gpu. Return -EBUSY instead. | 558 | * @no_wait_gpu Never wait for gpu. Return -EBUSY instead. |
553 | */ | 559 | */ |
554 | 560 | ||
555 | static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, | 561 | static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, |
556 | bool interruptible, | 562 | bool interruptible, |
557 | bool no_wait_reserve, | 563 | bool no_wait_gpu) |
558 | bool no_wait_gpu) | ||
559 | { | 564 | { |
560 | struct ttm_bo_device *bdev = bo->bdev; | 565 | struct ttm_bo_device *bdev = bo->bdev; |
566 | struct ttm_bo_driver *driver = bdev->driver; | ||
561 | struct ttm_bo_global *glob = bo->glob; | 567 | struct ttm_bo_global *glob = bo->glob; |
562 | int put_count; | 568 | int put_count; |
563 | int ret = 0; | 569 | int ret; |
564 | 570 | ||
565 | retry: | ||
566 | spin_lock(&bdev->fence_lock); | 571 | spin_lock(&bdev->fence_lock); |
567 | ret = ttm_bo_wait(bo, false, interruptible, no_wait_gpu); | 572 | ret = ttm_bo_wait(bo, false, false, true); |
568 | spin_unlock(&bdev->fence_lock); | ||
569 | 573 | ||
570 | if (unlikely(ret != 0)) | 574 | if (ret && !no_wait_gpu) { |
571 | return ret; | 575 | void *sync_obj; |
572 | 576 | ||
573 | retry_reserve: | 577 | /* |
574 | spin_lock(&glob->lru_lock); | 578 | * Take a reference to the fence and unreserve, |
579 | * at this point the buffer should be dead, so | ||
580 | * no new sync objects can be attached. | ||
581 | */ | ||
582 | sync_obj = driver->sync_obj_ref(&bo->sync_obj); | ||
583 | spin_unlock(&bdev->fence_lock); | ||
575 | 584 | ||
576 | if (unlikely(list_empty(&bo->ddestroy))) { | 585 | atomic_set(&bo->reserved, 0); |
586 | wake_up_all(&bo->event_queue); | ||
577 | spin_unlock(&glob->lru_lock); | 587 | spin_unlock(&glob->lru_lock); |
578 | return 0; | ||
579 | } | ||
580 | 588 | ||
581 | ret = ttm_bo_reserve_locked(bo, false, true, false, 0); | 589 | ret = driver->sync_obj_wait(sync_obj, false, interruptible); |
582 | 590 | driver->sync_obj_unref(&sync_obj); | |
583 | if (unlikely(ret == -EBUSY)) { | 591 | if (ret) |
584 | spin_unlock(&glob->lru_lock); | ||
585 | if (likely(!no_wait_reserve)) | ||
586 | ret = ttm_bo_wait_unreserved(bo, interruptible); | ||
587 | if (unlikely(ret != 0)) | ||
588 | return ret; | 592 | return ret; |
589 | 593 | ||
590 | goto retry_reserve; | 594 | /* |
591 | } | 595 | * remove sync_obj with ttm_bo_wait, the wait should be |
596 | * finished, and no new wait object should have been added. | ||
597 | */ | ||
598 | spin_lock(&bdev->fence_lock); | ||
599 | ret = ttm_bo_wait(bo, false, false, true); | ||
600 | WARN_ON(ret); | ||
601 | spin_unlock(&bdev->fence_lock); | ||
602 | if (ret) | ||
603 | return ret; | ||
592 | 604 | ||
593 | BUG_ON(ret != 0); | 605 | spin_lock(&glob->lru_lock); |
606 | ret = ttm_bo_reserve_locked(bo, false, true, false, 0); | ||
594 | 607 | ||
595 | /** | 608 | /* |
596 | * We can re-check for sync object without taking | 609 | * We raced, and lost, someone else holds the reservation now, |
597 | * the bo::lock since setting the sync object requires | 610 | * and is probably busy in ttm_bo_cleanup_memtype_use. |
598 | * also bo::reserved. A busy object at this point may | 611 | * |
599 | * be caused by another thread recently starting an accelerated | 612 | * Even if it's not the case, because we finished waiting any |
600 | * eviction. | 613 | * delayed destruction would succeed, so just return success |
601 | */ | 614 | * here. |
615 | */ | ||
616 | if (ret) { | ||
617 | spin_unlock(&glob->lru_lock); | ||
618 | return 0; | ||
619 | } | ||
620 | } else | ||
621 | spin_unlock(&bdev->fence_lock); | ||
602 | 622 | ||
603 | if (unlikely(bo->sync_obj)) { | 623 | if (ret || unlikely(list_empty(&bo->ddestroy))) { |
604 | atomic_set(&bo->reserved, 0); | 624 | atomic_set(&bo->reserved, 0); |
605 | wake_up_all(&bo->event_queue); | 625 | wake_up_all(&bo->event_queue); |
606 | spin_unlock(&glob->lru_lock); | 626 | spin_unlock(&glob->lru_lock); |
607 | goto retry; | 627 | return ret; |
608 | } | 628 | } |
609 | 629 | ||
610 | put_count = ttm_bo_del_from_lru(bo); | 630 | put_count = ttm_bo_del_from_lru(bo); |
@@ -647,9 +667,13 @@ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all) | |||
647 | kref_get(&nentry->list_kref); | 667 | kref_get(&nentry->list_kref); |
648 | } | 668 | } |
649 | 669 | ||
650 | spin_unlock(&glob->lru_lock); | 670 | ret = ttm_bo_reserve_locked(entry, false, !remove_all, false, 0); |
651 | ret = ttm_bo_cleanup_refs(entry, false, !remove_all, | 671 | if (!ret) |
652 | !remove_all); | 672 | ret = ttm_bo_cleanup_refs_and_unlock(entry, false, |
673 | !remove_all); | ||
674 | else | ||
675 | spin_unlock(&glob->lru_lock); | ||
676 | |||
653 | kref_put(&entry->list_kref, ttm_bo_release_list); | 677 | kref_put(&entry->list_kref, ttm_bo_release_list); |
654 | entry = nentry; | 678 | entry = nentry; |
655 | 679 | ||
@@ -800,9 +824,13 @@ retry: | |||
800 | kref_get(&bo->list_kref); | 824 | kref_get(&bo->list_kref); |
801 | 825 | ||
802 | if (!list_empty(&bo->ddestroy)) { | 826 | if (!list_empty(&bo->ddestroy)) { |
803 | spin_unlock(&glob->lru_lock); | 827 | ret = ttm_bo_reserve_locked(bo, interruptible, no_wait_reserve, false, 0); |
804 | ret = ttm_bo_cleanup_refs(bo, interruptible, | 828 | if (!ret) |
805 | no_wait_reserve, no_wait_gpu); | 829 | ret = ttm_bo_cleanup_refs_and_unlock(bo, interruptible, |
830 | no_wait_gpu); | ||
831 | else | ||
832 | spin_unlock(&glob->lru_lock); | ||
833 | |||
806 | kref_put(&bo->list_kref, ttm_bo_release_list); | 834 | kref_put(&bo->list_kref, ttm_bo_release_list); |
807 | 835 | ||
808 | return ret; | 836 | return ret; |
@@ -1796,8 +1824,9 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) | |||
1796 | kref_get(&bo->list_kref); | 1824 | kref_get(&bo->list_kref); |
1797 | 1825 | ||
1798 | if (!list_empty(&bo->ddestroy)) { | 1826 | if (!list_empty(&bo->ddestroy)) { |
1799 | spin_unlock(&glob->lru_lock); | 1827 | ttm_bo_reserve_locked(bo, false, false, false, 0); |
1800 | (void) ttm_bo_cleanup_refs(bo, false, false, false); | 1828 | ttm_bo_cleanup_refs_and_unlock(bo, false, false); |
1829 | |||
1801 | kref_put(&bo->list_kref, ttm_bo_release_list); | 1830 | kref_put(&bo->list_kref, ttm_bo_release_list); |
1802 | spin_lock(&glob->lru_lock); | 1831 | spin_lock(&glob->lru_lock); |
1803 | continue; | 1832 | continue; |