xref: /linux/drivers/gpu/drm/xe/xe_svm.c (revision 25294cb8a404e8116eecaf2f151ee2fd6c17fb9b)
16fd979c2SMatthew Brost // SPDX-License-Identifier: MIT
26fd979c2SMatthew Brost /*
36fd979c2SMatthew Brost  * Copyright © 2024 Intel Corporation
46fd979c2SMatthew Brost  */
56fd979c2SMatthew Brost 
65951fed8SMatthew Brost #include "xe_bo.h"
780bcbdfcSFrancois Dugast #include "xe_gt_stats.h"
8ab498828SMatthew Brost #include "xe_gt_tlb_invalidation.h"
9c5b3eb5aSMatthew Brost #include "xe_migrate.h"
108e5a5dc0SMatthew Brost #include "xe_module.h"
11ab498828SMatthew Brost #include "xe_pt.h"
126fd979c2SMatthew Brost #include "xe_svm.h"
13ecacec0fSMatthew Brost #include "xe_ttm_vram_mgr.h"
146fd979c2SMatthew Brost #include "xe_vm.h"
156fd979c2SMatthew Brost #include "xe_vm_types.h"
166fd979c2SMatthew Brost 
xe_svm_range_in_vram(struct xe_svm_range * range)17d92eabb3SMatthew Brost static bool xe_svm_range_in_vram(struct xe_svm_range *range)
18d92eabb3SMatthew Brost {
19794f5493SMatthew Brost 	/*
20794f5493SMatthew Brost 	 * Advisory only check whether the range is currently backed by VRAM
21794f5493SMatthew Brost 	 * memory.
22794f5493SMatthew Brost 	 */
23794f5493SMatthew Brost 
24794f5493SMatthew Brost 	struct drm_gpusvm_range_flags flags = {
25794f5493SMatthew Brost 		/* Pairs with WRITE_ONCE in drm_gpusvm.c */
26794f5493SMatthew Brost 		.__flags = READ_ONCE(range->base.flags.__flags),
27794f5493SMatthew Brost 	};
28794f5493SMatthew Brost 
29794f5493SMatthew Brost 	return flags.has_devmem_pages;
30d92eabb3SMatthew Brost }
31d92eabb3SMatthew Brost 
xe_svm_range_has_vram_binding(struct xe_svm_range * range)32d92eabb3SMatthew Brost static bool xe_svm_range_has_vram_binding(struct xe_svm_range *range)
33d92eabb3SMatthew Brost {
34d92eabb3SMatthew Brost 	/* Not reliable without notifier lock */
35d92eabb3SMatthew Brost 	return xe_svm_range_in_vram(range) && range->tile_present;
36d92eabb3SMatthew Brost }
37d92eabb3SMatthew Brost 
gpusvm_to_vm(struct drm_gpusvm * gpusvm)38ab498828SMatthew Brost static struct xe_vm *gpusvm_to_vm(struct drm_gpusvm *gpusvm)
39ab498828SMatthew Brost {
40ab498828SMatthew Brost 	return container_of(gpusvm, struct xe_vm, svm.gpusvm);
41ab498828SMatthew Brost }
42ab498828SMatthew Brost 
range_to_vm(struct drm_gpusvm_range * r)43ab498828SMatthew Brost static struct xe_vm *range_to_vm(struct drm_gpusvm_range *r)
44ab498828SMatthew Brost {
45ab498828SMatthew Brost 	return gpusvm_to_vm(r->gpusvm);
46ab498828SMatthew Brost }
47ab498828SMatthew Brost 
xe_svm_range_start(struct xe_svm_range * range)48ab498828SMatthew Brost static unsigned long xe_svm_range_start(struct xe_svm_range *range)
49ab498828SMatthew Brost {
50ab498828SMatthew Brost 	return drm_gpusvm_range_start(&range->base);
51ab498828SMatthew Brost }
52ab498828SMatthew Brost 
xe_svm_range_end(struct xe_svm_range * range)53ab498828SMatthew Brost static unsigned long xe_svm_range_end(struct xe_svm_range *range)
54ab498828SMatthew Brost {
55ab498828SMatthew Brost 	return drm_gpusvm_range_end(&range->base);
56ab498828SMatthew Brost }
57ab498828SMatthew Brost 
xe_svm_range_size(struct xe_svm_range * range)582f118c94SMatthew Brost static unsigned long xe_svm_range_size(struct xe_svm_range *range)
592f118c94SMatthew Brost {
602f118c94SMatthew Brost 	return drm_gpusvm_range_size(&range->base);
612f118c94SMatthew Brost }
622f118c94SMatthew Brost 
63d92eabb3SMatthew Brost #define range_debug(r__, operaton__)					\
64d92eabb3SMatthew Brost 	vm_dbg(&range_to_vm(&(r__)->base)->xe->drm,			\
65d92eabb3SMatthew Brost 	       "%s: asid=%u, gpusvm=%p, vram=%d,%d, seqno=%lu, " \
66d92eabb3SMatthew Brost 	       "start=0x%014lx, end=0x%014lx, size=%lu",		\
67d92eabb3SMatthew Brost 	       (operaton__), range_to_vm(&(r__)->base)->usm.asid,	\
68d92eabb3SMatthew Brost 	       (r__)->base.gpusvm,					\
69d92eabb3SMatthew Brost 	       xe_svm_range_in_vram((r__)) ? 1 : 0,			\
70d92eabb3SMatthew Brost 	       xe_svm_range_has_vram_binding((r__)) ? 1 : 0,		\
71d92eabb3SMatthew Brost 	       (r__)->base.notifier_seq,				\
72d92eabb3SMatthew Brost 	       xe_svm_range_start((r__)), xe_svm_range_end((r__)),	\
73d92eabb3SMatthew Brost 	       xe_svm_range_size((r__)))
74d92eabb3SMatthew Brost 
xe_svm_range_debug(struct xe_svm_range * range,const char * operation)75d92eabb3SMatthew Brost void xe_svm_range_debug(struct xe_svm_range *range, const char *operation)
76d92eabb3SMatthew Brost {
77d92eabb3SMatthew Brost 	range_debug(range, operation);
78d92eabb3SMatthew Brost }
79d92eabb3SMatthew Brost 
xe_svm_devm_owner(struct xe_device * xe)800c30c654SMatthew Brost static void *xe_svm_devm_owner(struct xe_device *xe)
810c30c654SMatthew Brost {
820c30c654SMatthew Brost 	return xe;
830c30c654SMatthew Brost }
840c30c654SMatthew Brost 
85ab498828SMatthew Brost static struct drm_gpusvm_range *
xe_svm_range_alloc(struct drm_gpusvm * gpusvm)86ab498828SMatthew Brost xe_svm_range_alloc(struct drm_gpusvm *gpusvm)
87ab498828SMatthew Brost {
88ab498828SMatthew Brost 	struct xe_svm_range *range;
89ab498828SMatthew Brost 
90ab498828SMatthew Brost 	range = kzalloc(sizeof(*range), GFP_KERNEL);
91ab498828SMatthew Brost 	if (!range)
92c1c9cad5SHarshit Mogalapalli 		return NULL;
93ab498828SMatthew Brost 
9463f6e480SMatthew Brost 	INIT_LIST_HEAD(&range->garbage_collector_link);
95ab498828SMatthew Brost 	xe_vm_get(gpusvm_to_vm(gpusvm));
96ab498828SMatthew Brost 
97ab498828SMatthew Brost 	return &range->base;
98ab498828SMatthew Brost }
99ab498828SMatthew Brost 
xe_svm_range_free(struct drm_gpusvm_range * range)100ab498828SMatthew Brost static void xe_svm_range_free(struct drm_gpusvm_range *range)
101ab498828SMatthew Brost {
102ab498828SMatthew Brost 	xe_vm_put(range_to_vm(range));
103ab498828SMatthew Brost 	kfree(range);
104ab498828SMatthew Brost }
105ab498828SMatthew Brost 
to_xe_range(struct drm_gpusvm_range * r)106ab498828SMatthew Brost static struct xe_svm_range *to_xe_range(struct drm_gpusvm_range *r)
107ab498828SMatthew Brost {
108ab498828SMatthew Brost 	return container_of(r, struct xe_svm_range, base);
109ab498828SMatthew Brost }
110ab498828SMatthew Brost 
11163f6e480SMatthew Brost static void
xe_svm_garbage_collector_add_range(struct xe_vm * vm,struct xe_svm_range * range,const struct mmu_notifier_range * mmu_range)11263f6e480SMatthew Brost xe_svm_garbage_collector_add_range(struct xe_vm *vm, struct xe_svm_range *range,
11363f6e480SMatthew Brost 				   const struct mmu_notifier_range *mmu_range)
11463f6e480SMatthew Brost {
11563f6e480SMatthew Brost 	struct xe_device *xe = vm->xe;
11663f6e480SMatthew Brost 
117d92eabb3SMatthew Brost 	range_debug(range, "GARBAGE COLLECTOR ADD");
118d92eabb3SMatthew Brost 
11963f6e480SMatthew Brost 	drm_gpusvm_range_set_unmapped(&range->base, mmu_range);
12063f6e480SMatthew Brost 
12163f6e480SMatthew Brost 	spin_lock(&vm->svm.garbage_collector.lock);
12263f6e480SMatthew Brost 	if (list_empty(&range->garbage_collector_link))
12363f6e480SMatthew Brost 		list_add_tail(&range->garbage_collector_link,
12463f6e480SMatthew Brost 			      &vm->svm.garbage_collector.range_list);
12563f6e480SMatthew Brost 	spin_unlock(&vm->svm.garbage_collector.lock);
12663f6e480SMatthew Brost 
12763f6e480SMatthew Brost 	queue_work(xe_device_get_root_tile(xe)->primary_gt->usm.pf_wq,
12863f6e480SMatthew Brost 		   &vm->svm.garbage_collector.work);
12963f6e480SMatthew Brost }
13063f6e480SMatthew Brost 
131ab498828SMatthew Brost static u8
xe_svm_range_notifier_event_begin(struct xe_vm * vm,struct drm_gpusvm_range * r,const struct mmu_notifier_range * mmu_range,u64 * adj_start,u64 * adj_end)132ab498828SMatthew Brost xe_svm_range_notifier_event_begin(struct xe_vm *vm, struct drm_gpusvm_range *r,
133ab498828SMatthew Brost 				  const struct mmu_notifier_range *mmu_range,
134ab498828SMatthew Brost 				  u64 *adj_start, u64 *adj_end)
135ab498828SMatthew Brost {
136ab498828SMatthew Brost 	struct xe_svm_range *range = to_xe_range(r);
137ab498828SMatthew Brost 	struct xe_device *xe = vm->xe;
138ab498828SMatthew Brost 	struct xe_tile *tile;
139ab498828SMatthew Brost 	u8 tile_mask = 0;
140ab498828SMatthew Brost 	u8 id;
141ab498828SMatthew Brost 
142ab498828SMatthew Brost 	xe_svm_assert_in_notifier(vm);
143ab498828SMatthew Brost 
144d92eabb3SMatthew Brost 	range_debug(range, "NOTIFIER");
145d92eabb3SMatthew Brost 
146ab498828SMatthew Brost 	/* Skip if already unmapped or if no binding exist */
147ab498828SMatthew Brost 	if (range->base.flags.unmapped || !range->tile_present)
148ab498828SMatthew Brost 		return 0;
149ab498828SMatthew Brost 
150d92eabb3SMatthew Brost 	range_debug(range, "NOTIFIER - EXECUTE");
151d92eabb3SMatthew Brost 
152ab498828SMatthew Brost 	/* Adjust invalidation to range boundaries */
153ab498828SMatthew Brost 	*adj_start = min(xe_svm_range_start(range), mmu_range->start);
154ab498828SMatthew Brost 	*adj_end = max(xe_svm_range_end(range), mmu_range->end);
155ab498828SMatthew Brost 
156ab498828SMatthew Brost 	/*
157ab498828SMatthew Brost 	 * XXX: Ideally would zap PTEs in one shot in xe_svm_invalidate but the
158ab498828SMatthew Brost 	 * invalidation code can't correctly cope with sparse ranges or
159ab498828SMatthew Brost 	 * invalidations spanning multiple ranges.
160ab498828SMatthew Brost 	 */
161ab498828SMatthew Brost 	for_each_tile(tile, xe, id)
162ab498828SMatthew Brost 		if (xe_pt_zap_ptes_range(tile, vm, range)) {
163ab498828SMatthew Brost 			tile_mask |= BIT(id);
164ab498828SMatthew Brost 			range->tile_invalidated |= BIT(id);
165ab498828SMatthew Brost 		}
166ab498828SMatthew Brost 
167ab498828SMatthew Brost 	return tile_mask;
168ab498828SMatthew Brost }
169ab498828SMatthew Brost 
170ab498828SMatthew Brost static void
xe_svm_range_notifier_event_end(struct xe_vm * vm,struct drm_gpusvm_range * r,const struct mmu_notifier_range * mmu_range)171ab498828SMatthew Brost xe_svm_range_notifier_event_end(struct xe_vm *vm, struct drm_gpusvm_range *r,
172ab498828SMatthew Brost 				const struct mmu_notifier_range *mmu_range)
173ab498828SMatthew Brost {
174ab498828SMatthew Brost 	struct drm_gpusvm_ctx ctx = { .in_notifier = true, };
175ab498828SMatthew Brost 
176ab498828SMatthew Brost 	xe_svm_assert_in_notifier(vm);
177ab498828SMatthew Brost 
178ab498828SMatthew Brost 	drm_gpusvm_range_unmap_pages(&vm->svm.gpusvm, r, &ctx);
17963f6e480SMatthew Brost 	if (!xe_vm_is_closed(vm) && mmu_range->event == MMU_NOTIFY_UNMAP)
18063f6e480SMatthew Brost 		xe_svm_garbage_collector_add_range(vm, to_xe_range(r),
18163f6e480SMatthew Brost 						   mmu_range);
182ab498828SMatthew Brost }
183ab498828SMatthew Brost 
xe_svm_invalidate(struct drm_gpusvm * gpusvm,struct drm_gpusvm_notifier * notifier,const struct mmu_notifier_range * mmu_range)1846fd979c2SMatthew Brost static void xe_svm_invalidate(struct drm_gpusvm *gpusvm,
1856fd979c2SMatthew Brost 			      struct drm_gpusvm_notifier *notifier,
1866fd979c2SMatthew Brost 			      const struct mmu_notifier_range *mmu_range)
1876fd979c2SMatthew Brost {
188ab498828SMatthew Brost 	struct xe_vm *vm = gpusvm_to_vm(gpusvm);
189ab498828SMatthew Brost 	struct xe_device *xe = vm->xe;
190ab498828SMatthew Brost 	struct xe_tile *tile;
191ab498828SMatthew Brost 	struct drm_gpusvm_range *r, *first;
192ab498828SMatthew Brost 	struct xe_gt_tlb_invalidation_fence
193ab498828SMatthew Brost 		fence[XE_MAX_TILES_PER_DEVICE * XE_MAX_GT_PER_TILE];
194ab498828SMatthew Brost 	u64 adj_start = mmu_range->start, adj_end = mmu_range->end;
195ab498828SMatthew Brost 	u8 tile_mask = 0;
196ab498828SMatthew Brost 	u8 id;
197ab498828SMatthew Brost 	u32 fence_id = 0;
198ab498828SMatthew Brost 	long err;
199ab498828SMatthew Brost 
200ab498828SMatthew Brost 	xe_svm_assert_in_notifier(vm);
201ab498828SMatthew Brost 
202d92eabb3SMatthew Brost 	vm_dbg(&gpusvm_to_vm(gpusvm)->xe->drm,
203d92eabb3SMatthew Brost 	       "INVALIDATE: asid=%u, gpusvm=%p, seqno=%lu, start=0x%016lx, end=0x%016lx, event=%d",
204d92eabb3SMatthew Brost 	       vm->usm.asid, gpusvm, notifier->notifier.invalidate_seq,
205d92eabb3SMatthew Brost 	       mmu_range->start, mmu_range->end, mmu_range->event);
206d92eabb3SMatthew Brost 
207ab498828SMatthew Brost 	/* Adjust invalidation to notifier boundaries */
208ab498828SMatthew Brost 	adj_start = max(drm_gpusvm_notifier_start(notifier), adj_start);
209ab498828SMatthew Brost 	adj_end = min(drm_gpusvm_notifier_end(notifier), adj_end);
210ab498828SMatthew Brost 
211ab498828SMatthew Brost 	first = drm_gpusvm_range_find(notifier, adj_start, adj_end);
212ab498828SMatthew Brost 	if (!first)
213ab498828SMatthew Brost 		return;
214ab498828SMatthew Brost 
215ab498828SMatthew Brost 	/*
216ab498828SMatthew Brost 	 * PTs may be getting destroyed so not safe to touch these but PT should
217ab498828SMatthew Brost 	 * be invalidated at this point in time. Regardless we still need to
218ab498828SMatthew Brost 	 * ensure any dma mappings are unmapped in the here.
219ab498828SMatthew Brost 	 */
220ab498828SMatthew Brost 	if (xe_vm_is_closed(vm))
221ab498828SMatthew Brost 		goto range_notifier_event_end;
222ab498828SMatthew Brost 
223ab498828SMatthew Brost 	/*
224ab498828SMatthew Brost 	 * XXX: Less than ideal to always wait on VM's resv slots if an
225ab498828SMatthew Brost 	 * invalidation is not required. Could walk range list twice to figure
226ab498828SMatthew Brost 	 * out if an invalidations is need, but also not ideal.
227ab498828SMatthew Brost 	 */
228ab498828SMatthew Brost 	err = dma_resv_wait_timeout(xe_vm_resv(vm),
229ab498828SMatthew Brost 				    DMA_RESV_USAGE_BOOKKEEP,
230ab498828SMatthew Brost 				    false, MAX_SCHEDULE_TIMEOUT);
231ab498828SMatthew Brost 	XE_WARN_ON(err <= 0);
232ab498828SMatthew Brost 
233ab498828SMatthew Brost 	r = first;
234ab498828SMatthew Brost 	drm_gpusvm_for_each_range(r, notifier, adj_start, adj_end)
235ab498828SMatthew Brost 		tile_mask |= xe_svm_range_notifier_event_begin(vm, r, mmu_range,
236ab498828SMatthew Brost 							       &adj_start,
237ab498828SMatthew Brost 							       &adj_end);
238ab498828SMatthew Brost 	if (!tile_mask)
239ab498828SMatthew Brost 		goto range_notifier_event_end;
240ab498828SMatthew Brost 
241ab498828SMatthew Brost 	xe_device_wmb(xe);
242ab498828SMatthew Brost 
243ab498828SMatthew Brost 	for_each_tile(tile, xe, id) {
244ab498828SMatthew Brost 		if (tile_mask & BIT(id)) {
245ab498828SMatthew Brost 			int err;
246ab498828SMatthew Brost 
247ab498828SMatthew Brost 			xe_gt_tlb_invalidation_fence_init(tile->primary_gt,
248ab498828SMatthew Brost 							  &fence[fence_id], true);
249ab498828SMatthew Brost 
250ab498828SMatthew Brost 			err = xe_gt_tlb_invalidation_range(tile->primary_gt,
251ab498828SMatthew Brost 							   &fence[fence_id],
252ab498828SMatthew Brost 							   adj_start,
253ab498828SMatthew Brost 							   adj_end,
254ab498828SMatthew Brost 							   vm->usm.asid);
255ab498828SMatthew Brost 			if (WARN_ON_ONCE(err < 0))
256ab498828SMatthew Brost 				goto wait;
257ab498828SMatthew Brost 			++fence_id;
258ab498828SMatthew Brost 
259ab498828SMatthew Brost 			if (!tile->media_gt)
260ab498828SMatthew Brost 				continue;
261ab498828SMatthew Brost 
262ab498828SMatthew Brost 			xe_gt_tlb_invalidation_fence_init(tile->media_gt,
263ab498828SMatthew Brost 							  &fence[fence_id], true);
264ab498828SMatthew Brost 
265ab498828SMatthew Brost 			err = xe_gt_tlb_invalidation_range(tile->media_gt,
266ab498828SMatthew Brost 							   &fence[fence_id],
267ab498828SMatthew Brost 							   adj_start,
268ab498828SMatthew Brost 							   adj_end,
269ab498828SMatthew Brost 							   vm->usm.asid);
270ab498828SMatthew Brost 			if (WARN_ON_ONCE(err < 0))
271ab498828SMatthew Brost 				goto wait;
272ab498828SMatthew Brost 			++fence_id;
273ab498828SMatthew Brost 		}
274ab498828SMatthew Brost 	}
275ab498828SMatthew Brost 
276ab498828SMatthew Brost wait:
277ab498828SMatthew Brost 	for (id = 0; id < fence_id; ++id)
278ab498828SMatthew Brost 		xe_gt_tlb_invalidation_fence_wait(&fence[id]);
279ab498828SMatthew Brost 
280ab498828SMatthew Brost range_notifier_event_end:
281ab498828SMatthew Brost 	r = first;
282ab498828SMatthew Brost 	drm_gpusvm_for_each_range(r, notifier, adj_start, adj_end)
283ab498828SMatthew Brost 		xe_svm_range_notifier_event_end(vm, r, mmu_range);
2846fd979c2SMatthew Brost }
2856fd979c2SMatthew Brost 
__xe_svm_garbage_collector(struct xe_vm * vm,struct xe_svm_range * range)28663f6e480SMatthew Brost static int __xe_svm_garbage_collector(struct xe_vm *vm,
28763f6e480SMatthew Brost 				      struct xe_svm_range *range)
28863f6e480SMatthew Brost {
289d1e6efdfSMatthew Brost 	struct dma_fence *fence;
290d1e6efdfSMatthew Brost 
291d92eabb3SMatthew Brost 	range_debug(range, "GARBAGE COLLECTOR");
292d92eabb3SMatthew Brost 
293d1e6efdfSMatthew Brost 	xe_vm_lock(vm, false);
294d1e6efdfSMatthew Brost 	fence = xe_vm_range_unbind(vm, range);
295d1e6efdfSMatthew Brost 	xe_vm_unlock(vm);
296d1e6efdfSMatthew Brost 	if (IS_ERR(fence))
297d1e6efdfSMatthew Brost 		return PTR_ERR(fence);
298d1e6efdfSMatthew Brost 	dma_fence_put(fence);
29963f6e480SMatthew Brost 
30063f6e480SMatthew Brost 	drm_gpusvm_range_remove(&vm->svm.gpusvm, &range->base);
30163f6e480SMatthew Brost 
30263f6e480SMatthew Brost 	return 0;
30363f6e480SMatthew Brost }
30463f6e480SMatthew Brost 
xe_svm_garbage_collector(struct xe_vm * vm)30563f6e480SMatthew Brost static int xe_svm_garbage_collector(struct xe_vm *vm)
30663f6e480SMatthew Brost {
30763f6e480SMatthew Brost 	struct xe_svm_range *range;
30863f6e480SMatthew Brost 	int err;
30963f6e480SMatthew Brost 
31063f6e480SMatthew Brost 	lockdep_assert_held_write(&vm->lock);
31163f6e480SMatthew Brost 
31263f6e480SMatthew Brost 	if (xe_vm_is_closed_or_banned(vm))
31363f6e480SMatthew Brost 		return -ENOENT;
31463f6e480SMatthew Brost 
31563f6e480SMatthew Brost 	spin_lock(&vm->svm.garbage_collector.lock);
31663f6e480SMatthew Brost 	for (;;) {
31763f6e480SMatthew Brost 		range = list_first_entry_or_null(&vm->svm.garbage_collector.range_list,
31863f6e480SMatthew Brost 						 typeof(*range),
31963f6e480SMatthew Brost 						 garbage_collector_link);
32063f6e480SMatthew Brost 		if (!range)
32163f6e480SMatthew Brost 			break;
32263f6e480SMatthew Brost 
32363f6e480SMatthew Brost 		list_del(&range->garbage_collector_link);
32463f6e480SMatthew Brost 		spin_unlock(&vm->svm.garbage_collector.lock);
32563f6e480SMatthew Brost 
32663f6e480SMatthew Brost 		err = __xe_svm_garbage_collector(vm, range);
32763f6e480SMatthew Brost 		if (err) {
32863f6e480SMatthew Brost 			drm_warn(&vm->xe->drm,
32963f6e480SMatthew Brost 				 "Garbage collection failed: %pe\n",
33063f6e480SMatthew Brost 				 ERR_PTR(err));
33163f6e480SMatthew Brost 			xe_vm_kill(vm, true);
33263f6e480SMatthew Brost 			return err;
33363f6e480SMatthew Brost 		}
33463f6e480SMatthew Brost 
33563f6e480SMatthew Brost 		spin_lock(&vm->svm.garbage_collector.lock);
33663f6e480SMatthew Brost 	}
33763f6e480SMatthew Brost 	spin_unlock(&vm->svm.garbage_collector.lock);
33863f6e480SMatthew Brost 
33963f6e480SMatthew Brost 	return 0;
34063f6e480SMatthew Brost }
34163f6e480SMatthew Brost 
xe_svm_garbage_collector_work_func(struct work_struct * w)34263f6e480SMatthew Brost static void xe_svm_garbage_collector_work_func(struct work_struct *w)
34363f6e480SMatthew Brost {
34463f6e480SMatthew Brost 	struct xe_vm *vm = container_of(w, struct xe_vm,
34563f6e480SMatthew Brost 					svm.garbage_collector.work);
34663f6e480SMatthew Brost 
34763f6e480SMatthew Brost 	down_write(&vm->lock);
34863f6e480SMatthew Brost 	xe_svm_garbage_collector(vm);
34963f6e480SMatthew Brost 	up_write(&vm->lock);
35063f6e480SMatthew Brost }
35163f6e480SMatthew Brost 
3526c55404dSThomas Hellström #if IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR)
3536c55404dSThomas Hellström 
page_to_vr(struct page * page)35411bbe0d9SThomas Hellström static struct xe_vram_region *page_to_vr(struct page *page)
35511bbe0d9SThomas Hellström {
356eb0ece16SLinus Torvalds 	return container_of(page_pgmap(page), struct xe_vram_region, pagemap);
35711bbe0d9SThomas Hellström }
35811bbe0d9SThomas Hellström 
vr_to_tile(struct xe_vram_region * vr)35911bbe0d9SThomas Hellström static struct xe_tile *vr_to_tile(struct xe_vram_region *vr)
36011bbe0d9SThomas Hellström {
36111bbe0d9SThomas Hellström 	return container_of(vr, struct xe_tile, mem.vram);
36211bbe0d9SThomas Hellström }
36311bbe0d9SThomas Hellström 
xe_vram_region_page_to_dpa(struct xe_vram_region * vr,struct page * page)36411bbe0d9SThomas Hellström static u64 xe_vram_region_page_to_dpa(struct xe_vram_region *vr,
36511bbe0d9SThomas Hellström 				      struct page *page)
36611bbe0d9SThomas Hellström {
36711bbe0d9SThomas Hellström 	u64 dpa;
36811bbe0d9SThomas Hellström 	struct xe_tile *tile = vr_to_tile(vr);
36911bbe0d9SThomas Hellström 	u64 pfn = page_to_pfn(page);
37011bbe0d9SThomas Hellström 	u64 offset;
37111bbe0d9SThomas Hellström 
37211bbe0d9SThomas Hellström 	xe_tile_assert(tile, is_device_private_page(page));
37311bbe0d9SThomas Hellström 	xe_tile_assert(tile, (pfn << PAGE_SHIFT) >= vr->hpa_base);
37411bbe0d9SThomas Hellström 
37511bbe0d9SThomas Hellström 	offset = (pfn << PAGE_SHIFT) - vr->hpa_base;
37611bbe0d9SThomas Hellström 	dpa = vr->dpa_base + offset;
37711bbe0d9SThomas Hellström 
37811bbe0d9SThomas Hellström 	return dpa;
37911bbe0d9SThomas Hellström }
38011bbe0d9SThomas Hellström 
381c5b3eb5aSMatthew Brost enum xe_svm_copy_dir {
382c5b3eb5aSMatthew Brost 	XE_SVM_COPY_TO_VRAM,
383c5b3eb5aSMatthew Brost 	XE_SVM_COPY_TO_SRAM,
384c5b3eb5aSMatthew Brost };
385c5b3eb5aSMatthew Brost 
xe_svm_copy(struct page ** pages,dma_addr_t * dma_addr,unsigned long npages,const enum xe_svm_copy_dir dir)386c5b3eb5aSMatthew Brost static int xe_svm_copy(struct page **pages, dma_addr_t *dma_addr,
387c5b3eb5aSMatthew Brost 		       unsigned long npages, const enum xe_svm_copy_dir dir)
388c5b3eb5aSMatthew Brost {
389c5b3eb5aSMatthew Brost 	struct xe_vram_region *vr = NULL;
390c5b3eb5aSMatthew Brost 	struct xe_tile *tile;
391c5b3eb5aSMatthew Brost 	struct dma_fence *fence = NULL;
392c5b3eb5aSMatthew Brost 	unsigned long i;
393c5b3eb5aSMatthew Brost #define XE_VRAM_ADDR_INVALID	~0x0ull
394c5b3eb5aSMatthew Brost 	u64 vram_addr = XE_VRAM_ADDR_INVALID;
395c5b3eb5aSMatthew Brost 	int err = 0, pos = 0;
396c5b3eb5aSMatthew Brost 	bool sram = dir == XE_SVM_COPY_TO_SRAM;
397c5b3eb5aSMatthew Brost 
398c5b3eb5aSMatthew Brost 	/*
399c5b3eb5aSMatthew Brost 	 * This flow is complex: it locates physically contiguous device pages,
400c5b3eb5aSMatthew Brost 	 * derives the starting physical address, and performs a single GPU copy
401c5b3eb5aSMatthew Brost 	 * to for every 8M chunk in a DMA address array. Both device pages and
402c5b3eb5aSMatthew Brost 	 * DMA addresses may be sparsely populated. If either is NULL, a copy is
403c5b3eb5aSMatthew Brost 	 * triggered based on the current search state. The last GPU copy is
404c5b3eb5aSMatthew Brost 	 * waited on to ensure all copies are complete.
405c5b3eb5aSMatthew Brost 	 */
406c5b3eb5aSMatthew Brost 
407c5b3eb5aSMatthew Brost 	for (i = 0; i < npages; ++i) {
408c5b3eb5aSMatthew Brost 		struct page *spage = pages[i];
409c5b3eb5aSMatthew Brost 		struct dma_fence *__fence;
410c5b3eb5aSMatthew Brost 		u64 __vram_addr;
411c5b3eb5aSMatthew Brost 		bool match = false, chunk, last;
412c5b3eb5aSMatthew Brost 
413c5b3eb5aSMatthew Brost #define XE_MIGRATE_CHUNK_SIZE	SZ_8M
414c5b3eb5aSMatthew Brost 		chunk = (i - pos) == (XE_MIGRATE_CHUNK_SIZE / PAGE_SIZE);
415c5b3eb5aSMatthew Brost 		last = (i + 1) == npages;
416c5b3eb5aSMatthew Brost 
417c5b3eb5aSMatthew Brost 		/* No CPU page and no device pages queue'd to copy */
418c5b3eb5aSMatthew Brost 		if (!dma_addr[i] && vram_addr == XE_VRAM_ADDR_INVALID)
419c5b3eb5aSMatthew Brost 			continue;
420c5b3eb5aSMatthew Brost 
421c5b3eb5aSMatthew Brost 		if (!vr && spage) {
422c5b3eb5aSMatthew Brost 			vr = page_to_vr(spage);
423c5b3eb5aSMatthew Brost 			tile = vr_to_tile(vr);
424c5b3eb5aSMatthew Brost 		}
425c5b3eb5aSMatthew Brost 		XE_WARN_ON(spage && page_to_vr(spage) != vr);
426c5b3eb5aSMatthew Brost 
427c5b3eb5aSMatthew Brost 		/*
428c5b3eb5aSMatthew Brost 		 * CPU page and device page valid, capture physical address on
429c5b3eb5aSMatthew Brost 		 * first device page, check if physical contiguous on subsequent
430c5b3eb5aSMatthew Brost 		 * device pages.
431c5b3eb5aSMatthew Brost 		 */
432c5b3eb5aSMatthew Brost 		if (dma_addr[i] && spage) {
433c5b3eb5aSMatthew Brost 			__vram_addr = xe_vram_region_page_to_dpa(vr, spage);
434c5b3eb5aSMatthew Brost 			if (vram_addr == XE_VRAM_ADDR_INVALID) {
435c5b3eb5aSMatthew Brost 				vram_addr = __vram_addr;
436c5b3eb5aSMatthew Brost 				pos = i;
437c5b3eb5aSMatthew Brost 			}
438c5b3eb5aSMatthew Brost 
439c5b3eb5aSMatthew Brost 			match = vram_addr + PAGE_SIZE * (i - pos) == __vram_addr;
440c5b3eb5aSMatthew Brost 		}
441c5b3eb5aSMatthew Brost 
442c5b3eb5aSMatthew Brost 		/*
443c5b3eb5aSMatthew Brost 		 * Mismatched physical address, 8M copy chunk, or last page -
444c5b3eb5aSMatthew Brost 		 * trigger a copy.
445c5b3eb5aSMatthew Brost 		 */
446c5b3eb5aSMatthew Brost 		if (!match || chunk || last) {
447c5b3eb5aSMatthew Brost 			/*
448c5b3eb5aSMatthew Brost 			 * Extra page for first copy if last page and matching
449c5b3eb5aSMatthew Brost 			 * physical address.
450c5b3eb5aSMatthew Brost 			 */
451c5b3eb5aSMatthew Brost 			int incr = (match && last) ? 1 : 0;
452c5b3eb5aSMatthew Brost 
453c5b3eb5aSMatthew Brost 			if (vram_addr != XE_VRAM_ADDR_INVALID) {
454d92eabb3SMatthew Brost 				if (sram) {
455d92eabb3SMatthew Brost 					vm_dbg(&tile->xe->drm,
456d92eabb3SMatthew Brost 					       "COPY TO SRAM - 0x%016llx -> 0x%016llx, NPAGES=%ld",
457d92eabb3SMatthew Brost 					       vram_addr, (u64)dma_addr[pos], i - pos + incr);
458c5b3eb5aSMatthew Brost 					__fence = xe_migrate_from_vram(tile->migrate,
459c5b3eb5aSMatthew Brost 								       i - pos + incr,
460c5b3eb5aSMatthew Brost 								       vram_addr,
461c5b3eb5aSMatthew Brost 								       dma_addr + pos);
462d92eabb3SMatthew Brost 				} else {
463d92eabb3SMatthew Brost 					vm_dbg(&tile->xe->drm,
464d92eabb3SMatthew Brost 					       "COPY TO VRAM - 0x%016llx -> 0x%016llx, NPAGES=%ld",
465d92eabb3SMatthew Brost 					       (u64)dma_addr[pos], vram_addr, i - pos + incr);
466c5b3eb5aSMatthew Brost 					__fence = xe_migrate_to_vram(tile->migrate,
467c5b3eb5aSMatthew Brost 								     i - pos + incr,
468c5b3eb5aSMatthew Brost 								     dma_addr + pos,
469c5b3eb5aSMatthew Brost 								     vram_addr);
470d92eabb3SMatthew Brost 				}
471c5b3eb5aSMatthew Brost 				if (IS_ERR(__fence)) {
472c5b3eb5aSMatthew Brost 					err = PTR_ERR(__fence);
473c5b3eb5aSMatthew Brost 					goto err_out;
474c5b3eb5aSMatthew Brost 				}
475c5b3eb5aSMatthew Brost 
476c5b3eb5aSMatthew Brost 				dma_fence_put(fence);
477c5b3eb5aSMatthew Brost 				fence = __fence;
478c5b3eb5aSMatthew Brost 			}
479c5b3eb5aSMatthew Brost 
480c5b3eb5aSMatthew Brost 			/* Setup physical address of next device page */
481c5b3eb5aSMatthew Brost 			if (dma_addr[i] && spage) {
482c5b3eb5aSMatthew Brost 				vram_addr = __vram_addr;
483c5b3eb5aSMatthew Brost 				pos = i;
484c5b3eb5aSMatthew Brost 			} else {
485c5b3eb5aSMatthew Brost 				vram_addr = XE_VRAM_ADDR_INVALID;
486c5b3eb5aSMatthew Brost 			}
487c5b3eb5aSMatthew Brost 
488c5b3eb5aSMatthew Brost 			/* Extra mismatched device page, copy it */
489c5b3eb5aSMatthew Brost 			if (!match && last && vram_addr != XE_VRAM_ADDR_INVALID) {
490d92eabb3SMatthew Brost 				if (sram) {
491d92eabb3SMatthew Brost 					vm_dbg(&tile->xe->drm,
492d92eabb3SMatthew Brost 					       "COPY TO SRAM - 0x%016llx -> 0x%016llx, NPAGES=%d",
493d92eabb3SMatthew Brost 					       vram_addr, (u64)dma_addr[pos], 1);
494c5b3eb5aSMatthew Brost 					__fence = xe_migrate_from_vram(tile->migrate, 1,
495c5b3eb5aSMatthew Brost 								       vram_addr,
496c5b3eb5aSMatthew Brost 								       dma_addr + pos);
497d92eabb3SMatthew Brost 				} else {
498d92eabb3SMatthew Brost 					vm_dbg(&tile->xe->drm,
499d92eabb3SMatthew Brost 					       "COPY TO VRAM - 0x%016llx -> 0x%016llx, NPAGES=%d",
500d92eabb3SMatthew Brost 					       (u64)dma_addr[pos], vram_addr, 1);
501c5b3eb5aSMatthew Brost 					__fence = xe_migrate_to_vram(tile->migrate, 1,
502c5b3eb5aSMatthew Brost 								     dma_addr + pos,
503c5b3eb5aSMatthew Brost 								     vram_addr);
504d92eabb3SMatthew Brost 				}
505c5b3eb5aSMatthew Brost 				if (IS_ERR(__fence)) {
506c5b3eb5aSMatthew Brost 					err = PTR_ERR(__fence);
507c5b3eb5aSMatthew Brost 					goto err_out;
508c5b3eb5aSMatthew Brost 				}
509c5b3eb5aSMatthew Brost 
510c5b3eb5aSMatthew Brost 				dma_fence_put(fence);
511c5b3eb5aSMatthew Brost 				fence = __fence;
512c5b3eb5aSMatthew Brost 			}
513c5b3eb5aSMatthew Brost 		}
514c5b3eb5aSMatthew Brost 	}
515c5b3eb5aSMatthew Brost 
516c5b3eb5aSMatthew Brost err_out:
517c5b3eb5aSMatthew Brost 	/* Wait for all copies to complete */
518c5b3eb5aSMatthew Brost 	if (fence) {
519c5b3eb5aSMatthew Brost 		dma_fence_wait(fence, false);
520c5b3eb5aSMatthew Brost 		dma_fence_put(fence);
521c5b3eb5aSMatthew Brost 	}
522c5b3eb5aSMatthew Brost 
523c5b3eb5aSMatthew Brost 	return err;
524c5b3eb5aSMatthew Brost #undef XE_MIGRATE_CHUNK_SIZE
525c5b3eb5aSMatthew Brost #undef XE_VRAM_ADDR_INVALID
526c5b3eb5aSMatthew Brost }
527c5b3eb5aSMatthew Brost 
xe_svm_copy_to_devmem(struct page ** pages,dma_addr_t * dma_addr,unsigned long npages)528c5b3eb5aSMatthew Brost static int xe_svm_copy_to_devmem(struct page **pages, dma_addr_t *dma_addr,
529c5b3eb5aSMatthew Brost 				 unsigned long npages)
530c5b3eb5aSMatthew Brost {
531c5b3eb5aSMatthew Brost 	return xe_svm_copy(pages, dma_addr, npages, XE_SVM_COPY_TO_VRAM);
532c5b3eb5aSMatthew Brost }
533c5b3eb5aSMatthew Brost 
xe_svm_copy_to_ram(struct page ** pages,dma_addr_t * dma_addr,unsigned long npages)534c5b3eb5aSMatthew Brost static int xe_svm_copy_to_ram(struct page **pages, dma_addr_t *dma_addr,
535c5b3eb5aSMatthew Brost 			      unsigned long npages)
536c5b3eb5aSMatthew Brost {
537c5b3eb5aSMatthew Brost 	return xe_svm_copy(pages, dma_addr, npages, XE_SVM_COPY_TO_SRAM);
538c5b3eb5aSMatthew Brost }
539c5b3eb5aSMatthew Brost 
to_xe_bo(struct drm_gpusvm_devmem * devmem_allocation)540ecacec0fSMatthew Brost static struct xe_bo *to_xe_bo(struct drm_gpusvm_devmem *devmem_allocation)
541ecacec0fSMatthew Brost {
542ecacec0fSMatthew Brost 	return container_of(devmem_allocation, struct xe_bo, devmem_allocation);
543ecacec0fSMatthew Brost }
544ecacec0fSMatthew Brost 
xe_svm_devmem_release(struct drm_gpusvm_devmem * devmem_allocation)5455951fed8SMatthew Brost static void xe_svm_devmem_release(struct drm_gpusvm_devmem *devmem_allocation)
5465951fed8SMatthew Brost {
5475951fed8SMatthew Brost 	struct xe_bo *bo = to_xe_bo(devmem_allocation);
5485951fed8SMatthew Brost 
5495951fed8SMatthew Brost 	xe_bo_put_async(bo);
5505951fed8SMatthew Brost }
5515951fed8SMatthew Brost 
block_offset_to_pfn(struct xe_vram_region * vr,u64 offset)552ecacec0fSMatthew Brost static u64 block_offset_to_pfn(struct xe_vram_region *vr, u64 offset)
553ecacec0fSMatthew Brost {
554ecacec0fSMatthew Brost 	return PHYS_PFN(offset + vr->hpa_base);
555ecacec0fSMatthew Brost }
556ecacec0fSMatthew Brost 
tile_to_buddy(struct xe_tile * tile)557ecacec0fSMatthew Brost static struct drm_buddy *tile_to_buddy(struct xe_tile *tile)
558ecacec0fSMatthew Brost {
559ecacec0fSMatthew Brost 	return &tile->mem.vram.ttm.mm;
560ecacec0fSMatthew Brost }
561ecacec0fSMatthew Brost 
xe_svm_populate_devmem_pfn(struct drm_gpusvm_devmem * devmem_allocation,unsigned long npages,unsigned long * pfn)562ecacec0fSMatthew Brost static int xe_svm_populate_devmem_pfn(struct drm_gpusvm_devmem *devmem_allocation,
563ecacec0fSMatthew Brost 				      unsigned long npages, unsigned long *pfn)
564ecacec0fSMatthew Brost {
565ecacec0fSMatthew Brost 	struct xe_bo *bo = to_xe_bo(devmem_allocation);
566ecacec0fSMatthew Brost 	struct ttm_resource *res = bo->ttm.resource;
567ecacec0fSMatthew Brost 	struct list_head *blocks = &to_xe_ttm_vram_mgr_resource(res)->blocks;
568ecacec0fSMatthew Brost 	struct drm_buddy_block *block;
569ecacec0fSMatthew Brost 	int j = 0;
570ecacec0fSMatthew Brost 
571ecacec0fSMatthew Brost 	list_for_each_entry(block, blocks, link) {
572ecacec0fSMatthew Brost 		struct xe_vram_region *vr = block->private;
573ecacec0fSMatthew Brost 		struct xe_tile *tile = vr_to_tile(vr);
574ecacec0fSMatthew Brost 		struct drm_buddy *buddy = tile_to_buddy(tile);
575ecacec0fSMatthew Brost 		u64 block_pfn = block_offset_to_pfn(vr, drm_buddy_block_offset(block));
576ecacec0fSMatthew Brost 		int i;
577ecacec0fSMatthew Brost 
578ecacec0fSMatthew Brost 		for (i = 0; i < drm_buddy_block_size(buddy, block) >> PAGE_SHIFT; ++i)
579ecacec0fSMatthew Brost 			pfn[j++] = block_pfn + i;
580ecacec0fSMatthew Brost 	}
581ecacec0fSMatthew Brost 
582ecacec0fSMatthew Brost 	return 0;
583ecacec0fSMatthew Brost }
584ecacec0fSMatthew Brost 
585c5b3eb5aSMatthew Brost static const struct drm_gpusvm_devmem_ops gpusvm_devmem_ops = {
5865951fed8SMatthew Brost 	.devmem_release = xe_svm_devmem_release,
587ecacec0fSMatthew Brost 	.populate_devmem_pfn = xe_svm_populate_devmem_pfn,
588c5b3eb5aSMatthew Brost 	.copy_to_devmem = xe_svm_copy_to_devmem,
589c5b3eb5aSMatthew Brost 	.copy_to_ram = xe_svm_copy_to_ram,
590c5b3eb5aSMatthew Brost };
591c5b3eb5aSMatthew Brost 
5926c55404dSThomas Hellström #endif
5936c55404dSThomas Hellström 
5946fd979c2SMatthew Brost static const struct drm_gpusvm_ops gpusvm_ops = {
595ab498828SMatthew Brost 	.range_alloc = xe_svm_range_alloc,
596ab498828SMatthew Brost 	.range_free = xe_svm_range_free,
5976fd979c2SMatthew Brost 	.invalidate = xe_svm_invalidate,
5986fd979c2SMatthew Brost };
5996fd979c2SMatthew Brost 
6006fd979c2SMatthew Brost static const unsigned long fault_chunk_sizes[] = {
6016fd979c2SMatthew Brost 	SZ_2M,
6026fd979c2SMatthew Brost 	SZ_64K,
6036fd979c2SMatthew Brost 	SZ_4K,
6046fd979c2SMatthew Brost };
6056fd979c2SMatthew Brost 
6066fd979c2SMatthew Brost /**
6076fd979c2SMatthew Brost  * xe_svm_init() - SVM initialize
6086fd979c2SMatthew Brost  * @vm: The VM.
6096fd979c2SMatthew Brost  *
6106fd979c2SMatthew Brost  * Initialize SVM state which is embedded within the VM.
6116fd979c2SMatthew Brost  *
6126fd979c2SMatthew Brost  * Return: 0 on success, negative error code on error.
6136fd979c2SMatthew Brost  */
xe_svm_init(struct xe_vm * vm)6146fd979c2SMatthew Brost int xe_svm_init(struct xe_vm *vm)
6156fd979c2SMatthew Brost {
6166fd979c2SMatthew Brost 	int err;
6176fd979c2SMatthew Brost 
61863f6e480SMatthew Brost 	spin_lock_init(&vm->svm.garbage_collector.lock);
61963f6e480SMatthew Brost 	INIT_LIST_HEAD(&vm->svm.garbage_collector.range_list);
62063f6e480SMatthew Brost 	INIT_WORK(&vm->svm.garbage_collector.work,
62163f6e480SMatthew Brost 		  xe_svm_garbage_collector_work_func);
62263f6e480SMatthew Brost 
6236fd979c2SMatthew Brost 	err = drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM", &vm->xe->drm,
6240c30c654SMatthew Brost 			      current->mm, xe_svm_devm_owner(vm->xe), 0,
6258e5a5dc0SMatthew Brost 			      vm->size, xe_modparam.svm_notifier_size * SZ_1M,
6268e5a5dc0SMatthew Brost 			      &gpusvm_ops, fault_chunk_sizes,
6276fd979c2SMatthew Brost 			      ARRAY_SIZE(fault_chunk_sizes));
6286fd979c2SMatthew Brost 	if (err)
6296fd979c2SMatthew Brost 		return err;
6306fd979c2SMatthew Brost 
6316fd979c2SMatthew Brost 	drm_gpusvm_driver_set_lock(&vm->svm.gpusvm, &vm->lock);
6326fd979c2SMatthew Brost 
6336fd979c2SMatthew Brost 	return 0;
6346fd979c2SMatthew Brost }
6356fd979c2SMatthew Brost 
6366fd979c2SMatthew Brost /**
6376fd979c2SMatthew Brost  * xe_svm_close() - SVM close
6386fd979c2SMatthew Brost  * @vm: The VM.
6396fd979c2SMatthew Brost  *
6406fd979c2SMatthew Brost  * Close SVM state (i.e., stop and flush all SVM actions).
6416fd979c2SMatthew Brost  */
xe_svm_close(struct xe_vm * vm)6426fd979c2SMatthew Brost void xe_svm_close(struct xe_vm *vm)
6436fd979c2SMatthew Brost {
6446fd979c2SMatthew Brost 	xe_assert(vm->xe, xe_vm_is_closed(vm));
64563f6e480SMatthew Brost 	flush_work(&vm->svm.garbage_collector.work);
6466fd979c2SMatthew Brost }
6476fd979c2SMatthew Brost 
6486fd979c2SMatthew Brost /**
6496fd979c2SMatthew Brost  * xe_svm_fini() - SVM finalize
6506fd979c2SMatthew Brost  * @vm: The VM.
6516fd979c2SMatthew Brost  *
6526fd979c2SMatthew Brost  * Finalize SVM state which is embedded within the VM.
6536fd979c2SMatthew Brost  */
xe_svm_fini(struct xe_vm * vm)6546fd979c2SMatthew Brost void xe_svm_fini(struct xe_vm *vm)
6556fd979c2SMatthew Brost {
6566fd979c2SMatthew Brost 	xe_assert(vm->xe, xe_vm_is_closed(vm));
6576fd979c2SMatthew Brost 
6586fd979c2SMatthew Brost 	drm_gpusvm_fini(&vm->svm.gpusvm);
6596fd979c2SMatthew Brost }
660ab498828SMatthew Brost 
xe_svm_range_is_valid(struct xe_svm_range * range,struct xe_tile * tile,bool devmem_only)6617d1d48fbSMatthew Brost static bool xe_svm_range_is_valid(struct xe_svm_range *range,
662794f5493SMatthew Brost 				  struct xe_tile *tile,
663794f5493SMatthew Brost 				  bool devmem_only)
6647d1d48fbSMatthew Brost {
665794f5493SMatthew Brost 	/*
666794f5493SMatthew Brost 	 * Advisory only check whether the range currently has a valid mapping,
667794f5493SMatthew Brost 	 * READ_ONCE pairs with WRITE_ONCE in xe_pt.c
668794f5493SMatthew Brost 	 */
669794f5493SMatthew Brost 	return ((READ_ONCE(range->tile_present) &
670794f5493SMatthew Brost 		 ~READ_ONCE(range->tile_invalidated)) & BIT(tile->id)) &&
671794f5493SMatthew Brost 		(!devmem_only || xe_svm_range_in_vram(range));
6727d1d48fbSMatthew Brost }
6737d1d48fbSMatthew Brost 
6746c55404dSThomas Hellström #if IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR)
tile_to_vr(struct xe_tile * tile)6752f118c94SMatthew Brost static struct xe_vram_region *tile_to_vr(struct xe_tile *tile)
6762f118c94SMatthew Brost {
6772f118c94SMatthew Brost 	return &tile->mem.vram;
6782f118c94SMatthew Brost }
6792f118c94SMatthew Brost 
xe_svm_alloc_vram(struct xe_vm * vm,struct xe_tile * tile,struct xe_svm_range * range,const struct drm_gpusvm_ctx * ctx)6802f118c94SMatthew Brost static int xe_svm_alloc_vram(struct xe_vm *vm, struct xe_tile *tile,
6812f118c94SMatthew Brost 			     struct xe_svm_range *range,
6822f118c94SMatthew Brost 			     const struct drm_gpusvm_ctx *ctx)
6832f118c94SMatthew Brost {
6842f118c94SMatthew Brost 	struct mm_struct *mm = vm->svm.gpusvm.mm;
6852f118c94SMatthew Brost 	struct xe_vram_region *vr = tile_to_vr(tile);
6862f118c94SMatthew Brost 	struct drm_buddy_block *block;
6872f118c94SMatthew Brost 	struct list_head *blocks;
6882f118c94SMatthew Brost 	struct xe_bo *bo;
6892f118c94SMatthew Brost 	ktime_t end = 0;
6902f118c94SMatthew Brost 	int err;
6912f118c94SMatthew Brost 
692d92eabb3SMatthew Brost 	range_debug(range, "ALLOCATE VRAM");
693d92eabb3SMatthew Brost 
6942f118c94SMatthew Brost 	if (!mmget_not_zero(mm))
6952f118c94SMatthew Brost 		return -EFAULT;
6962f118c94SMatthew Brost 	mmap_read_lock(mm);
6972f118c94SMatthew Brost 
6982f118c94SMatthew Brost retry:
6992f118c94SMatthew Brost 	bo = xe_bo_create_locked(tile_to_xe(tile), NULL, NULL,
7002f118c94SMatthew Brost 				 xe_svm_range_size(range),
7012f118c94SMatthew Brost 				 ttm_bo_type_device,
7023ca608dcSMatthew Brost 				 XE_BO_FLAG_VRAM_IF_DGFX(tile) |
7033ca608dcSMatthew Brost 				 XE_BO_FLAG_CPU_ADDR_MIRROR);
7042f118c94SMatthew Brost 	if (IS_ERR(bo)) {
7052f118c94SMatthew Brost 		err = PTR_ERR(bo);
7062f118c94SMatthew Brost 		if (xe_vm_validate_should_retry(NULL, err, &end))
7072f118c94SMatthew Brost 			goto retry;
7082f118c94SMatthew Brost 		goto unlock;
7092f118c94SMatthew Brost 	}
7102f118c94SMatthew Brost 
7112f118c94SMatthew Brost 	drm_gpusvm_devmem_init(&bo->devmem_allocation,
7122f118c94SMatthew Brost 			       vm->xe->drm.dev, mm,
7132f118c94SMatthew Brost 			       &gpusvm_devmem_ops,
7142f118c94SMatthew Brost 			       &tile->mem.vram.dpagemap,
7152f118c94SMatthew Brost 			       xe_svm_range_size(range));
7162f118c94SMatthew Brost 
7172f118c94SMatthew Brost 	blocks = &to_xe_ttm_vram_mgr_resource(bo->ttm.resource)->blocks;
7182f118c94SMatthew Brost 	list_for_each_entry(block, blocks, link)
7192f118c94SMatthew Brost 		block->private = vr;
7202f118c94SMatthew Brost 
7211d8c0557SThomas Hellström 	xe_bo_get(bo);
7222f118c94SMatthew Brost 	err = drm_gpusvm_migrate_to_devmem(&vm->svm.gpusvm, &range->base,
7232f118c94SMatthew Brost 					   &bo->devmem_allocation, ctx);
7242f118c94SMatthew Brost 	if (err)
7251d8c0557SThomas Hellström 		xe_svm_devmem_release(&bo->devmem_allocation);
7261d8c0557SThomas Hellström 
7271d8c0557SThomas Hellström 	xe_bo_unlock(bo);
7281d8c0557SThomas Hellström 	xe_bo_put(bo);
7292f118c94SMatthew Brost 
7302f118c94SMatthew Brost unlock:
7312f118c94SMatthew Brost 	mmap_read_unlock(mm);
7322f118c94SMatthew Brost 	mmput(mm);
7332f118c94SMatthew Brost 
7342f118c94SMatthew Brost 	return err;
7352f118c94SMatthew Brost }
7366c55404dSThomas Hellström #else
xe_svm_alloc_vram(struct xe_vm * vm,struct xe_tile * tile,struct xe_svm_range * range,const struct drm_gpusvm_ctx * ctx)7376c55404dSThomas Hellström static int xe_svm_alloc_vram(struct xe_vm *vm, struct xe_tile *tile,
7386c55404dSThomas Hellström 			     struct xe_svm_range *range,
7396c55404dSThomas Hellström 			     const struct drm_gpusvm_ctx *ctx)
7406c55404dSThomas Hellström {
7416c55404dSThomas Hellström 	return -EOPNOTSUPP;
7426c55404dSThomas Hellström }
7436c55404dSThomas Hellström #endif
7442f118c94SMatthew Brost 
supports_4K_migration(struct xe_device * xe)745794f5493SMatthew Brost static bool supports_4K_migration(struct xe_device *xe)
746794f5493SMatthew Brost {
747794f5493SMatthew Brost 	if (xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K)
748794f5493SMatthew Brost 		return false;
749794f5493SMatthew Brost 
750794f5493SMatthew Brost 	return true;
751794f5493SMatthew Brost }
752794f5493SMatthew Brost 
xe_svm_range_needs_migrate_to_vram(struct xe_svm_range * range,struct xe_vma * vma)753794f5493SMatthew Brost static bool xe_svm_range_needs_migrate_to_vram(struct xe_svm_range *range,
754794f5493SMatthew Brost 					       struct xe_vma *vma)
755794f5493SMatthew Brost {
756794f5493SMatthew Brost 	struct xe_vm *vm = range_to_vm(&range->base);
757794f5493SMatthew Brost 	u64 range_size = xe_svm_range_size(range);
758794f5493SMatthew Brost 
759794f5493SMatthew Brost 	if (!range->base.flags.migrate_devmem)
760794f5493SMatthew Brost 		return false;
761794f5493SMatthew Brost 
762794f5493SMatthew Brost 	if (xe_svm_range_in_vram(range)) {
763794f5493SMatthew Brost 		drm_dbg(&vm->xe->drm, "Range is already in VRAM\n");
764794f5493SMatthew Brost 		return false;
765794f5493SMatthew Brost 	}
766794f5493SMatthew Brost 
767*d6fb4f01SMaarten Lankhorst 	if (range_size < SZ_64K && !supports_4K_migration(vm->xe)) {
768794f5493SMatthew Brost 		drm_dbg(&vm->xe->drm, "Platform doesn't support SZ_4K range migration\n");
769794f5493SMatthew Brost 		return false;
770794f5493SMatthew Brost 	}
771794f5493SMatthew Brost 
772794f5493SMatthew Brost 	return true;
773794f5493SMatthew Brost }
774794f5493SMatthew Brost 
775ab498828SMatthew Brost /**
776ab498828SMatthew Brost  * xe_svm_handle_pagefault() - SVM handle page fault
777ab498828SMatthew Brost  * @vm: The VM.
778ab498828SMatthew Brost  * @vma: The CPU address mirror VMA.
77980bcbdfcSFrancois Dugast  * @gt: The gt upon the fault occurred.
780ab498828SMatthew Brost  * @fault_addr: The GPU fault address.
781ab498828SMatthew Brost  * @atomic: The fault atomic access bit.
782ab498828SMatthew Brost  *
7832f118c94SMatthew Brost  * Create GPU bindings for a SVM page fault. Optionally migrate to device
7842f118c94SMatthew Brost  * memory.
785ab498828SMatthew Brost  *
786ab498828SMatthew Brost  * Return: 0 on success, negative error code on error.
787ab498828SMatthew Brost  */
xe_svm_handle_pagefault(struct xe_vm * vm,struct xe_vma * vma,struct xe_gt * gt,u64 fault_addr,bool atomic)788ab498828SMatthew Brost int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
78980bcbdfcSFrancois Dugast 			    struct xe_gt *gt, u64 fault_addr,
790ab498828SMatthew Brost 			    bool atomic)
791ab498828SMatthew Brost {
7922f118c94SMatthew Brost 	struct drm_gpusvm_ctx ctx = {
7932f118c94SMatthew Brost 		.read_only = xe_vma_read_only(vma),
7942f118c94SMatthew Brost 		.devmem_possible = IS_DGFX(vm->xe) &&
7952f118c94SMatthew Brost 			IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR),
7962f118c94SMatthew Brost 		.check_pages_threshold = IS_DGFX(vm->xe) &&
7972f118c94SMatthew Brost 			IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR) ? SZ_64K : 0,
798794f5493SMatthew Brost 		.devmem_only = atomic && IS_DGFX(vm->xe) &&
799794f5493SMatthew Brost 			IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR),
8001b36ea2fSMatthew Brost 		.timeslice_ms = atomic && IS_DGFX(vm->xe) &&
8011b36ea2fSMatthew Brost 			IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR) ? 5 : 0,
8022f118c94SMatthew Brost 	};
8037d1d48fbSMatthew Brost 	struct xe_svm_range *range;
804ab498828SMatthew Brost 	struct drm_gpusvm_range *r;
8057d1d48fbSMatthew Brost 	struct drm_exec exec;
8067d1d48fbSMatthew Brost 	struct dma_fence *fence;
807794f5493SMatthew Brost 	int migrate_try_count = ctx.devmem_only ? 3 : 1;
80880bcbdfcSFrancois Dugast 	struct xe_tile *tile = gt_to_tile(gt);
8097d1d48fbSMatthew Brost 	ktime_t end = 0;
810ab498828SMatthew Brost 	int err;
811ab498828SMatthew Brost 
812ab498828SMatthew Brost 	lockdep_assert_held_write(&vm->lock);
813ab498828SMatthew Brost 	xe_assert(vm->xe, xe_vma_is_cpu_addr_mirror(vma));
814ab498828SMatthew Brost 
81580bcbdfcSFrancois Dugast 	xe_gt_stats_incr(gt, XE_GT_STATS_ID_SVM_PAGEFAULT_COUNT, 1);
81680bcbdfcSFrancois Dugast 
817ab498828SMatthew Brost retry:
81863f6e480SMatthew Brost 	/* Always process UNMAPs first so view SVM ranges is current */
81963f6e480SMatthew Brost 	err = xe_svm_garbage_collector(vm);
82063f6e480SMatthew Brost 	if (err)
82163f6e480SMatthew Brost 		return err;
822ab498828SMatthew Brost 
823ab498828SMatthew Brost 	r = drm_gpusvm_range_find_or_insert(&vm->svm.gpusvm, fault_addr,
824ab498828SMatthew Brost 					    xe_vma_start(vma), xe_vma_end(vma),
825ab498828SMatthew Brost 					    &ctx);
826ab498828SMatthew Brost 	if (IS_ERR(r))
827ab498828SMatthew Brost 		return PTR_ERR(r);
828ab498828SMatthew Brost 
829794f5493SMatthew Brost 	if (ctx.devmem_only && !r->flags.migrate_devmem)
830794f5493SMatthew Brost 		return -EACCES;
831794f5493SMatthew Brost 
8327d1d48fbSMatthew Brost 	range = to_xe_range(r);
833794f5493SMatthew Brost 	if (xe_svm_range_is_valid(range, tile, ctx.devmem_only))
8347d1d48fbSMatthew Brost 		return 0;
8357d1d48fbSMatthew Brost 
836d92eabb3SMatthew Brost 	range_debug(range, "PAGE FAULT");
837d92eabb3SMatthew Brost 
838794f5493SMatthew Brost 	if (--migrate_try_count >= 0 &&
839794f5493SMatthew Brost 	    xe_svm_range_needs_migrate_to_vram(range, vma)) {
8402f118c94SMatthew Brost 		err = xe_svm_alloc_vram(vm, tile, range, &ctx);
8411b36ea2fSMatthew Brost 		ctx.timeslice_ms <<= 1;	/* Double timeslice if we have to retry */
8422f118c94SMatthew Brost 		if (err) {
843794f5493SMatthew Brost 			if (migrate_try_count || !ctx.devmem_only) {
8442f118c94SMatthew Brost 				drm_dbg(&vm->xe->drm,
845794f5493SMatthew Brost 					"VRAM allocation failed, falling back to retrying fault, asid=%u, errno=%pe\n",
8462f118c94SMatthew Brost 					vm->usm.asid, ERR_PTR(err));
847ab498828SMatthew Brost 				goto retry;
848794f5493SMatthew Brost 			} else {
849794f5493SMatthew Brost 				drm_err(&vm->xe->drm,
850794f5493SMatthew Brost 					"VRAM allocation failed, retry count exceeded, asid=%u, errno=%pe\n",
851794f5493SMatthew Brost 					vm->usm.asid, ERR_PTR(err));
852794f5493SMatthew Brost 				return err;
853794f5493SMatthew Brost 			}
8542f118c94SMatthew Brost 		}
8552f118c94SMatthew Brost 	}
8562f118c94SMatthew Brost 
857d92eabb3SMatthew Brost 	range_debug(range, "GET PAGES");
8582f118c94SMatthew Brost 	err = drm_gpusvm_range_get_pages(&vm->svm.gpusvm, r, &ctx);
8592f118c94SMatthew Brost 	/* Corner where CPU mappings have changed */
8602f118c94SMatthew Brost 	if (err == -EOPNOTSUPP || err == -EFAULT || err == -EPERM) {
8611b36ea2fSMatthew Brost 		ctx.timeslice_ms <<= 1;	/* Double timeslice if we have to retry */
862794f5493SMatthew Brost 		if (migrate_try_count > 0 || !ctx.devmem_only) {
863d92eabb3SMatthew Brost 			if (err == -EOPNOTSUPP) {
864d92eabb3SMatthew Brost 				range_debug(range, "PAGE FAULT - EVICT PAGES");
865794f5493SMatthew Brost 				drm_gpusvm_range_evict(&vm->svm.gpusvm,
866794f5493SMatthew Brost 						       &range->base);
867d92eabb3SMatthew Brost 			}
8682f118c94SMatthew Brost 			drm_dbg(&vm->xe->drm,
8692f118c94SMatthew Brost 				"Get pages failed, falling back to retrying, asid=%u, gpusvm=%p, errno=%pe\n",
8702f118c94SMatthew Brost 				vm->usm.asid, &vm->svm.gpusvm, ERR_PTR(err));
871d92eabb3SMatthew Brost 			range_debug(range, "PAGE FAULT - RETRY PAGES");
8722f118c94SMatthew Brost 			goto retry;
873794f5493SMatthew Brost 		} else {
874794f5493SMatthew Brost 			drm_err(&vm->xe->drm,
875794f5493SMatthew Brost 				"Get pages failed, retry count exceeded, asid=%u, gpusvm=%p, errno=%pe\n",
876794f5493SMatthew Brost 				vm->usm.asid, &vm->svm.gpusvm, ERR_PTR(err));
877794f5493SMatthew Brost 		}
8782f118c94SMatthew Brost 	}
879d92eabb3SMatthew Brost 	if (err) {
880d92eabb3SMatthew Brost 		range_debug(range, "PAGE FAULT - FAIL PAGE COLLECT");
8817d1d48fbSMatthew Brost 		goto err_out;
882d92eabb3SMatthew Brost 	}
883d92eabb3SMatthew Brost 
884d92eabb3SMatthew Brost 	range_debug(range, "PAGE FAULT - BIND");
885ab498828SMatthew Brost 
8867d1d48fbSMatthew Brost retry_bind:
8877d1d48fbSMatthew Brost 	drm_exec_init(&exec, 0, 0);
8887d1d48fbSMatthew Brost 	drm_exec_until_all_locked(&exec) {
8897d1d48fbSMatthew Brost 		err = drm_exec_lock_obj(&exec, vm->gpuvm.r_obj);
8907d1d48fbSMatthew Brost 		drm_exec_retry_on_contention(&exec);
8917d1d48fbSMatthew Brost 		if (err) {
8927d1d48fbSMatthew Brost 			drm_exec_fini(&exec);
8937d1d48fbSMatthew Brost 			goto err_out;
8947d1d48fbSMatthew Brost 		}
8957d1d48fbSMatthew Brost 
8967d1d48fbSMatthew Brost 		fence = xe_vm_range_rebind(vm, vma, range, BIT(tile->id));
8977d1d48fbSMatthew Brost 		if (IS_ERR(fence)) {
8987d1d48fbSMatthew Brost 			drm_exec_fini(&exec);
8997d1d48fbSMatthew Brost 			err = PTR_ERR(fence);
900d92eabb3SMatthew Brost 			if (err == -EAGAIN) {
9011b36ea2fSMatthew Brost 				ctx.timeslice_ms <<= 1;	/* Double timeslice if we have to retry */
902d92eabb3SMatthew Brost 				range_debug(range, "PAGE FAULT - RETRY BIND");
9037d1d48fbSMatthew Brost 				goto retry;
904d92eabb3SMatthew Brost 			}
9057d1d48fbSMatthew Brost 			if (xe_vm_validate_should_retry(&exec, err, &end))
9067d1d48fbSMatthew Brost 				goto retry_bind;
9077d1d48fbSMatthew Brost 			goto err_out;
9087d1d48fbSMatthew Brost 		}
9097d1d48fbSMatthew Brost 	}
9107d1d48fbSMatthew Brost 	drm_exec_fini(&exec);
9117d1d48fbSMatthew Brost 
9127d1d48fbSMatthew Brost 	dma_fence_wait(fence, false);
9137d1d48fbSMatthew Brost 	dma_fence_put(fence);
9147d1d48fbSMatthew Brost 
9157d1d48fbSMatthew Brost err_out:
916ab498828SMatthew Brost 
917ab498828SMatthew Brost 	return err;
918ab498828SMatthew Brost }
919f0e4238fSMatthew Brost 
920f0e4238fSMatthew Brost /**
921f0e4238fSMatthew Brost  * xe_svm_has_mapping() - SVM has mappings
922f0e4238fSMatthew Brost  * @vm: The VM.
923f0e4238fSMatthew Brost  * @start: Start address.
924f0e4238fSMatthew Brost  * @end: End address.
925f0e4238fSMatthew Brost  *
926f0e4238fSMatthew Brost  * Check if an address range has SVM mappings.
927f0e4238fSMatthew Brost  *
928f0e4238fSMatthew Brost  * Return: True if address range has a SVM mapping, False otherwise
929f0e4238fSMatthew Brost  */
xe_svm_has_mapping(struct xe_vm * vm,u64 start,u64 end)930f0e4238fSMatthew Brost bool xe_svm_has_mapping(struct xe_vm *vm, u64 start, u64 end)
931f0e4238fSMatthew Brost {
932f0e4238fSMatthew Brost 	return drm_gpusvm_has_mapping(&vm->svm.gpusvm, start, end);
933f0e4238fSMatthew Brost }
9340c30c654SMatthew Brost 
9353ca608dcSMatthew Brost /**
9363ca608dcSMatthew Brost  * xe_svm_bo_evict() - SVM evict BO to system memory
9373ca608dcSMatthew Brost  * @bo: BO to evict
9383ca608dcSMatthew Brost  *
9393ca608dcSMatthew Brost  * SVM evict BO to system memory. GPU SVM layer ensures all device pages
9403ca608dcSMatthew Brost  * are evicted before returning.
9413ca608dcSMatthew Brost  *
9423ca608dcSMatthew Brost  * Return: 0 on success standard error code otherwise
9433ca608dcSMatthew Brost  */
xe_svm_bo_evict(struct xe_bo * bo)9443ca608dcSMatthew Brost int xe_svm_bo_evict(struct xe_bo *bo)
9453ca608dcSMatthew Brost {
9463ca608dcSMatthew Brost 	return drm_gpusvm_evict_to_ram(&bo->devmem_allocation);
9473ca608dcSMatthew Brost }
9483ca608dcSMatthew Brost 
9490c30c654SMatthew Brost #if IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR)
9506c55404dSThomas Hellström 
95111bbe0d9SThomas Hellström static struct drm_pagemap_device_addr
xe_drm_pagemap_device_map(struct drm_pagemap * dpagemap,struct device * dev,struct page * page,unsigned int order,enum dma_data_direction dir)95211bbe0d9SThomas Hellström xe_drm_pagemap_device_map(struct drm_pagemap *dpagemap,
95311bbe0d9SThomas Hellström 			  struct device *dev,
95411bbe0d9SThomas Hellström 			  struct page *page,
95511bbe0d9SThomas Hellström 			  unsigned int order,
95611bbe0d9SThomas Hellström 			  enum dma_data_direction dir)
95711bbe0d9SThomas Hellström {
95811bbe0d9SThomas Hellström 	struct device *pgmap_dev = dpagemap->dev;
95911bbe0d9SThomas Hellström 	enum drm_interconnect_protocol prot;
96011bbe0d9SThomas Hellström 	dma_addr_t addr;
96111bbe0d9SThomas Hellström 
96211bbe0d9SThomas Hellström 	if (pgmap_dev == dev) {
96311bbe0d9SThomas Hellström 		addr = xe_vram_region_page_to_dpa(page_to_vr(page), page);
96411bbe0d9SThomas Hellström 		prot = XE_INTERCONNECT_VRAM;
96511bbe0d9SThomas Hellström 	} else {
96611bbe0d9SThomas Hellström 		addr = DMA_MAPPING_ERROR;
96711bbe0d9SThomas Hellström 		prot = 0;
96811bbe0d9SThomas Hellström 	}
96911bbe0d9SThomas Hellström 
97011bbe0d9SThomas Hellström 	return drm_pagemap_device_addr_encode(addr, prot, order, dir);
97111bbe0d9SThomas Hellström }
97211bbe0d9SThomas Hellström 
97311bbe0d9SThomas Hellström static const struct drm_pagemap_ops xe_drm_pagemap_ops = {
97411bbe0d9SThomas Hellström 	.device_map = xe_drm_pagemap_device_map,
97511bbe0d9SThomas Hellström };
97611bbe0d9SThomas Hellström 
9770c30c654SMatthew Brost /**
9780c30c654SMatthew Brost  * xe_devm_add: Remap and provide memmap backing for device memory
9790c30c654SMatthew Brost  * @tile: tile that the memory region belongs to
9800c30c654SMatthew Brost  * @vr: vram memory region to remap
9810c30c654SMatthew Brost  *
9820c30c654SMatthew Brost  * This remap device memory to host physical address space and create
9830c30c654SMatthew Brost  * struct page to back device memory
9840c30c654SMatthew Brost  *
9850c30c654SMatthew Brost  * Return: 0 on success standard error code otherwise
9860c30c654SMatthew Brost  */
xe_devm_add(struct xe_tile * tile,struct xe_vram_region * vr)9870c30c654SMatthew Brost int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr)
9880c30c654SMatthew Brost {
9890c30c654SMatthew Brost 	struct xe_device *xe = tile_to_xe(tile);
9900c30c654SMatthew Brost 	struct device *dev = &to_pci_dev(xe->drm.dev)->dev;
9910c30c654SMatthew Brost 	struct resource *res;
9920c30c654SMatthew Brost 	void *addr;
9930c30c654SMatthew Brost 	int ret;
9940c30c654SMatthew Brost 
9950c30c654SMatthew Brost 	res = devm_request_free_mem_region(dev, &iomem_resource,
9960c30c654SMatthew Brost 					   vr->usable_size);
9970c30c654SMatthew Brost 	if (IS_ERR(res)) {
9980c30c654SMatthew Brost 		ret = PTR_ERR(res);
9990c30c654SMatthew Brost 		return ret;
10000c30c654SMatthew Brost 	}
10010c30c654SMatthew Brost 
10020c30c654SMatthew Brost 	vr->pagemap.type = MEMORY_DEVICE_PRIVATE;
10030c30c654SMatthew Brost 	vr->pagemap.range.start = res->start;
10040c30c654SMatthew Brost 	vr->pagemap.range.end = res->end;
10050c30c654SMatthew Brost 	vr->pagemap.nr_range = 1;
10060c30c654SMatthew Brost 	vr->pagemap.ops = drm_gpusvm_pagemap_ops_get();
10070c30c654SMatthew Brost 	vr->pagemap.owner = xe_svm_devm_owner(xe);
10080c30c654SMatthew Brost 	addr = devm_memremap_pages(dev, &vr->pagemap);
100911bbe0d9SThomas Hellström 
101011bbe0d9SThomas Hellström 	vr->dpagemap.dev = dev;
101111bbe0d9SThomas Hellström 	vr->dpagemap.ops = &xe_drm_pagemap_ops;
101211bbe0d9SThomas Hellström 
10130c30c654SMatthew Brost 	if (IS_ERR(addr)) {
10140c30c654SMatthew Brost 		devm_release_mem_region(dev, res->start, resource_size(res));
10150c30c654SMatthew Brost 		ret = PTR_ERR(addr);
10160c30c654SMatthew Brost 		drm_err(&xe->drm, "Failed to remap tile %d memory, errno %pe\n",
10170c30c654SMatthew Brost 			tile->id, ERR_PTR(ret));
10180c30c654SMatthew Brost 		return ret;
10190c30c654SMatthew Brost 	}
10200c30c654SMatthew Brost 	vr->hpa_base = res->start;
10210c30c654SMatthew Brost 
10220c30c654SMatthew Brost 	drm_dbg(&xe->drm, "Added tile %d memory [%llx-%llx] to devm, remapped to %pr\n",
10230c30c654SMatthew Brost 		tile->id, vr->io_start, vr->io_start + vr->usable_size, res);
10240c30c654SMatthew Brost 	return 0;
10250c30c654SMatthew Brost }
10260c30c654SMatthew Brost #else
xe_devm_add(struct xe_tile * tile,struct xe_vram_region * vr)10270c30c654SMatthew Brost int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr)
10280c30c654SMatthew Brost {
10290c30c654SMatthew Brost 	return 0;
10300c30c654SMatthew Brost }
10310c30c654SMatthew Brost #endif
1032564467e9SShuicheng Lin 
1033564467e9SShuicheng Lin /**
1034564467e9SShuicheng Lin  * xe_svm_flush() - SVM flush
1035564467e9SShuicheng Lin  * @vm: The VM.
1036564467e9SShuicheng Lin  *
1037564467e9SShuicheng Lin  * Flush all SVM actions.
1038564467e9SShuicheng Lin  */
xe_svm_flush(struct xe_vm * vm)1039564467e9SShuicheng Lin void xe_svm_flush(struct xe_vm *vm)
1040564467e9SShuicheng Lin {
1041564467e9SShuicheng Lin 	if (xe_vm_in_fault_mode(vm))
1042564467e9SShuicheng Lin 		flush_work(&vm->svm.garbage_collector.work);
1043564467e9SShuicheng Lin }
1044