Skip to content

Commit ae482ec

Browse files
mbrost05rodrigovivi
authored andcommitted
drm/xe: Add staging tree for VM binds
Concurrent VM bind staging and zapping of PTEs from a userptr notifier do not work because the view of PTEs is not stable. VM binds cannot acquire the notifier lock during staging, as memory allocations are required. To resolve this race condition, use a staging tree for VM binds that is committed only under the userptr notifier lock during the final step of the bind. This ensures a consistent view of the PTEs in the userptr notifier. A follow up may only use staging for VM in fault mode as this is the only mode in which the above race exists. v3: - Drop zap PTE change (Thomas) - s/xe_pt_entry/xe_pt_entry_staging (Thomas) Suggested-by: Thomas Hellström <thomas.hellstrom@linux.intel.com> Cc: <stable@vger.kernel.org> Fixes: e8babb2 ("drm/xe: Convert multiple bind ops into single job") Fixes: a708f65 ("drm/xe: Update PT layer with better error handling") Signed-off-by: Matthew Brost <matthew.brost@intel.com> Reviewed-by: Thomas Hellström <thomas.hellstrom@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20250228073058.59510-5-thomas.hellstrom@linux.intel.com Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com> (cherry picked from commit 6f39b0c) Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
1 parent 84211b1 commit ae482ec

File tree

3 files changed

+46
-19
lines changed

3 files changed

+46
-19
lines changed

drivers/gpu/drm/xe/xe_pt.c

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ struct xe_pt_dir {
2828
struct xe_pt pt;
2929
/** @children: Array of page-table child nodes */
3030
struct xe_ptw *children[XE_PDES];
31+
/** @staging: Array of page-table staging nodes */
32+
struct xe_ptw *staging[XE_PDES];
3133
};
3234

3335
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM)
@@ -48,9 +50,10 @@ static struct xe_pt_dir *as_xe_pt_dir(struct xe_pt *pt)
4850
return container_of(pt, struct xe_pt_dir, pt);
4951
}
5052

51-
static struct xe_pt *xe_pt_entry(struct xe_pt_dir *pt_dir, unsigned int index)
53+
static struct xe_pt *
54+
xe_pt_entry_staging(struct xe_pt_dir *pt_dir, unsigned int index)
5255
{
53-
return container_of(pt_dir->children[index], struct xe_pt, base);
56+
return container_of(pt_dir->staging[index], struct xe_pt, base);
5457
}
5558

5659
static u64 __xe_pt_empty_pte(struct xe_tile *tile, struct xe_vm *vm,
@@ -125,6 +128,7 @@ struct xe_pt *xe_pt_create(struct xe_vm *vm, struct xe_tile *tile,
125128
}
126129
pt->bo = bo;
127130
pt->base.children = level ? as_xe_pt_dir(pt)->children : NULL;
131+
pt->base.staging = level ? as_xe_pt_dir(pt)->staging : NULL;
128132

129133
if (vm->xef)
130134
xe_drm_client_add_bo(vm->xef->client, pt->bo);
@@ -206,8 +210,8 @@ void xe_pt_destroy(struct xe_pt *pt, u32 flags, struct llist_head *deferred)
206210
struct xe_pt_dir *pt_dir = as_xe_pt_dir(pt);
207211

208212
for (i = 0; i < XE_PDES; i++) {
209-
if (xe_pt_entry(pt_dir, i))
210-
xe_pt_destroy(xe_pt_entry(pt_dir, i), flags,
213+
if (xe_pt_entry_staging(pt_dir, i))
214+
xe_pt_destroy(xe_pt_entry_staging(pt_dir, i), flags,
211215
deferred);
212216
}
213217
}
@@ -376,8 +380,10 @@ xe_pt_insert_entry(struct xe_pt_stage_bind_walk *xe_walk, struct xe_pt *parent,
376380
/* Continue building a non-connected subtree. */
377381
struct iosys_map *map = &parent->bo->vmap;
378382

379-
if (unlikely(xe_child))
383+
if (unlikely(xe_child)) {
380384
parent->base.children[offset] = &xe_child->base;
385+
parent->base.staging[offset] = &xe_child->base;
386+
}
381387

382388
xe_pt_write(xe_walk->vm->xe, map, offset, pte);
383389
parent->num_live++;
@@ -614,6 +620,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
614620
.ops = &xe_pt_stage_bind_ops,
615621
.shifts = xe_normal_pt_shifts,
616622
.max_level = XE_PT_HIGHEST_LEVEL,
623+
.staging = true,
617624
},
618625
.vm = xe_vma_vm(vma),
619626
.tile = tile,
@@ -873,7 +880,7 @@ static void xe_pt_cancel_bind(struct xe_vma *vma,
873880
}
874881
}
875882

