On Tue, Jun 18, 2024 at 09:18:14AM +0200, Thomas Hellström wrote: > Rework the TTM swapping to use the LRU walker helper. > This helps fixing up the ttm_bo_swapout() interface > to be consistent about not requiring any locking. > > For now mimic the current behaviour of using trylock > only. We could be using ticket-locks here but defer > that until it's deemed necessary. The TTM swapout > functionality is a bit weird anyway since it > alternates between memory types without exhausting > TTM_PL_SYSTEM first. > > Cc: Christian König <christian.koe...@amd.com> > Cc: Somalapuram Amaranath <amaranath.somalapu...@amd.com> > Cc: Matthew Brost <matthew.br...@intel.com> > Cc: <dri-devel@lists.freedesktop.org> > Signed-off-by: Thomas Hellström <thomas.hellst...@linux.intel.com> > --- > drivers/gpu/drm/ttm/ttm_bo.c | 112 +++++++++++++++++++++---------- > drivers/gpu/drm/ttm/ttm_device.c | 30 ++------- > include/drm/ttm/ttm_bo.h | 5 +- > 3 files changed, 83 insertions(+), 64 deletions(-) > > diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c > index 43eda720657f..63a91b77f7da 100644 > --- a/drivers/gpu/drm/ttm/ttm_bo.c > +++ b/drivers/gpu/drm/ttm/ttm_bo.c > @@ -1118,11 +1118,23 @@ int ttm_bo_wait_ctx(struct ttm_buffer_object *bo, > struct ttm_operation_ctx *ctx) > } > EXPORT_SYMBOL(ttm_bo_wait_ctx); > > -int ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx > *ctx, > - gfp_t gfp_flags) > +/** > + * struct ttm_bo_swapout_walk - Parameters for the swapout walk > + */ > +struct ttm_bo_swapout_walk { > + /** @walk: The walk base parameters. */ > + struct ttm_lru_walk walk; > + /** @gfp_flags: The gfp flags to use for ttm_tt_swapout() */ > + gfp_t gfp_flags; > +}; > + > +static long > +ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo) > { > - struct ttm_place place; > - bool locked; > + struct ttm_place place = {.mem_type = bo->resource->mem_type}; > + struct ttm_bo_swapout_walk *swapout_walk = > + container_of(walk, typeof(*swapout_walk), walk); > + struct ttm_operation_ctx *ctx = walk->ctx; > long ret; > > /* > @@ -1131,28 +1143,29 @@ int ttm_bo_swapout(struct ttm_buffer_object *bo, > struct ttm_operation_ctx *ctx, > * The driver may use the fact that we're moving from SYSTEM > * as an indication that we're about to swap out. > */ > - memset(&place, 0, sizeof(place)); > - place.mem_type = bo->resource->mem_type; > - if (!ttm_bo_evict_swapout_allowable(bo, ctx, &place, &locked, NULL)) > - return -EBUSY; > + if (!bo->bdev->funcs->eviction_valuable(bo, &place)) { > + ret = -EBUSY; > + goto out; > + } > > if (!bo->ttm || !ttm_tt_is_populated(bo->ttm) || > bo->ttm->page_flags & TTM_TT_FLAG_EXTERNAL || > - bo->ttm->page_flags & TTM_TT_FLAG_SWAPPED || > - !ttm_bo_get_unless_zero(bo)) { > - if (locked) > - dma_resv_unlock(bo->base.resv); > - return -EBUSY; > + bo->ttm->page_flags & TTM_TT_FLAG_SWAPPED) { > + ret = -EBUSY;
I think answers my -EBUSY question from here [1]. In these cases we continue LRU walk as eviction of the BO is not valuable. [1] https://patchwork.freedesktop.org/patch/599606/?series=131815&rev=5#comment_1091419 > + goto out; > } > > if (bo->deleted) { > - ret = ttm_bo_cleanup_refs(bo, false, false, locked); > - ttm_bo_put(bo); > - return ret == -EBUSY ? -ENOSPC : ret; > - } > + pgoff_t num_pages = bo->ttm->num_pages; > > - /* TODO: Cleanup the locking */ > - spin_unlock(&bo->bdev->lru_lock); > + ret = ttm_bo_wait_ctx(bo, ctx); > + if (ret) > + goto out; > + > + ttm_bo_cleanup_memtype_use(bo); > + ret = num_pages; > + goto out; > + } > > /* > * Move to system cached > @@ -1164,12 +1177,13 @@ int ttm_bo_swapout(struct ttm_buffer_object *bo, > struct ttm_operation_ctx *ctx, > memset(&hop, 0, sizeof(hop)); > place.mem_type = TTM_PL_SYSTEM; > ret = ttm_resource_alloc(bo, &place, &evict_mem); > - if (unlikely(ret)) > + if (ret) > goto out; > > ret = ttm_bo_handle_move_mem(bo, evict_mem, true, ctx, &hop); > - if (unlikely(ret != 0)) { > - WARN(ret == -EMULTIHOP, "Unexpected multihop in swaput > - likely driver bug.\n"); > + if (ret) { > + WARN(ret == -EMULTIHOP, > + "Unexpected multihop in swapout - likely driver > bug.\n"); > ttm_resource_free(bo, &evict_mem); > goto out; > } > @@ -1179,30 +1193,54 @@ int ttm_bo_swapout(struct ttm_buffer_object *bo, > struct ttm_operation_ctx *ctx, > * Make sure BO is idle. > */ > ret = ttm_bo_wait_ctx(bo, ctx); > - if (unlikely(ret != 0)) > + if (ret) > goto out; > > ttm_bo_unmap_virtual(bo); > - > - /* > - * Swap out. Buffer will be swapped in again as soon as > - * anyone tries to access a ttm page. > - */ > if (bo->bdev->funcs->swap_notify) > bo->bdev->funcs->swap_notify(bo); > > if (ttm_tt_is_populated(bo->ttm)) > - ret = ttm_tt_swapout(bo->bdev, bo->ttm, gfp_flags); > + ret = ttm_tt_swapout(bo->bdev, bo->ttm, > swapout_walk->gfp_flags); > out: > + /* Consider some error codes fatal. Others may continue the walk. */ > + if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS || > + ret == -EAGAIN || ret > 0) > + return ret; Would it be more robust / clear to do the inverse of this? i.e. Return 0 on non-fatal error codes? > > - /* > - * Unreserve without putting on LRU to avoid swapping out an > - * already swapped buffer. > - */ > - if (locked) > - dma_resv_unlock(bo->base.resv); > - ttm_bo_put(bo); > - return ret == -EBUSY ? -ENOSPC : ret; > + return 0; > +} > + > +const struct ttm_lru_walk_ops ttm_swap_ops = { > + .process_bo = ttm_bo_swapout_cb, > +}; > + > +/** > + * ttm_bo_swapout() - Swap out buffer objects on the LRU list to shmem. > + * @bdev: The ttm device. > + * @ctx: The ttm_operation_ctx governing the swapout operation. > + * @man: The resource manager whose resources / buffer objects are > + * goint to be swapped out. > + * @gfp_flags: The gfp flags used for shmem page allocations. > + * @target: The desired number of pages to swap out. > + * > + * Return: The number of pages actually swapped out, or negative error code > + * on error. > + */ > +long ttm_bo_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, > + struct ttm_resource_manager *man, gfp_t gfp_flags, > + pgoff_t target) > +{ > + struct ttm_bo_swapout_walk swapout_walk = { > + .walk = { > + .ops = &ttm_swap_ops, > + .ctx = ctx, > + .trylock_only = true, > + }, > + .gfp_flags = gfp_flags, > + }; > + > + return ttm_lru_walk_for_evict(&swapout_walk.walk, bdev, man, target); > } > > void ttm_bo_tt_destroy(struct ttm_buffer_object *bo) > diff --git a/drivers/gpu/drm/ttm/ttm_device.c > b/drivers/gpu/drm/ttm/ttm_device.c > index f9e9b1ec8c8a..ee575d8a54c0 100644 > --- a/drivers/gpu/drm/ttm/ttm_device.c > +++ b/drivers/gpu/drm/ttm/ttm_device.c > @@ -148,40 +148,20 @@ int ttm_global_swapout(struct ttm_operation_ctx *ctx, > gfp_t gfp_flags) > int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx > *ctx, > gfp_t gfp_flags) > { > - struct ttm_resource_cursor cursor; > struct ttm_resource_manager *man; > - struct ttm_resource *res; > unsigned i; > - int ret; > + long lret; > > - spin_lock(&bdev->lru_lock); > for (i = TTM_PL_SYSTEM; i < TTM_NUM_MEM_TYPES; ++i) { > man = ttm_manager_type(bdev, i); > if (!man || !man->use_tt) > continue; > > - ttm_resource_manager_for_each_res(man, &cursor, res) { > - struct ttm_buffer_object *bo = res->bo; > - uint32_t num_pages; > - > - if (!bo || bo->resource != res) > - continue; > - > - num_pages = PFN_UP(bo->base.size); > - ret = ttm_bo_swapout(bo, ctx, gfp_flags); > - /* ttm_bo_swapout has dropped the lru_lock */ > - if (!ret) { > - ttm_resource_cursor_fini(&cursor); > - return num_pages; > - } > - if (ret != -EBUSY) { > - ttm_resource_cursor_fini(&cursor); > - return ret; > - } > - } > + lret = ttm_bo_swapout(bdev, ctx, man, gfp_flags, 1); With a target of 1 this will evict exactly 1 various sized BO which seems to match the current behavior. Just curious what is the usage of this function which evicts 1 BO from the device? Matt > + /* Can be both positive (num_pages) and negative (error) */ > + if (lret) > + return lret; > } > - ttm_resource_cursor_fini_locked(&cursor); > - spin_unlock(&bdev->lru_lock); > return 0; > } > EXPORT_SYMBOL(ttm_device_swapout); > diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h > index 8b032298d66e..472a55b69afb 100644 > --- a/include/drm/ttm/ttm_bo.h > +++ b/include/drm/ttm/ttm_bo.h > @@ -410,8 +410,9 @@ void ttm_bo_kunmap(struct ttm_bo_kmap_obj *map); > int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map); > void ttm_bo_vunmap(struct ttm_buffer_object *bo, struct iosys_map *map); > int ttm_bo_mmap_obj(struct vm_area_struct *vma, struct ttm_buffer_object > *bo); > -int ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx > *ctx, > - gfp_t gfp_flags); > +long ttm_bo_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, > + struct ttm_resource_manager *man, gfp_t gfp_flags, > + pgoff_t target); > void ttm_bo_pin(struct ttm_buffer_object *bo); > void ttm_bo_unpin(struct ttm_buffer_object *bo); > int ttm_mem_evict_first(struct ttm_device *bdev, > -- > 2.44.0 >