Skip to content

Commit 08b49e1

Browse files
icklerodrigovivi
authored andcommitted
drm/i915/gt: Defer enabling the breadcrumb interrupt to after submission
Move the register slow register write and readback from out of the critical path for execlists submission and delay it until the following worker, shaving off around 200us. Note that the same signal_irq_work() is allowed to run concurrently on each CPU (but it will only be queued once, once running though it can be requeued and reexecuted) so we have to remember to lock the global interactions as we cannot rely on the signal_irq_work() itself providing the serialisation (in constrast to a tasklet). By pushing the arm/disarm into the central signaling worker we can close the race for disarming the interrupt (and dropping its associated GT wakeref) on parking the engine. If we loose the race, that GT wakeref may be held indefinitely, preventing the machine from sleeping while the GPU is ostensibly idle. v2: Move the self-arming parking of the signal_irq_work to a flush of the irq-work from intel_breadcrumbs_park(). Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/2271 Fixes: e230056 ("drm/i915/gt: Hold context/request reference while breadcrumbs are active") Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20201123113717.20500-1-chris@chris-wilson.co.uk (cherry picked from commit 9d5612c) Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
1 parent b5e420f commit 08b49e1

1 file changed

Lines changed: 70 additions & 39 deletions

File tree

drivers/gpu/drm/i915/gt/intel_breadcrumbs.c

Lines changed: 70 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,21 @@
3030
#include "i915_trace.h"
3131
#include "intel_breadcrumbs.h"
3232
#include "intel_context.h"
33+
#include "intel_engine_pm.h"
3334
#include "intel_gt_pm.h"
3435
#include "intel_gt_requests.h"
3536

36-
static void irq_enable(struct intel_engine_cs *engine)
37+
static bool irq_enable(struct intel_engine_cs *engine)
3738
{
3839
if (!engine->irq_enable)
39-
return;
40+
return false;
4041

4142
/* Caller disables interrupts */
4243
spin_lock(&engine->gt->irq_lock);
4344
engine->irq_enable(engine);
4445
spin_unlock(&engine->gt->irq_lock);
46+
47+
return true;
4548
}
4649

4750
static void irq_disable(struct intel_engine_cs *engine)
@@ -57,12 +60,11 @@ static void irq_disable(struct intel_engine_cs *engine)
5760

5861
static void __intel_breadcrumbs_arm_irq(struct intel_breadcrumbs *b)
5962
{
60-
lockdep_assert_held(&b->irq_lock);
61-
62-
if (!b->irq_engine || b->irq_armed)
63-
return;
64-
65-
if (!intel_gt_pm_get_if_awake(b->irq_engine->gt))
63+
/*
64+
* Since we are waiting on a request, the GPU should be busy
65+
* and should have its own rpm reference.
66+
*/
67+
if (GEM_WARN_ON(!intel_gt_pm_get_if_awake(b->irq_engine->gt)))
6668
return;
6769

6870
/*
@@ -73,25 +75,24 @@ static void __intel_breadcrumbs_arm_irq(struct intel_breadcrumbs *b)
7375
*/
7476
WRITE_ONCE(b->irq_armed, true);
7577

76-
/*
77-
* Since we are waiting on a request, the GPU should be busy
78-
* and should have its own rpm reference. This is tracked
79-
* by i915->gt.awake, we can forgo holding our own wakref
80-
* for the interrupt as before i915->gt.awake is released (when
81-
* the driver is idle) we disarm the breadcrumbs.
82-
*/
83-
84-
if (!b->irq_enabled++)
85-
irq_enable(b->irq_engine);
78+
/* Requests may have completed before we could enable the interrupt. */
79+
if (!b->irq_enabled++ && irq_enable(b->irq_engine))
80+
irq_work_queue(&b->irq_work);
8681
}
8782

