selftest_engine_heartbeat.c (9295B)
1// SPDX-License-Identifier: MIT 2/* 3 * Copyright © 2018 Intel Corporation 4 */ 5 6#include <linux/sort.h> 7 8#include "i915_drv.h" 9 10#include "intel_gt_requests.h" 11#include "i915_selftest.h" 12#include "selftest_engine_heartbeat.h" 13 14static void reset_heartbeat(struct intel_engine_cs *engine) 15{ 16 intel_engine_set_heartbeat(engine, 17 engine->defaults.heartbeat_interval_ms); 18} 19 20static int timeline_sync(struct intel_timeline *tl) 21{ 22 struct dma_fence *fence; 23 long timeout; 24 25 fence = i915_active_fence_get(&tl->last_request); 26 if (!fence) 27 return 0; 28 29 timeout = dma_fence_wait_timeout(fence, true, HZ / 2); 30 dma_fence_put(fence); 31 if (timeout < 0) 32 return timeout; 33 34 return 0; 35} 36 37static int engine_sync_barrier(struct intel_engine_cs *engine) 38{ 39 return timeline_sync(engine->kernel_context->timeline); 40} 41 42struct pulse { 43 struct i915_active active; 44 struct kref kref; 45}; 46 47static int pulse_active(struct i915_active *active) 48{ 49 kref_get(&container_of(active, struct pulse, active)->kref); 50 return 0; 51} 52 53static void pulse_free(struct kref *kref) 54{ 55 struct pulse *p = container_of(kref, typeof(*p), kref); 56 57 i915_active_fini(&p->active); 58 kfree(p); 59} 60 61static void pulse_put(struct pulse *p) 62{ 63 kref_put(&p->kref, pulse_free); 64} 65 66static void pulse_retire(struct i915_active *active) 67{ 68 pulse_put(container_of(active, struct pulse, active)); 69} 70 71static struct pulse *pulse_create(void) 72{ 73 struct pulse *p; 74 75 p = kmalloc(sizeof(*p), GFP_KERNEL); 76 if (!p) 77 return p; 78 79 kref_init(&p->kref); 80 i915_active_init(&p->active, pulse_active, pulse_retire, 0); 81 82 return p; 83} 84 85static void pulse_unlock_wait(struct pulse *p) 86{ 87 i915_active_unlock_wait(&p->active); 88} 89 90static int __live_idle_pulse(struct intel_engine_cs *engine, 91 int (*fn)(struct intel_engine_cs *cs)) 92{ 93 struct pulse *p; 94 int err; 95 96 GEM_BUG_ON(!intel_engine_pm_is_awake(engine)); 97 98 p = pulse_create(); 99 if (!p) 100 return -ENOMEM; 101 102 err = i915_active_acquire(&p->active); 103 if (err) 104 goto out; 105 106 err = i915_active_acquire_preallocate_barrier(&p->active, engine); 107 if (err) { 108 i915_active_release(&p->active); 109 goto out; 110 } 111 112 i915_active_acquire_barrier(&p->active); 113 i915_active_release(&p->active); 114 115 GEM_BUG_ON(i915_active_is_idle(&p->active)); 116 GEM_BUG_ON(llist_empty(&engine->barrier_tasks)); 117 118 err = fn(engine); 119 if (err) 120 goto out; 121 122 GEM_BUG_ON(!llist_empty(&engine->barrier_tasks)); 123 124 if (engine_sync_barrier(engine)) { 125 struct drm_printer m = drm_err_printer("pulse"); 126 127 pr_err("%s: no heartbeat pulse?\n", engine->name); 128 intel_engine_dump(engine, &m, "%s", engine->name); 129 130 err = -ETIME; 131 goto out; 132 } 133 134 GEM_BUG_ON(READ_ONCE(engine->serial) != engine->wakeref_serial); 135 136 pulse_unlock_wait(p); /* synchronize with the retirement callback */ 137 138 if (!i915_active_is_idle(&p->active)) { 139 struct drm_printer m = drm_err_printer("pulse"); 140 141 pr_err("%s: heartbeat pulse did not flush idle tasks\n", 142 engine->name); 143 i915_active_print(&p->active, &m); 144 145 err = -EINVAL; 146 goto out; 147 } 148 149out: 150 pulse_put(p); 151 return err; 152} 153 154static int live_idle_flush(void *arg) 155{ 156 struct intel_gt *gt = arg; 157 struct intel_engine_cs *engine; 158 enum intel_engine_id id; 159 int err = 0; 160 161 /* Check that we can flush the idle barriers */ 162 163 for_each_engine(engine, gt, id) { 164 st_engine_heartbeat_disable(engine); 165 err = __live_idle_pulse(engine, intel_engine_flush_barriers); 166 st_engine_heartbeat_enable(engine); 167 if (err) 168 break; 169 } 170 171 return err; 172} 173 174static int live_idle_pulse(void *arg) 175{ 176 struct intel_gt *gt = arg; 177 struct intel_engine_cs *engine; 178 enum intel_engine_id id; 179 int err = 0; 180 181 /* Check that heartbeat pulses flush the idle barriers */ 182 183 for_each_engine(engine, gt, id) { 184 st_engine_heartbeat_disable(engine); 185 err = __live_idle_pulse(engine, intel_engine_pulse); 186 st_engine_heartbeat_enable(engine); 187 if (err && err != -ENODEV) 188 break; 189 190 err = 0; 191 } 192 193 return err; 194} 195 196static int cmp_u32(const void *_a, const void *_b) 197{ 198 const u32 *a = _a, *b = _b; 199 200 return *a - *b; 201} 202 203static int __live_heartbeat_fast(struct intel_engine_cs *engine) 204{ 205 const unsigned int error_threshold = max(20000u, jiffies_to_usecs(6)); 206 struct intel_context *ce; 207 struct i915_request *rq; 208 ktime_t t0, t1; 209 u32 times[5]; 210 int err; 211 int i; 212 213 ce = intel_context_create(engine); 214 if (IS_ERR(ce)) 215 return PTR_ERR(ce); 216 217 intel_engine_pm_get(engine); 218 219 err = intel_engine_set_heartbeat(engine, 1); 220 if (err) 221 goto err_pm; 222 223 for (i = 0; i < ARRAY_SIZE(times); i++) { 224 do { 225 /* Manufacture a tick */ 226 intel_engine_park_heartbeat(engine); 227 GEM_BUG_ON(engine->heartbeat.systole); 228 engine->serial++; /* pretend we are not idle! */ 229 intel_engine_unpark_heartbeat(engine); 230 231 flush_delayed_work(&engine->heartbeat.work); 232 if (!delayed_work_pending(&engine->heartbeat.work)) { 233 pr_err("%s: heartbeat %d did not start\n", 234 engine->name, i); 235 err = -EINVAL; 236 goto err_pm; 237 } 238 239 rcu_read_lock(); 240 rq = READ_ONCE(engine->heartbeat.systole); 241 if (rq) 242 rq = i915_request_get_rcu(rq); 243 rcu_read_unlock(); 244 } while (!rq); 245 246 t0 = ktime_get(); 247 while (rq == READ_ONCE(engine->heartbeat.systole)) 248 yield(); /* work is on the local cpu! */ 249 t1 = ktime_get(); 250 251 i915_request_put(rq); 252 times[i] = ktime_us_delta(t1, t0); 253 } 254 255 sort(times, ARRAY_SIZE(times), sizeof(times[0]), cmp_u32, NULL); 256 257 pr_info("%s: Heartbeat delay: %uus [%u, %u]\n", 258 engine->name, 259 times[ARRAY_SIZE(times) / 2], 260 times[0], 261 times[ARRAY_SIZE(times) - 1]); 262 263 /* 264 * Ideally, the upper bound on min work delay would be something like 265 * 2 * 2 (worst), +1 for scheduling, +1 for slack. In practice, we 266 * are, even with system_wq_highpri, at the mercy of the CPU scheduler 267 * and may be stuck behind some slow work for many millisecond. Such 268 * as our very own display workers. 269 */ 270 if (times[ARRAY_SIZE(times) / 2] > error_threshold) { 271 pr_err("%s: Heartbeat delay was %uus, expected less than %dus\n", 272 engine->name, 273 times[ARRAY_SIZE(times) / 2], 274 error_threshold); 275 err = -EINVAL; 276 } 277 278 reset_heartbeat(engine); 279err_pm: 280 intel_engine_pm_put(engine); 281 intel_context_put(ce); 282 return err; 283} 284 285static int live_heartbeat_fast(void *arg) 286{ 287 struct intel_gt *gt = arg; 288 struct intel_engine_cs *engine; 289 enum intel_engine_id id; 290 int err = 0; 291 292 /* Check that the heartbeat ticks at the desired rate. */ 293 if (!CONFIG_DRM_I915_HEARTBEAT_INTERVAL) 294 return 0; 295 296 for_each_engine(engine, gt, id) { 297 err = __live_heartbeat_fast(engine); 298 if (err) 299 break; 300 } 301 302 return err; 303} 304 305static int __live_heartbeat_off(struct intel_engine_cs *engine) 306{ 307 int err; 308 309 intel_engine_pm_get(engine); 310 311 engine->serial++; 312 flush_delayed_work(&engine->heartbeat.work); 313 if (!delayed_work_pending(&engine->heartbeat.work)) { 314 pr_err("%s: heartbeat not running\n", 315 engine->name); 316 err = -EINVAL; 317 goto err_pm; 318 } 319 320 err = intel_engine_set_heartbeat(engine, 0); 321 if (err) 322 goto err_pm; 323 324 engine->serial++; 325 flush_delayed_work(&engine->heartbeat.work); 326 if (delayed_work_pending(&engine->heartbeat.work)) { 327 pr_err("%s: heartbeat still running\n", 328 engine->name); 329 err = -EINVAL; 330 goto err_beat; 331 } 332 333 if (READ_ONCE(engine->heartbeat.systole)) { 334 pr_err("%s: heartbeat still allocated\n", 335 engine->name); 336 err = -EINVAL; 337 goto err_beat; 338 } 339 340err_beat: 341 reset_heartbeat(engine); 342err_pm: 343 intel_engine_pm_put(engine); 344 return err; 345} 346 347static int live_heartbeat_off(void *arg) 348{ 349 struct intel_gt *gt = arg; 350 struct intel_engine_cs *engine; 351 enum intel_engine_id id; 352 int err = 0; 353 354 /* Check that we can turn off heartbeat and not interrupt VIP */ 355 if (!CONFIG_DRM_I915_HEARTBEAT_INTERVAL) 356 return 0; 357 358 for_each_engine(engine, gt, id) { 359 if (!intel_engine_has_preemption(engine)) 360 continue; 361 362 err = __live_heartbeat_off(engine); 363 if (err) 364 break; 365 } 366 367 return err; 368} 369 370int intel_heartbeat_live_selftests(struct drm_i915_private *i915) 371{ 372 static const struct i915_subtest tests[] = { 373 SUBTEST(live_idle_flush), 374 SUBTEST(live_idle_pulse), 375 SUBTEST(live_heartbeat_fast), 376 SUBTEST(live_heartbeat_off), 377 }; 378 int saved_hangcheck; 379 int err; 380 381 if (intel_gt_is_wedged(to_gt(i915))) 382 return 0; 383 384 saved_hangcheck = i915->params.enable_hangcheck; 385 i915->params.enable_hangcheck = INT_MAX; 386 387 err = intel_gt_live_subtests(tests, to_gt(i915)); 388 389 i915->params.enable_hangcheck = saved_hangcheck; 390 return err; 391} 392 393void st_engine_heartbeat_disable(struct intel_engine_cs *engine) 394{ 395 engine->props.heartbeat_interval_ms = 0; 396 397 intel_engine_pm_get(engine); 398 intel_engine_park_heartbeat(engine); 399} 400 401void st_engine_heartbeat_enable(struct intel_engine_cs *engine) 402{ 403 intel_engine_pm_put(engine); 404 405 engine->props.heartbeat_interval_ms = 406 engine->defaults.heartbeat_interval_ms; 407} 408 409void st_engine_heartbeat_disable_no_pm(struct intel_engine_cs *engine) 410{ 411 engine->props.heartbeat_interval_ms = 0; 412 413 /* 414 * Park the heartbeat but without holding the PM lock as that 415 * makes the engines appear not-idle. Note that if/when unpark 416 * is called due to the PM lock being acquired later the 417 * heartbeat still won't be enabled because of the above = 0. 418 */ 419 if (intel_engine_pm_get_if_awake(engine)) { 420 intel_engine_park_heartbeat(engine); 421 intel_engine_pm_put(engine); 422 } 423} 424 425void st_engine_heartbeat_enable_no_pm(struct intel_engine_cs *engine) 426{ 427 engine->props.heartbeat_interval_ms = 428 engine->defaults.heartbeat_interval_ms; 429}