linux/drivers/gpu/drm/i915/gt/selftest_engine_pm.c
Chris Wilson 07779a76ee drm/i915: Mark up the calling context for intel_wakeref_put()
Previously, we assumed we could use mutex_trylock() within an atomic
context, falling back to a worker if contended. However, such trickery
is illegal inside interrupt context, and so we need to always use a
worker under such circumstances. As we normally are in process context,
we can typically use a plain mutex, and only defer to a work when we
know we are being called from an interrupt path.

Fixes: 51fbd8de87 ("drm/i915/pmu: Atomically acquire the gt_pm wakeref")
References: a0855d24fc ("locking/mutex: Complain upon mutex API misuse in IRQ contexts")
References: https://bugs.freedesktop.org/show_bug.cgi?id=111626
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191120125433.3767149-1-chris@chris-wilson.co.uk
2019-11-20 15:59:23 +00:00

85 lines
2.1 KiB
C

/*
* SPDX-License-Identifier: GPL-2.0
*
* Copyright © 2018 Intel Corporation
*/
#include "i915_selftest.h"
#include "selftest_engine.h"
#include "selftests/igt_atomic.h"
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_pm),
};
return intel_gt_live_subtests(tests, gt);
}