88-
static void __intel_breadcrumbs_disarm_irq(struct intel_breadcrumbs *b)
83+
static void intel_breadcrumbs_arm_irq(struct intel_breadcrumbs *b)
8984
{
90-
lockdep_assert_held(&b->irq_lock);
91-
92-
if (!b->irq_engine || !b->irq_armed)
85+
if (!b->irq_engine)
9386
return;
9487

88+
spin_lock(&b->irq_lock);
89+
if (!b->irq_armed)
90+
__intel_breadcrumbs_arm_irq(b);
91+
spin_unlock(&b->irq_lock);
92+
}
93+
94+
static void __intel_breadcrumbs_disarm_irq(struct intel_breadcrumbs *b)
95+
{
9596
GEM_BUG_ON(!b->irq_enabled);
9697
if (!--b->irq_enabled)
9798
irq_disable(b->irq_engine);
@@ -105,8 +106,6 @@ static void add_signaling_context(struct intel_breadcrumbs *b,
105106
{
106107
intel_context_get(ce);
107108
list_add_tail(&ce->signal_link, &b->signalers);
108-
if (list_is_first(&ce->signal_link, &b->signalers))
109-
__intel_breadcrumbs_arm_irq(b);
110109
}
111110

112111
static void remove_signaling_context(struct intel_breadcrumbs *b,
@@ -197,7 +196,32 @@ static void signal_irq_work(struct irq_work *work)
197196

198197
spin_lock(&b->irq_lock);
199198

200-
if (list_empty(&b->signalers))
199+
/*
200+
* Keep the irq armed until the interrupt after all listeners are gone.
201+
*
202+
* Enabling/disabling the interrupt is rather costly, roughly a couple
203+
* of hundred microseconds. If we are proactive and enable/disable
204+
* the interrupt around every request that wants a breadcrumb, we
205+
* quickly drown in the extra orders of magnitude of latency imposed
206+
* on request submission.
207+
*
208+
* So we try to be lazy, and keep the interrupts enabled until no
209+
* more listeners appear within a breadcrumb interrupt interval (that
210+
* is until a request completes that no one cares about). The
211+
* observation is that listeners come in batches, and will often
212+
* listen to a bunch of requests in succession. Though note on icl+,
213+
* interrupts are always enabled due to concerns with rc6 being
214+
* dysfunctional with per-engine interrupt masking.
215+
*
216+
* We also try to avoid raising too many interrupts, as they may
217+
* be generated by userspace batches and it is unfortunately rather
218+
* too easy to drown the CPU under a flood of GPU interrupts. Thus
219+
* whenever no one appears to be listening, we turn off the interrupts.
220+
* Fewer interrupts should conserve power -- at the very least, fewer
221+
* interrupt draw less ire from other users of the system and tools
222+
* like powertop.
223+
*/
224+
if (b->irq_armed && list_empty(&b->signalers))
201225
__intel_breadcrumbs_disarm_irq(b);
202226

203227
list_splice_init(&b->signaled_requests, &signal);
@@ -251,6 +275,9 @@ static void signal_irq_work(struct irq_work *work)
251275

252276
i915_request_put(rq);
253277
}
278+
279+
if (!READ_ONCE(b->irq_armed) && !list_empty(&b->signalers))
280+
intel_breadcrumbs_arm_irq(b);
254281
}
255282

256283
struct intel_breadcrumbs *
@@ -292,21 +319,22 @@ void intel_breadcrumbs_reset(struct intel_breadcrumbs *b)
292319

293320
void intel_breadcrumbs_park(struct intel_breadcrumbs *b)
294321
{
295-
unsigned long flags;
296-
297-
if (!READ_ONCE(b->irq_armed))
298-
return;
299-
300-
spin_lock_irqsave(&b->irq_lock, flags);
301-
__intel_breadcrumbs_disarm_irq(b);
302-
spin_unlock_irqrestore(&b->irq_lock, flags);
303-
304-
if (!list_empty(&b->signalers))
305-
irq_work_queue(&b->irq_work);
322+
/* Kick the work once more to drain the signalers */
323+
irq_work_sync(&b->irq_work);
324+
while (unlikely(READ_ONCE(b->irq_armed))) {
325+
local_irq_disable();
326+
signal_irq_work(&b->irq_work);
327+
local_irq_enable();
328+
cond_resched();
329+
}
330+
GEM_BUG_ON(!list_empty(&b->signalers));
306331
}
307332

308333
void intel_breadcrumbs_free(struct intel_breadcrumbs *b)
309334
{
335+
irq_work_sync(&b->irq_work);
336+
GEM_BUG_ON(!list_empty(&b->signalers));
337+
GEM_BUG_ON(b->irq_armed);
310338
kfree(b);
311339
}
312340

@@ -362,9 +390,12 @@ static void insert_breadcrumb(struct i915_request *rq,
362390
GEM_BUG_ON(!check_signal_order(ce, rq));
363391
set_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags);
364392

365-
/* Check after attaching to irq, interrupt may have already fired. */
366-
if (__request_completed(rq))
367-
irq_work_queue(&b->irq_work);
393+
/*
394+
* Defer enabling the interrupt to after HW submission and recheck
395+
* the request as it may have completed and raised the interrupt as
396+
* we were attaching it into the lists.
397+
*/
398+
irq_work_queue(&b->irq_work);
368399
}
369400

370401
bool i915_request_enable_breadcrumb(struct i915_request *rq)

0 commit comments

Comments
 (0)