Commit | Line | Data |
---|---|---|
3589fdbd TH |
1 | // SPDX-License-Identifier: MIT |
2 | /* | |
3 | * Copyright © 2021 Intel Corporation | |
4 | */ | |
5 | ||
6 | #include <drm/ttm/ttm_bo_driver.h> | |
7 | ||
63cf4cad | 8 | #include "i915_deps.h" |
3589fdbd TH |
9 | #include "i915_drv.h" |
10 | #include "intel_memory_region.h" | |
11 | #include "intel_region_ttm.h" | |
12 | ||
13 | #include "gem/i915_gem_object.h" | |
14 | #include "gem/i915_gem_region.h" | |
15 | #include "gem/i915_gem_ttm.h" | |
16 | #include "gem/i915_gem_ttm_move.h" | |
17 | ||
18 | #include "gt/intel_engine_pm.h" | |
19 | #include "gt/intel_gt.h" | |
20 | #include "gt/intel_migrate.h" | |
21 | ||
2b0a750c TH |
22 | /** |
23 | * DOC: Selftest failure modes for failsafe migration: | |
24 | * | |
25 | * For fail_gpu_migration, the gpu blit scheduled is always a clear blit | |
26 | * rather than a copy blit, and then we force the failure paths as if | |
27 | * the blit fence returned an error. | |
28 | * | |
29 | * For fail_work_allocation we fail the kmalloc of the async worker, we | |
30 | * sync the gpu blit. If it then fails, or fail_gpu_migration is set to | |
31 | * true, then a memcpy operation is performed sync. | |
32 | */ | |
33 | #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) | |
34 | static bool fail_gpu_migration; | |
35 | static bool fail_work_allocation; | |
36 | ||
37 | void i915_ttm_migrate_set_failure_modes(bool gpu_migration, | |
38 | bool work_allocation) | |
39 | { | |
40 | fail_gpu_migration = gpu_migration; | |
41 | fail_work_allocation = work_allocation; | |
42 | } | |
43 | #endif | |
44 | ||
3589fdbd TH |
45 | static enum i915_cache_level |
46 | i915_ttm_cache_level(struct drm_i915_private *i915, struct ttm_resource *res, | |
47 | struct ttm_tt *ttm) | |
48 | { | |
49 | return ((HAS_LLC(i915) || HAS_SNOOP(i915)) && | |
50 | !i915_ttm_gtt_binds_lmem(res) && | |
51 | ttm->caching == ttm_cached) ? I915_CACHE_LLC : | |
52 | I915_CACHE_NONE; | |
53 | } | |
54 | ||
55 | static struct intel_memory_region * | |
56 | i915_ttm_region(struct ttm_device *bdev, int ttm_mem_type) | |
57 | { | |
58 | struct drm_i915_private *i915 = container_of(bdev, typeof(*i915), bdev); | |
59 | ||
60 | /* There's some room for optimization here... */ | |
61 | GEM_BUG_ON(ttm_mem_type != I915_PL_SYSTEM && | |
62 | ttm_mem_type < I915_PL_LMEM0); | |
63 | if (ttm_mem_type == I915_PL_SYSTEM) | |
64 | return intel_memory_region_lookup(i915, INTEL_MEMORY_SYSTEM, | |
65 | 0); | |
66 | ||
67 | return intel_memory_region_lookup(i915, INTEL_MEMORY_LOCAL, | |
68 | ttm_mem_type - I915_PL_LMEM0); | |
69 | } | |
70 | ||
71 | /** | |
72 | * i915_ttm_adjust_domains_after_move - Adjust the GEM domains after a | |
73 | * TTM move | |
74 | * @obj: The gem object | |
75 | */ | |
76 | void i915_ttm_adjust_domains_after_move(struct drm_i915_gem_object *obj) | |
77 | { | |
78 | struct ttm_buffer_object *bo = i915_gem_to_ttm(obj); | |
79 | ||
80 | if (i915_ttm_cpu_maps_iomem(bo->resource) || bo->ttm->caching != ttm_cached) { | |
81 | obj->write_domain = I915_GEM_DOMAIN_WC; | |
82 | obj->read_domains = I915_GEM_DOMAIN_WC; | |
83 | } else { | |
84 | obj->write_domain = I915_GEM_DOMAIN_CPU; | |
85 | obj->read_domains = I915_GEM_DOMAIN_CPU; | |
86 | } | |
87 | } | |
88 | ||
89 | /** | |
90 | * i915_ttm_adjust_gem_after_move - Adjust the GEM state after a TTM move | |
91 | * @obj: The gem object | |
92 | * | |
93 | * Adjusts the GEM object's region, mem_flags and cache coherency after a | |
94 | * TTM move. | |
95 | */ | |
96 | void i915_ttm_adjust_gem_after_move(struct drm_i915_gem_object *obj) | |
97 | { | |
98 | struct ttm_buffer_object *bo = i915_gem_to_ttm(obj); | |
99 | unsigned int cache_level; | |
100 | unsigned int i; | |
101 | ||
102 | /* | |
103 | * If object was moved to an allowable region, update the object | |
104 | * region to consider it migrated. Note that if it's currently not | |
105 | * in an allowable region, it's evicted and we don't update the | |
106 | * object region. | |
107 | */ | |
108 | if (intel_region_to_ttm_type(obj->mm.region) != bo->resource->mem_type) { | |
109 | for (i = 0; i < obj->mm.n_placements; ++i) { | |
110 | struct intel_memory_region *mr = obj->mm.placements[i]; | |
111 | ||
112 | if (intel_region_to_ttm_type(mr) == bo->resource->mem_type && | |
113 | mr != obj->mm.region) { | |
114 | i915_gem_object_release_memory_region(obj); | |
115 | i915_gem_object_init_memory_region(obj, mr); | |
116 | break; | |
117 | } | |
118 | } | |
119 | } | |
120 | ||
121 | obj->mem_flags &= ~(I915_BO_FLAG_STRUCT_PAGE | I915_BO_FLAG_IOMEM); | |
122 | ||
123 | obj->mem_flags |= i915_ttm_cpu_maps_iomem(bo->resource) ? I915_BO_FLAG_IOMEM : | |
124 | I915_BO_FLAG_STRUCT_PAGE; | |
125 | ||
126 | cache_level = i915_ttm_cache_level(to_i915(bo->base.dev), bo->resource, | |
127 | bo->ttm); | |
128 | i915_gem_object_set_cache_coherency(obj, cache_level); | |
129 | } | |
130 | ||
131 | /** | |
132 | * i915_ttm_move_notify - Prepare an object for move | |
133 | * @bo: The ttm buffer object. | |
134 | * | |
135 | * This function prepares an object for move by removing all GPU bindings, | |
136 | * removing all CPU mapings and finally releasing the pages sg-table. | |
137 | * | |
138 | * Return: 0 if successful, negative error code on error. | |
139 | */ | |
140 | int i915_ttm_move_notify(struct ttm_buffer_object *bo) | |
141 | { | |
142 | struct drm_i915_gem_object *obj = i915_ttm_to_gem(bo); | |
143 | int ret; | |
144 | ||
145 | ret = i915_gem_object_unbind(obj, I915_GEM_OBJECT_UNBIND_ACTIVE); | |
146 | if (ret) | |
147 | return ret; | |
148 | ||
149 | ret = __i915_gem_object_put_pages(obj); | |
150 | if (ret) | |
151 | return ret; | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
2b0a750c TH |
156 | static struct dma_fence *i915_ttm_accel_move(struct ttm_buffer_object *bo, |
157 | bool clear, | |
158 | struct ttm_resource *dst_mem, | |
159 | struct ttm_tt *dst_ttm, | |
6385eb7a | 160 | struct sg_table *dst_st, |
11930817 | 161 | const struct i915_deps *deps) |
3589fdbd TH |
162 | { |
163 | struct drm_i915_private *i915 = container_of(bo->bdev, typeof(*i915), | |
164 | bdev); | |
165 | struct drm_i915_gem_object *obj = i915_ttm_to_gem(bo); | |
166 | struct i915_request *rq; | |
167 | struct ttm_tt *src_ttm = bo->ttm; | |
168 | enum i915_cache_level src_level, dst_level; | |
169 | int ret; | |
170 | ||
1a9c4db4 | 171 | if (!to_gt(i915)->migrate.context || intel_gt_is_wedged(to_gt(i915))) |
2b0a750c TH |
172 | return ERR_PTR(-EINVAL); |
173 | ||
174 | /* With fail_gpu_migration, we always perform a GPU clear. */ | |
175 | if (I915_SELFTEST_ONLY(fail_gpu_migration)) | |
176 | clear = true; | |
3589fdbd TH |
177 | |
178 | dst_level = i915_ttm_cache_level(i915, dst_mem, dst_ttm); | |
179 | if (clear) { | |
2b0a750c TH |
180 | if (bo->type == ttm_bo_type_kernel && |
181 | !I915_SELFTEST_ONLY(fail_gpu_migration)) | |
182 | return ERR_PTR(-EINVAL); | |
3589fdbd | 183 | |
1a9c4db4 | 184 | intel_engine_pm_get(to_gt(i915)->migrate.context->engine); |
11930817 | 185 | ret = intel_context_migrate_clear(to_gt(i915)->migrate.context, deps, |
3589fdbd TH |
186 | dst_st->sgl, dst_level, |
187 | i915_ttm_gtt_binds_lmem(dst_mem), | |
188 | 0, &rq); | |
3589fdbd TH |
189 | } else { |
190 | struct i915_refct_sgt *src_rsgt = | |
191 | i915_ttm_resource_get_st(obj, bo->resource); | |
192 | ||
193 | if (IS_ERR(src_rsgt)) | |
2b0a750c | 194 | return ERR_CAST(src_rsgt); |
3589fdbd TH |
195 | |
196 | src_level = i915_ttm_cache_level(i915, bo->resource, src_ttm); | |
1a9c4db4 MW |
197 | intel_engine_pm_get(to_gt(i915)->migrate.context->engine); |
198 | ret = intel_context_migrate_copy(to_gt(i915)->migrate.context, | |
11930817 | 199 | deps, src_rsgt->table.sgl, |
3589fdbd TH |
200 | src_level, |
201 | i915_ttm_gtt_binds_lmem(bo->resource), | |
202 | dst_st->sgl, dst_level, | |
203 | i915_ttm_gtt_binds_lmem(dst_mem), | |
204 | &rq); | |
2b0a750c | 205 | |
3589fdbd | 206 | i915_refct_sgt_put(src_rsgt); |
3589fdbd TH |
207 | } |
208 | ||
1a9c4db4 | 209 | intel_engine_pm_put(to_gt(i915)->migrate.context->engine); |
2b0a750c TH |
210 | |
211 | if (ret && rq) { | |
212 | i915_request_wait(rq, 0, MAX_SCHEDULE_TIMEOUT); | |
213 | i915_request_put(rq); | |
214 | } | |
215 | ||
216 | return ret ? ERR_PTR(ret) : &rq->fence; | |
217 | } | |
218 | ||
219 | /** | |
220 | * struct i915_ttm_memcpy_arg - argument for the bo memcpy functionality. | |
221 | * @_dst_iter: Storage space for the destination kmap iterator. | |
222 | * @_src_iter: Storage space for the source kmap iterator. | |
223 | * @dst_iter: Pointer to the destination kmap iterator. | |
224 | * @src_iter: Pointer to the source kmap iterator. | |
225 | * @clear: Whether to clear instead of copy. | |
226 | * @src_rsgt: Refcounted scatter-gather list of source memory. | |
227 | * @dst_rsgt: Refcounted scatter-gather list of destination memory. | |
228 | */ | |
229 | struct i915_ttm_memcpy_arg { | |
230 | union { | |
231 | struct ttm_kmap_iter_tt tt; | |
232 | struct ttm_kmap_iter_iomap io; | |
233 | } _dst_iter, | |
234 | _src_iter; | |
235 | struct ttm_kmap_iter *dst_iter; | |
236 | struct ttm_kmap_iter *src_iter; | |
237 | unsigned long num_pages; | |
238 | bool clear; | |
239 | struct i915_refct_sgt *src_rsgt; | |
240 | struct i915_refct_sgt *dst_rsgt; | |
241 | }; | |
242 | ||
243 | /** | |
244 | * struct i915_ttm_memcpy_work - Async memcpy worker under a dma-fence. | |
245 | * @fence: The dma-fence. | |
246 | * @work: The work struct use for the memcpy work. | |
247 | * @lock: The fence lock. Not used to protect anything else ATM. | |
248 | * @irq_work: Low latency worker to signal the fence since it can't be done | |
249 | * from the callback for lockdep reasons. | |
250 | * @cb: Callback for the accelerated migration fence. | |
251 | * @arg: The argument for the memcpy functionality. | |
252 | */ | |
253 | struct i915_ttm_memcpy_work { | |
254 | struct dma_fence fence; | |
255 | struct work_struct work; | |
256 | /* The fence lock */ | |
257 | spinlock_t lock; | |
258 | struct irq_work irq_work; | |
259 | struct dma_fence_cb cb; | |
260 | struct i915_ttm_memcpy_arg arg; | |
261 | }; | |
262 | ||
263 | static void i915_ttm_move_memcpy(struct i915_ttm_memcpy_arg *arg) | |
264 | { | |
265 | ttm_move_memcpy(arg->clear, arg->num_pages, | |
266 | arg->dst_iter, arg->src_iter); | |
267 | } | |
268 | ||
269 | static void i915_ttm_memcpy_init(struct i915_ttm_memcpy_arg *arg, | |
270 | struct ttm_buffer_object *bo, bool clear, | |
271 | struct ttm_resource *dst_mem, | |
272 | struct ttm_tt *dst_ttm, | |
273 | struct i915_refct_sgt *dst_rsgt) | |
274 | { | |
275 | struct drm_i915_gem_object *obj = i915_ttm_to_gem(bo); | |
276 | struct intel_memory_region *dst_reg, *src_reg; | |
277 | ||
278 | dst_reg = i915_ttm_region(bo->bdev, dst_mem->mem_type); | |
279 | src_reg = i915_ttm_region(bo->bdev, bo->resource->mem_type); | |
280 | GEM_BUG_ON(!dst_reg || !src_reg); | |
281 | ||
282 | arg->dst_iter = !i915_ttm_cpu_maps_iomem(dst_mem) ? | |
283 | ttm_kmap_iter_tt_init(&arg->_dst_iter.tt, dst_ttm) : | |
284 | ttm_kmap_iter_iomap_init(&arg->_dst_iter.io, &dst_reg->iomap, | |
285 | &dst_rsgt->table, dst_reg->region.start); | |
286 | ||
287 | arg->src_iter = !i915_ttm_cpu_maps_iomem(bo->resource) ? | |
288 | ttm_kmap_iter_tt_init(&arg->_src_iter.tt, bo->ttm) : | |
289 | ttm_kmap_iter_iomap_init(&arg->_src_iter.io, &src_reg->iomap, | |
290 | &obj->ttm.cached_io_rsgt->table, | |
291 | src_reg->region.start); | |
292 | arg->clear = clear; | |
293 | arg->num_pages = bo->base.size >> PAGE_SHIFT; | |
294 | ||
295 | arg->dst_rsgt = i915_refct_sgt_get(dst_rsgt); | |
296 | arg->src_rsgt = clear ? NULL : | |
297 | i915_ttm_resource_get_st(obj, bo->resource); | |
298 | } | |
299 | ||
300 | static void i915_ttm_memcpy_release(struct i915_ttm_memcpy_arg *arg) | |
301 | { | |
302 | i915_refct_sgt_put(arg->src_rsgt); | |
303 | i915_refct_sgt_put(arg->dst_rsgt); | |
304 | } | |
305 | ||
306 | static void __memcpy_work(struct work_struct *work) | |
307 | { | |
308 | struct i915_ttm_memcpy_work *copy_work = | |
309 | container_of(work, typeof(*copy_work), work); | |
310 | struct i915_ttm_memcpy_arg *arg = ©_work->arg; | |
311 | bool cookie = dma_fence_begin_signalling(); | |
312 | ||
313 | i915_ttm_move_memcpy(arg); | |
314 | dma_fence_end_signalling(cookie); | |
315 | ||
316 | dma_fence_signal(©_work->fence); | |
317 | ||
318 | i915_ttm_memcpy_release(arg); | |
319 | dma_fence_put(©_work->fence); | |
320 | } | |
321 | ||
322 | static void __memcpy_irq_work(struct irq_work *irq_work) | |
323 | { | |
324 | struct i915_ttm_memcpy_work *copy_work = | |
325 | container_of(irq_work, typeof(*copy_work), irq_work); | |
326 | struct i915_ttm_memcpy_arg *arg = ©_work->arg; | |
327 | ||
328 | dma_fence_signal(©_work->fence); | |
329 | i915_ttm_memcpy_release(arg); | |
330 | dma_fence_put(©_work->fence); | |
331 | } | |
332 | ||
333 | static void __memcpy_cb(struct dma_fence *fence, struct dma_fence_cb *cb) | |
334 | { | |
335 | struct i915_ttm_memcpy_work *copy_work = | |
336 | container_of(cb, typeof(*copy_work), cb); | |
337 | ||
338 | if (unlikely(fence->error || I915_SELFTEST_ONLY(fail_gpu_migration))) { | |
339 | INIT_WORK(©_work->work, __memcpy_work); | |
340 | queue_work(system_unbound_wq, ©_work->work); | |
341 | } else { | |
342 | init_irq_work(©_work->irq_work, __memcpy_irq_work); | |
343 | irq_work_queue(©_work->irq_work); | |
344 | } | |
345 | } | |
346 | ||
347 | static const char *get_driver_name(struct dma_fence *fence) | |
348 | { | |
349 | return "i915_ttm_memcpy_work"; | |
350 | } | |
351 | ||
352 | static const char *get_timeline_name(struct dma_fence *fence) | |
353 | { | |
354 | return "unbound"; | |
355 | } | |
356 | ||
357 | static const struct dma_fence_ops dma_fence_memcpy_ops = { | |
358 | .get_driver_name = get_driver_name, | |
359 | .get_timeline_name = get_timeline_name, | |
360 | }; | |
361 | ||
362 | static struct dma_fence * | |
363 | i915_ttm_memcpy_work_arm(struct i915_ttm_memcpy_work *work, | |
364 | struct dma_fence *dep) | |
365 | { | |
366 | int ret; | |
367 | ||
368 | spin_lock_init(&work->lock); | |
369 | dma_fence_init(&work->fence, &dma_fence_memcpy_ops, &work->lock, 0, 0); | |
370 | dma_fence_get(&work->fence); | |
371 | ret = dma_fence_add_callback(dep, &work->cb, __memcpy_cb); | |
372 | if (ret) { | |
373 | if (ret != -ENOENT) | |
374 | dma_fence_wait(dep, false); | |
375 | ||
376 | return ERR_PTR(I915_SELFTEST_ONLY(fail_gpu_migration) ? -EINVAL : | |
377 | dep->error); | |
378 | } | |
379 | ||
380 | return &work->fence; | |
3589fdbd TH |
381 | } |
382 | ||
6385eb7a | 383 | static struct dma_fence * |
11930817 TH |
384 | __i915_ttm_move(struct ttm_buffer_object *bo, |
385 | const struct ttm_operation_ctx *ctx, bool clear, | |
6385eb7a TH |
386 | struct ttm_resource *dst_mem, struct ttm_tt *dst_ttm, |
387 | struct i915_refct_sgt *dst_rsgt, bool allow_accel, | |
11930817 | 388 | const struct i915_deps *move_deps) |
3589fdbd | 389 | { |
2b0a750c TH |
390 | struct i915_ttm_memcpy_work *copy_work = NULL; |
391 | struct i915_ttm_memcpy_arg _arg, *arg = &_arg; | |
392 | struct dma_fence *fence = ERR_PTR(-EINVAL); | |
393 | ||
394 | if (allow_accel) { | |
395 | fence = i915_ttm_accel_move(bo, clear, dst_mem, dst_ttm, | |
11930817 | 396 | &dst_rsgt->table, move_deps); |
2b0a750c TH |
397 | |
398 | /* | |
399 | * We only need to intercept the error when moving to lmem. | |
400 | * When moving to system, TTM or shmem will provide us with | |
401 | * cleared pages. | |
402 | */ | |
403 | if (!IS_ERR(fence) && !i915_ttm_gtt_binds_lmem(dst_mem) && | |
404 | !I915_SELFTEST_ONLY(fail_gpu_migration || | |
405 | fail_work_allocation)) | |
406 | goto out; | |
407 | } | |
3589fdbd | 408 | |
2b0a750c TH |
409 | /* If we've scheduled gpu migration. Try to arm error intercept. */ |
410 | if (!IS_ERR(fence)) { | |
411 | struct dma_fence *dep = fence; | |
412 | ||
413 | if (!I915_SELFTEST_ONLY(fail_work_allocation)) | |
414 | copy_work = kzalloc(sizeof(*copy_work), GFP_KERNEL); | |
415 | ||
416 | if (copy_work) { | |
417 | arg = ©_work->arg; | |
418 | i915_ttm_memcpy_init(arg, bo, clear, dst_mem, dst_ttm, | |
419 | dst_rsgt); | |
420 | fence = i915_ttm_memcpy_work_arm(copy_work, dep); | |
421 | } else { | |
422 | dma_fence_wait(dep, false); | |
423 | fence = ERR_PTR(I915_SELFTEST_ONLY(fail_gpu_migration) ? | |
424 | -EINVAL : fence->error); | |
425 | } | |
426 | dma_fence_put(dep); | |
427 | ||
428 | if (!IS_ERR(fence)) | |
429 | goto out; | |
3526b607 TH |
430 | } else { |
431 | int err = PTR_ERR(fence); | |
432 | ||
433 | if (err == -EINTR || err == -ERESTARTSYS || err == -EAGAIN) | |
434 | return fence; | |
6385eb7a | 435 | |
3526b607 TH |
436 | if (move_deps) { |
437 | err = i915_deps_sync(move_deps, ctx); | |
438 | if (err) | |
439 | return ERR_PTR(err); | |
440 | } | |
2b0a750c TH |
441 | } |
442 | ||
443 | /* Error intercept failed or no accelerated migration to start with */ | |
444 | if (!copy_work) | |
445 | i915_ttm_memcpy_init(arg, bo, clear, dst_mem, dst_ttm, | |
446 | dst_rsgt); | |
447 | i915_ttm_move_memcpy(arg); | |
448 | i915_ttm_memcpy_release(arg); | |
449 | kfree(copy_work); | |
450 | ||
6385eb7a | 451 | return NULL; |
2b0a750c | 452 | out: |
6385eb7a | 453 | if (!fence && copy_work) { |
2b0a750c TH |
454 | i915_ttm_memcpy_release(arg); |
455 | kfree(copy_work); | |
3589fdbd | 456 | } |
6385eb7a TH |
457 | |
458 | return fence; | |
459 | } | |
460 | ||
11930817 TH |
461 | static int |
462 | prev_deps(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, | |
463 | struct i915_deps *deps) | |
6385eb7a | 464 | { |
6385eb7a TH |
465 | int ret; |
466 | ||
11930817 | 467 | ret = i915_deps_add_dependency(deps, bo->moving, ctx); |
6385eb7a | 468 | if (!ret) |
33654ef4 | 469 | ret = i915_deps_add_resv(deps, bo->base.resv, ctx); |
6385eb7a | 470 | |
11930817 | 471 | return ret; |
3589fdbd TH |
472 | } |
473 | ||
474 | /** | |
475 | * i915_ttm_move - The TTM move callback used by i915. | |
476 | * @bo: The buffer object. | |
477 | * @evict: Whether this is an eviction. | |
478 | * @dst_mem: The destination ttm resource. | |
479 | * @hop: If we need multihop, what temporary memory type to move to. | |
480 | * | |
481 | * Return: 0 if successful, negative error code otherwise. | |
482 | */ | |
483 | int i915_ttm_move(struct ttm_buffer_object *bo, bool evict, | |
484 | struct ttm_operation_ctx *ctx, | |
485 | struct ttm_resource *dst_mem, | |
486 | struct ttm_place *hop) | |
487 | { | |
488 | struct drm_i915_gem_object *obj = i915_ttm_to_gem(bo); | |
489 | struct ttm_resource_manager *dst_man = | |
490 | ttm_manager_type(bo->bdev, dst_mem->mem_type); | |
6385eb7a | 491 | struct dma_fence *migration_fence = NULL; |
3589fdbd TH |
492 | struct ttm_tt *ttm = bo->ttm; |
493 | struct i915_refct_sgt *dst_rsgt; | |
494 | bool clear; | |
495 | int ret; | |
496 | ||
6385eb7a TH |
497 | if (GEM_WARN_ON(!obj)) { |
498 | ttm_bo_move_null(bo, dst_mem); | |
499 | return 0; | |
500 | } | |
3589fdbd TH |
501 | |
502 | ret = i915_ttm_move_notify(bo); | |
503 | if (ret) | |
504 | return ret; | |
505 | ||
506 | if (obj->mm.madv != I915_MADV_WILLNEED) { | |
507 | i915_ttm_purge(obj); | |
508 | ttm_resource_free(bo, &dst_mem); | |
509 | return 0; | |
510 | } | |
511 | ||
512 | /* Populate ttm with pages if needed. Typically system memory. */ | |
513 | if (ttm && (dst_man->use_tt || (ttm->page_flags & TTM_TT_FLAG_SWAPPED))) { | |
514 | ret = ttm_tt_populate(bo->bdev, ttm, ctx); | |
515 | if (ret) | |
516 | return ret; | |
517 | } | |
518 | ||
519 | dst_rsgt = i915_ttm_resource_get_st(obj, dst_mem); | |
520 | if (IS_ERR(dst_rsgt)) | |
521 | return PTR_ERR(dst_rsgt); | |
522 | ||
523 | clear = !i915_ttm_cpu_maps_iomem(bo->resource) && (!ttm || !ttm_tt_is_populated(ttm)); | |
6385eb7a | 524 | if (!(clear && ttm && !(ttm->page_flags & TTM_TT_FLAG_ZERO_ALLOC))) { |
11930817 | 525 | struct i915_deps deps; |
6385eb7a | 526 | |
11930817 TH |
527 | i915_deps_init(&deps, GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN); |
528 | ret = prev_deps(bo, ctx, &deps); | |
529 | if (ret) { | |
6385eb7a | 530 | i915_refct_sgt_put(dst_rsgt); |
11930817 | 531 | return ret; |
6385eb7a TH |
532 | } |
533 | ||
11930817 TH |
534 | migration_fence = __i915_ttm_move(bo, ctx, clear, dst_mem, bo->ttm, |
535 | dst_rsgt, true, &deps); | |
536 | i915_deps_fini(&deps); | |
6385eb7a TH |
537 | } |
538 | ||
539 | /* We can possibly get an -ERESTARTSYS here */ | |
540 | if (IS_ERR(migration_fence)) { | |
541 | i915_refct_sgt_put(dst_rsgt); | |
542 | return PTR_ERR(migration_fence); | |
543 | } | |
544 | ||
545 | if (migration_fence) { | |
546 | ret = ttm_bo_move_accel_cleanup(bo, migration_fence, evict, | |
547 | true, dst_mem); | |
548 | if (ret) { | |
549 | dma_fence_wait(migration_fence, false); | |
550 | ttm_bo_move_sync_cleanup(bo, dst_mem); | |
551 | } | |
552 | dma_fence_put(migration_fence); | |
553 | } else { | |
554 | ttm_bo_move_sync_cleanup(bo, dst_mem); | |
555 | } | |
3589fdbd | 556 | |
3589fdbd TH |
557 | i915_ttm_adjust_domains_after_move(obj); |
558 | i915_ttm_free_cached_io_rsgt(obj); | |
559 | ||
560 | if (i915_ttm_gtt_binds_lmem(dst_mem) || i915_ttm_cpu_maps_iomem(dst_mem)) { | |
561 | obj->ttm.cached_io_rsgt = dst_rsgt; | |
562 | obj->ttm.get_io_page.sg_pos = dst_rsgt->table.sgl; | |
563 | obj->ttm.get_io_page.sg_idx = 0; | |
564 | } else { | |
565 | i915_refct_sgt_put(dst_rsgt); | |
566 | } | |
567 | ||
568 | i915_ttm_adjust_lru(obj); | |
569 | i915_ttm_adjust_gem_after_move(obj); | |
570 | return 0; | |
571 | } | |
05d1c761 TH |
572 | |
573 | /** | |
574 | * i915_gem_obj_copy_ttm - Copy the contents of one ttm-based gem object to | |
575 | * another | |
576 | * @dst: The destination object | |
577 | * @src: The source object | |
578 | * @allow_accel: Allow using the blitter. Otherwise TTM memcpy is used. | |
579 | * @intr: Whether to perform waits interruptible: | |
580 | * | |
581 | * Note: The caller is responsible for assuring that the underlying | |
582 | * TTM objects are populated if needed and locked. | |
583 | * | |
584 | * Return: Zero on success. Negative error code on error. If @intr == true, | |
585 | * then it may return -ERESTARTSYS or -EINTR. | |
586 | */ | |
587 | int i915_gem_obj_copy_ttm(struct drm_i915_gem_object *dst, | |
588 | struct drm_i915_gem_object *src, | |
589 | bool allow_accel, bool intr) | |
590 | { | |
591 | struct ttm_buffer_object *dst_bo = i915_gem_to_ttm(dst); | |
592 | struct ttm_buffer_object *src_bo = i915_gem_to_ttm(src); | |
593 | struct ttm_operation_ctx ctx = { | |
594 | .interruptible = intr, | |
595 | }; | |
596 | struct i915_refct_sgt *dst_rsgt; | |
11930817 | 597 | struct dma_fence *copy_fence; |
5652df82 | 598 | struct i915_deps deps; |
33654ef4 | 599 | int ret; |
05d1c761 TH |
600 | |
601 | assert_object_held(dst); | |
602 | assert_object_held(src); | |
5652df82 | 603 | i915_deps_init(&deps, GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN); |
05d1c761 | 604 | |
33654ef4 CK |
605 | ret = dma_resv_reserve_shared(src_bo->base.resv, 1); |
606 | if (ret) | |
607 | return ret; | |
608 | ||
609 | ret = i915_deps_add_resv(&deps, dst_bo->base.resv, &ctx); | |
610 | if (ret) | |
611 | return ret; | |
612 | ||
613 | ret = i915_deps_add_resv(&deps, src_bo->base.resv, &ctx); | |
05d1c761 TH |
614 | if (ret) |
615 | return ret; | |
616 | ||
617 | dst_rsgt = i915_ttm_resource_get_st(dst, dst_bo->resource); | |
11930817 | 618 | copy_fence = __i915_ttm_move(src_bo, &ctx, false, dst_bo->resource, |
5652df82 | 619 | dst_bo->ttm, dst_rsgt, allow_accel, |
11930817 | 620 | &deps); |
05d1c761 | 621 | |
11930817 | 622 | i915_deps_fini(&deps); |
05d1c761 | 623 | i915_refct_sgt_put(dst_rsgt); |
5652df82 TH |
624 | if (IS_ERR_OR_NULL(copy_fence)) |
625 | return PTR_ERR_OR_ZERO(copy_fence); | |
6385eb7a | 626 | |
5652df82 | 627 | dma_resv_add_excl_fence(dst_bo->base.resv, copy_fence); |
33654ef4 | 628 | dma_resv_add_shared_fence(src_bo->base.resv, copy_fence); |
5652df82 TH |
629 | |
630 | dma_fence_put(copy_fence); | |
05d1c761 TH |
631 | |
632 | return 0; | |
633 | } |