.. | .. |
---|
26 | 26 | * |
---|
27 | 27 | */ |
---|
28 | 28 | |
---|
29 | | -#include <drm/drmP.h> |
---|
30 | | -#include <drm/i915_drm.h> |
---|
| 29 | +#include "gem/i915_gem_context.h" |
---|
| 30 | +#include "gt/intel_gt_requests.h" |
---|
31 | 31 | |
---|
32 | 32 | #include "i915_drv.h" |
---|
33 | | -#include "intel_drv.h" |
---|
34 | 33 | #include "i915_trace.h" |
---|
35 | 34 | |
---|
36 | 35 | I915_SELFTEST_DECLARE(static struct igt_evict_ctl { |
---|
37 | 36 | bool fail_if_busy:1; |
---|
38 | 37 | } igt_evict_ctl;) |
---|
39 | 38 | |
---|
40 | | -static bool ggtt_is_idle(struct drm_i915_private *i915) |
---|
| 39 | +static int ggtt_flush(struct intel_gt *gt) |
---|
41 | 40 | { |
---|
42 | | - struct intel_engine_cs *engine; |
---|
43 | | - enum intel_engine_id id; |
---|
44 | | - |
---|
45 | | - if (i915->gt.active_requests) |
---|
46 | | - return false; |
---|
47 | | - |
---|
48 | | - for_each_engine(engine, i915, id) { |
---|
49 | | - if (!intel_engine_has_kernel_context(engine)) |
---|
50 | | - return false; |
---|
51 | | - } |
---|
52 | | - |
---|
53 | | - return true; |
---|
54 | | -} |
---|
55 | | - |
---|
56 | | -static int ggtt_flush(struct drm_i915_private *i915) |
---|
57 | | -{ |
---|
58 | | - int err; |
---|
59 | | - |
---|
60 | | - /* Not everything in the GGTT is tracked via vma (otherwise we |
---|
| 41 | + /* |
---|
| 42 | + * Not everything in the GGTT is tracked via vma (otherwise we |
---|
61 | 43 | * could evict as required with minimal stalling) so we are forced |
---|
62 | 44 | * to idle the GPU and explicitly retire outstanding requests in |
---|
63 | 45 | * the hopes that we can then remove contexts and the like only |
---|
64 | 46 | * bound by their active reference. |
---|
65 | 47 | */ |
---|
66 | | - err = i915_gem_switch_to_kernel_context(i915); |
---|
67 | | - if (err) |
---|
68 | | - return err; |
---|
69 | | - |
---|
70 | | - err = i915_gem_wait_for_idle(i915, |
---|
71 | | - I915_WAIT_INTERRUPTIBLE | |
---|
72 | | - I915_WAIT_LOCKED, |
---|
73 | | - MAX_SCHEDULE_TIMEOUT); |
---|
74 | | - if (err) |
---|
75 | | - return err; |
---|
76 | | - |
---|
77 | | - GEM_BUG_ON(!ggtt_is_idle(i915)); |
---|
78 | | - return 0; |
---|
| 48 | + return intel_gt_wait_for_idle(gt, MAX_SCHEDULE_TIMEOUT); |
---|
79 | 49 | } |
---|
80 | 50 | |
---|
81 | 51 | static bool |
---|
.. | .. |
---|
87 | 57 | if (i915_vma_is_pinned(vma)) |
---|
88 | 58 | return false; |
---|
89 | 59 | |
---|
90 | | - if (flags & PIN_NONFAULT && i915_vma_has_userfault(vma)) |
---|
91 | | - return false; |
---|
92 | | - |
---|
93 | 60 | list_add(&vma->evict_link, unwind); |
---|
94 | 61 | return drm_mm_scan_add_block(scan, &vma->node); |
---|
95 | 62 | } |
---|
.. | .. |
---|
99 | 66 | * @vm: address space to evict from |
---|
100 | 67 | * @min_size: size of the desired free space |
---|
101 | 68 | * @alignment: alignment constraint of the desired free space |
---|
102 | | - * @cache_level: cache_level for the desired space |
---|
| 69 | + * @color: color for the desired space |
---|
103 | 70 | * @start: start (inclusive) of the range from which to evict objects |
---|
104 | 71 | * @end: end (exclusive) of the range from which to evict objects |
---|
105 | 72 | * @flags: additional flags to control the eviction algorithm |
---|
.. | .. |
---|
120 | 87 | int |
---|
121 | 88 | i915_gem_evict_something(struct i915_address_space *vm, |
---|
122 | 89 | u64 min_size, u64 alignment, |
---|
123 | | - unsigned cache_level, |
---|
| 90 | + unsigned long color, |
---|
124 | 91 | u64 start, u64 end, |
---|
125 | 92 | unsigned flags) |
---|
126 | 93 | { |
---|
127 | | - struct drm_i915_private *dev_priv = vm->i915; |
---|
128 | 94 | struct drm_mm_scan scan; |
---|
129 | 95 | struct list_head eviction_list; |
---|
130 | | - struct list_head *phases[] = { |
---|
131 | | - &vm->inactive_list, |
---|
132 | | - &vm->active_list, |
---|
133 | | - NULL, |
---|
134 | | - }, **phase; |
---|
135 | 96 | struct i915_vma *vma, *next; |
---|
136 | 97 | struct drm_mm_node *node; |
---|
137 | 98 | enum drm_mm_insert_mode mode; |
---|
| 99 | + struct i915_vma *active; |
---|
138 | 100 | int ret; |
---|
139 | 101 | |
---|
140 | | - lockdep_assert_held(&vm->i915->drm.struct_mutex); |
---|
| 102 | + lockdep_assert_held(&vm->mutex); |
---|
141 | 103 | trace_i915_gem_evict(vm, min_size, alignment, flags); |
---|
142 | 104 | |
---|
143 | 105 | /* |
---|
144 | | - * The goal is to evict objects and amalgamate space in LRU order. |
---|
145 | | - * The oldest idle objects reside on the inactive list, which is in |
---|
146 | | - * retirement order. The next objects to retire are those in flight, |
---|
147 | | - * on the active list, again in retirement order. |
---|
| 106 | + * The goal is to evict objects and amalgamate space in rough LRU order. |
---|
| 107 | + * Since both active and inactive objects reside on the same list, |
---|
| 108 | + * in a mix of creation and last scanned order, as we process the list |
---|
| 109 | + * we sort it into inactive/active, which keeps the active portion |
---|
| 110 | + * in a rough MRU order. |
---|
148 | 111 | * |
---|
149 | 112 | * The retirement sequence is thus: |
---|
150 | | - * 1. Inactive objects (already retired) |
---|
151 | | - * 2. Active objects (will stall on unbinding) |
---|
152 | | - * |
---|
153 | | - * On each list, the oldest objects lie at the HEAD with the freshest |
---|
154 | | - * object on the TAIL. |
---|
| 113 | + * 1. Inactive objects (already retired, random order) |
---|
| 114 | + * 2. Active objects (will stall on unbinding, oldest scanned first) |
---|
155 | 115 | */ |
---|
156 | 116 | mode = DRM_MM_INSERT_BEST; |
---|
157 | 117 | if (flags & PIN_HIGH) |
---|
.. | .. |
---|
159 | 119 | if (flags & PIN_MAPPABLE) |
---|
160 | 120 | mode = DRM_MM_INSERT_LOW; |
---|
161 | 121 | drm_mm_scan_init_with_range(&scan, &vm->mm, |
---|
162 | | - min_size, alignment, cache_level, |
---|
| 122 | + min_size, alignment, color, |
---|
163 | 123 | start, end, mode); |
---|
164 | 124 | |
---|
165 | | - /* |
---|
166 | | - * Retire before we search the active list. Although we have |
---|
167 | | - * reasonable accuracy in our retirement lists, we may have |
---|
168 | | - * a stray pin (preventing eviction) that can only be resolved by |
---|
169 | | - * retiring. |
---|
170 | | - */ |
---|
171 | | - if (!(flags & PIN_NONBLOCK)) |
---|
172 | | - i915_retire_requests(dev_priv); |
---|
173 | | - else |
---|
174 | | - phases[1] = NULL; |
---|
| 125 | + intel_gt_retire_requests(vm->gt); |
---|
175 | 126 | |
---|
176 | 127 | search_again: |
---|
| 128 | + active = NULL; |
---|
177 | 129 | INIT_LIST_HEAD(&eviction_list); |
---|
178 | | - phase = phases; |
---|
179 | | - do { |
---|
180 | | - list_for_each_entry(vma, *phase, vm_link) |
---|
181 | | - if (mark_free(&scan, vma, flags, &eviction_list)) |
---|
182 | | - goto found; |
---|
183 | | - } while (*++phase); |
---|
| 130 | + list_for_each_entry_safe(vma, next, &vm->bound_list, vm_link) { |
---|
| 131 | + if (vma == active) { /* now seen this vma twice */ |
---|
| 132 | + if (flags & PIN_NONBLOCK) |
---|
| 133 | + break; |
---|
| 134 | + |
---|
| 135 | + active = ERR_PTR(-EAGAIN); |
---|
| 136 | + } |
---|
| 137 | + |
---|
| 138 | + /* |
---|
| 139 | + * We keep this list in a rough least-recently scanned order |
---|
| 140 | + * of active elements (inactive elements are cheap to reap). |
---|
| 141 | + * New entries are added to the end, and we move anything we |
---|
| 142 | + * scan to the end. The assumption is that the working set |
---|
| 143 | + * of applications is either steady state (and thanks to the |
---|
| 144 | + * userspace bo cache it almost always is) or volatile and |
---|
| 145 | + * frequently replaced after a frame, which are self-evicting! |
---|
| 146 | + * Given that assumption, the MRU order of the scan list is |
---|
| 147 | + * fairly static, and keeping it in least-recently scan order |
---|
| 148 | + * is suitable. |
---|
| 149 | + * |
---|
| 150 | + * To notice when we complete one full cycle, we record the |
---|
| 151 | + * first active element seen, before moving it to the tail. |
---|
| 152 | + */ |
---|
| 153 | + if (active != ERR_PTR(-EAGAIN) && i915_vma_is_active(vma)) { |
---|
| 154 | + if (!active) |
---|
| 155 | + active = vma; |
---|
| 156 | + |
---|
| 157 | + list_move_tail(&vma->vm_link, &vm->bound_list); |
---|
| 158 | + continue; |
---|
| 159 | + } |
---|
| 160 | + |
---|
| 161 | + if (mark_free(&scan, vma, flags, &eviction_list)) |
---|
| 162 | + goto found; |
---|
| 163 | + } |
---|
184 | 164 | |
---|
185 | 165 | /* Nothing found, clean up and bail out! */ |
---|
186 | 166 | list_for_each_entry_safe(vma, next, &eviction_list, evict_link) { |
---|
.. | .. |
---|
210 | 190 | * us a termination condition, when the last retired context is |
---|
211 | 191 | * the kernel's there is no more we can evict. |
---|
212 | 192 | */ |
---|
213 | | - if (!ggtt_is_idle(dev_priv)) { |
---|
214 | | - if (I915_SELFTEST_ONLY(igt_evict_ctl.fail_if_busy)) |
---|
215 | | - return -EBUSY; |
---|
| 193 | + if (I915_SELFTEST_ONLY(igt_evict_ctl.fail_if_busy)) |
---|
| 194 | + return -EBUSY; |
---|
216 | 195 | |
---|
217 | | - ret = ggtt_flush(dev_priv); |
---|
218 | | - if (ret) |
---|
219 | | - return ret; |
---|
| 196 | + ret = ggtt_flush(vm->gt); |
---|
| 197 | + if (ret) |
---|
| 198 | + return ret; |
---|
220 | 199 | |
---|
221 | | - cond_resched(); |
---|
222 | | - goto search_again; |
---|
223 | | - } |
---|
| 200 | + cond_resched(); |
---|
224 | 201 | |
---|
225 | | - /* |
---|
226 | | - * If we still have pending pageflip completions, drop |
---|
227 | | - * back to userspace to give our workqueues time to |
---|
228 | | - * acquire our locks and unpin the old scanouts. |
---|
229 | | - */ |
---|
230 | | - return intel_has_pending_fb_unpin(dev_priv) ? -EAGAIN : -ENOSPC; |
---|
| 202 | + flags |= PIN_NONBLOCK; |
---|
| 203 | + goto search_again; |
---|
231 | 204 | |
---|
232 | 205 | found: |
---|
233 | 206 | /* drm_mm doesn't allow any other other operations while |
---|
.. | .. |
---|
248 | 221 | list_for_each_entry_safe(vma, next, &eviction_list, evict_link) { |
---|
249 | 222 | __i915_vma_unpin(vma); |
---|
250 | 223 | if (ret == 0) |
---|
251 | | - ret = i915_vma_unbind(vma); |
---|
| 224 | + ret = __i915_vma_unbind(vma); |
---|
252 | 225 | } |
---|
253 | 226 | |
---|
254 | 227 | while (ret == 0 && (node = drm_mm_scan_color_evict(&scan))) { |
---|
255 | 228 | vma = container_of(node, struct i915_vma, node); |
---|
256 | | - ret = i915_vma_unbind(vma); |
---|
| 229 | + |
---|
| 230 | + /* If we find any non-objects (!vma), we cannot evict them */ |
---|
| 231 | + if (vma->node.color != I915_COLOR_UNEVICTABLE) |
---|
| 232 | + ret = __i915_vma_unbind(vma); |
---|
| 233 | + else |
---|
| 234 | + ret = -ENOSPC; /* XXX search failed, try again? */ |
---|
257 | 235 | } |
---|
258 | 236 | |
---|
259 | 237 | return ret; |
---|
.. | .. |
---|
279 | 257 | u64 start = target->start; |
---|
280 | 258 | u64 end = start + target->size; |
---|
281 | 259 | struct i915_vma *vma, *next; |
---|
282 | | - bool check_color; |
---|
283 | 260 | int ret = 0; |
---|
284 | 261 | |
---|
285 | | - lockdep_assert_held(&vm->i915->drm.struct_mutex); |
---|
| 262 | + lockdep_assert_held(&vm->mutex); |
---|
286 | 263 | GEM_BUG_ON(!IS_ALIGNED(start, I915_GTT_PAGE_SIZE)); |
---|
287 | 264 | GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE)); |
---|
288 | 265 | |
---|
289 | 266 | trace_i915_gem_evict_node(vm, target, flags); |
---|
290 | 267 | |
---|
291 | | - /* Retire before we search the active list. Although we have |
---|
| 268 | + /* |
---|
| 269 | + * Retire before we search the active list. Although we have |
---|
292 | 270 | * reasonable accuracy in our retirement lists, we may have |
---|
293 | 271 | * a stray pin (preventing eviction) that can only be resolved by |
---|
294 | 272 | * retiring. |
---|
295 | 273 | */ |
---|
296 | | - if (!(flags & PIN_NONBLOCK)) |
---|
297 | | - i915_retire_requests(vm->i915); |
---|
| 274 | + intel_gt_retire_requests(vm->gt); |
---|
298 | 275 | |
---|
299 | | - check_color = vm->mm.color_adjust; |
---|
300 | | - if (check_color) { |
---|
| 276 | + if (i915_vm_has_cache_coloring(vm)) { |
---|
301 | 277 | /* Expand search to cover neighbouring guard pages (or lack!) */ |
---|
302 | 278 | if (start) |
---|
303 | 279 | start -= I915_GTT_PAGE_SIZE; |
---|
.. | .. |
---|
314 | 290 | break; |
---|
315 | 291 | } |
---|
316 | 292 | |
---|
317 | | - GEM_BUG_ON(!node->allocated); |
---|
| 293 | + GEM_BUG_ON(!drm_mm_node_allocated(node)); |
---|
318 | 294 | vma = container_of(node, typeof(*vma), node); |
---|
319 | 295 | |
---|
320 | | - /* If we are using coloring to insert guard pages between |
---|
| 296 | + /* |
---|
| 297 | + * If we are using coloring to insert guard pages between |
---|
321 | 298 | * different cache domains within the address space, we have |
---|
322 | 299 | * to check whether the objects on either side of our range |
---|
323 | 300 | * abutt and conflict. If they are in conflict, then we evict |
---|
324 | 301 | * those as well to make room for our guard pages. |
---|
325 | 302 | */ |
---|
326 | | - if (check_color) { |
---|
| 303 | + if (i915_vm_has_cache_coloring(vm)) { |
---|
327 | 304 | if (node->start + node->size == target->start) { |
---|
328 | 305 | if (node->color == target->color) |
---|
329 | 306 | continue; |
---|
.. | .. |
---|
334 | 311 | } |
---|
335 | 312 | } |
---|
336 | 313 | |
---|
337 | | - if (flags & PIN_NONBLOCK && |
---|
338 | | - (i915_vma_is_pinned(vma) || i915_vma_is_active(vma))) { |
---|
339 | | - ret = -ENOSPC; |
---|
340 | | - break; |
---|
341 | | - } |
---|
342 | | - |
---|
343 | | - if (flags & PIN_NONFAULT && i915_vma_has_userfault(vma)) { |
---|
344 | | - ret = -ENOSPC; |
---|
345 | | - break; |
---|
346 | | - } |
---|
347 | | - |
---|
348 | | - /* Overlap of objects in the same batch? */ |
---|
349 | 314 | if (i915_vma_is_pinned(vma)) { |
---|
350 | 315 | ret = -ENOSPC; |
---|
351 | | - if (vma->exec_flags && |
---|
352 | | - *vma->exec_flags & EXEC_OBJECT_PINNED) |
---|
353 | | - ret = -EINVAL; |
---|
354 | 316 | break; |
---|
355 | 317 | } |
---|
356 | 318 | |
---|
357 | | - /* Never show fear in the face of dragons! |
---|
| 319 | + if (flags & PIN_NONBLOCK && i915_vma_is_active(vma)) { |
---|
| 320 | + ret = -ENOSPC; |
---|
| 321 | + break; |
---|
| 322 | + } |
---|
| 323 | + |
---|
| 324 | + /* |
---|
| 325 | + * Never show fear in the face of dragons! |
---|
358 | 326 | * |
---|
359 | 327 | * We cannot directly remove this node from within this |
---|
360 | 328 | * iterator and as with i915_gem_evict_something() we employ |
---|
.. | .. |
---|
369 | 337 | list_for_each_entry_safe(vma, next, &eviction_list, evict_link) { |
---|
370 | 338 | __i915_vma_unpin(vma); |
---|
371 | 339 | if (ret == 0) |
---|
372 | | - ret = i915_vma_unbind(vma); |
---|
| 340 | + ret = __i915_vma_unbind(vma); |
---|
373 | 341 | } |
---|
374 | 342 | |
---|
375 | 343 | return ret; |
---|
.. | .. |
---|
389 | 357 | */ |
---|
390 | 358 | int i915_gem_evict_vm(struct i915_address_space *vm) |
---|
391 | 359 | { |
---|
392 | | - struct list_head *phases[] = { |
---|
393 | | - &vm->inactive_list, |
---|
394 | | - &vm->active_list, |
---|
395 | | - NULL |
---|
396 | | - }, **phase; |
---|
397 | | - struct list_head eviction_list; |
---|
398 | | - struct i915_vma *vma, *next; |
---|
399 | | - int ret; |
---|
| 360 | + int ret = 0; |
---|
400 | 361 | |
---|
401 | | - lockdep_assert_held(&vm->i915->drm.struct_mutex); |
---|
| 362 | + lockdep_assert_held(&vm->mutex); |
---|
402 | 363 | trace_i915_gem_evict_vm(vm); |
---|
403 | 364 | |
---|
404 | 365 | /* Switch back to the default context in order to unpin |
---|
.. | .. |
---|
407 | 368 | * switch otherwise is ineffective. |
---|
408 | 369 | */ |
---|
409 | 370 | if (i915_is_ggtt(vm)) { |
---|
410 | | - ret = ggtt_flush(vm->i915); |
---|
| 371 | + ret = ggtt_flush(vm->gt); |
---|
411 | 372 | if (ret) |
---|
412 | 373 | return ret; |
---|
413 | 374 | } |
---|
414 | 375 | |
---|
415 | | - INIT_LIST_HEAD(&eviction_list); |
---|
416 | | - phase = phases; |
---|
417 | 376 | do { |
---|
418 | | - list_for_each_entry(vma, *phase, vm_link) { |
---|
| 377 | + struct i915_vma *vma, *vn; |
---|
| 378 | + LIST_HEAD(eviction_list); |
---|
| 379 | + |
---|
| 380 | + list_for_each_entry(vma, &vm->bound_list, vm_link) { |
---|
419 | 381 | if (i915_vma_is_pinned(vma)) |
---|
420 | 382 | continue; |
---|
421 | 383 | |
---|
422 | 384 | __i915_vma_pin(vma); |
---|
423 | 385 | list_add(&vma->evict_link, &eviction_list); |
---|
424 | 386 | } |
---|
425 | | - } while (*++phase); |
---|
| 387 | + if (list_empty(&eviction_list)) |
---|
| 388 | + break; |
---|
426 | 389 | |
---|
427 | | - ret = 0; |
---|
428 | | - list_for_each_entry_safe(vma, next, &eviction_list, evict_link) { |
---|
429 | | - __i915_vma_unpin(vma); |
---|
430 | | - if (ret == 0) |
---|
431 | | - ret = i915_vma_unbind(vma); |
---|
432 | | - } |
---|
| 390 | + ret = 0; |
---|
| 391 | + list_for_each_entry_safe(vma, vn, &eviction_list, evict_link) { |
---|
| 392 | + __i915_vma_unpin(vma); |
---|
| 393 | + if (ret == 0) |
---|
| 394 | + ret = __i915_vma_unbind(vma); |
---|
| 395 | + if (ret != -EINTR) /* "Get me out of here!" */ |
---|
| 396 | + ret = 0; |
---|
| 397 | + } |
---|
| 398 | + } while (ret == 0); |
---|
| 399 | + |
---|
433 | 400 | return ret; |
---|
434 | 401 | } |
---|
435 | 402 | |
---|