Skip to content

Commit ab49882

Browse files
committed
drm/xe: Add SVM range invalidation and page fault
Add SVM range invalidation vfunc which invalidates PTEs. A new PT layer function which accepts a SVM range is added to support this. In addition, add the basic page fault handler which allocates a SVM range which is used by SVM range invalidation vfunc. v2: - Don't run invalidation if VM is closed - Cycle notifier lock in xe_svm_close - Drop xe_gt_tlb_invalidation_fence_fini v3: - Better commit message (Thomas) - Add lockdep asserts (Thomas) - Add kernel doc (Thomas) - s/change/changed (Thomas) - Use new GPU SVM range / notifier structures - Ensure PTEs are zapped / dma mappings are unmapped on VM close (Thomas) v4: - Fix macro (Checkpatch) v5: - Use range start/end helpers (Thomas) - Use notifier start/end helpers (Thomas) v6: - Use min/max helpers (Himal) - Only compile if CONFIG_DRM_GPUSVM selected (CI, Lucas) Signed-off-by: Matthew Brost <[email protected]> Reviewed-by: Thomas Hellström <[email protected]> Reviewed-by: Himal Prasad Ghimiray <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 074e40d commit ab49882

File tree

6 files changed

+328
-6
lines changed

6 files changed

+328
-6
lines changed

drivers/gpu/drm/xe/xe_gt_pagefault.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "xe_guc.h"
2020
#include "xe_guc_ct.h"
2121
#include "xe_migrate.h"
22+
#include "xe_svm.h"
2223
#include "xe_trace_bo.h"
2324
#include "xe_vm.h"
2425

@@ -125,22 +126,22 @@ static int xe_pf_begin(struct drm_exec *exec, struct xe_vma *vma,
125126
return 0;
126127
}
127128

128-
static int handle_vma_pagefault(struct xe_gt *gt, struct pagefault *pf,
129-
struct xe_vma *vma)
129+
static int handle_vma_pagefault(struct xe_gt *gt, struct xe_vma *vma,
130+
bool atomic)
130131
{
131132
struct xe_vm *vm = xe_vma_vm(vma);
132133
struct xe_tile *tile = gt_to_tile(gt);
133134
struct drm_exec exec;
134135
struct dma_fence *fence;
135136
ktime_t end = 0;
136137
int err;
137-
bool atomic;
138+
139+
lockdep_assert_held_write(&vm->lock);
138140

139141
xe_gt_stats_incr(gt, XE_GT_STATS_ID_VMA_PAGEFAULT_COUNT, 1);
140142
xe_gt_stats_incr(gt, XE_GT_STATS_ID_VMA_PAGEFAULT_KB, xe_vma_size(vma) / 1024);
141143

142144
trace_xe_vma_pagefault(vma);
143-
atomic = access_is_atomic(pf->access_type);
144145

145146
/* Check if VMA is valid */
146147
if (vma_is_valid(tile, vma) && !atomic)
@@ -210,6 +211,7 @@ static int handle_pagefault(struct xe_gt *gt, struct pagefault *pf)
210211
struct xe_vm *vm;
211212
struct xe_vma *vma = NULL;
212213
int err;
214+
bool atomic;
213215

214216
/* SW isn't expected to handle TRTT faults */
215217
if (pf->trva_fault)
@@ -235,7 +237,13 @@ static int handle_pagefault(struct xe_gt *gt, struct pagefault *pf)
235237
goto unlock_vm;
236238
}
237239

238-
err = handle_vma_pagefault(gt, pf, vma);
240+
atomic = access_is_atomic(pf->access_type);
241+
242+
if (xe_vma_is_cpu_addr_mirror(vma))
243+
err = xe_svm_handle_pagefault(vm, vma, gt_to_tile(gt),
244+
pf->page_addr, atomic);
245+
else
246+
err = handle_vma_pagefault(gt, vma, atomic);
239247

240248
unlock_vm:
241249
if (!err)

drivers/gpu/drm/xe/xe_pt.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "xe_res_cursor.h"
2121
#include "xe_sched_job.h"
2222
#include "xe_sync.h"
23+
#include "xe_svm.h"
2324
#include "xe_trace.h"
2425
#include "xe_ttm_stolen_mgr.h"
2526
#include "xe_vm.h"
@@ -851,6 +852,46 @@ bool xe_pt_zap_ptes(struct xe_tile *tile, struct xe_vma *vma)
851852
return xe_walk.needs_invalidate;
852853
}
853854

