| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * Copyright © 2019 Intel Corporation |
| 4 | */ |
| 5 | |
| 6 | #include <drm/drm_print.h> |
| 7 | |
| 8 | #include "i915_selftest.h" |
| 9 | #include "intel_engine_heartbeat.h" |
| 10 | #include "intel_engine_pm.h" |
| 11 | #include "intel_gt.h" |
| 12 | |
| 13 | #include "gem/selftests/mock_context.h" |
| 14 | #include "selftests/igt_flush_test.h" |
| 15 | #include "selftests/mock_drm.h" |
| 16 | |
| 17 | static int request_sync(struct i915_request *rq) |
| 18 | { |
| 19 | struct intel_timeline *tl = i915_request_timeline(rq); |
| 20 | long timeout; |
| 21 | int err = 0; |
| 22 | |
| 23 | intel_timeline_get(timeline: tl); |
| 24 | i915_request_get(rq); |
| 25 | |
| 26 | /* Opencode i915_request_add() so we can keep the timeline locked. */ |
| 27 | __i915_request_commit(request: rq); |
| 28 | rq->sched.attr.priority = I915_PRIORITY_BARRIER; |
| 29 | __i915_request_queue_bh(rq); |
| 30 | |
| 31 | timeout = i915_request_wait(rq, flags: 0, HZ / 10); |
| 32 | if (timeout < 0) |
| 33 | err = timeout; |
| 34 | else |
| 35 | i915_request_retire_upto(rq); |
| 36 | |
| 37 | lockdep_unpin_lock(&tl->mutex, rq->cookie); |
| 38 | mutex_unlock(lock: &tl->mutex); |
| 39 | |
| 40 | i915_request_put(rq); |
| 41 | intel_timeline_put(timeline: tl); |
| 42 | |
| 43 | return err; |
| 44 | } |
| 45 | |
| 46 | static int context_sync(struct intel_context *ce) |
| 47 | { |
| 48 | struct intel_timeline *tl = ce->timeline; |
| 49 | int err = 0; |
| 50 | |
| 51 | mutex_lock(&tl->mutex); |
| 52 | do { |
| 53 | struct i915_request *rq; |
| 54 | long timeout; |
| 55 | |
| 56 | if (list_empty(head: &tl->requests)) |
| 57 | break; |
| 58 | |
| 59 | rq = list_last_entry(&tl->requests, typeof(*rq), link); |
| 60 | i915_request_get(rq); |
| 61 | |
| 62 | timeout = i915_request_wait(rq, flags: 0, HZ / 10); |
| 63 | if (timeout < 0) |
| 64 | err = timeout; |
| 65 | else |
| 66 | i915_request_retire_upto(rq); |
| 67 | |
| 68 | i915_request_put(rq); |
| 69 | } while (!err); |
| 70 | mutex_unlock(lock: &tl->mutex); |
| 71 | |
| 72 | /* Wait for all barriers to complete (remote CPU) before we check */ |
| 73 | i915_active_unlock_wait(ref: &ce->active); |
| 74 | return err; |
| 75 | } |
| 76 | |
| 77 | static int __live_context_size(struct intel_engine_cs *engine) |
| 78 | { |
| 79 | struct intel_context *ce; |
| 80 | struct i915_request *rq; |
| 81 | void *vaddr; |
| 82 | int err; |
| 83 | |
| 84 | ce = intel_context_create(engine); |
| 85 | if (IS_ERR(ptr: ce)) |
| 86 | return PTR_ERR(ptr: ce); |
| 87 | |
| 88 | err = intel_context_pin(ce); |
| 89 | if (err) |
| 90 | goto err; |
| 91 | |
| 92 | vaddr = i915_gem_object_pin_map_unlocked(obj: ce->state->obj, |
| 93 | type: intel_gt_coherent_map_type(gt: engine->gt, |
| 94 | obj: ce->state->obj, |
| 95 | always_coherent: false)); |
| 96 | if (IS_ERR(ptr: vaddr)) { |
| 97 | err = PTR_ERR(ptr: vaddr); |
| 98 | intel_context_unpin(ce); |
| 99 | goto err; |
| 100 | } |
| 101 | |
| 102 | /* |
| 103 | * Note that execlists also applies a redzone which it checks on |
| 104 | * context unpin when debugging. We are using the same location |
| 105 | * and same poison value so that our checks overlap. Despite the |
| 106 | * redundancy, we want to keep this little selftest so that we |
| 107 | * get coverage of any and all submission backends, and we can |
| 108 | * always extend this test to ensure we trick the HW into a |
| 109 | * compromising position wrt to the various sections that need |
| 110 | * to be written into the context state. |
| 111 | * |
| 112 | * TLDR; this overlaps with the execlists redzone. |
| 113 | */ |
| 114 | vaddr += engine->context_size - I915_GTT_PAGE_SIZE; |
| 115 | memset(vaddr, POISON_INUSE, I915_GTT_PAGE_SIZE); |
| 116 | |
| 117 | rq = intel_context_create_request(ce); |
| 118 | intel_context_unpin(ce); |
| 119 | if (IS_ERR(ptr: rq)) { |
| 120 | err = PTR_ERR(ptr: rq); |
| 121 | goto err_unpin; |
| 122 | } |
| 123 | |
| 124 | err = request_sync(rq); |
| 125 | if (err) |
| 126 | goto err_unpin; |
| 127 | |
| 128 | /* Force the context switch */ |
| 129 | rq = intel_engine_create_kernel_request(engine); |
| 130 | if (IS_ERR(ptr: rq)) { |
| 131 | err = PTR_ERR(ptr: rq); |
| 132 | goto err_unpin; |
| 133 | } |
| 134 | err = request_sync(rq); |
| 135 | if (err) |
| 136 | goto err_unpin; |
| 137 | |
| 138 | if (memchr_inv(p: vaddr, POISON_INUSE, I915_GTT_PAGE_SIZE)) { |
| 139 | pr_err("%s context overwrote trailing red-zone!" , engine->name); |
| 140 | err = -EINVAL; |
| 141 | } |
| 142 | |
| 143 | err_unpin: |
| 144 | i915_gem_object_unpin_map(obj: ce->state->obj); |
| 145 | err: |
| 146 | intel_context_put(ce); |
| 147 | return err; |
| 148 | } |
| 149 | |
| 150 | static int live_context_size(void *arg) |
| 151 | { |
| 152 | struct intel_gt *gt = arg; |
| 153 | struct intel_engine_cs *engine; |
| 154 | enum intel_engine_id id; |
| 155 | int err = 0; |
| 156 | |
| 157 | /* |
| 158 | * Check that our context sizes are correct by seeing if the |
| 159 | * HW tries to write past the end of one. |
| 160 | */ |
| 161 | |
| 162 | for_each_engine(engine, gt, id) { |
| 163 | struct file *saved; |
| 164 | |
| 165 | if (!engine->context_size) |
| 166 | continue; |
| 167 | |
| 168 | intel_engine_pm_get(engine); |
| 169 | |
| 170 | /* |
| 171 | * Hide the old default state -- we lie about the context size |
| 172 | * and get confused when the default state is smaller than |
| 173 | * expected. For our do nothing request, inheriting the |
| 174 | * active state is sufficient, we are only checking that we |
| 175 | * don't use more than we planned. |
| 176 | */ |
| 177 | saved = fetch_and_zero(&engine->default_state); |
| 178 | |
| 179 | /* Overlaps with the execlists redzone */ |
| 180 | engine->context_size += I915_GTT_PAGE_SIZE; |
| 181 | |
| 182 | err = __live_context_size(engine); |
| 183 | |
| 184 | engine->context_size -= I915_GTT_PAGE_SIZE; |
| 185 | |
| 186 | engine->default_state = saved; |
| 187 | |
| 188 | intel_engine_pm_put(engine); |
| 189 | |
| 190 | if (err) |
| 191 | break; |
| 192 | } |
| 193 | |
| 194 | return err; |
| 195 | } |
| 196 | |
| 197 | static int __live_active_context(struct intel_engine_cs *engine) |
| 198 | { |
| 199 | unsigned long saved_heartbeat; |
| 200 | struct intel_context *ce; |
| 201 | int pass; |
| 202 | int err; |
| 203 | |
| 204 | /* |
| 205 | * We keep active contexts alive until after a subsequent context |
| 206 | * switch as the final write from the context-save will be after |
| 207 | * we retire the final request. We track when we unpin the context, |
| 208 | * under the presumption that the final pin is from the last request, |
| 209 | * and instead of immediately unpinning the context, we add a task |
| 210 | * to unpin the context from the next idle-barrier. |
| 211 | * |
| 212 | * This test makes sure that the context is kept alive until a |
| 213 | * subsequent idle-barrier (emitted when the engine wakeref hits 0 |
| 214 | * with no more outstanding requests). |
| 215 | * |
| 216 | * In GuC submission mode we don't use idle barriers and we instead |
| 217 | * get a message from the GuC to signal that it is safe to unpin the |
| 218 | * context from memory. |
| 219 | */ |
| 220 | if (intel_engine_uses_guc(engine)) |
| 221 | return 0; |
| 222 | |
| 223 | if (intel_engine_pm_is_awake(engine)) { |
| 224 | pr_err("%s is awake before starting %s!\n" , |
| 225 | engine->name, __func__); |
| 226 | return -EINVAL; |
| 227 | } |
| 228 | |
| 229 | ce = intel_context_create(engine); |
| 230 | if (IS_ERR(ptr: ce)) |
| 231 | return PTR_ERR(ptr: ce); |
| 232 | |
| 233 | saved_heartbeat = engine->props.heartbeat_interval_ms; |
| 234 | engine->props.heartbeat_interval_ms = 0; |
| 235 | |
| 236 | for (pass = 0; pass <= 2; pass++) { |
| 237 | struct i915_request *rq; |
| 238 | |
| 239 | intel_engine_pm_get(engine); |
| 240 | |
| 241 | rq = intel_context_create_request(ce); |
| 242 | if (IS_ERR(ptr: rq)) { |
| 243 | err = PTR_ERR(ptr: rq); |
| 244 | goto out_engine; |
| 245 | } |
| 246 | |
| 247 | err = request_sync(rq); |
| 248 | if (err) |
| 249 | goto out_engine; |
| 250 | |
| 251 | /* Context will be kept active until after an idle-barrier. */ |
| 252 | if (i915_active_is_idle(ref: &ce->active)) { |
| 253 | pr_err("context is not active; expected idle-barrier (%s pass %d)\n" , |
| 254 | engine->name, pass); |
| 255 | err = -EINVAL; |
| 256 | goto out_engine; |
| 257 | } |
| 258 | |
| 259 | if (!intel_engine_pm_is_awake(engine)) { |
| 260 | pr_err("%s is asleep before idle-barrier\n" , |
| 261 | engine->name); |
| 262 | err = -EINVAL; |
| 263 | goto out_engine; |
| 264 | } |
| 265 | |
| 266 | out_engine: |
| 267 | intel_engine_pm_put(engine); |
| 268 | if (err) |
| 269 | goto err; |
| 270 | } |
| 271 | |
| 272 | /* Now make sure our idle-barriers are flushed */ |
| 273 | err = intel_engine_flush_barriers(engine); |
| 274 | if (err) |
| 275 | goto err; |
| 276 | |
| 277 | /* Wait for the barrier and in the process wait for engine to park */ |
| 278 | err = context_sync(ce: engine->kernel_context); |
| 279 | if (err) |
| 280 | goto err; |
| 281 | |
| 282 | if (!i915_active_is_idle(ref: &ce->active)) { |
| 283 | pr_err("context is still active!" ); |
| 284 | err = -EINVAL; |
| 285 | } |
| 286 | |
| 287 | intel_engine_pm_flush(engine); |
| 288 | |
| 289 | if (intel_engine_pm_is_awake(engine)) { |
| 290 | struct drm_printer p = drm_dbg_printer(drm: &engine->i915->drm, |
| 291 | category: DRM_UT_DRIVER, NULL); |
| 292 | |
| 293 | intel_engine_dump(engine, m: &p, |
| 294 | header: "%s is still awake:%d after idle-barriers\n" , |
| 295 | engine->name, |
| 296 | atomic_read(v: &engine->wakeref.count)); |
| 297 | GEM_TRACE_DUMP(); |
| 298 | |
| 299 | err = -EINVAL; |
| 300 | goto err; |
| 301 | } |
| 302 | |
| 303 | err: |
| 304 | engine->props.heartbeat_interval_ms = saved_heartbeat; |
| 305 | intel_context_put(ce); |
| 306 | return err; |
| 307 | } |
| 308 | |
| 309 | static int live_active_context(void *arg) |
| 310 | { |
| 311 | struct intel_gt *gt = arg; |
| 312 | struct intel_engine_cs *engine; |
| 313 | enum intel_engine_id id; |
| 314 | int err = 0; |
| 315 | |
| 316 | for_each_engine(engine, gt, id) { |
| 317 | err = __live_active_context(engine); |
| 318 | if (err) |
| 319 | break; |
| 320 | |
| 321 | err = igt_flush_test(i915: gt->i915); |
| 322 | if (err) |
| 323 | break; |
| 324 | } |
| 325 | |
| 326 | return err; |
| 327 | } |
| 328 | |
| 329 | static int __remote_sync(struct intel_context *ce, struct intel_context *remote) |
| 330 | { |
| 331 | struct i915_request *rq; |
| 332 | int err; |
| 333 | |
| 334 | err = intel_context_pin(ce: remote); |
| 335 | if (err) |
| 336 | return err; |
| 337 | |
| 338 | rq = intel_context_create_request(ce); |
| 339 | if (IS_ERR(ptr: rq)) { |
| 340 | err = PTR_ERR(ptr: rq); |
| 341 | goto unpin; |
| 342 | } |
| 343 | |
| 344 | err = intel_context_prepare_remote_request(ce: remote, rq); |
| 345 | if (err) { |
| 346 | i915_request_add(rq); |
| 347 | goto unpin; |
| 348 | } |
| 349 | |
| 350 | err = request_sync(rq); |
| 351 | |
| 352 | unpin: |
| 353 | intel_context_unpin(ce: remote); |
| 354 | return err; |
| 355 | } |
| 356 | |
| 357 | static int __live_remote_context(struct intel_engine_cs *engine) |
| 358 | { |
| 359 | struct intel_context *local, *remote; |
| 360 | unsigned long saved_heartbeat; |
| 361 | int pass; |
| 362 | int err; |
| 363 | |
| 364 | /* |
| 365 | * Check that our idle barriers do not interfere with normal |
| 366 | * activity tracking. In particular, check that operating |
| 367 | * on the context image remotely (intel_context_prepare_remote_request), |
| 368 | * which inserts foreign fences into intel_context.active, does not |
| 369 | * clobber the idle-barrier. |
| 370 | * |
| 371 | * In GuC submission mode we don't use idle barriers. |
| 372 | */ |
| 373 | if (intel_engine_uses_guc(engine)) |
| 374 | return 0; |
| 375 | |
| 376 | if (intel_engine_pm_is_awake(engine)) { |
| 377 | pr_err("%s is awake before starting %s!\n" , |
| 378 | engine->name, __func__); |
| 379 | return -EINVAL; |
| 380 | } |
| 381 | |
| 382 | remote = intel_context_create(engine); |
| 383 | if (IS_ERR(ptr: remote)) |
| 384 | return PTR_ERR(ptr: remote); |
| 385 | |
| 386 | local = intel_context_create(engine); |
| 387 | if (IS_ERR(ptr: local)) { |
| 388 | err = PTR_ERR(ptr: local); |
| 389 | goto err_remote; |
| 390 | } |
| 391 | |
| 392 | saved_heartbeat = engine->props.heartbeat_interval_ms; |
| 393 | engine->props.heartbeat_interval_ms = 0; |
| 394 | intel_engine_pm_get(engine); |
| 395 | |
| 396 | for (pass = 0; pass <= 2; pass++) { |
| 397 | err = __remote_sync(ce: local, remote); |
| 398 | if (err) |
| 399 | break; |
| 400 | |
| 401 | err = __remote_sync(ce: engine->kernel_context, remote); |
| 402 | if (err) |
| 403 | break; |
| 404 | |
| 405 | if (i915_active_is_idle(ref: &remote->active)) { |
| 406 | pr_err("remote context is not active; expected idle-barrier (%s pass %d)\n" , |
| 407 | engine->name, pass); |
| 408 | err = -EINVAL; |
| 409 | break; |
| 410 | } |
| 411 | } |
| 412 | |
| 413 | intel_engine_pm_put(engine); |
| 414 | engine->props.heartbeat_interval_ms = saved_heartbeat; |
| 415 | |
| 416 | intel_context_put(ce: local); |
| 417 | err_remote: |
| 418 | intel_context_put(ce: remote); |
| 419 | return err; |
| 420 | } |
| 421 | |
| 422 | static int live_remote_context(void *arg) |
| 423 | { |
| 424 | struct intel_gt *gt = arg; |
| 425 | struct intel_engine_cs *engine; |
| 426 | enum intel_engine_id id; |
| 427 | int err = 0; |
| 428 | |
| 429 | for_each_engine(engine, gt, id) { |
| 430 | err = __live_remote_context(engine); |
| 431 | if (err) |
| 432 | break; |
| 433 | |
| 434 | err = igt_flush_test(i915: gt->i915); |
| 435 | if (err) |
| 436 | break; |
| 437 | } |
| 438 | |
| 439 | return err; |
| 440 | } |
| 441 | |
| 442 | int intel_context_live_selftests(struct drm_i915_private *i915) |
| 443 | { |
| 444 | static const struct i915_subtest tests[] = { |
| 445 | SUBTEST(live_context_size), |
| 446 | SUBTEST(live_active_context), |
| 447 | SUBTEST(live_remote_context), |
| 448 | }; |
| 449 | struct intel_gt *gt = to_gt(i915); |
| 450 | |
| 451 | if (intel_gt_is_wedged(gt)) |
| 452 | return 0; |
| 453 | |
| 454 | return intel_gt_live_subtests(tests, gt); |
| 455 | } |
| 456 | |