420 lines · c
1// SPDX-License-Identifier: MIT2/*3 * Copyright �� 2021 Intel Corporation4 */5 6#include "gt/intel_gt_print.h"7#include "intel_guc_print.h"8#include "selftests/igt_spinner.h"9#include "selftests/intel_scheduler_helpers.h"10 11static int request_add_spin(struct i915_request *rq, struct igt_spinner *spin)12{13 int err = 0;14 15 i915_request_get(rq);16 i915_request_add(rq);17 if (spin && !igt_wait_for_spinner(spin, rq))18 err = -ETIMEDOUT;19 20 return err;21}22 23static struct i915_request *nop_user_request(struct intel_context *ce,24 struct i915_request *from)25{26 struct i915_request *rq;27 int ret;28 29 rq = intel_context_create_request(ce);30 if (IS_ERR(rq))31 return rq;32 33 if (from) {34 ret = i915_sw_fence_await_dma_fence(&rq->submit,35 &from->fence, 0,36 I915_FENCE_GFP);37 if (ret < 0) {38 i915_request_put(rq);39 return ERR_PTR(ret);40 }41 }42 43 i915_request_get(rq);44 i915_request_add(rq);45 46 return rq;47}48 49static int intel_guc_scrub_ctbs(void *arg)50{51 struct intel_gt *gt = arg;52 int ret = 0;53 int i;54 struct i915_request *last[3] = {NULL, NULL, NULL}, *rq;55 intel_wakeref_t wakeref;56 struct intel_engine_cs *engine;57 struct intel_context *ce;58 59 if (!intel_has_gpu_reset(gt))60 return 0;61 62 wakeref = intel_runtime_pm_get(gt->uncore->rpm);63 engine = intel_selftest_find_any_engine(gt);64 65 /* Submit requests and inject errors forcing G2H to be dropped */66 for (i = 0; i < 3; ++i) {67 ce = intel_context_create(engine);68 if (IS_ERR(ce)) {69 ret = PTR_ERR(ce);70 gt_err(gt, "Failed to create context %d: %pe\n", i, ce);71 goto err;72 }73 74 switch (i) {75 case 0:76 ce->drop_schedule_enable = true;77 break;78 case 1:79 ce->drop_schedule_disable = true;80 break;81 case 2:82 ce->drop_deregister = true;83 break;84 }85 86 rq = nop_user_request(ce, NULL);87 intel_context_put(ce);88 89 if (IS_ERR(rq)) {90 ret = PTR_ERR(rq);91 gt_err(gt, "Failed to create request %d: %pe\n", i, rq);92 goto err;93 }94 95 last[i] = rq;96 }97 98 for (i = 0; i < 3; ++i) {99 ret = i915_request_wait(last[i], 0, HZ);100 if (ret < 0) {101 gt_err(gt, "Last request failed to complete: %pe\n", ERR_PTR(ret));102 goto err;103 }104 i915_request_put(last[i]);105 last[i] = NULL;106 }107 108 /* Force all H2G / G2H to be submitted / processed */109 intel_gt_retire_requests(gt);110 msleep(500);111 112 /* Scrub missing G2H */113 intel_gt_handle_error(engine->gt, -1, 0, "selftest reset");114 115 /* GT will not idle if G2H are lost */116 ret = intel_gt_wait_for_idle(gt, HZ);117 if (ret < 0) {118 gt_err(gt, "GT failed to idle: %pe\n", ERR_PTR(ret));119 goto err;120 }121 122err:123 for (i = 0; i < 3; ++i)124 if (last[i])125 i915_request_put(last[i]);126 intel_runtime_pm_put(gt->uncore->rpm, wakeref);127 128 return ret;129}130 131/*132 * intel_guc_steal_guc_ids - Test to exhaust all guc_ids and then steal one133 *134 * This test creates a spinner which is used to block all subsequent submissions135 * until it completes. Next, a loop creates a context and a NOP request each136 * iteration until the guc_ids are exhausted (request creation returns -EAGAIN).137 * The spinner is ended, unblocking all requests created in the loop. At this138 * point all guc_ids are exhausted but are available to steal. Try to create139 * another request which should successfully steal a guc_id. Wait on last140 * request to complete, idle GPU, verify a guc_id was stolen via a counter, and141 * exit the test. Test also artificially reduces the number of guc_ids so the142 * test runs in a timely manner.143 */144static int intel_guc_steal_guc_ids(void *arg)145{146 struct intel_gt *gt = arg;147 struct intel_guc *guc = gt_to_guc(gt);148 int ret, sv, context_index = 0;149 intel_wakeref_t wakeref;150 struct intel_engine_cs *engine;151 struct intel_context **ce;152 struct igt_spinner spin;153 struct i915_request *spin_rq = NULL, *rq, *last = NULL;154 int number_guc_id_stolen = guc->number_guc_id_stolen;155 156 ce = kcalloc(GUC_MAX_CONTEXT_ID, sizeof(*ce), GFP_KERNEL);157 if (!ce) {158 guc_err(guc, "Context array allocation failed\n");159 return -ENOMEM;160 }161 162 wakeref = intel_runtime_pm_get(gt->uncore->rpm);163 engine = intel_selftest_find_any_engine(gt);164 sv = guc->submission_state.num_guc_ids;165 guc->submission_state.num_guc_ids = 512;166 167 /* Create spinner to block requests in below loop */168 ce[context_index] = intel_context_create(engine);169 if (IS_ERR(ce[context_index])) {170 ret = PTR_ERR(ce[context_index]);171 guc_err(guc, "Failed to create context: %pe\n", ce[context_index]);172 ce[context_index] = NULL;173 goto err_wakeref;174 }175 ret = igt_spinner_init(&spin, engine->gt);176 if (ret) {177 guc_err(guc, "Failed to create spinner: %pe\n", ERR_PTR(ret));178 goto err_contexts;179 }180 spin_rq = igt_spinner_create_request(&spin, ce[context_index],181 MI_ARB_CHECK);182 if (IS_ERR(spin_rq)) {183 ret = PTR_ERR(spin_rq);184 guc_err(guc, "Failed to create spinner request: %pe\n", spin_rq);185 goto err_contexts;186 }187 ret = request_add_spin(spin_rq, &spin);188 if (ret) {189 guc_err(guc, "Failed to add Spinner request: %pe\n", ERR_PTR(ret));190 goto err_spin_rq;191 }192 193 /* Use all guc_ids */194 while (ret != -EAGAIN) {195 ce[++context_index] = intel_context_create(engine);196 if (IS_ERR(ce[context_index])) {197 ret = PTR_ERR(ce[context_index]);198 guc_err(guc, "Failed to create context: %pe\n", ce[context_index]);199 ce[context_index--] = NULL;200 goto err_spin_rq;201 }202 203 rq = nop_user_request(ce[context_index], spin_rq);204 if (IS_ERR(rq)) {205 ret = PTR_ERR(rq);206 rq = NULL;207 if ((ret != -EAGAIN) || !last) {208 guc_err(guc, "Failed to create %srequest %d: %pe\n",209 last ? "" : "first ", context_index, ERR_PTR(ret));210 goto err_spin_rq;211 }212 } else {213 if (last)214 i915_request_put(last);215 last = rq;216 }217 }218 219 /* Release blocked requests */220 igt_spinner_end(&spin);221 ret = intel_selftest_wait_for_rq(spin_rq);222 if (ret) {223 guc_err(guc, "Spin request failed to complete: %pe\n", ERR_PTR(ret));224 i915_request_put(last);225 goto err_spin_rq;226 }227 i915_request_put(spin_rq);228 igt_spinner_fini(&spin);229 spin_rq = NULL;230 231 /* Wait for last request */232 ret = i915_request_wait(last, 0, HZ * 30);233 i915_request_put(last);234 if (ret < 0) {235 guc_err(guc, "Last request failed to complete: %pe\n", ERR_PTR(ret));236 goto err_spin_rq;237 }238 239 /* Try to steal guc_id */240 rq = nop_user_request(ce[context_index], NULL);241 if (IS_ERR(rq)) {242 ret = PTR_ERR(rq);243 guc_err(guc, "Failed to steal guc_id %d: %pe\n", context_index, rq);244 goto err_spin_rq;245 }246 247 /* Wait for request with stolen guc_id */248 ret = i915_request_wait(rq, 0, HZ);249 i915_request_put(rq);250 if (ret < 0) {251 guc_err(guc, "Request with stolen guc_id failed to complete: %pe\n", ERR_PTR(ret));252 goto err_spin_rq;253 }254 255 /* Wait for idle */256 ret = intel_gt_wait_for_idle(gt, HZ * 30);257 if (ret < 0) {258 guc_err(guc, "GT failed to idle: %pe\n", ERR_PTR(ret));259 goto err_spin_rq;260 }261 262 /* Verify a guc_id was stolen */263 if (guc->number_guc_id_stolen == number_guc_id_stolen) {264 guc_err(guc, "No guc_id was stolen");265 ret = -EINVAL;266 } else {267 ret = 0;268 }269 270err_spin_rq:271 if (spin_rq) {272 igt_spinner_end(&spin);273 intel_selftest_wait_for_rq(spin_rq);274 i915_request_put(spin_rq);275 igt_spinner_fini(&spin);276 intel_gt_wait_for_idle(gt, HZ * 30);277 }278err_contexts:279 for (; context_index >= 0 && ce[context_index]; --context_index)280 intel_context_put(ce[context_index]);281err_wakeref:282 intel_runtime_pm_put(gt->uncore->rpm, wakeref);283 kfree(ce);284 guc->submission_state.num_guc_ids = sv;285 286 return ret;287}288 289/*290 * Send a context schedule H2G message with an invalid context id.291 * This should generate a GUC_RESULT_INVALID_CONTEXT response.292 */293static int bad_h2g(struct intel_guc *guc)294{295 u32 action[] = {296 INTEL_GUC_ACTION_SCHED_CONTEXT,297 0x12345678,298 };299 300 return intel_guc_send_nb(guc, action, ARRAY_SIZE(action), 0);301}302 303/*304 * Set a spinner running to make sure the system is alive and active,305 * then send a bad but asynchronous H2G command and wait to see if an306 * error response is returned. If no response is received or if the307 * spinner dies then the test will fail.308 */309#define FAST_RESPONSE_TIMEOUT_MS 1000310static int intel_guc_fast_request(void *arg)311{312 struct intel_gt *gt = arg;313 struct intel_context *ce;314 struct igt_spinner spin;315 struct i915_request *rq;316 intel_wakeref_t wakeref;317 struct intel_engine_cs *engine = intel_selftest_find_any_engine(gt);318 bool spinning = false;319 int ret = 0;320 321 if (!engine)322 return 0;323 324 wakeref = intel_runtime_pm_get(gt->uncore->rpm);325 326 ce = intel_context_create(engine);327 if (IS_ERR(ce)) {328 ret = PTR_ERR(ce);329 gt_err(gt, "Failed to create spinner request: %pe\n", ce);330 goto err_pm;331 }332 333 ret = igt_spinner_init(&spin, engine->gt);334 if (ret) {335 gt_err(gt, "Failed to create spinner: %pe\n", ERR_PTR(ret));336 goto err_pm;337 }338 spinning = true;339 340 rq = igt_spinner_create_request(&spin, ce, MI_ARB_CHECK);341 intel_context_put(ce);342 if (IS_ERR(rq)) {343 ret = PTR_ERR(rq);344 gt_err(gt, "Failed to create spinner request: %pe\n", rq);345 goto err_spin;346 }347 348 ret = request_add_spin(rq, &spin);349 if (ret) {350 gt_err(gt, "Failed to add Spinner request: %pe\n", ERR_PTR(ret));351 goto err_rq;352 }353 354 gt->uc.guc.fast_response_selftest = 1;355 356 ret = bad_h2g(>->uc.guc);357 if (ret) {358 gt_err(gt, "Failed to send H2G: %pe\n", ERR_PTR(ret));359 goto err_rq;360 }361 362 ret = wait_for(gt->uc.guc.fast_response_selftest != 1 || i915_request_completed(rq),363 FAST_RESPONSE_TIMEOUT_MS);364 if (ret) {365 gt_err(gt, "Request wait failed: %pe\n", ERR_PTR(ret));366 goto err_rq;367 }368 369 if (i915_request_completed(rq)) {370 gt_err(gt, "Spinner died waiting for fast request error!\n");371 ret = -EIO;372 goto err_rq;373 }374 375 if (gt->uc.guc.fast_response_selftest != 2) {376 gt_err(gt, "Unexpected fast response count: %d\n",377 gt->uc.guc.fast_response_selftest);378 goto err_rq;379 }380 381 igt_spinner_end(&spin);382 spinning = false;383 384 ret = intel_selftest_wait_for_rq(rq);385 if (ret) {386 gt_err(gt, "Request failed to complete: %pe\n", ERR_PTR(ret));387 goto err_rq;388 }389 390err_rq:391 i915_request_put(rq);392 393err_spin:394 if (spinning)395 igt_spinner_end(&spin);396 igt_spinner_fini(&spin);397 398err_pm:399 intel_runtime_pm_put(gt->uncore->rpm, wakeref);400 return ret;401}402 403int intel_guc_live_selftests(struct drm_i915_private *i915)404{405 static const struct i915_subtest tests[] = {406 SUBTEST(intel_guc_scrub_ctbs),407 SUBTEST(intel_guc_steal_guc_ids),408 SUBTEST(intel_guc_fast_request),409 };410 struct intel_gt *gt = to_gt(i915);411 412 if (intel_gt_is_wedged(gt))413 return 0;414 415 if (!intel_uc_uses_guc_submission(>->uc))416 return 0;417 418 return intel_gt_live_subtests(tests, gt);419}420