876-
static void xe_pt_commit_locks_assert(struct xe_vma *vma)
883+
static void xe_pt_commit_prepare_locks_assert(struct xe_vma *vma)
877884
{
878885
struct xe_vm *vm = xe_vma_vm(vma);
879886

@@ -885,6 +892,16 @@ static void xe_pt_commit_locks_assert(struct xe_vma *vma)
885892
xe_vm_assert_held(vm);
886893
}
887894

895+
static void xe_pt_commit_locks_assert(struct xe_vma *vma)
896+
{
897+
struct xe_vm *vm = xe_vma_vm(vma);
898+
899+
xe_pt_commit_prepare_locks_assert(vma);
900+
901+
if (xe_vma_is_userptr(vma))
902+
lockdep_assert_held_read(&vm->userptr.notifier_lock);
903+
}
904+
888905
static void xe_pt_commit(struct xe_vma *vma,
889906
struct xe_vm_pgtable_update *entries,
890907
u32 num_entries, struct llist_head *deferred)
@@ -895,13 +912,17 @@ static void xe_pt_commit(struct xe_vma *vma,
895912

896913
for (i = 0; i < num_entries; i++) {
897914
struct xe_pt *pt = entries[i].pt;
915+
struct xe_pt_dir *pt_dir;
898916

899917
if (!pt->level)
900918
continue;
901919

920+
pt_dir = as_xe_pt_dir(pt);
902921
for (j = 0; j < entries[i].qwords; j++) {
903922
struct xe_pt *oldpte = entries[i].pt_entries[j].pt;
923+
int j_ = j + entries[i].ofs;
904924

925+
pt_dir->children[j_] = pt_dir->staging[j_];
905926
xe_pt_destroy(oldpte, xe_vma_vm(vma)->flags, deferred);
906927
}
907928
}
@@ -913,7 +934,7 @@ static void xe_pt_abort_bind(struct xe_vma *vma,
913934
{
914935
int i, j;
915936

916-
xe_pt_commit_locks_assert(vma);
937+
xe_pt_commit_prepare_locks_assert(vma);
917938

918939
for (i = num_entries - 1; i >= 0; --i) {
919940
struct xe_pt *pt = entries[i].pt;
@@ -928,10 +949,10 @@ static void xe_pt_abort_bind(struct xe_vma *vma,
928949
pt_dir = as_xe_pt_dir(pt);
929950
for (j = 0; j < entries[i].qwords; j++) {
930951
u32 j_ = j + entries[i].ofs;
931-
struct xe_pt *newpte = xe_pt_entry(pt_dir, j_);
952+
struct xe_pt *newpte = xe_pt_entry_staging(pt_dir, j_);
932953
struct xe_pt *oldpte = entries[i].pt_entries[j].pt;
933954

934-
pt_dir->children[j_] = oldpte ? &oldpte->base : 0;
955+
pt_dir->staging[j_] = oldpte ? &oldpte->base : 0;
935956
xe_pt_destroy(newpte, xe_vma_vm(vma)->flags, NULL);
936957
}
937958
}
@@ -943,7 +964,7 @@ static void xe_pt_commit_prepare_bind(struct xe_vma *vma,
943964
{
944965
u32 i, j;
945966

946-
xe_pt_commit_locks_assert(vma);
967+
xe_pt_commit_prepare_locks_assert(vma);
947968

948969
for (i = 0; i < num_entries; i++) {
949970
struct xe_pt *pt = entries[i].pt;
@@ -961,10 +982,10 @@ static void xe_pt_commit_prepare_bind(struct xe_vma *vma,
961982
struct xe_pt *newpte = entries[i].pt_entries[j].pt;
962983
struct xe_pt *oldpte = NULL;
963984

964-
if (xe_pt_entry(pt_dir, j_))
965-
oldpte = xe_pt_entry(pt_dir, j_);
985+
if (xe_pt_entry_staging(pt_dir, j_))
986+
oldpte = xe_pt_entry_staging(pt_dir, j_);
966987

967-
pt_dir->children[j_] = &newpte->base;
988+
pt_dir->staging[j_] = &newpte->base;
968989
entries[i].pt_entries[j].pt = oldpte;
969990
}
970991
}
@@ -1494,6 +1515,7 @@ static unsigned int xe_pt_stage_unbind(struct xe_tile *tile, struct xe_vma *vma,
14941515
.ops = &xe_pt_stage_unbind_ops,
14951516
.shifts = xe_normal_pt_shifts,
14961517
.max_level = XE_PT_HIGHEST_LEVEL,
1518+
.staging = true,
14971519
},
14981520
.tile = tile,
14991521
.modified_start = xe_vma_start(vma),
@@ -1535,7 +1557,7 @@ static void xe_pt_abort_unbind(struct xe_vma *vma,
15351557
{
15361558
int i, j;
15371559

1538-
xe_pt_commit_locks_assert(vma);
1560+
xe_pt_commit_prepare_locks_assert(vma);
15391561

15401562
for (i = num_entries - 1; i >= 0; --i) {
15411563
struct xe_vm_pgtable_update *entry = &entries[i];
@@ -1548,7 +1570,7 @@ static void xe_pt_abort_unbind(struct xe_vma *vma,
15481570
continue;
15491571

15501572
for (j = entry->ofs; j < entry->ofs + entry->qwords; j++)
1551-
pt_dir->children[j] =
1573+
pt_dir->staging[j] =
15521574
entries[i].pt_entries[j - entry->ofs].pt ?
15531575
&entries[i].pt_entries[j - entry->ofs].pt->base : NULL;
15541576
}
@@ -1561,7 +1583,7 @@ xe_pt_commit_prepare_unbind(struct xe_vma *vma,
15611583
{
15621584
int i, j;
15631585

1564-
xe_pt_commit_locks_assert(vma);
1586+
xe_pt_commit_prepare_locks_assert(vma);
15651587

15661588
for (i = 0; i < num_entries; ++i) {
15671589
struct xe_vm_pgtable_update *entry = &entries[i];
@@ -1575,8 +1597,8 @@ xe_pt_commit_prepare_unbind(struct xe_vma *vma,
15751597
pt_dir = as_xe_pt_dir(pt);
15761598
for (j = entry->ofs; j < entry->ofs + entry->qwords; j++) {
15771599
entry->pt_entries[j - entry->ofs].pt =
1578-
xe_pt_entry(pt_dir, j);
1579-
pt_dir->children[j] = NULL;
1600+
xe_pt_entry_staging(pt_dir, j);
1601+
pt_dir->staging[j] = NULL;
15801602
}
15811603
}
15821604
}

drivers/gpu/drm/xe/xe_pt_walk.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ int xe_pt_walk_range(struct xe_ptw *parent, unsigned int level,
7474
u64 addr, u64 end, struct xe_pt_walk *walk)
7575
{
7676
pgoff_t offset = xe_pt_offset(addr, level, walk);
77-
struct xe_ptw **entries = parent->children ? parent->children : NULL;
77+
struct xe_ptw **entries = walk->staging ? (parent->staging ?: NULL) :
78+
(parent->children ?: NULL);
7879
const struct xe_pt_walk_ops *ops = walk->ops;
7980
enum page_walk_action action;
8081
struct xe_ptw *child;

drivers/gpu/drm/xe/xe_pt_walk.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
/**
1212
* struct xe_ptw - base class for driver pagetable subclassing.
1313
* @children: Pointer to an array of children if any.
14+
* @staging: Pointer to an array of staging if any.
1415
*
1516
* Drivers could subclass this, and if it's a page-directory, typically
1617
* embed an array of xe_ptw pointers.
1718
*/
1819
struct xe_ptw {
1920
struct xe_ptw **children;
21+
struct xe_ptw **staging;
2022
};
2123

2224
/**
@@ -41,6 +43,8 @@ struct xe_pt_walk {
4143
* as shared pagetables.
4244
*/
4345
bool shared_pt_mode;
46+
/** @staging: Walk staging PT structure */
47+
bool staging;
4448
};
4549

4650
/**

0 commit comments

Comments
 (0)