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