cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

i915_gem_ttm_move.c (18222B)


      1// SPDX-License-Identifier: MIT
      2/*
      3 * Copyright © 2021 Intel Corporation
      4 */
      5
      6#include <drm/ttm/ttm_bo_driver.h>
      7
      8#include "i915_deps.h"
      9#include "i915_drv.h"
     10#include "intel_memory_region.h"
     11#include "intel_region_ttm.h"
     12
     13#include "gem/i915_gem_object.h"
     14#include "gem/i915_gem_region.h"
     15#include "gem/i915_gem_ttm.h"
     16#include "gem/i915_gem_ttm_move.h"
     17
     18#include "gt/intel_engine_pm.h"
     19#include "gt/intel_gt.h"
     20#include "gt/intel_migrate.h"
     21
     22/**
     23 * DOC: Selftest failure modes for failsafe migration:
     24 *
     25 * For fail_gpu_migration, the gpu blit scheduled is always a clear blit
     26 * rather than a copy blit, and then we force the failure paths as if
     27 * the blit fence returned an error.
     28 *
     29 * For fail_work_allocation we fail the kmalloc of the async worker, we
     30 * sync the gpu blit. If it then fails, or fail_gpu_migration is set to
     31 * true, then a memcpy operation is performed sync.
     32 */
     33#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
     34static bool fail_gpu_migration;
     35static bool fail_work_allocation;
     36
     37void i915_ttm_migrate_set_failure_modes(bool gpu_migration,
     38					bool work_allocation)
     39{
     40	fail_gpu_migration = gpu_migration;
     41	fail_work_allocation = work_allocation;
     42}
     43#endif
     44
     45static enum i915_cache_level
     46i915_ttm_cache_level(struct drm_i915_private *i915, struct ttm_resource *res,
     47		     struct ttm_tt *ttm)
     48{
     49	return ((HAS_LLC(i915) || HAS_SNOOP(i915)) &&
     50		!i915_ttm_gtt_binds_lmem(res) &&
     51		ttm->caching == ttm_cached) ? I915_CACHE_LLC :
     52		I915_CACHE_NONE;
     53}
     54
     55static struct intel_memory_region *
     56i915_ttm_region(struct ttm_device *bdev, int ttm_mem_type)
     57{
     58	struct drm_i915_private *i915 = container_of(bdev, typeof(*i915), bdev);
     59
     60	/* There's some room for optimization here... */
     61	GEM_BUG_ON(ttm_mem_type != I915_PL_SYSTEM &&
     62		   ttm_mem_type < I915_PL_LMEM0);
     63	if (ttm_mem_type == I915_PL_SYSTEM)
     64		return intel_memory_region_lookup(i915, INTEL_MEMORY_SYSTEM,
     65						  0);
     66
     67	return intel_memory_region_lookup(i915, INTEL_MEMORY_LOCAL,
     68					  ttm_mem_type - I915_PL_LMEM0);
     69}
     70
     71/**
     72 * i915_ttm_adjust_domains_after_move - Adjust the GEM domains after a
     73 * TTM move
     74 * @obj: The gem object
     75 */
     76void i915_ttm_adjust_domains_after_move(struct drm_i915_gem_object *obj)
     77{
     78	struct ttm_buffer_object *bo = i915_gem_to_ttm(obj);
     79
     80	if (i915_ttm_cpu_maps_iomem(bo->resource) || bo->ttm->caching != ttm_cached) {
     81		obj->write_domain = I915_GEM_DOMAIN_WC;
     82		obj->read_domains = I915_GEM_DOMAIN_WC;
     83	} else {
     84		obj->write_domain = I915_GEM_DOMAIN_CPU;
     85		obj->read_domains = I915_GEM_DOMAIN_CPU;
     86	}
     87}
     88
     89/**
     90 * i915_ttm_adjust_gem_after_move - Adjust the GEM state after a TTM move
     91 * @obj: The gem object
     92 *
     93 * Adjusts the GEM object's region, mem_flags and cache coherency after a
     94 * TTM move.
     95 */
     96void i915_ttm_adjust_gem_after_move(struct drm_i915_gem_object *obj)
     97{
     98	struct ttm_buffer_object *bo = i915_gem_to_ttm(obj);
     99	unsigned int cache_level;
    100	unsigned int i;
    101
    102	/*
    103	 * If object was moved to an allowable region, update the object
    104	 * region to consider it migrated. Note that if it's currently not
    105	 * in an allowable region, it's evicted and we don't update the
    106	 * object region.
    107	 */
    108	if (intel_region_to_ttm_type(obj->mm.region) != bo->resource->mem_type) {
    109		for (i = 0; i < obj->mm.n_placements; ++i) {
    110			struct intel_memory_region *mr = obj->mm.placements[i];
    111
    112			if (intel_region_to_ttm_type(mr) == bo->resource->mem_type &&
    113			    mr != obj->mm.region) {
    114				i915_gem_object_release_memory_region(obj);
    115				i915_gem_object_init_memory_region(obj, mr);
    116				break;
    117			}
    118		}
    119	}
    120
    121	obj->mem_flags &= ~(I915_BO_FLAG_STRUCT_PAGE | I915_BO_FLAG_IOMEM);
    122
    123	obj->mem_flags |= i915_ttm_cpu_maps_iomem(bo->resource) ? I915_BO_FLAG_IOMEM :
    124		I915_BO_FLAG_STRUCT_PAGE;
    125
    126	cache_level = i915_ttm_cache_level(to_i915(bo->base.dev), bo->resource,
    127					   bo->ttm);
    128	i915_gem_object_set_cache_coherency(obj, cache_level);
    129}
    130
    131/**
    132 * i915_ttm_move_notify - Prepare an object for move
    133 * @bo: The ttm buffer object.
    134 *
    135 * This function prepares an object for move by removing all GPU bindings,
    136 * removing all CPU mapings and finally releasing the pages sg-table.
    137 *
    138 * Return: 0 if successful, negative error code on error.
    139 */
    140int i915_ttm_move_notify(struct ttm_buffer_object *bo)
    141{
    142	struct drm_i915_gem_object *obj = i915_ttm_to_gem(bo);
    143	int ret;
    144
    145	/*
    146	 * Note: The async unbinding here will actually transform the
    147	 * blocking wait for unbind into a wait before finally submitting
    148	 * evict / migration blit and thus stall the migration timeline
    149	 * which may not be good for overall throughput. We should make
    150	 * sure we await the unbind fences *after* the migration blit
    151	 * instead of *before* as we currently do.
    152	 */
    153	ret = i915_gem_object_unbind(obj, I915_GEM_OBJECT_UNBIND_ACTIVE |
    154				     I915_GEM_OBJECT_UNBIND_ASYNC);
    155	if (ret)
    156		return ret;
    157
    158	ret = __i915_gem_object_put_pages(obj);
    159	if (ret)
    160		return ret;
    161
    162	return 0;
    163}
    164
    165static struct dma_fence *i915_ttm_accel_move(struct ttm_buffer_object *bo,
    166					     bool clear,
    167					     struct ttm_resource *dst_mem,
    168					     struct ttm_tt *dst_ttm,
    169					     struct sg_table *dst_st,
    170					     const struct i915_deps *deps)
    171{
    172	struct drm_i915_private *i915 = container_of(bo->bdev, typeof(*i915),
    173						     bdev);
    174	struct drm_i915_gem_object *obj = i915_ttm_to_gem(bo);
    175	struct i915_request *rq;
    176	struct ttm_tt *src_ttm = bo->ttm;
    177	enum i915_cache_level src_level, dst_level;
    178	int ret;
    179
    180	if (!to_gt(i915)->migrate.context || intel_gt_is_wedged(to_gt(i915)))
    181		return ERR_PTR(-EINVAL);
    182
    183	/* With fail_gpu_migration, we always perform a GPU clear. */
    184	if (I915_SELFTEST_ONLY(fail_gpu_migration))
    185		clear = true;
    186
    187	dst_level = i915_ttm_cache_level(i915, dst_mem, dst_ttm);
    188	if (clear) {
    189		if (bo->type == ttm_bo_type_kernel &&
    190		    !I915_SELFTEST_ONLY(fail_gpu_migration))
    191			return ERR_PTR(-EINVAL);
    192
    193		intel_engine_pm_get(to_gt(i915)->migrate.context->engine);
    194		ret = intel_context_migrate_clear(to_gt(i915)->migrate.context, deps,
    195						  dst_st->sgl, dst_level,
    196						  i915_ttm_gtt_binds_lmem(dst_mem),
    197						  0, &rq);
    198	} else {
    199		struct i915_refct_sgt *src_rsgt =
    200			i915_ttm_resource_get_st(obj, bo->resource);
    201
    202		if (IS_ERR(src_rsgt))
    203			return ERR_CAST(src_rsgt);
    204
    205		src_level = i915_ttm_cache_level(i915, bo->resource, src_ttm);
    206		intel_engine_pm_get(to_gt(i915)->migrate.context->engine);
    207		ret = intel_context_migrate_copy(to_gt(i915)->migrate.context,
    208						 deps, src_rsgt->table.sgl,
    209						 src_level,
    210						 i915_ttm_gtt_binds_lmem(bo->resource),
    211						 dst_st->sgl, dst_level,
    212						 i915_ttm_gtt_binds_lmem(dst_mem),
    213						 &rq);
    214
    215		i915_refct_sgt_put(src_rsgt);
    216	}
    217
    218	intel_engine_pm_put(to_gt(i915)->migrate.context->engine);
    219
    220	if (ret && rq) {
    221		i915_request_wait(rq, 0, MAX_SCHEDULE_TIMEOUT);
    222		i915_request_put(rq);
    223	}
    224
    225	return ret ? ERR_PTR(ret) : &rq->fence;
    226}
    227
    228/**
    229 * struct i915_ttm_memcpy_arg - argument for the bo memcpy functionality.
    230 * @_dst_iter: Storage space for the destination kmap iterator.
    231 * @_src_iter: Storage space for the source kmap iterator.
    232 * @dst_iter: Pointer to the destination kmap iterator.
    233 * @src_iter: Pointer to the source kmap iterator.
    234 * @clear: Whether to clear instead of copy.
    235 * @src_rsgt: Refcounted scatter-gather list of source memory.
    236 * @dst_rsgt: Refcounted scatter-gather list of destination memory.
    237 */
    238struct i915_ttm_memcpy_arg {
    239	union {
    240		struct ttm_kmap_iter_tt tt;
    241		struct ttm_kmap_iter_iomap io;
    242	} _dst_iter,
    243	_src_iter;
    244	struct ttm_kmap_iter *dst_iter;
    245	struct ttm_kmap_iter *src_iter;
    246	unsigned long num_pages;
    247	bool clear;
    248	struct i915_refct_sgt *src_rsgt;
    249	struct i915_refct_sgt *dst_rsgt;
    250};
    251
    252/**
    253 * struct i915_ttm_memcpy_work - Async memcpy worker under a dma-fence.
    254 * @fence: The dma-fence.
    255 * @work: The work struct use for the memcpy work.
    256 * @lock: The fence lock. Not used to protect anything else ATM.
    257 * @irq_work: Low latency worker to signal the fence since it can't be done
    258 * from the callback for lockdep reasons.
    259 * @cb: Callback for the accelerated migration fence.
    260 * @arg: The argument for the memcpy functionality.
    261 */
    262struct i915_ttm_memcpy_work {
    263	struct dma_fence fence;
    264	struct work_struct work;
    265	/* The fence lock */
    266	spinlock_t lock;
    267	struct irq_work irq_work;
    268	struct dma_fence_cb cb;
    269	struct i915_ttm_memcpy_arg arg;
    270};
    271
    272static void i915_ttm_move_memcpy(struct i915_ttm_memcpy_arg *arg)
    273{
    274	ttm_move_memcpy(arg->clear, arg->num_pages,
    275			arg->dst_iter, arg->src_iter);
    276}
    277
    278static void i915_ttm_memcpy_init(struct i915_ttm_memcpy_arg *arg,
    279				 struct ttm_buffer_object *bo, bool clear,
    280				 struct ttm_resource *dst_mem,
    281				 struct ttm_tt *dst_ttm,
    282				 struct i915_refct_sgt *dst_rsgt)
    283{
    284	struct drm_i915_gem_object *obj = i915_ttm_to_gem(bo);
    285	struct intel_memory_region *dst_reg, *src_reg;
    286
    287	dst_reg = i915_ttm_region(bo->bdev, dst_mem->mem_type);
    288	src_reg = i915_ttm_region(bo->bdev, bo->resource->mem_type);
    289	GEM_BUG_ON(!dst_reg || !src_reg);
    290
    291	arg->dst_iter = !i915_ttm_cpu_maps_iomem(dst_mem) ?
    292		ttm_kmap_iter_tt_init(&arg->_dst_iter.tt, dst_ttm) :
    293		ttm_kmap_iter_iomap_init(&arg->_dst_iter.io, &dst_reg->iomap,
    294					 &dst_rsgt->table, dst_reg->region.start);
    295
    296	arg->src_iter = !i915_ttm_cpu_maps_iomem(bo->resource) ?
    297		ttm_kmap_iter_tt_init(&arg->_src_iter.tt, bo->ttm) :
    298		ttm_kmap_iter_iomap_init(&arg->_src_iter.io, &src_reg->iomap,
    299					 &obj->ttm.cached_io_rsgt->table,
    300					 src_reg->region.start);
    301	arg->clear = clear;
    302	arg->num_pages = bo->base.size >> PAGE_SHIFT;
    303
    304	arg->dst_rsgt = i915_refct_sgt_get(dst_rsgt);
    305	arg->src_rsgt = clear ? NULL :
    306		i915_ttm_resource_get_st(obj, bo->resource);
    307}
    308
    309static void i915_ttm_memcpy_release(struct i915_ttm_memcpy_arg *arg)
    310{
    311	i915_refct_sgt_put(arg->src_rsgt);
    312	i915_refct_sgt_put(arg->dst_rsgt);
    313}
    314
    315static void __memcpy_work(struct work_struct *work)
    316{
    317	struct i915_ttm_memcpy_work *copy_work =
    318		container_of(work, typeof(*copy_work), work);
    319	struct i915_ttm_memcpy_arg *arg = &copy_work->arg;
    320	bool cookie = dma_fence_begin_signalling();
    321
    322	i915_ttm_move_memcpy(arg);
    323	dma_fence_end_signalling(cookie);
    324
    325	dma_fence_signal(&copy_work->fence);
    326
    327	i915_ttm_memcpy_release(arg);
    328	dma_fence_put(&copy_work->fence);
    329}
    330
    331static void __memcpy_irq_work(struct irq_work *irq_work)
    332{
    333	struct i915_ttm_memcpy_work *copy_work =
    334		container_of(irq_work, typeof(*copy_work), irq_work);
    335	struct i915_ttm_memcpy_arg *arg = &copy_work->arg;
    336
    337	dma_fence_signal(&copy_work->fence);
    338	i915_ttm_memcpy_release(arg);
    339	dma_fence_put(&copy_work->fence);
    340}
    341
    342static void __memcpy_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
    343{
    344	struct i915_ttm_memcpy_work *copy_work =
    345		container_of(cb, typeof(*copy_work), cb);
    346
    347	if (unlikely(fence->error || I915_SELFTEST_ONLY(fail_gpu_migration))) {
    348		INIT_WORK(&copy_work->work, __memcpy_work);
    349		queue_work(system_unbound_wq, &copy_work->work);
    350	} else {
    351		init_irq_work(&copy_work->irq_work, __memcpy_irq_work);
    352		irq_work_queue(&copy_work->irq_work);
    353	}
    354}
    355
    356static const char *get_driver_name(struct dma_fence *fence)
    357{
    358	return "i915_ttm_memcpy_work";
    359}
    360
    361static const char *get_timeline_name(struct dma_fence *fence)
    362{
    363	return "unbound";
    364}
    365
    366static const struct dma_fence_ops dma_fence_memcpy_ops = {
    367	.get_driver_name = get_driver_name,
    368	.get_timeline_name = get_timeline_name,
    369};
    370
    371static struct dma_fence *
    372i915_ttm_memcpy_work_arm(struct i915_ttm_memcpy_work *work,
    373			 struct dma_fence *dep)
    374{
    375	int ret;
    376
    377	spin_lock_init(&work->lock);
    378	dma_fence_init(&work->fence, &dma_fence_memcpy_ops, &work->lock, 0, 0);
    379	dma_fence_get(&work->fence);
    380	ret = dma_fence_add_callback(dep, &work->cb, __memcpy_cb);
    381	if (ret) {
    382		if (ret != -ENOENT)
    383			dma_fence_wait(dep, false);
    384
    385		return ERR_PTR(I915_SELFTEST_ONLY(fail_gpu_migration) ? -EINVAL :
    386			       dep->error);
    387	}
    388
    389	return &work->fence;
    390}
    391
    392static struct dma_fence *
    393__i915_ttm_move(struct ttm_buffer_object *bo,
    394		const struct ttm_operation_ctx *ctx, bool clear,
    395		struct ttm_resource *dst_mem, struct ttm_tt *dst_ttm,
    396		struct i915_refct_sgt *dst_rsgt, bool allow_accel,
    397		const struct i915_deps *move_deps)
    398{
    399	struct i915_ttm_memcpy_work *copy_work = NULL;
    400	struct i915_ttm_memcpy_arg _arg, *arg = &_arg;
    401	struct dma_fence *fence = ERR_PTR(-EINVAL);
    402
    403	if (allow_accel) {
    404		fence = i915_ttm_accel_move(bo, clear, dst_mem, dst_ttm,
    405					    &dst_rsgt->table, move_deps);
    406
    407		/*
    408		 * We only need to intercept the error when moving to lmem.
    409		 * When moving to system, TTM or shmem will provide us with
    410		 * cleared pages.
    411		 */
    412		if (!IS_ERR(fence) && !i915_ttm_gtt_binds_lmem(dst_mem) &&
    413		    !I915_SELFTEST_ONLY(fail_gpu_migration ||
    414					fail_work_allocation))
    415			goto out;
    416	}
    417
    418	/* If we've scheduled gpu migration. Try to arm error intercept. */
    419	if (!IS_ERR(fence)) {
    420		struct dma_fence *dep = fence;
    421
    422		if (!I915_SELFTEST_ONLY(fail_work_allocation))
    423			copy_work = kzalloc(sizeof(*copy_work), GFP_KERNEL);
    424
    425		if (copy_work) {
    426			arg = &copy_work->arg;
    427			i915_ttm_memcpy_init(arg, bo, clear, dst_mem, dst_ttm,
    428					     dst_rsgt);
    429			fence = i915_ttm_memcpy_work_arm(copy_work, dep);
    430		} else {
    431			dma_fence_wait(dep, false);
    432			fence = ERR_PTR(I915_SELFTEST_ONLY(fail_gpu_migration) ?
    433					-EINVAL : fence->error);
    434		}
    435		dma_fence_put(dep);
    436
    437		if (!IS_ERR(fence))
    438			goto out;
    439	} else {
    440		int err = PTR_ERR(fence);
    441
    442		if (err == -EINTR || err == -ERESTARTSYS || err == -EAGAIN)
    443			return fence;
    444
    445		if (move_deps) {
    446			err = i915_deps_sync(move_deps, ctx);
    447			if (err)
    448				return ERR_PTR(err);
    449		}
    450	}
    451
    452	/* Error intercept failed or no accelerated migration to start with */
    453	if (!copy_work)
    454		i915_ttm_memcpy_init(arg, bo, clear, dst_mem, dst_ttm,
    455				     dst_rsgt);
    456	i915_ttm_move_memcpy(arg);
    457	i915_ttm_memcpy_release(arg);
    458	kfree(copy_work);
    459
    460	return NULL;
    461out:
    462	if (!fence && copy_work) {
    463		i915_ttm_memcpy_release(arg);
    464		kfree(copy_work);
    465	}
    466
    467	return fence;
    468}
    469
    470/**
    471 * i915_ttm_move - The TTM move callback used by i915.
    472 * @bo: The buffer object.
    473 * @evict: Whether this is an eviction.
    474 * @dst_mem: The destination ttm resource.
    475 * @hop: If we need multihop, what temporary memory type to move to.
    476 *
    477 * Return: 0 if successful, negative error code otherwise.
    478 */
    479int i915_ttm_move(struct ttm_buffer_object *bo, bool evict,
    480		  struct ttm_operation_ctx *ctx,
    481		  struct ttm_resource *dst_mem,
    482		  struct ttm_place *hop)
    483{
    484	struct drm_i915_gem_object *obj = i915_ttm_to_gem(bo);
    485	struct ttm_resource_manager *dst_man =
    486		ttm_manager_type(bo->bdev, dst_mem->mem_type);
    487	struct dma_fence *migration_fence = NULL;
    488	struct ttm_tt *ttm = bo->ttm;
    489	struct i915_refct_sgt *dst_rsgt;
    490	bool clear;
    491	int ret;
    492
    493	if (GEM_WARN_ON(!obj)) {
    494		ttm_bo_move_null(bo, dst_mem);
    495		return 0;
    496	}
    497
    498	ret = i915_ttm_move_notify(bo);
    499	if (ret)
    500		return ret;
    501
    502	if (obj->mm.madv != I915_MADV_WILLNEED) {
    503		i915_ttm_purge(obj);
    504		ttm_resource_free(bo, &dst_mem);
    505		return 0;
    506	}
    507
    508	/* Populate ttm with pages if needed. Typically system memory. */
    509	if (ttm && (dst_man->use_tt || (ttm->page_flags & TTM_TT_FLAG_SWAPPED))) {
    510		ret = ttm_tt_populate(bo->bdev, ttm, ctx);
    511		if (ret)
    512			return ret;
    513	}
    514
    515	dst_rsgt = i915_ttm_resource_get_st(obj, dst_mem);
    516	if (IS_ERR(dst_rsgt))
    517		return PTR_ERR(dst_rsgt);
    518
    519	clear = !i915_ttm_cpu_maps_iomem(bo->resource) && (!ttm || !ttm_tt_is_populated(ttm));
    520	if (!(clear && ttm && !(ttm->page_flags & TTM_TT_FLAG_ZERO_ALLOC))) {
    521		struct i915_deps deps;
    522
    523		i915_deps_init(&deps, GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN);
    524		ret = i915_deps_add_resv(&deps, bo->base.resv, ctx);
    525		if (ret) {
    526			i915_refct_sgt_put(dst_rsgt);
    527			return ret;
    528		}
    529
    530		migration_fence = __i915_ttm_move(bo, ctx, clear, dst_mem, ttm,
    531						  dst_rsgt, true, &deps);
    532		i915_deps_fini(&deps);
    533	}
    534
    535	/* We can possibly get an -ERESTARTSYS here */
    536	if (IS_ERR(migration_fence)) {
    537		i915_refct_sgt_put(dst_rsgt);
    538		return PTR_ERR(migration_fence);
    539	}
    540
    541	if (migration_fence) {
    542		ret = ttm_bo_move_accel_cleanup(bo, migration_fence, evict,
    543						true, dst_mem);
    544		if (ret) {
    545			dma_fence_wait(migration_fence, false);
    546			ttm_bo_move_sync_cleanup(bo, dst_mem);
    547		}
    548		dma_fence_put(migration_fence);
    549	} else {
    550		ttm_bo_move_sync_cleanup(bo, dst_mem);
    551	}
    552
    553	i915_ttm_adjust_domains_after_move(obj);
    554	i915_ttm_free_cached_io_rsgt(obj);
    555
    556	if (i915_ttm_gtt_binds_lmem(dst_mem) || i915_ttm_cpu_maps_iomem(dst_mem)) {
    557		obj->ttm.cached_io_rsgt = dst_rsgt;
    558		obj->ttm.get_io_page.sg_pos = dst_rsgt->table.sgl;
    559		obj->ttm.get_io_page.sg_idx = 0;
    560	} else {
    561		i915_refct_sgt_put(dst_rsgt);
    562	}
    563
    564	i915_ttm_adjust_lru(obj);
    565	i915_ttm_adjust_gem_after_move(obj);
    566	return 0;
    567}
    568
    569/**
    570 * i915_gem_obj_copy_ttm - Copy the contents of one ttm-based gem object to
    571 * another
    572 * @dst: The destination object
    573 * @src: The source object
    574 * @allow_accel: Allow using the blitter. Otherwise TTM memcpy is used.
    575 * @intr: Whether to perform waits interruptible:
    576 *
    577 * Note: The caller is responsible for assuring that the underlying
    578 * TTM objects are populated if needed and locked.
    579 *
    580 * Return: Zero on success. Negative error code on error. If @intr == true,
    581 * then it may return -ERESTARTSYS or -EINTR.
    582 */
    583int i915_gem_obj_copy_ttm(struct drm_i915_gem_object *dst,
    584			  struct drm_i915_gem_object *src,
    585			  bool allow_accel, bool intr)
    586{
    587	struct ttm_buffer_object *dst_bo = i915_gem_to_ttm(dst);
    588	struct ttm_buffer_object *src_bo = i915_gem_to_ttm(src);
    589	struct ttm_operation_ctx ctx = {
    590		.interruptible = intr,
    591	};
    592	struct i915_refct_sgt *dst_rsgt;
    593	struct dma_fence *copy_fence;
    594	struct i915_deps deps;
    595	int ret;
    596
    597	assert_object_held(dst);
    598	assert_object_held(src);
    599	i915_deps_init(&deps, GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN);
    600
    601	ret = dma_resv_reserve_fences(src_bo->base.resv, 1);
    602	if (ret)
    603		return ret;
    604
    605	ret = dma_resv_reserve_fences(dst_bo->base.resv, 1);
    606	if (ret)
    607		return ret;
    608
    609	ret = i915_deps_add_resv(&deps, dst_bo->base.resv, &ctx);
    610	if (ret)
    611		return ret;
    612
    613	ret = i915_deps_add_resv(&deps, src_bo->base.resv, &ctx);
    614	if (ret)
    615		return ret;
    616
    617	dst_rsgt = i915_ttm_resource_get_st(dst, dst_bo->resource);
    618	copy_fence = __i915_ttm_move(src_bo, &ctx, false, dst_bo->resource,
    619				     dst_bo->ttm, dst_rsgt, allow_accel,
    620				     &deps);
    621
    622	i915_deps_fini(&deps);
    623	i915_refct_sgt_put(dst_rsgt);
    624	if (IS_ERR_OR_NULL(copy_fence))
    625		return PTR_ERR_OR_ZERO(copy_fence);
    626
    627	dma_resv_add_fence(dst_bo->base.resv, copy_fence, DMA_RESV_USAGE_WRITE);
    628	dma_resv_add_fence(src_bo->base.resv, copy_fence, DMA_RESV_USAGE_READ);
    629	dma_fence_put(copy_fence);
    630
    631	return 0;
    632}