selftest_migrate.c revision 1.1
1// SPDX-License-Identifier: MIT
2/*
3 * Copyright �� 2020 Intel Corporation
4 */
5
6#include <linux/sort.h>
7
8#include "selftests/i915_random.h"
9
10static const unsigned int sizes[] = {
11	SZ_4K,
12	SZ_64K,
13	SZ_2M,
14	CHUNK_SZ - SZ_4K,
15	CHUNK_SZ,
16	CHUNK_SZ + SZ_4K,
17	SZ_64M,
18};
19
20static struct drm_i915_gem_object *
21create_lmem_or_internal(struct drm_i915_private *i915, size_t size)
22{
23	struct drm_i915_gem_object *obj;
24
25	obj = i915_gem_object_create_lmem(i915, size, 0);
26	if (!IS_ERR(obj))
27		return obj;
28
29	return i915_gem_object_create_internal(i915, size);
30}
31
32static int copy(struct intel_migrate *migrate,
33		int (*fn)(struct intel_migrate *migrate,
34			  struct i915_gem_ww_ctx *ww,
35			  struct drm_i915_gem_object *src,
36			  struct drm_i915_gem_object *dst,
37			  struct i915_request **out),
38		u32 sz, struct rnd_state *prng)
39{
40	struct drm_i915_private *i915 = migrate->context->engine->i915;
41	struct drm_i915_gem_object *src, *dst;
42	struct i915_request *rq;
43	struct i915_gem_ww_ctx ww;
44	u32 *vaddr;
45	int err = 0;
46	int i;
47
48	src = create_lmem_or_internal(i915, sz);
49	if (IS_ERR(src))
50		return 0;
51
52	dst = i915_gem_object_create_internal(i915, sz);
53	if (IS_ERR(dst))
54		goto err_free_src;
55
56	for_i915_gem_ww(&ww, err, true) {
57		err = i915_gem_object_lock(src, &ww);
58		if (err)
59			continue;
60
61		err = i915_gem_object_lock(dst, &ww);
62		if (err)
63			continue;
64
65		vaddr = i915_gem_object_pin_map(src, I915_MAP_WC);
66		if (IS_ERR(vaddr)) {
67			err = PTR_ERR(vaddr);
68			continue;
69		}
70
71		for (i = 0; i < sz / sizeof(u32); i++)
72			vaddr[i] = i;
73		i915_gem_object_flush_map(src);
74
75		vaddr = i915_gem_object_pin_map(dst, I915_MAP_WC);
76		if (IS_ERR(vaddr)) {
77			err = PTR_ERR(vaddr);
78			goto unpin_src;
79		}
80
81		for (i = 0; i < sz / sizeof(u32); i++)
82			vaddr[i] = ~i;
83		i915_gem_object_flush_map(dst);
84
85		err = fn(migrate, &ww, src, dst, &rq);
86		if (!err)
87			continue;
88
89		if (err != -EDEADLK && err != -EINTR && err != -ERESTARTSYS)
90			pr_err("%ps failed, size: %u\n", fn, sz);
91		if (rq) {
92			i915_request_wait(rq, 0, HZ);
93			i915_request_put(rq);
94		}
95		i915_gem_object_unpin_map(dst);
96unpin_src:
97		i915_gem_object_unpin_map(src);
98	}
99	if (err)
100		goto err_out;
101
102	if (rq) {
103		if (i915_request_wait(rq, 0, HZ) < 0) {
104			pr_err("%ps timed out, size: %u\n", fn, sz);
105			err = -ETIME;
106		}
107		i915_request_put(rq);
108	}
109
110	for (i = 0; !err && i < sz / PAGE_SIZE; i++) {
111		int x = i * 1024 + i915_prandom_u32_max_state(1024, prng);
112
113		if (vaddr[x] != x) {
114			pr_err("%ps failed, size: %u, offset: %zu\n",
115			       fn, sz, x * sizeof(u32));
116			igt_hexdump(vaddr + i * 1024, 4096);
117			err = -EINVAL;
118		}
119	}
120
121	i915_gem_object_unpin_map(dst);
122	i915_gem_object_unpin_map(src);
123
124err_out:
125	i915_gem_object_put(dst);
126err_free_src:
127	i915_gem_object_put(src);
128
129	return err;
130}
131
132static int clear(struct intel_migrate *migrate,
133		 int (*fn)(struct intel_migrate *migrate,
134			   struct i915_gem_ww_ctx *ww,
135			   struct drm_i915_gem_object *obj,
136			   u32 value,
137			   struct i915_request **out),
138		 u32 sz, struct rnd_state *prng)
139{
140	struct drm_i915_private *i915 = migrate->context->engine->i915;
141	struct drm_i915_gem_object *obj;
142	struct i915_request *rq;
143	struct i915_gem_ww_ctx ww;
144	u32 *vaddr;
145	int err = 0;
146	int i;
147
148	obj = create_lmem_or_internal(i915, sz);
149	if (IS_ERR(obj))
150		return 0;
151
152	for_i915_gem_ww(&ww, err, true) {
153		err = i915_gem_object_lock(obj, &ww);
154		if (err)
155			continue;
156
157		vaddr = i915_gem_object_pin_map(obj, I915_MAP_WC);
158		if (IS_ERR(vaddr)) {
159			err = PTR_ERR(vaddr);
160			continue;
161		}
162
163		for (i = 0; i < sz / sizeof(u32); i++)
164			vaddr[i] = ~i;
165		i915_gem_object_flush_map(obj);
166
167		err = fn(migrate, &ww, obj, sz, &rq);
168		if (!err)
169			continue;
170
171		if (err != -EDEADLK && err != -EINTR && err != -ERESTARTSYS)
172			pr_err("%ps failed, size: %u\n", fn, sz);
173		if (rq) {
174			i915_request_wait(rq, 0, HZ);
175			i915_request_put(rq);
176		}
177		i915_gem_object_unpin_map(obj);
178	}
179	if (err)
180		goto err_out;
181
182	if (rq) {
183		if (i915_request_wait(rq, 0, HZ) < 0) {
184			pr_err("%ps timed out, size: %u\n", fn, sz);
185			err = -ETIME;
186		}
187		i915_request_put(rq);
188	}
189
190	for (i = 0; !err && i < sz / PAGE_SIZE; i++) {
191		int x = i * 1024 + i915_prandom_u32_max_state(1024, prng);
192
193		if (vaddr[x] != sz) {
194			pr_err("%ps failed, size: %u, offset: %zu\n",
195			       fn, sz, x * sizeof(u32));
196			igt_hexdump(vaddr + i * 1024, 4096);
197			err = -EINVAL;
198		}
199	}
200
201	i915_gem_object_unpin_map(obj);
202err_out:
203	i915_gem_object_put(obj);
204
205	return err;
206}
207
208static int __migrate_copy(struct intel_migrate *migrate,
209			  struct i915_gem_ww_ctx *ww,
210			  struct drm_i915_gem_object *src,
211			  struct drm_i915_gem_object *dst,
212			  struct i915_request **out)
213{
214	return intel_migrate_copy(migrate, ww, NULL,
215				  src->mm.pages->sgl, src->cache_level,
216				  i915_gem_object_is_lmem(src),
217				  dst->mm.pages->sgl, dst->cache_level,
218				  i915_gem_object_is_lmem(dst),
219				  out);
220}
221
222static int __global_copy(struct intel_migrate *migrate,
223			 struct i915_gem_ww_ctx *ww,
224			 struct drm_i915_gem_object *src,
225			 struct drm_i915_gem_object *dst,
226			 struct i915_request **out)
227{
228	return intel_context_migrate_copy(migrate->context, NULL,
229					  src->mm.pages->sgl, src->cache_level,
230					  i915_gem_object_is_lmem(src),
231					  dst->mm.pages->sgl, dst->cache_level,
232					  i915_gem_object_is_lmem(dst),
233					  out);
234}
235
236static int
237migrate_copy(struct intel_migrate *migrate, u32 sz, struct rnd_state *prng)
238{
239	return copy(migrate, __migrate_copy, sz, prng);
240}
241
242static int
243global_copy(struct intel_migrate *migrate, u32 sz, struct rnd_state *prng)
244{
245	return copy(migrate, __global_copy, sz, prng);
246}
247
248static int __migrate_clear(struct intel_migrate *migrate,
249			   struct i915_gem_ww_ctx *ww,
250			   struct drm_i915_gem_object *obj,
251			   u32 value,
252			   struct i915_request **out)
253{
254	return intel_migrate_clear(migrate, ww, NULL,
255				   obj->mm.pages->sgl,
256				   obj->cache_level,
257				   i915_gem_object_is_lmem(obj),
258				   value, out);
259}
260
261static int __global_clear(struct intel_migrate *migrate,
262			  struct i915_gem_ww_ctx *ww,
263			  struct drm_i915_gem_object *obj,
264			  u32 value,
265			  struct i915_request **out)
266{
267	return intel_context_migrate_clear(migrate->context, NULL,
268					   obj->mm.pages->sgl,
269					   obj->cache_level,
270					   i915_gem_object_is_lmem(obj),
271					   value, out);
272}
273
274static int
275migrate_clear(struct intel_migrate *migrate, u32 sz, struct rnd_state *prng)
276{
277	return clear(migrate, __migrate_clear, sz, prng);
278}
279
280static int
281global_clear(struct intel_migrate *migrate, u32 sz, struct rnd_state *prng)
282{
283	return clear(migrate, __global_clear, sz, prng);
284}
285
286static int live_migrate_copy(void *arg)
287{
288	struct intel_migrate *migrate = arg;
289	struct drm_i915_private *i915 = migrate->context->engine->i915;
290	I915_RND_STATE(prng);
291	int i;
292
293	for (i = 0; i < ARRAY_SIZE(sizes); i++) {
294		int err;
295
296		err = migrate_copy(migrate, sizes[i], &prng);
297		if (err == 0)
298			err = global_copy(migrate, sizes[i], &prng);
299		i915_gem_drain_freed_objects(i915);
300		if (err)
301			return err;
302	}
303
304	return 0;
305}
306
307static int live_migrate_clear(void *arg)
308{
309	struct intel_migrate *migrate = arg;
310	struct drm_i915_private *i915 = migrate->context->engine->i915;
311	I915_RND_STATE(prng);
312	int i;
313
314	for (i = 0; i < ARRAY_SIZE(sizes); i++) {
315		int err;
316
317		err = migrate_clear(migrate, sizes[i], &prng);
318		if (err == 0)
319			err = global_clear(migrate, sizes[i], &prng);
320
321		i915_gem_drain_freed_objects(i915);
322		if (err)
323			return err;
324	}
325
326	return 0;
327}
328
329struct threaded_migrate {
330	struct intel_migrate *migrate;
331	struct task_struct *tsk;
332	struct rnd_state prng;
333};
334
335static int threaded_migrate(struct intel_migrate *migrate,
336			    int (*fn)(void *arg),
337			    unsigned int flags)
338{
339	const unsigned int n_cpus = num_online_cpus() + 1;
340	struct threaded_migrate *thread;
341	I915_RND_STATE(prng);
342	unsigned int i;
343	int err = 0;
344
345	thread = kcalloc(n_cpus, sizeof(*thread), GFP_KERNEL);
346	if (!thread)
347		return 0;
348
349	for (i = 0; i < n_cpus; ++i) {
350		struct task_struct *tsk;
351
352		thread[i].migrate = migrate;
353		thread[i].prng =
354			I915_RND_STATE_INITIALIZER(prandom_u32_state(&prng));
355
356		tsk = kthread_run(fn, &thread[i], "igt-%d", i);
357		if (IS_ERR(tsk)) {
358			err = PTR_ERR(tsk);
359			break;
360		}
361
362		get_task_struct(tsk);
363		thread[i].tsk = tsk;
364	}
365
366	drm_msleep(10); /* start all threads before we kthread_stop() */
367
368	for (i = 0; i < n_cpus; ++i) {
369		struct task_struct *tsk = thread[i].tsk;
370		int status;
371
372		if (IS_ERR_OR_NULL(tsk))
373			continue;
374
375		status = kthread_stop(tsk);
376		if (status && !err)
377			err = status;
378
379		put_task_struct(tsk);
380	}
381
382	kfree(thread);
383	return err;
384}
385
386static int __thread_migrate_copy(void *arg)
387{
388	struct threaded_migrate *tm = arg;
389
390	return migrate_copy(tm->migrate, 2 * CHUNK_SZ, &tm->prng);
391}
392
393static int thread_migrate_copy(void *arg)
394{
395	return threaded_migrate(arg, __thread_migrate_copy, 0);
396}
397
398static int __thread_global_copy(void *arg)
399{
400	struct threaded_migrate *tm = arg;
401
402	return global_copy(tm->migrate, 2 * CHUNK_SZ, &tm->prng);
403}
404
405static int thread_global_copy(void *arg)
406{
407	return threaded_migrate(arg, __thread_global_copy, 0);
408}
409
410static int __thread_migrate_clear(void *arg)
411{
412	struct threaded_migrate *tm = arg;
413
414	return migrate_clear(tm->migrate, 2 * CHUNK_SZ, &tm->prng);
415}
416
417static int __thread_global_clear(void *arg)
418{
419	struct threaded_migrate *tm = arg;
420
421	return global_clear(tm->migrate, 2 * CHUNK_SZ, &tm->prng);
422}
423
424static int thread_migrate_clear(void *arg)
425{
426	return threaded_migrate(arg, __thread_migrate_clear, 0);
427}
428
429static int thread_global_clear(void *arg)
430{
431	return threaded_migrate(arg, __thread_global_clear, 0);
432}
433
434int intel_migrate_live_selftests(struct drm_i915_private *i915)
435{
436	static const struct i915_subtest tests[] = {
437		SUBTEST(live_migrate_copy),
438		SUBTEST(live_migrate_clear),
439		SUBTEST(thread_migrate_copy),
440		SUBTEST(thread_migrate_clear),
441		SUBTEST(thread_global_copy),
442		SUBTEST(thread_global_clear),
443	};
444	struct intel_gt *gt = &i915->gt;
445
446	if (!gt->migrate.context)
447		return 0;
448
449	return i915_subtests(tests, &gt->migrate);
450}
451
452static struct drm_i915_gem_object *
453create_init_lmem_internal(struct intel_gt *gt, size_t sz, bool try_lmem)
454{
455	struct drm_i915_gem_object *obj = NULL;
456	int err;
457
458	if (try_lmem)
459		obj = i915_gem_object_create_lmem(gt->i915, sz, 0);
460
461	if (IS_ERR_OR_NULL(obj)) {
462		obj = i915_gem_object_create_internal(gt->i915, sz);
463		if (IS_ERR(obj))
464			return obj;
465	}
466
467	i915_gem_object_trylock(obj);
468	err = i915_gem_object_pin_pages(obj);
469	if (err) {
470		i915_gem_object_unlock(obj);
471		i915_gem_object_put(obj);
472		return ERR_PTR(err);
473	}
474
475	return obj;
476}
477
478static int wrap_ktime_compare(const void *A, const void *B)
479{
480	const ktime_t *a = A, *b = B;
481
482	return ktime_compare(*a, *b);
483}
484
485static int __perf_clear_blt(struct intel_context *ce,
486			    struct scatterlist *sg,
487			    enum i915_cache_level cache_level,
488			    bool is_lmem,
489			    size_t sz)
490{
491	ktime_t t[5];
492	int pass;
493	int err = 0;
494
495	for (pass = 0; pass < ARRAY_SIZE(t); pass++) {
496		struct i915_request *rq;
497		ktime_t t0, t1;
498
499		t0 = ktime_get();
500
501		err = intel_context_migrate_clear(ce, NULL, sg, cache_level,
502						  is_lmem, 0, &rq);
503		if (rq) {
504			if (i915_request_wait(rq, 0, MAX_SCHEDULE_TIMEOUT) < 0)
505				err = -EIO;
506			i915_request_put(rq);
507		}
508		if (err)
509			break;
510
511		t1 = ktime_get();
512		t[pass] = ktime_sub(t1, t0);
513	}
514	if (err)
515		return err;
516
517	sort(t, ARRAY_SIZE(t), sizeof(*t), wrap_ktime_compare, NULL);
518	pr_info("%s: %zd KiB fill: %lld MiB/s\n",
519		ce->engine->name, sz >> 10,
520		div64_u64(mul_u32_u32(4 * sz,
521				      1000 * 1000 * 1000),
522			  t[1] + 2 * t[2] + t[3]) >> 20);
523	return 0;
524}
525
526static int perf_clear_blt(void *arg)
527{
528	struct intel_gt *gt = arg;
529	static const unsigned long sizes[] = {
530		SZ_4K,
531		SZ_64K,
532		SZ_2M,
533		SZ_64M
534	};
535	int i;
536
537	for (i = 0; i < ARRAY_SIZE(sizes); i++) {
538		struct drm_i915_gem_object *dst;
539		int err;
540
541		dst = create_init_lmem_internal(gt, sizes[i], true);
542		if (IS_ERR(dst))
543			return PTR_ERR(dst);
544
545		err = __perf_clear_blt(gt->migrate.context,
546				       dst->mm.pages->sgl,
547				       I915_CACHE_NONE,
548				       i915_gem_object_is_lmem(dst),
549				       sizes[i]);
550
551		i915_gem_object_unlock(dst);
552		i915_gem_object_put(dst);
553		if (err)
554			return err;
555	}
556
557	return 0;
558}
559
560static int __perf_copy_blt(struct intel_context *ce,
561			   struct scatterlist *src,
562			   enum i915_cache_level src_cache_level,
563			   bool src_is_lmem,
564			   struct scatterlist *dst,
565			   enum i915_cache_level dst_cache_level,
566			   bool dst_is_lmem,
567			   size_t sz)
568{
569	ktime_t t[5];
570	int pass;
571	int err = 0;
572
573	for (pass = 0; pass < ARRAY_SIZE(t); pass++) {
574		struct i915_request *rq;
575		ktime_t t0, t1;
576
577		t0 = ktime_get();
578
579		err = intel_context_migrate_copy(ce, NULL,
580						 src, src_cache_level,
581						 src_is_lmem,
582						 dst, dst_cache_level,
583						 dst_is_lmem,
584						 &rq);
585		if (rq) {
586			if (i915_request_wait(rq, 0, MAX_SCHEDULE_TIMEOUT) < 0)
587				err = -EIO;
588			i915_request_put(rq);
589		}
590		if (err)
591			break;
592
593		t1 = ktime_get();
594		t[pass] = ktime_sub(t1, t0);
595	}
596	if (err)
597		return err;
598
599	sort(t, ARRAY_SIZE(t), sizeof(*t), wrap_ktime_compare, NULL);
600	pr_info("%s: %zd KiB copy: %lld MiB/s\n",
601		ce->engine->name, sz >> 10,
602		div64_u64(mul_u32_u32(4 * sz,
603				      1000 * 1000 * 1000),
604			  t[1] + 2 * t[2] + t[3]) >> 20);
605	return 0;
606}
607
608static int perf_copy_blt(void *arg)
609{
610	struct intel_gt *gt = arg;
611	static const unsigned long sizes[] = {
612		SZ_4K,
613		SZ_64K,
614		SZ_2M,
615		SZ_64M
616	};
617	int i;
618
619	for (i = 0; i < ARRAY_SIZE(sizes); i++) {
620		struct drm_i915_gem_object *src, *dst;
621		int err;
622
623		src = create_init_lmem_internal(gt, sizes[i], true);
624		if (IS_ERR(src))
625			return PTR_ERR(src);
626
627		dst = create_init_lmem_internal(gt, sizes[i], false);
628		if (IS_ERR(dst)) {
629			err = PTR_ERR(dst);
630			goto err_src;
631		}
632
633		err = __perf_copy_blt(gt->migrate.context,
634				      src->mm.pages->sgl,
635				      I915_CACHE_NONE,
636				      i915_gem_object_is_lmem(src),
637				      dst->mm.pages->sgl,
638				      I915_CACHE_NONE,
639				      i915_gem_object_is_lmem(dst),
640				      sizes[i]);
641
642		i915_gem_object_unlock(dst);
643		i915_gem_object_put(dst);
644err_src:
645		i915_gem_object_unlock(src);
646		i915_gem_object_put(src);
647		if (err)
648			return err;
649	}
650
651	return 0;
652}
653
654int intel_migrate_perf_selftests(struct drm_i915_private *i915)
655{
656	static const struct i915_subtest tests[] = {
657		SUBTEST(perf_clear_blt),
658		SUBTEST(perf_copy_blt),
659	};
660	struct intel_gt *gt = &i915->gt;
661
662	if (intel_gt_is_wedged(gt))
663		return 0;
664
665	if (!gt->migrate.context)
666		return 0;
667
668	return intel_gt_live_subtests(tests, gt);
669}
670