Skip to content

Commit 67b807a

Browse files
committed
drm/i915: Delay disabling the user interrupt for breadcrumbs
A significant cost in setting up a wait is the overhead of enabling the interrupt. As we disable the interrupt whenever the queue of waiters is empty, if we are frequently waiting on alternating batches, we end up re-enabling the interrupt on a frequent basis. We do want to disable the interrupt during normal operations as under high load it may add several thousand interrupts/s - we have been known in the past to occupy whole cores with our interrupt handler after accidentally leaving user interrupts enabled. As a compromise, leave the interrupt enabled until the next IRQ, or the system is idle. This gives a small window for a waiter to keep the interrupt active and not be delayed by having to re-enable the interrupt. v2: Restore hangcheck/missed-irq detection for continuations v3: Be more careful restoring the hangcheck timer after reset v4: Be more careful restoring the fake irq after reset (if required!) v5: Redo changes to intel_engine_wakeup() v6: Factor out __intel_engine_wakeup() v7: Improve commentary for declaring a missed wakeup Signed-off-by: Chris Wilson <[email protected]> Cc: Tvrtko Ursulin <[email protected]> Cc: Mika Kuoppala <[email protected]> Reviewed-by: Tvrtko Ursulin <[email protected]> Link: http://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 19d0a57 commit 67b807a

File tree

4 files changed

+125
-64
lines changed

4 files changed

+125
-64
lines changed

drivers/gpu/drm/i915/i915_gem.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2988,8 +2988,10 @@ i915_gem_idle_work_handler(struct work_struct *work)
29882988
if (wait_for(intel_execlists_idle(dev_priv), 10))
29892989
DRM_ERROR("Timeout waiting for engines to idle\n");
29902990

2991-
for_each_engine(engine, dev_priv, id)
2991+
for_each_engine(engine, dev_priv, id) {
2992+
intel_engine_disarm_breadcrumbs(engine);
29922993
i915_gem_batch_pool_fini(&engine->batch_pool);
2994+
}
29932995

29942996
GEM_BUG_ON(!dev_priv->gt.awake);
29952997
dev_priv->gt.awake = false;

drivers/gpu/drm/i915/i915_irq.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,8 @@ static void notify_ring(struct intel_engine_cs *engine)
10601060
rq = wait->request;
10611061

10621062
wake_up_process(wait->tsk);
1063+
} else {
1064+
__intel_engine_disarm_breadcrumbs(engine);
10631065
}
10641066
spin_unlock(&engine->breadcrumbs.lock);
10651067

drivers/gpu/drm/i915/intel_breadcrumbs.c

Lines changed: 115 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,30 @@
2626

2727
#include "i915_drv.h"
2828

29-
unsigned int intel_engine_wakeup(struct intel_engine_cs *engine)
29+
static unsigned int __intel_breadcrumbs_wakeup(struct intel_breadcrumbs *b)
3030
{
3131
struct intel_wait *wait;
32-
unsigned long flags;
3332
unsigned int result = 0;
3433

35-
spin_lock_irqsave(&engine->breadcrumbs.lock, flags);
36-
wait = engine->breadcrumbs.first_wait;
34+
wait = b->first_wait;
3735
if (wait) {
3836
result = ENGINE_WAKEUP_WAITER;
39-
if (!wake_up_process(wait->tsk))
40-
result |= ENGINE_WAKEUP_ACTIVE;
37+
if (wake_up_process(wait->tsk))
38+
result |= ENGINE_WAKEUP_ASLEEP;
4139
}
42-
spin_unlock_irqrestore(&engine->breadcrumbs.lock, flags);
40+
41+
return result;
42+
}
43+
44+
unsigned int intel_engine_wakeup(struct intel_engine_cs *engine)
45+
{
46+
struct intel_breadcrumbs *b = &engine->breadcrumbs;
47+
unsigned long flags;
48+
unsigned int result;
49+
50+
spin_lock_irqsave(&b->lock, flags);
51+
result = __intel_breadcrumbs_wakeup(b);
52+
spin_unlock_irqrestore(&b->lock, flags);
4353

4454
return result;
4555
}
@@ -54,7 +64,7 @@ static void intel_breadcrumbs_hangcheck(unsigned long data)
5464
struct intel_engine_cs *engine = (struct intel_engine_cs *)data;
5565
struct intel_breadcrumbs *b = &engine->breadcrumbs;
5666

