18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * SPDX-License-Identifier: MIT
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright © 2018 Intel Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/random.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "gem/selftests/igt_gem_utils.h"
108c2ecf20Sopenharmony_ci#include "gem/selftests/mock_context.h"
118c2ecf20Sopenharmony_ci#include "gt/intel_gt.h"
128c2ecf20Sopenharmony_ci#include "gt/intel_gt_pm.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "i915_selftest.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "igt_flush_test.h"
178c2ecf20Sopenharmony_ci#include "mock_drm.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int switch_to_context(struct i915_gem_context *ctx)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct i915_gem_engines_iter it;
228c2ecf20Sopenharmony_ci	struct intel_context *ce;
238c2ecf20Sopenharmony_ci	int err = 0;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it) {
268c2ecf20Sopenharmony_ci		struct i915_request *rq;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci		rq = intel_context_create_request(ce);
298c2ecf20Sopenharmony_ci		if (IS_ERR(rq)) {
308c2ecf20Sopenharmony_ci			err = PTR_ERR(rq);
318c2ecf20Sopenharmony_ci			break;
328c2ecf20Sopenharmony_ci		}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci		i915_request_add(rq);
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci	i915_gem_context_unlock_engines(ctx);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return err;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void trash_stolen(struct drm_i915_private *i915)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct i915_ggtt *ggtt = &i915->ggtt;
448c2ecf20Sopenharmony_ci	const u64 slot = ggtt->error_capture.start;
458c2ecf20Sopenharmony_ci	const resource_size_t size = resource_size(&i915->dsm);
468c2ecf20Sopenharmony_ci	unsigned long page;
478c2ecf20Sopenharmony_ci	u32 prng = 0x12345678;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/* XXX: fsck. needs some more thought... */
508c2ecf20Sopenharmony_ci	if (!i915_ggtt_has_aperture(ggtt))
518c2ecf20Sopenharmony_ci		return;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	for (page = 0; page < size; page += PAGE_SIZE) {
548c2ecf20Sopenharmony_ci		const dma_addr_t dma = i915->dsm.start + page;
558c2ecf20Sopenharmony_ci		u32 __iomem *s;
568c2ecf20Sopenharmony_ci		int x;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci		ggtt->vm.insert_page(&ggtt->vm, dma, slot, I915_CACHE_NONE, 0);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci		s = io_mapping_map_atomic_wc(&ggtt->iomap, slot);
618c2ecf20Sopenharmony_ci		for (x = 0; x < PAGE_SIZE / sizeof(u32); x++) {
628c2ecf20Sopenharmony_ci			prng = next_pseudo_random32(prng);
638c2ecf20Sopenharmony_ci			iowrite32(prng, &s[x]);
648c2ecf20Sopenharmony_ci		}
658c2ecf20Sopenharmony_ci		io_mapping_unmap_atomic(s);
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	ggtt->vm.clear_range(&ggtt->vm, slot, PAGE_SIZE);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void simulate_hibernate(struct drm_i915_private *i915)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	intel_wakeref_t wakeref;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/*
788c2ecf20Sopenharmony_ci	 * As a final sting in the tail, invalidate stolen. Under a real S4,
798c2ecf20Sopenharmony_ci	 * stolen is lost and needs to be refilled on resume. However, under
808c2ecf20Sopenharmony_ci	 * CI we merely do S4-device testing (as full S4 is too unreliable
818c2ecf20Sopenharmony_ci	 * for automated testing across a cluster), so to simulate the effect
828c2ecf20Sopenharmony_ci	 * of stolen being trashed across S4, we trash it ourselves.
838c2ecf20Sopenharmony_ci	 */
848c2ecf20Sopenharmony_ci	trash_stolen(i915);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic int pm_prepare(struct drm_i915_private *i915)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	i915_gem_suspend(i915);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return 0;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void pm_suspend(struct drm_i915_private *i915)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	intel_wakeref_t wakeref;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
1018c2ecf20Sopenharmony_ci		i915_ggtt_suspend(&i915->ggtt);
1028c2ecf20Sopenharmony_ci		i915_gem_suspend_late(i915);
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic void pm_hibernate(struct drm_i915_private *i915)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	intel_wakeref_t wakeref;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
1118c2ecf20Sopenharmony_ci		i915_ggtt_suspend(&i915->ggtt);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		i915_gem_freeze(i915);
1148c2ecf20Sopenharmony_ci		i915_gem_freeze_late(i915);
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic void pm_resume(struct drm_i915_private *i915)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	intel_wakeref_t wakeref;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/*
1238c2ecf20Sopenharmony_ci	 * Both suspend and hibernate follow the same wakeup path and assume
1248c2ecf20Sopenharmony_ci	 * that runtime-pm just works.
1258c2ecf20Sopenharmony_ci	 */
1268c2ecf20Sopenharmony_ci	with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
1278c2ecf20Sopenharmony_ci		i915_ggtt_resume(&i915->ggtt);
1288c2ecf20Sopenharmony_ci		i915_gem_resume(i915);
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int igt_gem_suspend(void *arg)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = arg;
1358c2ecf20Sopenharmony_ci	struct i915_gem_context *ctx;
1368c2ecf20Sopenharmony_ci	struct file *file;
1378c2ecf20Sopenharmony_ci	int err;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	file = mock_file(i915);
1408c2ecf20Sopenharmony_ci	if (IS_ERR(file))
1418c2ecf20Sopenharmony_ci		return PTR_ERR(file);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	err = -ENOMEM;
1448c2ecf20Sopenharmony_ci	ctx = live_context(i915, file);
1458c2ecf20Sopenharmony_ci	if (!IS_ERR(ctx))
1468c2ecf20Sopenharmony_ci		err = switch_to_context(ctx);
1478c2ecf20Sopenharmony_ci	if (err)
1488c2ecf20Sopenharmony_ci		goto out;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	err = pm_prepare(i915);
1518c2ecf20Sopenharmony_ci	if (err)
1528c2ecf20Sopenharmony_ci		goto out;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	pm_suspend(i915);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/* Here be dragons! Note that with S3RST any S3 may become S4! */
1578c2ecf20Sopenharmony_ci	simulate_hibernate(i915);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	pm_resume(i915);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	err = switch_to_context(ctx);
1628c2ecf20Sopenharmony_ciout:
1638c2ecf20Sopenharmony_ci	fput(file);
1648c2ecf20Sopenharmony_ci	return err;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic int igt_gem_hibernate(void *arg)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = arg;
1708c2ecf20Sopenharmony_ci	struct i915_gem_context *ctx;
1718c2ecf20Sopenharmony_ci	struct file *file;
1728c2ecf20Sopenharmony_ci	int err;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	file = mock_file(i915);
1758c2ecf20Sopenharmony_ci	if (IS_ERR(file))
1768c2ecf20Sopenharmony_ci		return PTR_ERR(file);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	err = -ENOMEM;
1798c2ecf20Sopenharmony_ci	ctx = live_context(i915, file);
1808c2ecf20Sopenharmony_ci	if (!IS_ERR(ctx))
1818c2ecf20Sopenharmony_ci		err = switch_to_context(ctx);
1828c2ecf20Sopenharmony_ci	if (err)
1838c2ecf20Sopenharmony_ci		goto out;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	err = pm_prepare(i915);
1868c2ecf20Sopenharmony_ci	if (err)
1878c2ecf20Sopenharmony_ci		goto out;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	pm_hibernate(i915);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/* Here be dragons! */
1928c2ecf20Sopenharmony_ci	simulate_hibernate(i915);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	pm_resume(i915);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	err = switch_to_context(ctx);
1978c2ecf20Sopenharmony_ciout:
1988c2ecf20Sopenharmony_ci	fput(file);
1998c2ecf20Sopenharmony_ci	return err;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic int igt_gem_ww_ctx(void *arg)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = arg;
2058c2ecf20Sopenharmony_ci	struct drm_i915_gem_object *obj, *obj2;
2068c2ecf20Sopenharmony_ci	struct i915_gem_ww_ctx ww;
2078c2ecf20Sopenharmony_ci	int err = 0;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
2108c2ecf20Sopenharmony_ci	if (IS_ERR(obj))
2118c2ecf20Sopenharmony_ci		return PTR_ERR(obj);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	obj2 = i915_gem_object_create_internal(i915, PAGE_SIZE);
2148c2ecf20Sopenharmony_ci	if (IS_ERR(obj2)) {
2158c2ecf20Sopenharmony_ci		err = PTR_ERR(obj2);
2168c2ecf20Sopenharmony_ci		goto put1;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	i915_gem_ww_ctx_init(&ww, true);
2208c2ecf20Sopenharmony_ciretry:
2218c2ecf20Sopenharmony_ci	/* Lock the objects, twice for good measure (-EALREADY handling) */
2228c2ecf20Sopenharmony_ci	err = i915_gem_object_lock(obj, &ww);
2238c2ecf20Sopenharmony_ci	if (!err)
2248c2ecf20Sopenharmony_ci		err = i915_gem_object_lock_interruptible(obj, &ww);
2258c2ecf20Sopenharmony_ci	if (!err)
2268c2ecf20Sopenharmony_ci		err = i915_gem_object_lock_interruptible(obj2, &ww);
2278c2ecf20Sopenharmony_ci	if (!err)
2288c2ecf20Sopenharmony_ci		err = i915_gem_object_lock(obj2, &ww);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (err == -EDEADLK) {
2318c2ecf20Sopenharmony_ci		err = i915_gem_ww_ctx_backoff(&ww);
2328c2ecf20Sopenharmony_ci		if (!err)
2338c2ecf20Sopenharmony_ci			goto retry;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci	i915_gem_ww_ctx_fini(&ww);
2368c2ecf20Sopenharmony_ci	i915_gem_object_put(obj2);
2378c2ecf20Sopenharmony_ciput1:
2388c2ecf20Sopenharmony_ci	i915_gem_object_put(obj);
2398c2ecf20Sopenharmony_ci	return err;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ciint i915_gem_live_selftests(struct drm_i915_private *i915)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	static const struct i915_subtest tests[] = {
2458c2ecf20Sopenharmony_ci		SUBTEST(igt_gem_suspend),
2468c2ecf20Sopenharmony_ci		SUBTEST(igt_gem_hibernate),
2478c2ecf20Sopenharmony_ci		SUBTEST(igt_gem_ww_ctx),
2488c2ecf20Sopenharmony_ci	};
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (intel_gt_is_wedged(&i915->gt))
2518c2ecf20Sopenharmony_ci		return 0;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	return i915_live_subtests(tests, i915);
2548c2ecf20Sopenharmony_ci}
255