/* * SPDX-License-Identifier: MIT * * Copyright © 2014-2018 Intel Corporation */ #include "gem/i915_gem_object.h" #include "i915_drv.h" #include "intel_engine_pm.h" #include "intel_engine_pool.h" static struct intel_engine_cs *to_engine(struct intel_engine_pool *pool) { return container_of(pool, struct intel_engine_cs, pool); } static struct list_head * bucket_for_size(struct intel_engine_pool *pool, size_t sz) { int n; /* * Compute a power-of-two bucket, but throw everything greater than * 16KiB into the same bucket: i.e. the buckets hold objects of * (1 page, 2 pages, 4 pages, 8+ pages). */ n = fls(sz >> PAGE_SHIFT) - 1; if (n >= ARRAY_SIZE(pool->cache_list)) n = ARRAY_SIZE(pool->cache_list) - 1; return &pool->cache_list[n]; } static void node_free(struct intel_engine_pool_node *node) { i915_gem_object_put(node->obj); i915_active_fini(&node->active); kfree(node); } static int pool_active(struct i915_active *ref) { struct intel_engine_pool_node *node = container_of(ref, typeof(*node), active); struct dma_resv *resv = node->obj->base.resv; int err; if (dma_resv_trylock(resv)) { dma_resv_add_excl_fence(resv, NULL); dma_resv_unlock(resv); } err = i915_gem_object_pin_pages(node->obj); if (err) return err; /* Hide this pinned object from the shrinker until retired */ i915_gem_object_make_unshrinkable(node->obj); return 0; } static void pool_retire(struct i915_active *ref) { struct intel_engine_pool_node *node = container_of(ref, typeof(*node), active); struct intel_engine_pool *pool = node->pool; struct list_head *list = bucket_for_size(pool, node->obj->base.size); unsigned long flags; GEM_BUG_ON(!intel_engine_pm_is_awake(to_engine(pool))); i915_gem_object_unpin_pages(node->obj); /* Return this object to the shrinker pool */ i915_gem_object_make_purgeable(node->obj); spin_lock_irqsave(&pool->lock, flags); list_add(&node->link, list); spin_unlock_irqrestore(&pool->lock, flags); } static struct intel_engine_pool_node * node_create(struct intel_engine_pool *pool, size_t sz) { struct intel_engine_cs *engine = to_engine(pool); struct intel_engine_pool_node *node; struct drm_i915_gem_object *obj; node = kmalloc(sizeof(*node), GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_NOWARN); if (!node) return ERR_PTR(-ENOMEM); node->pool = pool; i915_active_init(engine->i915, &node->active, pool_active, pool_retire); obj = i915_gem_object_create_internal(engine->i915, sz); if (IS_ERR(obj)) { i915_active_fini(&node->active); kfree(node); return ERR_CAST(obj); } node->obj = obj; return node; } struct intel_engine_pool_node * intel_engine_pool_get(struct intel_engine_pool *pool, size_t size) { struct intel_engine_pool_node *node; struct list_head *list; unsigned long flags; int ret; GEM_BUG_ON(!intel_engine_pm_is_awake(to_engine(pool))); size = PAGE_ALIGN(size); list = bucket_for_size(pool, size); spin_lock_irqsave(&pool->lock, flags); list_for_each_entry(node, list, link) { if (node->obj->base.size < size) continue; list_del(&node->link); break; } spin_unlock_irqrestore(&pool->lock, flags); if (&node->link == list) { node = node_create(pool, size); if (IS_ERR(node)) return node; } ret = i915_active_acquire(&node->active); if (ret) { node_free(node); return ERR_PTR(ret); } return node; } void intel_engine_pool_init(struct intel_engine_pool *pool) { int n; spin_lock_init(&pool->lock); for (n = 0; n < ARRAY_SIZE(pool->cache_list); n++) INIT_LIST_HEAD(&pool->cache_list[n]); } void intel_engine_pool_park(struct intel_engine_pool *pool) { int n; for (n = 0; n < ARRAY_SIZE(pool->cache_list); n++) { struct list_head *list = &pool->cache_list[n]; struct intel_engine_pool_node *node, *nn; list_for_each_entry_safe(node, nn, list, link) node_free(node); INIT_LIST_HEAD(list); } } void intel_engine_pool_fini(struct intel_engine_pool *pool) { int n; for (n = 0; n < ARRAY_SIZE(pool->cache_list); n++) GEM_BUG_ON(!list_empty(&pool->cache_list[n])); }