Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/ttm: Add a macro to perform LRU iteration

Following the design direction communicated here:

https://lore.kernel.org/linux-mm/b7491378-defd-4f1c-31e2-29e4c77e2d67@amd.com/T/#ma918844aa8a6efe8768fdcda0c6590d5c93850c9

Export a LRU walker for driver shrinker use. The walker
initially supports only trylocking, since that's the
method used by shrinkes. The walker makes use of
scoped_guard() to allow exiting from the LRU walk loop
without performing any explicit unlocking or
cleanup.

v8:
- Split out from another patch.
- Use a struct for bool arguments to increase readability (Matt Brost).
- Unmap user-space cpu-mappings before shrinking pages.
- Explain non-fatal error codes (Matt Brost)

v10:
- Instead of using the existing helper, Wrap the interface inside out and
provide a loop to de-midlayer things the LRU iteration (Christian König).
- Removing the R-B by Matt Brost since the patch was significantly changed.

v11:
- Split the patch up to include just the LRU walk helper.

v12:
- Indent after scoped_guard() (Matt Brost)

v15:
- Adapt to new definition of scoped_guard()

Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Reviewed-by: Matthew Brost <matthew.brost@intel.com>
Acked-by: Dave Airlie <airlied@redhat.com>
Acked-by: Christian König <christian.koenig@amd.com>
Link: https://lore.kernel.org/intel-xe/20250305092220.123405-5-thomas.hellstrom@linux.intel.com

