aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2017-10-23 17:32:36 -0400
committerJani Nikula <jani.nikula@intel.com>2017-11-09 09:18:11 -0500
commit5d266692372dfb412150006df420185b666658f9 (patch)
treee017836c5220dcb28991ca8b52212ab4281411be
parent8a6fb5b5823d863b07f670dc9e791d4622d5b7e9 (diff)
drm/i915: Filter out spurious execlists context-switch interrupts
Back in commit a4b2b01523a8 ("drm/i915: Don't mark an execlists context-switch when idle") we noticed the presence of late context-switch interrupts. We were able to filter those out by looking at whether the ELSP remained active, but in commit beecec901790 ("drm/i915/execlists: Preemption!") that became problematic as we now anticipate receiving a context-switch event for preemption while ELSP may be empty. To restore the spurious interrupt suppression, add a counter for the expected number of pending context-switches and skip if we do not need to handle this interrupt to make forward progress. v2: Don't forget to switch on for preempt. v3: Reduce the counter to a on/off boolean tracker. Declare the HW as active when we first submit, and idle after the final completion event (with which we confirm the HW says it is idle), and track each source of activity separately. With a finite number of sources, it should aide us in debugging which gets stuck. Fixes: beecec901790 ("drm/i915/execlists: Preemption!") Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Cc: Michal Winiarski <michal.winiarski@intel.com> Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com> Cc: Arkadiusz Hiler <arkadiusz.hiler@intel.com> Cc: Mika Kuoppala <mika.kuoppala@intel.com> Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20171023213237.26536-3-chris@chris-wilson.co.uk Reviewed-by: Mika Kuoppala <mika.kuoppala@linux.intel.com> (cherry picked from commit 4a118ecbe99c93cf9f9582e83a88d03f18d6cb84) Signed-off-by: Jani Nikula <jani.nikula@intel.com>
-rw-r--r--drivers/gpu/drm/i915/i915_guc_submission.c3
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c6
-rw-r--r--drivers/gpu/drm/i915/intel_engine_cs.c5
-rw-r--r--drivers/gpu/drm/i915/intel_lrc.c27
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.h34
5 files changed, 62 insertions, 13 deletions
diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c
index a2e8114b739d..f84c267728fd 100644
--- a/drivers/gpu/drm/i915/i915_guc_submission.c
+++ b/drivers/gpu/drm/i915/i915_guc_submission.c
@@ -610,6 +610,7 @@ done:
610 execlists->first = rb; 610 execlists->first = rb;
611 if (submit) { 611 if (submit) {
612 port_assign(port, last); 612 port_assign(port, last);
613 execlists_set_active(execlists, EXECLISTS_ACTIVE_USER);
613 i915_guc_submit(engine); 614 i915_guc_submit(engine);
614 } 615 }
615 spin_unlock_irq(&engine->timeline->lock); 616 spin_unlock_irq(&engine->timeline->lock);
@@ -633,6 +634,8 @@ static void i915_guc_irq_handler(unsigned long data)
633 634
634 rq = port_request(&port[0]); 635 rq = port_request(&port[0]);
635 } 636 }
637 if (!rq)
638 execlists_clear_active(execlists, EXECLISTS_ACTIVE_USER);
636 639
637 if (!port_isset(last_port)) 640 if (!port_isset(last_port))
638 i915_guc_dequeue(engine); 641 i915_guc_dequeue(engine);
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index b1296a55c1e4..f8205841868b 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1388,8 +1388,10 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift)
1388 bool tasklet = false; 1388 bool tasklet = false;
1389 1389
1390 if (iir & (GT_CONTEXT_SWITCH_INTERRUPT << test_shift)) { 1390 if (iir & (GT_CONTEXT_SWITCH_INTERRUPT << test_shift)) {
1391 __set_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted); 1391 if (READ_ONCE(engine->execlists.active)) {
1392 tasklet = true; 1392 __set_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
1393 tasklet = true;
1394 }
1393 } 1395 }
1394 1396
1395 if (iir & (GT_RENDER_USER_INTERRUPT << test_shift)) { 1397 if (iir & (GT_RENDER_USER_INTERRUPT << test_shift)) {
diff --git a/drivers/gpu/drm/i915/intel_engine_cs.c b/drivers/gpu/drm/i915/intel_engine_cs.c
index a47a9c6bea52..ab5bf4e2e28e 100644
--- a/drivers/gpu/drm/i915/intel_engine_cs.c
+++ b/drivers/gpu/drm/i915/intel_engine_cs.c
@@ -1548,8 +1548,8 @@ bool intel_engine_is_idle(struct intel_engine_cs *engine)
1548 if (test_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted)) 1548 if (test_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted))
1549 return false; 1549 return false;
1550 1550
1551 /* Both ports drained, no more ELSP submission? */ 1551 /* Waiting to drain ELSP? */
1552 if (port_request(&engine->execlists.port[0])) 1552 if (READ_ONCE(engine->execlists.active))
1553 return false; 1553 return false;
1554 1554
1555 /* ELSP is empty, but there are ready requests? */ 1555 /* ELSP is empty, but there are ready requests? */
@@ -1749,6 +1749,7 @@ void intel_engine_dump(struct intel_engine_cs *engine, struct drm_printer *m)
1749 idx); 1749 idx);
1750 } 1750 }
1751 } 1751 }
1752 drm_printf(m, "\t\tHW active? 0x%x\n", execlists->active);
1752 rcu_read_unlock(); 1753 rcu_read_unlock();
1753 } else if (INTEL_GEN(dev_priv) > 6) { 1754 } else if (INTEL_GEN(dev_priv) > 6) {
1754 drm_printf(m, "\tPP_DIR_BASE: 0x%08x\n", 1755 drm_printf(m, "\tPP_DIR_BASE: 0x%08x\n",
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 7f45dd7dc3e5..5b87d5284a84 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -575,7 +575,8 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
575 * the state of the GPU is known (idle). 575 * the state of the GPU is known (idle).
576 */ 576 */
577 inject_preempt_context(engine); 577 inject_preempt_context(engine);
578 execlists->preempt = true; 578 execlists_set_active(execlists,
579 EXECLISTS_ACTIVE_PREEMPT);
579 goto unlock; 580 goto unlock;
580 } else { 581 } else {
581 /* 582 /*
@@ -683,8 +684,10 @@ done:
683unlock: 684unlock:
684 spin_unlock_irq(&engine->timeline->lock); 685 spin_unlock_irq(&engine->timeline->lock);
685 686
686 if (submit) 687 if (submit) {
688 execlists_set_active(execlists, EXECLISTS_ACTIVE_USER);
687 execlists_submit_ports(engine); 689 execlists_submit_ports(engine);
690 }
688} 691}
689 692
690static void 693static void
@@ -696,6 +699,7 @@ execlist_cancel_port_requests(struct intel_engine_execlists *execlists)
696 while (num_ports-- && port_isset(port)) { 699 while (num_ports-- && port_isset(port)) {
697 struct drm_i915_gem_request *rq = port_request(port); 700 struct drm_i915_gem_request *rq = port_request(port);
698 701
702 GEM_BUG_ON(!execlists->active);
699 execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_PREEMPTED); 703 execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_PREEMPTED);
700 i915_gem_request_put(rq); 704 i915_gem_request_put(rq);
701 705
@@ -861,15 +865,21 @@ static void intel_lrc_irq_handler(unsigned long data)
861 unwind_incomplete_requests(engine); 865 unwind_incomplete_requests(engine);
862 spin_unlock_irq(&engine->timeline->lock); 866 spin_unlock_irq(&engine->timeline->lock);
863 867
864 GEM_BUG_ON(!execlists->preempt); 868 GEM_BUG_ON(!execlists_is_active(execlists,
865 execlists->preempt = false; 869 EXECLISTS_ACTIVE_PREEMPT));
870 execlists_clear_active(execlists,
871 EXECLISTS_ACTIVE_PREEMPT);
866 continue; 872 continue;
867 } 873 }
868 874
869 if (status & GEN8_CTX_STATUS_PREEMPTED && 875 if (status & GEN8_CTX_STATUS_PREEMPTED &&
870 execlists->preempt) 876 execlists_is_active(execlists,
877 EXECLISTS_ACTIVE_PREEMPT))
871 continue; 878 continue;
872 879
880 GEM_BUG_ON(!execlists_is_active(execlists,
881 EXECLISTS_ACTIVE_USER));
882
873 /* Check the context/desc id for this event matches */ 883 /* Check the context/desc id for this event matches */
874 GEM_DEBUG_BUG_ON(buf[2 * head + 1] != port->context_id); 884 GEM_DEBUG_BUG_ON(buf[2 * head + 1] != port->context_id);
875 885
@@ -892,6 +902,9 @@ static void intel_lrc_irq_handler(unsigned long data)
892 /* After the final element, the hw should be idle */ 902 /* After the final element, the hw should be idle */
893 GEM_BUG_ON(port_count(port) == 0 && 903 GEM_BUG_ON(port_count(port) == 0 &&
894 !(status & GEN8_CTX_STATUS_ACTIVE_IDLE)); 904 !(status & GEN8_CTX_STATUS_ACTIVE_IDLE));
905 if (port_count(port) == 0)
906 execlists_clear_active(execlists,
907 EXECLISTS_ACTIVE_USER);
895 } 908 }
896 909
897 if (head != execlists->csb_head) { 910 if (head != execlists->csb_head) {
@@ -901,7 +914,7 @@ static void intel_lrc_irq_handler(unsigned long data)
901 } 914 }
902 } 915 }
903 916
904 if (!execlists->preempt) 917 if (!execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT))
905 execlists_dequeue(engine); 918 execlists_dequeue(engine);
906 919
907 intel_uncore_forcewake_put(dev_priv, execlists->fw_domains); 920 intel_uncore_forcewake_put(dev_priv, execlists->fw_domains);
@@ -1460,7 +1473,7 @@ static int gen8_init_common_ring(struct intel_engine_cs *engine)
1460 GT_CONTEXT_SWITCH_INTERRUPT << engine->irq_shift); 1473 GT_CONTEXT_SWITCH_INTERRUPT << engine->irq_shift);
1461 clear_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted); 1474 clear_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
1462 execlists->csb_head = -1; 1475 execlists->csb_head = -1;
1463 execlists->preempt = false; 1476 execlists->active = 0;
1464 1477
1465 /* After a GPU reset, we may have requests to replay */ 1478 /* After a GPU reset, we may have requests to replay */
1466 if (!i915_modparams.enable_guc_submission && execlists->first) 1479 if (!i915_modparams.enable_guc_submission && execlists->first)
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 17186f067408..6a42ed618a28 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -241,9 +241,17 @@ struct intel_engine_execlists {
241 } port[EXECLIST_MAX_PORTS]; 241 } port[EXECLIST_MAX_PORTS];
242 242
243 /** 243 /**
244 * @preempt: are we currently handling a preempting context switch? 244 * @active: is the HW active? We consider the HW as active after
245 * submitting any context for execution and until we have seen the
246 * last context completion event. After that, we do not expect any
247 * more events until we submit, and so can park the HW.
248 *
249 * As we have a small number of different sources from which we feed
250 * the HW, we track the state of each inside a single bitfield.
245 */ 251 */
246 bool preempt; 252 unsigned int active;
253#define EXECLISTS_ACTIVE_USER 0
254#define EXECLISTS_ACTIVE_PREEMPT 1
247 255
248 /** 256 /**
249 * @port_mask: number of execlist ports - 1 257 * @port_mask: number of execlist ports - 1
@@ -525,6 +533,27 @@ struct intel_engine_cs {
525 u32 (*get_cmd_length_mask)(u32 cmd_header); 533 u32 (*get_cmd_length_mask)(u32 cmd_header);
526}; 534};
527 535
536static inline void
537execlists_set_active(struct intel_engine_execlists *execlists,
538 unsigned int bit)
539{
540 __set_bit(bit, (unsigned long *)&execlists->active);
541}
542
543static inline void
544execlists_clear_active(struct intel_engine_execlists *execlists,
545 unsigned int bit)
546{
547 __clear_bit(bit, (unsigned long *)&execlists->active);
548}
549
550static inline bool
551execlists_is_active(const struct intel_engine_execlists *execlists,
552 unsigned int bit)
553{
554 return test_bit(bit, (unsigned long *)&execlists->active);
555}
556
528static inline unsigned int 557static inline unsigned int
529execlists_num_ports(const struct intel_engine_execlists * const execlists) 558execlists_num_ports(const struct intel_engine_execlists * const execlists)
530{ 559{
@@ -538,6 +567,7 @@ execlists_port_complete(struct intel_engine_execlists * const execlists,
538 const unsigned int m = execlists->port_mask; 567 const unsigned int m = execlists->port_mask;
539 568
540 GEM_BUG_ON(port_index(port, execlists) != 0); 569 GEM_BUG_ON(port_index(port, execlists) != 0);
570 GEM_BUG_ON(!execlists_is_active(execlists, EXECLISTS_ACTIVE_USER));
541 571
542 memmove(port, port + 1, m * sizeof(struct execlist_port)); 572 memmove(port, port + 1, m * sizeof(struct execlist_port));
543 memset(port + m, 0, sizeof(struct execlist_port)); 573 memset(port + m, 0, sizeof(struct execlist_port));