| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- // SPDX-License-Identifier: MIT
- /*
- * Copyright © 2018 Intel Corporation
- */
- #include <linux/crc32.h>
- #include "gem/i915_gem_stolen.h"
- #include "i915_memcpy.h"
- #include "i915_selftest.h"
- #include "intel_gpu_commands.h"
- #include "selftests/igt_reset.h"
- #include "selftests/igt_atomic.h"
- #include "selftests/igt_spinner.h"
- static int
- __igt_reset_stolen(struct intel_gt *gt,
- intel_engine_mask_t mask,
- const char *msg)
- {
- struct i915_ggtt *ggtt = gt->ggtt;
- const struct resource *dsm = >->i915->dsm.stolen;
- resource_size_t num_pages, page;
- struct intel_engine_cs *engine;
- intel_wakeref_t wakeref;
- enum intel_engine_id id;
- struct igt_spinner spin;
- long max, count;
- void *tmp;
- u32 *crc;
- int err;
- if (!drm_mm_node_allocated(&ggtt->error_capture))
- return 0;
- num_pages = resource_size(dsm) >> PAGE_SHIFT;
- if (!num_pages)
- return 0;
- crc = kmalloc_array(num_pages, sizeof(u32), GFP_KERNEL);
- if (!crc)
- return -ENOMEM;
- tmp = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!tmp) {
- err = -ENOMEM;
- goto err_crc;
- }
- igt_global_reset_lock(gt);
- wakeref = intel_runtime_pm_get(gt->uncore->rpm);
- err = igt_spinner_init(&spin, gt);
- if (err)
- goto err_lock;
- for_each_engine(engine, gt, id) {
- struct intel_context *ce;
- struct i915_request *rq;
- if (!(mask & engine->mask))
- continue;
- if (!intel_engine_can_store_dword(engine))
- continue;
- ce = intel_context_create(engine);
- if (IS_ERR(ce)) {
- err = PTR_ERR(ce);
- goto err_spin;
- }
- rq = igt_spinner_create_request(&spin, ce, MI_ARB_CHECK);
- intel_context_put(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto err_spin;
- }
- i915_request_add(rq);
- }
- for (page = 0; page < num_pages; page++) {
- dma_addr_t dma = (dma_addr_t)dsm->start + (page << PAGE_SHIFT);
- void __iomem *s;
- void *in;
- ggtt->vm.insert_page(&ggtt->vm, dma,
- ggtt->error_capture.start,
- i915_gem_get_pat_index(gt->i915,
- I915_CACHE_NONE),
- 0);
- mb();
- s = io_mapping_map_wc(&ggtt->iomap,
- ggtt->error_capture.start,
- PAGE_SIZE);
- if (!__drm_mm_interval_first(>->i915->mm.stolen,
- page << PAGE_SHIFT,
- ((page + 1) << PAGE_SHIFT) - 1))
- memset_io(s, STACK_MAGIC, PAGE_SIZE);
- in = (void __force *)s;
- if (i915_memcpy_from_wc(tmp, in, PAGE_SIZE))
- in = tmp;
- crc[page] = crc32_le(0, in, PAGE_SIZE);
- io_mapping_unmap(s);
- }
- mb();
- ggtt->vm.clear_range(&ggtt->vm, ggtt->error_capture.start, PAGE_SIZE);
- if (mask == ALL_ENGINES) {
- intel_gt_reset(gt, mask, NULL);
- } else {
- for_each_engine(engine, gt, id) {
- if (mask & engine->mask)
- intel_engine_reset(engine, NULL);
- }
- }
- max = -1;
- count = 0;
- for (page = 0; page < num_pages; page++) {
- dma_addr_t dma = (dma_addr_t)dsm->start + (page << PAGE_SHIFT);
- void __iomem *s;
- void *in;
- u32 x;
- ggtt->vm.insert_page(&ggtt->vm, dma,
- ggtt->error_capture.start,
- i915_gem_get_pat_index(gt->i915,
- I915_CACHE_NONE),
- 0);
- mb();
- s = io_mapping_map_wc(&ggtt->iomap,
- ggtt->error_capture.start,
- PAGE_SIZE);
- in = (void __force *)s;
- if (i915_memcpy_from_wc(tmp, in, PAGE_SIZE))
- in = tmp;
- x = crc32_le(0, in, PAGE_SIZE);
- if (x != crc[page] &&
- !__drm_mm_interval_first(>->i915->mm.stolen,
- page << PAGE_SHIFT,
- ((page + 1) << PAGE_SHIFT) - 1)) {
- pr_debug("unused stolen page %pa modified by GPU reset\n",
- &page);
- if (count++ == 0)
- igt_hexdump(in, PAGE_SIZE);
- max = page;
- }
- io_mapping_unmap(s);
- }
- mb();
- ggtt->vm.clear_range(&ggtt->vm, ggtt->error_capture.start, PAGE_SIZE);
- if (count > 0) {
- pr_info("%s reset clobbered %ld pages of stolen, last clobber at page %ld\n",
- msg, count, max);
- }
- if (max >= I915_GEM_STOLEN_BIAS >> PAGE_SHIFT) {
- pr_err("%s reset clobbered unreserved area [above %x] of stolen; may cause severe faults\n",
- msg, I915_GEM_STOLEN_BIAS);
- err = -EINVAL;
- }
- err_spin:
- igt_spinner_fini(&spin);
- err_lock:
- intel_runtime_pm_put(gt->uncore->rpm, wakeref);
- igt_global_reset_unlock(gt);
- kfree(tmp);
- err_crc:
- kfree(crc);
- return err;
- }
- static int igt_reset_device_stolen(void *arg)
- {
- return __igt_reset_stolen(arg, ALL_ENGINES, "device");
- }
- static int igt_reset_engines_stolen(void *arg)
- {
- struct intel_gt *gt = arg;
- struct intel_engine_cs *engine;
- enum intel_engine_id id;
- int err;
- if (!intel_has_reset_engine(gt))
- return 0;
- for_each_engine(engine, gt, id) {
- err = __igt_reset_stolen(gt, engine->mask, engine->name);
- if (err)
- return err;
- }
- return 0;
- }
- static int igt_global_reset(void *arg)
- {
- struct intel_gt *gt = arg;
- unsigned int reset_count;
- intel_wakeref_t wakeref;
- int err = 0;
- /* Check that we can issue a global GPU reset */
- igt_global_reset_lock(gt);
- wakeref = intel_runtime_pm_get(gt->uncore->rpm);
- reset_count = i915_reset_count(>->i915->gpu_error);
- intel_gt_reset(gt, ALL_ENGINES, NULL);
- if (i915_reset_count(>->i915->gpu_error) == reset_count) {
- pr_err("No GPU reset recorded!\n");
- err = -EINVAL;
- }
- intel_runtime_pm_put(gt->uncore->rpm, wakeref);
- igt_global_reset_unlock(gt);
- if (intel_gt_is_wedged(gt))
- err = -EIO;
- return err;
- }
- static int igt_wedged_reset(void *arg)
- {
- struct intel_gt *gt = arg;
- intel_wakeref_t wakeref;
- /* Check that we can recover a wedged device with a GPU reset */
- igt_global_reset_lock(gt);
- wakeref = intel_runtime_pm_get(gt->uncore->rpm);
- intel_gt_set_wedged(gt);
- GEM_BUG_ON(!intel_gt_is_wedged(gt));
- intel_gt_reset(gt, ALL_ENGINES, NULL);
- intel_runtime_pm_put(gt->uncore->rpm, wakeref);
- igt_global_reset_unlock(gt);
- return intel_gt_is_wedged(gt) ? -EIO : 0;
- }
- static int igt_atomic_reset(void *arg)
- {
- struct intel_gt *gt = arg;
- const typeof(*igt_atomic_phases) *p;
- intel_wakeref_t wakeref;
- int err = 0;
- /* Check that the resets are usable from atomic context */
- wakeref = intel_gt_pm_get(gt);
- igt_global_reset_lock(gt);
- /* Flush any requests before we get started and check basics */
- if (!igt_force_reset(gt))
- goto unlock;
- for (p = igt_atomic_phases; p->name; p++) {
- intel_engine_mask_t awake;
- GEM_TRACE("__intel_gt_reset under %s\n", p->name);
- awake = reset_prepare(gt);
- p->critical_section_begin();
- err = intel_gt_reset_all_engines(gt);
- p->critical_section_end();
- reset_finish(gt, awake);
- if (err) {
- pr_err("__intel_gt_reset failed under %s\n", p->name);
- break;
- }
- }
- /* As we poke around the guts, do a full reset before continuing. */
- igt_force_reset(gt);
- unlock:
- igt_global_reset_unlock(gt);
- intel_gt_pm_put(gt, wakeref);
- return err;
- }
- static int igt_atomic_engine_reset(void *arg)
- {
- struct intel_gt *gt = arg;
- const typeof(*igt_atomic_phases) *p;
- struct intel_engine_cs *engine;
- enum intel_engine_id id;
- intel_wakeref_t wakeref;
- int err = 0;
- /* Check that the resets are usable from atomic context */
- if (!intel_has_reset_engine(gt))
- return 0;
- if (intel_uc_uses_guc_submission(>->uc))
- return 0;
- wakeref = intel_gt_pm_get(gt);
- igt_global_reset_lock(gt);
- /* Flush any requests before we get started and check basics */
- if (!igt_force_reset(gt))
- goto out_unlock;
- for_each_engine(engine, gt, id) {
- struct tasklet_struct *t = &engine->sched_engine->tasklet;
- if (t->func)
- tasklet_disable(t);
- intel_engine_pm_get(engine);
- for (p = igt_atomic_phases; p->name; p++) {
- GEM_TRACE("intel_engine_reset(%s) under %s\n",
- engine->name, p->name);
- if (strcmp(p->name, "softirq"))
- local_bh_disable();
- p->critical_section_begin();
- err = __intel_engine_reset_bh(engine, NULL);
- p->critical_section_end();
- if (strcmp(p->name, "softirq"))
- local_bh_enable();
- if (err) {
- pr_err("intel_engine_reset(%s) failed under %s\n",
- engine->name, p->name);
- break;
- }
- }
- intel_engine_pm_put(engine);
- if (t->func) {
- tasklet_enable(t);
- tasklet_hi_schedule(t);
- }
- if (err)
- break;
- }
- /* As we poke around the guts, do a full reset before continuing. */
- igt_force_reset(gt);
- out_unlock:
- igt_global_reset_unlock(gt);
- intel_gt_pm_put(gt, wakeref);
- return err;
- }
- int intel_reset_live_selftests(struct drm_i915_private *i915)
- {
- static const struct i915_subtest tests[] = {
- SUBTEST(igt_global_reset), /* attempt to recover GPU first */
- SUBTEST(igt_reset_device_stolen),
- SUBTEST(igt_reset_engines_stolen),
- SUBTEST(igt_wedged_reset),
- SUBTEST(igt_atomic_reset),
- SUBTEST(igt_atomic_engine_reset),
- };
- struct intel_gt *gt = to_gt(i915);
- if (!intel_has_gpu_reset(gt))
- return 0;
- if (intel_gt_is_wedged(gt))
- return -EIO; /* we're long past hope of a successful reset */
- return intel_gt_live_subtests(tests, gt);
- }
|