blob: b08fc5390e8afcbfc17387d1eedf21b4b2ea0f32 [file] [log] [blame]
/*
* SPDX-License-Identifier: GPL-2.0
*
* Copyright © 2018 Intel Corporation
*/
#include "i915_selftest.h"
#include "selftest_engine.h"
#include "selftest_engine_heartbeat.h"
#include "selftests/igt_atomic.h"
#include "selftests/igt_flush_test.h"
#include "selftests/igt_spinner.h"
static int live_engine_busy_stats(void *arg)
{
struct intel_gt *gt = arg;
struct intel_engine_cs *engine;
enum intel_engine_id id;
struct igt_spinner spin;
int err = 0;
/*
* Check that if an engine supports busy-stats, they tell the truth.
*/
if (igt_spinner_init(&spin, gt))
return -ENOMEM;
GEM_BUG_ON(intel_gt_pm_is_awake(gt));
for_each_engine(engine, gt, id) {
struct i915_request *rq;
ktime_t de, dt;
ktime_t t[2];
if (!intel_engine_supports_stats(engine))
continue;
if (!intel_engine_can_store_dword(engine))
continue;
if (intel_gt_pm_wait_for_idle(gt)) {
err = -EBUSY;
break;
}
st_engine_heartbeat_disable(engine);
ENGINE_TRACE(engine, "measuring idle time\n");
preempt_disable();
de = intel_engine_get_busy_time(engine, &t[0]);
udelay(100);
de = ktime_sub(intel_engine_get_busy_time(engine, &t[1]), de);
preempt_enable();
dt = ktime_sub(t[1], t[0]);
if (de < 0 || de > 10) {
pr_err("%s: reported %lldns [%d%%] busyness while sleeping [for %lldns]\n",
engine->name,
de, (int)div64_u64(100 * de, dt), dt);
GEM_TRACE_DUMP();
err = -EINVAL;
goto end;
}
/* 100% busy */
rq = igt_spinner_create_request(&spin,
engine->kernel_context,
MI_NOOP);
if (IS_ERR(rq)) {
err = PTR_ERR(rq);
goto end;
}
i915_request_add(rq);
if (!igt_wait_for_spinner(&spin, rq)) {
intel_gt_set_wedged(engine->gt);
err = -ETIME;
goto end;
}
ENGINE_TRACE(engine, "measuring busy time\n");
preempt_disable();
de = intel_engine_get_busy_time(engine, &t[0]);
udelay(100);
de = ktime_sub(intel_engine_get_busy_time(engine, &t[1]), de);
preempt_enable();
dt = ktime_sub(t[1], t[0]);
if (100 * de < 95 * dt || 95 * de > 100 * dt) {
pr_err("%s: reported %lldns [%d%%] busyness while spinning [for %lldns]\n",
engine->name,
de, (int)div64_u64(100 * de, dt), dt);
GEM_TRACE_DUMP();
err = -EINVAL;
goto end;
}
end:
st_engine_heartbeat_enable(engine);
igt_spinner_end(&spin);
if (igt_flush_test(gt->i915))
err = -EIO;
if (err)
break;
}
igt_spinner_fini(&spin);
if (igt_flush_test(gt->i915))
err = -EIO;
return err;
}
static int live_engine_pm(void *arg)
{
struct intel_gt *gt = arg;
struct intel_engine_cs *engine;
enum intel_engine_id id;
/*
* Check we can call intel_engine_pm_put from any context. No
* failures are reported directly, but if we mess up lockdep should
* tell us.
*/
if (intel_gt_pm_wait_for_idle(gt)) {
pr_err("Unable to flush GT pm before test\n");
return -EBUSY;
}
GEM_BUG_ON(intel_gt_pm_is_awake(gt));
for_each_engine(engine, gt, id) {
const typeof(*igt_atomic_phases) *p;
for (p = igt_atomic_phases; p->name; p++) {
/*
* Acquisition is always synchronous, except if we
* know that the engine is already awake, in which
* case we should use intel_engine_pm_get_if_awake()
* to atomically grab the wakeref.
*
* In practice,
* intel_engine_pm_get();
* intel_engine_pm_put();
* occurs in one thread, while simultaneously
* intel_engine_pm_get_if_awake();
* intel_engine_pm_put();
* occurs from atomic context in another.
*/
GEM_BUG_ON(intel_engine_pm_is_awake(engine));
intel_engine_pm_get(engine);
p->critical_section_begin();
if (!intel_engine_pm_get_if_awake(engine))
pr_err("intel_engine_pm_get_if_awake(%s) failed under %s\n",
engine->name, p->name);
else
intel_engine_pm_put_async(engine);
intel_engine_pm_put_async(engine);
p->critical_section_end();
intel_engine_pm_flush(engine);
if (intel_engine_pm_is_awake(engine)) {
pr_err("%s is still awake after flushing pm\n",
engine->name);
return -EINVAL;
}
/* gt wakeref is async (deferred to workqueue) */
if (intel_gt_pm_wait_for_idle(gt)) {
pr_err("GT failed to idle\n");
return -EINVAL;
}
}
}
return 0;
}
int live_engine_pm_selftests(struct intel_gt *gt)
{
static const struct i915_subtest tests[] = {
SUBTEST(live_engine_busy_stats),
SUBTEST(live_engine_pm),
};
return intel_gt_live_subtests(tests, gt);
}