26
26
27
27
#include "i915_drv.h"
28
28
29
- unsigned int intel_engine_wakeup (struct intel_engine_cs * engine )
29
+ static unsigned int __intel_breadcrumbs_wakeup (struct intel_breadcrumbs * b )
30
30
{
31
31
struct intel_wait * wait ;
32
- unsigned long flags ;
33
32
unsigned int result = 0 ;
34
33
35
- spin_lock_irqsave (& engine -> breadcrumbs .lock , flags );
36
- wait = engine -> breadcrumbs .first_wait ;
34
+ wait = b -> first_wait ;
37
35
if (wait ) {
38
36
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 ;
41
39
}
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 );
43
53
44
54
return result ;
45
55
}
@@ -54,7 +64,7 @@ static void intel_breadcrumbs_hangcheck(unsigned long data)
54
64
struct intel_engine_cs * engine = (struct intel_engine_cs * )data ;
55
65
struct intel_breadcrumbs * b = & engine -> breadcrumbs ;
56
66
57
- if (!b -> irq_enabled )
67
+ if (!b -> irq_armed )
58
68
return ;
59
69
60
70
if (b -> hangcheck_interrupts != atomic_read (& engine -> irq_count )) {
@@ -63,23 +73,32 @@ static void intel_breadcrumbs_hangcheck(unsigned long data)
63
73
return ;
64
74
}
65
75
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
67
80
* to process the pending interrupt (e.g, low priority task on a loaded
68
81
* 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]!
69
87
*/
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 {
71
93
mod_timer (& b -> hangcheck , wait_timeout ());
72
- return ;
73
94
}
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 );
78
95
}
79
96
80
97
static void intel_breadcrumbs_fake_irq (unsigned long data )
81
98
{
82
99
struct intel_engine_cs * engine = (struct intel_engine_cs * )data ;
100
+ struct intel_breadcrumbs * b = & engine -> breadcrumbs ;
101
+ unsigned long flags ;
83
102
84
103
/*
85
104
* The timer persists in case we cannot enable interrupts,
@@ -88,10 +107,15 @@ static void intel_breadcrumbs_fake_irq(unsigned long data)
88
107
* every jiffie in order to kick the oldest waiter to do the
89
108
* coherent seqno check.
90
109
*/
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 )
92
116
return ;
93
117
94
- mod_timer (& engine -> breadcrumbs . fake_irq , jiffies + 1 );
118
+ mod_timer (& b -> fake_irq , jiffies + 1 );
95
119
96
120
/* Ensure that even if the GPU hangs, we get woken up.
97
121
*
@@ -127,6 +151,42 @@ static void irq_disable(struct intel_engine_cs *engine)
127
151
spin_unlock (& engine -> i915 -> irq_lock );
128
152
}
129
153
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
+
130
190
static bool use_fake_irq (const struct intel_breadcrumbs * b )
131
191
{
132
192
const struct intel_engine_cs * engine =
@@ -144,16 +204,33 @@ static bool use_fake_irq(const struct intel_breadcrumbs *b)
144
204
return atomic_read (& engine -> irq_count ) == b -> hangcheck_interrupts ;
145
205
}
146
206
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
+
147
216
static void __intel_breadcrumbs_enable_irq (struct intel_breadcrumbs * b )
148
217
{
149
218
struct intel_engine_cs * engine =
150
219
container_of (b , struct intel_engine_cs , breadcrumbs );
151
220
struct drm_i915_private * i915 = engine -> i915 ;
152
221
153
222
assert_spin_locked (& b -> lock );
154
- if (b -> rpm_wakelock )
223
+ if (b -> irq_armed )
155
224
return ;
156
225
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
+
157
234
if (I915_SELFTEST_ONLY (b -> mock )) {
158
235
/* For our mock objects we want to avoid interaction
159
236
* with the real hardware (which is not set up). So
@@ -162,17 +239,15 @@ static void __intel_breadcrumbs_enable_irq(struct intel_breadcrumbs *b)
162
239
* implementation to call intel_engine_wakeup()
163
240
* itself when it wants to simulate a user interrupt,
164
241
*/
165
- b -> rpm_wakelock = true;
166
242
return ;
167
243
}
168
244
169
245
/* 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.
173
250
*/
174
- intel_runtime_pm_get_noresume (i915 );
175
- b -> rpm_wakelock = true;
176
251
177
252
/* No interrupts? Kick the waiter every jiffie! */
178
253
if (intel_irqs_enabled (i915 )) {
@@ -181,34 +256,7 @@ static void __intel_breadcrumbs_enable_irq(struct intel_breadcrumbs *b)
181
256
b -> irq_enabled = true;
182
257
}
183
258
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 );
212
260
}
213
261
214
262
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,
430
478
wake_up_process (b -> first_wait -> tsk );
431
479
} else {
432
480
b -> first_wait = NULL ;
433
- __intel_breadcrumbs_disable_irq (b );
434
481
}
435
482
} else {
436
483
GEM_BUG_ON (rb_first (& b -> waiters ) == & wait -> node );
@@ -722,15 +769,22 @@ void intel_engine_reset_breadcrumbs(struct intel_engine_cs *engine)
722
769
cancel_fake_irq (engine );
723
770
spin_lock_irq (& b -> lock );
724
771
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
732
775
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 );
734
788
735
789
spin_unlock_irq (& b -> lock );
736
790
}
0 commit comments