Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/i915/gt: Distinguish the virtual breadcrumbs from the irq breadcrumbs

On the virtual engines, we only use the intel_breadcrumbs for tracking
signaling of stale breadcrumbs from the irq_workers. They do not have
any associated interrupt handling, active requests are passed to a
physical engine and associated breadcrumb interrupt handler. This causes
issues for us as we need to ensure that we do not actually try and
enable interrupts and the powermanagement required for them on the
virtual engine, as they will never be disabled. Instead, let's
specify the physical engine used for interrupt handler on a particular
breadcrumb.

v2: Drop b->irq_armed = true mocking for no interrupt HW

Fixes: 4fe6abb8f513 ("drm/i915/gt: Ignore irq enabling on the virtual engines")
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200731154834.8378-4-chris@chris-wilson.co.uk
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Signed-off-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>

authored by

Chris Wilson and committed by
Joonas Lahtinen
b3786b29 56f581ba

+162 -95
+40 -36
drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
··· 28 28 29 29 #include "i915_drv.h" 30 30 #include "i915_trace.h" 31 + #include "intel_breadcrumbs.h" 31 32 #include "intel_gt_pm.h" 32 33 #include "intel_gt_requests.h" 33 34 ··· 56 55 57 56 static void __intel_breadcrumbs_disarm_irq(struct intel_breadcrumbs *b) 58 57 { 59 - struct intel_engine_cs *engine = 60 - container_of(b, struct intel_engine_cs, breadcrumbs); 61 - 62 58 lockdep_assert_held(&b->irq_lock); 59 + 60 + if (!b->irq_engine || !b->irq_armed) 61 + return; 63 62 64 63 GEM_BUG_ON(!b->irq_enabled); 65 64 if (!--b->irq_enabled) 66 - irq_disable(engine); 65 + irq_disable(b->irq_engine); 67 66 68 67 WRITE_ONCE(b->irq_armed, false); 69 - intel_gt_pm_put_async(engine->gt); 68 + intel_gt_pm_put_async(b->irq_engine->gt); 70 69 } 71 70 72 - void intel_engine_disarm_breadcrumbs(struct intel_engine_cs *engine) 71 + void intel_breadcrumbs_park(struct intel_breadcrumbs *b) 73 72 { 74 - struct intel_breadcrumbs *b = &engine->breadcrumbs; 75 73 unsigned long flags; 76 74 77 75 if (!READ_ONCE(b->irq_armed)) 78 76 return; 79 77 80 78 spin_lock_irqsave(&b->irq_lock, flags); 81 - if (b->irq_armed) 82 - __intel_breadcrumbs_disarm_irq(b); 79 + __intel_breadcrumbs_disarm_irq(b); 83 80 spin_unlock_irqrestore(&b->irq_lock, flags); 84 81 } 85 82 ··· 132 133 133 134 static void add_retire(struct intel_breadcrumbs *b, struct intel_timeline *tl) 134 135 { 135 - struct intel_engine_cs *engine = 136 - container_of(b, struct intel_engine_cs, breadcrumbs); 137 - 138 - if (unlikely(intel_engine_is_virtual(engine))) 139 - engine = intel_virtual_engine_get_sibling(engine, 0); 140 - 141 - intel_engine_add_retire(engine, tl); 136 + if (b->irq_engine) 137 + intel_engine_add_retire(b->irq_engine, tl); 142 138 } 143 139 144 140 static bool __signal_request(struct i915_request *rq, struct list_head *signals) ··· 158 164 159 165 spin_lock(&b->irq_lock); 160 166 161 - if (b->irq_armed && list_empty(&b->signalers)) 167 + if (list_empty(&b->signalers)) 162 168 __intel_breadcrumbs_disarm_irq(b); 163 169 164 170 list_splice_init(&b->signaled_requests, &signal); ··· 216 222 217 223 static void __intel_breadcrumbs_arm_irq(struct intel_breadcrumbs *b) 218 224 { 219 - struct intel_engine_cs *engine = 220 - container_of(b, struct intel_engine_cs, breadcrumbs); 221 - 222 225 lockdep_assert_held(&b->irq_lock); 223 - if (b->irq_armed) 226 + 227 + if (!b->irq_engine || b->irq_armed) 224 228 return; 225 229 226 - if (!intel_gt_pm_get_if_awake(engine->gt)) 230 + if (!intel_gt_pm_get_if_awake(b->irq_engine->gt)) 227 231 return; 228 232 229 233 /* ··· 241 249 */ 242 250 243 251 if (!b->irq_enabled++) 244 - irq_enable(engine); 252 + irq_enable(b->irq_engine); 245 253 } 246 254 247 - void intel_engine_init_breadcrumbs(struct intel_engine_cs *engine) 255 + struct intel_breadcrumbs * 256 + intel_breadcrumbs_create(struct intel_engine_cs *irq_engine) 248 257 { 249 - struct intel_breadcrumbs *b = &engine->breadcrumbs; 258 + struct intel_breadcrumbs *b; 259 + 260 + b = kzalloc(sizeof(*b), GFP_KERNEL); 261 + if (!b) 262 + return NULL; 250 263 251 264 spin_lock_init(&b->irq_lock); 252 265 INIT_LIST_HEAD(&b->signalers); 253 266 INIT_LIST_HEAD(&b->signaled_requests); 254 267 255 268 init_irq_work(&b->irq_work, signal_irq_work); 269 + 270 + b->irq_engine = irq_engine; 271 + 272 + return b; 256 273 } 257 274 258 - void intel_engine_reset_breadcrumbs(struct intel_engine_cs *engine) 275 + void intel_breadcrumbs_reset(struct intel_breadcrumbs *b) 259 276 { 260 - struct intel_breadcrumbs *b = &engine->breadcrumbs; 261 277 unsigned long flags; 278 + 279 + if (!b->irq_engine) 280 + return; 262 281 263 282 spin_lock_irqsave(&b->irq_lock, flags); 264 283 265 284 if (b->irq_enabled) 266 - irq_enable(engine); 285 + irq_enable(b->irq_engine); 267 286 else 268 - irq_disable(engine); 287 + irq_disable(b->irq_engine); 269 288 270 289 spin_unlock_irqrestore(&b->irq_lock, flags); 271 290 } 272 291 273 - void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine) 292 + void intel_breadcrumbs_free(struct intel_breadcrumbs *b) 274 293 { 294 + kfree(b); 275 295 } 276 296 277 297 static void insert_breadcrumb(struct i915_request *rq, ··· 373 369 * request submit/unsubmit path, and so we must be more careful to 374 370 * acquire the right lock. 375 371 */ 376 - b = &READ_ONCE(rq->engine)->breadcrumbs; 372 + b = READ_ONCE(rq->engine)->breadcrumbs; 377 373 spin_lock(&b->irq_lock); 378 - while (unlikely(b != &READ_ONCE(rq->engine)->breadcrumbs)) { 374 + while (unlikely(b != READ_ONCE(rq->engine)->breadcrumbs)) { 379 375 spin_unlock(&b->irq_lock); 380 - b = &READ_ONCE(rq->engine)->breadcrumbs; 376 + b = READ_ONCE(rq->engine)->breadcrumbs; 381 377 spin_lock(&b->irq_lock); 382 378 } 383 379 ··· 398 394 399 395 void i915_request_cancel_breadcrumb(struct i915_request *rq) 400 396 { 401 - struct intel_breadcrumbs *b = &rq->engine->breadcrumbs; 397 + struct intel_breadcrumbs *b = rq->engine->breadcrumbs; 402 398 403 399 /* 404 400 * We must wait for b->irq_lock so that we know the interrupt handler ··· 422 418 void intel_engine_print_breadcrumbs(struct intel_engine_cs *engine, 423 419 struct drm_printer *p) 424 420 { 425 - struct intel_breadcrumbs *b = &engine->breadcrumbs; 421 + struct intel_breadcrumbs *b = engine->breadcrumbs; 426 422 struct intel_context *ce; 427 423 struct i915_request *rq; 428 424 429 - if (list_empty(&b->signalers)) 425 + if (!b || list_empty(&b->signalers)) 430 426 return; 431 427 432 428 drm_printf(p, "Signals:\n");
+36
drivers/gpu/drm/i915/gt/intel_breadcrumbs.h
··· 1 + /* SPDX-License-Identifier: MIT */ 2 + /* 3 + * Copyright © 2019 Intel Corporation 4 + */ 5 + 6 + #ifndef __INTEL_BREADCRUMBS__ 7 + #define __INTEL_BREADCRUMBS__ 8 + 9 + #include <linux/irq_work.h> 10 + 11 + #include "intel_engine_types.h" 12 + 13 + struct drm_printer; 14 + struct i915_request; 15 + struct intel_breadcrumbs; 16 + 17 + struct intel_breadcrumbs * 18 + intel_breadcrumbs_create(struct intel_engine_cs *irq_engine); 19 + void intel_breadcrumbs_free(struct intel_breadcrumbs *b); 20 + 21 + void intel_breadcrumbs_reset(struct intel_breadcrumbs *b); 22 + void intel_breadcrumbs_park(struct intel_breadcrumbs *b); 23 + 24 + static inline void 25 + intel_engine_signal_breadcrumbs(struct intel_engine_cs *engine) 26 + { 27 + irq_work_queue(&engine->breadcrumbs->irq_work); 28 + } 29 + 30 + void intel_engine_print_breadcrumbs(struct intel_engine_cs *engine, 31 + struct drm_printer *p); 32 + 33 + bool i915_request_enable_breadcrumb(struct i915_request *request); 34 + void i915_request_cancel_breadcrumb(struct i915_request *request); 35 + 36 + #endif /* __INTEL_BREADCRUMBS__ */
+47
drivers/gpu/drm/i915/gt/intel_breadcrumbs_types.h
··· 1 + /* SPDX-License-Identifier: MIT */ 2 + /* 3 + * Copyright © 2019 Intel Corporation 4 + */ 5 + 6 + #ifndef __INTEL_BREADCRUMBS_TYPES__ 7 + #define __INTEL_BREADCRUMBS_TYPES__ 8 + 9 + #include <linux/irq_work.h> 10 + #include <linux/list.h> 11 + #include <linux/spinlock.h> 12 + #include <linux/types.h> 13 + 14 + /* 15 + * Rather than have every client wait upon all user interrupts, 16 + * with the herd waking after every interrupt and each doing the 17 + * heavyweight seqno dance, we delegate the task (of being the 18 + * bottom-half of the user interrupt) to the first client. After 19 + * every interrupt, we wake up one client, who does the heavyweight 20 + * coherent seqno read and either goes back to sleep (if incomplete), 21 + * or wakes up all the completed clients in parallel, before then 22 + * transferring the bottom-half status to the next client in the queue. 23 + * 24 + * Compared to walking the entire list of waiters in a single dedicated 25 + * bottom-half, we reduce the latency of the first waiter by avoiding 26 + * a context switch, but incur additional coherent seqno reads when 27 + * following the chain of request breadcrumbs. Since it is most likely 28 + * that we have a single client waiting on each seqno, then reducing 29 + * the overhead of waking that client is much preferred. 30 + */ 31 + struct intel_breadcrumbs { 32 + spinlock_t irq_lock; /* protects the lists used in hardirq context */ 33 + 34 + /* Not all breadcrumbs are attached to physical HW */ 35 + struct intel_engine_cs *irq_engine; 36 + 37 + struct list_head signalers; 38 + struct list_head signaled_requests; 39 + 40 + struct irq_work irq_work; /* for use from inside irq_lock */ 41 + 42 + unsigned int irq_enabled; 43 + 44 + bool irq_armed; 45 + }; 46 + 47 + #endif /* __INTEL_BREADCRUMBS_TYPES__ */
-17
drivers/gpu/drm/i915/gt/intel_engine.h
··· 223 223 224 224 void intel_engine_init_execlists(struct intel_engine_cs *engine); 225 225 226 - void intel_engine_init_breadcrumbs(struct intel_engine_cs *engine); 227 - void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine); 228 - 229 - void intel_engine_disarm_breadcrumbs(struct intel_engine_cs *engine); 230 - 231 - static inline void 232 - intel_engine_signal_breadcrumbs(struct intel_engine_cs *engine) 233 - { 234 - irq_work_queue(&engine->breadcrumbs.irq_work); 235 - } 236 - 237 - void intel_engine_reset_breadcrumbs(struct intel_engine_cs *engine); 238 - void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine); 239 - 240 - void intel_engine_print_breadcrumbs(struct intel_engine_cs *engine, 241 - struct drm_printer *p); 242 - 243 226 static inline u32 *__gen8_emit_pipe_control(u32 *batch, u32 flags0, u32 flags1, u32 offset) 244 227 { 245 228 memset(batch, 0, 6 * sizeof(u32));
+12 -2
drivers/gpu/drm/i915/gt/intel_engine_cs.c
··· 28 28 29 29 #include "i915_drv.h" 30 30 31 + #include "intel_breadcrumbs.h" 31 32 #include "intel_context.h" 32 33 #include "intel_engine.h" 33 34 #include "intel_engine_pm.h" ··· 701 700 if (err) 702 701 return err; 703 702 703 + engine->breadcrumbs = intel_breadcrumbs_create(engine); 704 + if (!engine->breadcrumbs) { 705 + err = -ENOMEM; 706 + goto err_status; 707 + } 708 + 704 709 intel_engine_init_active(engine, ENGINE_PHYSICAL); 705 - intel_engine_init_breadcrumbs(engine); 706 710 intel_engine_init_execlists(engine); 707 711 intel_engine_init_cmd_parser(engine); 708 712 intel_engine_init__pm(engine); ··· 722 716 intel_engine_init_ctx_wa(engine); 723 717 724 718 return 0; 719 + 720 + err_status: 721 + cleanup_status_page(engine); 722 + return err; 725 723 } 726 724 727 725 struct measure_breadcrumb { ··· 924 914 tasklet_kill(&engine->execlists.tasklet); /* flush the callback */ 925 915 926 916 cleanup_status_page(engine); 917 + intel_breadcrumbs_free(engine->breadcrumbs); 927 918 928 919 intel_engine_fini_retire(engine); 929 - intel_engine_fini_breadcrumbs(engine); 930 920 intel_engine_cleanup_cmd_parser(engine); 931 921 932 922 if (engine->default_state)
+2 -1
drivers/gpu/drm/i915/gt/intel_engine_pm.c
··· 6 6 7 7 #include "i915_drv.h" 8 8 9 + #include "intel_breadcrumbs.h" 9 10 #include "intel_context.h" 10 11 #include "intel_engine.h" 11 12 #include "intel_engine_heartbeat.h" ··· 248 247 call_idle_barriers(engine); /* cleanup after wedging */ 249 248 250 249 intel_engine_park_heartbeat(engine); 251 - intel_engine_disarm_breadcrumbs(engine); 250 + intel_breadcrumbs_park(engine->breadcrumbs); 252 251 253 252 /* Must be reset upon idling, or we may miss the busy wakeup. */ 254 253 GEM_BUG_ON(engine->execlists.queue_priority_hint != INT_MIN);
+3 -28
drivers/gpu/drm/i915/gt/intel_engine_types.h
··· 22 22 #include "i915_pmu.h" 23 23 #include "i915_priolist_types.h" 24 24 #include "i915_selftest.h" 25 + #include "intel_breadcrumbs_types.h" 25 26 #include "intel_sseu.h" 26 27 #include "intel_timeline_types.h" 27 28 #include "intel_uncore.h" ··· 374 373 */ 375 374 struct ewma__engine_latency latency; 376 375 377 - /* Rather than have every client wait upon all user interrupts, 378 - * with the herd waking after every interrupt and each doing the 379 - * heavyweight seqno dance, we delegate the task (of being the 380 - * bottom-half of the user interrupt) to the first client. After 381 - * every interrupt, we wake up one client, who does the heavyweight 382 - * coherent seqno read and either goes back to sleep (if incomplete), 383 - * or wakes up all the completed clients in parallel, before then 384 - * transferring the bottom-half status to the next client in the queue. 385 - * 386 - * Compared to walking the entire list of waiters in a single dedicated 387 - * bottom-half, we reduce the latency of the first waiter by avoiding 388 - * a context switch, but incur additional coherent seqno reads when 389 - * following the chain of request breadcrumbs. Since it is most likely 390 - * that we have a single client waiting on each seqno, then reducing 391 - * the overhead of waking that client is much preferred. 392 - */ 393 - struct intel_breadcrumbs { 394 - spinlock_t irq_lock; 395 - struct list_head signalers; 396 - 397 - struct list_head signaled_requests; 398 - 399 - struct irq_work irq_work; /* for use from inside irq_lock */ 400 - 401 - unsigned int irq_enabled; 402 - 403 - bool irq_armed; 404 - } breadcrumbs; 376 + /* Keep track of all the seqno used, a trail of breadcrumbs */ 377 + struct intel_breadcrumbs *breadcrumbs; 405 378 406 379 struct intel_engine_pmu { 407 380 /**
+1
drivers/gpu/drm/i915/gt/intel_gt_irq.c
··· 8 8 9 9 #include "i915_drv.h" 10 10 #include "i915_irq.h" 11 + #include "intel_breadcrumbs.h" 11 12 #include "intel_gt.h" 12 13 #include "intel_gt_irq.h" 13 14 #include "intel_uncore.h"
+8 -3
drivers/gpu/drm/i915/gt/intel_lrc.c
··· 137 137 #include "i915_perf.h" 138 138 #include "i915_trace.h" 139 139 #include "i915_vgpu.h" 140 + #include "intel_breadcrumbs.h" 140 141 #include "intel_context.h" 141 142 #include "intel_engine_pm.h" 142 143 #include "intel_gt.h" ··· 4113 4112 { 4114 4113 intel_mocs_init_engine(engine); 4115 4114 4116 - intel_engine_reset_breadcrumbs(engine); 4115 + intel_breadcrumbs_reset(engine->breadcrumbs); 4117 4116 4118 4117 if (GEM_SHOW_DEBUG() && unexpected_starting_state(engine)) { 4119 4118 struct drm_printer p = drm_debug_printer(__func__); ··· 5715 5714 snprintf(ve->base.name, sizeof(ve->base.name), "virtual"); 5716 5715 5717 5716 intel_engine_init_active(&ve->base, ENGINE_VIRTUAL); 5718 - intel_engine_init_breadcrumbs(&ve->base); 5719 5717 intel_engine_init_execlists(&ve->base); 5720 - ve->base.breadcrumbs.irq_armed = true; /* fake HW, used for irq_work */ 5721 5718 5722 5719 ve->base.cops = &virtual_context_ops; 5723 5720 ve->base.request_alloc = execlists_request_alloc; ··· 5731 5732 (unsigned long)ve); 5732 5733 5733 5734 intel_context_init(&ve->context, &ve->base); 5735 + 5736 + ve->base.breadcrumbs = intel_breadcrumbs_create(NULL); 5737 + if (!ve->base.breadcrumbs) { 5738 + err = -ENOMEM; 5739 + goto err_put; 5740 + } 5734 5741 5735 5742 for (n = 0; n < count; n++) { 5736 5743 struct intel_engine_cs *sibling = siblings[n];
+1
drivers/gpu/drm/i915/gt/intel_reset.c
··· 15 15 #include "i915_drv.h" 16 16 #include "i915_gpu_error.h" 17 17 #include "i915_irq.h" 18 + #include "intel_breadcrumbs.h" 18 19 #include "intel_engine_pm.h" 19 20 #include "intel_gt.h" 20 21 #include "intel_gt_pm.h"
+2 -1
drivers/gpu/drm/i915/gt/intel_ring_submission.c
··· 32 32 #include "gen6_ppgtt.h" 33 33 #include "gen7_renderclear.h" 34 34 #include "i915_drv.h" 35 + #include "intel_breadcrumbs.h" 35 36 #include "intel_context.h" 36 37 #include "intel_gt.h" 37 38 #include "intel_reset.h" ··· 256 255 else 257 256 ring_setup_status_page(engine); 258 257 259 - intel_engine_reset_breadcrumbs(engine); 258 + intel_breadcrumbs_reset(engine->breadcrumbs); 260 259 261 260 /* Enforce ordering by reading HEAD register back */ 262 261 ENGINE_POSTING_READ(engine, RING_HEAD);
+1
drivers/gpu/drm/i915/gt/intel_rps.c
··· 7 7 #include <drm/i915_drm.h> 8 8 9 9 #include "i915_drv.h" 10 + #include "intel_breadcrumbs.h" 10 11 #include "intel_gt.h" 11 12 #include "intel_gt_clock_utils.h" 12 13 #include "intel_gt_irq.h"
+7 -3
drivers/gpu/drm/i915/gt/mock_engine.c
··· 260 260 261 261 GEM_BUG_ON(timer_pending(&mock->hw_delay)); 262 262 263 + intel_breadcrumbs_free(engine->breadcrumbs); 264 + 263 265 intel_context_unpin(engine->kernel_context); 264 266 intel_context_put(engine->kernel_context); 265 267 266 268 intel_engine_fini_retire(engine); 267 - intel_engine_fini_breadcrumbs(engine); 268 269 } 269 270 270 271 struct intel_engine_cs *mock_engine(struct drm_i915_private *i915, ··· 323 322 struct intel_context *ce; 324 323 325 324 intel_engine_init_active(engine, ENGINE_MOCK); 326 - intel_engine_init_breadcrumbs(engine); 327 325 intel_engine_init_execlists(engine); 328 326 intel_engine_init__pm(engine); 329 327 intel_engine_init_retire(engine); 328 + 329 + engine->breadcrumbs = intel_breadcrumbs_create(NULL); 330 + if (!engine->breadcrumbs) 331 + return -ENOMEM; 330 332 331 333 ce = create_kernel_context(engine); 332 334 if (IS_ERR(ce)) ··· 342 338 return 0; 343 339 344 340 err_breadcrumbs: 345 - intel_engine_fini_breadcrumbs(engine); 341 + intel_breadcrumbs_free(engine->breadcrumbs); 346 342 return -ENOMEM; 347 343 } 348 344
+1
drivers/gpu/drm/i915/i915_irq.c
··· 41 41 #include "display/intel_lpe_audio.h" 42 42 #include "display/intel_psr.h" 43 43 44 + #include "gt/intel_breadcrumbs.h" 44 45 #include "gt/intel_gt.h" 45 46 #include "gt/intel_gt_irq.h" 46 47 #include "gt/intel_gt_pm_irq.h"
+1
drivers/gpu/drm/i915/i915_request.c
··· 31 31 #include <linux/sched/signal.h> 32 32 33 33 #include "gem/i915_gem_context.h" 34 + #include "gt/intel_breadcrumbs.h" 34 35 #include "gt/intel_context.h" 35 36 #include "gt/intel_ring.h" 36 37 #include "gt/intel_rps.h"
-4
drivers/gpu/drm/i915/i915_request.h
··· 361 361 void __i915_request_unsubmit(struct i915_request *request); 362 362 void i915_request_unsubmit(struct i915_request *request); 363 363 364 - /* Note: part of the intel_breadcrumbs family */ 365 - bool i915_request_enable_breadcrumb(struct i915_request *request); 366 - void i915_request_cancel_breadcrumb(struct i915_request *request); 367 - 368 364 long i915_request_wait(struct i915_request *rq, 369 365 unsigned int flags, 370 366 long timeout)