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

selftest_migrate.c (18904B)


      1// SPDX-License-Identifier: MIT
      2/*
      3 * Copyright © 2020 Intel Corporation
      4 */
      5
      6#include <linux/sort.h>
      7
      8#include "gem/i915_gem_internal.h"
      9
     10#include "selftests/i915_random.h"
     11
     12static const unsigned int sizes[] = {
     13	SZ_4K,
     14	SZ_64K,
     15	SZ_2M,
     16	CHUNK_SZ - SZ_4K,
     17	CHUNK_SZ,
     18	CHUNK_SZ + SZ_4K,
     19	SZ_64M,
     20};
     21
     22static struct drm_i915_gem_object *
     23create_lmem_or_internal(struct drm_i915_private *i915, size_t size)
     24{
     25	struct drm_i915_gem_object *obj;
     26
     27	obj = i915_gem_object_create_lmem(i915, size, 0);
     28	if (!IS_ERR(obj))
     29		return obj;
     30
     31	return i915_gem_object_create_internal(i915, size);
     32}
     33
     34static int copy(struct intel_migrate *migrate,
     35		int (*fn)(struct intel_migrate *migrate,
     36			  struct i915_gem_ww_ctx *ww,
     37			  struct drm_i915_gem_object *src,
     38			  struct drm_i915_gem_object *dst,
     39			  struct i915_request **out),
     40		u32 sz, struct rnd_state *prng)
     41{
     42	struct drm_i915_private *i915 = migrate->context->engine->i915;
     43	struct drm_i915_gem_object *src, *dst;
     44	struct i915_request *rq;
     45	struct i915_gem_ww_ctx ww;
     46	u32 *vaddr;
     47	int err = 0;
     48	int i;
     49
     50	src = create_lmem_or_internal(i915, sz);
     51	if (IS_ERR(src))
     52		return 0;
     53
     54	sz = src->base.size;
     55	dst = i915_gem_object_create_internal(i915, sz);
     56	if (IS_ERR(dst))
     57		goto err_free_src;
     58
     59	for_i915_gem_ww(&ww, err, true) {
     60		err = i915_gem_object_lock(src, &ww);
     61		if (err)
     62			continue;
     63
     64		err = i915_gem_object_lock(dst, &ww);
     65		if (err)
     66			continue;
     67
     68		vaddr = i915_gem_object_pin_map(src, I915_MAP_WC);
     69		if (IS_ERR(vaddr)) {
     70			err = PTR_ERR(vaddr);
     71			continue;
     72		}
     73
     74		for (i = 0; i < sz / sizeof(u32); i++)
     75			vaddr[i] = i;
     76		i915_gem_object_flush_map(src);
     77
     78		vaddr = i915_gem_object_pin_map(dst, I915_MAP_WC);
     79		if (IS_ERR(vaddr)) {
     80			err = PTR_ERR(vaddr);
     81			goto unpin_src;
     82		}
     83
     84		for (i = 0; i < sz / sizeof(u32); i++)
     85			vaddr[i] = ~i;
     86		i915_gem_object_flush_map(dst);
     87
     88		err = fn(migrate, &ww, src, dst, &rq);
     89		if (!err)
     90			continue;
     91
     92		if (err != -EDEADLK && err != -EINTR && err != -ERESTARTSYS)
     93			pr_err("%ps failed, size: %u\n", fn, sz);
     94		if (rq) {
     95			i915_request_wait(rq, 0, HZ);
     96			i915_request_put(rq);
     97		}
     98		i915_gem_object_unpin_map(dst);
     99unpin_src:
    100		i915_gem_object_unpin_map(src);
    101	}
    102	if (err)
    103		goto err_out;
    104
    105	if (rq) {
    106		if (i915_request_wait(rq, 0, HZ) < 0) {
    107			pr_err("%ps timed out, size: %u\n", fn, sz);
    108			err = -ETIME;
    109		}
    110		i915_request_put(rq);
    111	}
    112
    113	for (i = 0; !err && i < sz / PAGE_SIZE; i++) {
    114		int x = i * 1024 + i915_prandom_u32_max_state(1024, prng);
    115
    116		if (vaddr[x] != x) {
    117			pr_err("%ps failed, size: %u, offset: %zu\n",
    118			       fn, sz, x * sizeof(u32));
    119			igt_hexdump(vaddr + i * 1024, 4096);
    120			err = -EINVAL;
    121		}
    122	}
    123
    124	i915_gem_object_unpin_map(dst);
    125	i915_gem_object_unpin_map(src);
    126
    127err_out:
    128	i915_gem_object_put(dst);
    129err_free_src:
    130	i915_gem_object_put(src);
    131
    132	return err;
    133}
    134
    135static int intel_context_copy_ccs(struct intel_context *ce,
    136				  const struct i915_deps *deps,
    137				  struct scatterlist *sg,
    138				  enum i915_cache_level cache_level,
    139				  bool write_to_ccs,
    140				  struct i915_request **out)
    141{
    142	u8 src_access = write_to_ccs ? DIRECT_ACCESS : INDIRECT_ACCESS;
    143	u8 dst_access = write_to_ccs ? INDIRECT_ACCESS : DIRECT_ACCESS;
    144	struct sgt_dma it = sg_sgt(sg);
    145	struct i915_request *rq;
    146	u32 offset;
    147	int err;
    148
    149	GEM_BUG_ON(ce->vm != ce->engine->gt->migrate.context->vm);
    150	*out = NULL;
    151
    152	GEM_BUG_ON(ce->ring->size < SZ_64K);
    153
    154	offset = 0;
    155	if (HAS_64K_PAGES(ce->engine->i915))
    156		offset = CHUNK_SZ;
    157
    158	do {
    159		int len;
    160
    161		rq = i915_request_create(ce);
    162		if (IS_ERR(rq)) {
    163			err = PTR_ERR(rq);
    164			goto out_ce;
    165		}
    166
    167		if (deps) {
    168			err = i915_request_await_deps(rq, deps);
    169			if (err)
    170				goto out_rq;
    171
    172			if (rq->engine->emit_init_breadcrumb) {
    173				err = rq->engine->emit_init_breadcrumb(rq);
    174				if (err)
    175					goto out_rq;
    176			}
    177
    178			deps = NULL;
    179		}
    180
    181		/* The PTE updates + clear must not be interrupted. */
    182		err = emit_no_arbitration(rq);
    183		if (err)
    184			goto out_rq;
    185
    186		len = emit_pte(rq, &it, cache_level, true, offset, CHUNK_SZ);
    187		if (len <= 0) {
    188			err = len;
    189			goto out_rq;
    190		}
    191
    192		err = rq->engine->emit_flush(rq, EMIT_INVALIDATE);
    193		if (err)
    194			goto out_rq;
    195
    196		err = emit_copy_ccs(rq, offset, dst_access,
    197				    offset, src_access, len);
    198		if (err)
    199			goto out_rq;
    200
    201		err = rq->engine->emit_flush(rq, EMIT_INVALIDATE);
    202
    203		/* Arbitration is re-enabled between requests. */
    204out_rq:
    205		if (*out)
    206			i915_request_put(*out);
    207		*out = i915_request_get(rq);
    208		i915_request_add(rq);
    209		if (err || !it.sg || !sg_dma_len(it.sg))
    210			break;
    211
    212		cond_resched();
    213	} while (1);
    214
    215out_ce:
    216	return err;
    217}
    218
    219static int
    220intel_migrate_ccs_copy(struct intel_migrate *m,
    221		       struct i915_gem_ww_ctx *ww,
    222		       const struct i915_deps *deps,
    223		       struct scatterlist *sg,
    224		       enum i915_cache_level cache_level,
    225		       bool write_to_ccs,
    226		       struct i915_request **out)
    227{
    228	struct intel_context *ce;
    229	int err;
    230
    231	*out = NULL;
    232	if (!m->context)
    233		return -ENODEV;
    234
    235	ce = intel_migrate_create_context(m);
    236	if (IS_ERR(ce))
    237		ce = intel_context_get(m->context);
    238	GEM_BUG_ON(IS_ERR(ce));
    239
    240	err = intel_context_pin_ww(ce, ww);
    241	if (err)
    242		goto out;
    243
    244	err = intel_context_copy_ccs(ce, deps, sg, cache_level,
    245				     write_to_ccs, out);
    246
    247	intel_context_unpin(ce);
    248out:
    249	intel_context_put(ce);
    250	return err;
    251}
    252
    253static int clear(struct intel_migrate *migrate,
    254		 int (*fn)(struct intel_migrate *migrate,
    255			   struct i915_gem_ww_ctx *ww,
    256			   struct drm_i915_gem_object *obj,
    257			   u32 value,
    258			   struct i915_request **out),
    259		 u32 sz, struct rnd_state *prng)
    260{
    261	struct drm_i915_private *i915 = migrate->context->engine->i915;
    262	struct drm_i915_gem_object *obj;
    263	struct i915_request *rq;
    264	struct i915_gem_ww_ctx ww;
    265	u32 *vaddr, val = 0;
    266	bool ccs_cap = false;
    267	int err = 0;
    268	int i;
    269
    270	obj = create_lmem_or_internal(i915, sz);
    271	if (IS_ERR(obj))
    272		return 0;
    273
    274	/* Consider the rounded up memory too */
    275	sz = obj->base.size;
    276
    277	if (HAS_FLAT_CCS(i915) && i915_gem_object_is_lmem(obj))
    278		ccs_cap = true;
    279
    280	for_i915_gem_ww(&ww, err, true) {
    281		int ccs_bytes, ccs_bytes_per_chunk;
    282
    283		err = i915_gem_object_lock(obj, &ww);
    284		if (err)
    285			continue;
    286
    287		vaddr = i915_gem_object_pin_map(obj, I915_MAP_WC);
    288		if (IS_ERR(vaddr)) {
    289			err = PTR_ERR(vaddr);
    290			continue;
    291		}
    292
    293		for (i = 0; i < sz / sizeof(u32); i++)
    294			vaddr[i] = ~i;
    295		i915_gem_object_flush_map(obj);
    296
    297		if (ccs_cap && !val) {
    298			/* Write the obj data into ccs surface */
    299			err = intel_migrate_ccs_copy(migrate, &ww, NULL,
    300						     obj->mm.pages->sgl,
    301						     obj->cache_level,
    302						     true, &rq);
    303			if (rq && !err) {
    304				if (i915_request_wait(rq, 0, HZ) < 0) {
    305					pr_err("%ps timed out, size: %u\n",
    306					       fn, sz);
    307					err = -ETIME;
    308				}
    309				i915_request_put(rq);
    310				rq = NULL;
    311			}
    312			if (err)
    313				continue;
    314		}
    315
    316		err = fn(migrate, &ww, obj, val, &rq);
    317		if (rq && !err) {
    318			if (i915_request_wait(rq, 0, HZ) < 0) {
    319				pr_err("%ps timed out, size: %u\n", fn, sz);
    320				err = -ETIME;
    321			}
    322			i915_request_put(rq);
    323			rq = NULL;
    324		}
    325		if (err)
    326			continue;
    327
    328		i915_gem_object_flush_map(obj);
    329
    330		/* Verify the set/clear of the obj mem */
    331		for (i = 0; !err && i < sz / PAGE_SIZE; i++) {
    332			int x = i * 1024 +
    333				i915_prandom_u32_max_state(1024, prng);
    334
    335			if (vaddr[x] != val) {
    336				pr_err("%ps failed, (%u != %u), offset: %zu\n",
    337				       fn, vaddr[x], val,  x * sizeof(u32));
    338				igt_hexdump(vaddr + i * 1024, 4096);
    339				err = -EINVAL;
    340			}
    341		}
    342		if (err)
    343			continue;
    344
    345		if (ccs_cap && !val) {
    346			for (i = 0; i < sz / sizeof(u32); i++)
    347				vaddr[i] = ~i;
    348			i915_gem_object_flush_map(obj);
    349
    350			err = intel_migrate_ccs_copy(migrate, &ww, NULL,
    351						     obj->mm.pages->sgl,
    352						     obj->cache_level,
    353						     false, &rq);
    354			if (rq && !err) {
    355				if (i915_request_wait(rq, 0, HZ) < 0) {
    356					pr_err("%ps timed out, size: %u\n",
    357					       fn, sz);
    358					err = -ETIME;
    359				}
    360				i915_request_put(rq);
    361				rq = NULL;
    362			}
    363			if (err)
    364				continue;
    365
    366			ccs_bytes = GET_CCS_BYTES(i915, sz);
    367			ccs_bytes_per_chunk = GET_CCS_BYTES(i915, CHUNK_SZ);
    368			i915_gem_object_flush_map(obj);
    369
    370			for (i = 0; !err && i < DIV_ROUND_UP(ccs_bytes, PAGE_SIZE); i++) {
    371				int offset = ((i * PAGE_SIZE)  /
    372					ccs_bytes_per_chunk) * CHUNK_SZ / sizeof(u32);
    373				int ccs_bytes_left = (ccs_bytes - i * PAGE_SIZE) / sizeof(u32);
    374				int x = i915_prandom_u32_max_state(min_t(int, 1024,
    375									 ccs_bytes_left), prng);
    376
    377				if (vaddr[offset + x]) {
    378					pr_err("%ps ccs clearing failed, offset: %ld/%d\n",
    379					       fn, i * PAGE_SIZE + x * sizeof(u32), ccs_bytes);
    380					igt_hexdump(vaddr + offset,
    381						    min_t(int, 4096,
    382							  ccs_bytes_left * sizeof(u32)));
    383					err = -EINVAL;
    384				}
    385			}
    386
    387			if (err)
    388				continue;
    389		}
    390		i915_gem_object_unpin_map(obj);
    391	}
    392
    393	if (err) {
    394		if (err != -EDEADLK && err != -EINTR && err != -ERESTARTSYS)
    395			pr_err("%ps failed, size: %u\n", fn, sz);
    396		if (rq && err != -EINVAL) {
    397			i915_request_wait(rq, 0, HZ);
    398			i915_request_put(rq);
    399		}
    400
    401		i915_gem_object_unpin_map(obj);
    402	}
    403
    404	i915_gem_object_put(obj);
    405	return err;
    406}
    407
    408static int __migrate_copy(struct intel_migrate *migrate,
    409			  struct i915_gem_ww_ctx *ww,
    410			  struct drm_i915_gem_object *src,
    411			  struct drm_i915_gem_object *dst,
    412			  struct i915_request **out)
    413{
    414	return intel_migrate_copy(migrate, ww, NULL,
    415				  src->mm.pages->sgl, src->cache_level,
    416				  i915_gem_object_is_lmem(src),
    417				  dst->mm.pages->sgl, dst->cache_level,
    418				  i915_gem_object_is_lmem(dst),
    419				  out);
    420}
    421
    422static int __global_copy(struct intel_migrate *migrate,
    423			 struct i915_gem_ww_ctx *ww,
    424			 struct drm_i915_gem_object *src,
    425			 struct drm_i915_gem_object *dst,
    426			 struct i915_request **out)
    427{
    428	return intel_context_migrate_copy(migrate->context, NULL,
    429					  src->mm.pages->sgl, src->cache_level,
    430					  i915_gem_object_is_lmem(src),
    431					  dst->mm.pages->sgl, dst->cache_level,
    432					  i915_gem_object_is_lmem(dst),
    433					  out);
    434}
    435
    436static int
    437migrate_copy(struct intel_migrate *migrate, u32 sz, struct rnd_state *prng)
    438{
    439	return copy(migrate, __migrate_copy, sz, prng);
    440}
    441
    442static int
    443global_copy(struct intel_migrate *migrate, u32 sz, struct rnd_state *prng)
    444{
    445	return copy(migrate, __global_copy, sz, prng);
    446}
    447
    448static int __migrate_clear(struct intel_migrate *migrate,
    449			   struct i915_gem_ww_ctx *ww,
    450			   struct drm_i915_gem_object *obj,
    451			   u32 value,
    452			   struct i915_request **out)
    453{
    454	return intel_migrate_clear(migrate, ww, NULL,
    455				   obj->mm.pages->sgl,
    456				   obj->cache_level,
    457				   i915_gem_object_is_lmem(obj),
    458				   value, out);
    459}
    460
    461static int __global_clear(struct intel_migrate *migrate,
    462			  struct i915_gem_ww_ctx *ww,
    463			  struct drm_i915_gem_object *obj,
    464			  u32 value,
    465			  struct i915_request **out)
    466{
    467	return intel_context_migrate_clear(migrate->context, NULL,
    468					   obj->mm.pages->sgl,
    469					   obj->cache_level,
    470					   i915_gem_object_is_lmem(obj),
    471					   value, out);
    472}
    473
    474static int
    475migrate_clear(struct intel_migrate *migrate, u32 sz, struct rnd_state *prng)
    476{
    477	return clear(migrate, __migrate_clear, sz, prng);
    478}
    479
    480static int
    481global_clear(struct intel_migrate *migrate, u32 sz, struct rnd_state *prng)
    482{
    483	return clear(migrate, __global_clear, sz, prng);
    484}
    485
    486static int live_migrate_copy(void *arg)
    487{
    488	struct intel_migrate *migrate = arg;
    489	struct drm_i915_private *i915 = migrate->context->engine->i915;
    490	I915_RND_STATE(prng);
    491	int i;
    492
    493	for (i = 0; i < ARRAY_SIZE(sizes); i++) {
    494		int err;
    495
    496		err = migrate_copy(migrate, sizes[i], &prng);
    497		if (err == 0)
    498			err = global_copy(migrate, sizes[i], &prng);
    499		i915_gem_drain_freed_objects(i915);
    500		if (err)
    501			return err;
    502	}
    503
    504	return 0;
    505}
    506
    507static int live_migrate_clear(void *arg)
    508{
    509	struct intel_migrate *migrate = arg;
    510	struct drm_i915_private *i915 = migrate->context->engine->i915;
    511	I915_RND_STATE(prng);
    512	int i;
    513
    514	for (i = 0; i < ARRAY_SIZE(sizes); i++) {
    515		int err;
    516
    517		err = migrate_clear(migrate, sizes[i], &prng);
    518		if (err == 0)
    519			err = global_clear(migrate, sizes[i], &prng);
    520
    521		i915_gem_drain_freed_objects(i915);
    522		if (err)
    523			return err;
    524	}
    525
    526	return 0;
    527}
    528
    529struct threaded_migrate {
    530	struct intel_migrate *migrate;
    531	struct task_struct *tsk;
    532	struct rnd_state prng;
    533};
    534
    535static int threaded_migrate(struct intel_migrate *migrate,
    536			    int (*fn)(void *arg),
    537			    unsigned int flags)
    538{
    539	const unsigned int n_cpus = num_online_cpus() + 1;
    540	struct threaded_migrate *thread;
    541	I915_RND_STATE(prng);
    542	unsigned int i;
    543	int err = 0;
    544
    545	thread = kcalloc(n_cpus, sizeof(*thread), GFP_KERNEL);
    546	if (!thread)
    547		return 0;
    548
    549	for (i = 0; i < n_cpus; ++i) {
    550		struct task_struct *tsk;
    551
    552		thread[i].migrate = migrate;
    553		thread[i].prng =
    554			I915_RND_STATE_INITIALIZER(prandom_u32_state(&prng));
    555
    556		tsk = kthread_run(fn, &thread[i], "igt-%d", i);
    557		if (IS_ERR(tsk)) {
    558			err = PTR_ERR(tsk);
    559			break;
    560		}
    561
    562		get_task_struct(tsk);
    563		thread[i].tsk = tsk;
    564	}
    565
    566	msleep(10); /* start all threads before we kthread_stop() */
    567
    568	for (i = 0; i < n_cpus; ++i) {
    569		struct task_struct *tsk = thread[i].tsk;
    570		int status;
    571
    572		if (IS_ERR_OR_NULL(tsk))
    573			continue;
    574
    575		status = kthread_stop(tsk);
    576		if (status && !err)
    577			err = status;
    578
    579		put_task_struct(tsk);
    580	}
    581
    582	kfree(thread);
    583	return err;
    584}
    585
    586static int __thread_migrate_copy(void *arg)
    587{
    588	struct threaded_migrate *tm = arg;
    589
    590	return migrate_copy(tm->migrate, 2 * CHUNK_SZ, &tm->prng);
    591}
    592
    593static int thread_migrate_copy(void *arg)
    594{
    595	return threaded_migrate(arg, __thread_migrate_copy, 0);
    596}
    597
    598static int __thread_global_copy(void *arg)
    599{
    600	struct threaded_migrate *tm = arg;
    601
    602	return global_copy(tm->migrate, 2 * CHUNK_SZ, &tm->prng);
    603}
    604
    605static int thread_global_copy(void *arg)
    606{
    607	return threaded_migrate(arg, __thread_global_copy, 0);
    608}
    609
    610static int __thread_migrate_clear(void *arg)
    611{
    612	struct threaded_migrate *tm = arg;
    613
    614	return migrate_clear(tm->migrate, 2 * CHUNK_SZ, &tm->prng);
    615}
    616
    617static int __thread_global_clear(void *arg)
    618{
    619	struct threaded_migrate *tm = arg;
    620
    621	return global_clear(tm->migrate, 2 * CHUNK_SZ, &tm->prng);
    622}
    623
    624static int thread_migrate_clear(void *arg)
    625{
    626	return threaded_migrate(arg, __thread_migrate_clear, 0);
    627}
    628
    629static int thread_global_clear(void *arg)
    630{
    631	return threaded_migrate(arg, __thread_global_clear, 0);
    632}
    633
    634int intel_migrate_live_selftests(struct drm_i915_private *i915)
    635{
    636	static const struct i915_subtest tests[] = {
    637		SUBTEST(live_migrate_copy),
    638		SUBTEST(live_migrate_clear),
    639		SUBTEST(thread_migrate_copy),
    640		SUBTEST(thread_migrate_clear),
    641		SUBTEST(thread_global_copy),
    642		SUBTEST(thread_global_clear),
    643	};
    644	struct intel_gt *gt = to_gt(i915);
    645
    646	if (!gt->migrate.context)
    647		return 0;
    648
    649	return i915_subtests(tests, &gt->migrate);
    650}
    651
    652static struct drm_i915_gem_object *
    653create_init_lmem_internal(struct intel_gt *gt, size_t sz, bool try_lmem)
    654{
    655	struct drm_i915_gem_object *obj = NULL;
    656	int err;
    657
    658	if (try_lmem)
    659		obj = i915_gem_object_create_lmem(gt->i915, sz, 0);
    660
    661	if (IS_ERR_OR_NULL(obj)) {
    662		obj = i915_gem_object_create_internal(gt->i915, sz);
    663		if (IS_ERR(obj))
    664			return obj;
    665	}
    666
    667	i915_gem_object_trylock(obj, NULL);
    668	err = i915_gem_object_pin_pages(obj);
    669	if (err) {
    670		i915_gem_object_unlock(obj);
    671		i915_gem_object_put(obj);
    672		return ERR_PTR(err);
    673	}
    674
    675	return obj;
    676}
    677
    678static int wrap_ktime_compare(const void *A, const void *B)
    679{
    680	const ktime_t *a = A, *b = B;
    681
    682	return ktime_compare(*a, *b);
    683}
    684
    685static int __perf_clear_blt(struct intel_context *ce,
    686			    struct scatterlist *sg,
    687			    enum i915_cache_level cache_level,
    688			    bool is_lmem,
    689			    size_t sz)
    690{
    691	ktime_t t[5];
    692	int pass;
    693	int err = 0;
    694
    695	for (pass = 0; pass < ARRAY_SIZE(t); pass++) {
    696		struct i915_request *rq;
    697		ktime_t t0, t1;
    698
    699		t0 = ktime_get();
    700
    701		err = intel_context_migrate_clear(ce, NULL, sg, cache_level,
    702						  is_lmem, 0, &rq);
    703		if (rq) {
    704			if (i915_request_wait(rq, 0, MAX_SCHEDULE_TIMEOUT) < 0)
    705				err = -EIO;
    706			i915_request_put(rq);
    707		}
    708		if (err)
    709			break;
    710
    711		t1 = ktime_get();
    712		t[pass] = ktime_sub(t1, t0);
    713	}
    714	if (err)
    715		return err;
    716
    717	sort(t, ARRAY_SIZE(t), sizeof(*t), wrap_ktime_compare, NULL);
    718	pr_info("%s: %zd KiB fill: %lld MiB/s\n",
    719		ce->engine->name, sz >> 10,
    720		div64_u64(mul_u32_u32(4 * sz,
    721				      1000 * 1000 * 1000),
    722			  t[1] + 2 * t[2] + t[3]) >> 20);
    723	return 0;
    724}
    725
    726static int perf_clear_blt(void *arg)
    727{
    728	struct intel_gt *gt = arg;
    729	static const unsigned long sizes[] = {
    730		SZ_4K,
    731		SZ_64K,
    732		SZ_2M,
    733		SZ_64M
    734	};
    735	int i;
    736
    737	for (i = 0; i < ARRAY_SIZE(sizes); i++) {
    738		struct drm_i915_gem_object *dst;
    739		int err;
    740
    741		dst = create_init_lmem_internal(gt, sizes[i], true);
    742		if (IS_ERR(dst))
    743			return PTR_ERR(dst);
    744
    745		err = __perf_clear_blt(gt->migrate.context,
    746				       dst->mm.pages->sgl,
    747				       I915_CACHE_NONE,
    748				       i915_gem_object_is_lmem(dst),
    749				       sizes[i]);
    750
    751		i915_gem_object_unlock(dst);
    752		i915_gem_object_put(dst);
    753		if (err)
    754			return err;
    755	}
    756
    757	return 0;
    758}
    759
    760static int __perf_copy_blt(struct intel_context *ce,
    761			   struct scatterlist *src,
    762			   enum i915_cache_level src_cache_level,
    763			   bool src_is_lmem,
    764			   struct scatterlist *dst,
    765			   enum i915_cache_level dst_cache_level,
    766			   bool dst_is_lmem,
    767			   size_t sz)
    768{
    769	ktime_t t[5];
    770	int pass;
    771	int err = 0;
    772
    773	for (pass = 0; pass < ARRAY_SIZE(t); pass++) {
    774		struct i915_request *rq;
    775		ktime_t t0, t1;
    776
    777		t0 = ktime_get();
    778
    779		err = intel_context_migrate_copy(ce, NULL,
    780						 src, src_cache_level,
    781						 src_is_lmem,
    782						 dst, dst_cache_level,
    783						 dst_is_lmem,
    784						 &rq);
    785		if (rq) {
    786			if (i915_request_wait(rq, 0, MAX_SCHEDULE_TIMEOUT) < 0)
    787				err = -EIO;
    788			i915_request_put(rq);
    789		}
    790		if (err)
    791			break;
    792
    793		t1 = ktime_get();
    794		t[pass] = ktime_sub(t1, t0);
    795	}
    796	if (err)
    797		return err;
    798
    799	sort(t, ARRAY_SIZE(t), sizeof(*t), wrap_ktime_compare, NULL);
    800	pr_info("%s: %zd KiB copy: %lld MiB/s\n",
    801		ce->engine->name, sz >> 10,
    802		div64_u64(mul_u32_u32(4 * sz,
    803				      1000 * 1000 * 1000),
    804			  t[1] + 2 * t[2] + t[3]) >> 20);
    805	return 0;
    806}
    807
    808static int perf_copy_blt(void *arg)
    809{
    810	struct intel_gt *gt = arg;
    811	static const unsigned long sizes[] = {
    812		SZ_4K,
    813		SZ_64K,
    814		SZ_2M,
    815		SZ_64M
    816	};
    817	int i;
    818
    819	for (i = 0; i < ARRAY_SIZE(sizes); i++) {
    820		struct drm_i915_gem_object *src, *dst;
    821		size_t sz;
    822		int err;
    823
    824		src = create_init_lmem_internal(gt, sizes[i], true);
    825		if (IS_ERR(src))
    826			return PTR_ERR(src);
    827
    828		sz = src->base.size;
    829		dst = create_init_lmem_internal(gt, sz, false);
    830		if (IS_ERR(dst)) {
    831			err = PTR_ERR(dst);
    832			goto err_src;
    833		}
    834
    835		err = __perf_copy_blt(gt->migrate.context,
    836				      src->mm.pages->sgl,
    837				      I915_CACHE_NONE,
    838				      i915_gem_object_is_lmem(src),
    839				      dst->mm.pages->sgl,
    840				      I915_CACHE_NONE,
    841				      i915_gem_object_is_lmem(dst),
    842				      sz);
    843
    844		i915_gem_object_unlock(dst);
    845		i915_gem_object_put(dst);
    846err_src:
    847		i915_gem_object_unlock(src);
    848		i915_gem_object_put(src);
    849		if (err)
    850			return err;
    851	}
    852
    853	return 0;
    854}
    855
    856int intel_migrate_perf_selftests(struct drm_i915_private *i915)
    857{
    858	static const struct i915_subtest tests[] = {
    859		SUBTEST(perf_clear_blt),
    860		SUBTEST(perf_copy_blt),
    861	};
    862	struct intel_gt *gt = to_gt(i915);
    863
    864	if (intel_gt_is_wedged(gt))
    865		return 0;
    866
    867	if (!gt->migrate.context)
    868		return 0;
    869
    870	return intel_gt_live_subtests(tests, gt);
    871}