57-
if (!b->irq_enabled)
67+
if (!b->irq_armed)
5868
return;
5969

6070
if (b->hangcheck_interrupts != atomic_read(&engine->irq_count)) {
@@ -63,23 +73,32 @@ static void intel_breadcrumbs_hangcheck(unsigned long data)
6373
return;
6474
}
6575

66-
/* If the waiter was currently running, assume it hasn't had a chance
76+
/* We keep the hangcheck time alive until we disarm the irq, even
77+
* if there are no waiters at present.
78+
*
79+
* If the waiter was currently running, assume it hasn't had a chance
6780
* to process the pending interrupt (e.g, low priority task on a loaded
6881
* system) and wait until it sleeps before declaring a missed interrupt.
82+
*
83+
* If the waiter was asleep (and not even pending a wakeup), then we
84+
* must have missed an interrupt as the GPU has stopped advancing
85+
* but we still have a waiter. Assuming all batches complete within
86+
* DRM_I915_HANGCHECK_JIFFIES [1.5s]!
6987
*/
70-
if (intel_engine_wakeup(engine) & ENGINE_WAKEUP_ACTIVE) {
88+
if (intel_engine_wakeup(engine) & ENGINE_WAKEUP_ASLEEP) {
89+
DRM_DEBUG("Hangcheck timer elapsed... %s idle\n", engine->name);
90+
set_bit(engine->id, &engine->i915->gpu_error.missed_irq_rings);
91+
mod_timer(&engine->breadcrumbs.fake_irq, jiffies + 1);
92+
} else {
7193
mod_timer(&b->hangcheck, wait_timeout());
72-
return;
7394
}
74-
75-
DRM_DEBUG("Hangcheck timer elapsed... %s idle\n", engine->name);
76-
set_bit(engine->id, &engine->i915->gpu_error.missed_irq_rings);
77-
mod_timer(&engine->breadcrumbs.fake_irq, jiffies + 1);
7895
}
7996

8097
static void intel_breadcrumbs_fake_irq(unsigned long data)
8198
{
8299
struct intel_engine_cs *engine = (struct intel_engine_cs *)data;
100+
struct intel_breadcrumbs *b = &engine->breadcrumbs;
101+
unsigned long flags;
83102

84103
/*
85104
* The timer persists in case we cannot enable interrupts,
@@ -88,10 +107,15 @@ static void intel_breadcrumbs_fake_irq(unsigned long data)
88107
* every jiffie in order to kick the oldest waiter to do the
89108
* coherent seqno check.
90109
*/
91-
if (!intel_engine_wakeup(engine))
110+
111+
spin_lock_irqsave(&b->lock, flags);
112+
if (!__intel_breadcrumbs_wakeup(b))
113+
__intel_engine_disarm_breadcrumbs(engine);
114+
spin_unlock_irqrestore(&b->lock, flags);
115+
if (!b->irq_armed)
92116
return;
93117

94-
mod_timer(&engine->breadcrumbs.fake_irq, jiffies + 1);
118+
mod_timer(&b->fake_irq, jiffies + 1);
95119

96120
/* Ensure that even if the GPU hangs, we get woken up.
97121
*
@@ -127,6 +151,42 @@ static void irq_disable(struct intel_engine_cs *engine)
127151
spin_unlock(&engine->i915->irq_lock);
128152
}
129153

154+
void __intel_engine_disarm_breadcrumbs(struct intel_engine_cs *engine)
155+
{
156+
struct intel_breadcrumbs *b = &engine->breadcrumbs;
157+
158+
assert_spin_locked(&b->lock);
159+
160+
if (b->irq_enabled) {
161+
irq_disable(engine);
162+
b->irq_enabled = false;
163+
}
164+
165+
b->irq_armed = false;
166+
}
167+
168+
void intel_engine_disarm_breadcrumbs(struct intel_engine_cs *engine)
169+
{
170+
struct intel_breadcrumbs *b = &engine->breadcrumbs;
171+
unsigned long flags;
172+
173+
if (!b->irq_armed)
174+
return;
175+
176+
spin_lock_irqsave(&b->lock, flags);
177+
178+
/* We only disarm the irq when we are idle (all requests completed),
179+
* so if there remains a sleeping waiter, it missed the request
180+
* completion.
181+
*/
182+
if (__intel_breadcrumbs_wakeup(b) & ENGINE_WAKEUP_ASLEEP)
183+
set_bit(engine->id, &engine->i915->gpu_error.missed_irq_rings);
184+
185+
__intel_engine_disarm_breadcrumbs(engine);
186+
187+
spin_unlock_irqrestore(&b->lock, flags);
188+
}
189+
130190
static bool use_fake_irq(const struct intel_breadcrumbs *b)
131191
{
132192
const struct intel_engine_cs *engine =
@@ -144,16 +204,33 @@ static bool use_fake_irq(const struct intel_breadcrumbs *b)
144204
return atomic_read(&engine->irq_count) == b->hangcheck_interrupts;
145205
}
146206

