[RFC 10/11] drm/ttm: Implement ttm memory evict functions
Oak Zeng
oak.zeng at intel.com
Thu Nov 2 04:33:05 UTC 2023
Implement ttm_mem_evict_valuable, ttm_mem_evict_entity and
ttm_mem_evict_busy_entity. Those are callback functions from
drm lru manager. Register those functions during drm lru entity
initialization. Those 3 functions are splitted from original
ttm_mem_evict_first function.
Reimplemented ttm_mem_evict_first function using drm_lru_evict_first
function. For now, drm_lru_evict_first just calls back to above 3
functions which are splitted from ttm_mem_evict_first function, so
there is no function change. In the future, when SVM code is added,
drm_lru_evict_first function can also calls into SVM resource eviction
functions, thus TTM and SVM can mutually evict resources from each
other.
Signed-off-by: Oak Zeng <oak.zeng at intel.com>
---
drivers/gpu/drm/ttm/ttm_bo.c | 192 ++++++++++++++++++++++++++++-------
include/drm/ttm/ttm_bo.h | 2 +
2 files changed, 158 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 4a5ffa920665..9ec7a246e2ad 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -587,50 +587,148 @@ static int ttm_mem_evict_wait_busy(struct ttm_buffer_object *busy_bo,
return r == -EDEADLK ? -EBUSY : r;
}
-int ttm_mem_evict_first(struct ttm_device *bdev,
- struct ttm_resource_manager *man,
- const struct ttm_place *place,
- struct ttm_operation_ctx *ctx,
- struct ww_acquire_ctx *ticket)
+struct ttm_mem_evict_ctx {
+ const struct ttm_place *place;
+ struct ttm_operation_ctx *ctx;
+ struct ww_acquire_ctx *ticket;
+};
+
+/**
+ * ttm_mem_evict_allowable
+ *
+ * @lru_entity: The struct ttm_resource::lru_entity when this resource is
+ * added to drm lru list.
+ * @place: The preferred ttm placement where we want to evict memory for
+ * more memory space. If the current ttm_resource doesn't match the preferred
+ * placement, then there is no need to evict the current resource.
+ * @ctx: ttm operation context
+ * @ticket: dma reservation's context used to lock resource
+ * @busy: used to return whether the current resource is busy (i.e., locked
+ * by other clients)
+ * @locked: used to return whether this resource is locked during this check,
+ * i.e., successfully trylocked bo's dma reservation object
+ *
+ * Check whether we are allowed to evict a memory resource. Return true if we
+ * are allowed to evict resource; otherwise false.
+ *
+ * When this function returns true, a resource reference counter (bo's reference)
+ * is hold. This reference counter need to be released after evict operation later
+ * on.
+ *
+ * This function should be called with lru_lock hold.
+ */
+bool ttm_mem_evict_allowable(struct drm_lru_entity *lru_entity,
+ const struct drm_lru_evict_ctx *lru_evict_ctx,
+ bool *busy, bool *locked)
{
- struct ttm_buffer_object *bo = NULL, *busy_bo = NULL;
- struct drm_lru_cursor cursor;
struct ttm_resource *res;
- struct drm_lru_entity *entity;
- bool locked = false;
- int ret;
+ struct ttm_buffer_object *bo = NULL;
+ struct ttm_device *bdev;
+ const struct ttm_place *place;
+ struct ttm_operation_ctx *ctx;
+ struct ww_acquire_ctx *ticket;
+ struct ttm_mem_evict_ctx *evict_ctx;
- spin_lock(bdev->lru_lock);
- drm_lru_for_each_entity(man->lru_mgr, &cursor, entity) {
- bool busy;
+ evict_ctx = (struct ttm_mem_evict_ctx *)lru_evict_ctx;
+ place = evict_ctx->place;
+ ctx = evict_ctx->ctx;
+ ticket = evict_ctx->ticket;
- res = container_of(entity, struct ttm_resource, lru_entity);
- if (!ttm_bo_evict_swapout_allowable(res->bo, ctx, place,
- &locked, &busy)) {
- if (busy && !busy_bo && ticket !=
- dma_resv_locking_ctx(res->bo->base.resv))
- busy_bo = res->bo;
- continue;
- }
+ res = container_of(lru_entity, struct ttm_resource, lru_entity);
+ bo = res->bo;
+ bdev = bo->bdev;
- if (ttm_bo_get_unless_zero(res->bo)) {
- bo = res->bo;
- break;
- }
- if (locked)
- dma_resv_unlock(res->bo->base.resv);
- }
+ if (!ttm_bo_evict_swapout_allowable(bo, ctx, place, locked, busy)) {
+ if (busy && ticket != dma_resv_locking_ctx(bo->base.resv))
+ *busy = true;
- if (!bo) {
- if (busy_bo && !ttm_bo_get_unless_zero(busy_bo))
- busy_bo = NULL;
- spin_unlock(bdev->lru_lock);
- ret = ttm_mem_evict_wait_busy(busy_bo, ctx, ticket);
- if (busy_bo)
- ttm_bo_put(busy_bo);
- return ret;
+ return false;
}
+ if (ttm_bo_get_unless_zero(bo))
+ return true;
+
+ if (locked)
+ dma_resv_unlock(bo->base.resv);
+
+ return false;
+}
+
+/**
+ * ttm_mem_evict_busy_entity
+ *
+ * @lru_entity: The struct ttm_resource::lru_entity when this resource is
+ * added to drm lru list.
+ * @ctx: ttm operation context
+ * @ticket: dma reservation's context used to lock resource
+ *
+ * Evict a busy memory resource.
+ * This function should be called with lru_lock hold.
+ */
+int ttm_mem_evict_busy_entity(struct drm_lru_entity *lru_entity,
+ const struct drm_lru_evict_ctx *lru_evict_ctx)
+{
+ struct ttm_resource *res;
+ struct ttm_buffer_object *bo = NULL;
+ struct ttm_device *bdev;
+ int ret;
+ struct ttm_operation_ctx *ctx;
+ struct ww_acquire_ctx *ticket;
+ struct ttm_mem_evict_ctx *evict_ctx;
+
+ evict_ctx = (struct ttm_mem_evict_ctx *)lru_evict_ctx;
+ ctx = evict_ctx->ctx;
+ ticket = evict_ctx->ticket;
+
+ res = container_of(lru_entity, struct ttm_resource, lru_entity);
+ bo = res->bo;
+ bdev = bo->bdev;
+
+ if (bo && !ttm_bo_get_unless_zero(bo))
+ bo = NULL;
+ spin_unlock(bdev->lru_lock);
+ ret = ttm_mem_evict_wait_busy(bo, ctx, ticket);
+ /* FIXME: this is code copied originally from ttm_mem_evict_first.
+ * 1) Shouldn't we ttm_bo_evict this bo also? Otherwise how can we
+ * make any memory space?
+ * 2) We also need to check whether this busy entity is in the same
+ * ttm_place as specified in lru_evict_ctx::place; if not, there is
+ * no help to wait this busy entity.
+ */
+ if (bo)
+ ttm_bo_put(bo);
+
+ return ret;
+}
+
+/**
+ * @lru_entity: The struct ttm_resource::lru_entity when this resource is
+ * added to drm lru list.
+ * @ctx: ttm operation context
+ * @locked: whether this resource is dma-reserved (if reserved, we need to
+ * unreserve it in this function)
+ *
+ * Evict a memory resource corresponding to a lru_entity. This should be
+ * called holding lru_lock
+ *
+ */
+int ttm_mem_evict_entity(struct drm_lru_entity *lru_entity,
+ const struct drm_lru_evict_ctx *lru_evict_ctx, bool locked)
+{
+ struct ttm_resource *res;
+ struct ttm_buffer_object *bo = NULL;
+ struct ttm_device *bdev;
+ int ret;
+ struct ttm_operation_ctx *ctx;
+ struct ttm_mem_evict_ctx *evict_ctx;
+
+ evict_ctx = (struct ttm_mem_evict_ctx *)lru_evict_ctx;
+ ctx = evict_ctx->ctx;
+
+ res = container_of(lru_entity, struct ttm_resource, lru_entity);
+ bo = res->bo;
+ bdev = bo->bdev;
+
if (bo->deleted) {
ret = ttm_bo_cleanup_refs(bo, ctx->interruptible,
ctx->no_wait_gpu, locked);
@@ -650,6 +748,28 @@ int ttm_mem_evict_first(struct ttm_device *bdev,
return ret;
}
+struct drm_lru_evict_func ttm_evict_func = {
+ .evict_allowable = ttm_mem_evict_allowable,
+ .evict_busy_entity = ttm_mem_evict_busy_entity,
+ .evict_entity = ttm_mem_evict_entity
+};
+EXPORT_SYMBOL(ttm_evict_func);
+
+int ttm_mem_evict_first(struct ttm_device *bdev,
+ struct ttm_resource_manager *man,
+ const struct ttm_place *place,
+ struct ttm_operation_ctx *ctx,
+ struct ww_acquire_ctx *ticket)
+{
+ struct drm_lru_evict_ctx evict_ctx = {
+ .data1 = place,
+ .data2 = ctx,
+ .data3 = ticket
+ };
+
+ return drm_lru_evict_first(man->lru_mgr, &evict_ctx);
+}
+
/**
* ttm_bo_pin - Pin the buffer object.
* @bo: The buffer object to pin
diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
index 49f32df32204..223b198fe371 100644
--- a/include/drm/ttm/ttm_bo.h
+++ b/include/drm/ttm/ttm_bo.h
@@ -50,6 +50,7 @@ struct ttm_place;
struct ttm_resource;
struct ttm_resource_manager;
struct ttm_tt;
+struct drm_lru_evict_func;
/**
* enum ttm_bo_type
@@ -424,4 +425,5 @@ pgprot_t ttm_io_prot(struct ttm_buffer_object *bo, struct ttm_resource *res,
pgprot_t tmp);
void ttm_bo_tt_destroy(struct ttm_buffer_object *bo);
+extern struct drm_lru_evict_func ttm_evict_func;
#endif
--
2.26.3
More information about the dri-devel
mailing list