855+
/**
856+
* xe_pt_zap_ptes_range() - Zap (zero) gpu ptes of a SVM range
857+
* @tile: The tile we're zapping for.
858+
* @vm: The VM we're zapping for.
859+
* @range: The SVM range we're zapping for.
860+
*
861+
* SVM invalidation needs to be able to zap the gpu ptes of a given address
862+
* range. In order to be able to do that, that function needs access to the
863+
* shared page-table entries so it can either clear the leaf PTEs or
864+
* clear the pointers to lower-level page-tables. The caller is required
865+
* to hold the SVM notifier lock.
866+
*
867+
* Return: Whether ptes were actually updated and a TLB invalidation is
868+
* required.
869+
*/
870+
bool xe_pt_zap_ptes_range(struct xe_tile *tile, struct xe_vm *vm,
871+
struct xe_svm_range *range)
872+
{
873+
struct xe_pt_zap_ptes_walk xe_walk = {
874+
.base = {
875+
.ops = &xe_pt_zap_ptes_ops,
876+
.shifts = xe_normal_pt_shifts,
877+
.max_level = XE_PT_HIGHEST_LEVEL,
878+
},
879+
.tile = tile,
880+
};
881+
struct xe_pt *pt = vm->pt_root[tile->id];
882+
u8 pt_mask = (range->tile_present & ~range->tile_invalidated);
883+
884+
xe_svm_assert_in_notifier(vm);
885+
886+
if (!(pt_mask & BIT(tile->id)))
887+
return false;
888+
889+
(void)xe_pt_walk_shared(&pt->base, pt->level, range->base.itree.start,
890+
range->base.itree.last + 1, &xe_walk.base);
891+
892+
return xe_walk.needs_invalidate;
893+
}
894+
854895
static void
855896
xe_vm_populate_pgtable(struct xe_migrate_pt_update *pt_update, struct xe_tile *tile,
856897
struct iosys_map *map, void *data,

drivers/gpu/drm/xe/xe_pt.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,7 @@ void xe_pt_update_ops_fini(struct xe_tile *tile, struct xe_vma_ops *vops);
4545
void xe_pt_update_ops_abort(struct xe_tile *tile, struct xe_vma_ops *vops);
4646

4747
bool xe_pt_zap_ptes(struct xe_tile *tile, struct xe_vma *vma);
48+
bool xe_pt_zap_ptes_range(struct xe_tile *tile, struct xe_vm *vm,
49+
struct xe_svm_range *range);
4850

4951
#endif

drivers/gpu/drm/xe/xe_svm.c

Lines changed: 228 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,204 @@
33
* Copyright © 2024 Intel Corporation
44
*/
55

6+
#include "xe_gt_tlb_invalidation.h"
7+
#include "xe_pt.h"
68
#include "xe_svm.h"
79
#include "xe_vm.h"
810
#include "xe_vm_types.h"
911

12+
static struct xe_vm *gpusvm_to_vm(struct drm_gpusvm *gpusvm)
13+
{
14+
return container_of(gpusvm, struct xe_vm, svm.gpusvm);
15+
}
16+
17+
static struct xe_vm *range_to_vm(struct drm_gpusvm_range *r)
18+
{
19+
return gpusvm_to_vm(r->gpusvm);
20+
}
21+
22+
static unsigned long xe_svm_range_start(struct xe_svm_range *range)
23+
{
24+
return drm_gpusvm_range_start(&range->base);
25+
}
26+
27+
static unsigned long xe_svm_range_end(struct xe_svm_range *range)
28+
{
29+
return drm_gpusvm_range_end(&range->base);
30+
}
31+
32+
static struct drm_gpusvm_range *
33+
xe_svm_range_alloc(struct drm_gpusvm *gpusvm)
34+
{
35+
struct xe_svm_range *range;
36+
37+
range = kzalloc(sizeof(*range), GFP_KERNEL);
38+
if (!range)
39+
return ERR_PTR(-ENOMEM);
40+
41+
xe_vm_get(gpusvm_to_vm(gpusvm));
42+
43+
return &range->base;
44+
}
45+
46+
static void xe_svm_range_free(struct drm_gpusvm_range *range)
47+
{
48+
xe_vm_put(range_to_vm(range));
49+
kfree(range);
50+
}
51+
52+
static struct xe_svm_range *to_xe_range(struct drm_gpusvm_range *r)
53+
{
54+
return container_of(r, struct xe_svm_range, base);
55+
}
56+
57+
static u8
58+
xe_svm_range_notifier_event_begin(struct xe_vm *vm, struct drm_gpusvm_range *r,
59+
const struct mmu_notifier_range *mmu_range,
60+
u64 *adj_start, u64 *adj_end)
61+
{
62+
struct xe_svm_range *range = to_xe_range(r);
63+
struct xe_device *xe = vm->xe;
64+
struct xe_tile *tile;
65+
u8 tile_mask = 0;
66+
u8 id;
67+
68+
xe_svm_assert_in_notifier(vm);
69+
70+
/* Skip if already unmapped or if no binding exist */
71+
if (range->base.flags.unmapped || !range->tile_present)
72+
return 0;
73+
74+
/* Adjust invalidation to range boundaries */
75+
*adj_start = min(xe_svm_range_start(range), mmu_range->start);
76+
*adj_end = max(xe_svm_range_end(range), mmu_range->end);
77+
78+
/*
79+
* XXX: Ideally would zap PTEs in one shot in xe_svm_invalidate but the
80+
* invalidation code can't correctly cope with sparse ranges or
81+
* invalidations spanning multiple ranges.
82+
*/
83+
for_each_tile(tile, xe, id)
84+
if (xe_pt_zap_ptes_range(tile, vm, range)) {
85+
tile_mask |= BIT(id);
86+
range->tile_invalidated |= BIT(id);
87+
}
88+
89+
return tile_mask;
90+
}
91+
92+
static void
93+
xe_svm_range_notifier_event_end(struct xe_vm *vm, struct drm_gpusvm_range *r,
94+
const struct mmu_notifier_range *mmu_range)
95+
{
96+
struct drm_gpusvm_ctx ctx = { .in_notifier = true, };
97+
98+
xe_svm_assert_in_notifier(vm);
99+
100+
drm_gpusvm_range_unmap_pages(&vm->svm.gpusvm, r, &ctx);
101+
/* TODO: Add range to garbage collector if VM is not closed */
102+
}
103+
10104
static void xe_svm_invalidate(struct drm_gpusvm *gpusvm,
11105
struct drm_gpusvm_notifier *notifier,
12106
const struct mmu_notifier_range *mmu_range)
13107
{
14-
/* TODO: Implement */
108+
struct xe_vm *vm = gpusvm_to_vm(gpusvm);
109+
struct xe_device *xe = vm->xe;
110+
struct xe_tile *tile;
111+
struct drm_gpusvm_range *r, *first;
112+
struct xe_gt_tlb_invalidation_fence
113+
fence[XE_MAX_TILES_PER_DEVICE * XE_MAX_GT_PER_TILE];
114+
u64 adj_start = mmu_range->start, adj_end = mmu_range->end;
115+
u8 tile_mask = 0;
116+
u8 id;
117+
u32 fence_id = 0;
118+
long err;
119+
120+
xe_svm_assert_in_notifier(vm);
121+
122+
/* Adjust invalidation to notifier boundaries */
123+
adj_start = max(drm_gpusvm_notifier_start(notifier), adj_start);
124+
adj_end = min(drm_gpusvm_notifier_end(notifier), adj_end);
125+
126+
first = drm_gpusvm_range_find(notifier, adj_start, adj_end);
127+
if (!first)
128+
return;
129+
130+
/*
131+
* PTs may be getting destroyed so not safe to touch these but PT should
132+
* be invalidated at this point in time. Regardless we still need to
133+
* ensure any dma mappings are unmapped in the here.
134+
*/
135+
if (xe_vm_is_closed(vm))
136+
goto range_notifier_event_end;
137+
138+
/*
139+
* XXX: Less than ideal to always wait on VM's resv slots if an
140+
* invalidation is not required. Could walk range list twice to figure
141+
* out if an invalidations is need, but also not ideal.
142+
*/
143+
err = dma_resv_wait_timeout(xe_vm_resv(vm),
144+
DMA_RESV_USAGE_BOOKKEEP,
145+
false, MAX_SCHEDULE_TIMEOUT);
146+
XE_WARN_ON(err <= 0);
147+
148+
r = first;
149+
drm_gpusvm_for_each_range(r, notifier, adj_start, adj_end)
150+
tile_mask |= xe_svm_range_notifier_event_begin(vm, r, mmu_range,
151+
&adj_start,
152+
&adj_end);
153+
if (!tile_mask)
154+
goto range_notifier_event_end;
155+
156+
xe_device_wmb(xe);
157+
158+
for_each_tile(tile, xe, id) {
159+
if (tile_mask & BIT(id)) {
160+
int err;
161+
162+
xe_gt_tlb_invalidation_fence_init(tile->primary_gt,
163+
&fence[fence_id], true);
164+
165+
err = xe_gt_tlb_invalidation_range(tile->primary_gt,
166+
&fence[fence_id],
167+
adj_start,
168+
adj_end,
169+
vm->usm.asid);
170+
if (WARN_ON_ONCE(err < 0))
171+
goto wait;
172+
++fence_id;
173+
174+
if (!tile->media_gt)
175+
continue;
176+
177+
xe_gt_tlb_invalidation_fence_init(tile->media_gt,
178+
&fence[fence_id], true);
179+
180+
err = xe_gt_tlb_invalidation_range(tile->media_gt,
181+
&fence[fence_id],
182+
adj_start,
183+
adj_end,
184+
vm->usm.asid);
185+
if (WARN_ON_ONCE(err < 0))
186+
goto wait;
187+
++fence_id;
188+
}
189+
}
190+
191+
wait:
192+
for (id = 0; id < fence_id; ++id)
193+
xe_gt_tlb_invalidation_fence_wait(&fence[id]);
194+
195+
range_notifier_event_end:
196+
r = first;
197+
drm_gpusvm_for_each_range(r, notifier, adj_start, adj_end)
198+
xe_svm_range_notifier_event_end(vm, r, mmu_range);
15199
}
16200

17201
static const struct drm_gpusvm_ops gpusvm_ops = {
202+
.range_alloc = xe_svm_range_alloc,
203+
.range_free = xe_svm_range_free,
18204
.invalidate = xe_svm_invalidate,
19205
};
20206

@@ -71,3 +257,44 @@ void xe_svm_fini(struct xe_vm *vm)
71257

72258
drm_gpusvm_fini(&vm->svm.gpusvm);
73259
}
260+
261+
/**
262+
* xe_svm_handle_pagefault() - SVM handle page fault
263+
* @vm: The VM.
264+
* @vma: The CPU address mirror VMA.
265+
* @tile: The tile upon the fault occurred.
266+
* @fault_addr: The GPU fault address.
267+
* @atomic: The fault atomic access bit.
268+
*
269+
* Create GPU bindings for a SVM page fault.
270+
*
271+
* Return: 0 on success, negative error code on error.
272+
*/
273+
int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
274+
struct xe_tile *tile, u64 fault_addr,
275+
bool atomic)
276+
{
277+
struct drm_gpusvm_ctx ctx = { .read_only = xe_vma_read_only(vma), };
278+
struct drm_gpusvm_range *r;
279+
int err;
280+
281+
lockdep_assert_held_write(&vm->lock);
282+
xe_assert(vm->xe, xe_vma_is_cpu_addr_mirror(vma));
283+
284+
retry:
285+
/* TODO: Run garbage collector */
286+
287+
r = drm_gpusvm_range_find_or_insert(&vm->svm.gpusvm, fault_addr,
288+
xe_vma_start(vma), xe_vma_end(vma),
289+
&ctx);
290+
if (IS_ERR(r))
291+
return PTR_ERR(r);
292+
293+
err = drm_gpusvm_range_get_pages(&vm->svm.gpusvm, r, &ctx);
294+
if (err == -EFAULT || err == -EPERM) /* Corner where CPU mappings have changed */
295+
goto retry;
296+
297+
/* TODO: Issue bind */
298+
299+
return err;
300+
}

0 commit comments

Comments
 (0)