207+
static void enable_fake_irq(struct intel_breadcrumbs *b)
208+
{
209+
/* Ensure we never sleep indefinitely */
210+
if (!b->irq_enabled || use_fake_irq(b))
211+
mod_timer(&b->fake_irq, jiffies + 1);
212+
else
213+
mod_timer(&b->hangcheck, wait_timeout());
214+
}
215+
147216
static void __intel_breadcrumbs_enable_irq(struct intel_breadcrumbs *b)
148217
{
149218
struct intel_engine_cs *engine =
150219
container_of(b, struct intel_engine_cs, breadcrumbs);
151220
struct drm_i915_private *i915 = engine->i915;
152221

153222
assert_spin_locked(&b->lock);
154-
if (b->rpm_wakelock)
223+
if (b->irq_armed)
155224
return;
156225

226+
/* The breadcrumb irq will be disarmed on the interrupt after the
227+
* waiters are signaled. This gives us a single interrupt window in
228+
* which we can add a new waiter and avoid the cost of re-enabling
229+
* the irq.
230+
*/
231+
b->irq_armed = true;
232+
GEM_BUG_ON(b->irq_enabled);
233+
157234
if (I915_SELFTEST_ONLY(b->mock)) {
158235
/* For our mock objects we want to avoid interaction
159236
* with the real hardware (which is not set up). So
@@ -162,17 +239,15 @@ static void __intel_breadcrumbs_enable_irq(struct intel_breadcrumbs *b)
162239
* implementation to call intel_engine_wakeup()
163240
* itself when it wants to simulate a user interrupt,
164241
*/
165-
b->rpm_wakelock = true;
166242
return;
167243
}
168244

169245
/* Since we are waiting on a request, the GPU should be busy
170-
* and should have its own rpm reference. For completeness,
171-
* record an rpm reference for ourselves to cover the
172-
* interrupt we unmask.
246+
* and should have its own rpm reference. This is tracked
247+
* by i915->gt.awake, we can forgo holding our own wakref
248+
* for the interrupt as before i915->gt.awake is released (when
249+
* the driver is idle) we disarm the breadcrumbs.
173250
*/
174-
intel_runtime_pm_get_noresume(i915);
175-
b->rpm_wakelock = true;
176251

177252
/* No interrupts? Kick the waiter every jiffie! */
178253
if (intel_irqs_enabled(i915)) {
@@ -181,34 +256,7 @@ static void __intel_breadcrumbs_enable_irq(struct intel_breadcrumbs *b)
181256
b->irq_enabled = true;
182257
}
183258

184-
/* Ensure we never sleep indefinitely */
185-
if (!b->irq_enabled || use_fake_irq(b))
186-
mod_timer(&b->fake_irq, jiffies + 1);
187-
else
188-
mod_timer(&b->hangcheck, wait_timeout());
189-
}
190-
191-
static void __intel_breadcrumbs_disable_irq(struct intel_breadcrumbs *b)
192-
{
193-
struct intel_engine_cs *engine =
194-
container_of(b, struct intel_engine_cs, breadcrumbs);
195-
196-
assert_spin_locked(&b->lock);
197-
if (!b->rpm_wakelock)
198-
return;
199-
200-
if (I915_SELFTEST_ONLY(b->mock)) {
201-
b->rpm_wakelock = false;
202-
return;
203-
}
204-
205-
if (b->irq_enabled) {
206-
irq_disable(engine);
207-
b->irq_enabled = false;
208-
}
209-
210-
intel_runtime_pm_put(engine->i915);
211-
b->rpm_wakelock = false;
259+
enable_fake_irq(b);
212260
}
213261

