162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 OR MIT */ 262306a36Sopenharmony_ci/************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA 562306a36Sopenharmony_ci * All Rights Reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 862306a36Sopenharmony_ci * copy of this software and associated documentation files (the 962306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 1062306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 1162306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 1262306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 1362306a36Sopenharmony_ci * the following conditions: 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the 1662306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 1762306a36Sopenharmony_ci * of the Software. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2062306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2162306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 2262306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 2362306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2462306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 2562306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci **************************************************************************/ 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define pr_fmt(fmt) "[TTM] " fmt 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <drm/ttm/ttm_bo.h> 3562306a36Sopenharmony_ci#include <drm/ttm/ttm_placement.h> 3662306a36Sopenharmony_ci#include <drm/ttm/ttm_tt.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include <linux/jiffies.h> 3962306a36Sopenharmony_ci#include <linux/slab.h> 4062306a36Sopenharmony_ci#include <linux/sched.h> 4162306a36Sopenharmony_ci#include <linux/mm.h> 4262306a36Sopenharmony_ci#include <linux/file.h> 4362306a36Sopenharmony_ci#include <linux/module.h> 4462306a36Sopenharmony_ci#include <linux/atomic.h> 4562306a36Sopenharmony_ci#include <linux/dma-resv.h> 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#include "ttm_module.h" 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo, 5062306a36Sopenharmony_ci struct ttm_placement *placement) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct drm_printer p = drm_debug_printer(TTM_PFX); 5362306a36Sopenharmony_ci struct ttm_resource_manager *man; 5462306a36Sopenharmony_ci int i, mem_type; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci for (i = 0; i < placement->num_placement; i++) { 5762306a36Sopenharmony_ci mem_type = placement->placement[i].mem_type; 5862306a36Sopenharmony_ci drm_printf(&p, " placement[%d]=0x%08X (%d)\n", 5962306a36Sopenharmony_ci i, placement->placement[i].flags, mem_type); 6062306a36Sopenharmony_ci man = ttm_manager_type(bo->bdev, mem_type); 6162306a36Sopenharmony_ci ttm_resource_manager_debug(man, &p); 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/** 6662306a36Sopenharmony_ci * ttm_bo_move_to_lru_tail 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * @bo: The buffer object. 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * Move this BO to the tail of all lru lists used to lookup and reserve an 7162306a36Sopenharmony_ci * object. This function must be called with struct ttm_global::lru_lock 7262306a36Sopenharmony_ci * held, and is used to make a BO less likely to be considered for eviction. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_civoid ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci dma_resv_assert_held(bo->base.resv); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (bo->resource) 7962306a36Sopenharmony_ci ttm_resource_move_to_lru_tail(bo->resource); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_move_to_lru_tail); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/** 8462306a36Sopenharmony_ci * ttm_bo_set_bulk_move - update BOs bulk move object 8562306a36Sopenharmony_ci * 8662306a36Sopenharmony_ci * @bo: The buffer object. 8762306a36Sopenharmony_ci * @bulk: bulk move structure 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * Update the BOs bulk move object, making sure that resources are added/removed 9062306a36Sopenharmony_ci * as well. A bulk move allows to move many resource on the LRU at once, 9162306a36Sopenharmony_ci * resulting in much less overhead of maintaining the LRU. 9262306a36Sopenharmony_ci * The only requirement is that the resources stay together on the LRU and are 9362306a36Sopenharmony_ci * never separated. This is enforces by setting the bulk_move structure on a BO. 9462306a36Sopenharmony_ci * ttm_lru_bulk_move_tail() should be used to move all resources to the tail of 9562306a36Sopenharmony_ci * their LRU list. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_civoid ttm_bo_set_bulk_move(struct ttm_buffer_object *bo, 9862306a36Sopenharmony_ci struct ttm_lru_bulk_move *bulk) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci dma_resv_assert_held(bo->base.resv); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (bo->bulk_move == bulk) 10362306a36Sopenharmony_ci return; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci spin_lock(&bo->bdev->lru_lock); 10662306a36Sopenharmony_ci if (bo->resource) 10762306a36Sopenharmony_ci ttm_resource_del_bulk_move(bo->resource, bo); 10862306a36Sopenharmony_ci bo->bulk_move = bulk; 10962306a36Sopenharmony_ci if (bo->resource) 11062306a36Sopenharmony_ci ttm_resource_add_bulk_move(bo->resource, bo); 11162306a36Sopenharmony_ci spin_unlock(&bo->bdev->lru_lock); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_set_bulk_move); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, 11662306a36Sopenharmony_ci struct ttm_resource *mem, bool evict, 11762306a36Sopenharmony_ci struct ttm_operation_ctx *ctx, 11862306a36Sopenharmony_ci struct ttm_place *hop) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct ttm_device *bdev = bo->bdev; 12162306a36Sopenharmony_ci bool old_use_tt, new_use_tt; 12262306a36Sopenharmony_ci int ret; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci old_use_tt = !bo->resource || ttm_manager_type(bdev, bo->resource->mem_type)->use_tt; 12562306a36Sopenharmony_ci new_use_tt = ttm_manager_type(bdev, mem->mem_type)->use_tt; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci ttm_bo_unmap_virtual(bo); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* 13062306a36Sopenharmony_ci * Create and bind a ttm if required. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (new_use_tt) { 13462306a36Sopenharmony_ci /* Zero init the new TTM structure if the old location should 13562306a36Sopenharmony_ci * have used one as well. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci ret = ttm_tt_create(bo, old_use_tt); 13862306a36Sopenharmony_ci if (ret) 13962306a36Sopenharmony_ci goto out_err; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (mem->mem_type != TTM_PL_SYSTEM) { 14262306a36Sopenharmony_ci ret = ttm_tt_populate(bo->bdev, bo->ttm, ctx); 14362306a36Sopenharmony_ci if (ret) 14462306a36Sopenharmony_ci goto out_err; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ret = dma_resv_reserve_fences(bo->base.resv, 1); 14962306a36Sopenharmony_ci if (ret) 15062306a36Sopenharmony_ci goto out_err; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci ret = bdev->funcs->move(bo, evict, ctx, mem, hop); 15362306a36Sopenharmony_ci if (ret) { 15462306a36Sopenharmony_ci if (ret == -EMULTIHOP) 15562306a36Sopenharmony_ci return ret; 15662306a36Sopenharmony_ci goto out_err; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ctx->bytes_moved += bo->base.size; 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ciout_err: 16362306a36Sopenharmony_ci if (!old_use_tt) 16462306a36Sopenharmony_ci ttm_bo_tt_destroy(bo); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return ret; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * Call bo::reserved. 17162306a36Sopenharmony_ci * Will release GPU memory type usage on destruction. 17262306a36Sopenharmony_ci * This is the place to put in driver specific hooks to release 17362306a36Sopenharmony_ci * driver private resources. 17462306a36Sopenharmony_ci * Will release the bo::reserved lock. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void ttm_bo_cleanup_memtype_use(struct ttm_buffer_object *bo) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci if (bo->bdev->funcs->delete_mem_notify) 18062306a36Sopenharmony_ci bo->bdev->funcs->delete_mem_notify(bo); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ttm_bo_tt_destroy(bo); 18362306a36Sopenharmony_ci ttm_resource_free(bo, &bo->resource); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int ttm_bo_individualize_resv(struct ttm_buffer_object *bo) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci int r; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (bo->base.resv == &bo->base._resv) 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci BUG_ON(!dma_resv_trylock(&bo->base._resv)); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci r = dma_resv_copy_fences(&bo->base._resv, bo->base.resv); 19662306a36Sopenharmony_ci dma_resv_unlock(&bo->base._resv); 19762306a36Sopenharmony_ci if (r) 19862306a36Sopenharmony_ci return r; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (bo->type != ttm_bo_type_sg) { 20162306a36Sopenharmony_ci /* This works because the BO is about to be destroyed and nobody 20262306a36Sopenharmony_ci * reference it any more. The only tricky case is the trylock on 20362306a36Sopenharmony_ci * the resv object while holding the lru_lock. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci spin_lock(&bo->bdev->lru_lock); 20662306a36Sopenharmony_ci bo->base.resv = &bo->base._resv; 20762306a36Sopenharmony_ci spin_unlock(&bo->bdev->lru_lock); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return r; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct dma_resv *resv = &bo->base._resv; 21662306a36Sopenharmony_ci struct dma_resv_iter cursor; 21762306a36Sopenharmony_ci struct dma_fence *fence; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci dma_resv_iter_begin(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP); 22062306a36Sopenharmony_ci dma_resv_for_each_fence_unlocked(&cursor, fence) { 22162306a36Sopenharmony_ci if (!fence->ops->signaled) 22262306a36Sopenharmony_ci dma_fence_enable_sw_signaling(fence); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci dma_resv_iter_end(&cursor); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/** 22862306a36Sopenharmony_ci * ttm_bo_cleanup_refs 22962306a36Sopenharmony_ci * If bo idle, remove from lru lists, and unref. 23062306a36Sopenharmony_ci * If not idle, block if possible. 23162306a36Sopenharmony_ci * 23262306a36Sopenharmony_ci * Must be called with lru_lock and reservation held, this function 23362306a36Sopenharmony_ci * will drop the lru lock and optionally the reservation lock before returning. 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci * @bo: The buffer object to clean-up 23662306a36Sopenharmony_ci * @interruptible: Any sleeps should occur interruptibly. 23762306a36Sopenharmony_ci * @no_wait_gpu: Never wait for gpu. Return -EBUSY instead. 23862306a36Sopenharmony_ci * @unlock_resv: Unlock the reservation lock as well. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, 24262306a36Sopenharmony_ci bool interruptible, bool no_wait_gpu, 24362306a36Sopenharmony_ci bool unlock_resv) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct dma_resv *resv = &bo->base._resv; 24662306a36Sopenharmony_ci int ret; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (dma_resv_test_signaled(resv, DMA_RESV_USAGE_BOOKKEEP)) 24962306a36Sopenharmony_ci ret = 0; 25062306a36Sopenharmony_ci else 25162306a36Sopenharmony_ci ret = -EBUSY; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (ret && !no_wait_gpu) { 25462306a36Sopenharmony_ci long lret; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (unlock_resv) 25762306a36Sopenharmony_ci dma_resv_unlock(bo->base.resv); 25862306a36Sopenharmony_ci spin_unlock(&bo->bdev->lru_lock); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci lret = dma_resv_wait_timeout(resv, DMA_RESV_USAGE_BOOKKEEP, 26162306a36Sopenharmony_ci interruptible, 26262306a36Sopenharmony_ci 30 * HZ); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (lret < 0) 26562306a36Sopenharmony_ci return lret; 26662306a36Sopenharmony_ci else if (lret == 0) 26762306a36Sopenharmony_ci return -EBUSY; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci spin_lock(&bo->bdev->lru_lock); 27062306a36Sopenharmony_ci if (unlock_resv && !dma_resv_trylock(bo->base.resv)) { 27162306a36Sopenharmony_ci /* 27262306a36Sopenharmony_ci * We raced, and lost, someone else holds the reservation now, 27362306a36Sopenharmony_ci * and is probably busy in ttm_bo_cleanup_memtype_use. 27462306a36Sopenharmony_ci * 27562306a36Sopenharmony_ci * Even if it's not the case, because we finished waiting any 27662306a36Sopenharmony_ci * delayed destruction would succeed, so just return success 27762306a36Sopenharmony_ci * here. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci spin_unlock(&bo->bdev->lru_lock); 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci ret = 0; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (ret) { 28662306a36Sopenharmony_ci if (unlock_resv) 28762306a36Sopenharmony_ci dma_resv_unlock(bo->base.resv); 28862306a36Sopenharmony_ci spin_unlock(&bo->bdev->lru_lock); 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci spin_unlock(&bo->bdev->lru_lock); 29362306a36Sopenharmony_ci ttm_bo_cleanup_memtype_use(bo); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (unlock_resv) 29662306a36Sopenharmony_ci dma_resv_unlock(bo->base.resv); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* 30262306a36Sopenharmony_ci * Block for the dma_resv object to become idle, lock the buffer and clean up 30362306a36Sopenharmony_ci * the resource and tt object. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_cistatic void ttm_bo_delayed_delete(struct work_struct *work) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct ttm_buffer_object *bo; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci bo = container_of(work, typeof(*bo), delayed_delete); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP, false, 31262306a36Sopenharmony_ci MAX_SCHEDULE_TIMEOUT); 31362306a36Sopenharmony_ci dma_resv_lock(bo->base.resv, NULL); 31462306a36Sopenharmony_ci ttm_bo_cleanup_memtype_use(bo); 31562306a36Sopenharmony_ci dma_resv_unlock(bo->base.resv); 31662306a36Sopenharmony_ci ttm_bo_put(bo); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void ttm_bo_release(struct kref *kref) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct ttm_buffer_object *bo = 32262306a36Sopenharmony_ci container_of(kref, struct ttm_buffer_object, kref); 32362306a36Sopenharmony_ci struct ttm_device *bdev = bo->bdev; 32462306a36Sopenharmony_ci int ret; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci WARN_ON_ONCE(bo->pin_count); 32762306a36Sopenharmony_ci WARN_ON_ONCE(bo->bulk_move); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (!bo->deleted) { 33062306a36Sopenharmony_ci ret = ttm_bo_individualize_resv(bo); 33162306a36Sopenharmony_ci if (ret) { 33262306a36Sopenharmony_ci /* Last resort, if we fail to allocate memory for the 33362306a36Sopenharmony_ci * fences block for the BO to become idle 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci dma_resv_wait_timeout(bo->base.resv, 33662306a36Sopenharmony_ci DMA_RESV_USAGE_BOOKKEEP, false, 33762306a36Sopenharmony_ci 30 * HZ); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (bo->bdev->funcs->release_notify) 34162306a36Sopenharmony_ci bo->bdev->funcs->release_notify(bo); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci drm_vma_offset_remove(bdev->vma_manager, &bo->base.vma_node); 34462306a36Sopenharmony_ci ttm_mem_io_free(bdev, bo->resource); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (!dma_resv_test_signaled(bo->base.resv, 34762306a36Sopenharmony_ci DMA_RESV_USAGE_BOOKKEEP) || 34862306a36Sopenharmony_ci (want_init_on_free() && (bo->ttm != NULL)) || 34962306a36Sopenharmony_ci !dma_resv_trylock(bo->base.resv)) { 35062306a36Sopenharmony_ci /* The BO is not idle, resurrect it for delayed destroy */ 35162306a36Sopenharmony_ci ttm_bo_flush_all_fences(bo); 35262306a36Sopenharmony_ci bo->deleted = true; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci spin_lock(&bo->bdev->lru_lock); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* 35762306a36Sopenharmony_ci * Make pinned bos immediately available to 35862306a36Sopenharmony_ci * shrinkers, now that they are queued for 35962306a36Sopenharmony_ci * destruction. 36062306a36Sopenharmony_ci * 36162306a36Sopenharmony_ci * FIXME: QXL is triggering this. Can be removed when the 36262306a36Sopenharmony_ci * driver is fixed. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci if (bo->pin_count) { 36562306a36Sopenharmony_ci bo->pin_count = 0; 36662306a36Sopenharmony_ci ttm_resource_move_to_lru_tail(bo->resource); 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci kref_init(&bo->kref); 37062306a36Sopenharmony_ci spin_unlock(&bo->bdev->lru_lock); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci INIT_WORK(&bo->delayed_delete, ttm_bo_delayed_delete); 37362306a36Sopenharmony_ci queue_work(bdev->wq, &bo->delayed_delete); 37462306a36Sopenharmony_ci return; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci ttm_bo_cleanup_memtype_use(bo); 37862306a36Sopenharmony_ci dma_resv_unlock(bo->base.resv); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci atomic_dec(&ttm_glob.bo_count); 38262306a36Sopenharmony_ci bo->destroy(bo); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/** 38662306a36Sopenharmony_ci * ttm_bo_put 38762306a36Sopenharmony_ci * 38862306a36Sopenharmony_ci * @bo: The buffer object. 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * Unreference a buffer object. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_civoid ttm_bo_put(struct ttm_buffer_object *bo) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci kref_put(&bo->kref, ttm_bo_release); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_put); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int ttm_bo_bounce_temp_buffer(struct ttm_buffer_object *bo, 39962306a36Sopenharmony_ci struct ttm_resource **mem, 40062306a36Sopenharmony_ci struct ttm_operation_ctx *ctx, 40162306a36Sopenharmony_ci struct ttm_place *hop) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct ttm_placement hop_placement; 40462306a36Sopenharmony_ci struct ttm_resource *hop_mem; 40562306a36Sopenharmony_ci int ret; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci hop_placement.num_placement = hop_placement.num_busy_placement = 1; 40862306a36Sopenharmony_ci hop_placement.placement = hop_placement.busy_placement = hop; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* find space in the bounce domain */ 41162306a36Sopenharmony_ci ret = ttm_bo_mem_space(bo, &hop_placement, &hop_mem, ctx); 41262306a36Sopenharmony_ci if (ret) 41362306a36Sopenharmony_ci return ret; 41462306a36Sopenharmony_ci /* move to the bounce domain */ 41562306a36Sopenharmony_ci ret = ttm_bo_handle_move_mem(bo, hop_mem, false, ctx, NULL); 41662306a36Sopenharmony_ci if (ret) { 41762306a36Sopenharmony_ci ttm_resource_free(bo, &hop_mem); 41862306a36Sopenharmony_ci return ret; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int ttm_bo_evict(struct ttm_buffer_object *bo, 42462306a36Sopenharmony_ci struct ttm_operation_ctx *ctx) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct ttm_device *bdev = bo->bdev; 42762306a36Sopenharmony_ci struct ttm_resource *evict_mem; 42862306a36Sopenharmony_ci struct ttm_placement placement; 42962306a36Sopenharmony_ci struct ttm_place hop; 43062306a36Sopenharmony_ci int ret = 0; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci memset(&hop, 0, sizeof(hop)); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci dma_resv_assert_held(bo->base.resv); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci placement.num_placement = 0; 43762306a36Sopenharmony_ci placement.num_busy_placement = 0; 43862306a36Sopenharmony_ci bdev->funcs->evict_flags(bo, &placement); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (!placement.num_placement && !placement.num_busy_placement) { 44162306a36Sopenharmony_ci ret = ttm_bo_wait_ctx(bo, ctx); 44262306a36Sopenharmony_ci if (ret) 44362306a36Sopenharmony_ci return ret; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * Since we've already synced, this frees backing store 44762306a36Sopenharmony_ci * immediately. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci return ttm_bo_pipeline_gutting(bo); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci ret = ttm_bo_mem_space(bo, &placement, &evict_mem, ctx); 45362306a36Sopenharmony_ci if (ret) { 45462306a36Sopenharmony_ci if (ret != -ERESTARTSYS) { 45562306a36Sopenharmony_ci pr_err("Failed to find memory space for buffer 0x%p eviction\n", 45662306a36Sopenharmony_ci bo); 45762306a36Sopenharmony_ci ttm_bo_mem_space_debug(bo, &placement); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci goto out; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci do { 46362306a36Sopenharmony_ci ret = ttm_bo_handle_move_mem(bo, evict_mem, true, ctx, &hop); 46462306a36Sopenharmony_ci if (ret != -EMULTIHOP) 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci ret = ttm_bo_bounce_temp_buffer(bo, &evict_mem, ctx, &hop); 46862306a36Sopenharmony_ci } while (!ret); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (ret) { 47162306a36Sopenharmony_ci ttm_resource_free(bo, &evict_mem); 47262306a36Sopenharmony_ci if (ret != -ERESTARTSYS && ret != -EINTR) 47362306a36Sopenharmony_ci pr_err("Buffer eviction failed\n"); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ciout: 47662306a36Sopenharmony_ci return ret; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/** 48062306a36Sopenharmony_ci * ttm_bo_eviction_valuable 48162306a36Sopenharmony_ci * 48262306a36Sopenharmony_ci * @bo: The buffer object to evict 48362306a36Sopenharmony_ci * @place: the placement we need to make room for 48462306a36Sopenharmony_ci * 48562306a36Sopenharmony_ci * Check if it is valuable to evict the BO to make room for the given placement. 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_cibool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, 48862306a36Sopenharmony_ci const struct ttm_place *place) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct ttm_resource *res = bo->resource; 49162306a36Sopenharmony_ci struct ttm_device *bdev = bo->bdev; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci dma_resv_assert_held(bo->base.resv); 49462306a36Sopenharmony_ci if (bo->resource->mem_type == TTM_PL_SYSTEM) 49562306a36Sopenharmony_ci return true; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* Don't evict this BO if it's outside of the 49862306a36Sopenharmony_ci * requested placement range 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci return ttm_resource_intersects(bdev, res, place, bo->base.size); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_eviction_valuable); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/* 50562306a36Sopenharmony_ci * Check the target bo is allowable to be evicted or swapout, including cases: 50662306a36Sopenharmony_ci * 50762306a36Sopenharmony_ci * a. if share same reservation object with ctx->resv, have assumption 50862306a36Sopenharmony_ci * reservation objects should already be locked, so not lock again and 50962306a36Sopenharmony_ci * return true directly when either the opreation allow_reserved_eviction 51062306a36Sopenharmony_ci * or the target bo already is in delayed free list; 51162306a36Sopenharmony_ci * 51262306a36Sopenharmony_ci * b. Otherwise, trylock it. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_cistatic bool ttm_bo_evict_swapout_allowable(struct ttm_buffer_object *bo, 51562306a36Sopenharmony_ci struct ttm_operation_ctx *ctx, 51662306a36Sopenharmony_ci const struct ttm_place *place, 51762306a36Sopenharmony_ci bool *locked, bool *busy) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci bool ret = false; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (bo->pin_count) { 52262306a36Sopenharmony_ci *locked = false; 52362306a36Sopenharmony_ci if (busy) 52462306a36Sopenharmony_ci *busy = false; 52562306a36Sopenharmony_ci return false; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (bo->base.resv == ctx->resv) { 52962306a36Sopenharmony_ci dma_resv_assert_held(bo->base.resv); 53062306a36Sopenharmony_ci if (ctx->allow_res_evict) 53162306a36Sopenharmony_ci ret = true; 53262306a36Sopenharmony_ci *locked = false; 53362306a36Sopenharmony_ci if (busy) 53462306a36Sopenharmony_ci *busy = false; 53562306a36Sopenharmony_ci } else { 53662306a36Sopenharmony_ci ret = dma_resv_trylock(bo->base.resv); 53762306a36Sopenharmony_ci *locked = ret; 53862306a36Sopenharmony_ci if (busy) 53962306a36Sopenharmony_ci *busy = !ret; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (ret && place && (bo->resource->mem_type != place->mem_type || 54362306a36Sopenharmony_ci !bo->bdev->funcs->eviction_valuable(bo, place))) { 54462306a36Sopenharmony_ci ret = false; 54562306a36Sopenharmony_ci if (*locked) { 54662306a36Sopenharmony_ci dma_resv_unlock(bo->base.resv); 54762306a36Sopenharmony_ci *locked = false; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return ret; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/** 55562306a36Sopenharmony_ci * ttm_mem_evict_wait_busy - wait for a busy BO to become available 55662306a36Sopenharmony_ci * 55762306a36Sopenharmony_ci * @busy_bo: BO which couldn't be locked with trylock 55862306a36Sopenharmony_ci * @ctx: operation context 55962306a36Sopenharmony_ci * @ticket: acquire ticket 56062306a36Sopenharmony_ci * 56162306a36Sopenharmony_ci * Try to lock a busy buffer object to avoid failing eviction. 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_cistatic int ttm_mem_evict_wait_busy(struct ttm_buffer_object *busy_bo, 56462306a36Sopenharmony_ci struct ttm_operation_ctx *ctx, 56562306a36Sopenharmony_ci struct ww_acquire_ctx *ticket) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci int r; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (!busy_bo || !ticket) 57062306a36Sopenharmony_ci return -EBUSY; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (ctx->interruptible) 57362306a36Sopenharmony_ci r = dma_resv_lock_interruptible(busy_bo->base.resv, 57462306a36Sopenharmony_ci ticket); 57562306a36Sopenharmony_ci else 57662306a36Sopenharmony_ci r = dma_resv_lock(busy_bo->base.resv, ticket); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * TODO: It would be better to keep the BO locked until allocation is at 58062306a36Sopenharmony_ci * least tried one more time, but that would mean a much larger rework 58162306a36Sopenharmony_ci * of TTM. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci if (!r) 58462306a36Sopenharmony_ci dma_resv_unlock(busy_bo->base.resv); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return r == -EDEADLK ? -EBUSY : r; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ciint ttm_mem_evict_first(struct ttm_device *bdev, 59062306a36Sopenharmony_ci struct ttm_resource_manager *man, 59162306a36Sopenharmony_ci const struct ttm_place *place, 59262306a36Sopenharmony_ci struct ttm_operation_ctx *ctx, 59362306a36Sopenharmony_ci struct ww_acquire_ctx *ticket) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct ttm_buffer_object *bo = NULL, *busy_bo = NULL; 59662306a36Sopenharmony_ci struct ttm_resource_cursor cursor; 59762306a36Sopenharmony_ci struct ttm_resource *res; 59862306a36Sopenharmony_ci bool locked = false; 59962306a36Sopenharmony_ci int ret; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci spin_lock(&bdev->lru_lock); 60262306a36Sopenharmony_ci ttm_resource_manager_for_each_res(man, &cursor, res) { 60362306a36Sopenharmony_ci bool busy; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (!ttm_bo_evict_swapout_allowable(res->bo, ctx, place, 60662306a36Sopenharmony_ci &locked, &busy)) { 60762306a36Sopenharmony_ci if (busy && !busy_bo && ticket != 60862306a36Sopenharmony_ci dma_resv_locking_ctx(res->bo->base.resv)) 60962306a36Sopenharmony_ci busy_bo = res->bo; 61062306a36Sopenharmony_ci continue; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (ttm_bo_get_unless_zero(res->bo)) { 61462306a36Sopenharmony_ci bo = res->bo; 61562306a36Sopenharmony_ci break; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci if (locked) 61862306a36Sopenharmony_ci dma_resv_unlock(res->bo->base.resv); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (!bo) { 62262306a36Sopenharmony_ci if (busy_bo && !ttm_bo_get_unless_zero(busy_bo)) 62362306a36Sopenharmony_ci busy_bo = NULL; 62462306a36Sopenharmony_ci spin_unlock(&bdev->lru_lock); 62562306a36Sopenharmony_ci ret = ttm_mem_evict_wait_busy(busy_bo, ctx, ticket); 62662306a36Sopenharmony_ci if (busy_bo) 62762306a36Sopenharmony_ci ttm_bo_put(busy_bo); 62862306a36Sopenharmony_ci return ret; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (bo->deleted) { 63262306a36Sopenharmony_ci ret = ttm_bo_cleanup_refs(bo, ctx->interruptible, 63362306a36Sopenharmony_ci ctx->no_wait_gpu, locked); 63462306a36Sopenharmony_ci ttm_bo_put(bo); 63562306a36Sopenharmony_ci return ret; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci spin_unlock(&bdev->lru_lock); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci ret = ttm_bo_evict(bo, ctx); 64162306a36Sopenharmony_ci if (locked) 64262306a36Sopenharmony_ci ttm_bo_unreserve(bo); 64362306a36Sopenharmony_ci else 64462306a36Sopenharmony_ci ttm_bo_move_to_lru_tail_unlocked(bo); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci ttm_bo_put(bo); 64762306a36Sopenharmony_ci return ret; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/** 65162306a36Sopenharmony_ci * ttm_bo_pin - Pin the buffer object. 65262306a36Sopenharmony_ci * @bo: The buffer object to pin 65362306a36Sopenharmony_ci * 65462306a36Sopenharmony_ci * Make sure the buffer is not evicted any more during memory pressure. 65562306a36Sopenharmony_ci * @bo must be unpinned again by calling ttm_bo_unpin(). 65662306a36Sopenharmony_ci */ 65762306a36Sopenharmony_civoid ttm_bo_pin(struct ttm_buffer_object *bo) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci dma_resv_assert_held(bo->base.resv); 66062306a36Sopenharmony_ci WARN_ON_ONCE(!kref_read(&bo->kref)); 66162306a36Sopenharmony_ci spin_lock(&bo->bdev->lru_lock); 66262306a36Sopenharmony_ci if (bo->resource) 66362306a36Sopenharmony_ci ttm_resource_del_bulk_move(bo->resource, bo); 66462306a36Sopenharmony_ci ++bo->pin_count; 66562306a36Sopenharmony_ci spin_unlock(&bo->bdev->lru_lock); 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_pin); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci/** 67062306a36Sopenharmony_ci * ttm_bo_unpin - Unpin the buffer object. 67162306a36Sopenharmony_ci * @bo: The buffer object to unpin 67262306a36Sopenharmony_ci * 67362306a36Sopenharmony_ci * Allows the buffer object to be evicted again during memory pressure. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_civoid ttm_bo_unpin(struct ttm_buffer_object *bo) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci dma_resv_assert_held(bo->base.resv); 67862306a36Sopenharmony_ci WARN_ON_ONCE(!kref_read(&bo->kref)); 67962306a36Sopenharmony_ci if (WARN_ON_ONCE(!bo->pin_count)) 68062306a36Sopenharmony_ci return; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci spin_lock(&bo->bdev->lru_lock); 68362306a36Sopenharmony_ci --bo->pin_count; 68462306a36Sopenharmony_ci if (bo->resource) 68562306a36Sopenharmony_ci ttm_resource_add_bulk_move(bo->resource, bo); 68662306a36Sopenharmony_ci spin_unlock(&bo->bdev->lru_lock); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_unpin); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci/* 69162306a36Sopenharmony_ci * Add the last move fence to the BO as kernel dependency and reserve a new 69262306a36Sopenharmony_ci * fence slot. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_cistatic int ttm_bo_add_move_fence(struct ttm_buffer_object *bo, 69562306a36Sopenharmony_ci struct ttm_resource_manager *man, 69662306a36Sopenharmony_ci struct ttm_resource *mem, 69762306a36Sopenharmony_ci bool no_wait_gpu) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct dma_fence *fence; 70062306a36Sopenharmony_ci int ret; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci spin_lock(&man->move_lock); 70362306a36Sopenharmony_ci fence = dma_fence_get(man->move); 70462306a36Sopenharmony_ci spin_unlock(&man->move_lock); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (!fence) 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (no_wait_gpu) { 71062306a36Sopenharmony_ci ret = dma_fence_is_signaled(fence) ? 0 : -EBUSY; 71162306a36Sopenharmony_ci dma_fence_put(fence); 71262306a36Sopenharmony_ci return ret; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci dma_resv_add_fence(bo->base.resv, fence, DMA_RESV_USAGE_KERNEL); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci ret = dma_resv_reserve_fences(bo->base.resv, 1); 71862306a36Sopenharmony_ci dma_fence_put(fence); 71962306a36Sopenharmony_ci return ret; 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci/* 72362306a36Sopenharmony_ci * Repeatedly evict memory from the LRU for @mem_type until we create enough 72462306a36Sopenharmony_ci * space, or we've evicted everything and there isn't enough space. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_cistatic int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, 72762306a36Sopenharmony_ci const struct ttm_place *place, 72862306a36Sopenharmony_ci struct ttm_resource **mem, 72962306a36Sopenharmony_ci struct ttm_operation_ctx *ctx) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci struct ttm_device *bdev = bo->bdev; 73262306a36Sopenharmony_ci struct ttm_resource_manager *man; 73362306a36Sopenharmony_ci struct ww_acquire_ctx *ticket; 73462306a36Sopenharmony_ci int ret; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci man = ttm_manager_type(bdev, place->mem_type); 73762306a36Sopenharmony_ci ticket = dma_resv_locking_ctx(bo->base.resv); 73862306a36Sopenharmony_ci do { 73962306a36Sopenharmony_ci ret = ttm_resource_alloc(bo, place, mem); 74062306a36Sopenharmony_ci if (likely(!ret)) 74162306a36Sopenharmony_ci break; 74262306a36Sopenharmony_ci if (unlikely(ret != -ENOSPC)) 74362306a36Sopenharmony_ci return ret; 74462306a36Sopenharmony_ci ret = ttm_mem_evict_first(bdev, man, place, ctx, 74562306a36Sopenharmony_ci ticket); 74662306a36Sopenharmony_ci if (unlikely(ret != 0)) 74762306a36Sopenharmony_ci return ret; 74862306a36Sopenharmony_ci } while (1); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci return ttm_bo_add_move_fence(bo, man, *mem, ctx->no_wait_gpu); 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci/** 75462306a36Sopenharmony_ci * ttm_bo_mem_space 75562306a36Sopenharmony_ci * 75662306a36Sopenharmony_ci * @bo: Pointer to a struct ttm_buffer_object. the data of which 75762306a36Sopenharmony_ci * we want to allocate space for. 75862306a36Sopenharmony_ci * @placement: Proposed new placement for the buffer object. 75962306a36Sopenharmony_ci * @mem: A struct ttm_resource. 76062306a36Sopenharmony_ci * @ctx: if and how to sleep, lock buffers and alloc memory 76162306a36Sopenharmony_ci * 76262306a36Sopenharmony_ci * Allocate memory space for the buffer object pointed to by @bo, using 76362306a36Sopenharmony_ci * the placement flags in @placement, potentially evicting other idle buffer objects. 76462306a36Sopenharmony_ci * This function may sleep while waiting for space to become available. 76562306a36Sopenharmony_ci * Returns: 76662306a36Sopenharmony_ci * -EBUSY: No space available (only if no_wait == 1). 76762306a36Sopenharmony_ci * -ENOMEM: Could not allocate memory for the buffer object, either due to 76862306a36Sopenharmony_ci * fragmentation or concurrent allocators. 76962306a36Sopenharmony_ci * -ERESTARTSYS: An interruptible sleep was interrupted by a signal. 77062306a36Sopenharmony_ci */ 77162306a36Sopenharmony_ciint ttm_bo_mem_space(struct ttm_buffer_object *bo, 77262306a36Sopenharmony_ci struct ttm_placement *placement, 77362306a36Sopenharmony_ci struct ttm_resource **mem, 77462306a36Sopenharmony_ci struct ttm_operation_ctx *ctx) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci struct ttm_device *bdev = bo->bdev; 77762306a36Sopenharmony_ci bool type_found = false; 77862306a36Sopenharmony_ci int i, ret; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci ret = dma_resv_reserve_fences(bo->base.resv, 1); 78162306a36Sopenharmony_ci if (unlikely(ret)) 78262306a36Sopenharmony_ci return ret; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci for (i = 0; i < placement->num_placement; ++i) { 78562306a36Sopenharmony_ci const struct ttm_place *place = &placement->placement[i]; 78662306a36Sopenharmony_ci struct ttm_resource_manager *man; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci man = ttm_manager_type(bdev, place->mem_type); 78962306a36Sopenharmony_ci if (!man || !ttm_resource_manager_used(man)) 79062306a36Sopenharmony_ci continue; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci type_found = true; 79362306a36Sopenharmony_ci ret = ttm_resource_alloc(bo, place, mem); 79462306a36Sopenharmony_ci if (ret == -ENOSPC) 79562306a36Sopenharmony_ci continue; 79662306a36Sopenharmony_ci if (unlikely(ret)) 79762306a36Sopenharmony_ci goto error; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci ret = ttm_bo_add_move_fence(bo, man, *mem, ctx->no_wait_gpu); 80062306a36Sopenharmony_ci if (unlikely(ret)) { 80162306a36Sopenharmony_ci ttm_resource_free(bo, mem); 80262306a36Sopenharmony_ci if (ret == -EBUSY) 80362306a36Sopenharmony_ci continue; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci goto error; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci return 0; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci for (i = 0; i < placement->num_busy_placement; ++i) { 81162306a36Sopenharmony_ci const struct ttm_place *place = &placement->busy_placement[i]; 81262306a36Sopenharmony_ci struct ttm_resource_manager *man; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci man = ttm_manager_type(bdev, place->mem_type); 81562306a36Sopenharmony_ci if (!man || !ttm_resource_manager_used(man)) 81662306a36Sopenharmony_ci continue; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci type_found = true; 81962306a36Sopenharmony_ci ret = ttm_bo_mem_force_space(bo, place, mem, ctx); 82062306a36Sopenharmony_ci if (likely(!ret)) 82162306a36Sopenharmony_ci return 0; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (ret && ret != -EBUSY) 82462306a36Sopenharmony_ci goto error; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci ret = -ENOMEM; 82862306a36Sopenharmony_ci if (!type_found) { 82962306a36Sopenharmony_ci pr_err(TTM_PFX "No compatible memory type found\n"); 83062306a36Sopenharmony_ci ret = -EINVAL; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cierror: 83462306a36Sopenharmony_ci return ret; 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_mem_space); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic int ttm_bo_move_buffer(struct ttm_buffer_object *bo, 83962306a36Sopenharmony_ci struct ttm_placement *placement, 84062306a36Sopenharmony_ci struct ttm_operation_ctx *ctx) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct ttm_resource *mem; 84362306a36Sopenharmony_ci struct ttm_place hop; 84462306a36Sopenharmony_ci int ret; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci dma_resv_assert_held(bo->base.resv); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci /* 84962306a36Sopenharmony_ci * Determine where to move the buffer. 85062306a36Sopenharmony_ci * 85162306a36Sopenharmony_ci * If driver determines move is going to need 85262306a36Sopenharmony_ci * an extra step then it will return -EMULTIHOP 85362306a36Sopenharmony_ci * and the buffer will be moved to the temporary 85462306a36Sopenharmony_ci * stop and the driver will be called to make 85562306a36Sopenharmony_ci * the second hop. 85662306a36Sopenharmony_ci */ 85762306a36Sopenharmony_ci ret = ttm_bo_mem_space(bo, placement, &mem, ctx); 85862306a36Sopenharmony_ci if (ret) 85962306a36Sopenharmony_ci return ret; 86062306a36Sopenharmony_cibounce: 86162306a36Sopenharmony_ci ret = ttm_bo_handle_move_mem(bo, mem, false, ctx, &hop); 86262306a36Sopenharmony_ci if (ret == -EMULTIHOP) { 86362306a36Sopenharmony_ci ret = ttm_bo_bounce_temp_buffer(bo, &mem, ctx, &hop); 86462306a36Sopenharmony_ci if (ret) 86562306a36Sopenharmony_ci goto out; 86662306a36Sopenharmony_ci /* try and move to final place now. */ 86762306a36Sopenharmony_ci goto bounce; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ciout: 87062306a36Sopenharmony_ci if (ret) 87162306a36Sopenharmony_ci ttm_resource_free(bo, &mem); 87262306a36Sopenharmony_ci return ret; 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci/** 87662306a36Sopenharmony_ci * ttm_bo_validate 87762306a36Sopenharmony_ci * 87862306a36Sopenharmony_ci * @bo: The buffer object. 87962306a36Sopenharmony_ci * @placement: Proposed placement for the buffer object. 88062306a36Sopenharmony_ci * @ctx: validation parameters. 88162306a36Sopenharmony_ci * 88262306a36Sopenharmony_ci * Changes placement and caching policy of the buffer object 88362306a36Sopenharmony_ci * according proposed placement. 88462306a36Sopenharmony_ci * Returns 88562306a36Sopenharmony_ci * -EINVAL on invalid proposed placement. 88662306a36Sopenharmony_ci * -ENOMEM on out-of-memory condition. 88762306a36Sopenharmony_ci * -EBUSY if no_wait is true and buffer busy. 88862306a36Sopenharmony_ci * -ERESTARTSYS if interrupted by a signal. 88962306a36Sopenharmony_ci */ 89062306a36Sopenharmony_ciint ttm_bo_validate(struct ttm_buffer_object *bo, 89162306a36Sopenharmony_ci struct ttm_placement *placement, 89262306a36Sopenharmony_ci struct ttm_operation_ctx *ctx) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci int ret; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci dma_resv_assert_held(bo->base.resv); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci /* 89962306a36Sopenharmony_ci * Remove the backing store if no placement is given. 90062306a36Sopenharmony_ci */ 90162306a36Sopenharmony_ci if (!placement->num_placement && !placement->num_busy_placement) 90262306a36Sopenharmony_ci return ttm_bo_pipeline_gutting(bo); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* Check whether we need to move buffer. */ 90562306a36Sopenharmony_ci if (bo->resource && ttm_resource_compat(bo->resource, placement)) 90662306a36Sopenharmony_ci return 0; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* Moving of pinned BOs is forbidden */ 90962306a36Sopenharmony_ci if (bo->pin_count) 91062306a36Sopenharmony_ci return -EINVAL; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci ret = ttm_bo_move_buffer(bo, placement, ctx); 91362306a36Sopenharmony_ci if (ret) 91462306a36Sopenharmony_ci return ret; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* 91762306a36Sopenharmony_ci * We might need to add a TTM. 91862306a36Sopenharmony_ci */ 91962306a36Sopenharmony_ci if (!bo->resource || bo->resource->mem_type == TTM_PL_SYSTEM) { 92062306a36Sopenharmony_ci ret = ttm_tt_create(bo, true); 92162306a36Sopenharmony_ci if (ret) 92262306a36Sopenharmony_ci return ret; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci return 0; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_validate); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci/** 92962306a36Sopenharmony_ci * ttm_bo_init_reserved 93062306a36Sopenharmony_ci * 93162306a36Sopenharmony_ci * @bdev: Pointer to a ttm_device struct. 93262306a36Sopenharmony_ci * @bo: Pointer to a ttm_buffer_object to be initialized. 93362306a36Sopenharmony_ci * @type: Requested type of buffer object. 93462306a36Sopenharmony_ci * @placement: Initial placement for buffer object. 93562306a36Sopenharmony_ci * @alignment: Data alignment in pages. 93662306a36Sopenharmony_ci * @ctx: TTM operation context for memory allocation. 93762306a36Sopenharmony_ci * @sg: Scatter-gather table. 93862306a36Sopenharmony_ci * @resv: Pointer to a dma_resv, or NULL to let ttm allocate one. 93962306a36Sopenharmony_ci * @destroy: Destroy function. Use NULL for kfree(). 94062306a36Sopenharmony_ci * 94162306a36Sopenharmony_ci * This function initializes a pre-allocated struct ttm_buffer_object. 94262306a36Sopenharmony_ci * As this object may be part of a larger structure, this function, 94362306a36Sopenharmony_ci * together with the @destroy function, enables driver-specific objects 94462306a36Sopenharmony_ci * derived from a ttm_buffer_object. 94562306a36Sopenharmony_ci * 94662306a36Sopenharmony_ci * On successful return, the caller owns an object kref to @bo. The kref and 94762306a36Sopenharmony_ci * list_kref are usually set to 1, but note that in some situations, other 94862306a36Sopenharmony_ci * tasks may already be holding references to @bo as well. 94962306a36Sopenharmony_ci * Furthermore, if resv == NULL, the buffer's reservation lock will be held, 95062306a36Sopenharmony_ci * and it is the caller's responsibility to call ttm_bo_unreserve. 95162306a36Sopenharmony_ci * 95262306a36Sopenharmony_ci * If a failure occurs, the function will call the @destroy function. Thus, 95362306a36Sopenharmony_ci * after a failure, dereferencing @bo is illegal and will likely cause memory 95462306a36Sopenharmony_ci * corruption. 95562306a36Sopenharmony_ci * 95662306a36Sopenharmony_ci * Returns 95762306a36Sopenharmony_ci * -ENOMEM: Out of memory. 95862306a36Sopenharmony_ci * -EINVAL: Invalid placement flags. 95962306a36Sopenharmony_ci * -ERESTARTSYS: Interrupted by signal while sleeping waiting for resources. 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ciint ttm_bo_init_reserved(struct ttm_device *bdev, struct ttm_buffer_object *bo, 96262306a36Sopenharmony_ci enum ttm_bo_type type, struct ttm_placement *placement, 96362306a36Sopenharmony_ci uint32_t alignment, struct ttm_operation_ctx *ctx, 96462306a36Sopenharmony_ci struct sg_table *sg, struct dma_resv *resv, 96562306a36Sopenharmony_ci void (*destroy) (struct ttm_buffer_object *)) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci int ret; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci kref_init(&bo->kref); 97062306a36Sopenharmony_ci bo->bdev = bdev; 97162306a36Sopenharmony_ci bo->type = type; 97262306a36Sopenharmony_ci bo->page_alignment = alignment; 97362306a36Sopenharmony_ci bo->destroy = destroy; 97462306a36Sopenharmony_ci bo->pin_count = 0; 97562306a36Sopenharmony_ci bo->sg = sg; 97662306a36Sopenharmony_ci bo->bulk_move = NULL; 97762306a36Sopenharmony_ci if (resv) 97862306a36Sopenharmony_ci bo->base.resv = resv; 97962306a36Sopenharmony_ci else 98062306a36Sopenharmony_ci bo->base.resv = &bo->base._resv; 98162306a36Sopenharmony_ci atomic_inc(&ttm_glob.bo_count); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* 98462306a36Sopenharmony_ci * For ttm_bo_type_device buffers, allocate 98562306a36Sopenharmony_ci * address space from the device. 98662306a36Sopenharmony_ci */ 98762306a36Sopenharmony_ci if (bo->type == ttm_bo_type_device || bo->type == ttm_bo_type_sg) { 98862306a36Sopenharmony_ci ret = drm_vma_offset_add(bdev->vma_manager, &bo->base.vma_node, 98962306a36Sopenharmony_ci PFN_UP(bo->base.size)); 99062306a36Sopenharmony_ci if (ret) 99162306a36Sopenharmony_ci goto err_put; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci /* passed reservation objects should already be locked, 99562306a36Sopenharmony_ci * since otherwise lockdep will be angered in radeon. 99662306a36Sopenharmony_ci */ 99762306a36Sopenharmony_ci if (!resv) 99862306a36Sopenharmony_ci WARN_ON(!dma_resv_trylock(bo->base.resv)); 99962306a36Sopenharmony_ci else 100062306a36Sopenharmony_ci dma_resv_assert_held(resv); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci ret = ttm_bo_validate(bo, placement, ctx); 100362306a36Sopenharmony_ci if (unlikely(ret)) 100462306a36Sopenharmony_ci goto err_unlock; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci return 0; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cierr_unlock: 100962306a36Sopenharmony_ci if (!resv) 101062306a36Sopenharmony_ci dma_resv_unlock(bo->base.resv); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cierr_put: 101362306a36Sopenharmony_ci ttm_bo_put(bo); 101462306a36Sopenharmony_ci return ret; 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_init_reserved); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci/** 101962306a36Sopenharmony_ci * ttm_bo_init_validate 102062306a36Sopenharmony_ci * 102162306a36Sopenharmony_ci * @bdev: Pointer to a ttm_device struct. 102262306a36Sopenharmony_ci * @bo: Pointer to a ttm_buffer_object to be initialized. 102362306a36Sopenharmony_ci * @type: Requested type of buffer object. 102462306a36Sopenharmony_ci * @placement: Initial placement for buffer object. 102562306a36Sopenharmony_ci * @alignment: Data alignment in pages. 102662306a36Sopenharmony_ci * @interruptible: If needing to sleep to wait for GPU resources, 102762306a36Sopenharmony_ci * sleep interruptible. 102862306a36Sopenharmony_ci * pinned in physical memory. If this behaviour is not desired, this member 102962306a36Sopenharmony_ci * holds a pointer to a persistent shmem object. Typically, this would 103062306a36Sopenharmony_ci * point to the shmem object backing a GEM object if TTM is used to back a 103162306a36Sopenharmony_ci * GEM user interface. 103262306a36Sopenharmony_ci * @sg: Scatter-gather table. 103362306a36Sopenharmony_ci * @resv: Pointer to a dma_resv, or NULL to let ttm allocate one. 103462306a36Sopenharmony_ci * @destroy: Destroy function. Use NULL for kfree(). 103562306a36Sopenharmony_ci * 103662306a36Sopenharmony_ci * This function initializes a pre-allocated struct ttm_buffer_object. 103762306a36Sopenharmony_ci * As this object may be part of a larger structure, this function, 103862306a36Sopenharmony_ci * together with the @destroy function, 103962306a36Sopenharmony_ci * enables driver-specific objects derived from a ttm_buffer_object. 104062306a36Sopenharmony_ci * 104162306a36Sopenharmony_ci * On successful return, the caller owns an object kref to @bo. The kref and 104262306a36Sopenharmony_ci * list_kref are usually set to 1, but note that in some situations, other 104362306a36Sopenharmony_ci * tasks may already be holding references to @bo as well. 104462306a36Sopenharmony_ci * 104562306a36Sopenharmony_ci * If a failure occurs, the function will call the @destroy function, Thus, 104662306a36Sopenharmony_ci * after a failure, dereferencing @bo is illegal and will likely cause memory 104762306a36Sopenharmony_ci * corruption. 104862306a36Sopenharmony_ci * 104962306a36Sopenharmony_ci * Returns 105062306a36Sopenharmony_ci * -ENOMEM: Out of memory. 105162306a36Sopenharmony_ci * -EINVAL: Invalid placement flags. 105262306a36Sopenharmony_ci * -ERESTARTSYS: Interrupted by signal while sleeping waiting for resources. 105362306a36Sopenharmony_ci */ 105462306a36Sopenharmony_ciint ttm_bo_init_validate(struct ttm_device *bdev, struct ttm_buffer_object *bo, 105562306a36Sopenharmony_ci enum ttm_bo_type type, struct ttm_placement *placement, 105662306a36Sopenharmony_ci uint32_t alignment, bool interruptible, 105762306a36Sopenharmony_ci struct sg_table *sg, struct dma_resv *resv, 105862306a36Sopenharmony_ci void (*destroy) (struct ttm_buffer_object *)) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci struct ttm_operation_ctx ctx = { interruptible, false }; 106162306a36Sopenharmony_ci int ret; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci ret = ttm_bo_init_reserved(bdev, bo, type, placement, alignment, &ctx, 106462306a36Sopenharmony_ci sg, resv, destroy); 106562306a36Sopenharmony_ci if (ret) 106662306a36Sopenharmony_ci return ret; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci if (!resv) 106962306a36Sopenharmony_ci ttm_bo_unreserve(bo); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci return 0; 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_init_validate); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci/* 107662306a36Sopenharmony_ci * buffer object vm functions. 107762306a36Sopenharmony_ci */ 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci/** 108062306a36Sopenharmony_ci * ttm_bo_unmap_virtual 108162306a36Sopenharmony_ci * 108262306a36Sopenharmony_ci * @bo: tear down the virtual mappings for this BO 108362306a36Sopenharmony_ci */ 108462306a36Sopenharmony_civoid ttm_bo_unmap_virtual(struct ttm_buffer_object *bo) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct ttm_device *bdev = bo->bdev; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci drm_vma_node_unmap(&bo->base.vma_node, bdev->dev_mapping); 108962306a36Sopenharmony_ci ttm_mem_io_free(bdev, bo->resource); 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_unmap_virtual); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci/** 109462306a36Sopenharmony_ci * ttm_bo_wait_ctx - wait for buffer idle. 109562306a36Sopenharmony_ci * 109662306a36Sopenharmony_ci * @bo: The buffer object. 109762306a36Sopenharmony_ci * @ctx: defines how to wait 109862306a36Sopenharmony_ci * 109962306a36Sopenharmony_ci * Waits for the buffer to be idle. Used timeout depends on the context. 110062306a36Sopenharmony_ci * Returns -EBUSY if wait timed outt, -ERESTARTSYS if interrupted by a signal or 110162306a36Sopenharmony_ci * zero on success. 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_ciint ttm_bo_wait_ctx(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci long ret; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci if (ctx->no_wait_gpu) { 110862306a36Sopenharmony_ci if (dma_resv_test_signaled(bo->base.resv, 110962306a36Sopenharmony_ci DMA_RESV_USAGE_BOOKKEEP)) 111062306a36Sopenharmony_ci return 0; 111162306a36Sopenharmony_ci else 111262306a36Sopenharmony_ci return -EBUSY; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci ret = dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP, 111662306a36Sopenharmony_ci ctx->interruptible, 15 * HZ); 111762306a36Sopenharmony_ci if (unlikely(ret < 0)) 111862306a36Sopenharmony_ci return ret; 111962306a36Sopenharmony_ci if (unlikely(ret == 0)) 112062306a36Sopenharmony_ci return -EBUSY; 112162306a36Sopenharmony_ci return 0; 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_wait_ctx); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ciint ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, 112662306a36Sopenharmony_ci gfp_t gfp_flags) 112762306a36Sopenharmony_ci{ 112862306a36Sopenharmony_ci struct ttm_place place; 112962306a36Sopenharmony_ci bool locked; 113062306a36Sopenharmony_ci long ret; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci /* 113362306a36Sopenharmony_ci * While the bo may already reside in SYSTEM placement, set 113462306a36Sopenharmony_ci * SYSTEM as new placement to cover also the move further below. 113562306a36Sopenharmony_ci * The driver may use the fact that we're moving from SYSTEM 113662306a36Sopenharmony_ci * as an indication that we're about to swap out. 113762306a36Sopenharmony_ci */ 113862306a36Sopenharmony_ci memset(&place, 0, sizeof(place)); 113962306a36Sopenharmony_ci place.mem_type = bo->resource->mem_type; 114062306a36Sopenharmony_ci if (!ttm_bo_evict_swapout_allowable(bo, ctx, &place, &locked, NULL)) 114162306a36Sopenharmony_ci return -EBUSY; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (!bo->ttm || !ttm_tt_is_populated(bo->ttm) || 114462306a36Sopenharmony_ci bo->ttm->page_flags & TTM_TT_FLAG_EXTERNAL || 114562306a36Sopenharmony_ci bo->ttm->page_flags & TTM_TT_FLAG_SWAPPED || 114662306a36Sopenharmony_ci !ttm_bo_get_unless_zero(bo)) { 114762306a36Sopenharmony_ci if (locked) 114862306a36Sopenharmony_ci dma_resv_unlock(bo->base.resv); 114962306a36Sopenharmony_ci return -EBUSY; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (bo->deleted) { 115362306a36Sopenharmony_ci ret = ttm_bo_cleanup_refs(bo, false, false, locked); 115462306a36Sopenharmony_ci ttm_bo_put(bo); 115562306a36Sopenharmony_ci return ret == -EBUSY ? -ENOSPC : ret; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* TODO: Cleanup the locking */ 115962306a36Sopenharmony_ci spin_unlock(&bo->bdev->lru_lock); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci /* 116262306a36Sopenharmony_ci * Move to system cached 116362306a36Sopenharmony_ci */ 116462306a36Sopenharmony_ci if (bo->resource->mem_type != TTM_PL_SYSTEM) { 116562306a36Sopenharmony_ci struct ttm_resource *evict_mem; 116662306a36Sopenharmony_ci struct ttm_place hop; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci memset(&hop, 0, sizeof(hop)); 116962306a36Sopenharmony_ci place.mem_type = TTM_PL_SYSTEM; 117062306a36Sopenharmony_ci ret = ttm_resource_alloc(bo, &place, &evict_mem); 117162306a36Sopenharmony_ci if (unlikely(ret)) 117262306a36Sopenharmony_ci goto out; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci ret = ttm_bo_handle_move_mem(bo, evict_mem, true, ctx, &hop); 117562306a36Sopenharmony_ci if (unlikely(ret != 0)) { 117662306a36Sopenharmony_ci WARN(ret == -EMULTIHOP, "Unexpected multihop in swaput - likely driver bug.\n"); 117762306a36Sopenharmony_ci ttm_resource_free(bo, &evict_mem); 117862306a36Sopenharmony_ci goto out; 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci /* 118362306a36Sopenharmony_ci * Make sure BO is idle. 118462306a36Sopenharmony_ci */ 118562306a36Sopenharmony_ci ret = ttm_bo_wait_ctx(bo, ctx); 118662306a36Sopenharmony_ci if (unlikely(ret != 0)) 118762306a36Sopenharmony_ci goto out; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci ttm_bo_unmap_virtual(bo); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci /* 119262306a36Sopenharmony_ci * Swap out. Buffer will be swapped in again as soon as 119362306a36Sopenharmony_ci * anyone tries to access a ttm page. 119462306a36Sopenharmony_ci */ 119562306a36Sopenharmony_ci if (bo->bdev->funcs->swap_notify) 119662306a36Sopenharmony_ci bo->bdev->funcs->swap_notify(bo); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci if (ttm_tt_is_populated(bo->ttm)) 119962306a36Sopenharmony_ci ret = ttm_tt_swapout(bo->bdev, bo->ttm, gfp_flags); 120062306a36Sopenharmony_ciout: 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci /* 120362306a36Sopenharmony_ci * Unreserve without putting on LRU to avoid swapping out an 120462306a36Sopenharmony_ci * already swapped buffer. 120562306a36Sopenharmony_ci */ 120662306a36Sopenharmony_ci if (locked) 120762306a36Sopenharmony_ci dma_resv_unlock(bo->base.resv); 120862306a36Sopenharmony_ci ttm_bo_put(bo); 120962306a36Sopenharmony_ci return ret == -EBUSY ? -ENOSPC : ret; 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_civoid ttm_bo_tt_destroy(struct ttm_buffer_object *bo) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci if (bo->ttm == NULL) 121562306a36Sopenharmony_ci return; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci ttm_tt_unpopulate(bo->bdev, bo->ttm); 121862306a36Sopenharmony_ci ttm_tt_destroy(bo->bdev, bo->ttm); 121962306a36Sopenharmony_ci bo->ttm = NULL; 122062306a36Sopenharmony_ci} 1221