+208 -4
+136 -4
drivers/gpu/drm/ttm/ttm_bo_util.c
··· 769 769 return ret; 770 770 } 771 771 772 - static bool ttm_lru_walk_trylock(struct ttm_lru_walk *walk, 772 + static bool ttm_lru_walk_trylock(struct ttm_operation_ctx *ctx, 773 773 struct ttm_buffer_object *bo, 774 774 bool *needs_unlock) 775 775 { 776 - struct ttm_operation_ctx *ctx = walk->ctx; 777 - 778 776 *needs_unlock = false; 779 777 780 778 if (dma_resv_trylock(bo->base.resv)) { ··· 875 877 * since if we do it the other way around, and the trylock fails, 876 878 * we need to drop the lru lock to put the bo. 877 879 */ 878 - if (ttm_lru_walk_trylock(walk, bo, &bo_needs_unlock)) 880 + if (ttm_lru_walk_trylock(walk->ctx, bo, &bo_needs_unlock)) 879 881 bo_locked = true; 880 882 else if (!walk->ticket || walk->ctx->no_wait_gpu || 881 883 walk->trylock_only) ··· 918 920 919 921 return progress; 920 922 } 923 + EXPORT_SYMBOL(ttm_lru_walk_for_evict); 924 + 925 + static void ttm_bo_lru_cursor_cleanup_bo(struct ttm_bo_lru_cursor *curs) 926 + { 927 + struct ttm_buffer_object *bo = curs->bo; 928 + 929 + if (bo) { 930 + if (curs->needs_unlock) 931 + dma_resv_unlock(bo->base.resv); 932 + ttm_bo_put(bo); 933 + curs->bo = NULL; 934 + } 935 + } 936 + 937 + /** 938 + * ttm_bo_lru_cursor_fini() - Stop using a struct ttm_bo_lru_cursor 939 + * and clean up any iteration it was used for. 940 + * @curs: The cursor. 941 + */ 942 + void ttm_bo_lru_cursor_fini(struct ttm_bo_lru_cursor *curs) 943 + { 944 + spinlock_t *lru_lock = &curs->res_curs.man->bdev->lru_lock; 945 + 946 + ttm_bo_lru_cursor_cleanup_bo(curs); 947 + spin_lock(lru_lock); 948 + ttm_resource_cursor_fini(&curs->res_curs); 949 + spin_unlock(lru_lock); 950 + } 951 + EXPORT_SYMBOL(ttm_bo_lru_cursor_fini); 952 + 953 + /** 954 + * ttm_bo_lru_cursor_init() - Initialize a struct ttm_bo_lru_cursor 955 + * @curs: The ttm_bo_lru_cursor to initialize. 956 + * @man: The ttm resource_manager whose LRU lists to iterate over. 957 + * @ctx: The ttm_operation_ctx to govern the locking. 958 + * 959 + * Initialize a struct ttm_bo_lru_cursor. Currently only trylocking 960 + * or prelocked buffer objects are available as detailed by 961 + * @ctx::resv and @ctx::allow_res_evict. Ticketlocking is not 962 + * supported. 963 + * 964 + * Return: Pointer to @curs. The function does not fail. 965 + */ 966 + struct ttm_bo_lru_cursor * 967 + ttm_bo_lru_cursor_init(struct ttm_bo_lru_cursor *curs, 968 + struct ttm_resource_manager *man, 969 + struct ttm_operation_ctx *ctx) 970 + { 971 + memset(curs, 0, sizeof(*curs)); 972 + ttm_resource_cursor_init(&curs->res_curs, man); 973 + curs->ctx = ctx; 974 + 975 + return curs; 976 + } 977 + EXPORT_SYMBOL(ttm_bo_lru_cursor_init); 978 + 979 + static struct ttm_buffer_object * 980 + ttm_bo_from_res_reserved(struct ttm_resource *res, struct ttm_bo_lru_cursor *curs) 981 + { 982 + struct ttm_buffer_object *bo = res->bo; 983 + 984 + if (!ttm_lru_walk_trylock(curs->ctx, bo, &curs->needs_unlock)) 985 + return NULL; 986 + 987 + if (!ttm_bo_get_unless_zero(bo)) { 988 + if (curs->needs_unlock) 989 + dma_resv_unlock(bo->base.resv); 990 + return NULL; 991 + } 992 + 993 + curs->bo = bo; 994 + return bo; 995 + } 996 + 997 + /** 998 + * ttm_bo_lru_cursor_next() - Continue iterating a manager's LRU lists 999 + * to find and lock buffer object. 1000 + * @curs: The cursor initialized using ttm_bo_lru_cursor_init() and 1001 + * ttm_bo_lru_cursor_first(). 1002 + * 1003 + * Return: A pointer to a locked and reference-counted buffer object, 1004 + * or NULL if none could be found and looping should be terminated. 1005 + */ 1006 + struct ttm_buffer_object *ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs) 1007 + { 1008 + spinlock_t *lru_lock = &curs->res_curs.man->bdev->lru_lock; 1009 + struct ttm_resource *res = NULL; 1010 + struct ttm_buffer_object *bo; 1011 + 1012 + ttm_bo_lru_cursor_cleanup_bo(curs); 1013 + 1014 + spin_lock(lru_lock); 1015 + for (;;) { 1016 + res = ttm_resource_manager_next(&curs->res_curs); 1017 + if (!res) 1018 + break; 1019 + 1020 + bo = ttm_bo_from_res_reserved(res, curs); 1021 + if (bo) 1022 + break; 1023 + } 1024 + 1025 + spin_unlock(lru_lock); 1026 + return res ? bo : NULL; 1027 + } 1028 + EXPORT_SYMBOL(ttm_bo_lru_cursor_next); 1029 + 1030 + /** 1031 + * ttm_bo_lru_cursor_first() - Start iterating a manager's LRU lists 1032 + * to find and lock buffer object. 1033 + * @curs: The cursor initialized using ttm_bo_lru_cursor_init(). 1034 + * 1035 + * Return: A pointer to a locked and reference-counted buffer object, 1036 + * or NULL if none could be found and looping should be terminated. 1037 + */ 1038 + struct ttm_buffer_object *ttm_bo_lru_cursor_first(struct ttm_bo_lru_cursor *curs) 1039 + { 1040 + spinlock_t *lru_lock = &curs->res_curs.man->bdev->lru_lock; 1041 + struct ttm_buffer_object *bo; 1042 + struct ttm_resource *res; 1043 + 1044 + spin_lock(lru_lock); 1045 + res = ttm_resource_manager_first(&curs->res_curs); 1046 + if (!res) { 1047 + spin_unlock(lru_lock); 1048 + return NULL; 1049 + } 1050 + 1051 + bo = ttm_bo_from_res_reserved(res, curs); 1052 + spin_unlock(lru_lock); 1053 + 1054 + return bo ? bo : ttm_bo_lru_cursor_next(curs); 1055 + } 1056 + EXPORT_SYMBOL(ttm_bo_lru_cursor_first);
+72
include/drm/ttm/ttm_bo.h
··· 467 467 int ttm_bo_populate(struct ttm_buffer_object *bo, 468 468 struct ttm_operation_ctx *ctx); 469 469 470 + /* Driver LRU walk helpers initially targeted for shrinking. */ 471 + 472 + /** 473 + * struct ttm_bo_lru_cursor - Iterator cursor for TTM LRU list looping 474 + */ 475 + struct ttm_bo_lru_cursor { 476 + /** @res_curs: Embedded struct ttm_resource_cursor. */ 477 + struct ttm_resource_cursor res_curs; 478 + /** 479 + * @ctx: The struct ttm_operation_ctx used while looping. 480 + * governs the locking mode. 481 + */ 482 + struct ttm_operation_ctx *ctx; 483 + /** 484 + * @bo: Buffer object pointer if a buffer object is refcounted, 485 + * NULL otherwise. 486 + */ 487 + struct ttm_buffer_object *bo; 488 + /** 489 + * @needs_unlock: Valid iff @bo != NULL. The bo resv needs 490 + * unlock before the next iteration or after loop exit. 491 + */ 492 + bool needs_unlock; 493 + }; 494 + 495 + void ttm_bo_lru_cursor_fini(struct ttm_bo_lru_cursor *curs); 496 + 497 + struct ttm_bo_lru_cursor * 498 + ttm_bo_lru_cursor_init(struct ttm_bo_lru_cursor *curs, 499 + struct ttm_resource_manager *man, 500 + struct ttm_operation_ctx *ctx); 501 + 502 + struct ttm_buffer_object *ttm_bo_lru_cursor_first(struct ttm_bo_lru_cursor *curs); 503 + 504 + struct ttm_buffer_object *ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs); 505 + 506 + /* 507 + * Defines needed to use autocleanup (linux/cleanup.h) with struct ttm_bo_lru_cursor. 508 + */ 509 + DEFINE_CLASS(ttm_bo_lru_cursor, struct ttm_bo_lru_cursor *, 510 + if (_T) {ttm_bo_lru_cursor_fini(_T); }, 511 + ttm_bo_lru_cursor_init(curs, man, ctx), 512 + struct ttm_bo_lru_cursor *curs, struct ttm_resource_manager *man, 513 + struct ttm_operation_ctx *ctx); 514 + static inline void * 515 + class_ttm_bo_lru_cursor_lock_ptr(class_ttm_bo_lru_cursor_t *_T) 516 + { return *_T; } 517 + #define class_ttm_bo_lru_cursor_is_conditional false 518 + 519 + /** 520 + * ttm_bo_lru_for_each_reserved_guarded() - Iterate over buffer objects owning 521 + * resources on LRU lists. 522 + * @_cursor: struct ttm_bo_lru_cursor to use for the iteration. 523 + * @_man: The resource manager whose LRU lists to iterate over. 524 + * @_ctx: The struct ttm_operation_context to govern the @_bo locking. 525 + * @_bo: The struct ttm_buffer_object pointer pointing to the buffer object 526 + * for the current iteration. 527 + * 528 + * Iterate over all resources of @_man and for each resource, attempt to 529 + * reference and lock (using the locking mode detailed in @_ctx) the buffer 530 + * object it points to. If successful, assign @_bo to the address of the 531 + * buffer object and update @_cursor. The iteration is guarded in the 532 + * sense that @_cursor will be initialized before looping start and cleaned 533 + * up at looping termination, even if terminated prematurely by, for 534 + * example a return or break statement. Exiting the loop will also unlock 535 + * (if needed) and unreference @_bo. 536 + */ 537 + #define ttm_bo_lru_for_each_reserved_guarded(_cursor, _man, _ctx, _bo) \ 538 + scoped_guard(ttm_bo_lru_cursor, _cursor, _man, _ctx) \ 539 + for ((_bo) = ttm_bo_lru_cursor_first(_cursor); (_bo); \ 540 + (_bo) = ttm_bo_lru_cursor_next(_cursor)) 541 + 470 542 #endif