Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,354 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2018 Intel Corporation
*/
#include <linux/kref.h>
#include <linux/string_helpers.h>
#include <drm/drm_print.h>
#include "gem/i915_gem_pm.h"
#include "gt/intel_gt.h"
#include "i915_selftest.h"
#include "igt_flush_test.h"
#include "lib_sw_fence.h"
struct live_active {
struct i915_active base;
struct kref ref;
bool retired;
};
static void __live_get(struct live_active *active)
{
kref_get(&active->ref);
}
static void __live_free(struct live_active *active)
{
i915_active_fini(&active->base);
kfree(active);
}
static void __live_release(struct kref *ref)
{
struct live_active *active = container_of(ref, typeof(*active), ref);
__live_free(active);
}
static void __live_put(struct live_active *active)
{
kref_put(&active->ref, __live_release);
}
static int __live_active(struct i915_active *base)
{
struct live_active *active = container_of(base, typeof(*active), base);
__live_get(active);
return 0;
}
static void __live_retire(struct i915_active *base)
{
struct live_active *active = container_of(base, typeof(*active), base);
active->retired = true;
__live_put(active);
}
static struct live_active *__live_alloc(struct drm_i915_private *i915)
{
struct live_active *active;
active = kzalloc_obj(*active);
if (!active)
return NULL;
kref_init(&active->ref);
i915_active_init(&active->base, __live_active, __live_retire, 0);
return active;
}
static struct live_active *
__live_active_setup(struct drm_i915_private *i915)
{
struct intel_engine_cs *engine;
struct i915_sw_fence *submit;
struct live_active *active;
unsigned int count = 0;
int err = 0;
active = __live_alloc(i915);
if (!active)
return ERR_PTR(-ENOMEM);
submit = heap_fence_create(GFP_KERNEL);
if (!submit) {
kfree(active);
return ERR_PTR(-ENOMEM);
}
err = i915_active_acquire(&active->base);
if (err)
goto out;
for_each_uabi_engine(engine, i915) {
struct i915_request *rq;
rq = intel_engine_create_kernel_request(engine);
if (IS_ERR(rq)) {
err = PTR_ERR(rq);
break;
}
err = i915_sw_fence_await_sw_fence_gfp(&rq->submit,
submit,
GFP_KERNEL);
if (err >= 0)
err = i915_active_add_request(&active->base, rq);
i915_request_add(rq);
if (err) {
pr_err("Failed to track active ref!\n");
break;
}
count++;
}
i915_active_release(&active->base);
if (READ_ONCE(active->retired) && count) {
pr_err("i915_active retired before submission!\n");
err = -EINVAL;
}
if (atomic_read(&active->base.count) != count) {
pr_err("i915_active not tracking all requests, found %d, expected %d\n",
atomic_read(&active->base.count), count);
err = -EINVAL;
}
out:
i915_sw_fence_commit(submit);
heap_fence_put(submit);
if (err) {
__live_put(active);
active = ERR_PTR(err);
}
return active;
}
static int live_active_wait(void *arg)
{
struct drm_i915_private *i915 = arg;
struct live_active *active;
int err = 0;
/* Check that we get a callback when requests retire upon waiting */
active = __live_active_setup(i915);
if (IS_ERR(active))
return PTR_ERR(active);
__i915_active_wait(&active->base, TASK_UNINTERRUPTIBLE);
if (!READ_ONCE(active->retired)) {
struct drm_printer p = drm_err_printer(&i915->drm, __func__);
drm_printf(&p, "i915_active not retired after waiting!\n");
i915_active_print(&active->base, &p);
err = -EINVAL;
}
__live_put(active);
if (igt_flush_test(i915))
err = -EIO;
return err;
}
static int live_active_retire(void *arg)
{
struct drm_i915_private *i915 = arg;
struct live_active *active;
int err = 0;
/* Check that we get a callback when requests are indirectly retired */
active = __live_active_setup(i915);
if (IS_ERR(active))
return PTR_ERR(active);
/* waits for & retires all requests */
if (igt_flush_test(i915))
err = -EIO;
if (!READ_ONCE(active->retired)) {
struct drm_printer p = drm_err_printer(&i915->drm, __func__);
drm_printf(&p, "i915_active not retired after flushing!\n");
i915_active_print(&active->base, &p);
err = -EINVAL;
}
__live_put(active);
return err;
}
static int live_active_barrier(void *arg)
{
struct drm_i915_private *i915 = arg;
struct intel_engine_cs *engine;
struct live_active *active;
int err = 0;
/* Check that we get a callback when requests retire upon waiting */
active = __live_alloc(i915);
if (!active)
return -ENOMEM;
err = i915_active_acquire(&active->base);
if (err)
goto out;
for_each_uabi_engine(engine, i915) {
err = i915_active_acquire_preallocate_barrier(&active->base,
engine);
if (err)
break;
i915_active_acquire_barrier(&active->base);
}
i915_active_release(&active->base);
if (err)
goto out;
__i915_active_wait(&active->base, TASK_UNINTERRUPTIBLE);
if (!READ_ONCE(active->retired)) {
pr_err("i915_active not retired after flushing barriers!\n");
err = -EINVAL;
}
out:
__live_put(active);
if (igt_flush_test(i915))
err = -EIO;
return err;
}
int i915_active_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
SUBTEST(live_active_wait),
SUBTEST(live_active_retire),
SUBTEST(live_active_barrier),
};
if (intel_gt_is_wedged(to_gt(i915)))
return 0;
return i915_subtests(tests, i915);
}
static struct intel_engine_cs *node_to_barrier(struct active_node *it)
{
struct intel_engine_cs *engine;
if (!is_barrier(&it->base))
return NULL;
engine = __barrier_to_engine(it);
smp_rmb(); /* serialise with add_active_barriers */
if (!is_barrier(&it->base))
return NULL;
return engine;
}
void i915_active_print(struct i915_active *ref, struct drm_printer *m)
{
drm_printf(m, "active %ps:%ps\n", ref->active, ref->retire);
drm_printf(m, "\tcount: %d\n", atomic_read(&ref->count));
drm_printf(m, "\tpreallocated barriers? %s\n",
str_yes_no(!llist_empty(&ref->preallocated_barriers)));
if (i915_active_acquire_if_busy(ref)) {
struct active_node *it, *n;
rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
struct intel_engine_cs *engine;
engine = node_to_barrier(it);
if (engine) {
drm_printf(m, "\tbarrier: %s\n", engine->name);
continue;
}
if (i915_active_fence_isset(&it->base)) {
drm_printf(m,
"\ttimeline: %llx\n", it->timeline);
continue;
}
}
i915_active_release(ref);
}
}
static void spin_unlock_wait(spinlock_t *lock)
{
spin_lock_irq(lock);
spin_unlock_irq(lock);
}
static void active_flush(struct i915_active *ref,
struct i915_active_fence *active)
{
struct dma_fence *fence;
fence = xchg(__active_fence_slot(active), NULL);
if (!fence)
return;
spin_lock_irq(dma_fence_spinlock(fence));
__list_del_entry(&active->cb.node);
spin_unlock_irq(dma_fence_spinlock(fence)); /* serialise with fence->cb_list */
atomic_dec(&ref->count);
GEM_BUG_ON(!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags));
}
void i915_active_unlock_wait(struct i915_active *ref)
{
if (i915_active_acquire_if_busy(ref)) {
struct active_node *it, *n;
/* Wait for all active callbacks */
rcu_read_lock();
active_flush(ref, &ref->excl);
rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node)
active_flush(ref, &it->base);
rcu_read_unlock();
i915_active_release(ref);
}
/* And wait for the retire callback */
spin_unlock_wait(&ref->tree_lock);
/* ... which may have been on a thread instead */
flush_work(&ref->work);
}
@@ -0,0 +1,262 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2018 Intel Corporation
*/
#include <linux/prandom.h>
#include "gem/i915_gem_internal.h"
#include "gem/i915_gem_pm.h"
#include "gem/selftests/igt_gem_utils.h"
#include "gem/selftests/mock_context.h"
#include "gt/intel_gt.h"
#include "gt/intel_gt_pm.h"
#include "i915_selftest.h"
#include "igt_flush_test.h"
#include "mock_drm.h"
static int switch_to_context(struct i915_gem_context *ctx)
{
struct i915_gem_engines_iter it;
struct intel_context *ce;
int err = 0;
for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it) {
struct i915_request *rq;
rq = intel_context_create_request(ce);
if (IS_ERR(rq)) {
err = PTR_ERR(rq);
break;
}
i915_request_add(rq);
}
i915_gem_context_unlock_engines(ctx);
return err;
}
static void trash_stolen(struct drm_i915_private *i915)
{
struct i915_ggtt *ggtt = to_gt(i915)->ggtt;
const u64 slot = ggtt->error_capture.start;
const resource_size_t size = resource_size(&i915->dsm.stolen);
struct rnd_state prng;
unsigned long page;
/* XXX: fsck. needs some more thought... */
if (!i915_ggtt_has_aperture(ggtt))
return;
prandom_seed_state(&prng, 0x12345678);
for (page = 0; page < size; page += PAGE_SIZE) {
const dma_addr_t dma = i915->dsm.stolen.start + page;
u32 __iomem *s;
int x;
ggtt->vm.insert_page(&ggtt->vm, dma, slot,
i915_gem_get_pat_index(i915,
I915_CACHE_NONE),
0);
s = io_mapping_map_atomic_wc(&ggtt->iomap, slot);
for (x = 0; x < PAGE_SIZE / sizeof(u32); x++) {
iowrite32(prandom_u32_state(&prng), &s[x]);
}
io_mapping_unmap_atomic(s);
}
ggtt->vm.clear_range(&ggtt->vm, slot, PAGE_SIZE);
}
static void simulate_hibernate(struct drm_i915_private *i915)
{
intel_wakeref_t wakeref;
wakeref = intel_runtime_pm_get(&i915->runtime_pm);
/*
* As a final string in the tail, invalidate stolen. Under a real S4,
* stolen is lost and needs to be refilled on resume. However, under
* CI we merely do S4-device testing (as full S4 is too unreliable
* for automated testing across a cluster), so to simulate the effect
* of stolen being trashed across S4, we trash it ourselves.
*/
trash_stolen(i915);
intel_runtime_pm_put(&i915->runtime_pm, wakeref);
}
static int igt_pm_prepare(struct drm_i915_private *i915)
{
i915_gem_suspend(i915);
return 0;
}
static void igt_pm_suspend(struct drm_i915_private *i915)
{
intel_wakeref_t wakeref;
with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
i915_ggtt_suspend(to_gt(i915)->ggtt);
i915_gem_suspend_late(i915);
}
}
static void igt_pm_hibernate(struct drm_i915_private *i915)
{
intel_wakeref_t wakeref;
with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
i915_ggtt_suspend(to_gt(i915)->ggtt);
i915_gem_freeze(i915);
i915_gem_freeze_late(i915);
}
}
static void igt_pm_resume(struct drm_i915_private *i915)
{
intel_wakeref_t wakeref;
/*
* Both suspend and hibernate follow the same wakeup path and assume
* that runtime-pm just works.
*/
with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
i915_ggtt_resume(to_gt(i915)->ggtt);
if (GRAPHICS_VER(i915) >= 8)
setup_private_pat(to_gt(i915));
i915_gem_resume(i915);
}
}
static int igt_gem_suspend(void *arg)
{
struct drm_i915_private *i915 = arg;
struct i915_gem_context *ctx;
struct file *file;
int err;
file = mock_file(i915);
if (IS_ERR(file))
return PTR_ERR(file);
err = -ENOMEM;
ctx = live_context(i915, file);
if (!IS_ERR(ctx))
err = switch_to_context(ctx);
if (err)
goto out;
err = igt_pm_prepare(i915);
if (err)
goto out;
igt_pm_suspend(i915);
/* Here be dragons! Note that with S3RST any S3 may become S4! */
simulate_hibernate(i915);
igt_pm_resume(i915);
err = switch_to_context(ctx);
out:
fput(file);
return err;
}
static int igt_gem_hibernate(void *arg)
{
struct drm_i915_private *i915 = arg;
struct i915_gem_context *ctx;
struct file *file;
int err;
file = mock_file(i915);
if (IS_ERR(file))
return PTR_ERR(file);
err = -ENOMEM;
ctx = live_context(i915, file);
if (!IS_ERR(ctx))
err = switch_to_context(ctx);
if (err)
goto out;
err = igt_pm_prepare(i915);
if (err)
goto out;
igt_pm_hibernate(i915);
/* Here be dragons! */
simulate_hibernate(i915);
igt_pm_resume(i915);
err = switch_to_context(ctx);
out:
fput(file);
return err;
}
static int igt_gem_ww_ctx(void *arg)
{
struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj, *obj2;
struct i915_gem_ww_ctx ww;
int err = 0;
obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
if (IS_ERR(obj))
return PTR_ERR(obj);
obj2 = i915_gem_object_create_internal(i915, PAGE_SIZE);
if (IS_ERR(obj2)) {
err = PTR_ERR(obj2);
goto put1;
}
i915_gem_ww_ctx_init(&ww, true);
retry:
/* Lock the objects, twice for good measure (-EALREADY handling) */
err = i915_gem_object_lock(obj, &ww);
if (!err)
err = i915_gem_object_lock_interruptible(obj, &ww);
if (!err)
err = i915_gem_object_lock_interruptible(obj2, &ww);
if (!err)
err = i915_gem_object_lock(obj2, &ww);
if (err == -EDEADLK) {
err = i915_gem_ww_ctx_backoff(&ww);
if (!err)
goto retry;
}
i915_gem_ww_ctx_fini(&ww);
i915_gem_object_put(obj2);
put1:
i915_gem_object_put(obj);
return err;
}
int i915_gem_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_gem_suspend),
SUBTEST(igt_gem_hibernate),
SUBTEST(igt_gem_ww_ctx),
};
if (intel_gt_is_wedged(to_gt(i915)))
return 0;
return i915_live_subtests(tests, i915);
}
@@ -0,0 +1,571 @@
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "gem/i915_gem_internal.h"
#include "gem/i915_gem_pm.h"
#include "gem/selftests/igt_gem_utils.h"
#include "gem/selftests/mock_context.h"
#include "gt/intel_gt.h"
#include "gt/intel_gt_print.h"
#include "i915_selftest.h"
#include "igt_flush_test.h"
#include "lib_sw_fence.h"
#include "mock_drm.h"
#include "mock_gem_device.h"
static void quirk_add(struct drm_i915_gem_object *obj,
struct list_head *objects)
{
/* quirk is only for live tiled objects, use it to declare ownership */
GEM_BUG_ON(i915_gem_object_has_tiling_quirk(obj));
i915_gem_object_set_tiling_quirk(obj);
list_add(&obj->st_link, objects);
}
static int populate_ggtt(struct i915_ggtt *ggtt, struct list_head *objects)
{
struct drm_i915_gem_object *obj;
unsigned long count;
count = 0;
do {
struct i915_vma *vma;
obj = i915_gem_object_create_internal(ggtt->vm.i915,
I915_GTT_PAGE_SIZE);
if (IS_ERR(obj))
return PTR_ERR(obj);
vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0);
if (IS_ERR(vma)) {
i915_gem_object_put(obj);
if (vma == ERR_PTR(-ENOSPC))
break;
return PTR_ERR(vma);
}
quirk_add(obj, objects);
count++;
} while (1);
pr_debug("Filled GGTT with %lu pages [%llu total]\n",
count, ggtt->vm.total / PAGE_SIZE);
if (list_empty(&ggtt->vm.bound_list)) {
pr_err("No objects on the GGTT inactive list!\n");
return -EINVAL;
}
return 0;
}
static void unpin_ggtt(struct i915_ggtt *ggtt)
{
struct i915_vma *vma;
list_for_each_entry(vma, &ggtt->vm.bound_list, vm_link)
if (i915_gem_object_has_tiling_quirk(vma->obj))
i915_vma_unpin(vma);
}
static void cleanup_objects(struct i915_ggtt *ggtt, struct list_head *list)
{
struct drm_i915_gem_object *obj, *on;
list_for_each_entry_safe(obj, on, list, st_link) {
GEM_BUG_ON(!i915_gem_object_has_tiling_quirk(obj));
i915_gem_object_set_tiling_quirk(obj);
i915_gem_object_put(obj);
}
i915_gem_drain_freed_objects(ggtt->vm.i915);
}
static int igt_evict_something(void *arg)
{
struct intel_gt *gt = arg;
struct i915_ggtt *ggtt = gt->ggtt;
LIST_HEAD(objects);
int err;
/* Fill the GGTT with pinned objects and try to evict one. */
err = populate_ggtt(ggtt, &objects);
if (err)
goto cleanup;
/* Everything is pinned, nothing should happen */
mutex_lock(&ggtt->vm.mutex);
err = i915_gem_evict_something(&ggtt->vm, NULL,
I915_GTT_PAGE_SIZE, 0, 0,
0, U64_MAX,
0);
mutex_unlock(&ggtt->vm.mutex);
if (err != -ENOSPC) {
pr_err("i915_gem_evict_something failed on a full GGTT with err=%d\n",
err);
goto cleanup;
}
unpin_ggtt(ggtt);
/* Everything is unpinned, we should be able to evict something */
mutex_lock(&ggtt->vm.mutex);
err = i915_gem_evict_something(&ggtt->vm, NULL,
I915_GTT_PAGE_SIZE, 0, 0,
0, U64_MAX,
0);
mutex_unlock(&ggtt->vm.mutex);
if (err) {
pr_err("i915_gem_evict_something failed on a full GGTT with err=%d\n",
err);
goto cleanup;
}
cleanup:
cleanup_objects(ggtt, &objects);
return err;
}
static int igt_overcommit(void *arg)
{
struct intel_gt *gt = arg;
struct i915_ggtt *ggtt = gt->ggtt;
struct drm_i915_gem_object *obj;
struct i915_vma *vma;
LIST_HEAD(objects);
int err;
/* Fill the GGTT with pinned objects and then try to pin one more.
* We expect it to fail.
*/
err = populate_ggtt(ggtt, &objects);
if (err)
goto cleanup;
obj = i915_gem_object_create_internal(gt->i915, I915_GTT_PAGE_SIZE);
if (IS_ERR(obj)) {
err = PTR_ERR(obj);
goto cleanup;
}
quirk_add(obj, &objects);
vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0);
if (vma != ERR_PTR(-ENOSPC)) {
pr_err("Failed to evict+insert, i915_gem_object_ggtt_pin returned err=%d\n", (int)PTR_ERR_OR_ZERO(vma));
err = -EINVAL;
goto cleanup;
}
cleanup:
cleanup_objects(ggtt, &objects);
return err;
}
static int igt_evict_for_vma(void *arg)
{
struct intel_gt *gt = arg;
struct i915_ggtt *ggtt = gt->ggtt;
struct drm_mm_node target = {
.start = 0,
.size = 4096,
};
LIST_HEAD(objects);
int err;
/* Fill the GGTT with pinned objects and try to evict a range. */
err = populate_ggtt(ggtt, &objects);
if (err)
goto cleanup;
/* Everything is pinned, nothing should happen */
mutex_lock(&ggtt->vm.mutex);
err = i915_gem_evict_for_node(&ggtt->vm, NULL, &target, 0);
mutex_unlock(&ggtt->vm.mutex);
if (err != -ENOSPC) {
pr_err("i915_gem_evict_for_node on a full GGTT returned err=%d\n",
err);
goto cleanup;
}
unpin_ggtt(ggtt);
/* Everything is unpinned, we should be able to evict the node */
mutex_lock(&ggtt->vm.mutex);
err = i915_gem_evict_for_node(&ggtt->vm, NULL, &target, 0);
mutex_unlock(&ggtt->vm.mutex);
if (err) {
pr_err("i915_gem_evict_for_node returned err=%d\n",
err);
goto cleanup;
}
cleanup:
cleanup_objects(ggtt, &objects);
return err;
}
static void mock_color_adjust(const struct drm_mm_node *node,
unsigned long color,
u64 *start,
u64 *end)
{
}
static int igt_evict_for_cache_color(void *arg)
{
struct intel_gt *gt = arg;
struct i915_ggtt *ggtt = gt->ggtt;
const unsigned long flags = PIN_OFFSET_FIXED;
struct drm_mm_node target = {
.start = I915_GTT_PAGE_SIZE * 2,
.size = I915_GTT_PAGE_SIZE,
.color = i915_gem_get_pat_index(gt->i915, I915_CACHE_LLC),
};
struct drm_i915_gem_object *obj;
struct i915_vma *vma;
LIST_HEAD(objects);
int err;
/*
* Currently the use of color_adjust for the GGTT is limited to cache
* coloring and guard pages, and so the presence of mm.color_adjust for
* the GGTT is assumed to be i915_ggtt_color_adjust, hence using a mock
* color adjust will work just fine for our purposes.
*/
ggtt->vm.mm.color_adjust = mock_color_adjust;
GEM_BUG_ON(!i915_vm_has_cache_coloring(&ggtt->vm));
obj = i915_gem_object_create_internal(gt->i915, I915_GTT_PAGE_SIZE);
if (IS_ERR(obj)) {
err = PTR_ERR(obj);
goto cleanup;
}
i915_gem_object_set_cache_coherency(obj, I915_CACHE_LLC);
quirk_add(obj, &objects);
vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
I915_GTT_PAGE_SIZE | flags);
if (IS_ERR(vma)) {
pr_err("[0]i915_gem_object_ggtt_pin failed\n");
err = PTR_ERR(vma);
goto cleanup;
}
obj = i915_gem_object_create_internal(gt->i915, I915_GTT_PAGE_SIZE);
if (IS_ERR(obj)) {
err = PTR_ERR(obj);
goto cleanup;
}
i915_gem_object_set_cache_coherency(obj, I915_CACHE_LLC);
quirk_add(obj, &objects);
/* Neighbouring; same colour - should fit */
vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
(I915_GTT_PAGE_SIZE * 2) | flags);
if (IS_ERR(vma)) {
pr_err("[1]i915_gem_object_ggtt_pin failed\n");
err = PTR_ERR(vma);
goto cleanup;
}
i915_vma_unpin(vma);
/* Remove just the second vma */
mutex_lock(&ggtt->vm.mutex);
err = i915_gem_evict_for_node(&ggtt->vm, NULL, &target, 0);
mutex_unlock(&ggtt->vm.mutex);
if (err) {
pr_err("[0]i915_gem_evict_for_node returned err=%d\n", err);
goto cleanup;
}
/* Attempt to remove the first *pinned* vma, by removing the (empty)
* neighbour -- this should fail.
*/
target.color = i915_gem_get_pat_index(gt->i915, I915_CACHE_L3_LLC);
mutex_lock(&ggtt->vm.mutex);
err = i915_gem_evict_for_node(&ggtt->vm, NULL, &target, 0);
mutex_unlock(&ggtt->vm.mutex);
if (!err) {
pr_err("[1]i915_gem_evict_for_node returned err=%d\n", err);
err = -EINVAL;
goto cleanup;
}
err = 0;
cleanup:
unpin_ggtt(ggtt);
cleanup_objects(ggtt, &objects);
ggtt->vm.mm.color_adjust = NULL;
return err;
}
static int igt_evict_vm(void *arg)
{
struct intel_gt *gt = arg;
struct i915_ggtt *ggtt = gt->ggtt;
struct i915_gem_ww_ctx ww;
LIST_HEAD(objects);
int err;
/* Fill the GGTT with pinned objects and try to evict everything. */
err = populate_ggtt(ggtt, &objects);
if (err)
goto cleanup;
/* Everything is pinned, nothing should happen */
mutex_lock(&ggtt->vm.mutex);
err = i915_gem_evict_vm(&ggtt->vm, NULL, NULL);
mutex_unlock(&ggtt->vm.mutex);
if (err) {
pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
err);
goto cleanup;
}
unpin_ggtt(ggtt);
for_i915_gem_ww(&ww, err, false) {
mutex_lock(&ggtt->vm.mutex);
err = i915_gem_evict_vm(&ggtt->vm, &ww, NULL);
mutex_unlock(&ggtt->vm.mutex);
}
if (err) {
pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
err);
goto cleanup;
}
cleanup:
cleanup_objects(ggtt, &objects);
return err;
}
static int igt_evict_contexts(void *arg)
{
const u64 PRETEND_GGTT_SIZE = 16ull << 20;
struct intel_gt *gt = arg;
struct i915_ggtt *ggtt = gt->ggtt;
struct drm_i915_private *i915 = gt->i915;
struct intel_engine_cs *engine;
enum intel_engine_id id;
struct reserved {
struct drm_mm_node node;
struct reserved *next;
} *reserved = NULL;
intel_wakeref_t wakeref;
struct drm_mm_node hole;
unsigned long count;
int err;
/*
* The purpose of this test is to verify that we will trigger an
* eviction in the GGTT when constructing a request that requires
* additional space in the GGTT for pinning the context. This space
* is not directly tied to the request so reclaiming it requires
* extra work.
*
* As such this test is only meaningful for full-ppgtt environments
* where the GTT space of the request is separate from the GGTT
* allocation required to build the request.
*/
if (!HAS_FULL_PPGTT(i915))
return 0;
wakeref = intel_runtime_pm_get(&i915->runtime_pm);
/* Reserve a block so that we know we have enough to fit a few rq */
memset(&hole, 0, sizeof(hole));
mutex_lock(&ggtt->vm.mutex);
err = i915_gem_gtt_insert(&ggtt->vm, NULL, &hole,
PRETEND_GGTT_SIZE, 0, I915_COLOR_UNEVICTABLE,
0, ggtt->vm.total,
PIN_NOEVICT);
if (err)
goto out_locked;
/* Make the GGTT appear small by filling it with unevictable nodes */
count = 0;
do {
struct reserved *r;
mutex_unlock(&ggtt->vm.mutex);
r = kzalloc_objs(*r, 1);
mutex_lock(&ggtt->vm.mutex);
if (!r) {
err = -ENOMEM;
goto out_locked;
}
if (i915_gem_gtt_insert(&ggtt->vm, NULL, &r->node,
1ul << 20, 0, I915_COLOR_UNEVICTABLE,
0, ggtt->vm.total,
PIN_NOEVICT)) {
kfree(r);
break;
}
r->next = reserved;
reserved = r;
count++;
} while (1);
drm_mm_remove_node(&hole);
mutex_unlock(&ggtt->vm.mutex);
pr_info("Filled GGTT with %lu 1MiB nodes\n", count);
/* Overfill the GGTT with context objects and so try to evict one. */
for_each_engine(engine, gt, id) {
struct i915_sw_fence fence;
struct i915_request *last = NULL;
count = 0;
onstack_fence_init(&fence);
do {
struct intel_context *ce;
struct i915_request *rq;
ce = intel_context_create(engine);
if (IS_ERR(ce))
break;
/* We will need some GGTT space for the rq's context */
igt_evict_ctl.fail_if_busy = true;
rq = intel_context_create_request(ce);
igt_evict_ctl.fail_if_busy = false;
intel_context_put(ce);
if (IS_ERR(rq)) {
/* When full, fail_if_busy will trigger EBUSY */
if (PTR_ERR(rq) != -EBUSY) {
pr_err("Unexpected error from request alloc (on %s): %d\n",
engine->name,
(int)PTR_ERR(rq));
err = PTR_ERR(rq);
}
break;
}
/* Keep every request/ctx pinned until we are full */
err = i915_sw_fence_await_sw_fence_gfp(&rq->submit,
&fence,
GFP_KERNEL);
if (err < 0)
break;
i915_request_add(rq);
count++;
if (last)
i915_request_put(last);
last = i915_request_get(rq);
err = 0;
} while(1);
onstack_fence_fini(&fence);
pr_info("Submitted %lu contexts/requests on %s\n",
count, engine->name);
if (err)
break;
if (last) {
if (i915_request_wait(last, 0, HZ) < 0) {
err = -EIO;
i915_request_put(last);
pr_err("Failed waiting for last request (on %s)",
engine->name);
break;
}
i915_request_put(last);
}
err = intel_gt_wait_for_idle(engine->gt, HZ * 3);
if (err) {
gt_err(engine->gt, "Failed to idle GT (on %s)",
engine->name);
break;
}
}
mutex_lock(&ggtt->vm.mutex);
out_locked:
if (igt_flush_test(i915))
err = -EIO;
while (reserved) {
struct reserved *next = reserved->next;
drm_mm_remove_node(&reserved->node);
kfree(reserved);
reserved = next;
}
if (drm_mm_node_allocated(&hole))
drm_mm_remove_node(&hole);
mutex_unlock(&ggtt->vm.mutex);
intel_runtime_pm_put(&i915->runtime_pm, wakeref);
return err;
}
int i915_gem_evict_mock_selftests(void)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_evict_something),
SUBTEST(igt_evict_for_vma),
SUBTEST(igt_evict_for_cache_color),
SUBTEST(igt_evict_vm),
SUBTEST(igt_overcommit),
};
struct drm_i915_private *i915;
intel_wakeref_t wakeref;
int err = 0;
i915 = mock_gem_device();
if (!i915)
return -ENOMEM;
with_intel_runtime_pm(&i915->runtime_pm, wakeref)
err = i915_subtests(tests, to_gt(i915));
mock_destroy_device(i915);
return err;
}
int i915_gem_evict_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_evict_contexts),
};
if (intel_gt_is_wedged(to_gt(i915)))
return 0;
return intel_gt_live_subtests(tests, to_gt(i915));
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef selftest
#define selftest(x, y)
#endif
/*
* List each unit test as selftest(name, function)
*
* The name is used as both an enum and expanded as subtest__name to create
* a module parameter. It must be unique and legal for a C identifier.
*
* The function should be of type int function(void). It may be conditionally
* compiled using #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST).
*
* Tests are executed in order by igt/i915_selftest
*/
selftest(sanitycheck, i915_live_sanitycheck) /* keep first (igt selfcheck) */
selftest(uncore, intel_uncore_live_selftests)
selftest(workarounds, intel_workarounds_live_selftests)
selftest(gt_engines, intel_engine_live_selftests)
selftest(gt_timelines, intel_timeline_live_selftests)
selftest(gt_contexts, intel_context_live_selftests)
selftest(gt_lrc, intel_lrc_live_selftests)
selftest(gt_mocs, intel_mocs_live_selftests)
selftest(gt_pm, intel_gt_pm_live_selftests)
selftest(gt_heartbeat, intel_heartbeat_live_selftests)
selftest(gt_tlb, intel_tlb_live_selftests)
selftest(requests, i915_request_live_selftests)
selftest(migrate, intel_migrate_live_selftests)
selftest(active, i915_active_live_selftests)
selftest(objects, i915_gem_object_live_selftests)
selftest(mman, i915_gem_mman_live_selftests)
selftest(dmabuf, i915_gem_dmabuf_live_selftests)
selftest(vma, i915_vma_live_selftests)
selftest(coherency, i915_gem_coherency_live_selftests)
selftest(gtt, i915_gem_gtt_live_selftests)
selftest(gem, i915_gem_live_selftests)
selftest(evict, i915_gem_evict_live_selftests)
selftest(hugepages, i915_gem_huge_page_live_selftests)
selftest(gem_contexts, i915_gem_context_live_selftests)
selftest(client, i915_gem_client_blt_live_selftests)
selftest(gem_migrate, i915_gem_migrate_live_selftests)
selftest(reset, intel_reset_live_selftests)
selftest(memory_region, intel_memory_region_live_selftests)
selftest(hangcheck, intel_hangcheck_live_selftests)
selftest(execlists, intel_execlists_live_selftests)
selftest(ring_submission, intel_ring_submission_live_selftests)
selftest(perf, i915_perf_live_selftests)
selftest(slpc, intel_slpc_live_selftests)
selftest(guc, intel_guc_live_selftests)
selftest(guc_multi_lrc, intel_guc_multi_lrc_live_selftests)
selftest(guc_hang, intel_guc_hang_check)
/* Here be dragons: keep last to run last! */
selftest(late_gt_pm, intel_gt_pm_late_selftests)
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef selftest
#define selftest(x, y)
#endif
/*
* List each unit test as selftest(name, function)
*
* The name is used as both an enum and expanded as subtest__name to create
* a module parameter. It must be unique and legal for a C identifier.
*
* The function should be of type int function(void). It may be conditionally
* compiled using #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST).
*
* Tests are executed in order by igt/i915_selftest
*/
selftest(sanitycheck, i915_mock_sanitycheck) /* keep first (igt selfcheck) */
selftest(shmem, shmem_utils_mock_selftests)
selftest(fence, i915_sw_fence_mock_selftests)
selftest(scatterlist, scatterlist_mock_selftests)
selftest(syncmap, i915_syncmap_mock_selftests)
selftest(uncore, intel_uncore_mock_selftests)
selftest(ring, intel_ring_mock_selftests)
selftest(engine, intel_engine_cs_mock_selftests)
selftest(timelines, intel_timeline_mock_selftests)
selftest(requests, i915_request_mock_selftests)
selftest(objects, i915_gem_object_mock_selftests)
selftest(phys, i915_gem_phys_mock_selftests)
selftest(dmabuf, i915_gem_dmabuf_mock_selftests)
selftest(vma, i915_vma_mock_selftests)
selftest(evict, i915_gem_evict_mock_selftests)
selftest(gtt, i915_gem_gtt_mock_selftests)
selftest(hugepages, i915_gem_huge_page_mock_selftests)
selftest(memory_region, intel_memory_region_mock_selftests)
@@ -0,0 +1,445 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2019 Intel Corporation
*/
#include <linux/kref.h>
#include "gem/i915_gem_pm.h"
#include "gt/intel_gt.h"
#include "i915_selftest.h"
#include "igt_flush_test.h"
#include "lib_sw_fence.h"
#define TEST_OA_CONFIG_UUID "12345678-1234-1234-1234-1234567890ab"
static int
alloc_empty_config(struct i915_perf *perf)
{
struct i915_oa_config *oa_config;
oa_config = kzalloc_obj(*oa_config);
if (!oa_config)
return -ENOMEM;
oa_config->perf = perf;
kref_init(&oa_config->ref);
strscpy(oa_config->uuid, TEST_OA_CONFIG_UUID, sizeof(oa_config->uuid));
mutex_lock(&perf->metrics_lock);
oa_config->id = idr_alloc(&perf->metrics_idr, oa_config, 2, 0, GFP_KERNEL);
if (oa_config->id < 0) {
mutex_unlock(&perf->metrics_lock);
i915_oa_config_put(oa_config);
return -ENOMEM;
}
mutex_unlock(&perf->metrics_lock);
return 0;
}
static void
destroy_empty_config(struct i915_perf *perf)
{
struct i915_oa_config *oa_config = NULL, *tmp;
int id;
mutex_lock(&perf->metrics_lock);
idr_for_each_entry(&perf->metrics_idr, tmp, id) {
if (!strcmp(tmp->uuid, TEST_OA_CONFIG_UUID)) {
oa_config = tmp;
break;
}
}
if (oa_config)
idr_remove(&perf->metrics_idr, oa_config->id);
mutex_unlock(&perf->metrics_lock);
if (oa_config)
i915_oa_config_put(oa_config);
}
static struct i915_oa_config *
get_empty_config(struct i915_perf *perf)
{
struct i915_oa_config *oa_config = NULL, *tmp;
int id;
mutex_lock(&perf->metrics_lock);
idr_for_each_entry(&perf->metrics_idr, tmp, id) {
if (!strcmp(tmp->uuid, TEST_OA_CONFIG_UUID)) {
oa_config = i915_oa_config_get(tmp);
break;
}
}
mutex_unlock(&perf->metrics_lock);
return oa_config;
}
static struct i915_perf_stream *
test_stream(struct i915_perf *perf)
{
struct drm_i915_perf_open_param param = {};
struct i915_oa_config *oa_config = get_empty_config(perf);
struct perf_open_properties props = {
.engine = intel_engine_lookup_user(perf->i915,
I915_ENGINE_CLASS_RENDER,
0),
.sample_flags = SAMPLE_OA_REPORT,
.oa_format = GRAPHICS_VER(perf->i915) == 12 ?
I915_OA_FORMAT_A32u40_A4u32_B8_C8 : I915_OA_FORMAT_C4_B8,
};
struct i915_perf_stream *stream;
struct intel_gt *gt;
if (!props.engine)
return NULL;
gt = props.engine->gt;
if (!oa_config)
return NULL;
props.metrics_set = oa_config->id;
stream = kzalloc_obj(*stream);
if (!stream) {
i915_oa_config_put(oa_config);
return NULL;
}
stream->perf = perf;
mutex_lock(&gt->perf.lock);
if (i915_oa_stream_init(stream, &param, &props)) {
kfree(stream);
stream = NULL;
}
mutex_unlock(&gt->perf.lock);
i915_oa_config_put(oa_config);
return stream;
}
static void stream_destroy(struct i915_perf_stream *stream)
{
struct intel_gt *gt = stream->engine->gt;
mutex_lock(&gt->perf.lock);
i915_perf_destroy_locked(stream);
mutex_unlock(&gt->perf.lock);
}
static int live_sanitycheck(void *arg)
{
struct drm_i915_private *i915 = arg;
struct i915_perf_stream *stream;
/* Quick check we can create a perf stream */
stream = test_stream(&i915->perf);
if (!stream)
return -EINVAL;
stream_destroy(stream);
return 0;
}
static int write_timestamp(struct i915_request *rq, int slot)
{
u32 *cs;
int len;
cs = intel_ring_begin(rq, 6);
if (IS_ERR(cs))
return PTR_ERR(cs);
len = 5;
if (GRAPHICS_VER(rq->i915) >= 8)
len++;
*cs++ = GFX_OP_PIPE_CONTROL(len);
*cs++ = PIPE_CONTROL_GLOBAL_GTT_IVB |
PIPE_CONTROL_STORE_DATA_INDEX |
PIPE_CONTROL_WRITE_TIMESTAMP;
*cs++ = slot * sizeof(u32);
*cs++ = 0;
*cs++ = 0;
*cs++ = 0;
intel_ring_advance(rq, cs);
return 0;
}
static ktime_t poll_status(struct i915_request *rq, int slot)
{
while (!intel_read_status_page(rq->engine, slot) &&
!i915_request_completed(rq))
cpu_relax();
return ktime_get();
}
static int live_noa_delay(void *arg)
{
struct drm_i915_private *i915 = arg;
struct i915_perf_stream *stream;
struct i915_request *rq;
ktime_t t0, t1;
u64 expected;
u32 delay;
int err;
int i;
/* Check that the GPU delays matches expectations */
stream = test_stream(&i915->perf);
if (!stream)
return -ENOMEM;
expected = atomic64_read(&stream->perf->noa_programming_delay);
if (stream->engine->class != RENDER_CLASS) {
err = -ENODEV;
goto out;
}
for (i = 0; i < 4; i++)
intel_write_status_page(stream->engine, 0x100 + i, 0);
rq = intel_engine_create_kernel_request(stream->engine);
if (IS_ERR(rq)) {
err = PTR_ERR(rq);
goto out;
}
if (rq->engine->emit_init_breadcrumb) {
err = rq->engine->emit_init_breadcrumb(rq);
if (err) {
i915_request_add(rq);
goto out;
}
}
err = write_timestamp(rq, 0x100);
if (err) {
i915_request_add(rq);
goto out;
}
err = rq->engine->emit_bb_start(rq,
i915_ggtt_offset(stream->noa_wait), 0,
I915_DISPATCH_SECURE);
if (err) {
i915_request_add(rq);
goto out;
}
err = write_timestamp(rq, 0x102);
if (err) {
i915_request_add(rq);
goto out;
}
i915_request_get(rq);
i915_request_add(rq);
preempt_disable();
t0 = poll_status(rq, 0x100);
t1 = poll_status(rq, 0x102);
preempt_enable();
pr_info("CPU delay: %lluns, expected %lluns\n",
ktime_sub(t1, t0), expected);
delay = intel_read_status_page(stream->engine, 0x102);
delay -= intel_read_status_page(stream->engine, 0x100);
delay = intel_gt_clock_interval_to_ns(stream->engine->gt, delay);
pr_info("GPU delay: %uns, expected %lluns\n",
delay, expected);
if (4 * delay < 3 * expected || 2 * delay > 3 * expected) {
pr_err("GPU delay [%uus] outside of expected threshold! [%lluus, %lluus]\n",
delay / 1000,
div_u64(3 * expected, 4000),
div_u64(3 * expected, 2000));
err = -EINVAL;
}
i915_request_put(rq);
out:
stream_destroy(stream);
return err;
}
static int live_noa_gpr(void *arg)
{
struct drm_i915_private *i915 = arg;
struct i915_perf_stream *stream;
struct intel_context *ce;
struct i915_request *rq;
u32 *cs, *store;
void *scratch;
u32 gpr0;
int err;
int i;
/* Check that the delay does not clobber user context state (GPR) */
stream = test_stream(&i915->perf);
if (!stream)
return -ENOMEM;
gpr0 = i915_mmio_reg_offset(GEN8_RING_CS_GPR(stream->engine->mmio_base, 0));
ce = intel_context_create(stream->engine);
if (IS_ERR(ce)) {
err = PTR_ERR(ce);
goto out;
}
/* Poison the ce->vm so we detect writes not to the GGTT gt->scratch */
scratch = __px_vaddr(ce->vm->scratch[0]);
memset(scratch, POISON_FREE, PAGE_SIZE);
rq = intel_context_create_request(ce);
if (IS_ERR(rq)) {
err = PTR_ERR(rq);
goto out_ce;
}
i915_request_get(rq);
if (rq->engine->emit_init_breadcrumb) {
err = rq->engine->emit_init_breadcrumb(rq);
if (err) {
i915_request_add(rq);
goto out_rq;
}
}
/* Fill the 16 qword [32 dword] GPR with a known unlikely value */
cs = intel_ring_begin(rq, 2 * 32 + 2);
if (IS_ERR(cs)) {
err = PTR_ERR(cs);
i915_request_add(rq);
goto out_rq;
}
*cs++ = MI_LOAD_REGISTER_IMM(32);
for (i = 0; i < 32; i++) {
*cs++ = gpr0 + i * sizeof(u32);
*cs++ = STACK_MAGIC;
}
*cs++ = MI_NOOP;
intel_ring_advance(rq, cs);
/* Execute the GPU delay */
err = rq->engine->emit_bb_start(rq,
i915_ggtt_offset(stream->noa_wait), 0,
I915_DISPATCH_SECURE);
if (err) {
i915_request_add(rq);
goto out_rq;
}
/* Read the GPR back, using the pinned global HWSP for convenience */
store = memset32(rq->engine->status_page.addr + 512, 0, 32);
for (i = 0; i < 32; i++) {
u32 cmd;
cs = intel_ring_begin(rq, 4);
if (IS_ERR(cs)) {
err = PTR_ERR(cs);
i915_request_add(rq);
goto out_rq;
}
cmd = MI_STORE_REGISTER_MEM;
if (GRAPHICS_VER(i915) >= 8)
cmd++;
cmd |= MI_USE_GGTT;
*cs++ = cmd;
*cs++ = gpr0 + i * sizeof(u32);
*cs++ = i915_ggtt_offset(rq->engine->status_page.vma) +
offset_in_page(store) +
i * sizeof(u32);
*cs++ = 0;
intel_ring_advance(rq, cs);
}
i915_request_add(rq);
if (i915_request_wait(rq, I915_WAIT_INTERRUPTIBLE, HZ / 2) < 0) {
pr_err("noa_wait timed out\n");
intel_gt_set_wedged(stream->engine->gt);
err = -EIO;
goto out_rq;
}
/* Verify that the GPR contain our expected values */
for (i = 0; i < 32; i++) {
if (store[i] == STACK_MAGIC)
continue;
pr_err("GPR[%d] lost, found:%08x, expected:%08x!\n",
i, store[i], STACK_MAGIC);
err = -EINVAL;
}
/* Verify that the user's scratch page was not used for GPR storage */
if (memchr_inv(scratch, POISON_FREE, PAGE_SIZE)) {
pr_err("Scratch page overwritten!\n");
igt_hexdump(scratch, 4096);
err = -EINVAL;
}
out_rq:
i915_request_put(rq);
out_ce:
intel_context_put(ce);
out:
stream_destroy(stream);
return err;
}
int i915_perf_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
SUBTEST(live_sanitycheck),
SUBTEST(live_noa_delay),
SUBTEST(live_noa_gpr),
};
struct i915_perf *perf = &i915->perf;
int err;
if (!perf->metrics_kobj || !perf->ops.enable_metric_set)
return 0;
if (intel_gt_is_wedged(to_gt(i915)))
return 0;
err = alloc_empty_config(&i915->perf);
if (err)
return err;
err = i915_live_subtests(tests, i915);
destroy_empty_config(&i915->perf);
return err;
}
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef selftest
#define selftest(x, y)
#endif
/*
* List each unit test as selftest(name, function)
*
* The name is used as both an enum and expanded as subtest__name to create
* a module parameter. It must be unique and legal for a C identifier.
*
* The function should be of type int function(void). It may be conditionally
* compiled using #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST).
*
* Tests are executed in order by igt/i915_selftest
*/
selftest(engine_cs, intel_engine_cs_perf_selftests)
selftest(request, i915_request_perf_selftests)
selftest(migrate, intel_migrate_perf_selftests)
selftest(region, intel_memory_region_perf_selftests)
@@ -0,0 +1,109 @@
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/types.h>
#include "i915_random.h"
#include "i915_utils.h"
u64 i915_prandom_u64_state(struct rnd_state *rnd)
{
u64 x;
x = prandom_u32_state(rnd);
x <<= 32;
x |= prandom_u32_state(rnd);
return x;
}
void i915_prandom_shuffle(void *arr, size_t elsz, size_t count,
struct rnd_state *state)
{
char stack[128];
if (WARN_ON(elsz > sizeof(stack) || count > U32_MAX))
return;
if (!elsz || !count)
return;
/* Fisher-Yates shuffle courtesy of Knuth */
while (--count) {
size_t swp;
swp = i915_prandom_u32_max_state(count + 1, state);
if (swp == count)
continue;
memcpy(stack, arr + count * elsz, elsz);
memcpy(arr + count * elsz, arr + swp * elsz, elsz);
memcpy(arr + swp * elsz, stack, elsz);
}
}
void i915_random_reorder(unsigned int *order, unsigned int count,
struct rnd_state *state)
{
i915_prandom_shuffle(order, sizeof(*order), count, state);
}
unsigned int *i915_random_order(unsigned int count, struct rnd_state *state)
{
unsigned int *order, i;
order = kmalloc_array(count, sizeof(*order),
GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_NOWARN);
if (!order)
return order;
for (i = 0; i < count; i++)
order[i] = i;
i915_random_reorder(order, count, state);
return order;
}
u64 igt_random_offset(struct rnd_state *state,
u64 start, u64 end,
u64 len, u64 align)
{
u64 range, addr;
BUG_ON(range_overflows(start, len, end));
BUG_ON(round_up(start, align) > round_down(end - len, align));
range = round_down(end - len, align) - round_up(start, align);
if (range) {
addr = i915_prandom_u64_state(state);
div64_u64_rem(addr, range, &addr);
start += addr;
}
return round_up(start, align);
}
@@ -0,0 +1,65 @@
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#ifndef __I915_SELFTESTS_RANDOM_H__
#define __I915_SELFTESTS_RANDOM_H__
#include <linux/math64.h>
#include <linux/prandom.h>
#include "../i915_selftest.h"
#define I915_RND_STATE_INITIALIZER(x) ({ \
struct rnd_state state__; \
prandom_seed_state(&state__, (x)); \
state__; \
})
#define I915_RND_STATE(name__) \
struct rnd_state name__ = I915_RND_STATE_INITIALIZER(i915_selftest.random_seed)
#define I915_RND_SUBSTATE(name__, parent__) \
struct rnd_state name__ = I915_RND_STATE_INITIALIZER(prandom_u32_state(&(parent__)))
u64 i915_prandom_u64_state(struct rnd_state *rnd);
static inline u32 i915_prandom_u32_max_state(u32 ep_ro, struct rnd_state *state)
{
return upper_32_bits(mul_u32_u32(prandom_u32_state(state), ep_ro));
}
unsigned int *i915_random_order(unsigned int count,
struct rnd_state *state);
void i915_random_reorder(unsigned int *order,
unsigned int count,
struct rnd_state *state);
void i915_prandom_shuffle(void *arr, size_t elsz, size_t count,
struct rnd_state *state);
u64 igt_random_offset(struct rnd_state *state,
u64 start, u64 end,
u64 len, u64 align);
#endif /* !__I915_SELFTESTS_RANDOM_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,590 @@
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <linux/random.h>
#include "gt/intel_gt.h"
#include "gt/intel_gt_pm.h"
#include "gt/intel_gt_regs.h"
#include "gt/uc/intel_gsc_fw.h"
#include "i915_driver.h"
#include "i915_drv.h"
#include "i915_jiffies.h"
#include "i915_selftest.h"
#include "i915_wait_util.h"
#include "igt_flush_test.h"
struct i915_selftest i915_selftest __read_mostly = {
.timeout_ms = 500,
};
int i915_mock_sanitycheck(void)
{
pr_info(DRIVER_NAME ": %s() - ok!\n", __func__);
return 0;
}
int i915_live_sanitycheck(struct drm_i915_private *i915)
{
pr_info("%s: %s() - ok!\n", i915->drm.driver->name, __func__);
return 0;
}
enum {
#define selftest(name, func) mock_##name,
#include "i915_mock_selftests.h"
#undef selftest
};
enum {
#define selftest(name, func) live_##name,
#include "i915_live_selftests.h"
#undef selftest
};
enum {
#define selftest(name, func) perf_##name,
#include "i915_perf_selftests.h"
#undef selftest
};
struct selftest {
bool enabled;
const char *name;
union {
int (*mock)(void);
int (*live)(struct drm_i915_private *);
};
};
#define selftest(n, f) [mock_##n] = { .name = #n, { .mock = f } },
static struct selftest mock_selftests[] = {
#include "i915_mock_selftests.h"
};
#undef selftest
#define selftest(n, f) [live_##n] = { .name = #n, { .live = f } },
static struct selftest live_selftests[] = {
#include "i915_live_selftests.h"
};
#undef selftest
#define selftest(n, f) [perf_##n] = { .name = #n, { .live = f } },
static struct selftest perf_selftests[] = {
#include "i915_perf_selftests.h"
};
#undef selftest
/* Embed the line number into the parameter name so that we can order tests */
#define selftest(n, func) selftest_0(n, func, param(n))
#define param(n) __PASTE(igt__, __PASTE(__LINE__, __mock_##n))
#define selftest_0(n, func, id) \
module_param_named(id, mock_selftests[mock_##n].enabled, bool, 0400);
#include "i915_mock_selftests.h"
#undef selftest_0
#undef param
#define param(n) __PASTE(igt__, __PASTE(__LINE__, __live_##n))
#define selftest_0(n, func, id) \
module_param_named(id, live_selftests[live_##n].enabled, bool, 0400);
#include "i915_live_selftests.h"
#undef selftest_0
#undef param
#define param(n) __PASTE(igt__, __PASTE(__LINE__, __perf_##n))
#define selftest_0(n, func, id) \
module_param_named(id, perf_selftests[perf_##n].enabled, bool, 0400);
#include "i915_perf_selftests.h"
#undef selftest_0
#undef param
#undef selftest
static void set_default_test_all(struct selftest *st, unsigned int count)
{
unsigned int i;
for (i = 0; i < count; i++)
if (st[i].enabled)
return;
for (i = 0; i < count; i++)
st[i].enabled = true;
}
static bool
__gsc_proxy_init_progressing(struct intel_gsc_uc *gsc)
{
return intel_gsc_uc_fw_proxy_get_status(gsc) == -EAGAIN;
}
static void
__wait_gsc_proxy_completed(struct drm_i915_private *i915)
{
bool need_to_wait = (IS_ENABLED(CONFIG_INTEL_MEI_GSC_PROXY) &&
i915->media_gt &&
HAS_ENGINE(i915->media_gt, GSC0) &&
intel_uc_fw_is_loadable(&i915->media_gt->uc.gsc.fw));
/*
* The gsc proxy component depends on the kernel component driver load ordering
* and in corner cases (the first time after an IFWI flash), init-completion
* firmware flows take longer.
*/
unsigned long timeout_ms = 8000;
if (need_to_wait && wait_for(!__gsc_proxy_init_progressing(&i915->media_gt->uc.gsc),
timeout_ms))
pr_warn(DRIVER_NAME "Timed out waiting for gsc_proxy_completion!\n");
}
static void
__wait_gsc_huc_load_completed(struct drm_i915_private *i915)
{
/* this only applies to DG2, so we only care about GT0 */
struct intel_huc *huc = &to_gt(i915)->uc.huc;
bool need_to_wait = (IS_ENABLED(CONFIG_INTEL_MEI_PXP) &&
intel_huc_wait_required(huc));
/*
* The GSC and PXP mei bringup depends on the kernel boot ordering, so
* to account for the worst case scenario the HuC code waits for up to
* 10s for the GSC driver to load and then another 5s for the PXP
* component to bind before giving up, even though those steps normally
* complete in less than a second from the i915 load. We match that
* timeout here, but we expect to bail early due to the fence being
* signalled even in a failure case, as it is extremely unlikely that
* both components will use their full timeout.
*/
unsigned long timeout_ms = 15000;
if (need_to_wait &&
wait_for(i915_sw_fence_done(&huc->delayed_load.fence), timeout_ms))
pr_warn(DRIVER_NAME "Timed out waiting for huc load via GSC!\n");
}
static struct mm_struct *get_selftest_mm(int u_pid_nr)
{
struct task_struct *task = NULL;
struct mm_struct *mm = NULL;
struct pid *u_pid = NULL;
if (u_pid_nr < 1)
return NULL;
u_pid = find_get_pid(u_pid_nr);
if (!u_pid) {
pr_warn("Could not find PID: %d\n", u_pid_nr);
return NULL;
}
task = get_pid_task(u_pid, PIDTYPE_PID);
put_pid(u_pid);
if (!task) {
pr_warn("Could not find task for PID: %d\n", u_pid_nr);
return NULL;
}
if (task->flags & PF_KTHREAD) {
pr_warn("Task not in userspace: %d\n", u_pid_nr);
put_task_struct(task);
return NULL;
}
mm = get_task_mm(task);
put_task_struct(task);
if (!mm) {
pr_warn("Could not find address space of task with PID: %d\n", u_pid_nr);
return NULL;
}
return mm;
}
static int __run_selftests(const char *name,
struct selftest *st,
unsigned int count,
void *data)
{
struct mm_struct *mm = NULL;
int u_pid_nr = -1;
int err = 0;
if (i915_selftest.userspace_pid)
u_pid_nr = i915_selftest.userspace_pid;
while (!i915_selftest.random_seed)
i915_selftest.random_seed = get_random_u32();
i915_selftest.timeout_jiffies =
i915_selftest.timeout_ms ?
msecs_to_jiffies_timeout(i915_selftest.timeout_ms) :
MAX_SCHEDULE_TIMEOUT;
set_default_test_all(st, count);
pr_info(DRIVER_NAME ": Performing %s selftests with st_random_seed=0x%x st_timeout=%u\n",
name, i915_selftest.random_seed, i915_selftest.timeout_ms);
/*
* If we are running in a kthread on a multi NUMA system and the user passed
* a valid PID of a userspace task, then we may borrow its address space
* to prepare a safe environment for the mmap selftests.
*/
if (!current->mm && u_pid_nr > 0) {
mm = get_selftest_mm(u_pid_nr);
if (mm) {
kthread_use_mm(mm);
if (unlikely(!current->mm)) {
mmput(mm);
mm = NULL;
pr_warn("Could not set mm as current->mm\n");
}
}
}
/* Tests are listed in order in i915_*_selftests.h */
for (; count--; st++) {
if (!st->enabled)
continue;
cond_resched();
if (signal_pending(current)) {
if (mm) {
kthread_unuse_mm(mm);
mmput_async(mm);
}
return -EINTR;
}
pr_info(DRIVER_NAME ": Running %s\n", st->name);
if (data)
err = st->live(data);
else
err = st->mock();
if (err == -EINTR && !signal_pending(current))
err = 0;
if (err)
break;
}
if (WARN(err > 0 || err == -ENOTTY,
"%s returned %d, conflicting with selftest's magic values!\n",
st->name, err))
err = -1;
if (mm) {
kthread_unuse_mm(mm);
mmput_async(mm);
}
return err;
}
#define run_selftests(x, data) \
__run_selftests(#x, x##_selftests, ARRAY_SIZE(x##_selftests), data)
int i915_mock_selftests(void)
{
int err;
if (!i915_selftest.mock)
return 0;
err = run_selftests(mock, NULL);
if (err) {
i915_selftest.mock = err;
return 1;
}
if (i915_selftest.mock < 0) {
i915_selftest.mock = -ENOTTY;
return 1;
}
return 0;
}
int i915_live_selftests(struct pci_dev *pdev)
{
struct drm_i915_private *i915 = pdev_to_i915(pdev);
struct intel_uncore *uncore = &i915->uncore;
int err;
u32 pg_enable;
intel_wakeref_t wakeref;
if (!i915_selftest.live)
return 0;
/*
* FIXME Disable render powergating, this is temporary wa and should be removed
* after fixing real cause of forcewake timeouts.
*/
with_intel_runtime_pm(uncore->rpm, wakeref) {
if (IS_GFX_GT_IP_RANGE(to_gt(i915), IP_VER(12, 00), IP_VER(12, 74))) {
pg_enable = intel_uncore_read(uncore, GEN9_PG_ENABLE);
if (pg_enable & GEN9_RENDER_PG_ENABLE)
intel_uncore_write_fw(uncore, GEN9_PG_ENABLE,
pg_enable & ~GEN9_RENDER_PG_ENABLE);
}
}
__wait_gsc_proxy_completed(i915);
__wait_gsc_huc_load_completed(i915);
err = run_selftests(live, i915);
if (err) {
i915_selftest.live = err;
return err;
}
if (i915_selftest.live < 0) {
i915_selftest.live = -ENOTTY;
return 1;
}
return 0;
}
int i915_perf_selftests(struct pci_dev *pdev)
{
struct drm_i915_private *i915 = pdev_to_i915(pdev);
int err;
if (!i915_selftest.perf)
return 0;
__wait_gsc_proxy_completed(i915);
__wait_gsc_huc_load_completed(i915);
err = run_selftests(perf, i915);
if (err) {
i915_selftest.perf = err;
return err;
}
if (i915_selftest.perf < 0) {
i915_selftest.perf = -ENOTTY;
return 1;
}
return 0;
}
static bool apply_subtest_filter(const char *caller, const char *name)
{
char *filter, *sep, *tok;
bool result = true;
filter = kstrdup(i915_selftest.filter, GFP_KERNEL);
for (sep = filter; (tok = strsep(&sep, ","));) {
bool allow = true;
char *sl;
if (*tok == '!') {
allow = false;
tok++;
}
if (*tok == '\0')
continue;
sl = strchr(tok, '/');
if (sl) {
*sl++ = '\0';
if (strcmp(tok, caller)) {
if (allow)
result = false;
continue;
}
tok = sl;
}
if (strcmp(tok, name)) {
if (allow)
result = false;
continue;
}
result = allow;
break;
}
kfree(filter);
return result;
}
int __i915_nop_setup(void *data)
{
return 0;
}
int __i915_nop_teardown(int err, void *data)
{
return err;
}
int __i915_live_setup(void *data)
{
struct drm_i915_private *i915 = data;
/* The selftests expect an idle system */
if (intel_gt_pm_wait_for_idle(to_gt(i915)))
return -EIO;
return intel_gt_terminally_wedged(to_gt(i915));
}
int __i915_live_teardown(int err, void *data)
{
struct drm_i915_private *i915 = data;
if (igt_flush_test(i915))
err = -EIO;
i915_gem_drain_freed_objects(i915);
return err;
}
int __intel_gt_live_setup(void *data)
{
struct intel_gt *gt = data;
/* The selftests expect an idle system */
if (intel_gt_pm_wait_for_idle(gt))
return -EIO;
return intel_gt_terminally_wedged(gt);
}
int __intel_gt_live_teardown(int err, void *data)
{
struct intel_gt *gt = data;
if (igt_flush_test(gt->i915))
err = -EIO;
i915_gem_drain_freed_objects(gt->i915);
return err;
}
int __i915_subtests(const char *caller,
int (*setup)(void *data),
int (*teardown)(int err, void *data),
const struct i915_subtest *st,
unsigned int count,
void *data)
{
int err;
for (; count--; st++) {
cond_resched();
if (signal_pending(current))
return -EINTR;
if (!apply_subtest_filter(caller, st->name))
continue;
err = setup(data);
if (err) {
pr_err(DRIVER_NAME "/%s: setup failed for %s\n",
caller, st->name);
return err;
}
pr_info(DRIVER_NAME ": Running %s/%s\n", caller, st->name);
GEM_TRACE("Running %s/%s\n", caller, st->name);
err = teardown(st->func(data), data);
if (err && err != -EINTR) {
pr_err(DRIVER_NAME "/%s: %s failed with error %d\n",
caller, st->name, err);
return err;
}
}
return 0;
}
bool __igt_timeout(unsigned long timeout, const char *fmt, ...)
{
va_list va;
if (!signal_pending(current)) {
cond_resched();
if (time_before(jiffies, timeout))
return false;
}
if (fmt) {
va_start(va, fmt);
vprintk(fmt, va);
va_end(va);
}
return true;
}
void igt_hexdump(const void *buf, size_t len)
{
const size_t rowsize = 8 * sizeof(u32);
const void *prev = NULL;
bool skip = false;
size_t pos;
for (pos = 0; pos < len; pos += rowsize) {
char line[128];
if (prev && !memcmp(prev, buf + pos, rowsize)) {
if (!skip) {
pr_info("*\n");
skip = true;
}
continue;
}
WARN_ON_ONCE(hex_dump_to_buffer(buf + pos, len - pos,
rowsize, sizeof(u32),
line, sizeof(line),
false) >= sizeof(line));
pr_info("[%04zx] %s\n", pos, line);
prev = buf + pos;
skip = false;
}
}
module_param_named(st_random_seed, i915_selftest.random_seed, uint, 0400);
module_param_named(st_timeout, i915_selftest.timeout_ms, uint, 0400);
module_param_named(st_filter, i915_selftest.filter, charp, 0400);
module_param_named(st_userspace_pid, i915_selftest.userspace_pid, uint, 0400);
MODULE_PARM_DESC(st_userspace_pid, "For usage in tests that map userspace memory and require address space with controllable lifetime.");
module_param_named_unsafe(mock_selftests, i915_selftest.mock, int, 0400);
MODULE_PARM_DESC(mock_selftests, "Run selftests before loading, using mock hardware (0:disabled [default], 1:run tests then load driver, -1:run tests then leave dummy module)");
module_param_named_unsafe(live_selftests, i915_selftest.live, int, 0400);
MODULE_PARM_DESC(live_selftests, "Run selftests after driver initialisation on the live system (0:disabled [default], 1:run tests then continue, -1:run tests then exit module)");
module_param_named_unsafe(perf_selftests, i915_selftest.perf, int, 0400);
MODULE_PARM_DESC(perf_selftests, "Run performance orientated selftests after driver initialisation on the live system (0:disabled [default], 1:run tests then continue, -1:run tests then exit module)");
@@ -0,0 +1,767 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/prime_numbers.h>
#include "../i915_selftest.h"
static int
fence_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
{
switch (state) {
case FENCE_COMPLETE:
break;
case FENCE_FREE:
/* Leave the fence for the caller to free it after testing */
break;
}
return NOTIFY_DONE;
}
static struct i915_sw_fence *alloc_fence(void)
{
struct i915_sw_fence *fence;
fence = kmalloc_obj(*fence);
if (!fence)
return NULL;
i915_sw_fence_init(fence, fence_notify);
return fence;
}
static void free_fence(struct i915_sw_fence *fence)
{
i915_sw_fence_fini(fence);
kfree(fence);
}
static int __test_self(struct i915_sw_fence *fence)
{
if (i915_sw_fence_done(fence))
return -EINVAL;
i915_sw_fence_commit(fence);
if (!i915_sw_fence_done(fence))
return -EINVAL;
i915_sw_fence_wait(fence);
if (!i915_sw_fence_done(fence))
return -EINVAL;
return 0;
}
static int test_self(void *arg)
{
struct i915_sw_fence *fence;
int ret;
/* Test i915_sw_fence signaling and completion testing */
fence = alloc_fence();
if (!fence)
return -ENOMEM;
ret = __test_self(fence);
free_fence(fence);
return ret;
}
static int test_dag(void *arg)
{
struct i915_sw_fence *A, *B, *C;
int ret = -EINVAL;
/* Test detection of cycles within the i915_sw_fence graphs */
if (!IS_ENABLED(CONFIG_DRM_I915_SW_FENCE_CHECK_DAG))
return 0;
A = alloc_fence();
if (!A)
return -ENOMEM;
if (i915_sw_fence_await_sw_fence_gfp(A, A, GFP_KERNEL) != -EINVAL) {
pr_err("recursive cycle not detected (AA)\n");
goto err_A;
}
B = alloc_fence();
if (!B) {
ret = -ENOMEM;
goto err_A;
}
i915_sw_fence_await_sw_fence_gfp(A, B, GFP_KERNEL);
if (i915_sw_fence_await_sw_fence_gfp(B, A, GFP_KERNEL) != -EINVAL) {
pr_err("single depth cycle not detected (BAB)\n");
goto err_B;
}
C = alloc_fence();
if (!C) {
ret = -ENOMEM;
goto err_B;
}
if (i915_sw_fence_await_sw_fence_gfp(B, C, GFP_KERNEL) == -EINVAL) {
pr_err("invalid cycle detected\n");
goto err_C;
}
if (i915_sw_fence_await_sw_fence_gfp(C, B, GFP_KERNEL) != -EINVAL) {
pr_err("single depth cycle not detected (CBC)\n");
goto err_C;
}
if (i915_sw_fence_await_sw_fence_gfp(C, A, GFP_KERNEL) != -EINVAL) {
pr_err("cycle not detected (BA, CB, AC)\n");
goto err_C;
}
if (i915_sw_fence_await_sw_fence_gfp(A, C, GFP_KERNEL) == -EINVAL) {
pr_err("invalid cycle detected\n");
goto err_C;
}
i915_sw_fence_commit(A);
i915_sw_fence_commit(B);
i915_sw_fence_commit(C);
ret = 0;
if (!i915_sw_fence_done(C)) {
pr_err("fence C not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(B)) {
pr_err("fence B not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(A)) {
pr_err("fence A not done\n");
ret = -EINVAL;
}
err_C:
free_fence(C);
err_B:
free_fence(B);
err_A:
free_fence(A);
return ret;
}
static int test_AB(void *arg)
{
struct i915_sw_fence *A, *B;
int ret;
/* Test i915_sw_fence (A) waiting on an event source (B) */
A = alloc_fence();
if (!A)
return -ENOMEM;
B = alloc_fence();
if (!B) {
ret = -ENOMEM;
goto err_A;
}
ret = i915_sw_fence_await_sw_fence_gfp(A, B, GFP_KERNEL);
if (ret < 0)
goto err_B;
if (ret == 0) {
pr_err("Incorrectly reported fence A was complete before await\n");
ret = -EINVAL;
goto err_B;
}
ret = -EINVAL;
i915_sw_fence_commit(A);
if (i915_sw_fence_done(A))
goto err_B;
i915_sw_fence_commit(B);
if (!i915_sw_fence_done(B)) {
pr_err("Fence B is not done\n");
goto err_B;
}
if (!i915_sw_fence_done(A)) {
pr_err("Fence A is not done\n");
goto err_B;
}
ret = 0;
err_B:
free_fence(B);
err_A:
free_fence(A);
return ret;
}
static int test_ABC(void *arg)
{
struct i915_sw_fence *A, *B, *C;
int ret;
/* Test a chain of fences, A waits on B who waits on C */
A = alloc_fence();
if (!A)
return -ENOMEM;
B = alloc_fence();
if (!B) {
ret = -ENOMEM;
goto err_A;
}
C = alloc_fence();
if (!C) {
ret = -ENOMEM;
goto err_B;
}
ret = i915_sw_fence_await_sw_fence_gfp(A, B, GFP_KERNEL);
if (ret < 0)
goto err_C;
if (ret == 0) {
pr_err("Incorrectly reported fence B was complete before await\n");
goto err_C;
}
ret = i915_sw_fence_await_sw_fence_gfp(B, C, GFP_KERNEL);
if (ret < 0)
goto err_C;
if (ret == 0) {
pr_err("Incorrectly reported fence C was complete before await\n");
goto err_C;
}
ret = -EINVAL;
i915_sw_fence_commit(A);
if (i915_sw_fence_done(A)) {
pr_err("Fence A completed early\n");
goto err_C;
}
i915_sw_fence_commit(B);
if (i915_sw_fence_done(B)) {
pr_err("Fence B completed early\n");
goto err_C;
}
if (i915_sw_fence_done(A)) {
pr_err("Fence A completed early (after signaling B)\n");
goto err_C;
}
i915_sw_fence_commit(C);
ret = 0;
if (!i915_sw_fence_done(C)) {
pr_err("Fence C not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(B)) {
pr_err("Fence B not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(A)) {
pr_err("Fence A not done\n");
ret = -EINVAL;
}
err_C:
free_fence(C);
err_B:
free_fence(B);
err_A:
free_fence(A);
return ret;
}
static int test_AB_C(void *arg)
{
struct i915_sw_fence *A, *B, *C;
int ret = -EINVAL;
/* Test multiple fences (AB) waiting on a single event (C) */
A = alloc_fence();
if (!A)
return -ENOMEM;
B = alloc_fence();
if (!B) {
ret = -ENOMEM;
goto err_A;
}
C = alloc_fence();
if (!C) {
ret = -ENOMEM;
goto err_B;
}
ret = i915_sw_fence_await_sw_fence_gfp(A, C, GFP_KERNEL);
if (ret < 0)
goto err_C;
if (ret == 0) {
ret = -EINVAL;
goto err_C;
}
ret = i915_sw_fence_await_sw_fence_gfp(B, C, GFP_KERNEL);
if (ret < 0)
goto err_C;
if (ret == 0) {
ret = -EINVAL;
goto err_C;
}
i915_sw_fence_commit(A);
i915_sw_fence_commit(B);
ret = 0;
if (i915_sw_fence_done(A)) {
pr_err("Fence A completed early\n");
ret = -EINVAL;
}
if (i915_sw_fence_done(B)) {
pr_err("Fence B completed early\n");
ret = -EINVAL;
}
i915_sw_fence_commit(C);
if (!i915_sw_fence_done(C)) {
pr_err("Fence C not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(B)) {
pr_err("Fence B not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(A)) {
pr_err("Fence A not done\n");
ret = -EINVAL;
}
err_C:
free_fence(C);
err_B:
free_fence(B);
err_A:
free_fence(A);
return ret;
}
static int test_C_AB(void *arg)
{
struct i915_sw_fence *A, *B, *C;
int ret;
/* Test multiple event sources (A,B) for a single fence (C) */
A = alloc_fence();
if (!A)
return -ENOMEM;
B = alloc_fence();
if (!B) {
ret = -ENOMEM;
goto err_A;
}
C = alloc_fence();
if (!C) {
ret = -ENOMEM;
goto err_B;
}
ret = i915_sw_fence_await_sw_fence_gfp(C, A, GFP_KERNEL);
if (ret < 0)
goto err_C;
if (ret == 0) {
ret = -EINVAL;
goto err_C;
}
ret = i915_sw_fence_await_sw_fence_gfp(C, B, GFP_KERNEL);
if (ret < 0)
goto err_C;
if (ret == 0) {
ret = -EINVAL;
goto err_C;
}
ret = 0;
i915_sw_fence_commit(C);
if (i915_sw_fence_done(C))
ret = -EINVAL;
i915_sw_fence_commit(A);
i915_sw_fence_commit(B);
if (!i915_sw_fence_done(A)) {
pr_err("Fence A not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(B)) {
pr_err("Fence B not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(C)) {
pr_err("Fence C not done\n");
ret = -EINVAL;
}
err_C:
free_fence(C);
err_B:
free_fence(B);
err_A:
free_fence(A);
return ret;
}
static int test_chain(void *arg)
{
int nfences = 4096;
struct i915_sw_fence **fences;
int ret, i;
/* Test a long chain of fences */
fences = kmalloc_objs(*fences, nfences);
if (!fences)
return -ENOMEM;
for (i = 0; i < nfences; i++) {
fences[i] = alloc_fence();
if (!fences[i]) {
nfences = i;
ret = -ENOMEM;
goto err;
}
if (i > 0) {
ret = i915_sw_fence_await_sw_fence_gfp(fences[i],
fences[i - 1],
GFP_KERNEL);
if (ret < 0) {
nfences = i + 1;
goto err;
}
i915_sw_fence_commit(fences[i]);
}
}
ret = 0;
for (i = nfences; --i; ) {
if (i915_sw_fence_done(fences[i])) {
if (ret == 0)
pr_err("Fence[%d] completed early\n", i);
ret = -EINVAL;
}
}
i915_sw_fence_commit(fences[0]);
for (i = 0; ret == 0 && i < nfences; i++) {
if (!i915_sw_fence_done(fences[i])) {
pr_err("Fence[%d] is not done\n", i);
ret = -EINVAL;
}
}
err:
for (i = 0; i < nfences; i++)
free_fence(fences[i]);
kfree(fences);
return ret;
}
struct task_ipc {
struct work_struct work;
struct completion started;
struct i915_sw_fence *in, *out;
int value;
};
static void task_ipc(struct work_struct *work)
{
struct task_ipc *ipc = container_of(work, typeof(*ipc), work);
complete(&ipc->started);
i915_sw_fence_wait(ipc->in);
smp_store_mb(ipc->value, 1);
i915_sw_fence_commit(ipc->out);
}
static int test_ipc(void *arg)
{
struct task_ipc ipc;
struct workqueue_struct *wq;
int ret = 0;
wq = alloc_workqueue("i1915-selftest", WQ_PERCPU, 0);
if (wq == NULL)
return -ENOMEM;
/* Test use of i915_sw_fence as an interprocess signaling mechanism */
ipc.in = alloc_fence();
if (!ipc.in) {
ret = -ENOMEM;
goto err_work;
}
ipc.out = alloc_fence();
if (!ipc.out) {
ret = -ENOMEM;
goto err_in;
}
/* use a completion to avoid chicken-and-egg testing */
init_completion(&ipc.started);
ipc.value = 0;
INIT_WORK_ONSTACK(&ipc.work, task_ipc);
queue_work(wq, &ipc.work);
wait_for_completion(&ipc.started);
usleep_range(1000, 2000);
if (READ_ONCE(ipc.value)) {
pr_err("worker updated value before i915_sw_fence was signaled\n");
ret = -EINVAL;
}
i915_sw_fence_commit(ipc.in);
i915_sw_fence_wait(ipc.out);
if (!READ_ONCE(ipc.value)) {
pr_err("worker signaled i915_sw_fence before value was posted\n");
ret = -EINVAL;
}
flush_work(&ipc.work);
destroy_work_on_stack(&ipc.work);
free_fence(ipc.out);
err_in:
free_fence(ipc.in);
err_work:
destroy_workqueue(wq);
return ret;
}
static int test_timer(void *arg)
{
unsigned long target, delay;
struct timed_fence tf;
preempt_disable();
timed_fence_init(&tf, target = jiffies);
if (!i915_sw_fence_done(&tf.fence)) {
pr_err("Fence with immediate expiration not signaled\n");
goto err;
}
preempt_enable();
timed_fence_fini(&tf);
for_each_prime_number(delay, i915_selftest.timeout_jiffies/2) {
preempt_disable();
timed_fence_init(&tf, target = jiffies + delay);
if (i915_sw_fence_done(&tf.fence)) {
pr_err("Fence with future expiration (%lu jiffies) already signaled\n", delay);
goto err;
}
preempt_enable();
i915_sw_fence_wait(&tf.fence);
preempt_disable();
if (!i915_sw_fence_done(&tf.fence)) {
pr_err("Fence not signaled after wait\n");
goto err;
}
if (time_before(jiffies, target)) {
pr_err("Fence signaled too early, target=%lu, now=%lu\n",
target, jiffies);
goto err;
}
preempt_enable();
timed_fence_fini(&tf);
}
return 0;
err:
preempt_enable();
timed_fence_fini(&tf);
return -EINVAL;
}
static const char *mock_name(struct dma_fence *fence)
{
return "mock";
}
static const struct dma_fence_ops mock_fence_ops = {
.get_driver_name = mock_name,
.get_timeline_name = mock_name,
};
static DEFINE_SPINLOCK(mock_fence_lock);
static struct dma_fence *alloc_dma_fence(void)
{
struct dma_fence *dma;
dma = kmalloc_obj(*dma);
if (dma)
dma_fence_init(dma, &mock_fence_ops, &mock_fence_lock, 0, 0);
return dma;
}
static struct i915_sw_fence *
wrap_dma_fence(struct dma_fence *dma, unsigned long delay)
{
struct i915_sw_fence *fence;
int err;
fence = alloc_fence();
if (!fence)
return ERR_PTR(-ENOMEM);
err = i915_sw_fence_await_dma_fence(fence, dma, delay, GFP_NOWAIT);
i915_sw_fence_commit(fence);
if (err < 0) {
free_fence(fence);
return ERR_PTR(err);
}
return fence;
}
static int test_dma_fence(void *arg)
{
struct i915_sw_fence *timeout = NULL, *not = NULL;
unsigned long delay = i915_selftest.timeout_jiffies;
unsigned long end, sleep;
struct dma_fence *dma;
int err;
dma = alloc_dma_fence();
if (!dma)
return -ENOMEM;
timeout = wrap_dma_fence(dma, delay);
if (IS_ERR(timeout)) {
err = PTR_ERR(timeout);
goto err;
}
not = wrap_dma_fence(dma, 0);
if (IS_ERR(not)) {
err = PTR_ERR(not);
goto err;
}
err = -EINVAL;
if (i915_sw_fence_done(timeout) || i915_sw_fence_done(not)) {
pr_err("Fences immediately signaled\n");
goto err;
}
/* We round the timeout for the fence up to the next second */
end = round_jiffies_up(jiffies + delay);
sleep = jiffies_to_usecs(delay) / 3;
usleep_range(sleep, 2 * sleep);
if (time_after(jiffies, end)) {
pr_debug("Slept too long, delay=%lu, (target=%lu, now=%lu) skipping\n",
delay, end, jiffies);
goto skip;
}
if (i915_sw_fence_done(timeout) || i915_sw_fence_done(not)) {
pr_err("Fences signaled too early\n");
goto err;
}
if (!wait_event_timeout(timeout->wait,
i915_sw_fence_done(timeout),
2 * (end - jiffies) + 1)) {
pr_err("Timeout fence unsignaled!\n");
goto err;
}
if (i915_sw_fence_done(not)) {
pr_err("No timeout fence signaled!\n");
goto err;
}
skip:
dma_fence_signal(dma);
if (!i915_sw_fence_done(timeout) || !i915_sw_fence_done(not)) {
pr_err("Fences unsignaled\n");
goto err;
}
free_fence(not);
free_fence(timeout);
dma_fence_put(dma);
return 0;
err:
dma_fence_signal(dma);
if (!IS_ERR_OR_NULL(timeout))
free_fence(timeout);
if (!IS_ERR_OR_NULL(not))
free_fence(not);
dma_fence_put(dma);
return err;
}
int i915_sw_fence_mock_selftests(void)
{
static const struct i915_subtest tests[] = {
SUBTEST(test_self),
SUBTEST(test_dag),
SUBTEST(test_AB),
SUBTEST(test_ABC),
SUBTEST(test_AB_C),
SUBTEST(test_C_AB),
SUBTEST(test_chain),
SUBTEST(test_ipc),
SUBTEST(test_timer),
SUBTEST(test_dma_fence),
};
return i915_subtests(tests, NULL);
}
@@ -0,0 +1,616 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "../i915_selftest.h"
#include "i915_random.h"
static char *
__sync_print(struct i915_syncmap *p,
char *buf, unsigned long *sz,
unsigned int depth,
unsigned int last,
unsigned int idx)
{
unsigned long len;
unsigned int i, X;
if (depth) {
unsigned int d;
for (d = 0; d < depth - 1; d++) {
if (last & BIT(depth - d - 1))
len = scnprintf(buf, *sz, "| ");
else
len = scnprintf(buf, *sz, " ");
buf += len;
*sz -= len;
}
len = scnprintf(buf, *sz, "%x-> ", idx);
buf += len;
*sz -= len;
}
/* We mark bits after the prefix as "X" */
len = scnprintf(buf, *sz, "0x%016llx", p->prefix << p->height << SHIFT);
buf += len;
*sz -= len;
X = (p->height + SHIFT) / 4;
scnprintf(buf - X, *sz + X, "%*s", X, "XXXXXXXXXXXXXXXXX");
if (!p->height) {
for_each_set_bit(i, (unsigned long *)&p->bitmap, KSYNCMAP) {
len = scnprintf(buf, *sz, " %x:%x,",
i, __sync_seqno(p)[i]);
buf += len;
*sz -= len;
}
buf -= 1;
*sz += 1;
}
len = scnprintf(buf, *sz, "\n");
buf += len;
*sz -= len;
if (p->height) {
for_each_set_bit(i, (unsigned long *)&p->bitmap, KSYNCMAP) {
buf = __sync_print(__sync_child(p)[i], buf, sz,
depth + 1,
last << 1 | ((p->bitmap >> (i + 1)) ? 1 : 0),
i);
}
}
return buf;
}
static bool
i915_syncmap_print_to_buf(struct i915_syncmap *p, char *buf, unsigned long sz)
{
if (!p)
return false;
while (p->parent)
p = p->parent;
__sync_print(p, buf, &sz, 0, 1, 0);
return true;
}
static int check_syncmap_free(struct i915_syncmap **sync)
{
i915_syncmap_free(sync);
if (*sync) {
pr_err("sync not cleared after free\n");
return -EINVAL;
}
return 0;
}
static int dump_syncmap(struct i915_syncmap *sync, int err)
{
char *buf;
if (!err)
return check_syncmap_free(&sync);
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
goto skip;
if (i915_syncmap_print_to_buf(sync, buf, PAGE_SIZE))
pr_err("%s", buf);
kfree(buf);
skip:
i915_syncmap_free(&sync);
return err;
}
static int igt_syncmap_init(void *arg)
{
struct i915_syncmap *sync = (void *)~0ul;
/*
* Cursory check that we can initialise a random pointer and transform
* it into the root pointer of a syncmap.
*/
i915_syncmap_init(&sync);
return check_syncmap_free(&sync);
}
static int check_seqno(struct i915_syncmap *leaf, unsigned int idx, u32 seqno)
{
if (leaf->height) {
pr_err("%s: not a leaf, height is %d\n",
__func__, leaf->height);
return -EINVAL;
}
if (__sync_seqno(leaf)[idx] != seqno) {
pr_err("%s: seqno[%d], found %x, expected %x\n",
__func__, idx, __sync_seqno(leaf)[idx], seqno);
return -EINVAL;
}
return 0;
}
static int check_one(struct i915_syncmap **sync, u64 context, u32 seqno)
{
int err;
err = i915_syncmap_set(sync, context, seqno);
if (err)
return err;
if ((*sync)->height) {
pr_err("Inserting first context=%llx did not return leaf (height=%d, prefix=%llx\n",
context, (*sync)->height, (*sync)->prefix);
return -EINVAL;
}
if ((*sync)->parent) {
pr_err("Inserting first context=%llx created branches!\n",
context);
return -EINVAL;
}
if (hweight32((*sync)->bitmap) != 1) {
pr_err("First bitmap does not contain a single entry, found %x (count=%d)!\n",
(*sync)->bitmap, hweight32((*sync)->bitmap));
return -EINVAL;
}
err = check_seqno((*sync), ilog2((*sync)->bitmap), seqno);
if (err)
return err;
if (!i915_syncmap_is_later(sync, context, seqno)) {
pr_err("Lookup of first context=%llx/seqno=%x failed!\n",
context, seqno);
return -EINVAL;
}
return 0;
}
static int igt_syncmap_one(void *arg)
{
I915_RND_STATE(prng);
IGT_TIMEOUT(end_time);
struct i915_syncmap *sync;
unsigned long max = 1;
int err;
/*
* Check that inserting a new id, creates a leaf and only that leaf.
*/
i915_syncmap_init(&sync);
do {
u64 context = i915_prandom_u64_state(&prng);
unsigned long loop;
err = check_syncmap_free(&sync);
if (err)
goto out;
for (loop = 0; loop <= max; loop++) {
err = check_one(&sync, context,
prandom_u32_state(&prng));
if (err)
goto out;
}
max++;
} while (!__igt_timeout(end_time, NULL));
pr_debug("%s: Completed %lu single insertions\n",
__func__, max * (max - 1) / 2);
out:
return dump_syncmap(sync, err);
}
static int check_leaf(struct i915_syncmap **sync, u64 context, u32 seqno)
{
int err;
err = i915_syncmap_set(sync, context, seqno);
if (err)
return err;
if ((*sync)->height) {
pr_err("Inserting context=%llx did not return leaf (height=%d, prefix=%llx\n",
context, (*sync)->height, (*sync)->prefix);
return -EINVAL;
}
if (hweight32((*sync)->bitmap) != 1) {
pr_err("First entry into leaf (context=%llx) does not contain a single entry, found %x (count=%d)!\n",
context, (*sync)->bitmap, hweight32((*sync)->bitmap));
return -EINVAL;
}
err = check_seqno((*sync), ilog2((*sync)->bitmap), seqno);
if (err)
return err;
if (!i915_syncmap_is_later(sync, context, seqno)) {
pr_err("Lookup of first entry context=%llx/seqno=%x failed!\n",
context, seqno);
return -EINVAL;
}
return 0;
}
static int igt_syncmap_join_above(void *arg)
{
struct i915_syncmap *sync;
unsigned int pass, order;
int err;
i915_syncmap_init(&sync);
/*
* When we have a new id that doesn't fit inside the existing tree,
* we need to add a new layer above.
*
* 1: 0x00000001
* 2: 0x00000010
* 3: 0x00000100
* 4: 0x00001000
* ...
* Each pass the common prefix shrinks and we have to insert a join.
* Each join will only contain two branches, the latest of which
* is always a leaf.
*
* If we then reuse the same set of contexts, we expect to build an
* identical tree.
*/
for (pass = 0; pass < 3; pass++) {
for (order = 0; order < 64; order += SHIFT) {
u64 context = BIT_ULL(order);
struct i915_syncmap *join;
err = check_leaf(&sync, context, 0);
if (err)
goto out;
join = sync->parent;
if (!join) /* very first insert will have no parents */
continue;
if (!join->height) {
pr_err("Parent with no height!\n");
err = -EINVAL;
goto out;
}
if (hweight32(join->bitmap) != 2) {
pr_err("Join does not have 2 children: %x (%d)\n",
join->bitmap, hweight32(join->bitmap));
err = -EINVAL;
goto out;
}
if (__sync_child(join)[__sync_branch_idx(join, context)] != sync) {
pr_err("Leaf misplaced in parent!\n");
err = -EINVAL;
goto out;
}
}
}
out:
return dump_syncmap(sync, err);
}
static int igt_syncmap_join_below(void *arg)
{
struct i915_syncmap *sync;
unsigned int step, order, idx;
int err = -ENODEV;
i915_syncmap_init(&sync);
/*
* Check that we can split a compacted branch by replacing it with
* a join.
*/
for (step = 0; step < KSYNCMAP; step++) {
for (order = 64 - SHIFT; order > 0; order -= SHIFT) {
u64 context = step * BIT_ULL(order);
err = i915_syncmap_set(&sync, context, 0);
if (err)
goto out;
if (sync->height) {
pr_err("Inserting context=%llx (order=%d, step=%d) did not return leaf (height=%d, prefix=%llx\n",
context, order, step, sync->height, sync->prefix);
err = -EINVAL;
goto out;
}
}
}
for (step = 0; step < KSYNCMAP; step++) {
for (order = SHIFT; order < 64; order += SHIFT) {
u64 context = step * BIT_ULL(order);
if (!i915_syncmap_is_later(&sync, context, 0)) {
pr_err("1: context %llx (order=%d, step=%d) not found\n",
context, order, step);
err = -EINVAL;
goto out;
}
for (idx = 1; idx < KSYNCMAP; idx++) {
if (i915_syncmap_is_later(&sync, context + idx, 0)) {
pr_err("1: context %llx (order=%d, step=%d) should not exist\n",
context + idx, order, step);
err = -EINVAL;
goto out;
}
}
}
}
for (order = SHIFT; order < 64; order += SHIFT) {
for (step = 0; step < KSYNCMAP; step++) {
u64 context = step * BIT_ULL(order);
if (!i915_syncmap_is_later(&sync, context, 0)) {
pr_err("2: context %llx (order=%d, step=%d) not found\n",
context, order, step);
err = -EINVAL;
goto out;
}
}
}
out:
return dump_syncmap(sync, err);
}
static int igt_syncmap_neighbours(void *arg)
{
I915_RND_STATE(prng);
IGT_TIMEOUT(end_time);
struct i915_syncmap *sync;
int err = -ENODEV;
/*
* Each leaf holds KSYNCMAP seqno. Check that when we create KSYNCMAP
* neighbouring ids, they all fit into the same leaf.
*/
i915_syncmap_init(&sync);
do {
u64 context = i915_prandom_u64_state(&prng) & ~MASK;
unsigned int idx;
if (i915_syncmap_is_later(&sync, context, 0)) /* Skip repeats */
continue;
for (idx = 0; idx < KSYNCMAP; idx++) {
err = i915_syncmap_set(&sync, context + idx, 0);
if (err)
goto out;
if (sync->height) {
pr_err("Inserting context=%llx did not return leaf (height=%d, prefix=%llx\n",
context, sync->height, sync->prefix);
err = -EINVAL;
goto out;
}
if (sync->bitmap != BIT(idx + 1) - 1) {
pr_err("Inserting neighbouring context=0x%llx+%d, did not fit into the same leaf bitmap=%x (%d), expected %lx (%d)\n",
context, idx,
sync->bitmap, hweight32(sync->bitmap),
BIT(idx + 1) - 1, idx + 1);
err = -EINVAL;
goto out;
}
}
} while (!__igt_timeout(end_time, NULL));
out:
return dump_syncmap(sync, err);
}
static int igt_syncmap_compact(void *arg)
{
struct i915_syncmap *sync;
unsigned int idx, order;
int err = -ENODEV;
i915_syncmap_init(&sync);
/*
* The syncmap are "space efficient" compressed radix trees - any
* branch with only one child is skipped and replaced by the child.
*
* If we construct a tree with ids that are neighbouring at a non-zero
* height, we form a join but each child of that join is directly a
* leaf holding the single id.
*/
for (order = SHIFT; order < 64; order += SHIFT) {
err = check_syncmap_free(&sync);
if (err)
goto out;
/* Create neighbours in the parent */
for (idx = 0; idx < KSYNCMAP; idx++) {
u64 context = idx * BIT_ULL(order) + idx;
err = i915_syncmap_set(&sync, context, 0);
if (err)
goto out;
if (sync->height) {
pr_err("Inserting context=%llx (order=%d, idx=%d) did not return leaf (height=%d, prefix=%llx\n",
context, order, idx,
sync->height, sync->prefix);
err = -EINVAL;
goto out;
}
}
sync = sync->parent;
if (sync->parent) {
pr_err("Parent (join) of last leaf was not the sync!\n");
err = -EINVAL;
goto out;
}
if (sync->height != order) {
pr_err("Join does not have the expected height, found %d, expected %d\n",
sync->height, order);
err = -EINVAL;
goto out;
}
if (sync->bitmap != BIT(KSYNCMAP) - 1) {
pr_err("Join is not full!, found %x (%d) expected %lx (%d)\n",
sync->bitmap, hweight32(sync->bitmap),
BIT(KSYNCMAP) - 1, KSYNCMAP);
err = -EINVAL;
goto out;
}
/* Each of our children should be a leaf */
for (idx = 0; idx < KSYNCMAP; idx++) {
struct i915_syncmap *leaf = __sync_child(sync)[idx];
if (leaf->height) {
pr_err("Child %d is a not leaf!\n", idx);
err = -EINVAL;
goto out;
}
if (leaf->parent != sync) {
pr_err("Child %d is not attached to us!\n",
idx);
err = -EINVAL;
goto out;
}
if (!is_power_of_2(leaf->bitmap)) {
pr_err("Child %d holds more than one id, found %x (%d)\n",
idx, leaf->bitmap, hweight32(leaf->bitmap));
err = -EINVAL;
goto out;
}
if (leaf->bitmap != BIT(idx)) {
pr_err("Child %d has wrong seqno idx, found %d, expected %d\n",
idx, ilog2(leaf->bitmap), idx);
err = -EINVAL;
goto out;
}
}
}
out:
return dump_syncmap(sync, err);
}
static int igt_syncmap_random(void *arg)
{
I915_RND_STATE(prng);
IGT_TIMEOUT(end_time);
struct i915_syncmap *sync;
unsigned long count, phase, i;
u32 seqno;
int err;
i915_syncmap_init(&sync);
/*
* Having tried to test the individual operations within i915_syncmap,
* run a smoketest exploring the entire u64 space with random
* insertions.
*/
count = 0;
phase = jiffies + HZ/100 + 1;
do {
u64 context = i915_prandom_u64_state(&prng);
err = i915_syncmap_set(&sync, context, 0);
if (err)
goto out;
count++;
} while (!time_after(jiffies, phase));
seqno = 0;
phase = 0;
do {
I915_RND_STATE(ctx);
u32 last_seqno = seqno;
bool expect;
seqno = prandom_u32_state(&prng);
expect = seqno_later(last_seqno, seqno);
for (i = 0; i < count; i++) {
u64 context = i915_prandom_u64_state(&ctx);
if (i915_syncmap_is_later(&sync, context, seqno) != expect) {
pr_err("context=%llu, last=%u this=%u did not match expectation (%d)\n",
context, last_seqno, seqno, expect);
err = -EINVAL;
goto out;
}
err = i915_syncmap_set(&sync, context, seqno);
if (err)
goto out;
}
phase++;
} while (!__igt_timeout(end_time, NULL));
pr_debug("Completed %lu passes, each of %lu contexts\n", phase, count);
out:
return dump_syncmap(sync, err);
}
int i915_syncmap_mock_selftests(void)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_syncmap_init),
SUBTEST(igt_syncmap_one),
SUBTEST(igt_syncmap_join_above),
SUBTEST(igt_syncmap_join_below),
SUBTEST(igt_syncmap_neighbours),
SUBTEST(igt_syncmap_compact),
SUBTEST(igt_syncmap_random),
};
return i915_subtests(tests, NULL);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2018 Intel Corporation
*/
#include <linux/preempt.h>
#include <linux/bottom_half.h>
#include <linux/irqflags.h>
#include "igt_atomic.h"
static void __preempt_begin(void)
{
preempt_disable();
}
static void __preempt_end(void)
{
preempt_enable();
}
static void __softirq_begin(void)
{
local_bh_disable();
}
static void __softirq_end(void)
{
local_bh_enable();
}
static void __hardirq_begin(void)
{
local_irq_disable();
}
static void __hardirq_end(void)
{
local_irq_enable();
}
const struct igt_atomic_section igt_atomic_phases[] = {
{ "preempt", __preempt_begin, __preempt_end },
{ "softirq", __softirq_begin, __softirq_end },
{ "hardirq", __hardirq_begin, __hardirq_end },
{ }
};
@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2018 Intel Corporation
*/
#ifndef IGT_ATOMIC_H
#define IGT_ATOMIC_H
struct igt_atomic_section {
const char *name;
void (*critical_section_begin)(void);
void (*critical_section_end)(void);
};
extern const struct igt_atomic_section igt_atomic_phases[];
#endif /* IGT_ATOMIC_H */
@@ -0,0 +1,51 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2018 Intel Corporation
*/
#include "gt/intel_gt.h"
#include "gt/intel_gt_requests.h"
#include "i915_drv.h"
#include "i915_selftest.h"
#include "igt_flush_test.h"
int igt_flush_test(struct drm_i915_private *i915)
{
struct intel_gt *gt;
unsigned int i;
int ret = 0;
for_each_gt(gt, i915, i) {
struct intel_engine_cs *engine;
unsigned long timeout_ms = 0;
unsigned int id;
if (intel_gt_is_wedged(gt))
ret = -EIO;
for_each_engine(engine, gt, id) {
if (engine->props.preempt_timeout_ms > timeout_ms)
timeout_ms = engine->props.preempt_timeout_ms;
}
cond_resched();
/* 2x longest preempt timeout, experimentally determined */
if (intel_gt_wait_for_idle(gt, HZ * timeout_ms / 500) == -ETIME) {
pr_err("%pS timed out, cancelling all further testing.\n",
__builtin_return_address(0));
GEM_TRACE("%pS timed out.\n",
__builtin_return_address(0));
GEM_TRACE_DUMP();
intel_gt_set_wedged(gt);
ret = -EIO;
}
}
return ret;
}
@@ -0,0 +1,14 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2018 Intel Corporation
*/
#ifndef IGT_FLUSH_TEST_H
#define IGT_FLUSH_TEST_H
struct drm_i915_private;
int igt_flush_test(struct drm_i915_private *i915);
#endif /* IGT_FLUSH_TEST_H */
@@ -0,0 +1,83 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2018 Intel Corporation
*/
#include "i915_drv.h"
#include "gt/intel_gt.h"
#include "gt/intel_gt_print.h"
#include "../i915_selftest.h"
#include "igt_flush_test.h"
#include "igt_live_test.h"
int igt_live_test_begin(struct igt_live_test *t,
struct drm_i915_private *i915,
const char *func,
const char *name)
{
struct intel_engine_cs *engine;
enum intel_engine_id id;
struct intel_gt *gt;
unsigned int i;
int err;
t->i915 = i915;
t->func = func;
t->name = name;
for_each_gt(gt, i915, i) {
err = intel_gt_wait_for_idle(gt, MAX_SCHEDULE_TIMEOUT);
if (err) {
gt_err(gt, "%s(%s): GT failed to idle before, with err=%d!",
func, name, err);
return err;
}
for_each_engine(engine, gt, id)
t->reset_engine[i][id] =
i915_reset_engine_count(&i915->gpu_error,
engine);
}
t->reset_global = i915_reset_count(&i915->gpu_error);
return 0;
}
int igt_live_test_end(struct igt_live_test *t)
{
struct drm_i915_private *i915 = t->i915;
struct intel_engine_cs *engine;
enum intel_engine_id id;
struct intel_gt *gt;
unsigned int i;
if (igt_flush_test(i915))
return -EIO;
if (t->reset_global != i915_reset_count(&i915->gpu_error)) {
pr_err("%s(%s): GPU was reset %d times!\n",
t->func, t->name,
i915_reset_count(&i915->gpu_error) - t->reset_global);
return -EIO;
}
for_each_gt(gt, i915, i) {
for_each_engine(engine, gt, id) {
if (t->reset_engine[i][id] ==
i915_reset_engine_count(&i915->gpu_error, engine))
continue;
gt_err(gt, "%s(%s): engine '%s' was reset %d times!\n",
t->func, t->name, engine->name,
i915_reset_engine_count(&i915->gpu_error, engine) -
t->reset_engine[i][id]);
return -EIO;
}
}
return 0;
}
@@ -0,0 +1,36 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2019 Intel Corporation
*/
#ifndef IGT_LIVE_TEST_H
#define IGT_LIVE_TEST_H
#include "gt/intel_gt_defines.h" /* for I915_MAX_GT */
#include "gt/intel_engine.h" /* for I915_NUM_ENGINES */
struct drm_i915_private;
struct igt_live_test {
struct drm_i915_private *i915;
const char *func;
const char *name;
unsigned int reset_global;
unsigned int reset_engine[I915_MAX_GT][I915_NUM_ENGINES];
};
/*
* Flush the GPU state before and after the test to ensure that no residual
* code is running on the GPU that may affect this test. Also compare the
* state before and after the test and alert if it unexpectedly changes,
* e.g. if the GPU was reset.
*/
int igt_live_test_begin(struct igt_live_test *t,
struct drm_i915_private *i915,
const char *func,
const char *name);
int igt_live_test_end(struct igt_live_test *t);
#endif /* IGT_LIVE_TEST_H */
@@ -0,0 +1,65 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2019 Intel Corporation
*/
#include <drm/drm_file.h>
#include "i915_drv.h"
#include "igt_mmap.h"
unsigned long igt_mmap_offset_with_file(struct drm_i915_private *i915,
u64 offset,
unsigned long size,
unsigned long prot,
unsigned long flags,
struct file *file)
{
struct drm_vma_offset_node *node;
unsigned long addr;
int err;
/* no need to refcount, we own this object */
drm_vma_offset_lock_lookup(i915->drm.vma_offset_manager);
node = drm_vma_offset_exact_lookup_locked(i915->drm.vma_offset_manager,
offset / PAGE_SIZE, size / PAGE_SIZE);
drm_vma_offset_unlock_lookup(i915->drm.vma_offset_manager);
if (GEM_WARN_ON(!node)) {
pr_info("Failed to lookup %llx\n", offset);
return -ENOENT;
}
err = drm_vma_node_allow(node, file->private_data);
if (err) {
return err;
}
addr = vm_mmap(file, 0, drm_vma_node_size(node) << PAGE_SHIFT,
prot, flags, drm_vma_node_offset_addr(node));
drm_vma_node_revoke(node, file->private_data);
return addr;
}
unsigned long igt_mmap_offset(struct drm_i915_private *i915,
u64 offset,
unsigned long size,
unsigned long prot,
unsigned long flags)
{
struct file *file;
unsigned long addr;
/* Pretend to open("/dev/dri/card0") */
file = mock_drm_getfile(i915->drm.primary, O_RDWR);
if (IS_ERR(file))
return PTR_ERR(file);
addr = igt_mmap_offset_with_file(i915, offset, size, prot, flags, file);
fput(file);
return addr;
}
@@ -0,0 +1,29 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2019 Intel Corporation
*/
#ifndef IGT_MMAP_H
#define IGT_MMAP_H
#include <linux/types.h>
struct drm_i915_private;
struct drm_vma_offset_node;
struct file;
unsigned long igt_mmap_offset(struct drm_i915_private *i915,
u64 offset,
unsigned long size,
unsigned long prot,
unsigned long flags);
unsigned long igt_mmap_offset_with_file(struct drm_i915_private *i915,
u64 offset,
unsigned long size,
unsigned long prot,
unsigned long flags,
struct file *file);
#endif /* IGT_MMAP_H */
@@ -0,0 +1,51 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2018 Intel Corporation
*/
#include "igt_reset.h"
#include "gt/intel_engine.h"
#include "gt/intel_gt.h"
#include "../i915_drv.h"
void igt_global_reset_lock(struct intel_gt *gt)
{
struct intel_engine_cs *engine;
enum intel_engine_id id;
pr_debug("%s: current gpu_error=%08lx\n", __func__, gt->reset.flags);
while (test_and_set_bit(I915_RESET_BACKOFF, &gt->reset.flags))
wait_event(gt->reset.queue,
!test_bit(I915_RESET_BACKOFF, &gt->reset.flags));
for_each_engine(engine, gt, id) {
while (test_and_set_bit(I915_RESET_ENGINE + id,
&gt->reset.flags))
wait_on_bit(&gt->reset.flags, I915_RESET_ENGINE + id,
TASK_UNINTERRUPTIBLE);
}
}
void igt_global_reset_unlock(struct intel_gt *gt)
{
struct intel_engine_cs *engine;
enum intel_engine_id id;
for_each_engine(engine, gt, id)
clear_and_wake_up_bit(I915_RESET_ENGINE + id, &gt->reset.flags);
clear_bit(I915_RESET_BACKOFF, &gt->reset.flags);
wake_up_all(&gt->reset.queue);
}
bool igt_force_reset(struct intel_gt *gt)
{
intel_gt_set_wedged(gt);
intel_gt_reset(gt, 0, NULL);
return !intel_gt_is_wedged(gt);
}
@@ -0,0 +1,18 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2018 Intel Corporation
*/
#ifndef __I915_SELFTESTS_IGT_RESET_H__
#define __I915_SELFTESTS_IGT_RESET_H__
#include <linux/types.h>
struct intel_gt;
void igt_global_reset_lock(struct intel_gt *gt);
void igt_global_reset_unlock(struct intel_gt *gt);
bool igt_force_reset(struct intel_gt *gt);
#endif
@@ -0,0 +1,265 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2018 Intel Corporation
*/
#include "gem/i915_gem_internal.h"
#include "gem/selftests/igt_gem_utils.h"
#include "gt/intel_gpu_commands.h"
#include "gt/intel_gt.h"
#include "i915_wait_util.h"
#include "igt_spinner.h"
int igt_spinner_init(struct igt_spinner *spin, struct intel_gt *gt)
{
int err;
memset(spin, 0, sizeof(*spin));
spin->gt = gt;
spin->hws = i915_gem_object_create_internal(gt->i915, PAGE_SIZE);
if (IS_ERR(spin->hws)) {
err = PTR_ERR(spin->hws);
goto err;
}
i915_gem_object_set_cache_coherency(spin->hws, I915_CACHE_LLC);
spin->obj = i915_gem_object_create_internal(gt->i915, PAGE_SIZE);
if (IS_ERR(spin->obj)) {
err = PTR_ERR(spin->obj);
goto err_hws;
}
return 0;
err_hws:
i915_gem_object_put(spin->hws);
err:
return err;
}
static void *igt_spinner_pin_obj(struct intel_context *ce,
struct i915_gem_ww_ctx *ww,
struct drm_i915_gem_object *obj,
unsigned int mode, struct i915_vma **vma)
{
void *vaddr;
int ret;
*vma = i915_vma_instance(obj, ce->vm, NULL);
if (IS_ERR(*vma))
return ERR_CAST(*vma);
ret = i915_gem_object_lock(obj, ww);
if (ret)
return ERR_PTR(ret);
vaddr = i915_gem_object_pin_map(obj, mode);
if (!ww)
i915_gem_object_unlock(obj);
if (IS_ERR(vaddr))
return vaddr;
if (ww)
ret = i915_vma_pin_ww(*vma, ww, 0, 0, PIN_USER);
else
ret = i915_vma_pin(*vma, 0, 0, PIN_USER);
if (ret) {
i915_gem_object_unpin_map(obj);
return ERR_PTR(ret);
}
return vaddr;
}
int igt_spinner_pin(struct igt_spinner *spin,
struct intel_context *ce,
struct i915_gem_ww_ctx *ww)
{
void *vaddr;
if (spin->ce && WARN_ON(spin->ce != ce))
return -ENODEV;
spin->ce = ce;
if (!spin->seqno) {
vaddr = igt_spinner_pin_obj(ce, ww, spin->hws, I915_MAP_WB, &spin->hws_vma);
if (IS_ERR(vaddr))
return PTR_ERR(vaddr);
spin->seqno = memset(vaddr, 0xff, PAGE_SIZE);
}
if (!spin->batch) {
unsigned int mode;
mode = intel_gt_coherent_map_type(spin->gt, spin->obj, false);
vaddr = igt_spinner_pin_obj(ce, ww, spin->obj, mode, &spin->batch_vma);
if (IS_ERR(vaddr))
return PTR_ERR(vaddr);
spin->batch = vaddr;
}
return 0;
}
static unsigned int seqno_offset(u64 fence)
{
return offset_in_page(sizeof(u32) * fence);
}
static u64 hws_address(const struct i915_vma *hws,
const struct i915_request *rq)
{
return i915_vma_offset(hws) + seqno_offset(rq->fence.context);
}
struct i915_request *
igt_spinner_create_request(struct igt_spinner *spin,
struct intel_context *ce,
u32 arbitration_command)
{
struct intel_engine_cs *engine = ce->engine;
struct i915_request *rq = NULL;
struct i915_vma *hws, *vma;
unsigned int flags;
u32 *batch;
int err;
GEM_BUG_ON(spin->gt != ce->vm->gt);
if (!intel_engine_can_store_dword(ce->engine))
return ERR_PTR(-ENODEV);
if (!spin->batch) {
err = igt_spinner_pin(spin, ce, NULL);
if (err)
return ERR_PTR(err);
}
hws = spin->hws_vma;
vma = spin->batch_vma;
rq = intel_context_create_request(ce);
if (IS_ERR(rq))
return ERR_CAST(rq);
err = igt_vma_move_to_active_unlocked(vma, rq, 0);
if (err)
goto cancel_rq;
err = igt_vma_move_to_active_unlocked(hws, rq, 0);
if (err)
goto cancel_rq;
batch = spin->batch;
if (GRAPHICS_VER(rq->i915) >= 8) {
*batch++ = MI_STORE_DWORD_IMM_GEN4;
*batch++ = lower_32_bits(hws_address(hws, rq));
*batch++ = upper_32_bits(hws_address(hws, rq));
} else if (GRAPHICS_VER(rq->i915) >= 6) {
*batch++ = MI_STORE_DWORD_IMM_GEN4;
*batch++ = 0;
*batch++ = hws_address(hws, rq);
} else if (GRAPHICS_VER(rq->i915) >= 4) {
*batch++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
*batch++ = 0;
*batch++ = hws_address(hws, rq);
} else {
*batch++ = MI_STORE_DWORD_IMM | MI_MEM_VIRTUAL;
*batch++ = hws_address(hws, rq);
}
*batch++ = rq->fence.seqno;
*batch++ = arbitration_command;
memset32(batch, MI_NOOP, 128);
batch += 128;
if (GRAPHICS_VER(rq->i915) >= 8)
*batch++ = MI_BATCH_BUFFER_START | BIT(8) | 1;
else if (IS_HASWELL(rq->i915))
*batch++ = MI_BATCH_BUFFER_START | MI_BATCH_PPGTT_HSW;
else if (GRAPHICS_VER(rq->i915) >= 6)
*batch++ = MI_BATCH_BUFFER_START;
else
*batch++ = MI_BATCH_BUFFER_START | MI_BATCH_GTT;
*batch++ = lower_32_bits(i915_vma_offset(vma));
*batch++ = upper_32_bits(i915_vma_offset(vma));
*batch++ = MI_BATCH_BUFFER_END; /* not reached */
intel_gt_chipset_flush(engine->gt);
if (engine->emit_init_breadcrumb) {
err = engine->emit_init_breadcrumb(rq);
if (err)
goto cancel_rq;
}
flags = 0;
if (GRAPHICS_VER(rq->i915) <= 5)
flags |= I915_DISPATCH_SECURE;
err = engine->emit_bb_start(rq, i915_vma_offset(vma), PAGE_SIZE, flags);
cancel_rq:
if (err) {
i915_request_set_error_once(rq, err);
i915_request_add(rq);
}
return err ? ERR_PTR(err) : rq;
}
static u32
hws_seqno(const struct igt_spinner *spin, const struct i915_request *rq)
{
u32 *seqno = spin->seqno + seqno_offset(rq->fence.context);
return READ_ONCE(*seqno);
}
void igt_spinner_end(struct igt_spinner *spin)
{
if (!spin->batch)
return;
*spin->batch = MI_BATCH_BUFFER_END;
intel_gt_chipset_flush(spin->gt);
}
void igt_spinner_fini(struct igt_spinner *spin)
{
igt_spinner_end(spin);
if (spin->batch) {
i915_vma_unpin(spin->batch_vma);
i915_gem_object_unpin_map(spin->obj);
}
i915_gem_object_put(spin->obj);
if (spin->seqno) {
i915_vma_unpin(spin->hws_vma);
i915_gem_object_unpin_map(spin->hws);
}
i915_gem_object_put(spin->hws);
}
bool igt_wait_for_spinner(struct igt_spinner *spin, struct i915_request *rq)
{
if (i915_request_is_ready(rq))
intel_engine_flush_submission(rq->engine);
return !(wait_for_us(i915_seqno_passed(hws_seqno(spin, rq),
rq->fence.seqno),
100) &&
wait_for(i915_seqno_passed(hws_seqno(spin, rq),
rq->fence.seqno),
50));
}
@@ -0,0 +1,43 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2018 Intel Corporation
*/
#ifndef __I915_SELFTESTS_IGT_SPINNER_H__
#define __I915_SELFTESTS_IGT_SPINNER_H__
#include "gem/i915_gem_context.h"
#include "gt/intel_engine.h"
#include "i915_drv.h"
#include "i915_request.h"
#include "i915_selftest.h"
struct intel_gt;
struct igt_spinner {
struct intel_gt *gt;
struct drm_i915_gem_object *hws;
struct drm_i915_gem_object *obj;
struct intel_context *ce;
struct i915_vma *hws_vma, *batch_vma;
u32 *batch;
void *seqno;
};
int igt_spinner_init(struct igt_spinner *spin, struct intel_gt *gt);
int igt_spinner_pin(struct igt_spinner *spin,
struct intel_context *ce,
struct i915_gem_ww_ctx *ww);
void igt_spinner_fini(struct igt_spinner *spin);
struct i915_request *
igt_spinner_create_request(struct igt_spinner *spin,
struct intel_context *ce,
u32 arbitration_command);
void igt_spinner_end(struct igt_spinner *spin);
bool igt_wait_for_spinner(struct igt_spinner *spin, struct i915_request *rq);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2021 Intel Corporation
*/
#include <linux/jiffies.h>
//#include "gt/intel_engine_user.h"
#include "gt/intel_gt.h"
#include "i915_drv.h"
#include "i915_selftest.h"
#include "selftests/intel_scheduler_helpers.h"
#define REDUCED_TIMESLICE 5
#define REDUCED_PREEMPT 10
#define WAIT_FOR_RESET_TIME_MS 10000
struct intel_engine_cs *intel_selftest_find_any_engine(struct intel_gt *gt)
{
struct intel_engine_cs *engine;
enum intel_engine_id id;
for_each_engine(engine, gt, id)
return engine;
pr_err("No valid engine found!\n");
return NULL;
}
int intel_selftest_modify_policy(struct intel_engine_cs *engine,
struct intel_selftest_saved_policy *saved,
enum selftest_scheduler_modify modify_type)
{
int err;
saved->reset = engine->i915->params.reset;
saved->flags = engine->flags;
saved->timeslice = engine->props.timeslice_duration_ms;
saved->preempt_timeout = engine->props.preempt_timeout_ms;
switch (modify_type) {
case SELFTEST_SCHEDULER_MODIFY_FAST_RESET:
/*
* Enable force pre-emption on time slice expiration
* together with engine reset on pre-emption timeout.
* This is required to make the GuC notice and reset
* the single hanging context.
* Also, reduce the preemption timeout to something
* small to speed the test up.
*/
engine->i915->params.reset = 2;
engine->flags |= I915_ENGINE_WANT_FORCED_PREEMPTION;
engine->props.timeslice_duration_ms = REDUCED_TIMESLICE;
engine->props.preempt_timeout_ms = REDUCED_PREEMPT;
break;
case SELFTEST_SCHEDULER_MODIFY_NO_HANGCHECK:
engine->props.preempt_timeout_ms = 0;
break;
default:
pr_err("Invalid scheduler policy modification type: %d!\n", modify_type);
return -EINVAL;
}
if (!intel_engine_uses_guc(engine))
return 0;
err = intel_guc_global_policies_update(&engine->gt->uc.guc);
if (err)
intel_selftest_restore_policy(engine, saved);
return err;
}
int intel_selftest_restore_policy(struct intel_engine_cs *engine,
struct intel_selftest_saved_policy *saved)
{
/* Restore the original policies */
engine->i915->params.reset = saved->reset;
engine->flags = saved->flags;
engine->props.timeslice_duration_ms = saved->timeslice;
engine->props.preempt_timeout_ms = saved->preempt_timeout;
if (!intel_engine_uses_guc(engine))
return 0;
return intel_guc_global_policies_update(&engine->gt->uc.guc);
}
int intel_selftest_wait_for_rq(struct i915_request *rq)
{
long ret;
ret = i915_request_wait(rq, 0, msecs_to_jiffies(WAIT_FOR_RESET_TIME_MS));
if (ret < 0)
return ret;
return 0;
}
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2021 Intel Corporation
*/
#ifndef _INTEL_SELFTEST_SCHEDULER_HELPERS_H_
#define _INTEL_SELFTEST_SCHEDULER_HELPERS_H_
#include <linux/types.h>
struct i915_request;
struct intel_engine_cs;
struct intel_gt;
struct intel_selftest_saved_policy {
u32 flags;
u32 reset;
u64 timeslice;
u64 preempt_timeout;
};
enum selftest_scheduler_modify {
SELFTEST_SCHEDULER_MODIFY_NO_HANGCHECK = 0,
SELFTEST_SCHEDULER_MODIFY_FAST_RESET,
};
struct intel_engine_cs *intel_selftest_find_any_engine(struct intel_gt *gt);
int intel_selftest_modify_policy(struct intel_engine_cs *engine,
struct intel_selftest_saved_policy *saved,
enum selftest_scheduler_modify modify_type);
int intel_selftest_restore_policy(struct intel_engine_cs *engine,
struct intel_selftest_saved_policy *saved);
int intel_selftest_wait_for_rq(struct i915_request *rq);
#endif
@@ -0,0 +1,293 @@
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "../i915_selftest.h"
#include "gt/intel_gt.h"
static int intel_fw_table_check(const struct intel_forcewake_range *ranges,
unsigned int num_ranges,
bool is_watertight)
{
unsigned int i;
s32 prev;
for (i = 0, prev = -1; i < num_ranges; i++, ranges++) {
/* Check that the table is watertight */
if (is_watertight && (prev + 1) != (s32)ranges->start) {
pr_err("%s: entry[%d]:(%x, %x) is not watertight to previous (%x)\n",
__func__, i, ranges->start, ranges->end, prev);
return -EINVAL;
}
/* Check that the table never goes backwards */
if (prev >= (s32)ranges->start) {
pr_err("%s: entry[%d]:(%x, %x) is less than the previous (%x)\n",
__func__, i, ranges->start, ranges->end, prev);
return -EINVAL;
}
/* Check that the entry is valid */
if (ranges->start >= ranges->end) {
pr_err("%s: entry[%d]:(%x, %x) has negative length\n",
__func__, i, ranges->start, ranges->end);
return -EINVAL;
}
prev = ranges->end;
}
return 0;
}
static int intel_shadow_table_check(void)
{
struct {
const struct i915_mmio_range *regs;
unsigned int size;
} range_lists[] = {
{ gen8_shadowed_regs, ARRAY_SIZE(gen8_shadowed_regs) },
{ gen11_shadowed_regs, ARRAY_SIZE(gen11_shadowed_regs) },
{ gen12_shadowed_regs, ARRAY_SIZE(gen12_shadowed_regs) },
{ dg2_shadowed_regs, ARRAY_SIZE(dg2_shadowed_regs) },
{ mtl_shadowed_regs, ARRAY_SIZE(mtl_shadowed_regs) },
{ xelpmp_shadowed_regs, ARRAY_SIZE(xelpmp_shadowed_regs) },
};
const struct i915_mmio_range *range;
unsigned int i, j;
s32 prev;
for (j = 0; j < ARRAY_SIZE(range_lists); ++j) {
range = range_lists[j].regs;
for (i = 0, prev = -1; i < range_lists[j].size; i++, range++) {
if (range->end < range->start) {
pr_err("%s: range[%d]:(%06x-%06x) has end before start\n",
__func__, i, range->start, range->end);
return -EINVAL;
}
if (prev >= (s32)range->start) {
pr_err("%s: range[%d]:(%06x-%06x) is before end of previous (%06x)\n",
__func__, i, range->start, range->end, prev);
return -EINVAL;
}
if (range->start % 4) {
pr_err("%s: range[%d]:(%06x-%06x) has non-dword-aligned start\n",
__func__, i, range->start, range->end);
return -EINVAL;
}
prev = range->end;
}
}
return 0;
}
int intel_uncore_mock_selftests(void)
{
struct {
const struct intel_forcewake_range *ranges;
unsigned int num_ranges;
bool is_watertight;
} fw[] = {
{ __vlv_fw_ranges, ARRAY_SIZE(__vlv_fw_ranges), false },
{ __chv_fw_ranges, ARRAY_SIZE(__chv_fw_ranges), false },
{ __gen9_fw_ranges, ARRAY_SIZE(__gen9_fw_ranges), true },
{ __gen11_fw_ranges, ARRAY_SIZE(__gen11_fw_ranges), true },
{ __gen12_fw_ranges, ARRAY_SIZE(__gen12_fw_ranges), true },
{ __mtl_fw_ranges, ARRAY_SIZE(__mtl_fw_ranges), true },
{ __xelpmp_fw_ranges, ARRAY_SIZE(__xelpmp_fw_ranges), true },
};
int err, i;
for (i = 0; i < ARRAY_SIZE(fw); i++) {
err = intel_fw_table_check(fw[i].ranges,
fw[i].num_ranges,
fw[i].is_watertight);
if (err)
return err;
}
err = intel_shadow_table_check();
if (err)
return err;
return 0;
}
static int live_forcewake_ops(void *arg)
{
static const struct reg {
const char *name;
u8 min_graphics_ver;
u8 max_graphics_ver;
unsigned long platforms;
unsigned int offset;
} registers[] = {
{
"RING_START",
6, 7,
0x38,
},
{
"RING_MI_MODE",
8, U8_MAX,
0x9c,
}
};
const struct reg *r;
struct intel_gt *gt = arg;
struct intel_uncore_forcewake_domain *domain;
struct intel_uncore *uncore = gt->uncore;
struct intel_engine_cs *engine;
enum intel_engine_id id;
intel_wakeref_t wakeref;
unsigned int tmp;
int err = 0;
GEM_BUG_ON(gt->awake);
/* vlv/chv with their pcu behave differently wrt reads */
if (IS_VALLEYVIEW(gt->i915) || IS_CHERRYVIEW(gt->i915)) {
pr_debug("PCU fakes forcewake badly; skipping\n");
return 0;
}
/*
* Not quite as reliable across the gen as one would hope.
*
* Either our theory of operation is incorrect, or there remain
* external parties interfering with the powerwells.
*
* https://bugs.freedesktop.org/show_bug.cgi?id=110210
*/
if (!IS_ENABLED(CONFIG_DRM_I915_SELFTEST_BROKEN))
return 0;
/* We have to pick carefully to get the exact behaviour we need */
for (r = registers; r->name; r++)
if (IS_GRAPHICS_VER(gt->i915, r->min_graphics_ver, r->max_graphics_ver))
break;
if (!r->name) {
pr_debug("Forcewaked register not known for %s; skipping\n",
intel_platform_name(INTEL_INFO(gt->i915)->platform));
return 0;
}
wakeref = intel_runtime_pm_get(uncore->rpm);
for_each_fw_domain(domain, uncore, tmp) {
smp_store_mb(domain->active, false);
if (!hrtimer_cancel(&domain->timer))
continue;
intel_uncore_fw_release_timer(&domain->timer);
}
for_each_engine(engine, gt, id) {
i915_reg_t mmio = _MMIO(engine->mmio_base + r->offset);
u32 __iomem *reg = intel_uncore_regs(uncore) + engine->mmio_base + r->offset;
enum forcewake_domains fw_domains;
u32 val;
if (!engine->default_state)
continue;
fw_domains = intel_uncore_forcewake_for_reg(uncore, mmio,
FW_REG_READ);
if (!fw_domains)
continue;
for_each_fw_domain_masked(domain, fw_domains, uncore, tmp) {
if (!domain->wake_count)
continue;
pr_err("fw_domain %s still active, aborting test!\n",
intel_uncore_forcewake_domain_to_str(domain->id));
err = -EINVAL;
goto out_rpm;
}
intel_uncore_forcewake_get(uncore, fw_domains);
val = readl(reg);
intel_uncore_forcewake_put(uncore, fw_domains);
/* Flush the forcewake release (delayed onto a timer) */
for_each_fw_domain_masked(domain, fw_domains, uncore, tmp) {
smp_store_mb(domain->active, false);
if (hrtimer_cancel(&domain->timer))
intel_uncore_fw_release_timer(&domain->timer);
preempt_disable();
err = wait_ack_clear(domain, FORCEWAKE_KERNEL);
preempt_enable();
if (err) {
pr_err("Failed to clear fw_domain %s\n",
intel_uncore_forcewake_domain_to_str(domain->id));
goto out_rpm;
}
}
if (!val) {
pr_err("%s:%s was zero while fw was held!\n",
engine->name, r->name);
err = -EINVAL;
goto out_rpm;
}
/* We then expect the read to return 0 outside of the fw */
if (wait_for(readl(reg) == 0, 100)) {
pr_err("%s:%s=%0x, fw_domains 0x%x still up after 100ms!\n",
engine->name, r->name, readl(reg), fw_domains);
err = -ETIMEDOUT;
goto out_rpm;
}
}
out_rpm:
intel_runtime_pm_put(uncore->rpm, wakeref);
return err;
}
static int live_fw_table(void *arg)
{
struct intel_gt *gt = arg;
/* Confirm the table we load is still valid */
return intel_fw_table_check(gt->uncore->fw_domains_table,
gt->uncore->fw_domains_table_entries,
GRAPHICS_VER(gt->i915) >= 9);
}
int intel_uncore_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
SUBTEST(live_fw_table),
SUBTEST(live_forcewake_ops),
};
return intel_gt_live_subtests(tests, to_gt(i915));
}
@@ -0,0 +1,136 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "lib_sw_fence.h"
/* Small library of different fence types useful for writing tests */
static int
nop_fence_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
{
return NOTIFY_DONE;
}
void __onstack_fence_init(struct i915_sw_fence *fence,
const char *name,
struct lock_class_key *key)
{
debug_fence_init_onstack(fence);
__init_waitqueue_head(&fence->wait, name, key);
atomic_set(&fence->pending, 1);
fence->error = 0;
fence->fn = nop_fence_notify;
}
void onstack_fence_fini(struct i915_sw_fence *fence)
{
if (!fence->fn)
return;
i915_sw_fence_commit(fence);
i915_sw_fence_fini(fence);
}
static void timed_fence_wake(struct timer_list *t)
{
struct timed_fence *tf = timer_container_of(tf, t, timer);
i915_sw_fence_commit(&tf->fence);
}
void timed_fence_init(struct timed_fence *tf, unsigned long expires)
{
onstack_fence_init(&tf->fence);
timer_setup_on_stack(&tf->timer, timed_fence_wake, 0);
if (time_after(expires, jiffies))
mod_timer(&tf->timer, expires);
else
i915_sw_fence_commit(&tf->fence);
}
void timed_fence_fini(struct timed_fence *tf)
{
if (timer_delete_sync(&tf->timer))
i915_sw_fence_commit(&tf->fence);
timer_destroy_on_stack(&tf->timer);
i915_sw_fence_fini(&tf->fence);
}
struct heap_fence {
struct i915_sw_fence fence;
union {
struct kref ref;
struct rcu_head rcu;
};
};
static int
heap_fence_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
{
struct heap_fence *h = container_of(fence, typeof(*h), fence);
switch (state) {
case FENCE_COMPLETE:
break;
case FENCE_FREE:
heap_fence_put(&h->fence);
}
return NOTIFY_DONE;
}
struct i915_sw_fence *heap_fence_create(gfp_t gfp)
{
struct heap_fence *h;
h = kmalloc_obj(*h, gfp);
if (!h)
return NULL;
i915_sw_fence_init(&h->fence, heap_fence_notify);
refcount_set(&h->ref.refcount, 2);
return &h->fence;
}
static void heap_fence_release(struct kref *ref)
{
struct heap_fence *h = container_of(ref, typeof(*h), ref);
i915_sw_fence_fini(&h->fence);
kfree_rcu(h, rcu);
}
void heap_fence_put(struct i915_sw_fence *fence)
{
struct heap_fence *h = container_of(fence, typeof(*h), fence);
kref_put(&h->ref, heap_fence_release);
}
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* lib_sw_fence.h - library routines for testing N:M synchronisation points
*
* Copyright (C) 2017 Intel Corporation
*/
#ifndef _LIB_SW_FENCE_H_
#define _LIB_SW_FENCE_H_
#include <linux/timer.h>
#include "../i915_sw_fence.h"
#ifdef CONFIG_LOCKDEP
#define onstack_fence_init(fence) \
do { \
static struct lock_class_key __key; \
\
__onstack_fence_init((fence), #fence, &__key); \
} while (0)
#else
#define onstack_fence_init(fence) \
__onstack_fence_init((fence), NULL, NULL)
#endif
void __onstack_fence_init(struct i915_sw_fence *fence,
const char *name,
struct lock_class_key *key);
void onstack_fence_fini(struct i915_sw_fence *fence);
struct timed_fence {
struct i915_sw_fence fence;
struct timer_list timer;
};
void timed_fence_init(struct timed_fence *tf, unsigned long expires);
void timed_fence_fini(struct timed_fence *tf);
struct i915_sw_fence *heap_fence_create(gfp_t gfp);
void heap_fence_put(struct i915_sw_fence *fence);
#endif /* _LIB_SW_FENCE_H_ */
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2020 Intel Corporation
*/
#include <asm/msr.h>
#include "i915_drv.h"
#include "librapl.h"
bool librapl_supported(const struct drm_i915_private *i915)
{
/* Discrete cards require hwmon integration */
if (IS_DGFX(i915))
return false;
return librapl_energy_uJ();
}
u64 librapl_energy_uJ(void)
{
unsigned long long power;
u32 units;
if (rdmsrq_safe(MSR_RAPL_POWER_UNIT, &power))
return 0;
units = (power & 0x1f00) >> 8;
if (rdmsrq_safe(MSR_PP1_ENERGY_STATUS, &power))
return 0;
return (1000000 * power) >> units; /* convert to uJ */
}
@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2020 Intel Corporation
*/
#ifndef SELFTEST_LIBRAPL_H
#define SELFTEST_LIBRAPL_H
#include <linux/types.h>
struct drm_i915_private;
bool librapl_supported(const struct drm_i915_private *i915);
u64 librapl_energy_uJ(void);
#endif /* SELFTEST_LIBRAPL_H */
@@ -0,0 +1,45 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#ifndef __MOCK_DRM_H
#define __MOCK_DRM_H
#include <drm/drm_file.h>
#include "i915_drv.h"
struct drm_file;
struct file;
static inline struct file *mock_file(struct drm_i915_private *i915)
{
return mock_drm_getfile(i915->drm.primary, O_RDWR);
}
static inline struct drm_file *to_drm_file(struct file *f)
{
return f->private_data;
}
#endif /* !__MOCK_DRM_H */
@@ -0,0 +1,284 @@
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/iommu.h>
#include <drm/drm_managed.h>
#include "display/intel_display_device.h"
#include "gt/intel_gt.h"
#include "gt/intel_gt_requests.h"
#include "gt/mock_engine.h"
#include "i915_driver.h"
#include "intel_memory_region.h"
#include "intel_region_ttm.h"
#include "mock_request.h"
#include "mock_gem_device.h"
#include "mock_gtt.h"
#include "mock_uncore.h"
#include "mock_region.h"
#include "gem/selftests/mock_context.h"
#include "gem/selftests/mock_gem_object.h"
void mock_device_flush(struct drm_i915_private *i915)
{
struct intel_gt *gt = to_gt(i915);
struct intel_engine_cs *engine;
enum intel_engine_id id;
do {
for_each_engine(engine, gt, id)
mock_engine_flush(engine);
} while (intel_gt_retire_requests_timeout(gt, MAX_SCHEDULE_TIMEOUT,
NULL));
}
static void mock_device_release(struct drm_device *dev)
{
struct drm_i915_private *i915 = to_i915(dev);
if (!i915->do_release)
goto out;
mock_device_flush(i915);
intel_gt_driver_remove(to_gt(i915));
i915_gem_drain_workqueue(i915);
mock_fini_ggtt(to_gt(i915)->ggtt);
destroy_workqueue(i915->unordered_wq);
destroy_workqueue(i915->wq);
intel_region_ttm_device_fini(i915);
intel_gt_driver_late_release_all(i915);
intel_memory_regions_driver_release(i915);
drm_mode_config_cleanup(&i915->drm);
out:
i915_params_free(&i915->params);
}
static const struct drm_driver mock_driver = {
.name = "mock",
.driver_features = DRIVER_GEM,
.release = mock_device_release,
};
static void release_dev(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
kfree(pdev);
}
static int pm_domain_resume(struct device *dev)
{
return pm_generic_runtime_resume(dev);
}
static int pm_domain_suspend(struct device *dev)
{
return pm_generic_runtime_suspend(dev);
}
static struct dev_pm_domain pm_domain = {
.ops = {
.runtime_suspend = pm_domain_suspend,
.runtime_resume = pm_domain_resume,
},
};
static void mock_gt_probe(struct drm_i915_private *i915)
{
i915->gt[0]->name = "Mock GT";
}
static const struct intel_device_info mock_info = {
.__runtime.graphics.ip.ver = -1,
.__runtime.page_sizes = (I915_GTT_PAGE_SIZE_4K |
I915_GTT_PAGE_SIZE_64K |
I915_GTT_PAGE_SIZE_2M),
.memory_regions = BIT(INTEL_REGION_SMEM),
.platform_engine_mask = BIT(0),
/* simply use legacy cache level for mock device */
.max_pat_index = 3,
.cachelevel_to_pat = {
[I915_CACHE_NONE] = 0,
[I915_CACHE_LLC] = 1,
[I915_CACHE_L3_LLC] = 2,
[I915_CACHE_WT] = 3,
},
};
struct drm_i915_private *mock_gem_device(void)
{
#if IS_ENABLED(CONFIG_IOMMU_API) && IS_ENABLED(CONFIG_INTEL_IOMMU)
static struct dev_iommu fake_iommu = { .priv = (void *)-1 };
#endif
struct drm_i915_private *i915;
struct intel_display *display;
struct pci_dev *pdev;
int ret;
pdev = kzalloc_obj(*pdev);
if (!pdev)
return NULL;
device_initialize(&pdev->dev);
pdev->class = PCI_BASE_CLASS_DISPLAY << 16;
pdev->dev.release = release_dev;
dev_set_name(&pdev->dev, "mock");
dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
#if IS_ENABLED(CONFIG_IOMMU_API) && IS_ENABLED(CONFIG_INTEL_IOMMU)
/* HACK to disable iommu for the fake device; force identity mapping */
pdev->dev.iommu = &fake_iommu;
#endif
if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
put_device(&pdev->dev);
return NULL;
}
i915 = devm_drm_dev_alloc(&pdev->dev, &mock_driver,
struct drm_i915_private, drm);
if (IS_ERR(i915)) {
pr_err("Failed to allocate mock GEM device: err=%ld\n", PTR_ERR(i915));
devres_release_group(&pdev->dev, NULL);
put_device(&pdev->dev);
return NULL;
}
pci_set_drvdata(pdev, &i915->drm);
/* Device parameters start as a copy of module parameters. */
i915_params_copy(&i915->params, &i915_modparams);
/* Set up device info and initial runtime info. */
intel_device_info_driver_create(i915, pdev->device, &mock_info);
/* FIXME: Can we run selftests using a mock device without display? */
display = intel_display_device_probe(pdev, i915_driver_parent_interface());
if (IS_ERR(display))
goto err_device;
i915->display = display;
dev_pm_domain_set(&pdev->dev, &pm_domain);
pm_runtime_enable(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
if (pm_runtime_enabled(&pdev->dev))
WARN_ON(pm_runtime_get_sync(&pdev->dev));
intel_runtime_pm_init_early(&i915->runtime_pm);
/* wakeref tracking has significant overhead */
i915->runtime_pm.no_wakeref_tracking = true;
/* Using the global GTT may ask questions about KMS users, so prepare */
drm_mode_config_init(&i915->drm);
intel_memory_regions_hw_probe(i915);
spin_lock_init(&i915->gpu_error.lock);
i915_gem_init__mm(i915);
intel_root_gt_init_early(i915);
mock_uncore_init(&i915->uncore, i915);
atomic_inc(&to_gt(i915)->wakeref.count); /* disable; no hw support */
to_gt(i915)->awake = INTEL_WAKEREF_MOCK_GT;
mock_gt_probe(i915);
ret = intel_region_ttm_device_init(i915);
if (ret)
goto err_ttm;
i915->wq = alloc_ordered_workqueue("mock", 0);
if (!i915->wq)
goto err_drv;
i915->unordered_wq = alloc_workqueue("mock-unordered", WQ_PERCPU, 0);
if (!i915->unordered_wq)
goto err_wq;
mock_init_contexts(i915);
/* allocate the ggtt */
ret = intel_gt_assign_ggtt(to_gt(i915));
if (ret)
goto err_unlock;
mock_init_ggtt(to_gt(i915));
to_gt(i915)->vm = i915_vm_get(&to_gt(i915)->ggtt->vm);
to_gt(i915)->info.engine_mask = BIT(0);
to_gt(i915)->engine[RCS0] = mock_engine(i915, "mock", RCS0);
if (!to_gt(i915)->engine[RCS0])
goto err_unlock;
if (mock_engine_init(to_gt(i915)->engine[RCS0]))
goto err_context;
__clear_bit(I915_WEDGED, &to_gt(i915)->reset.flags);
intel_engines_driver_register(i915);
i915->do_release = true;
ida_init(&i915->selftest.mock_region_instances);
return i915;
err_context:
intel_gt_driver_remove(to_gt(i915));
err_unlock:
destroy_workqueue(i915->unordered_wq);
err_wq:
destroy_workqueue(i915->wq);
err_drv:
intel_region_ttm_device_fini(i915);
err_ttm:
intel_gt_driver_late_release_all(i915);
intel_memory_regions_driver_release(i915);
drm_mode_config_cleanup(&i915->drm);
err_device:
mock_destroy_device(i915);
return NULL;
}
void mock_destroy_device(struct drm_i915_private *i915)
{
struct device *dev = i915->drm.dev;
intel_display_device_remove(i915->display);
i915->display = NULL;
devres_release_group(dev, NULL);
put_device(dev);
}
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __MOCK_GEM_DEVICE_H__
#define __MOCK_GEM_DEVICE_H__
struct drm_i915_private;
struct drm_i915_private *mock_gem_device(void);
void mock_device_flush(struct drm_i915_private *i915);
void mock_destroy_device(struct drm_i915_private *i915);
#endif /* !__MOCK_GEM_DEVICE_H__ */
@@ -0,0 +1,136 @@
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "mock_gtt.h"
static void mock_insert_page(struct i915_address_space *vm,
dma_addr_t addr,
u64 offset,
unsigned int pat_index,
u32 flags)
{
}
static void mock_insert_entries(struct i915_address_space *vm,
struct i915_vma_resource *vma_res,
unsigned int pat_index, u32 flags)
{
}
static void mock_bind_ppgtt(struct i915_address_space *vm,
struct i915_vm_pt_stash *stash,
struct i915_vma_resource *vma_res,
unsigned int pat_index,
u32 flags)
{
GEM_BUG_ON(flags & I915_VMA_GLOBAL_BIND);
vma_res->bound_flags |= flags;
}
static void mock_unbind_ppgtt(struct i915_address_space *vm,
struct i915_vma_resource *vma_res)
{
}
static void mock_cleanup(struct i915_address_space *vm)
{
}
static void mock_clear_range(struct i915_address_space *vm,
u64 start, u64 length)
{
}
struct i915_ppgtt *mock_ppgtt(struct drm_i915_private *i915, const char *name)
{
struct i915_ppgtt *ppgtt;
ppgtt = kzalloc_obj(*ppgtt);
if (!ppgtt)
return NULL;
ppgtt->vm.gt = to_gt(i915);
ppgtt->vm.i915 = i915;
ppgtt->vm.total = round_down(U64_MAX, PAGE_SIZE);
ppgtt->vm.dma = i915->drm.dev;
i915_address_space_init(&ppgtt->vm, VM_CLASS_PPGTT);
ppgtt->vm.alloc_pt_dma = alloc_pt_dma;
ppgtt->vm.alloc_scratch_dma = alloc_pt_dma;
ppgtt->vm.clear_range = mock_clear_range;
ppgtt->vm.insert_page = mock_insert_page;
ppgtt->vm.insert_entries = mock_insert_entries;
ppgtt->vm.cleanup = mock_cleanup;
ppgtt->vm.vma_ops.bind_vma = mock_bind_ppgtt;
ppgtt->vm.vma_ops.unbind_vma = mock_unbind_ppgtt;
return ppgtt;
}
static void mock_bind_ggtt(struct i915_address_space *vm,
struct i915_vm_pt_stash *stash,
struct i915_vma_resource *vma_res,
unsigned int pat_index,
u32 flags)
{
}
static void mock_unbind_ggtt(struct i915_address_space *vm,
struct i915_vma_resource *vma_res)
{
}
void mock_init_ggtt(struct intel_gt *gt)
{
struct i915_ggtt *ggtt = gt->ggtt;
ggtt->vm.gt = gt;
ggtt->vm.i915 = gt->i915;
ggtt->vm.is_ggtt = true;
ggtt->gmadr = DEFINE_RES_MEM(0, 2048 * PAGE_SIZE);
ggtt->mappable_end = resource_size(&ggtt->gmadr);
ggtt->vm.total = 4096 * PAGE_SIZE;
ggtt->vm.alloc_pt_dma = alloc_pt_dma;
ggtt->vm.alloc_scratch_dma = alloc_pt_dma;
ggtt->vm.clear_range = mock_clear_range;
ggtt->vm.insert_page = mock_insert_page;
ggtt->vm.insert_entries = mock_insert_entries;
ggtt->vm.cleanup = mock_cleanup;
ggtt->vm.vma_ops.bind_vma = mock_bind_ggtt;
ggtt->vm.vma_ops.unbind_vma = mock_unbind_ggtt;
i915_address_space_init(&ggtt->vm, VM_CLASS_GGTT);
}
void mock_fini_ggtt(struct i915_ggtt *ggtt)
{
i915_address_space_fini(&ggtt->vm);
}
@@ -0,0 +1,37 @@
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#ifndef __MOCK_GTT_H
#define __MOCK_GTT_H
struct drm_i915_private;
struct i915_ggtt;
struct intel_gt;
void mock_init_ggtt(struct intel_gt *gt);
void mock_fini_ggtt(struct i915_ggtt *ggtt);
struct i915_ppgtt *mock_ppgtt(struct drm_i915_private *i915, const char *name);
#endif /* !__MOCK_GTT_H */
@@ -0,0 +1,124 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2019-2021 Intel Corporation
*/
#include <drm/ttm/ttm_placement.h>
#include <linux/scatterlist.h>
#include "gem/i915_gem_region.h"
#include "intel_memory_region.h"
#include "intel_region_ttm.h"
#include "mock_region.h"
static void mock_region_put_pages(struct drm_i915_gem_object *obj,
struct sg_table *pages)
{
i915_refct_sgt_put(obj->mm.rsgt);
obj->mm.rsgt = NULL;
intel_region_ttm_resource_free(obj->mm.region, obj->mm.res);
}
static int mock_region_get_pages(struct drm_i915_gem_object *obj)
{
struct sg_table *pages;
int err;
obj->mm.res = intel_region_ttm_resource_alloc(obj->mm.region,
obj->bo_offset,
obj->base.size,
obj->flags);
if (IS_ERR(obj->mm.res))
return PTR_ERR(obj->mm.res);
obj->mm.rsgt = intel_region_ttm_resource_to_rsgt(obj->mm.region,
obj->mm.res,
obj->mm.region->min_page_size);
if (IS_ERR(obj->mm.rsgt)) {
err = PTR_ERR(obj->mm.rsgt);
goto err_free_resource;
}
pages = &obj->mm.rsgt->table;
__i915_gem_object_set_pages(obj, pages);
return 0;
err_free_resource:
intel_region_ttm_resource_free(obj->mm.region, obj->mm.res);
return err;
}
static const struct drm_i915_gem_object_ops mock_region_obj_ops = {
.name = "mock-region",
.get_pages = mock_region_get_pages,
.put_pages = mock_region_put_pages,
.release = i915_gem_object_release_memory_region,
};
static int mock_object_init(struct intel_memory_region *mem,
struct drm_i915_gem_object *obj,
resource_size_t offset,
resource_size_t size,
resource_size_t page_size,
unsigned int flags)
{
static struct lock_class_key lock_class;
struct drm_i915_private *i915 = mem->i915;
if (size > resource_size(&mem->region))
return -E2BIG;
drm_gem_private_object_init(&i915->drm, &obj->base, size);
i915_gem_object_init(obj, &mock_region_obj_ops, &lock_class, flags);
obj->bo_offset = offset;
obj->read_domains = I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT;
i915_gem_object_set_cache_coherency(obj, I915_CACHE_NONE);
i915_gem_object_init_memory_region(obj, mem);
return 0;
}
static int mock_region_fini(struct intel_memory_region *mem)
{
struct drm_i915_private *i915 = mem->i915;
int instance = mem->instance;
int ret;
ret = intel_region_ttm_fini(mem);
ida_free(&i915->selftest.mock_region_instances, instance);
return ret;
}
static const struct intel_memory_region_ops mock_region_ops = {
.init = intel_region_ttm_init,
.release = mock_region_fini,
.init_object = mock_object_init,
};
struct intel_memory_region *
mock_region_create(struct drm_i915_private *i915,
resource_size_t start,
resource_size_t size,
resource_size_t min_page_size,
resource_size_t io_start,
resource_size_t io_size)
{
int instance = ida_alloc_max(&i915->selftest.mock_region_instances,
TTM_NUM_MEM_TYPES - TTM_PL_PRIV - 1,
GFP_KERNEL);
if (instance < 0)
return ERR_PTR(instance);
return intel_memory_region_create(i915, start, size, min_page_size,
io_start, io_size,
INTEL_MEMORY_MOCK, instance,
&mock_region_ops);
}
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2019 Intel Corporation
*/
#ifndef __MOCK_REGION_H
#define __MOCK_REGION_H
#include <linux/types.h>
struct drm_i915_private;
struct intel_memory_region;
struct intel_memory_region *
mock_region_create(struct drm_i915_private *i915,
resource_size_t start,
resource_size_t size,
resource_size_t min_page_size,
resource_size_t io_start,
resource_size_t io_size);
#endif /* !__MOCK_REGION_H */
@@ -0,0 +1,59 @@
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "gem/selftests/igt_gem_utils.h"
#include "gt/mock_engine.h"
#include "mock_request.h"
struct i915_request *
mock_request(struct intel_context *ce, unsigned long delay)
{
struct i915_request *request;
/* NB the i915->requests slab cache is enlarged to fit mock_request */
request = intel_context_create_request(ce);
if (IS_ERR(request))
return request;
request->mock.delay = delay;
return request;
}
bool mock_cancel_request(struct i915_request *request)
{
struct mock_engine *engine =
container_of(request->engine, typeof(*engine), base);
bool was_queued;
spin_lock_irq(&engine->hw_lock);
was_queued = !list_empty(&request->mock.link);
list_del_init(&request->mock.link);
spin_unlock_irq(&engine->hw_lock);
if (was_queued)
i915_request_unsubmit(request);
return was_queued;
}
@@ -0,0 +1,37 @@
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#ifndef __MOCK_REQUEST__
#define __MOCK_REQUEST__
#include <linux/list.h>
#include "../i915_request.h"
struct i915_request *
mock_request(struct intel_context *ce, unsigned long delay);
bool mock_cancel_request(struct i915_request *request);
#endif /* !__MOCK_REQUEST__ */
@@ -0,0 +1,49 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "mock_uncore.h"
#define __nop_write(x) \
static void \
nop_write##x(struct intel_uncore *uncore, i915_reg_t reg, u##x val, bool trace) { }
__nop_write(8)
__nop_write(16)
__nop_write(32)
#define __nop_read(x) \
static u##x \
nop_read##x(struct intel_uncore *uncore, i915_reg_t reg, bool trace) { return 0; }
__nop_read(8)
__nop_read(16)
__nop_read(32)
__nop_read(64)
void mock_uncore_init(struct intel_uncore *uncore,
struct drm_i915_private *i915)
{
intel_uncore_init_early(uncore, to_gt(i915));
ASSIGN_RAW_WRITE_MMIO_VFUNCS(uncore, nop);
ASSIGN_RAW_READ_MMIO_VFUNCS(uncore, nop);
}
@@ -0,0 +1,34 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#ifndef __MOCK_UNCORE_H
#define __MOCK_UNCORE_H
struct drm_i915_private;
struct intel_uncore;
void mock_uncore_init(struct intel_uncore *uncore,
struct drm_i915_private *i915);
#endif /* !__MOCK_UNCORE_H */
@@ -0,0 +1,384 @@
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <linux/prime_numbers.h>
#include <linux/prandom.h>
#include "i915_selftest.h"
#include "i915_utils.h"
#define PFN_BIAS (1 << 10)
struct pfn_table {
struct sg_table st;
unsigned long start, end;
};
typedef unsigned int (*npages_fn_t)(unsigned long n,
unsigned long count,
struct rnd_state *rnd);
static noinline int expect_pfn_sg(struct pfn_table *pt,
npages_fn_t npages_fn,
struct rnd_state *rnd,
const char *who,
unsigned long timeout)
{
struct scatterlist *sg;
unsigned long pfn, n;
pfn = pt->start;
for_each_sg(pt->st.sgl, sg, pt->st.nents, n) {
struct page *page = sg_page(sg);
unsigned int npages = npages_fn(n, pt->st.nents, rnd);
if (page_to_pfn(page) != pfn) {
pr_err("%s: %s left pages out of order, expected pfn %lu, found pfn %lu (using for_each_sg)\n",
__func__, who, pfn, page_to_pfn(page));
return -EINVAL;
}
if (sg->length != npages * PAGE_SIZE) {
pr_err("%s: %s copied wrong sg length, expected size %lu, found %u (using for_each_sg)\n",
__func__, who, npages * PAGE_SIZE, sg->length);
return -EINVAL;
}
if (igt_timeout(timeout, "%s timed out\n", who))
return -EINTR;
pfn += npages;
}
if (pfn != pt->end) {
pr_err("%s: %s finished on wrong pfn, expected %lu, found %lu\n",
__func__, who, pt->end, pfn);
return -EINVAL;
}
return 0;
}
static noinline int expect_pfn_sg_page_iter(struct pfn_table *pt,
const char *who,
unsigned long timeout)
{
struct sg_page_iter sgiter;
unsigned long pfn;
pfn = pt->start;
for_each_sg_page(pt->st.sgl, &sgiter, pt->st.nents, 0) {
struct page *page = sg_page_iter_page(&sgiter);
if (page != pfn_to_page(pfn)) {
pr_err("%s: %s left pages out of order, expected pfn %lu, found pfn %lu (using for_each_sg_page)\n",
__func__, who, pfn, page_to_pfn(page));
return -EINVAL;
}
if (igt_timeout(timeout, "%s timed out\n", who))
return -EINTR;
pfn++;
}
if (pfn != pt->end) {
pr_err("%s: %s finished on wrong pfn, expected %lu, found %lu\n",
__func__, who, pt->end, pfn);
return -EINVAL;
}
return 0;
}
static noinline int expect_pfn_sgtiter(struct pfn_table *pt,
const char *who,
unsigned long timeout)
{
struct sgt_iter sgt;
struct page *page;
unsigned long pfn;
pfn = pt->start;
for_each_sgt_page(page, sgt, &pt->st) {
if (page != pfn_to_page(pfn)) {
pr_err("%s: %s left pages out of order, expected pfn %lu, found pfn %lu (using for_each_sgt_page)\n",
__func__, who, pfn, page_to_pfn(page));
return -EINVAL;
}
if (igt_timeout(timeout, "%s timed out\n", who))
return -EINTR;
pfn++;
}
if (pfn != pt->end) {
pr_err("%s: %s finished on wrong pfn, expected %lu, found %lu\n",
__func__, who, pt->end, pfn);
return -EINVAL;
}
return 0;
}
static int expect_pfn_sgtable(struct pfn_table *pt,
npages_fn_t npages_fn,
struct rnd_state *rnd,
const char *who,
unsigned long timeout)
{
int err;
err = expect_pfn_sg(pt, npages_fn, rnd, who, timeout);
if (err)
return err;
err = expect_pfn_sg_page_iter(pt, who, timeout);
if (err)
return err;
err = expect_pfn_sgtiter(pt, who, timeout);
if (err)
return err;
return 0;
}
static unsigned int one(unsigned long n,
unsigned long count,
struct rnd_state *rnd)
{
return 1;
}
static unsigned int grow(unsigned long n,
unsigned long count,
struct rnd_state *rnd)
{
return n + 1;
}
static unsigned int shrink(unsigned long n,
unsigned long count,
struct rnd_state *rnd)
{
return count - n;
}
static unsigned int random(unsigned long n,
unsigned long count,
struct rnd_state *rnd)
{
return 1 + (prandom_u32_state(rnd) % 1024);
}
static unsigned int random_page_size_pages(unsigned long n,
unsigned long count,
struct rnd_state *rnd)
{
/* 4K, 64K, 2M */
static unsigned int page_count[] = {
BIT(12) >> PAGE_SHIFT,
BIT(16) >> PAGE_SHIFT,
BIT(21) >> PAGE_SHIFT,
};
return page_count[(prandom_u32_state(rnd) % 3)];
}
static inline bool page_contiguous(struct page *first,
struct page *last,
unsigned long npages)
{
return first + npages == last;
}
static int alloc_table(struct pfn_table *pt,
unsigned long count, unsigned long max,
npages_fn_t npages_fn,
struct rnd_state *rnd,
int alloc_error)
{
struct scatterlist *sg;
unsigned long n, pfn;
/* restricted by sg_alloc_table */
if (overflows_type(max, unsigned int))
return -E2BIG;
if (sg_alloc_table(&pt->st, max,
GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN))
return alloc_error;
/* count should be less than 20 to prevent overflowing sg->length */
GEM_BUG_ON(overflows_type(count * PAGE_SIZE, sg->length));
/* Construct a table where each scatterlist contains different number
* of entries. The idea is to check that we can iterate the individual
* pages from inside the coalesced lists.
*/
pt->start = PFN_BIAS;
pfn = pt->start;
sg = pt->st.sgl;
for (n = 0; n < count; n++) {
unsigned long npages = npages_fn(n, count, rnd);
/* Nobody expects the Sparse Memmap! */
if (!page_contiguous(pfn_to_page(pfn),
pfn_to_page(pfn + npages),
npages)) {
sg_free_table(&pt->st);
return -ENOSPC;
}
if (n)
sg = sg_next(sg);
sg_set_page(sg, pfn_to_page(pfn), npages * PAGE_SIZE, 0);
GEM_BUG_ON(page_to_pfn(sg_page(sg)) != pfn);
GEM_BUG_ON(sg->length != npages * PAGE_SIZE);
GEM_BUG_ON(sg->offset != 0);
pfn += npages;
}
sg_mark_end(sg);
pt->st.nents = n;
pt->end = pfn;
return 0;
}
static const npages_fn_t npages_funcs[] = {
one,
grow,
shrink,
random,
random_page_size_pages,
NULL,
};
static int igt_sg_alloc(void *ignored)
{
IGT_TIMEOUT(end_time);
const unsigned long max_order = 20; /* approximating a 4GiB object */
struct rnd_state prng;
unsigned long prime;
int alloc_error = -ENOMEM;
for_each_prime_number(prime, max_order) {
unsigned long size = BIT(prime);
int offset;
for (offset = -1; offset <= 1; offset++) {
unsigned long sz = size + offset;
const npages_fn_t *npages;
struct pfn_table pt;
int err;
for (npages = npages_funcs; *npages; npages++) {
prandom_seed_state(&prng,
i915_selftest.random_seed);
err = alloc_table(&pt, sz, sz, *npages, &prng,
alloc_error);
if (err == -ENOSPC)
break;
if (err)
return err;
prandom_seed_state(&prng,
i915_selftest.random_seed);
err = expect_pfn_sgtable(&pt, *npages, &prng,
"sg_alloc_table",
end_time);
sg_free_table(&pt.st);
if (err)
return err;
}
}
/* Test at least one continuation before accepting oom */
if (size > SG_MAX_SINGLE_ALLOC)
alloc_error = -ENOSPC;
}
return 0;
}
static int igt_sg_trim(void *ignored)
{
IGT_TIMEOUT(end_time);
const unsigned long max = PAGE_SIZE; /* not prime! */
struct pfn_table pt;
unsigned long prime;
int alloc_error = -ENOMEM;
for_each_prime_number(prime, max) {
const npages_fn_t *npages;
int err;
for (npages = npages_funcs; *npages; npages++) {
struct rnd_state prng;
prandom_seed_state(&prng, i915_selftest.random_seed);
err = alloc_table(&pt, prime, max, *npages, &prng,
alloc_error);
if (err == -ENOSPC)
break;
if (err)
return err;
if (i915_sg_trim(&pt.st)) {
if (pt.st.orig_nents != prime ||
pt.st.nents != prime) {
pr_err("i915_sg_trim failed (nents %u, orig_nents %u), expected %lu\n",
pt.st.nents, pt.st.orig_nents, prime);
err = -EINVAL;
} else {
prandom_seed_state(&prng,
i915_selftest.random_seed);
err = expect_pfn_sgtable(&pt,
*npages, &prng,
"i915_sg_trim",
end_time);
}
}
sg_free_table(&pt.st);
if (err)
return err;
}
/* Test at least one continuation before accepting oom */
if (prime > SG_MAX_SINGLE_ALLOC)
alloc_error = -ENOSPC;
}
return 0;
}
int scatterlist_mock_selftests(void)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_sg_alloc),
SUBTEST(igt_sg_trim),
};
return i915_subtests(tests, NULL);
}