drm/i915/gem: Implement object migration
Introduce an interface to migrate objects between regions. This is primarily intended to migrate objects to LMEM for display and to SYSTEM for dma-buf, but might be reused in one form or another for performance-based migration. v2: - Verify that the memory region given as an id really exists. (Reported by Matthew Auld) - Call i915_gem_object_{init,release}_memory_region() when switching region to handle also switching region lists. (Reported by Matthew Auld) v3: - Fix i915_gem_object_can_migrate() to return true if object is already in the correct region, even if the object ops doesn't have a migrate() callback. - Update typo in commit message. - Fix kerneldoc of i915_gem_object_wait_migration(). v4: - Improve documentation (Suggested by Mattew Auld and Michael Ruhl) - Always assume TTM migration hits a TTM move and unsets the pages through move_notify. (Reported by Matthew Auld) - Add a dma_fence_might_wait() annotation to i915_gem_object_wait_migration() (Suggested by Daniel Vetter) v5: - Re-add might_sleep() instead of __dma_fence_might_wait(), Sent v4 with the wrong version, didn't compile and __dma_fence_might_wait() is not exported. - Added an R-B. Reported-by: kernel test robot <lkp@intel.com> Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com> Reviewed-by: Michael J. Ruhl <michael.j.ruhl@intel.com> Reviewed-by: Matthew Auld <matthew.auld@intel.com> Signed-off-by: Matthew Auld <matthew.auld@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20210629151203.209465-2-thomas.hellstrom@linux.intel.com
This commit is contained in:
Родитель
0454a490bd
Коммит
b6e913e19c
|
@ -513,6 +513,118 @@ bool i915_gem_object_has_iomem(const struct drm_i915_gem_object *obj)
|
|||
return obj->mem_flags & I915_BO_FLAG_IOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* i915_gem_object_can_migrate - Whether an object likely can be migrated
|
||||
*
|
||||
* @obj: The object to migrate
|
||||
* @id: The region intended to migrate to
|
||||
*
|
||||
* Check whether the object backend supports migration to the
|
||||
* given region. Note that pinning may affect the ability to migrate as
|
||||
* returned by this function.
|
||||
*
|
||||
* This function is primarily intended as a helper for checking the
|
||||
* possibility to migrate objects and might be slightly less permissive
|
||||
* than i915_gem_object_migrate() when it comes to objects with the
|
||||
* I915_BO_ALLOC_USER flag set.
|
||||
*
|
||||
* Return: true if migration is possible, false otherwise.
|
||||
*/
|
||||
bool i915_gem_object_can_migrate(struct drm_i915_gem_object *obj,
|
||||
enum intel_region_id id)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(obj->base.dev);
|
||||
unsigned int num_allowed = obj->mm.n_placements;
|
||||
struct intel_memory_region *mr;
|
||||
unsigned int i;
|
||||
|
||||
GEM_BUG_ON(id >= INTEL_REGION_UNKNOWN);
|
||||
GEM_BUG_ON(obj->mm.madv != I915_MADV_WILLNEED);
|
||||
|
||||
mr = i915->mm.regions[id];
|
||||
if (!mr)
|
||||
return false;
|
||||
|
||||
if (obj->mm.region == mr)
|
||||
return true;
|
||||
|
||||
if (!i915_gem_object_evictable(obj))
|
||||
return false;
|
||||
|
||||
if (!obj->ops->migrate)
|
||||
return false;
|
||||
|
||||
if (!(obj->flags & I915_BO_ALLOC_USER))
|
||||
return true;
|
||||
|
||||
if (num_allowed == 0)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < num_allowed; ++i) {
|
||||
if (mr == obj->mm.placements[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* i915_gem_object_migrate - Migrate an object to the desired region id
|
||||
* @obj: The object to migrate.
|
||||
* @ww: An optional struct i915_gem_ww_ctx. If NULL, the backend may
|
||||
* not be successful in evicting other objects to make room for this object.
|
||||
* @id: The region id to migrate to.
|
||||
*
|
||||
* Attempt to migrate the object to the desired memory region. The
|
||||
* object backend must support migration and the object may not be
|
||||
* pinned, (explicitly pinned pages or pinned vmas). The object must
|
||||
* be locked.
|
||||
* On successful completion, the object will have pages pointing to
|
||||
* memory in the new region, but an async migration task may not have
|
||||
* completed yet, and to accomplish that, i915_gem_object_wait_migration()
|
||||
* must be called.
|
||||
*
|
||||
* This function is a bit more permissive than i915_gem_object_can_migrate()
|
||||
* to allow for migrating objects where the caller knows exactly what is
|
||||
* happening. For example within selftests. More specifically this
|
||||
* function allows migrating I915_BO_ALLOC_USER objects to regions
|
||||
* that are not in the list of allowable regions.
|
||||
*
|
||||
* Note: the @ww parameter is not used yet, but included to make sure
|
||||
* callers put some effort into obtaining a valid ww ctx if one is
|
||||
* available.
|
||||
*
|
||||
* Return: 0 on success. Negative error code on failure. In particular may
|
||||
* return -ENXIO on lack of region space, -EDEADLK for deadlock avoidance
|
||||
* if @ww is set, -EINTR or -ERESTARTSYS if signal pending, and
|
||||
* -EBUSY if the object is pinned.
|
||||
*/
|
||||
int i915_gem_object_migrate(struct drm_i915_gem_object *obj,
|
||||
struct i915_gem_ww_ctx *ww,
|
||||
enum intel_region_id id)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(obj->base.dev);
|
||||
struct intel_memory_region *mr;
|
||||
|
||||
GEM_BUG_ON(id >= INTEL_REGION_UNKNOWN);
|
||||
GEM_BUG_ON(obj->mm.madv != I915_MADV_WILLNEED);
|
||||
assert_object_held(obj);
|
||||
|
||||
mr = i915->mm.regions[id];
|
||||
GEM_BUG_ON(!mr);
|
||||
|
||||
if (obj->mm.region == mr)
|
||||
return 0;
|
||||
|
||||
if (!i915_gem_object_evictable(obj))
|
||||
return -EBUSY;
|
||||
|
||||
if (!obj->ops->migrate)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return obj->ops->migrate(obj, mr);
|
||||
}
|
||||
|
||||
void i915_gem_init__objects(struct drm_i915_private *i915)
|
||||
{
|
||||
INIT_WORK(&i915->mm.free_work, __i915_gem_free_work);
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include "i915_gem_ww.h"
|
||||
#include "i915_vma_types.h"
|
||||
|
||||
enum intel_region_id;
|
||||
|
||||
/*
|
||||
* XXX: There is a prevalence of the assumption that we fit the
|
||||
* object's page count inside a 32bit _signed_ variable. Let's document
|
||||
|
@ -597,6 +599,16 @@ bool i915_gem_object_migratable(struct drm_i915_gem_object *obj);
|
|||
|
||||
bool i915_gem_object_validates_to_lmem(struct drm_i915_gem_object *obj);
|
||||
|
||||
int i915_gem_object_migrate(struct drm_i915_gem_object *obj,
|
||||
struct i915_gem_ww_ctx *ww,
|
||||
enum intel_region_id id);
|
||||
|
||||
bool i915_gem_object_can_migrate(struct drm_i915_gem_object *obj,
|
||||
enum intel_region_id id);
|
||||
|
||||
int i915_gem_object_wait_migration(struct drm_i915_gem_object *obj,
|
||||
unsigned int flags);
|
||||
|
||||
#ifdef CONFIG_MMU_NOTIFIER
|
||||
static inline bool
|
||||
i915_gem_object_is_userptr(struct drm_i915_gem_object *obj)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
struct drm_i915_gem_object;
|
||||
struct intel_fronbuffer;
|
||||
struct intel_memory_region;
|
||||
|
||||
/*
|
||||
* struct i915_lut_handle tracks the fast lookups from handle to vma used
|
||||
|
@ -77,6 +78,14 @@ struct drm_i915_gem_object_ops {
|
|||
* delayed_free - Override the default delayed free implementation
|
||||
*/
|
||||
void (*delayed_free)(struct drm_i915_gem_object *obj);
|
||||
|
||||
/**
|
||||
* migrate - Migrate object to a different region either for
|
||||
* pinning or for as long as the object lock is held.
|
||||
*/
|
||||
int (*migrate)(struct drm_i915_gem_object *obj,
|
||||
struct intel_memory_region *mr);
|
||||
|
||||
void (*release)(struct drm_i915_gem_object *obj);
|
||||
|
||||
const struct vm_operations_struct *mmap_ops;
|
||||
|
|
|
@ -617,7 +617,8 @@ struct ttm_device_funcs *i915_ttm_driver(void)
|
|||
return &i915_ttm_bo_driver;
|
||||
}
|
||||
|
||||
static int i915_ttm_get_pages(struct drm_i915_gem_object *obj)
|
||||
static int __i915_ttm_get_pages(struct drm_i915_gem_object *obj,
|
||||
struct ttm_placement *placement)
|
||||
{
|
||||
struct ttm_buffer_object *bo = i915_gem_to_ttm(obj);
|
||||
struct ttm_operation_ctx ctx = {
|
||||
|
@ -625,19 +626,12 @@ static int i915_ttm_get_pages(struct drm_i915_gem_object *obj)
|
|||
.no_wait_gpu = false,
|
||||
};
|
||||
struct sg_table *st;
|
||||
struct ttm_place requested, busy[I915_TTM_MAX_PLACEMENTS];
|
||||
struct ttm_placement placement;
|
||||
int real_num_busy;
|
||||
int ret;
|
||||
|
||||
GEM_BUG_ON(obj->mm.n_placements > I915_TTM_MAX_PLACEMENTS);
|
||||
|
||||
/* Move to the requested placement. */
|
||||
i915_ttm_placement_from_obj(obj, &requested, busy, &placement);
|
||||
|
||||
/* First try only the requested placement. No eviction. */
|
||||
real_num_busy = fetch_and_zero(&placement.num_busy_placement);
|
||||
ret = ttm_bo_validate(bo, &placement, &ctx);
|
||||
real_num_busy = fetch_and_zero(&placement->num_busy_placement);
|
||||
ret = ttm_bo_validate(bo, placement, &ctx);
|
||||
if (ret) {
|
||||
ret = i915_ttm_err_to_gem(ret);
|
||||
/*
|
||||
|
@ -652,8 +646,8 @@ static int i915_ttm_get_pages(struct drm_i915_gem_object *obj)
|
|||
* If the initial attempt fails, allow all accepted placements,
|
||||
* evicting if necessary.
|
||||
*/
|
||||
placement.num_busy_placement = real_num_busy;
|
||||
ret = ttm_bo_validate(bo, &placement, &ctx);
|
||||
placement->num_busy_placement = real_num_busy;
|
||||
ret = ttm_bo_validate(bo, placement, &ctx);
|
||||
if (ret)
|
||||
return i915_ttm_err_to_gem(ret);
|
||||
}
|
||||
|
@ -668,6 +662,7 @@ static int i915_ttm_get_pages(struct drm_i915_gem_object *obj)
|
|||
i915_ttm_adjust_gem_after_move(obj);
|
||||
}
|
||||
|
||||
GEM_WARN_ON(obj->mm.pages);
|
||||
/* Object either has a page vector or is an iomem object */
|
||||
st = bo->ttm ? i915_ttm_tt_get_st(bo->ttm) : obj->ttm.cached_io_st;
|
||||
if (IS_ERR(st))
|
||||
|
@ -678,6 +673,63 @@ static int i915_ttm_get_pages(struct drm_i915_gem_object *obj)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int i915_ttm_get_pages(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct ttm_place requested, busy[I915_TTM_MAX_PLACEMENTS];
|
||||
struct ttm_placement placement;
|
||||
|
||||
GEM_BUG_ON(obj->mm.n_placements > I915_TTM_MAX_PLACEMENTS);
|
||||
|
||||
/* Move to the requested placement. */
|
||||
i915_ttm_placement_from_obj(obj, &requested, busy, &placement);
|
||||
|
||||
return __i915_ttm_get_pages(obj, &placement);
|
||||
}
|
||||
|
||||
/**
|
||||
* DOC: Migration vs eviction
|
||||
*
|
||||
* GEM migration may not be the same as TTM migration / eviction. If
|
||||
* the TTM core decides to evict an object it may be evicted to a
|
||||
* TTM memory type that is not in the object's allowable GEM regions, or
|
||||
* in fact theoretically to a TTM memory type that doesn't correspond to
|
||||
* a GEM memory region. In that case the object's GEM region is not
|
||||
* updated, and the data is migrated back to the GEM region at
|
||||
* get_pages time. TTM may however set up CPU ptes to the object even
|
||||
* when it is evicted.
|
||||
* Gem forced migration using the i915_ttm_migrate() op, is allowed even
|
||||
* to regions that are not in the object's list of allowable placements.
|
||||
*/
|
||||
static int i915_ttm_migrate(struct drm_i915_gem_object *obj,
|
||||
struct intel_memory_region *mr)
|
||||
{
|
||||
struct ttm_place requested;
|
||||
struct ttm_placement placement;
|
||||
int ret;
|
||||
|
||||
i915_ttm_place_from_region(mr, &requested, obj->flags);
|
||||
placement.num_placement = 1;
|
||||
placement.num_busy_placement = 1;
|
||||
placement.placement = &requested;
|
||||
placement.busy_placement = &requested;
|
||||
|
||||
ret = __i915_ttm_get_pages(obj, &placement);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Reinitialize the region bindings. This is primarily
|
||||
* required for objects where the new region is not in
|
||||
* its allowable placements.
|
||||
*/
|
||||
if (obj->mm.region != mr) {
|
||||
i915_gem_object_release_memory_region(obj);
|
||||
i915_gem_object_init_memory_region(obj, mr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i915_ttm_put_pages(struct drm_i915_gem_object *obj,
|
||||
struct sg_table *st)
|
||||
{
|
||||
|
@ -814,6 +866,7 @@ static const struct drm_i915_gem_object_ops i915_gem_ttm_obj_ops = {
|
|||
.truncate = i915_ttm_purge,
|
||||
.adjust_lru = i915_ttm_adjust_lru,
|
||||
.delayed_free = i915_ttm_delayed_free,
|
||||
.migrate = i915_ttm_migrate,
|
||||
.mmap_offset = i915_ttm_mmap_offset,
|
||||
.mmap_ops = &vm_ops_ttm,
|
||||
};
|
||||
|
|
|
@ -290,3 +290,22 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
|
|||
i915_gem_object_put(obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* i915_gem_object_wait_migration - Sync an accelerated migration operation
|
||||
* @obj: The migrating object.
|
||||
* @flags: waiting flags. Currently supports only I915_WAIT_INTERRUPTIBLE.
|
||||
*
|
||||
* Wait for any pending async migration operation on the object,
|
||||
* whether it's explicitly (i915_gem_object_migrate()) or implicitly
|
||||
* (swapin, initial clearing) initiated.
|
||||
*
|
||||
* Return: 0 if successful, -ERESTARTSYS if a signal was hit during waiting.
|
||||
*/
|
||||
int i915_gem_object_wait_migration(struct drm_i915_gem_object *obj,
|
||||
unsigned int flags)
|
||||
{
|
||||
might_sleep();
|
||||
/* NOP for now. */
|
||||
return 0;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче