diff options
Diffstat (limited to 'drivers/gpu/drm/ttm')
-rw-r--r-- | drivers/gpu/drm/ttm/ttm_bo.c | 271 | ||||
-rw-r--r-- | drivers/gpu/drm/ttm/ttm_bo_util.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/ttm/ttm_bo_vm.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/ttm/ttm_page_alloc_dma.c | 2 |
4 files changed, 139 insertions, 149 deletions
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 5df596fb0280..9e07c3f75156 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -145,34 +145,12 @@ static inline uint32_t ttm_bo_type_flags(unsigned type) return 1 << (type); } -static void ttm_bo_release_list(struct kref *list_kref) -{ - struct ttm_buffer_object *bo = - container_of(list_kref, struct ttm_buffer_object, list_kref); - size_t acc_size = bo->acc_size; - - BUG_ON(kref_read(&bo->list_kref)); - BUG_ON(kref_read(&bo->kref)); - BUG_ON(bo->mem.mm_node != NULL); - BUG_ON(!list_empty(&bo->lru)); - BUG_ON(!list_empty(&bo->ddestroy)); - ttm_tt_destroy(bo->ttm); - atomic_dec(&ttm_bo_glob.bo_count); - dma_fence_put(bo->moving); - if (!ttm_bo_uses_embedded_gem_object(bo)) - dma_resv_fini(&bo->base._resv); - bo->destroy(bo); - ttm_mem_global_free(&ttm_mem_glob, acc_size); -} - static void ttm_bo_add_mem_to_lru(struct ttm_buffer_object *bo, struct ttm_mem_reg *mem) { struct ttm_bo_device *bdev = bo->bdev; struct ttm_mem_type_manager *man; - dma_resv_assert_held(bo->base.resv); - if (!list_empty(&bo->lru)) return; @@ -181,21 +159,14 @@ static void ttm_bo_add_mem_to_lru(struct ttm_buffer_object *bo, man = &bdev->man[mem->mem_type]; list_add_tail(&bo->lru, &man->lru[bo->priority]); - kref_get(&bo->list_kref); if (!(man->flags & TTM_MEMTYPE_FLAG_FIXED) && bo->ttm && !(bo->ttm->page_flags & (TTM_PAGE_FLAG_SG | TTM_PAGE_FLAG_SWAPPED))) { list_add_tail(&bo->swap, &ttm_bo_glob.swap_lru[bo->priority]); - kref_get(&bo->list_kref); } } -static void ttm_bo_ref_bug(struct kref *list_kref) -{ - BUG(); -} - static void ttm_bo_del_from_lru(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; @@ -203,12 +174,10 @@ static void ttm_bo_del_from_lru(struct ttm_buffer_object *bo) if (!list_empty(&bo->swap)) { list_del_init(&bo->swap); - kref_put(&bo->list_kref, ttm_bo_ref_bug); notify = true; } if (!list_empty(&bo->lru)) { list_del_init(&bo->lru); - kref_put(&bo->list_kref, ttm_bo_ref_bug); notify = true; } @@ -372,14 +341,7 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, } moved: - if (bo->evicted) { - if (bdev->driver->invalidate_caches) { - ret = bdev->driver->invalidate_caches(bdev, bo->mem.placement); - if (ret) - pr_err("Can not flush read caches\n"); - } - bo->evicted = false; - } + bo->evicted = false; if (bo->mem.mm_node) bo->offset = (bo->mem.start << PAGE_SHIFT) + @@ -428,92 +390,49 @@ static int ttm_bo_individualize_resv(struct ttm_buffer_object *bo) BUG_ON(!dma_resv_trylock(&bo->base._resv)); r = dma_resv_copy_fences(&bo->base._resv, bo->base.resv); + dma_resv_unlock(&bo->base._resv); if (r) - dma_resv_unlock(&bo->base._resv); + return r; + + if (bo->type != ttm_bo_type_sg) { + /* This works because the BO is about to be destroyed and nobody + * reference it any more. The only tricky case is the trylock on + * the resv object while holding the lru_lock. + */ + spin_lock(&ttm_bo_glob.lru_lock); + bo->base.resv = &bo->base._resv; + spin_unlock(&ttm_bo_glob.lru_lock); + } return r; } static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo) { + struct dma_resv *resv = &bo->base._resv; struct dma_resv_list *fobj; struct dma_fence *fence; int i; - fobj = dma_resv_get_list(&bo->base._resv); - fence = dma_resv_get_excl(&bo->base._resv); + rcu_read_lock(); + fobj = rcu_dereference(resv->fence); + fence = rcu_dereference(resv->fence_excl); if (fence && !fence->ops->signaled) dma_fence_enable_sw_signaling(fence); for (i = 0; fobj && i < fobj->shared_count; ++i) { - fence = rcu_dereference_protected(fobj->shared[i], - dma_resv_held(bo->base.resv)); + fence = rcu_dereference(fobj->shared[i]); if (!fence->ops->signaled) dma_fence_enable_sw_signaling(fence); } -} - -static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) -{ - struct ttm_bo_device *bdev = bo->bdev; - int ret; - - ret = ttm_bo_individualize_resv(bo); - if (ret) { - /* Last resort, if we fail to allocate memory for the - * fences block for the BO to become idle - */ - dma_resv_wait_timeout_rcu(bo->base.resv, true, false, - 30 * HZ); - spin_lock(&ttm_bo_glob.lru_lock); - goto error; - } - - spin_lock(&ttm_bo_glob.lru_lock); - ret = dma_resv_trylock(bo->base.resv) ? 0 : -EBUSY; - if (!ret) { - if (dma_resv_test_signaled_rcu(&bo->base._resv, true)) { - ttm_bo_del_from_lru(bo); - spin_unlock(&ttm_bo_glob.lru_lock); - if (bo->base.resv != &bo->base._resv) - dma_resv_unlock(&bo->base._resv); - - ttm_bo_cleanup_memtype_use(bo); - dma_resv_unlock(bo->base.resv); - return; - } - - ttm_bo_flush_all_fences(bo); - - /* - * Make NO_EVICT bos immediately available to - * shrinkers, now that they are queued for - * destruction. - */ - if (bo->mem.placement & TTM_PL_FLAG_NO_EVICT) { - bo->mem.placement &= ~TTM_PL_FLAG_NO_EVICT; - ttm_bo_move_to_lru_tail(bo, NULL); - } - - dma_resv_unlock(bo->base.resv); - } - if (bo->base.resv != &bo->base._resv) - dma_resv_unlock(&bo->base._resv); - -error: - kref_get(&bo->list_kref); - list_add_tail(&bo->ddestroy, &bdev->ddestroy); - spin_unlock(&ttm_bo_glob.lru_lock); - - schedule_delayed_work(&bdev->wq, - ((HZ / 100) < 1) ? 1 : HZ / 100); + rcu_read_unlock(); } /** * function ttm_bo_cleanup_refs - * If bo idle, remove from delayed- and lru lists, and unref. - * If not idle, do nothing. + * If bo idle, remove from lru lists, and unref. + * If not idle, block if possible. * * Must be called with lru_lock and reservation held, this function * will drop the lru lock and optionally the reservation lock before returning. @@ -527,14 +446,9 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, bool interruptible, bool no_wait_gpu, bool unlock_resv) { - struct dma_resv *resv; + struct dma_resv *resv = &bo->base._resv; int ret; - if (unlikely(list_empty(&bo->ddestroy))) - resv = bo->base.resv; - else - resv = &bo->base._resv; - if (dma_resv_test_signaled_rcu(resv, true)) ret = 0; else @@ -547,9 +461,8 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, dma_resv_unlock(bo->base.resv); spin_unlock(&ttm_bo_glob.lru_lock); - lret = dma_resv_wait_timeout_rcu(resv, true, - interruptible, - 30 * HZ); + lret = dma_resv_wait_timeout_rcu(resv, true, interruptible, + 30 * HZ); if (lret < 0) return lret; @@ -581,14 +494,14 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, ttm_bo_del_from_lru(bo); list_del_init(&bo->ddestroy); - kref_put(&bo->list_kref, ttm_bo_ref_bug); - spin_unlock(&ttm_bo_glob.lru_lock); ttm_bo_cleanup_memtype_use(bo); if (unlock_resv) dma_resv_unlock(bo->base.resv); + ttm_bo_put(bo); + return 0; } @@ -610,8 +523,9 @@ static bool ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all) bo = list_first_entry(&bdev->ddestroy, struct ttm_buffer_object, ddestroy); - kref_get(&bo->list_kref); list_move_tail(&bo->ddestroy, &removed); + if (!ttm_bo_get_unless_zero(bo)) + continue; if (remove_all || bo->base.resv != &bo->base._resv) { spin_unlock(&glob->lru_lock); @@ -626,7 +540,7 @@ static bool ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all) spin_unlock(&glob->lru_lock); } - kref_put(&bo->list_kref, ttm_bo_release_list); + ttm_bo_put(bo); spin_lock(&glob->lru_lock); } list_splice_tail(&removed, &bdev->ddestroy); @@ -652,16 +566,69 @@ static void ttm_bo_release(struct kref *kref) container_of(kref, struct ttm_buffer_object, kref); struct ttm_bo_device *bdev = bo->bdev; struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type]; + size_t acc_size = bo->acc_size; + int ret; - if (bo->bdev->driver->release_notify) - bo->bdev->driver->release_notify(bo); + if (!bo->deleted) { + ret = ttm_bo_individualize_resv(bo); + if (ret) { + /* Last resort, if we fail to allocate memory for the + * fences block for the BO to become idle + */ + dma_resv_wait_timeout_rcu(bo->base.resv, true, false, + 30 * HZ); + } - drm_vma_offset_remove(bdev->vma_manager, &bo->base.vma_node); - ttm_mem_io_lock(man, false); - ttm_mem_io_free_vm(bo); - ttm_mem_io_unlock(man); - ttm_bo_cleanup_refs_or_queue(bo); - kref_put(&bo->list_kref, ttm_bo_release_list); + if (bo->bdev->driver->release_notify) + bo->bdev->driver->release_notify(bo); + + drm_vma_offset_remove(bdev->vma_manager, &bo->base.vma_node); + ttm_mem_io_lock(man, false); + ttm_mem_io_free_vm(bo); + ttm_mem_io_unlock(man); + } + + if (!dma_resv_test_signaled_rcu(bo->base.resv, true)) { + /* The BO is not idle, resurrect it for delayed destroy */ + ttm_bo_flush_all_fences(bo); + bo->deleted = true; + + spin_lock(&ttm_bo_glob.lru_lock); + + /* + * Make NO_EVICT bos immediately available to + * shrinkers, now that they are queued for + * destruction. + */ + if (bo->mem.placement & TTM_PL_FLAG_NO_EVICT) { + bo->mem.placement &= ~TTM_PL_FLAG_NO_EVICT; + ttm_bo_del_from_lru(bo); + ttm_bo_add_mem_to_lru(bo, &bo->mem); + } + + kref_init(&bo->kref); + list_add_tail(&bo->ddestroy, &bdev->ddestroy); + spin_unlock(&ttm_bo_glob.lru_lock); + + schedule_delayed_work(&bdev->wq, + ((HZ / 100) < 1) ? 1 : HZ / 100); + return; + } + + spin_lock(&ttm_bo_glob.lru_lock); + ttm_bo_del_from_lru(bo); + list_del(&bo->ddestroy); + spin_unlock(&ttm_bo_glob.lru_lock); + + ttm_bo_cleanup_memtype_use(bo); + + BUG_ON(bo->mem.mm_node != NULL); + atomic_dec(&ttm_bo_glob.bo_count); + dma_fence_put(bo->moving); + if (!ttm_bo_uses_embedded_gem_object(bo)) + dma_resv_fini(&bo->base._resv); + bo->destroy(bo); + ttm_mem_global_free(&ttm_mem_glob, acc_size); } void ttm_bo_put(struct ttm_buffer_object *bo) @@ -764,8 +731,7 @@ static bool ttm_bo_evict_swapout_allowable(struct ttm_buffer_object *bo, if (bo->base.resv == ctx->resv) { dma_resv_assert_held(bo->base.resv); - if (ctx->flags & TTM_OPT_FLAG_ALLOW_RES_EVICT - || !list_empty(&bo->ddestroy)) + if (ctx->flags & TTM_OPT_FLAG_ALLOW_RES_EVICT) ret = true; *locked = false; if (busy) @@ -846,6 +812,11 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, dma_resv_unlock(bo->base.resv); continue; } + if (!ttm_bo_get_unless_zero(bo)) { + if (locked) + dma_resv_unlock(bo->base.resv); + continue; + } break; } @@ -857,21 +828,19 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, } if (!bo) { - if (busy_bo) - kref_get(&busy_bo->list_kref); + if (busy_bo && !ttm_bo_get_unless_zero(busy_bo)) + busy_bo = NULL; spin_unlock(&ttm_bo_glob.lru_lock); ret = ttm_mem_evict_wait_busy(busy_bo, ctx, ticket); if (busy_bo) - kref_put(&busy_bo->list_kref, ttm_bo_release_list); + ttm_bo_put(busy_bo); return ret; } - kref_get(&bo->list_kref); - - if (!list_empty(&bo->ddestroy)) { + if (bo->deleted) { ret = ttm_bo_cleanup_refs(bo, ctx->interruptible, ctx->no_wait_gpu, locked); - kref_put(&bo->list_kref, ttm_bo_release_list); + ttm_bo_put(bo); return ret; } @@ -881,7 +850,7 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, if (locked) ttm_bo_unreserve(bo); - kref_put(&bo->list_kref, ttm_bo_release_list); + ttm_bo_put(bo); return ret; } @@ -1226,6 +1195,18 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, uint32_t new_flags; dma_resv_assert_held(bo->base.resv); + + /* + * Remove the backing store if no placement is given. + */ + if (!placement->num_placement && !placement->num_busy_placement) { + ret = ttm_bo_pipeline_gutting(bo); + if (ret) + return ret; + + return ttm_tt_create(bo, false); + } + /* * Check whether we need to move buffer. */ @@ -1293,7 +1274,6 @@ int ttm_bo_init_reserved(struct ttm_bo_device *bdev, bo->destroy = destroy ? destroy : ttm_bo_default_destroy; kref_init(&bo->kref); - kref_init(&bo->list_kref); INIT_LIST_HEAD(&bo->lru); INIT_LIST_HEAD(&bo->ddestroy); INIT_LIST_HEAD(&bo->swap); @@ -1813,11 +1793,18 @@ int ttm_bo_swapout(struct ttm_bo_global *glob, struct ttm_operation_ctx *ctx) spin_lock(&glob->lru_lock); for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { list_for_each_entry(bo, &glob->swap_lru[i], swap) { - if (ttm_bo_evict_swapout_allowable(bo, ctx, &locked, - NULL)) { - ret = 0; - break; + if (!ttm_bo_evict_swapout_allowable(bo, ctx, &locked, + NULL)) + continue; + + if (!ttm_bo_get_unless_zero(bo)) { + if (locked) + dma_resv_unlock(bo->base.resv); + continue; } + + ret = 0; + break; } if (!ret) break; @@ -1828,11 +1815,9 @@ int ttm_bo_swapout(struct ttm_bo_global *glob, struct ttm_operation_ctx *ctx) return ret; } - kref_get(&bo->list_kref); - - if (!list_empty(&bo->ddestroy)) { + if (bo->deleted) { ret = ttm_bo_cleanup_refs(bo, false, false, locked); - kref_put(&bo->list_kref, ttm_bo_release_list); + ttm_bo_put(bo); return ret; } @@ -1886,7 +1871,7 @@ out: */ if (locked) dma_resv_unlock(bo->base.resv); - kref_put(&bo->list_kref, ttm_bo_release_list); + ttm_bo_put(bo); return ret; } EXPORT_SYMBOL(ttm_bo_swapout); diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 953c82a4f573..52d2b71f1588 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -507,11 +507,10 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, fbo->base.moving = NULL; drm_vma_node_reset(&fbo->base.base.vma_node); - kref_init(&fbo->base.list_kref); kref_init(&fbo->base.kref); fbo->base.destroy = &ttm_transfered_destroy; fbo->base.acc_size = 0; - if (bo->base.resv == &bo->base._resv) + if (bo->type != ttm_bo_type_sg) fbo->base.base.resv = &fbo->base.base._resv; dma_resv_init(&fbo->base.base._resv); diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 389128b8c4dd..cb8829ca6c7f 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -59,9 +59,10 @@ static vm_fault_t ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo, /* * If possible, avoid waiting for GPU with mmap_sem - * held. + * held. We only do this if the fault allows retry and this + * is the first attempt. */ - if (vmf->flags & FAULT_FLAG_ALLOW_RETRY) { + if (fault_flag_allow_retry_first(vmf->flags)) { ret = VM_FAULT_RETRY; if (vmf->flags & FAULT_FLAG_RETRY_NOWAIT) goto out_unlock; @@ -135,7 +136,12 @@ vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo, * for the buffer to become unreserved. */ if (unlikely(!dma_resv_trylock(bo->base.resv))) { - if (vmf->flags & FAULT_FLAG_ALLOW_RETRY) { + /* + * If the fault allows retry and this is the first + * fault attempt, we try to release the mmap_sem + * before waiting + */ + if (fault_flag_allow_retry_first(vmf->flags)) { if (!(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) { ttm_bo_get(bo); up_read(&vmf->vma->vm_mm->mmap_sem); diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c index bf876faea592..faefaaef7909 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c @@ -604,7 +604,7 @@ static struct dma_pool *ttm_dma_pool_init(struct device *dev, gfp_t flags, p = pool->name; for (i = 0; i < ARRAY_SIZE(t); i++) { if (type & t[i]) { - p += snprintf(p, sizeof(pool->name) - (p - pool->name), + p += scnprintf(p, sizeof(pool->name) - (p - pool->name), "%s", n[i]); } } |