214262
static inline struct intel_wait *to_wait(struct rb_node *node)
@@ -430,7 +478,6 @@ static void __intel_engine_remove_wait(struct intel_engine_cs *engine,
430478
wake_up_process(b->first_wait->tsk);
431479
} else {
432480
b->first_wait = NULL;
433-
__intel_breadcrumbs_disable_irq(b);
434481
}
435482
} else {
436483
GEM_BUG_ON(rb_first(&b->waiters) == &wait->node);
@@ -722,15 +769,22 @@ void intel_engine_reset_breadcrumbs(struct intel_engine_cs *engine)
722769
cancel_fake_irq(engine);
723770
spin_lock_irq(&b->lock);
724771

725-
__intel_breadcrumbs_disable_irq(b);
726-
if (intel_engine_has_waiter(engine)) {
727-
__intel_breadcrumbs_enable_irq(b);
728-
if (test_bit(ENGINE_IRQ_BREADCRUMB, &engine->irq_posted))
729-
wake_up_process(b->first_wait->tsk);
730-
} else {
731-
/* sanitize the IMR and unmask any auxiliary interrupts */
772+
if (b->irq_enabled)
773+
irq_enable(engine);
774+
else
732775
irq_disable(engine);
733-
}
776+
777+
/* We set the IRQ_BREADCRUMB bit when we enable the irq presuming the
778+
* GPU is active and may have already executed the MI_USER_INTERRUPT
779+
* before the CPU is ready to receive. However, the engine is currently
780+
* idle (we haven't started it yet), there is no possibility for a
781+
* missed interrupt as we enabled the irq and so we can clear the
782+
* immediate wakeup (until a real interrupt arrives for the waiter).
783+
*/
784+
clear_bit(ENGINE_IRQ_BREADCRUMB, &engine->irq_posted);
785+
786+
if (b->irq_armed)
787+
enable_fake_irq(b);
734788

735789
spin_unlock_irq(&b->lock);
736790
}

drivers/gpu/drm/i915/intel_ringbuffer.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ struct intel_engine_cs {
246246

247247
unsigned int hangcheck_interrupts;
248248

249+
bool irq_armed : 1;
249250
bool irq_enabled : 1;
250-
bool rpm_wakelock : 1;
251251
I915_SELFTEST_DECLARE(bool mock : 1);
252252
} breadcrumbs;
253253

@@ -644,7 +644,10 @@ static inline bool intel_engine_has_waiter(const struct intel_engine_cs *engine)
644644

645645
unsigned int intel_engine_wakeup(struct intel_engine_cs *engine);
646646
#define ENGINE_WAKEUP_WAITER BIT(0)
647-
#define ENGINE_WAKEUP_ACTIVE BIT(1)
647+
#define ENGINE_WAKEUP_ASLEEP BIT(1)
648+
649+
void __intel_engine_disarm_breadcrumbs(struct intel_engine_cs *engine);
650+
void intel_engine_disarm_breadcrumbs(struct intel_engine_cs *engine);
648651

649652
void intel_engine_reset_breadcrumbs(struct intel_engine_cs *engine);
650653
void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine);

0 commit comments

Comments
 (0)