Commit b06d47be authored by Matthew Brost's avatar Matthew Brost Committed by Rodrigo Vivi

drm/xe: Port Xe to GPUVA

Rather than open coding VM binds and VMA tracking, use the GPUVA
library. GPUVA provides a common infrastructure for VM binds to use mmap
/ munmap semantics and support for VK sparse bindings.

The concepts are:

1) xe_vm inherits from drm_gpuva_manager
2) xe_vma inherits from drm_gpuva
3) xe_vma_op inherits from drm_gpuva_op
4) VM bind operations (MAP, UNMAP, PREFETCH, UNMAP_ALL) call into the
GPUVA code to generate an VMA operations list which is parsed, committed,
and executed.

v2 (CI): Add break after default in case statement.
v3: Rebase
v4: Fix some error handling
v5: Use unlocked version VMA in error paths
v6: Rebase, address some review feedback mainly Thomas H
v7: Fix compile error in xe_vma_op_unwind, address checkpatch
Signed-off-by: default avatarMatthew Brost <matthew.brost@intel.com>
Reviewed-by: default avatarThomas Hellström <thomas.hellstrom@linux.intel.com>
Signed-off-by: default avatarRodrigo Vivi <rodrigo.vivi@intel.com>
parent 5cecdd0b
...@@ -21,6 +21,7 @@ config DRM_XE ...@@ -21,6 +21,7 @@ config DRM_XE
select VMAP_PFN select VMAP_PFN
select DRM_TTM select DRM_TTM
select DRM_TTM_HELPER select DRM_TTM_HELPER
select DRM_GPUVM
select DRM_SCHED select DRM_SCHED
select MMU_NOTIFIER select MMU_NOTIFIER
select WANT_DEV_COREDUMP select WANT_DEV_COREDUMP
......
...@@ -300,7 +300,7 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test) ...@@ -300,7 +300,7 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
/* First part of the test, are we updating our pagetable bo with a new entry? */ /* First part of the test, are we updating our pagetable bo with a new entry? */
xe_map_wr(xe, &bo->vmap, XE_PAGE_SIZE * (NUM_KERNEL_PDE - 1), u64, xe_map_wr(xe, &bo->vmap, XE_PAGE_SIZE * (NUM_KERNEL_PDE - 1), u64,
0xdeaddeadbeefbeef); 0xdeaddeadbeefbeef);
expected = xe_pte_encode(NULL, pt, 0, XE_CACHE_WB, 0, 0); expected = xe_pte_encode(NULL, pt, 0, XE_CACHE_WB, 0);
if (m->eng->vm->flags & XE_VM_FLAGS_64K) if (m->eng->vm->flags & XE_VM_FLAGS_64K)
expected |= XE_PTE_PS64; expected |= XE_PTE_PS64;
if (xe_bo_is_vram(pt)) if (xe_bo_is_vram(pt))
......
...@@ -412,7 +412,9 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo, ...@@ -412,7 +412,9 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo,
{ {
struct dma_resv_iter cursor; struct dma_resv_iter cursor;
struct dma_fence *fence; struct dma_fence *fence;
struct xe_vma *vma; struct drm_gpuva *gpuva;
struct drm_gem_object *obj = &bo->ttm.base;
struct drm_gpuvm_bo *vm_bo;
int ret = 0; int ret = 0;
dma_resv_assert_held(bo->ttm.base.resv); dma_resv_assert_held(bo->ttm.base.resv);
...@@ -425,7 +427,9 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo, ...@@ -425,7 +427,9 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo,
dma_resv_iter_end(&cursor); dma_resv_iter_end(&cursor);
} }
list_for_each_entry(vma, &bo->vmas, bo_link) { drm_gem_for_each_gpuvm_bo(vm_bo, obj) {
drm_gpuvm_bo_for_each_va(gpuva, vm_bo) {
struct xe_vma *vma = gpuva_to_vma(gpuva);
struct xe_vm *vm = xe_vma_vm(vma); struct xe_vm *vm = xe_vma_vm(vma);
trace_xe_vma_evict(vma); trace_xe_vma_evict(vma);
...@@ -454,7 +458,6 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo, ...@@ -454,7 +458,6 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo,
} else { } else {
bool vm_resv_locked = false; bool vm_resv_locked = false;
struct xe_vm *vm = xe_vma_vm(vma);
/* /*
* We need to put the vma on the vm's rebind_list, * We need to put the vma on the vm's rebind_list,
...@@ -462,9 +465,9 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo, ...@@ -462,9 +465,9 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo,
* that we indeed have it locked, put the vma an the * that we indeed have it locked, put the vma an the
* vm's notifier.rebind_list instead and scoop later. * vm's notifier.rebind_list instead and scoop later.
*/ */
if (dma_resv_trylock(&vm->resv)) if (dma_resv_trylock(xe_vm_resv(vm)))
vm_resv_locked = true; vm_resv_locked = true;
else if (ctx->resv != &vm->resv) { else if (ctx->resv != xe_vm_resv(vm)) {
spin_lock(&vm->notifier.list_lock); spin_lock(&vm->notifier.list_lock);
list_move_tail(&vma->notifier.rebind_link, list_move_tail(&vma->notifier.rebind_link,
&vm->notifier.rebind_list); &vm->notifier.rebind_list);
...@@ -477,7 +480,8 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo, ...@@ -477,7 +480,8 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo,
list_add_tail(&vma->rebind_link, &vm->rebind_list); list_add_tail(&vma->rebind_link, &vm->rebind_list);
if (vm_resv_locked) if (vm_resv_locked)
dma_resv_unlock(&vm->resv); dma_resv_unlock(xe_vm_resv(vm));
}
} }
} }
...@@ -1285,7 +1289,7 @@ xe_bo_create_locked_range(struct xe_device *xe, ...@@ -1285,7 +1289,7 @@ xe_bo_create_locked_range(struct xe_device *xe,
} }
} }
bo = __xe_bo_create_locked(xe, bo, tile, vm ? &vm->resv : NULL, bo = __xe_bo_create_locked(xe, bo, tile, vm ? xe_vm_resv(vm) : NULL,
vm && !xe_vm_in_fault_mode(vm) && vm && !xe_vm_in_fault_mode(vm) &&
flags & XE_BO_CREATE_USER_BIT ? flags & XE_BO_CREATE_USER_BIT ?
&vm->lru_bulk_move : NULL, size, &vm->lru_bulk_move : NULL, size,
...@@ -1293,6 +1297,13 @@ xe_bo_create_locked_range(struct xe_device *xe, ...@@ -1293,6 +1297,13 @@ xe_bo_create_locked_range(struct xe_device *xe,
if (IS_ERR(bo)) if (IS_ERR(bo))
return bo; return bo;
/*
* Note that instead of taking a reference no the drm_gpuvm_resv_bo(),
* to ensure the shared resv doesn't disappear under the bo, the bo
* will keep a reference to the vm, and avoid circular references
* by having all the vm's bo refereferences released at vm close
* time.
*/
if (vm && xe_bo_is_user(bo)) if (vm && xe_bo_is_user(bo))
xe_vm_get(vm); xe_vm_get(vm);
bo->vm = vm; bo->vm = vm;
...@@ -1600,7 +1611,7 @@ int xe_bo_validate(struct xe_bo *bo, struct xe_vm *vm, bool allow_res_evict) ...@@ -1600,7 +1611,7 @@ int xe_bo_validate(struct xe_bo *bo, struct xe_vm *vm, bool allow_res_evict)
xe_vm_assert_held(vm); xe_vm_assert_held(vm);
ctx.allow_res_evict = allow_res_evict; ctx.allow_res_evict = allow_res_evict;
ctx.resv = &vm->resv; ctx.resv = xe_vm_resv(vm);
} }
return ttm_bo_validate(&bo->ttm, &bo->placement, &ctx); return ttm_bo_validate(&bo->ttm, &bo->placement, &ctx);
......
...@@ -9,6 +9,15 @@ ...@@ -9,6 +9,15 @@
#include "xe_bo_types.h" #include "xe_bo_types.h"
#include "xe_macros.h" #include "xe_macros.h"
#include "xe_vm_types.h" #include "xe_vm_types.h"
#include "xe_vm.h"
/**
* xe_vm_assert_held(vm) - Assert that the vm's reservation object is held.
* @vm: The vm
*/
#define xe_vm_assert_held(vm) dma_resv_assert_held(xe_vm_resv(vm))
#define XE_DEFAULT_GTT_SIZE_MB 3072ULL /* 3GB by default */ #define XE_DEFAULT_GTT_SIZE_MB 3072ULL /* 3GB by default */
...@@ -149,7 +158,7 @@ void xe_bo_unlock(struct xe_bo *bo, struct ww_acquire_ctx *ww); ...@@ -149,7 +158,7 @@ void xe_bo_unlock(struct xe_bo *bo, struct ww_acquire_ctx *ww);
static inline void xe_bo_unlock_vm_held(struct xe_bo *bo) static inline void xe_bo_unlock_vm_held(struct xe_bo *bo)
{ {
if (bo) { if (bo) {
XE_BUG_ON(bo->vm && bo->ttm.base.resv != &bo->vm->resv); XE_BUG_ON(bo->vm && bo->ttm.base.resv != xe_vm_resv(bo->vm));
if (bo->vm) if (bo->vm)
xe_vm_assert_held(bo->vm); xe_vm_assert_held(bo->vm);
else else
......
...@@ -134,7 +134,7 @@ static struct drm_driver driver = { ...@@ -134,7 +134,7 @@ static struct drm_driver driver = {
.driver_features = .driver_features =
DRIVER_GEM | DRIVER_GEM |
DRIVER_RENDER | DRIVER_SYNCOBJ | DRIVER_RENDER | DRIVER_SYNCOBJ |
DRIVER_SYNCOBJ_TIMELINE, DRIVER_SYNCOBJ_TIMELINE | DRIVER_GEM_GPUVA,
.open = xe_file_open, .open = xe_file_open,
.postclose = xe_file_close, .postclose = xe_file_close,
......
...@@ -350,7 +350,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file) ...@@ -350,7 +350,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
/* Wait behind munmap style rebinds */ /* Wait behind munmap style rebinds */
if (!xe_vm_no_dma_fences(vm)) { if (!xe_vm_no_dma_fences(vm)) {
err = drm_sched_job_add_resv_dependencies(&job->drm, err = drm_sched_job_add_resv_dependencies(&job->drm,
&vm->resv, xe_vm_resv(vm),
DMA_RESV_USAGE_KERNEL); DMA_RESV_USAGE_KERNEL);
if (err) if (err)
goto err_put_job; goto err_put_job;
...@@ -378,7 +378,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file) ...@@ -378,7 +378,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
xe_sched_job_arm(job); xe_sched_job_arm(job);
if (!xe_vm_no_dma_fences(vm)) { if (!xe_vm_no_dma_fences(vm)) {
/* Block userptr invalidations / BO eviction */ /* Block userptr invalidations / BO eviction */
dma_resv_add_fence(&vm->resv, dma_resv_add_fence(xe_vm_resv(vm),
&job->drm.s_fence->finished, &job->drm.s_fence->finished,
DMA_RESV_USAGE_BOOKKEEP); DMA_RESV_USAGE_BOOKKEEP);
......
...@@ -75,10 +75,10 @@ static bool vma_is_valid(struct xe_tile *tile, struct xe_vma *vma) ...@@ -75,10 +75,10 @@ static bool vma_is_valid(struct xe_tile *tile, struct xe_vma *vma)
!(BIT(tile->id) & vma->usm.tile_invalidated); !(BIT(tile->id) & vma->usm.tile_invalidated);
} }
static bool vma_matches(struct xe_vma *vma, struct xe_vma *lookup) static bool vma_matches(struct xe_vma *vma, u64 page_addr)
{ {
if (xe_vma_start(lookup) > xe_vma_end(vma) - 1 || if (page_addr > xe_vma_end(vma) - 1 ||
xe_vma_end(lookup) - 1 < xe_vma_start(vma)) page_addr + SZ_4K - 1 < xe_vma_start(vma))
return false; return false;
return true; return true;
...@@ -91,16 +91,14 @@ static bool only_needs_bo_lock(struct xe_bo *bo) ...@@ -91,16 +91,14 @@ static bool only_needs_bo_lock(struct xe_bo *bo)
static struct xe_vma *lookup_vma(struct xe_vm *vm, u64 page_addr) static struct xe_vma *lookup_vma(struct xe_vm *vm, u64 page_addr)
{ {
struct xe_vma *vma = NULL, lookup; struct xe_vma *vma = NULL;
lookup.start = page_addr;
lookup.end = lookup.start + SZ_4K - 1;
if (vm->usm.last_fault_vma) { /* Fast lookup */ if (vm->usm.last_fault_vma) { /* Fast lookup */
if (vma_matches(vm->usm.last_fault_vma, &lookup)) if (vma_matches(vm->usm.last_fault_vma, page_addr))
vma = vm->usm.last_fault_vma; vma = vm->usm.last_fault_vma;
} }
if (!vma) if (!vma)
vma = xe_vm_find_overlapping_vma(vm, &lookup); vma = xe_vm_find_overlapping_vma(vm, page_addr, SZ_4K);
return vma; return vma;
} }
...@@ -489,12 +487,8 @@ static struct xe_vma *get_acc_vma(struct xe_vm *vm, struct acc *acc) ...@@ -489,12 +487,8 @@ static struct xe_vma *get_acc_vma(struct xe_vm *vm, struct acc *acc)
{ {
u64 page_va = acc->va_range_base + (ffs(acc->sub_granularity) - 1) * u64 page_va = acc->va_range_base + (ffs(acc->sub_granularity) - 1) *
sub_granularity_in_byte(acc->granularity); sub_granularity_in_byte(acc->granularity);
struct xe_vma lookup;
lookup.start = page_va;
lookup.end = lookup.start + SZ_4K - 1;
return xe_vm_find_overlapping_vma(vm, &lookup); return xe_vm_find_overlapping_vma(vm, page_va, SZ_4K);
} }
static int handle_acc(struct xe_gt *gt, struct acc *acc) static int handle_acc(struct xe_gt *gt, struct acc *acc)
......
...@@ -197,7 +197,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m, ...@@ -197,7 +197,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
/* Map the entire BO in our level 0 pt */ /* Map the entire BO in our level 0 pt */
for (i = 0, level = 0; i < num_entries; level++) { for (i = 0, level = 0; i < num_entries; level++) {
entry = xe_pte_encode(NULL, bo, i * XE_PAGE_SIZE, entry = xe_pte_encode(NULL, bo, i * XE_PAGE_SIZE,
XE_CACHE_WB, 0, 0); XE_CACHE_WB, 0);
xe_map_wr(xe, &bo->vmap, map_ofs + level * 8, u64, entry); xe_map_wr(xe, &bo->vmap, map_ofs + level * 8, u64, entry);
...@@ -216,7 +216,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m, ...@@ -216,7 +216,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
i += vm->flags & XE_VM_FLAGS_64K ? XE_64K_PAGE_SIZE : i += vm->flags & XE_VM_FLAGS_64K ? XE_64K_PAGE_SIZE :
XE_PAGE_SIZE) { XE_PAGE_SIZE) {
entry = xe_pte_encode(NULL, batch, i, entry = xe_pte_encode(NULL, batch, i,
XE_CACHE_WB, 0, 0); XE_CACHE_WB, 0);
xe_map_wr(xe, &bo->vmap, map_ofs + level * 8, u64, xe_map_wr(xe, &bo->vmap, map_ofs + level * 8, u64,
entry); entry);
...@@ -1068,7 +1068,7 @@ xe_migrate_update_pgtables_cpu(struct xe_migrate *m, ...@@ -1068,7 +1068,7 @@ xe_migrate_update_pgtables_cpu(struct xe_migrate *m,
DMA_RESV_USAGE_KERNEL)) DMA_RESV_USAGE_KERNEL))
return ERR_PTR(-ETIME); return ERR_PTR(-ETIME);
if (wait_vm && !dma_resv_test_signaled(&vm->resv, if (wait_vm && !dma_resv_test_signaled(xe_vm_resv(vm),
DMA_RESV_USAGE_BOOKKEEP)) DMA_RESV_USAGE_BOOKKEEP))
return ERR_PTR(-ETIME); return ERR_PTR(-ETIME);
...@@ -1159,7 +1159,8 @@ xe_migrate_update_pgtables(struct xe_migrate *m, ...@@ -1159,7 +1159,8 @@ xe_migrate_update_pgtables(struct xe_migrate *m,
u64 addr; u64 addr;
int err = 0; int err = 0;
bool usm = !eng && xe->info.supports_usm; bool usm = !eng && xe->info.supports_usm;
bool first_munmap_rebind = vma && vma->first_munmap_rebind; bool first_munmap_rebind = vma &&
vma->gpuva.flags & XE_VMA_FIRST_REBIND;
struct xe_engine *eng_override = !eng ? m->eng : eng; struct xe_engine *eng_override = !eng ? m->eng : eng;
/* Use the CPU if no in syncs and engine is idle */ /* Use the CPU if no in syncs and engine is idle */
...@@ -1232,8 +1233,7 @@ xe_migrate_update_pgtables(struct xe_migrate *m, ...@@ -1232,8 +1233,7 @@ xe_migrate_update_pgtables(struct xe_migrate *m,
BUG_ON(pt_bo->size != SZ_4K); BUG_ON(pt_bo->size != SZ_4K);
addr = xe_pte_encode(NULL, pt_bo, 0, XE_CACHE_WB, addr = xe_pte_encode(NULL, pt_bo, 0, XE_CACHE_WB, 0);
0, 0);
bb->cs[bb->len++] = lower_32_bits(addr); bb->cs[bb->len++] = lower_32_bits(addr);
bb->cs[bb->len++] = upper_32_bits(addr); bb->cs[bb->len++] = upper_32_bits(addr);
} }
...@@ -1281,7 +1281,7 @@ xe_migrate_update_pgtables(struct xe_migrate *m, ...@@ -1281,7 +1281,7 @@ xe_migrate_update_pgtables(struct xe_migrate *m,
* trigger preempts before moving forward * trigger preempts before moving forward
*/ */
if (first_munmap_rebind) { if (first_munmap_rebind) {
err = job_add_deps(job, &vm->resv, err = job_add_deps(job, xe_vm_resv(vm),
DMA_RESV_USAGE_BOOKKEEP); DMA_RESV_USAGE_BOOKKEEP);
if (err) if (err)
goto err_job; goto err_job;
......
...@@ -100,15 +100,15 @@ static dma_addr_t vma_addr(struct xe_vma *vma, u64 offset, ...@@ -100,15 +100,15 @@ static dma_addr_t vma_addr(struct xe_vma *vma, u64 offset,
} }
} }
static u64 __pte_encode(u64 pte, enum xe_cache_level cache, u32 flags, static u64 __pte_encode(u64 pte, enum xe_cache_level cache,
u32 pt_level) struct xe_vma *vma, u32 pt_level)
{ {
pte |= XE_PAGE_PRESENT | XE_PAGE_RW; pte |= XE_PAGE_PRESENT | XE_PAGE_RW;
if (unlikely(flags & XE_PTE_FLAG_READ_ONLY)) if (unlikely(vma && xe_vma_read_only(vma)))
pte &= ~XE_PAGE_RW; pte &= ~XE_PAGE_RW;
if (unlikely(flags & XE_PTE_FLAG_NULL)) if (unlikely(vma && xe_vma_is_null(vma)))
pte |= XE_PTE_NULL; pte |= XE_PTE_NULL;
/* FIXME: I don't think the PPAT handling is correct for MTL */ /* FIXME: I don't think the PPAT handling is correct for MTL */
...@@ -142,7 +142,6 @@ static u64 __pte_encode(u64 pte, enum xe_cache_level cache, u32 flags, ...@@ -142,7 +142,6 @@ static u64 __pte_encode(u64 pte, enum xe_cache_level cache, u32 flags,
* @bo: If @vma is NULL, representing the memory to point to. * @bo: If @vma is NULL, representing the memory to point to.
* @offset: The offset into @vma or @bo. * @offset: The offset into @vma or @bo.
* @cache: The cache level indicating * @cache: The cache level indicating
* @flags: Currently only supports PTE_READ_ONLY for read-only access.
* @pt_level: The page-table level of the page-table into which the entry * @pt_level: The page-table level of the page-table into which the entry
* is to be inserted. * is to be inserted.
* *
...@@ -150,7 +149,7 @@ static u64 __pte_encode(u64 pte, enum xe_cache_level cache, u32 flags, ...@@ -150,7 +149,7 @@ static u64 __pte_encode(u64 pte, enum xe_cache_level cache, u32 flags,
*/ */
u64 xe_pte_encode(struct xe_vma *vma, struct xe_bo *bo, u64 xe_pte_encode(struct xe_vma *vma, struct xe_bo *bo,
u64 offset, enum xe_cache_level cache, u64 offset, enum xe_cache_level cache,
u32 flags, u32 pt_level) u32 pt_level)
{ {
u64 pte; u64 pte;
bool is_vram; bool is_vram;
...@@ -162,11 +161,11 @@ u64 xe_pte_encode(struct xe_vma *vma, struct xe_bo *bo, ...@@ -162,11 +161,11 @@ u64 xe_pte_encode(struct xe_vma *vma, struct xe_bo *bo,
if (is_vram) { if (is_vram) {
pte |= XE_PPGTT_PTE_LM; pte |= XE_PPGTT_PTE_LM;
if (vma && vma->use_atomic_access_pte_bit) if (vma && vma->gpuva.flags & XE_VMA_ATOMIC_PTE_BIT)
pte |= XE_USM_PPGTT_PTE_AE; pte |= XE_USM_PPGTT_PTE_AE;
} }
return __pte_encode(pte, cache, flags, pt_level); return __pte_encode(pte, cache, vma, pt_level);
} }
static u64 __xe_pt_empty_pte(struct xe_tile *tile, struct xe_vm *vm, static u64 __xe_pt_empty_pte(struct xe_tile *tile, struct xe_vm *vm,
...@@ -179,7 +178,7 @@ static u64 __xe_pt_empty_pte(struct xe_tile *tile, struct xe_vm *vm, ...@@ -179,7 +178,7 @@ static u64 __xe_pt_empty_pte(struct xe_tile *tile, struct xe_vm *vm,
if (level == 0) { if (level == 0) {
u64 empty = xe_pte_encode(NULL, vm->scratch_bo[id], 0, u64 empty = xe_pte_encode(NULL, vm->scratch_bo[id], 0,
XE_CACHE_WB, 0, 0); XE_CACHE_WB, 0);
return empty; return empty;
} else { } else {
...@@ -424,10 +423,9 @@ struct xe_pt_stage_bind_walk { ...@@ -424,10 +423,9 @@ struct xe_pt_stage_bind_walk {
*/ */
bool needs_64K; bool needs_64K;
/** /**
* @pte_flags: Flags determining PTE setup. These are not flags * @vma: VMA being mapped
* encoded directly in the PTE. See @default_pte for those.
*/ */
u32 pte_flags; struct xe_vma *vma;
/* Also input, but is updated during the walk*/ /* Also input, but is updated during the walk*/
/** @curs: The DMA address cursor. */ /** @curs: The DMA address cursor. */
...@@ -564,7 +562,7 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, ...@@ -564,7 +562,7 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level,
return false; return false;
/* null VMA's do not have dma addresses */ /* null VMA's do not have dma addresses */
if (xe_walk->pte_flags & XE_PTE_FLAG_NULL) if (xe_vma_is_null(xe_walk->vma))
return true; return true;
/* Is the DMA address huge PTE size aligned? */ /* Is the DMA address huge PTE size aligned? */
...@@ -590,7 +588,7 @@ xe_pt_scan_64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk) ...@@ -590,7 +588,7 @@ xe_pt_scan_64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk)
return false; return false;
/* null VMA's do not have dma addresses */ /* null VMA's do not have dma addresses */
if (xe_walk->pte_flags & XE_PTE_FLAG_NULL) if (xe_vma_is_null(xe_walk->vma))
return true; return true;
xe_res_next(&curs, addr - xe_walk->va_curs_start); xe_res_next(&curs, addr - xe_walk->va_curs_start);
...@@ -643,14 +641,13 @@ xe_pt_stage_bind_entry(struct xe_ptw *parent, pgoff_t offset, ...@@ -643,14 +641,13 @@ xe_pt_stage_bind_entry(struct xe_ptw *parent, pgoff_t offset,
/* Is this a leaf entry ?*/ /* Is this a leaf entry ?*/
if (level == 0 || xe_pt_hugepte_possible(addr, next, level, xe_walk)) { if (level == 0 || xe_pt_hugepte_possible(addr, next, level, xe_walk)) {
struct xe_res_cursor *curs = xe_walk->curs; struct xe_res_cursor *curs = xe_walk->curs;
bool is_null = xe_walk->pte_flags & XE_PTE_FLAG_NULL; bool is_null = xe_vma_is_null(xe_walk->vma);
XE_WARN_ON(xe_walk->va_curs_start != addr); XE_WARN_ON(xe_walk->va_curs_start != addr);
pte = __pte_encode(is_null ? 0 : pte = __pte_encode(is_null ? 0 :
xe_res_dma(curs) + xe_walk->dma_offset, xe_res_dma(curs) + xe_walk->dma_offset,
xe_walk->cache, xe_walk->pte_flags, xe_walk->cache, xe_walk->vma, level);
level);
pte |= xe_walk->default_pte; pte |= xe_walk->default_pte;
/* /*
...@@ -762,7 +759,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, ...@@ -762,7 +759,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
.tile = tile, .tile = tile,
.curs = &curs, .curs = &curs,
.va_curs_start = xe_vma_start(vma), .va_curs_start = xe_vma_start(vma),
.pte_flags = vma->pte_flags, .vma = vma,
.wupd.entries = entries, .wupd.entries = entries,
.needs_64K = (xe_vma_vm(vma)->flags & XE_VM_FLAGS_64K) && is_vram, .needs_64K = (xe_vma_vm(vma)->flags & XE_VM_FLAGS_64K) && is_vram,
}; };
...@@ -771,7 +768,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, ...@@ -771,7 +768,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
if (is_vram) { if (is_vram) {
xe_walk.default_pte = XE_PPGTT_PTE_LM; xe_walk.default_pte = XE_PPGTT_PTE_LM;
if (vma && vma->use_atomic_access_pte_bit) if (vma && vma->gpuva.flags & XE_VMA_ATOMIC_PTE_BIT)
xe_walk.default_pte |= XE_USM_PPGTT_PTE_AE; xe_walk.default_pte |= XE_USM_PPGTT_PTE_AE;
xe_walk.dma_offset = vram_region_gpu_offset(bo->ttm.resource); xe_walk.dma_offset = vram_region_gpu_offset(bo->ttm.resource);
xe_walk.cache = XE_CACHE_WB; xe_walk.cache = XE_CACHE_WB;
...@@ -990,7 +987,7 @@ static void xe_pt_commit_locks_assert(struct xe_vma *vma) ...@@ -990,7 +987,7 @@ static void xe_pt_commit_locks_assert(struct xe_vma *vma)
else if (!xe_vma_is_null(vma)) else if (!xe_vma_is_null(vma))
dma_resv_assert_held(xe_vma_bo(vma)->ttm.base.resv); dma_resv_assert_held(xe_vma_bo(vma)->ttm.base.resv);
dma_resv_assert_held(&vm->resv); xe_vm_assert_held(vm);
} }
static void xe_pt_commit_bind(struct xe_vma *vma, static void xe_pt_commit_bind(struct xe_vma *vma,
...@@ -1343,6 +1340,7 @@ __xe_pt_bind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_engine *e, ...@@ -1343,6 +1340,7 @@ __xe_pt_bind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_engine *e,
syncs, num_syncs, syncs, num_syncs,
&bind_pt_update.base); &bind_pt_update.base);
if (!IS_ERR(fence)) { if (!IS_ERR(fence)) {
bool last_munmap_rebind = vma->gpuva.flags & XE_VMA_LAST_REBIND;
LLIST_HEAD(deferred); LLIST_HEAD(deferred);
/* TLB invalidation must be done before signaling rebind */ /* TLB invalidation must be done before signaling rebind */
...@@ -1358,8 +1356,8 @@ __xe_pt_bind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_engine *e, ...@@ -1358,8 +1356,8 @@ __xe_pt_bind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_engine *e,
} }
/* add shared fence now for pagetable delayed destroy */ /* add shared fence now for pagetable delayed destroy */
dma_resv_add_fence(&vm->resv, fence, !rebind && dma_resv_add_fence(xe_vm_resv(vm), fence, !rebind &&
vma->last_munmap_rebind ? last_munmap_rebind ?
DMA_RESV_USAGE_KERNEL : DMA_RESV_USAGE_KERNEL :
DMA_RESV_USAGE_BOOKKEEP); DMA_RESV_USAGE_BOOKKEEP);
...@@ -1377,7 +1375,7 @@ __xe_pt_bind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_engine *e, ...@@ -1377,7 +1375,7 @@ __xe_pt_bind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_engine *e,
up_read(&vm->userptr.notifier_lock); up_read(&vm->userptr.notifier_lock);
xe_bo_put_commit(&deferred); xe_bo_put_commit(&deferred);
} }
if (!rebind && vma->last_munmap_rebind && if (!rebind && last_munmap_rebind &&
xe_vm_in_compute_mode(vm)) xe_vm_in_compute_mode(vm))
queue_work(vm->xe->ordered_wq, queue_work(vm->xe->ordered_wq,
&vm->preempt.rebind_work); &vm->preempt.rebind_work);
...@@ -1676,7 +1674,7 @@ __xe_pt_unbind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_engine *e ...@@ -1676,7 +1674,7 @@ __xe_pt_unbind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_engine *e
fence = &ifence->base.base; fence = &ifence->base.base;
/* add shared fence now for pagetable delayed destroy */ /* add shared fence now for pagetable delayed destroy */
dma_resv_add_fence(&vm->resv, fence, dma_resv_add_fence(xe_vm_resv(vm), fence,
DMA_RESV_USAGE_BOOKKEEP); DMA_RESV_USAGE_BOOKKEEP);
/* This fence will be installed by caller when doing eviction */ /* This fence will be installed by caller when doing eviction */
......
...@@ -50,5 +50,5 @@ u64 xe_pde_encode(struct xe_bo *bo, u64 bo_offset, ...@@ -50,5 +50,5 @@ u64 xe_pde_encode(struct xe_bo *bo, u64 bo_offset,
u64 xe_pte_encode(struct xe_vma *vma, struct xe_bo *bo, u64 xe_pte_encode(struct xe_vma *vma, struct xe_bo *bo,
u64 offset, enum xe_cache_level cache, u64 offset, enum xe_cache_level cache,
u32 flags, u32 pt_level); u32 pt_level);
#endif #endif
...@@ -65,7 +65,7 @@ int xe_vma_userptr_pin_pages(struct xe_vma *vma) ...@@ -65,7 +65,7 @@ int xe_vma_userptr_pin_pages(struct xe_vma *vma)
lockdep_assert_held(&vm->lock); lockdep_assert_held(&vm->lock);
XE_BUG_ON(!xe_vma_is_userptr(vma)); XE_BUG_ON(!xe_vma_is_userptr(vma));
retry: retry:
if (vma->destroyed) if (vma->gpuva.flags & XE_VMA_DESTROYED)
return 0; return 0;
notifier_seq = mmu_interval_read_begin(&vma->userptr.notifier); notifier_seq = mmu_interval_read_begin(&vma->userptr.notifier);
...@@ -312,7 +312,7 @@ static void resume_and_reinstall_preempt_fences(struct xe_vm *vm) ...@@ -312,7 +312,7 @@ static void resume_and_reinstall_preempt_fences(struct xe_vm *vm)
list_for_each_entry(e, &vm->preempt.engines, compute.link) { list_for_each_entry(e, &vm->preempt.engines, compute.link) {
e->ops->resume(e); e->ops->resume(e);
dma_resv_add_fence(&vm->resv, e->compute.pfence, dma_resv_add_fence(xe_vm_resv(vm), e->compute.pfence,
DMA_RESV_USAGE_BOOKKEEP); DMA_RESV_USAGE_BOOKKEEP);
xe_vm_fence_all_extobjs(vm, e->compute.pfence, xe_vm_fence_all_extobjs(vm, e->compute.pfence,
DMA_RESV_USAGE_BOOKKEEP); DMA_RESV_USAGE_BOOKKEEP);
...@@ -350,7 +350,7 @@ int xe_vm_add_compute_engine(struct xe_vm *vm, struct xe_engine *e) ...@@ -350,7 +350,7 @@ int xe_vm_add_compute_engine(struct xe_vm *vm, struct xe_engine *e)
down_read(&vm->userptr.notifier_lock); down_read(&vm->userptr.notifier_lock);
dma_resv_add_fence(&vm->resv, pfence, dma_resv_add_fence(xe_vm_resv(vm), pfence,
DMA_RESV_USAGE_BOOKKEEP); DMA_RESV_USAGE_BOOKKEEP);
xe_vm_fence_all_extobjs(vm, pfence, DMA_RESV_USAGE_BOOKKEEP); xe_vm_fence_all_extobjs(vm, pfence, DMA_RESV_USAGE_BOOKKEEP);
...@@ -466,7 +466,7 @@ int xe_vm_lock_dma_resv(struct xe_vm *vm, struct ww_acquire_ctx *ww, ...@@ -466,7 +466,7 @@ int xe_vm_lock_dma_resv(struct xe_vm *vm, struct ww_acquire_ctx *ww,
xe_bo_assert_held(xe_vma_bo(vma)); xe_bo_assert_held(xe_vma_bo(vma));
list_del_init(&vma->notifier.rebind_link); list_del_init(&vma->notifier.rebind_link);
if (vma->tile_present && !vma->destroyed) if (vma->tile_present && !(vma->gpuva.flags & XE_VMA_DESTROYED))
list_move_tail(&vma->rebind_link, &vm->rebind_list); list_move_tail(&vma->rebind_link, &vm->rebind_list);
} }
spin_unlock(&vm->notifier.list_lock); spin_unlock(&vm->notifier.list_lock);
...@@ -609,7 +609,8 @@ static void preempt_rebind_work_func(struct work_struct *w) ...@@ -609,7 +609,8 @@ static void preempt_rebind_work_func(struct work_struct *w)
goto out_unlock; goto out_unlock;
list_for_each_entry(vma, &vm->rebind_list, rebind_link) { list_for_each_entry(vma, &vm->rebind_list, rebind_link) {
if (xe_vma_has_no_bo(vma) || vma->destroyed) if (xe_vma_has_no_bo(vma) ||
vma->gpuva.flags & XE_VMA_DESTROYED)
continue; continue;
err = xe_bo_validate(xe_vma_bo(vma), vm, false); err = xe_bo_validate(xe_vma_bo(vma), vm, false);
...@@ -629,7 +630,7 @@ static void preempt_rebind_work_func(struct work_struct *w) ...@@ -629,7 +630,7 @@ static void preempt_rebind_work_func(struct work_struct *w)
} }
/* Wait on munmap style VM unbinds */ /* Wait on munmap style VM unbinds */
wait = dma_resv_wait_timeout(&vm->resv, wait = dma_resv_wait_timeout(xe_vm_resv(vm),
DMA_RESV_USAGE_KERNEL, DMA_RESV_USAGE_KERNEL,
false, MAX_SCHEDULE_TIMEOUT); false, MAX_SCHEDULE_TIMEOUT);
if (wait <= 0) { if (wait <= 0) {
...@@ -725,7 +726,8 @@ static bool vma_userptr_invalidate(struct mmu_interval_notifier *mni, ...@@ -725,7 +726,8 @@ static bool vma_userptr_invalidate(struct mmu_interval_notifier *mni,
* Tell exec and rebind worker they need to repin and rebind this * Tell exec and rebind worker they need to repin and rebind this
* userptr. * userptr.
*/ */
if (!xe_vm_in_fault_mode(vm) && !vma->destroyed && vma->tile_present) { if (!xe_vm_in_fault_mode(vm) &&
!(vma->gpuva.flags & XE_VMA_DESTROYED) && vma->tile_present) {
spin_lock(&vm->userptr.invalidated_lock); spin_lock(&vm->userptr.invalidated_lock);
list_move_tail(&vma->userptr.invalidate_link, list_move_tail(&vma->userptr.invalidate_link,
&vm->userptr.invalidated); &vm->userptr.invalidated);
...@@ -740,13 +742,13 @@ static bool vma_userptr_invalidate(struct mmu_interval_notifier *mni, ...@@ -740,13 +742,13 @@ static bool vma_userptr_invalidate(struct mmu_interval_notifier *mni,
* unbinds to complete, and those are attached as BOOKMARK fences * unbinds to complete, and those are attached as BOOKMARK fences
* to the vm. * to the vm.
*/ */
dma_resv_iter_begin(&cursor, &vm->resv, dma_resv_iter_begin(&cursor, xe_vm_resv(vm),
DMA_RESV_USAGE_BOOKKEEP); DMA_RESV_USAGE_BOOKKEEP);
dma_resv_for_each_fence_unlocked(&cursor, fence) dma_resv_for_each_fence_unlocked(&cursor, fence)
dma_fence_enable_sw_signaling(fence); dma_fence_enable_sw_signaling(fence);
dma_resv_iter_end(&cursor); dma_resv_iter_end(&cursor);
err = dma_resv_wait_timeout(&vm->resv, err = dma_resv_wait_timeout(xe_vm_resv(vm),
DMA_RESV_USAGE_BOOKKEEP, DMA_RESV_USAGE_BOOKKEEP,
false, MAX_SCHEDULE_TIMEOUT); false, MAX_SCHEDULE_TIMEOUT);
XE_WARN_ON(err <= 0); XE_WARN_ON(err <= 0);
...@@ -792,7 +794,7 @@ int xe_vm_userptr_pin(struct xe_vm *vm) ...@@ -792,7 +794,7 @@ int xe_vm_userptr_pin(struct xe_vm *vm)
} }
/* Take lock and move to rebind_list for rebinding. */ /* Take lock and move to rebind_list for rebinding. */
err = dma_resv_lock_interruptible(&vm->resv, NULL); err = dma_resv_lock_interruptible(xe_vm_resv(vm), NULL);
if (err) if (err)
goto out_err; goto out_err;
...@@ -801,7 +803,7 @@ int xe_vm_userptr_pin(struct xe_vm *vm) ...@@ -801,7 +803,7 @@ int xe_vm_userptr_pin(struct xe_vm *vm)
list_move_tail(&vma->rebind_link, &vm->rebind_list); list_move_tail(&vma->rebind_link, &vm->rebind_list);
} }
dma_resv_unlock(&vm->resv); dma_resv_unlock(xe_vm_resv(vm));
return 0; return 0;
...@@ -830,7 +832,8 @@ int xe_vm_userptr_check_repin(struct xe_vm *vm) ...@@ -830,7 +832,8 @@ int xe_vm_userptr_check_repin(struct xe_vm *vm)
static struct dma_fence * static struct dma_fence *
xe_vm_bind_vma(struct xe_vma *vma, struct xe_engine *e, xe_vm_bind_vma(struct xe_vma *vma, struct xe_engine *e,
struct xe_sync_entry *syncs, u32 num_syncs); struct xe_sync_entry *syncs, u32 num_syncs,
bool first_op, bool last_op);
struct dma_fence *xe_vm_rebind(struct xe_vm *vm, bool rebind_worker) struct dma_fence *xe_vm_rebind(struct xe_vm *vm, bool rebind_worker)
{ {
...@@ -851,7 +854,7 @@ struct dma_fence *xe_vm_rebind(struct xe_vm *vm, bool rebind_worker) ...@@ -851,7 +854,7 @@ struct dma_fence *xe_vm_rebind(struct xe_vm *vm, bool rebind_worker)
trace_xe_vma_rebind_worker(vma); trace_xe_vma_rebind_worker(vma);
else else
trace_xe_vma_rebind_exec(vma); trace_xe_vma_rebind_exec(vma);
fence = xe_vm_bind_vma(vma, NULL, NULL, 0); fence = xe_vm_bind_vma(vma, NULL, NULL, 0, false, false);
if (IS_ERR(fence)) if (IS_ERR(fence))
return fence; return fence;
} }
...@@ -887,14 +890,14 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm, ...@@ -887,14 +890,14 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
INIT_LIST_HEAD(&vma->notifier.rebind_link); INIT_LIST_HEAD(&vma->notifier.rebind_link);
INIT_LIST_HEAD(&vma->extobj.link); INIT_LIST_HEAD(&vma->extobj.link);
vma->vm = vm; INIT_LIST_HEAD(&vma->gpuva.gem.entry);
vma->start = start; vma->gpuva.vm = &vm->gpuvm;
vma->end = end; vma->gpuva.va.addr = start;
vma->pte_flags = 0; vma->gpuva.va.range = end - start + 1;
if (read_only) if (read_only)
vma->pte_flags |= XE_PTE_FLAG_READ_ONLY; vma->gpuva.flags |= XE_VMA_READ_ONLY;
if (is_null) if (is_null)
vma->pte_flags |= XE_PTE_FLAG_NULL; vma->gpuva.flags |= DRM_GPUVA_SPARSE;
if (tile_mask) { if (tile_mask) {
vma->tile_mask = tile_mask; vma->tile_mask = tile_mask;
...@@ -904,19 +907,30 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm, ...@@ -904,19 +907,30 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
} }
if (vm->xe->info.platform == XE_PVC) if (vm->xe->info.platform == XE_PVC)
vma->use_atomic_access_pte_bit = true; vma->gpuva.flags |= XE_VMA_ATOMIC_PTE_BIT;
if (bo) { if (bo) {
struct drm_gpuvm_bo *vm_bo;
xe_bo_assert_held(bo); xe_bo_assert_held(bo);
vma->bo_offset = bo_offset_or_userptr;
vma->bo = xe_bo_get(bo); vm_bo = drm_gpuvm_bo_obtain(vma->gpuva.vm, &bo->ttm.base);
list_add_tail(&vma->bo_link, &bo->vmas); if (IS_ERR(vm_bo)) {
kfree(vma);
return ERR_CAST(vm_bo);
}
drm_gem_object_get(&bo->ttm.base);
vma->gpuva.gem.obj = &bo->ttm.base;
vma->gpuva.gem.offset = bo_offset_or_userptr;
drm_gpuva_link(&vma->gpuva, vm_bo);
drm_gpuvm_bo_put(vm_bo);
} else /* userptr or null */ { } else /* userptr or null */ {
if (!is_null) { if (!is_null) {
u64 size = end - start + 1; u64 size = end - start + 1;
int err; int err;
vma->userptr.ptr = bo_offset_or_userptr; vma->gpuva.gem.offset = bo_offset_or_userptr;
err = mmu_interval_notifier_insert(&vma->userptr.notifier, err = mmu_interval_notifier_insert(&vma->userptr.notifier,
current->mm, current->mm,
...@@ -991,12 +1005,20 @@ static struct xe_vma * ...@@ -991,12 +1005,20 @@ static struct xe_vma *
bo_has_vm_references_locked(struct xe_bo *bo, struct xe_vm *vm, bo_has_vm_references_locked(struct xe_bo *bo, struct xe_vm *vm,
struct xe_vma *ignore) struct xe_vma *ignore)
{ {
struct xe_vma *vma; struct drm_gpuvm_bo *vm_bo;
struct drm_gpuva *va;
struct drm_gem_object *obj = &bo->ttm.base;
xe_bo_assert_held(bo);
drm_gem_for_each_gpuvm_bo(vm_bo, obj) {
drm_gpuvm_bo_for_each_va(va, vm_bo) {
struct xe_vma *vma = gpuva_to_vma(va);
list_for_each_entry(vma, &bo->vmas, bo_link) {
if (vma != ignore && xe_vma_vm(vma) == vm) if (vma != ignore && xe_vma_vm(vma) == vm)
return vma; return vma;
} }
}
return NULL; return NULL;
} }
...@@ -1016,6 +1038,8 @@ static bool bo_has_vm_references(struct xe_bo *bo, struct xe_vm *vm, ...@@ -1016,6 +1038,8 @@ static bool bo_has_vm_references(struct xe_bo *bo, struct xe_vm *vm,
static void __vm_insert_extobj(struct xe_vm *vm, struct xe_vma *vma) static void __vm_insert_extobj(struct xe_vm *vm, struct xe_vma *vma)
{ {
lockdep_assert_held_write(&vm->lock);
list_add(&vma->extobj.link, &vm->extobj.list); list_add(&vma->extobj.link, &vm->extobj.list);
vm->extobj.entries++; vm->extobj.entries++;
} }
...@@ -1049,19 +1073,21 @@ static void xe_vma_destroy(struct xe_vma *vma, struct dma_fence *fence) ...@@ -1049,19 +1073,21 @@ static void xe_vma_destroy(struct xe_vma *vma, struct dma_fence *fence)
XE_BUG_ON(!list_empty(&vma->unbind_link)); XE_BUG_ON(!list_empty(&vma->unbind_link));
if (xe_vma_is_userptr(vma)) { if (xe_vma_is_userptr(vma)) {
XE_WARN_ON(!vma->destroyed); XE_WARN_ON(!(vma->gpuva.flags & XE_VMA_DESTROYED));
spin_lock(&vm->userptr.invalidated_lock); spin_lock(&vm->userptr.invalidated_lock);
list_del_init(&vma->userptr.invalidate_link); list_del_init(&vma->userptr.invalidate_link);
spin_unlock(&vm->userptr.invalidated_lock); spin_unlock(&vm->userptr.invalidated_lock);
list_del(&vma->userptr_link); list_del(&vma->userptr_link);
} else if (!xe_vma_is_null(vma)) { } else if (!xe_vma_is_null(vma)) {
xe_bo_assert_held(xe_vma_bo(vma)); xe_bo_assert_held(xe_vma_bo(vma));
list_del(&vma->bo_link);
spin_lock(&vm->notifier.list_lock); spin_lock(&vm->notifier.list_lock);
list_del(&vma->notifier.rebind_link); list_del(&vma->notifier.rebind_link);
spin_unlock(&vm->notifier.list_lock); spin_unlock(&vm->notifier.list_lock);
drm_gpuva_unlink(&vma->gpuva);
if (!xe_vma_bo(vma)->vm && vm_remove_extobj(vma)) { if (!xe_vma_bo(vma)->vm && vm_remove_extobj(vma)) {
struct xe_vma *other; struct xe_vma *other;
...@@ -1116,65 +1142,34 @@ static void xe_vma_destroy_unlocked(struct xe_vma *vma) ...@@ -1116,65 +1142,34 @@ static void xe_vma_destroy_unlocked(struct xe_vma *vma)
xe_bo_put(bo); xe_bo_put(bo);
} }
static struct xe_vma *to_xe_vma(const struct rb_node *node)
{
BUILD_BUG_ON(offsetof(struct xe_vma, vm_node) != 0);
return (struct xe_vma *)node;
}
static int xe_vma_cmp(struct xe_vma *a, struct xe_vma *b)
{
if (xe_vma_end(a) - 1 < xe_vma_start(b)) {
return -1;
} else if (xe_vma_end(b) - 1 < xe_vma_start(a)) {
return 1;
} else {
return 0;
}
}
static bool xe_vma_less_cb(struct rb_node *a, const struct rb_node *b)
{
return xe_vma_cmp(to_xe_vma(a), to_xe_vma(b)) < 0;
}
int xe_vma_cmp_vma_cb(const void *key, const struct rb_node *node)
{
struct xe_vma *cmp = to_xe_vma(node);
struct xe_vma *own = (struct xe_vma *)key;
if (xe_vma_start(own) > xe_vma_end(cmp) - 1)
return 1;
if (xe_vma_end(own) - 1 < xe_vma_start(cmp))
return -1;
return 0;
}
struct xe_vma * struct xe_vma *
xe_vm_find_overlapping_vma(struct xe_vm *vm, struct xe_vma *vma) xe_vm_find_overlapping_vma(struct xe_vm *vm, u64 start, u64 range)
{ {
struct rb_node *node; struct drm_gpuva *gpuva;
lockdep_assert_held(&vm->lock); lockdep_assert_held(&vm->lock);
if (xe_vm_is_closed_or_banned(vm)) if (xe_vm_is_closed_or_banned(vm))
return NULL; return NULL;
XE_BUG_ON(xe_vma_end(vma) > vm->size); XE_BUG_ON(start + range > vm->size);
node = rb_find(vma, &vm->vmas, xe_vma_cmp_vma_cb); gpuva = drm_gpuva_find_first(&vm->gpuvm, start, range);
return node ? to_xe_vma(node) : NULL; return gpuva ? gpuva_to_vma(gpuva) : NULL;
} }
static void xe_vm_insert_vma(struct xe_vm *vm, struct xe_vma *vma) static int xe_vm_insert_vma(struct xe_vm *vm, struct xe_vma *vma)
{ {
int err;
XE_BUG_ON(xe_vma_vm(vma) != vm); XE_BUG_ON(xe_vma_vm(vma) != vm);
lockdep_assert_held(&vm->lock); lockdep_assert_held(&vm->lock);
rb_add(&vma->vm_node, &vm->vmas, xe_vma_less_cb); err = drm_gpuva_insert(&vm->gpuvm, &vma->gpuva);
XE_WARN_ON(err); /* Shouldn't be possible */
return err;
} }
static void xe_vm_remove_vma(struct xe_vm *vm, struct xe_vma *vma) static void xe_vm_remove_vma(struct xe_vm *vm, struct xe_vma *vma)
...@@ -1182,18 +1177,38 @@ static void xe_vm_remove_vma(struct xe_vm *vm, struct xe_vma *vma) ...@@ -1182,18 +1177,38 @@ static void xe_vm_remove_vma(struct xe_vm *vm, struct xe_vma *vma)
XE_BUG_ON(xe_vma_vm(vma) != vm); XE_BUG_ON(xe_vma_vm(vma) != vm);
lockdep_assert_held(&vm->lock); lockdep_assert_held(&vm->lock);
rb_erase(&vma->vm_node, &vm->vmas); drm_gpuva_remove(&vma->gpuva);
if (vm->usm.last_fault_vma == vma) if (vm->usm.last_fault_vma == vma)
vm->usm.last_fault_vma = NULL; vm->usm.last_fault_vma = NULL;
} }
static void async_op_work_func(struct work_struct *w); static struct drm_gpuva_op *xe_vm_op_alloc(void)
{
struct xe_vma_op *op;
op = kzalloc(sizeof(*op), GFP_KERNEL);
if (unlikely(!op))
return NULL;
return &op->base;
}
static void xe_vm_free(struct drm_gpuvm *gpuvm);
static struct drm_gpuvm_ops gpuvm_ops = {
.op_alloc = xe_vm_op_alloc,
.vm_free = xe_vm_free,
};
static void xe_vma_op_work_func(struct work_struct *w);
static void vm_destroy_work_func(struct work_struct *w); static void vm_destroy_work_func(struct work_struct *w);
struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags) struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
{ {
struct drm_gem_object *vm_resv_obj;
struct xe_vm *vm; struct xe_vm *vm;
int err, i = 0, number_tiles = 0; int err, number_tiles = 0;
struct xe_tile *tile; struct xe_tile *tile;
u8 id; u8 id;
...@@ -1202,12 +1217,9 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags) ...@@ -1202,12 +1217,9 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
vm->xe = xe; vm->xe = xe;
kref_init(&vm->refcount);
dma_resv_init(&vm->resv);
vm->size = 1ull << xe_pt_shift(xe->info.vm_max_level + 1); vm->size = 1ull << xe_pt_shift(xe->info.vm_max_level + 1);
vm->vmas = RB_ROOT;
vm->flags = flags; vm->flags = flags;
init_rwsem(&vm->lock); init_rwsem(&vm->lock);
...@@ -1223,7 +1235,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags) ...@@ -1223,7 +1235,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
spin_lock_init(&vm->notifier.list_lock); spin_lock_init(&vm->notifier.list_lock);
INIT_LIST_HEAD(&vm->async_ops.pending); INIT_LIST_HEAD(&vm->async_ops.pending);
INIT_WORK(&vm->async_ops.work, async_op_work_func); INIT_WORK(&vm->async_ops.work, xe_vma_op_work_func);
spin_lock_init(&vm->async_ops.lock); spin_lock_init(&vm->async_ops.lock);
INIT_WORK(&vm->destroy_work, vm_destroy_work_func); INIT_WORK(&vm->destroy_work, vm_destroy_work_func);
...@@ -1239,9 +1251,20 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags) ...@@ -1239,9 +1251,20 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
xe_device_mem_access_get(xe); xe_device_mem_access_get(xe);
} }
err = dma_resv_lock_interruptible(&vm->resv, NULL); vm_resv_obj = drm_gpuvm_resv_object_alloc(&xe->drm);
if (!vm_resv_obj) {
err = -ENOMEM;
goto err_no_resv;
}
drm_gpuvm_init(&vm->gpuvm, "Xe VM", 0, &xe->drm, vm_resv_obj,
0, vm->size, 0, 0, &gpuvm_ops);
drm_gem_object_put(vm_resv_obj);
err = dma_resv_lock_interruptible(xe_vm_resv(vm), NULL);
if (err) if (err)
goto err_put; goto err_close;
if (IS_DGFX(xe) && xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K) if (IS_DGFX(xe) && xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K)
vm->flags |= XE_VM_FLAGS_64K; vm->flags |= XE_VM_FLAGS_64K;
...@@ -1255,7 +1278,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags) ...@@ -1255,7 +1278,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
if (IS_ERR(vm->pt_root[id])) { if (IS_ERR(vm->pt_root[id])) {
err = PTR_ERR(vm->pt_root[id]); err = PTR_ERR(vm->pt_root[id]);
vm->pt_root[id] = NULL; vm->pt_root[id] = NULL;
goto err_destroy_root; goto err_unlock_close;
} }
} }
...@@ -1266,7 +1289,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags) ...@@ -1266,7 +1289,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
err = xe_pt_create_scratch(xe, tile, vm); err = xe_pt_create_scratch(xe, tile, vm);
if (err) if (err)
goto err_scratch_pt; goto err_unlock_close;
} }
vm->batch_invalidate_tlb = true; vm->batch_invalidate_tlb = true;
} }
...@@ -1289,7 +1312,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags) ...@@ -1289,7 +1312,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
xe_pt_populate_empty(tile, vm, vm->pt_root[id]); xe_pt_populate_empty(tile, vm, vm->pt_root[id]);
} }
dma_resv_unlock(&vm->resv); dma_resv_unlock(xe_vm_resv(vm));
/* Kernel migration VM shouldn't have a circular loop.. */ /* Kernel migration VM shouldn't have a circular loop.. */
if (!(flags & XE_VM_FLAG_MIGRATION)) { if (!(flags & XE_VM_FLAG_MIGRATION)) {
...@@ -1307,8 +1330,8 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags) ...@@ -1307,8 +1330,8 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
ENGINE_FLAG_VM); ENGINE_FLAG_VM);
xe_vm_put(migrate_vm); xe_vm_put(migrate_vm);
if (IS_ERR(eng)) { if (IS_ERR(eng)) {
xe_vm_close_and_put(vm); err = PTR_ERR(eng);
return ERR_CAST(eng); goto err_close;
} }
vm->eng[id] = eng; vm->eng[id] = eng;
number_tiles++; number_tiles++;
...@@ -1329,27 +1352,13 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags) ...@@ -1329,27 +1352,13 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
return vm; return vm;
err_scratch_pt: err_unlock_close:
for_each_tile(tile, xe, id) { dma_resv_unlock(xe_vm_resv(vm));
if (!vm->pt_root[id]) err_close:
continue; xe_vm_close_and_put(vm);
return ERR_PTR(err);
i = vm->pt_root[id]->level; err_no_resv:
while (i)
if (vm->scratch_pt[id][--i])
xe_pt_destroy(vm->scratch_pt[id][i],
vm->flags, NULL);
xe_bo_unpin(vm->scratch_bo[id]);
xe_bo_put(vm->scratch_bo[id]);
}
err_destroy_root:
for_each_tile(tile, xe, id) {
if (vm->pt_root[id])
xe_pt_destroy(vm->pt_root[id], vm->flags, NULL);
}
dma_resv_unlock(&vm->resv);
err_put:
dma_resv_fini(&vm->resv);
kfree(vm); kfree(vm);
if (!(flags & XE_VM_FLAG_MIGRATION)) { if (!(flags & XE_VM_FLAG_MIGRATION)) {
xe_device_mem_access_put(xe); xe_device_mem_access_put(xe);
...@@ -1404,16 +1413,17 @@ static void xe_vm_close(struct xe_vm *vm) ...@@ -1404,16 +1413,17 @@ static void xe_vm_close(struct xe_vm *vm)
void xe_vm_close_and_put(struct xe_vm *vm) void xe_vm_close_and_put(struct xe_vm *vm)
{ {
struct rb_root contested = RB_ROOT; LIST_HEAD(contested);
struct ww_acquire_ctx ww; struct ww_acquire_ctx ww;
struct xe_device *xe = vm->xe; struct xe_device *xe = vm->xe;
struct xe_tile *tile; struct xe_tile *tile;
struct xe_vma *vma, *next_vma;
struct drm_gpuva *gpuva, *next;
u8 id; u8 id;
XE_BUG_ON(vm->preempt.num_engines); XE_BUG_ON(vm->preempt.num_engines);
xe_vm_close(vm); xe_vm_close(vm);
flush_async_ops(vm); flush_async_ops(vm);
if (xe_vm_in_compute_mode(vm)) if (xe_vm_in_compute_mode(vm))
flush_work(&vm->preempt.rebind_work); flush_work(&vm->preempt.rebind_work);
...@@ -1428,16 +1438,16 @@ void xe_vm_close_and_put(struct xe_vm *vm) ...@@ -1428,16 +1438,16 @@ void xe_vm_close_and_put(struct xe_vm *vm)
down_write(&vm->lock); down_write(&vm->lock);
xe_vm_lock(vm, &ww, 0, false); xe_vm_lock(vm, &ww, 0, false);
while (vm->vmas.rb_node) { drm_gpuvm_for_each_va_safe(gpuva, next, &vm->gpuvm) {
struct xe_vma *vma = to_xe_vma(vm->vmas.rb_node); vma = gpuva_to_vma(gpuva);
if (xe_vma_has_no_bo(vma)) { if (xe_vma_has_no_bo(vma)) {
down_read(&vm->userptr.notifier_lock); down_read(&vm->userptr.notifier_lock);
vma->destroyed = true; vma->gpuva.flags |= XE_VMA_DESTROYED;
up_read(&vm->userptr.notifier_lock); up_read(&vm->userptr.notifier_lock);
} }
rb_erase(&vma->vm_node, &vm->vmas); xe_vm_remove_vma(vm, vma);
/* easy case, remove from VMA? */ /* easy case, remove from VMA? */
if (xe_vma_has_no_bo(vma) || xe_vma_bo(vma)->vm) { if (xe_vma_has_no_bo(vma) || xe_vma_bo(vma)->vm) {
...@@ -1445,7 +1455,7 @@ void xe_vm_close_and_put(struct xe_vm *vm) ...@@ -1445,7 +1455,7 @@ void xe_vm_close_and_put(struct xe_vm *vm)
continue; continue;
} }
rb_add(&vma->vm_node, &contested, xe_vma_less_cb); list_add_tail(&vma->unbind_link, &contested);
} }
/* /*
...@@ -1465,23 +1475,22 @@ void xe_vm_close_and_put(struct xe_vm *vm) ...@@ -1465,23 +1475,22 @@ void xe_vm_close_and_put(struct xe_vm *vm)
xe_pt_destroy(vm->scratch_pt[id][i], vm->flags, xe_pt_destroy(vm->scratch_pt[id][i], vm->flags,
NULL); NULL);
} }
if (vm->pt_root[id]) {
xe_pt_destroy(vm->pt_root[id], vm->flags, NULL);
vm->pt_root[id] = NULL;
}
} }
xe_vm_unlock(vm, &ww); xe_vm_unlock(vm, &ww);
if (contested.rb_node) {
/* /*
* VM is now dead, cannot re-add nodes to vm->vmas if it's NULL * VM is now dead, cannot re-add nodes to vm->vmas if it's NULL
* Since we hold a refcount to the bo, we can remove and free * Since we hold a refcount to the bo, we can remove and free
* the members safely without locking. * the members safely without locking.
*/ */
while (contested.rb_node) { list_for_each_entry_safe(vma, next_vma, &contested, unbind_link) {
struct xe_vma *vma = to_xe_vma(contested.rb_node); list_del_init(&vma->unbind_link);
rb_erase(&vma->vm_node, &contested);
xe_vma_destroy_unlocked(vma); xe_vma_destroy_unlocked(vma);
} }
}
if (vm->async_ops.error_capture.addr) if (vm->async_ops.error_capture.addr)
wake_up_all(&vm->async_ops.error_capture.wq); wake_up_all(&vm->async_ops.error_capture.wq);
...@@ -1503,7 +1512,6 @@ static void vm_destroy_work_func(struct work_struct *w) ...@@ -1503,7 +1512,6 @@ static void vm_destroy_work_func(struct work_struct *w)
{ {
struct xe_vm *vm = struct xe_vm *vm =
container_of(w, struct xe_vm, destroy_work); container_of(w, struct xe_vm, destroy_work);
struct ww_acquire_ctx ww;
struct xe_device *xe = vm->xe; struct xe_device *xe = vm->xe;
struct xe_tile *tile; struct xe_tile *tile;
u8 id; u8 id;
...@@ -1524,29 +1532,17 @@ static void vm_destroy_work_func(struct work_struct *w) ...@@ -1524,29 +1532,17 @@ static void vm_destroy_work_func(struct work_struct *w)
} }
} }
/* for_each_tile(tile, xe, id)
* XXX: We delay destroying the PT root until the VM if freed as PT root XE_WARN_ON(vm->pt_root[id]);
* is needed for xe_vm_lock to work. If we remove that dependency this
* can be moved to xe_vm_close_and_put.
*/
xe_vm_lock(vm, &ww, 0, false);
for_each_tile(tile, xe, id) {
if (vm->pt_root[id]) {
xe_pt_destroy(vm->pt_root[id], vm->flags, NULL);
vm->pt_root[id] = NULL;
}
}
xe_vm_unlock(vm, &ww);
trace_xe_vm_free(vm); trace_xe_vm_free(vm);
dma_fence_put(vm->rebind_fence); dma_fence_put(vm->rebind_fence);
dma_resv_fini(&vm->resv);
kfree(vm); kfree(vm);
} }
void xe_vm_free(struct kref *ref) static void xe_vm_free(struct drm_gpuvm *gpuvm)
{ {
struct xe_vm *vm = container_of(ref, struct xe_vm, refcount); struct xe_vm *vm = container_of(gpuvm, struct xe_vm, gpuvm);
/* To destroy the VM we need to be able to sleep */ /* To destroy the VM we need to be able to sleep */
queue_work(system_unbound_wq, &vm->destroy_work); queue_work(system_unbound_wq, &vm->destroy_work);
...@@ -1573,7 +1569,8 @@ u64 xe_vm_pdp4_descriptor(struct xe_vm *vm, struct xe_tile *tile) ...@@ -1573,7 +1569,8 @@ u64 xe_vm_pdp4_descriptor(struct xe_vm *vm, struct xe_tile *tile)
static struct dma_fence * static struct dma_fence *
xe_vm_unbind_vma(struct xe_vma *vma, struct xe_engine *e, xe_vm_unbind_vma(struct xe_vma *vma, struct xe_engine *e,
struct xe_sync_entry *syncs, u32 num_syncs) struct xe_sync_entry *syncs, u32 num_syncs,
bool first_op, bool last_op)
{ {
struct xe_tile *tile; struct xe_tile *tile;
struct dma_fence *fence = NULL; struct dma_fence *fence = NULL;
...@@ -1598,7 +1595,8 @@ xe_vm_unbind_vma(struct xe_vma *vma, struct xe_engine *e, ...@@ -1598,7 +1595,8 @@ xe_vm_unbind_vma(struct xe_vma *vma, struct xe_engine *e,
if (!(vma->tile_present & BIT(id))) if (!(vma->tile_present & BIT(id)))
goto next; goto next;
fence = __xe_pt_unbind_vma(tile, vma, e, syncs, num_syncs); fence = __xe_pt_unbind_vma(tile, vma, e, first_op ? syncs : NULL,
first_op ? num_syncs : 0);
if (IS_ERR(fence)) { if (IS_ERR(fence)) {
err = PTR_ERR(fence); err = PTR_ERR(fence);
goto err_fences; goto err_fences;
...@@ -1624,8 +1622,11 @@ xe_vm_unbind_vma(struct xe_vma *vma, struct xe_engine *e, ...@@ -1624,8 +1622,11 @@ xe_vm_unbind_vma(struct xe_vma *vma, struct xe_engine *e,
} }
} }
if (last_op) {
for (i = 0; i < num_syncs; i++) for (i = 0; i < num_syncs; i++)
xe_sync_entry_signal(&syncs[i], NULL, cf ? &cf->base : fence); xe_sync_entry_signal(&syncs[i], NULL,
cf ? &cf->base : fence);
}
return cf ? &cf->base : !fence ? dma_fence_get_stub() : fence; return cf ? &cf->base : !fence ? dma_fence_get_stub() : fence;
...@@ -1643,7 +1644,8 @@ xe_vm_unbind_vma(struct xe_vma *vma, struct xe_engine *e, ...@@ -1643,7 +1644,8 @@ xe_vm_unbind_vma(struct xe_vma *vma, struct xe_engine *e,
static struct dma_fence * static struct dma_fence *
xe_vm_bind_vma(struct xe_vma *vma, struct xe_engine *e, xe_vm_bind_vma(struct xe_vma *vma, struct xe_engine *e,
struct xe_sync_entry *syncs, u32 num_syncs) struct xe_sync_entry *syncs, u32 num_syncs,
bool first_op, bool last_op)
{ {
struct xe_tile *tile; struct xe_tile *tile;
struct dma_fence *fence; struct dma_fence *fence;
...@@ -1668,7 +1670,8 @@ xe_vm_bind_vma(struct xe_vma *vma, struct xe_engine *e, ...@@ -1668,7 +1670,8 @@ xe_vm_bind_vma(struct xe_vma *vma, struct xe_engine *e,
if (!(vma->tile_mask & BIT(id))) if (!(vma->tile_mask & BIT(id)))
goto next; goto next;
fence = __xe_pt_bind_vma(tile, vma, e, syncs, num_syncs, fence = __xe_pt_bind_vma(tile, vma, e, first_op ? syncs : NULL,
first_op ? num_syncs : 0,
vma->tile_present & BIT(id)); vma->tile_present & BIT(id));
if (IS_ERR(fence)) { if (IS_ERR(fence)) {
err = PTR_ERR(fence); err = PTR_ERR(fence);
...@@ -1695,8 +1698,11 @@ xe_vm_bind_vma(struct xe_vma *vma, struct xe_engine *e, ...@@ -1695,8 +1698,11 @@ xe_vm_bind_vma(struct xe_vma *vma, struct xe_engine *e,
} }
} }
if (last_op) {
for (i = 0; i < num_syncs; i++) for (i = 0; i < num_syncs; i++)
xe_sync_entry_signal(&syncs[i], NULL, cf ? &cf->base : fence); xe_sync_entry_signal(&syncs[i], NULL,
cf ? &cf->base : fence);
}
return cf ? &cf->base : fence; return cf ? &cf->base : fence;
...@@ -1794,15 +1800,29 @@ int xe_vm_async_fence_wait_start(struct dma_fence *fence) ...@@ -1794,15 +1800,29 @@ int xe_vm_async_fence_wait_start(struct dma_fence *fence)
static int __xe_vm_bind(struct xe_vm *vm, struct xe_vma *vma, static int __xe_vm_bind(struct xe_vm *vm, struct xe_vma *vma,
struct xe_engine *e, struct xe_sync_entry *syncs, struct xe_engine *e, struct xe_sync_entry *syncs,
u32 num_syncs, struct async_op_fence *afence) u32 num_syncs, struct async_op_fence *afence,
bool immediate, bool first_op, bool last_op)
{ {
struct dma_fence *fence; struct dma_fence *fence;
xe_vm_assert_held(vm); xe_vm_assert_held(vm);
fence = xe_vm_bind_vma(vma, e, syncs, num_syncs); if (immediate) {
fence = xe_vm_bind_vma(vma, e, syncs, num_syncs, first_op,
last_op);
if (IS_ERR(fence)) if (IS_ERR(fence))
return PTR_ERR(fence); return PTR_ERR(fence);
} else {
int i;
XE_BUG_ON(!xe_vm_in_fault_mode(vm));
fence = dma_fence_get_stub();
if (last_op) {
for (i = 0; i < num_syncs; i++)
xe_sync_entry_signal(&syncs[i], NULL, fence);
}
}
if (afence) if (afence)
add_async_op_fence_cb(vm, fence, afence); add_async_op_fence_cb(vm, fence, afence);
...@@ -1812,32 +1832,35 @@ static int __xe_vm_bind(struct xe_vm *vm, struct xe_vma *vma, ...@@ -1812,32 +1832,35 @@ static int __xe_vm_bind(struct xe_vm *vm, struct xe_vma *vma,
static int xe_vm_bind(struct xe_vm *vm, struct xe_vma *vma, struct xe_engine *e, static int xe_vm_bind(struct xe_vm *vm, struct xe_vma *vma, struct xe_engine *e,
struct xe_bo *bo, struct xe_sync_entry *syncs, struct xe_bo *bo, struct xe_sync_entry *syncs,
u32 num_syncs, struct async_op_fence *afence) u32 num_syncs, struct async_op_fence *afence,
bool immediate, bool first_op, bool last_op)
{ {
int err; int err;
xe_vm_assert_held(vm); xe_vm_assert_held(vm);
xe_bo_assert_held(bo); xe_bo_assert_held(bo);
if (bo) { if (bo && immediate) {
err = xe_bo_validate(bo, vm, true); err = xe_bo_validate(bo, vm, true);
if (err) if (err)
return err; return err;
} }
return __xe_vm_bind(vm, vma, e, syncs, num_syncs, afence); return __xe_vm_bind(vm, vma, e, syncs, num_syncs, afence, immediate,
first_op, last_op);
} }
static int xe_vm_unbind(struct xe_vm *vm, struct xe_vma *vma, static int xe_vm_unbind(struct xe_vm *vm, struct xe_vma *vma,
struct xe_engine *e, struct xe_sync_entry *syncs, struct xe_engine *e, struct xe_sync_entry *syncs,
u32 num_syncs, struct async_op_fence *afence) u32 num_syncs, struct async_op_fence *afence,
bool first_op, bool last_op)
{ {
struct dma_fence *fence; struct dma_fence *fence;
xe_vm_assert_held(vm); xe_vm_assert_held(vm);
xe_bo_assert_held(xe_vma_bo(vma)); xe_bo_assert_held(xe_vma_bo(vma));
fence = xe_vm_unbind_vma(vma, e, syncs, num_syncs); fence = xe_vm_unbind_vma(vma, e, syncs, num_syncs, first_op, last_op);
if (IS_ERR(fence)) if (IS_ERR(fence))
return PTR_ERR(fence); return PTR_ERR(fence);
if (afence) if (afence)
...@@ -2066,7 +2089,8 @@ static const u32 region_to_mem_type[] = { ...@@ -2066,7 +2089,8 @@ static const u32 region_to_mem_type[] = {
static int xe_vm_prefetch(struct xe_vm *vm, struct xe_vma *vma, static int xe_vm_prefetch(struct xe_vm *vm, struct xe_vma *vma,
struct xe_engine *e, u32 region, struct xe_engine *e, u32 region,
struct xe_sync_entry *syncs, u32 num_syncs, struct xe_sync_entry *syncs, u32 num_syncs,
struct async_op_fence *afence) struct async_op_fence *afence, bool first_op,
bool last_op)
{ {
int err; int err;
...@@ -2080,14 +2104,16 @@ static int xe_vm_prefetch(struct xe_vm *vm, struct xe_vma *vma, ...@@ -2080,14 +2104,16 @@ static int xe_vm_prefetch(struct xe_vm *vm, struct xe_vma *vma,
if (vma->tile_mask != (vma->tile_present & ~vma->usm.tile_invalidated)) { if (vma->tile_mask != (vma->tile_present & ~vma->usm.tile_invalidated)) {
return xe_vm_bind(vm, vma, e, xe_vma_bo(vma), syncs, num_syncs, return xe_vm_bind(vm, vma, e, xe_vma_bo(vma), syncs, num_syncs,
afence); afence, true, first_op, last_op);
} else { } else {
int i; int i;
/* Nothing to do, signal fences now */ /* Nothing to do, signal fences now */
if (last_op) {
for (i = 0; i < num_syncs; i++) for (i = 0; i < num_syncs; i++)
xe_sync_entry_signal(&syncs[i], NULL, xe_sync_entry_signal(&syncs[i], NULL,
dma_fence_get_stub()); dma_fence_get_stub());
}
if (afence) if (afence)
dma_fence_signal(&afence->fence); dma_fence_signal(&afence->fence);
return 0; return 0;
...@@ -2096,29 +2122,6 @@ static int xe_vm_prefetch(struct xe_vm *vm, struct xe_vma *vma, ...@@ -2096,29 +2122,6 @@ static int xe_vm_prefetch(struct xe_vm *vm, struct xe_vma *vma,
#define VM_BIND_OP(op) (op & 0xffff) #define VM_BIND_OP(op) (op & 0xffff)
static int __vm_bind_ioctl(struct xe_vm *vm, struct xe_vma *vma,
struct xe_engine *e, struct xe_bo *bo, u32 op,
u32 region, struct xe_sync_entry *syncs,
u32 num_syncs, struct async_op_fence *afence)
{
switch (VM_BIND_OP(op)) {
case XE_VM_BIND_OP_MAP:
return xe_vm_bind(vm, vma, e, bo, syncs, num_syncs, afence);
case XE_VM_BIND_OP_UNMAP:
case XE_VM_BIND_OP_UNMAP_ALL:
return xe_vm_unbind(vm, vma, e, syncs, num_syncs, afence);
case XE_VM_BIND_OP_MAP_USERPTR:
return xe_vm_bind(vm, vma, e, NULL, syncs, num_syncs, afence);
case XE_VM_BIND_OP_PREFETCH:
return xe_vm_prefetch(vm, vma, e, region, syncs, num_syncs,
afence);
break;
default:
XE_BUG_ON("NOT POSSIBLE");
return -EINVAL;
}
}
struct ttm_buffer_object *xe_vm_ttm_bo(struct xe_vm *vm) struct ttm_buffer_object *xe_vm_ttm_bo(struct xe_vm *vm)
{ {
int idx = vm->flags & XE_VM_FLAG_MIGRATION ? int idx = vm->flags & XE_VM_FLAG_MIGRATION ?
...@@ -2134,810 +2137,847 @@ static void xe_vm_tv_populate(struct xe_vm *vm, struct ttm_validate_buffer *tv) ...@@ -2134,810 +2137,847 @@ static void xe_vm_tv_populate(struct xe_vm *vm, struct ttm_validate_buffer *tv)
tv->bo = xe_vm_ttm_bo(vm); tv->bo = xe_vm_ttm_bo(vm);
} }
static bool is_map_op(u32 op) static void vm_set_async_error(struct xe_vm *vm, int err)
{
return VM_BIND_OP(op) == XE_VM_BIND_OP_MAP ||
VM_BIND_OP(op) == XE_VM_BIND_OP_MAP_USERPTR;
}
static bool is_unmap_op(u32 op)
{ {
return VM_BIND_OP(op) == XE_VM_BIND_OP_UNMAP || lockdep_assert_held(&vm->lock);
VM_BIND_OP(op) == XE_VM_BIND_OP_UNMAP_ALL; vm->async_ops.error = err;
} }
static int vm_bind_ioctl(struct xe_vm *vm, struct xe_vma *vma, static int vm_bind_ioctl_lookup_vma(struct xe_vm *vm, struct xe_bo *bo,
struct xe_engine *e, struct xe_bo *bo, u64 addr, u64 range, u32 op)
struct drm_xe_vm_bind_op *bind_op,
struct xe_sync_entry *syncs, u32 num_syncs,
struct async_op_fence *afence)
{ {
LIST_HEAD(objs); struct xe_device *xe = vm->xe;
LIST_HEAD(dups); struct xe_vma *vma;
struct ttm_validate_buffer tv_bo, tv_vm; bool async = !!(op & XE_VM_BIND_FLAG_ASYNC);
struct ww_acquire_ctx ww;
struct xe_bo *vbo;
int err, i;
lockdep_assert_held(&vm->lock); lockdep_assert_held(&vm->lock);
XE_BUG_ON(!list_empty(&vma->unbind_link));
/* Binds deferred to faults, signal fences now */
if (xe_vm_in_fault_mode(vm) && is_map_op(bind_op->op) &&
!(bind_op->op & XE_VM_BIND_FLAG_IMMEDIATE)) {
for (i = 0; i < num_syncs; i++)
xe_sync_entry_signal(&syncs[i], NULL,
dma_fence_get_stub());
if (afence)
dma_fence_signal(&afence->fence);
return 0;
}
xe_vm_tv_populate(vm, &tv_vm);
list_add_tail(&tv_vm.head, &objs);
vbo = xe_vma_bo(vma);
if (vbo) {
/*
* An unbind can drop the last reference to the BO and
* the BO is needed for ttm_eu_backoff_reservation so
* take a reference here.
*/
xe_bo_get(vbo);
if (!vbo->vm) {
tv_bo.bo = &vbo->ttm;
tv_bo.num_shared = 1;
list_add(&tv_bo.head, &objs);
}
}
again: switch (VM_BIND_OP(op)) {
err = ttm_eu_reserve_buffers(&ww, &objs, true, &dups); case XE_VM_BIND_OP_MAP:
if (!err) { case XE_VM_BIND_OP_MAP_USERPTR:
err = __vm_bind_ioctl(vm, vma, e, bo, vma = xe_vm_find_overlapping_vma(vm, addr, range);
bind_op->op, bind_op->region, syncs, if (XE_IOCTL_ERR(xe, vma && !async))
num_syncs, afence); return -EBUSY;
ttm_eu_backoff_reservation(&ww, &objs); break;
if (err == -EAGAIN && xe_vma_is_userptr(vma)) { case XE_VM_BIND_OP_UNMAP:
lockdep_assert_held_write(&vm->lock); case XE_VM_BIND_OP_PREFETCH:
err = xe_vma_userptr_pin_pages(vma); vma = xe_vm_find_overlapping_vma(vm, addr, range);
if (!err) if (XE_IOCTL_ERR(xe, !vma))
goto again; return -ENODATA; /* Not an actual error, IOCTL
} cleans up returns and 0 */
if (XE_IOCTL_ERR(xe, (xe_vma_start(vma) != addr ||
xe_vma_end(vma) != addr + range) && !async))
return -EINVAL;
break;
case XE_VM_BIND_OP_UNMAP_ALL:
if (XE_IOCTL_ERR(xe, list_empty(&bo->ttm.base.gpuva.list)))
return -ENODATA; /* Not an actual error, IOCTL
cleans up returns and 0 */
break;
default:
XE_BUG_ON("NOT POSSIBLE");
return -EINVAL;
} }
xe_bo_put(vbo);
return err; return 0;
} }
struct async_op { static void prep_vma_destroy(struct xe_vm *vm, struct xe_vma *vma,
struct xe_vma *vma; bool post_commit)
struct xe_engine *engine;
struct xe_bo *bo;
struct drm_xe_vm_bind_op bind_op;
struct xe_sync_entry *syncs;
u32 num_syncs;
struct list_head link;
struct async_op_fence *fence;
};
static void async_op_cleanup(struct xe_vm *vm, struct async_op *op)
{ {
while (op->num_syncs--) down_read(&vm->userptr.notifier_lock);
xe_sync_entry_cleanup(&op->syncs[op->num_syncs]); vma->gpuva.flags |= XE_VMA_DESTROYED;
kfree(op->syncs); up_read(&vm->userptr.notifier_lock);
xe_bo_put(op->bo); if (post_commit)
if (op->engine) xe_vm_remove_vma(vm, vma);
xe_engine_put(op->engine);
xe_vm_put(vm);
if (op->fence)
dma_fence_put(&op->fence->fence);
kfree(op);
} }
static struct async_op *next_async_op(struct xe_vm *vm) #undef ULL
#define ULL unsigned long long
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM)
static void print_op(struct xe_device *xe, struct drm_gpuva_op *op)
{ {
return list_first_entry_or_null(&vm->async_ops.pending, struct xe_vma *vma;
struct async_op, link);
}
static void vm_set_async_error(struct xe_vm *vm, int err) switch (op->op) {
case DRM_GPUVA_OP_MAP:
vm_dbg(&xe->drm, "MAP: addr=0x%016llx, range=0x%016llx",
(ULL)op->map.va.addr, (ULL)op->map.va.range);
break;
case DRM_GPUVA_OP_REMAP:
vma = gpuva_to_vma(op->remap.unmap->va);
vm_dbg(&xe->drm, "REMAP:UNMAP: addr=0x%016llx, range=0x%016llx, keep=%d",
(ULL)xe_vma_start(vma), (ULL)xe_vma_size(vma),
op->unmap.keep ? 1 : 0);
if (op->remap.prev)
vm_dbg(&xe->drm,
"REMAP:PREV: addr=0x%016llx, range=0x%016llx",
(ULL)op->remap.prev->va.addr,
(ULL)op->remap.prev->va.range);
if (op->remap.next)
vm_dbg(&xe->drm,
"REMAP:NEXT: addr=0x%016llx, range=0x%016llx",
(ULL)op->remap.next->va.addr,
(ULL)op->remap.next->va.range);
break;
case DRM_GPUVA_OP_UNMAP:
vma = gpuva_to_vma(op->unmap.va);
vm_dbg(&xe->drm, "UNMAP: addr=0x%016llx, range=0x%016llx, keep=%d",
(ULL)xe_vma_start(vma), (ULL)xe_vma_size(vma),
op->unmap.keep ? 1 : 0);
break;
default:
XE_BUG_ON("NOT POSSIBLE");
}
}
#else
static void print_op(struct xe_device *xe, struct drm_gpuva_op *op)
{ {
lockdep_assert_held(&vm->lock);
vm->async_ops.error = err;
} }
#endif
static void async_op_work_func(struct work_struct *w) /*
* Create operations list from IOCTL arguments, setup operations fields so parse
* and commit steps are decoupled from IOCTL arguments. This step can fail.
*/
static struct drm_gpuva_ops *
vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_bo *bo,
u64 bo_offset_or_userptr, u64 addr, u64 range,
u32 operation, u64 tile_mask, u32 region)
{ {
struct xe_vm *vm = container_of(w, struct xe_vm, async_ops.work); struct drm_gem_object *obj = bo ? &bo->ttm.base : NULL;
struct ww_acquire_ctx ww;
for (;;) { struct drm_gpuva_ops *ops;
struct async_op *op; struct drm_gpuva_op *__op;
struct xe_vma_op *op;
struct drm_gpuvm_bo *vm_bo;
int err; int err;
if (vm->async_ops.error && !xe_vm_is_closed(vm)) lockdep_assert_held_write(&vm->lock);
break;
spin_lock_irq(&vm->async_ops.lock); vm_dbg(&vm->xe->drm,
op = next_async_op(vm); "op=%d, addr=0x%016llx, range=0x%016llx, bo_offset_or_userptr=0x%016llx",
if (op) VM_BIND_OP(operation), (ULL)addr, (ULL)range,
list_del_init(&op->link); (ULL)bo_offset_or_userptr);
spin_unlock_irq(&vm->async_ops.lock);
if (!op) switch (VM_BIND_OP(operation)) {
break; case XE_VM_BIND_OP_MAP:
case XE_VM_BIND_OP_MAP_USERPTR:
ops = drm_gpuvm_sm_map_ops_create(&vm->gpuvm, addr, range,
obj, bo_offset_or_userptr);
if (IS_ERR(ops))
return ops;
if (!xe_vm_is_closed(vm)) { drm_gpuva_for_each_op(__op, ops) {
bool first, last; struct xe_vma_op *op = gpuva_op_to_vma_op(__op);
down_write(&vm->lock); op->tile_mask = tile_mask;
again: op->map.immediate =
first = op->vma->first_munmap_rebind; operation & XE_VM_BIND_FLAG_IMMEDIATE;
last = op->vma->last_munmap_rebind; op->map.read_only =
#ifdef TEST_VM_ASYNC_OPS_ERROR operation & XE_VM_BIND_FLAG_READONLY;
#define FORCE_ASYNC_OP_ERROR BIT(31) op->map.is_null = operation & XE_VM_BIND_FLAG_NULL;
if (!(op->bind_op.op & FORCE_ASYNC_OP_ERROR)) {
err = vm_bind_ioctl(vm, op->vma, op->engine,
op->bo, &op->bind_op,
op->syncs, op->num_syncs,
op->fence);
} else {
err = -ENOMEM;
op->bind_op.op &= ~FORCE_ASYNC_OP_ERROR;
} }
#else break;
err = vm_bind_ioctl(vm, op->vma, op->engine, op->bo, case XE_VM_BIND_OP_UNMAP:
&op->bind_op, op->syncs, ops = drm_gpuvm_sm_unmap_ops_create(&vm->gpuvm, addr, range);
op->num_syncs, op->fence); if (IS_ERR(ops))
#endif return ops;
/*
* In order for the fencing to work (stall behind
* existing jobs / prevent new jobs from running) all
* the dma-resv slots need to be programmed in a batch
* relative to execs / the rebind worker. The vm->lock
* ensure this.
*/
if (!err && ((first && VM_BIND_OP(op->bind_op.op) ==
XE_VM_BIND_OP_UNMAP) ||
vm->async_ops.munmap_rebind_inflight)) {
if (last) {
op->vma->last_munmap_rebind = false;
vm->async_ops.munmap_rebind_inflight =
false;
} else {
vm->async_ops.munmap_rebind_inflight =
true;
async_op_cleanup(vm, op);
spin_lock_irq(&vm->async_ops.lock); drm_gpuva_for_each_op(__op, ops) {
op = next_async_op(vm); struct xe_vma_op *op = gpuva_op_to_vma_op(__op);
XE_BUG_ON(!op);
list_del_init(&op->link);
spin_unlock_irq(&vm->async_ops.lock);
goto again; op->tile_mask = tile_mask;
}
} }
if (err) { break;
trace_xe_vma_fail(op->vma); case XE_VM_BIND_OP_PREFETCH:
drm_warn(&vm->xe->drm, "Async VM op(%d) failed with %d", ops = drm_gpuvm_prefetch_ops_create(&vm->gpuvm, addr, range);
VM_BIND_OP(op->bind_op.op), if (IS_ERR(ops))
err); return ops;
spin_lock_irq(&vm->async_ops.lock); drm_gpuva_for_each_op(__op, ops) {
list_add(&op->link, &vm->async_ops.pending); struct xe_vma_op *op = gpuva_op_to_vma_op(__op);
spin_unlock_irq(&vm->async_ops.lock);
vm_set_async_error(vm, err); op->tile_mask = tile_mask;
up_write(&vm->lock); op->prefetch.region = region;
}
break;
case XE_VM_BIND_OP_UNMAP_ALL:
XE_BUG_ON(!bo);
if (vm->async_ops.error_capture.addr) err = xe_bo_lock(bo, &ww, 0, true);
vm_error_capture(vm, err, if (err)
op->bind_op.op, return ERR_PTR(err);
op->bind_op.addr,
op->bind_op.range); vm_bo = drm_gpuvm_bo_find(&vm->gpuvm, obj);
if (!vm_bo)
break; break;
ops = drm_gpuvm_bo_unmap_ops_create(vm_bo);
drm_gpuvm_bo_put(vm_bo);
xe_bo_unlock(bo, &ww);
if (IS_ERR(ops))
return ops;
drm_gpuva_for_each_op(__op, ops) {
struct xe_vma_op *op = gpuva_op_to_vma_op(__op);
op->tile_mask = tile_mask;
}
break;
default:
XE_BUG_ON("NOT POSSIBLE");
ops = ERR_PTR(-EINVAL);
} }
up_write(&vm->lock);
} else {
trace_xe_vma_flush(op->vma);
if (is_unmap_op(op->bind_op.op)) { #ifdef TEST_VM_ASYNC_OPS_ERROR
down_write(&vm->lock); if (operation & FORCE_ASYNC_OP_ERROR) {
xe_vma_destroy_unlocked(op->vma); op = list_first_entry_or_null(&ops->list, struct xe_vma_op,
up_write(&vm->lock); base.entry);
if (op)
op->inject_error = true;
} }
#endif
if (op->fence && !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, if (!IS_ERR(ops))
&op->fence->fence.flags)) { drm_gpuva_for_each_op(__op, ops)
if (!xe_vm_no_dma_fences(vm)) { print_op(vm->xe, __op);
op->fence->started = true;
smp_wmb(); return ops;
wake_up_all(&op->fence->wq); }
static struct xe_vma *new_vma(struct xe_vm *vm, struct drm_gpuva_op_map *op,
u64 tile_mask, bool read_only, bool is_null)
{
struct xe_bo *bo = op->gem.obj ? gem_to_xe_bo(op->gem.obj) : NULL;
struct xe_vma *vma;
struct ww_acquire_ctx ww;
int err;
lockdep_assert_held_write(&vm->lock);
if (bo) {
err = xe_bo_lock(bo, &ww, 0, true);
if (err)
return ERR_PTR(err);
} }
dma_fence_signal(&op->fence->fence); vma = xe_vma_create(vm, bo, op->gem.offset,
op->va.addr, op->va.addr +
op->va.range - 1, read_only, is_null,
tile_mask);
if (bo)
xe_bo_unlock(bo, &ww);
if (xe_vma_is_userptr(vma)) {
err = xe_vma_userptr_pin_pages(vma);
if (err) {
prep_vma_destroy(vm, vma, false);
xe_vma_destroy_unlocked(vma);
return ERR_PTR(err);
}
} else if (!xe_vma_has_no_bo(vma) && !bo->vm) {
vm_insert_extobj(vm, vma);
err = add_preempt_fences(vm, bo);
if (err) {
prep_vma_destroy(vm, vma, false);
xe_vma_destroy_unlocked(vma);
return ERR_PTR(err);
} }
} }
async_op_cleanup(vm, op); return vma;
}
} }
static int __vm_bind_ioctl_async(struct xe_vm *vm, struct xe_vma *vma, /*
struct xe_engine *e, struct xe_bo *bo, * Parse operations list and create any resources needed for the operations
struct drm_xe_vm_bind_op *bind_op, * prior to fully committing to the operations. This setup can fail.
struct xe_sync_entry *syncs, u32 num_syncs) */
static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct xe_engine *e,
struct drm_gpuva_ops **ops, int num_ops_list,
struct xe_sync_entry *syncs, u32 num_syncs,
struct list_head *ops_list, bool async)
{ {
struct async_op *op; struct xe_vma_op *last_op = NULL;
bool installed = false; struct list_head *async_list = NULL;
u64 seqno; struct async_op_fence *fence = NULL;
int i; int err, i;
lockdep_assert_held(&vm->lock); lockdep_assert_held_write(&vm->lock);
XE_BUG_ON(num_ops_list > 1 && !async);
op = kmalloc(sizeof(*op), GFP_KERNEL); if (num_syncs && async) {
if (!op) { u64 seqno;
return -ENOMEM;
}
if (num_syncs) { fence = kmalloc(sizeof(*fence), GFP_KERNEL);
op->fence = kmalloc(sizeof(*op->fence), GFP_KERNEL); if (!fence)
if (!op->fence) {
kfree(op);
return -ENOMEM; return -ENOMEM;
}
seqno = e ? ++e->bind.fence_seqno : ++vm->async_ops.fence.seqno; seqno = e ? ++e->bind.fence_seqno : ++vm->async_ops.fence.seqno;
dma_fence_init(&op->fence->fence, &async_op_fence_ops, dma_fence_init(&fence->fence, &async_op_fence_ops,
&vm->async_ops.lock, e ? e->bind.fence_ctx : &vm->async_ops.lock, e ? e->bind.fence_ctx :
vm->async_ops.fence.context, seqno); vm->async_ops.fence.context, seqno);
if (!xe_vm_no_dma_fences(vm)) { if (!xe_vm_no_dma_fences(vm)) {
op->fence->vm = vm; fence->vm = vm;
op->fence->started = false; fence->started = false;
init_waitqueue_head(&op->fence->wq); init_waitqueue_head(&fence->wq);
} }
} else {
op->fence = NULL;
} }
op->vma = vma;
op->engine = e;
op->bo = bo;
op->bind_op = *bind_op;
op->syncs = syncs;
op->num_syncs = num_syncs;
INIT_LIST_HEAD(&op->link);
for (i = 0; i < num_syncs; i++) for (i = 0; i < num_ops_list; ++i) {
installed |= xe_sync_entry_signal(&syncs[i], NULL, struct drm_gpuva_ops *__ops = ops[i];
&op->fence->fence); struct drm_gpuva_op *__op;
if (!installed && op->fence)
dma_fence_signal(&op->fence->fence);
spin_lock_irq(&vm->async_ops.lock); drm_gpuva_for_each_op(__op, __ops) {
list_add_tail(&op->link, &vm->async_ops.pending); struct xe_vma_op *op = gpuva_op_to_vma_op(__op);
spin_unlock_irq(&vm->async_ops.lock); bool first = !async_list;
if (!vm->async_ops.error) XE_BUG_ON(!first && !async);
queue_work(system_unbound_wq, &vm->async_ops.work);
return 0; INIT_LIST_HEAD(&op->link);
} if (first)
async_list = ops_list;
static int vm_bind_ioctl_async(struct xe_vm *vm, struct xe_vma *vma, list_add_tail(&op->link, async_list);
struct xe_engine *e, struct xe_bo *bo,
struct drm_xe_vm_bind_op *bind_op,
struct xe_sync_entry *syncs, u32 num_syncs)
{
struct xe_vma *__vma, *next;
struct list_head rebind_list;
struct xe_sync_entry *in_syncs = NULL, *out_syncs = NULL;
u32 num_in_syncs = 0, num_out_syncs = 0;
bool first = true, last;
int err;
int i;
lockdep_assert_held(&vm->lock);
/* Not a linked list of unbinds + rebinds, easy */ if (first) {
if (list_empty(&vma->unbind_link)) op->flags |= XE_VMA_OP_FIRST;
return __vm_bind_ioctl_async(vm, vma, e, bo, bind_op, op->num_syncs = num_syncs;
syncs, num_syncs); op->syncs = syncs;
}
/* op->engine = e;
* Linked list of unbinds + rebinds, decompose syncs into 'in / out'
* passing the 'in' to the first operation and 'out' to the last. Also
* the reference counting is a little tricky, increment the VM / bind
* engine ref count on all but the last operation and increment the BOs
* ref count on each rebind.
*/
XE_BUG_ON(VM_BIND_OP(bind_op->op) != XE_VM_BIND_OP_UNMAP && switch (op->base.op) {
VM_BIND_OP(bind_op->op) != XE_VM_BIND_OP_UNMAP_ALL && case DRM_GPUVA_OP_MAP:
VM_BIND_OP(bind_op->op) != XE_VM_BIND_OP_PREFETCH); {
struct xe_vma *vma;
/* Decompose syncs */ vma = new_vma(vm, &op->base.map,
if (num_syncs) { op->tile_mask, op->map.read_only,
in_syncs = kmalloc(sizeof(*in_syncs) * num_syncs, GFP_KERNEL); op->map.is_null);
out_syncs = kmalloc(sizeof(*out_syncs) * num_syncs, GFP_KERNEL); if (IS_ERR(vma)) {
if (!in_syncs || !out_syncs) { err = PTR_ERR(vma);
err = -ENOMEM; goto free_fence;
goto out_error;
} }
for (i = 0; i < num_syncs; ++i) { op->map.vma = vma;
bool signal = syncs[i].flags & DRM_XE_SYNC_SIGNAL; break;
if (signal)
out_syncs[num_out_syncs++] = syncs[i];
else
in_syncs[num_in_syncs++] = syncs[i];
}
}
/* Do unbinds + move rebinds to new list */
INIT_LIST_HEAD(&rebind_list);
list_for_each_entry_safe(__vma, next, &vma->unbind_link, unbind_link) {
if (__vma->destroyed ||
VM_BIND_OP(bind_op->op) == XE_VM_BIND_OP_PREFETCH) {
list_del_init(&__vma->unbind_link);
xe_bo_get(bo);
err = __vm_bind_ioctl_async(xe_vm_get(vm), __vma,
e ? xe_engine_get(e) : NULL,
bo, bind_op, first ?
in_syncs : NULL,
first ? num_in_syncs : 0);
if (err) {
xe_bo_put(bo);
xe_vm_put(vm);
if (e)
xe_engine_put(e);
goto out_error;
}
in_syncs = NULL;
first = false;
} else {
list_move_tail(&__vma->unbind_link, &rebind_list);
}
} }
last = list_empty(&rebind_list); case DRM_GPUVA_OP_REMAP:
if (!last) { if (op->base.remap.prev) {
xe_vm_get(vm); struct xe_vma *vma;
if (e) bool read_only =
xe_engine_get(e); op->base.remap.unmap->va->flags &
} XE_VMA_READ_ONLY;
err = __vm_bind_ioctl_async(vm, vma, e, bool is_null =
bo, bind_op, op->base.remap.unmap->va->flags &
first ? in_syncs : DRM_GPUVA_SPARSE;
last ? out_syncs : NULL,
first ? num_in_syncs : vma = new_vma(vm, op->base.remap.prev,
last ? num_out_syncs : 0); op->tile_mask, read_only,
if (err) { is_null);
if (!last) { if (IS_ERR(vma)) {
xe_vm_put(vm); err = PTR_ERR(vma);
if (e) goto free_fence;
xe_engine_put(e);
} }
goto out_error;
op->remap.prev = vma;
} }
in_syncs = NULL;
/* Do rebinds */ if (op->base.remap.next) {
list_for_each_entry_safe(__vma, next, &rebind_list, unbind_link) { struct xe_vma *vma;
list_del_init(&__vma->unbind_link); bool read_only =
last = list_empty(&rebind_list); op->base.remap.unmap->va->flags &
XE_VMA_READ_ONLY;
bool is_null =
op->base.remap.unmap->va->flags &
DRM_GPUVA_SPARSE;
if (xe_vma_is_userptr(__vma)) { vma = new_vma(vm, op->base.remap.next,
bind_op->op = XE_VM_BIND_FLAG_ASYNC | op->tile_mask, read_only,
XE_VM_BIND_OP_MAP_USERPTR; is_null);
} else { if (IS_ERR(vma)) {
bind_op->op = XE_VM_BIND_FLAG_ASYNC | err = PTR_ERR(vma);
XE_VM_BIND_OP_MAP; goto free_fence;
xe_bo_get(xe_vma_bo(__vma));
} }
if (!last) { op->remap.next = vma;
xe_vm_get(vm);
if (e)
xe_engine_get(e);
} }
err = __vm_bind_ioctl_async(vm, __vma, e, /* XXX: Support no doing remaps */
xe_vma_bo(__vma), bind_op, last ? op->remap.start =
out_syncs : NULL, xe_vma_start(gpuva_to_vma(op->base.remap.unmap->va));
last ? num_out_syncs : 0); op->remap.range =
if (err) { xe_vma_size(gpuva_to_vma(op->base.remap.unmap->va));
if (!last) { break;
xe_vm_put(vm); case DRM_GPUVA_OP_UNMAP:
if (e) op->unmap.start =
xe_engine_put(e); xe_vma_start(gpuva_to_vma(op->base.unmap.va));
op->unmap.range =
xe_vma_size(gpuva_to_vma(op->base.unmap.va));
break;
case DRM_GPUVA_OP_PREFETCH:
/* Nothing to do */
break;
default:
XE_BUG_ON("NOT POSSIBLE");
} }
goto out_error;
last_op = op;
} }
last_op->ops = __ops;
} }
kfree(syncs); if (!last_op)
return 0; return -ENODATA;
out_error: last_op->flags |= XE_VMA_OP_LAST;
kfree(in_syncs); last_op->num_syncs = num_syncs;
kfree(out_syncs); last_op->syncs = syncs;
kfree(syncs); last_op->fence = fence;
return 0;
free_fence:
kfree(fence);
return err; return err;
} }
static int __vm_bind_ioctl_lookup_vma(struct xe_vm *vm, struct xe_bo *bo, static int xe_vma_op_commit(struct xe_vm *vm, struct xe_vma_op *op)
u64 addr, u64 range, u32 op)
{ {
struct xe_device *xe = vm->xe; int err = 0;
struct xe_vma *vma, lookup;
bool async = !!(op & XE_VM_BIND_FLAG_ASYNC);
lockdep_assert_held(&vm->lock);
lookup.start = addr; lockdep_assert_held_write(&vm->lock);
lookup.end = addr + range - 1;
switch (VM_BIND_OP(op)) { switch (op->base.op) {
case XE_VM_BIND_OP_MAP: case DRM_GPUVA_OP_MAP:
case XE_VM_BIND_OP_MAP_USERPTR: err |= xe_vm_insert_vma(vm, op->map.vma);
vma = xe_vm_find_overlapping_vma(vm, &lookup);
if (XE_IOCTL_ERR(xe, vma))
return -EBUSY;
break; break;
case XE_VM_BIND_OP_UNMAP: case DRM_GPUVA_OP_REMAP:
case XE_VM_BIND_OP_PREFETCH: prep_vma_destroy(vm, gpuva_to_vma(op->base.remap.unmap->va),
vma = xe_vm_find_overlapping_vma(vm, &lookup); true);
if (XE_IOCTL_ERR(xe, !vma) || if (op->remap.prev)
XE_IOCTL_ERR(xe, (xe_vma_start(vma) != addr || err |= xe_vm_insert_vma(vm, op->remap.prev);
xe_vma_end(vma) != addr + range) && !async)) if (op->remap.next)
return -EINVAL; err |= xe_vm_insert_vma(vm, op->remap.next);
break; break;
case XE_VM_BIND_OP_UNMAP_ALL: case DRM_GPUVA_OP_UNMAP:
prep_vma_destroy(vm, gpuva_to_vma(op->base.unmap.va), true);
break;
case DRM_GPUVA_OP_PREFETCH:
/* Nothing to do */
break; break;
default: default:
XE_BUG_ON("NOT POSSIBLE"); XE_BUG_ON("NOT POSSIBLE");
return -EINVAL;
} }
return 0; op->flags |= XE_VMA_OP_COMMITTED;
} return err;
static void prep_vma_destroy(struct xe_vm *vm, struct xe_vma *vma)
{
down_read(&vm->userptr.notifier_lock);
vma->destroyed = true;
up_read(&vm->userptr.notifier_lock);
xe_vm_remove_vma(vm, vma);
} }
static int prep_replacement_vma(struct xe_vm *vm, struct xe_vma *vma) static int __xe_vma_op_execute(struct xe_vm *vm, struct xe_vma *vma,
struct xe_vma_op *op)
{ {
LIST_HEAD(objs);
LIST_HEAD(dups);
struct ttm_validate_buffer tv_bo, tv_vm;
struct ww_acquire_ctx ww;
struct xe_bo *vbo;
int err; int err;
if (xe_vma_bo(vma) && !xe_vma_bo(vma)->vm) { lockdep_assert_held_write(&vm->lock);
vm_insert_extobj(vm, vma);
err = add_preempt_fences(vm, xe_vma_bo(vma));
if (err)
return err;
}
return 0;
}
/* xe_vm_tv_populate(vm, &tv_vm);
* Find all overlapping VMAs in lookup range and add to a list in the returned list_add_tail(&tv_vm.head, &objs);
* VMA, all of VMAs found will be unbound. Also possibly add 2 new VMAs that vbo = xe_vma_bo(vma);
* need to be bound if first / last VMAs are not fully unbound. This is akin to if (vbo) {
* how munmap works. /*
* An unbind can drop the last reference to the BO and
* the BO is needed for ttm_eu_backoff_reservation so
* take a reference here.
*/ */
static struct xe_vma *vm_unbind_lookup_vmas(struct xe_vm *vm, xe_bo_get(vbo);
struct xe_vma *lookup)
{
struct xe_vma *vma = xe_vm_find_overlapping_vma(vm, lookup);
struct rb_node *node;
struct xe_vma *first = vma, *last = vma, *new_first = NULL,
*new_last = NULL, *__vma, *next;
int err = 0;
bool first_munmap_rebind = false;
lockdep_assert_held(&vm->lock); if (!vbo->vm) {
XE_BUG_ON(!vma); tv_bo.bo = &vbo->ttm;
tv_bo.num_shared = 1;
node = &vma->vm_node; list_add(&tv_bo.head, &objs);
while ((node = rb_next(node))) {
if (!xe_vma_cmp_vma_cb(lookup, node)) {
__vma = to_xe_vma(node);
list_add_tail(&__vma->unbind_link, &vma->unbind_link);
last = __vma;
} else {
break;
} }
} }
node = &vma->vm_node; again:
while ((node = rb_prev(node))) { err = ttm_eu_reserve_buffers(&ww, &objs, true, &dups);
if (!xe_vma_cmp_vma_cb(lookup, node)) { if (err) {
__vma = to_xe_vma(node); xe_bo_put(vbo);
list_add(&__vma->unbind_link, &vma->unbind_link); return err;
first = __vma;
} else {
break;
}
} }
if (xe_vma_start(first) != xe_vma_start(lookup)) { xe_vm_assert_held(vm);
struct ww_acquire_ctx ww; xe_bo_assert_held(xe_vma_bo(vma));
if (xe_vma_bo(first)) switch (op->base.op) {
err = xe_bo_lock(xe_vma_bo(first), &ww, 0, true); case DRM_GPUVA_OP_MAP:
if (err) err = xe_vm_bind(vm, vma, op->engine, xe_vma_bo(vma),
goto unwind; op->syncs, op->num_syncs, op->fence,
new_first = xe_vma_create(xe_vma_vm(first), xe_vma_bo(first), op->map.immediate || !xe_vm_in_fault_mode(vm),
xe_vma_bo(first) ? op->flags & XE_VMA_OP_FIRST,
xe_vma_bo_offset(first) : op->flags & XE_VMA_OP_LAST);
xe_vma_userptr(first), break;
xe_vma_start(first), case DRM_GPUVA_OP_REMAP:
xe_vma_start(lookup) - 1, {
xe_vma_read_only(first), bool prev = !!op->remap.prev;
(first->pte_flags & bool next = !!op->remap.next;
XE_PTE_FLAG_NULL),
first->tile_mask); if (!op->remap.unmap_done) {
if (xe_vma_bo(first)) vm->async_ops.munmap_rebind_inflight = true;
xe_bo_unlock(xe_vma_bo(first), &ww); if (prev || next)
if (!new_first) { vma->gpuva.flags |= XE_VMA_FIRST_REBIND;
err = -ENOMEM; err = xe_vm_unbind(vm, vma, op->engine, op->syncs,
goto unwind; op->num_syncs,
} !prev && !next ? op->fence : NULL,
if (xe_vma_is_userptr(first)) { op->flags & XE_VMA_OP_FIRST,
err = xe_vma_userptr_pin_pages(new_first); op->flags & XE_VMA_OP_LAST && !prev &&
if (err) !next);
goto unwind;
}
err = prep_replacement_vma(vm, new_first);
if (err) if (err)
goto unwind; break;
op->remap.unmap_done = true;
} }
if (xe_vma_end(last) != xe_vma_end(lookup)) { if (prev) {
struct ww_acquire_ctx ww; op->remap.prev->gpuva.flags |= XE_VMA_LAST_REBIND;
u64 chunk = xe_vma_end(lookup) - xe_vma_start(last); err = xe_vm_bind(vm, op->remap.prev, op->engine,
xe_vma_bo(op->remap.prev), op->syncs,
if (xe_vma_bo(last)) op->num_syncs,
err = xe_bo_lock(xe_vma_bo(last), &ww, 0, true); !next ? op->fence : NULL, true, false,
op->flags & XE_VMA_OP_LAST && !next);
op->remap.prev->gpuva.flags &= ~XE_VMA_LAST_REBIND;
if (err) if (err)
goto unwind; break;
new_last = xe_vma_create(xe_vma_vm(last), xe_vma_bo(last), op->remap.prev = NULL;
xe_vma_bo(last) ?
xe_vma_bo_offset(last) + chunk :
xe_vma_userptr(last) + chunk,
xe_vma_start(last) + chunk,
xe_vma_end(last) - 1,
xe_vma_read_only(last),
(last->pte_flags & XE_PTE_FLAG_NULL),
last->tile_mask);
if (xe_vma_bo(last))
xe_bo_unlock(xe_vma_bo(last), &ww);
if (!new_last) {
err = -ENOMEM;
goto unwind;
}
if (xe_vma_is_userptr(last)) {
err = xe_vma_userptr_pin_pages(new_last);
if (err)
goto unwind;
} }
err = prep_replacement_vma(vm, new_last);
if (next) {
op->remap.next->gpuva.flags |= XE_VMA_LAST_REBIND;
err = xe_vm_bind(vm, op->remap.next, op->engine,
xe_vma_bo(op->remap.next),
op->syncs, op->num_syncs,
op->fence, true, false,
op->flags & XE_VMA_OP_LAST);
op->remap.next->gpuva.flags &= ~XE_VMA_LAST_REBIND;
if (err) if (err)
goto unwind; break;
op->remap.next = NULL;
} }
vm->async_ops.munmap_rebind_inflight = false;
prep_vma_destroy(vm, vma); break;
if (list_empty(&vma->unbind_link) && (new_first || new_last))
vma->first_munmap_rebind = true;
list_for_each_entry(__vma, &vma->unbind_link, unbind_link) {
if ((new_first || new_last) && !first_munmap_rebind) {
__vma->first_munmap_rebind = true;
first_munmap_rebind = true;
} }
prep_vma_destroy(vm, __vma); case DRM_GPUVA_OP_UNMAP:
err = xe_vm_unbind(vm, vma, op->engine, op->syncs,
op->num_syncs, op->fence,
op->flags & XE_VMA_OP_FIRST,
op->flags & XE_VMA_OP_LAST);
break;
case DRM_GPUVA_OP_PREFETCH:
err = xe_vm_prefetch(vm, vma, op->engine, op->prefetch.region,
op->syncs, op->num_syncs, op->fence,
op->flags & XE_VMA_OP_FIRST,
op->flags & XE_VMA_OP_LAST);
break;
default:
XE_BUG_ON("NOT POSSIBLE");
} }
if (new_first) {
xe_vm_insert_vma(vm, new_first); ttm_eu_backoff_reservation(&ww, &objs);
list_add_tail(&new_first->unbind_link, &vma->unbind_link); if (err == -EAGAIN && xe_vma_is_userptr(vma)) {
if (!new_last) lockdep_assert_held_write(&vm->lock);
new_first->last_munmap_rebind = true; err = xe_vma_userptr_pin_pages(vma);
if (!err)
goto again;
} }
if (new_last) { xe_bo_put(vbo);
xe_vm_insert_vma(vm, new_last);
list_add_tail(&new_last->unbind_link, &vma->unbind_link); if (err)
new_last->last_munmap_rebind = true; trace_xe_vma_fail(vma);
return err;
}
static int xe_vma_op_execute(struct xe_vm *vm, struct xe_vma_op *op)
{
int ret = 0;
lockdep_assert_held_write(&vm->lock);
#ifdef TEST_VM_ASYNC_OPS_ERROR
if (op->inject_error) {
op->inject_error = false;
return -ENOMEM;
} }
#endif
return vma; switch (op->base.op) {
case DRM_GPUVA_OP_MAP:
ret = __xe_vma_op_execute(vm, op->map.vma, op);
break;
case DRM_GPUVA_OP_REMAP:
{
struct xe_vma *vma;
unwind: if (!op->remap.unmap_done)
list_for_each_entry_safe(__vma, next, &vma->unbind_link, unbind_link) vma = gpuva_to_vma(op->base.remap.unmap->va);
list_del_init(&__vma->unbind_link); else if (op->remap.prev)
if (new_last) { vma = op->remap.prev;
prep_vma_destroy(vm, new_last); else
xe_vma_destroy_unlocked(new_last); vma = op->remap.next;
ret = __xe_vma_op_execute(vm, vma, op);
break;
} }
if (new_first) { case DRM_GPUVA_OP_UNMAP:
prep_vma_destroy(vm, new_first); ret = __xe_vma_op_execute(vm, gpuva_to_vma(op->base.unmap.va),
xe_vma_destroy_unlocked(new_first); op);
break;
case DRM_GPUVA_OP_PREFETCH:
ret = __xe_vma_op_execute(vm,
gpuva_to_vma(op->base.prefetch.va),
op);
break;
default:
XE_BUG_ON("NOT POSSIBLE");
} }
return ERR_PTR(err); return ret;
} }
/* static void xe_vma_op_cleanup(struct xe_vm *vm, struct xe_vma_op *op)
* Similar to vm_unbind_lookup_vmas, find all VMAs in lookup range to prefetch
*/
static struct xe_vma *vm_prefetch_lookup_vmas(struct xe_vm *vm,
struct xe_vma *lookup,
u32 region)
{ {
struct xe_vma *vma = xe_vm_find_overlapping_vma(vm, lookup), *__vma, bool last = op->flags & XE_VMA_OP_LAST;
*next;
struct rb_node *node;
if (!xe_vma_has_no_bo(vma)) { if (last) {
if (!xe_bo_can_migrate(xe_vma_bo(vma), region_to_mem_type[region])) while (op->num_syncs--)
return ERR_PTR(-EINVAL); xe_sync_entry_cleanup(&op->syncs[op->num_syncs]);
kfree(op->syncs);
if (op->engine)
xe_engine_put(op->engine);
if (op->fence)
dma_fence_put(&op->fence->fence);
}
if (!list_empty(&op->link)) {
spin_lock_irq(&vm->async_ops.lock);
list_del(&op->link);
spin_unlock_irq(&vm->async_ops.lock);
} }
if (op->ops)
drm_gpuva_ops_free(&vm->gpuvm, op->ops);
if (last)
xe_vm_put(vm);
}
node = &vma->vm_node; static void xe_vma_op_unwind(struct xe_vm *vm, struct xe_vma_op *op,
while ((node = rb_next(node))) { bool post_commit)
if (!xe_vma_cmp_vma_cb(lookup, node)) { {
__vma = to_xe_vma(node); lockdep_assert_held_write(&vm->lock);
if (!xe_vma_has_no_bo(__vma)) {
if (!xe_bo_can_migrate(xe_vma_bo(__vma), region_to_mem_type[region])) switch (op->base.op) {
goto flush_list; case DRM_GPUVA_OP_MAP:
if (op->map.vma) {
prep_vma_destroy(vm, op->map.vma, post_commit);
xe_vma_destroy_unlocked(op->map.vma);
} }
list_add_tail(&__vma->unbind_link, &vma->unbind_link); break;
} else { case DRM_GPUVA_OP_UNMAP:
{
struct xe_vma *vma = gpuva_to_vma(op->base.unmap.va);
down_read(&vm->userptr.notifier_lock);
vma->gpuva.flags &= ~XE_VMA_DESTROYED;
up_read(&vm->userptr.notifier_lock);
if (post_commit)
xe_vm_insert_vma(vm, vma);
break; break;
} }
} case DRM_GPUVA_OP_REMAP:
{
struct xe_vma *vma = gpuva_to_vma(op->base.remap.unmap->va);
node = &vma->vm_node; if (op->remap.prev) {
while ((node = rb_prev(node))) { prep_vma_destroy(vm, op->remap.prev, post_commit);
if (!xe_vma_cmp_vma_cb(lookup, node)) { xe_vma_destroy_unlocked(op->remap.prev);
__vma = to_xe_vma(node);
if (!xe_vma_has_no_bo(__vma)) {
if (!xe_bo_can_migrate(xe_vma_bo(__vma), region_to_mem_type[region]))
goto flush_list;
} }
list_add(&__vma->unbind_link, &vma->unbind_link); if (op->remap.next) {
} else { prep_vma_destroy(vm, op->remap.next, post_commit);
xe_vma_destroy_unlocked(op->remap.next);
}
down_read(&vm->userptr.notifier_lock);
vma->gpuva.flags &= ~XE_VMA_DESTROYED;
up_read(&vm->userptr.notifier_lock);
if (post_commit)
xe_vm_insert_vma(vm, vma);
break; break;
} }
case DRM_GPUVA_OP_PREFETCH:
/* Nothing to do */
break;
default:
XE_BUG_ON("NOT POSSIBLE");
} }
}
return vma; static struct xe_vma_op *next_vma_op(struct xe_vm *vm)
{
flush_list: return list_first_entry_or_null(&vm->async_ops.pending,
list_for_each_entry_safe(__vma, next, &vma->unbind_link, struct xe_vma_op, link);
unbind_link)
list_del_init(&__vma->unbind_link);
return ERR_PTR(-EINVAL);
} }
static struct xe_vma *vm_unbind_all_lookup_vmas(struct xe_vm *vm, static void xe_vma_op_work_func(struct work_struct *w)
struct xe_bo *bo)
{ {
struct xe_vma *first = NULL, *vma; struct xe_vm *vm = container_of(w, struct xe_vm, async_ops.work);
lockdep_assert_held(&vm->lock); for (;;) {
xe_bo_assert_held(bo); struct xe_vma_op *op;
int err;
list_for_each_entry(vma, &bo->vmas, bo_link) { if (vm->async_ops.error && !xe_vm_is_closed(vm))
if (xe_vma_vm(vma) != vm) break;
continue;
prep_vma_destroy(vm, vma); spin_lock_irq(&vm->async_ops.lock);
if (!first) op = next_vma_op(vm);
first = vma; spin_unlock_irq(&vm->async_ops.lock);
else
list_add_tail(&vma->unbind_link, &first->unbind_link); if (!op)
break;
if (!xe_vm_is_closed(vm)) {
down_write(&vm->lock);
err = xe_vma_op_execute(vm, op);
if (err) {
drm_warn(&vm->xe->drm,
"Async VM op(%d) failed with %d",
op->base.op, err);
vm_set_async_error(vm, err);
up_write(&vm->lock);
if (vm->async_ops.error_capture.addr)
vm_error_capture(vm, err, 0, 0, 0);
break;
} }
up_write(&vm->lock);
} else {
struct xe_vma *vma;
switch (op->base.op) {
case DRM_GPUVA_OP_REMAP:
vma = gpuva_to_vma(op->base.remap.unmap->va);
trace_xe_vma_flush(vma);
down_write(&vm->lock);
xe_vma_destroy_unlocked(vma);
up_write(&vm->lock);
break;
case DRM_GPUVA_OP_UNMAP:
vma = gpuva_to_vma(op->base.unmap.va);
trace_xe_vma_flush(vma);
return first; down_write(&vm->lock);
xe_vma_destroy_unlocked(vma);
up_write(&vm->lock);
break;
default:
/* Nothing to do */
break;
}
if (op->fence && !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
&op->fence->fence.flags)) {
if (!xe_vm_no_dma_fences(vm)) {
op->fence->started = true;
wake_up_all(&op->fence->wq);
}
dma_fence_signal(&op->fence->fence);
}
}
xe_vma_op_cleanup(vm, op);
}
} }
static struct xe_vma *vm_bind_ioctl_lookup_vma(struct xe_vm *vm, static int vm_bind_ioctl_ops_commit(struct xe_vm *vm,
struct xe_bo *bo, struct list_head *ops_list, bool async)
u64 bo_offset_or_userptr,
u64 addr, u64 range, u32 op,
u64 tile_mask, u32 region)
{ {
struct ww_acquire_ctx ww; struct xe_vma_op *op, *last_op, *next;
struct xe_vma *vma, lookup;
int err; int err;
lockdep_assert_held(&vm->lock); lockdep_assert_held_write(&vm->lock);
lookup.start = addr;
lookup.end = addr + range - 1;
switch (VM_BIND_OP(op)) { list_for_each_entry(op, ops_list, link) {
case XE_VM_BIND_OP_MAP: last_op = op;
if (bo) { err = xe_vma_op_commit(vm, op);
err = xe_bo_lock(bo, &ww, 0, true);
if (err) if (err)
return ERR_PTR(err); goto unwind;
} }
vma = xe_vma_create(vm, bo, bo_offset_or_userptr, addr,
addr + range - 1,
op & XE_VM_BIND_FLAG_READONLY,
op & XE_VM_BIND_FLAG_NULL,
tile_mask);
if (bo)
xe_bo_unlock(bo, &ww);
if (!vma)
return ERR_PTR(-ENOMEM);
xe_vm_insert_vma(vm, vma); if (!async) {
if (bo && !bo->vm) { err = xe_vma_op_execute(vm, last_op);
vm_insert_extobj(vm, vma); if (err)
err = add_preempt_fences(vm, bo); goto unwind;
if (err) { xe_vma_op_cleanup(vm, last_op);
prep_vma_destroy(vm, vma); } else {
xe_vma_destroy_unlocked(vma); int i;
bool installed = false;
return ERR_PTR(err); for (i = 0; i < last_op->num_syncs; i++)
} installed |= xe_sync_entry_signal(&last_op->syncs[i],
NULL,
&last_op->fence->fence);
if (!installed && last_op->fence)
dma_fence_signal(&last_op->fence->fence);
spin_lock_irq(&vm->async_ops.lock);
list_splice_tail(ops_list, &vm->async_ops.pending);
spin_unlock_irq(&vm->async_ops.lock);
if (!vm->async_ops.error)
queue_work(system_unbound_wq, &vm->async_ops.work);
} }
break;
case XE_VM_BIND_OP_UNMAP:
vma = vm_unbind_lookup_vmas(vm, &lookup);
break;
case XE_VM_BIND_OP_PREFETCH:
vma = vm_prefetch_lookup_vmas(vm, &lookup, region);
break;
case XE_VM_BIND_OP_UNMAP_ALL:
XE_BUG_ON(!bo);
err = xe_bo_lock(bo, &ww, 0, true); return 0;
if (err)
return ERR_PTR(err);
vma = vm_unbind_all_lookup_vmas(vm, bo);
if (!vma)
vma = ERR_PTR(-EINVAL);
xe_bo_unlock(bo, &ww);
break;
case XE_VM_BIND_OP_MAP_USERPTR:
XE_BUG_ON(bo);
vma = xe_vma_create(vm, NULL, bo_offset_or_userptr, addr, unwind:
addr + range - 1, list_for_each_entry_reverse(op, ops_list, link)
op & XE_VM_BIND_FLAG_READONLY, xe_vma_op_unwind(vm, op, op->flags & XE_VMA_OP_COMMITTED);
op & XE_VM_BIND_FLAG_NULL, list_for_each_entry_safe(op, next, ops_list, link)
tile_mask); xe_vma_op_cleanup(vm, op);
if (!vma)
return ERR_PTR(-ENOMEM);
err = xe_vma_userptr_pin_pages(vma); return err;
if (err) { }
prep_vma_destroy(vm, vma);
xe_vma_destroy_unlocked(vma);
return ERR_PTR(err); /*
} else { * Unwind operations list, called after a failure of vm_bind_ioctl_ops_create or
xe_vm_insert_vma(vm, vma); * vm_bind_ioctl_ops_parse.
*/
static void vm_bind_ioctl_ops_unwind(struct xe_vm *vm,
struct drm_gpuva_ops **ops,
int num_ops_list)
{
int i;
for (i = 0; i < num_ops_list; ++i) {
struct drm_gpuva_ops *__ops = ops[i];
struct drm_gpuva_op *__op;
if (!__ops)
continue;
drm_gpuva_for_each_op(__op, __ops) {
struct xe_vma_op *op = gpuva_op_to_vma_op(__op);
xe_vma_op_unwind(vm, op, false);
} }
break;
default:
XE_BUG_ON("NOT POSSIBLE");
vma = ERR_PTR(-EINVAL);
} }
return vma;
} }
#ifdef TEST_VM_ASYNC_OPS_ERROR #ifdef TEST_VM_ASYNC_OPS_ERROR
...@@ -2963,8 +3003,6 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, ...@@ -2963,8 +3003,6 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe,
int i; int i;
if (XE_IOCTL_ERR(xe, args->extensions) || if (XE_IOCTL_ERR(xe, args->extensions) ||
XE_IOCTL_ERR(xe, args->pad || args->pad2) ||
XE_IOCTL_ERR(xe, args->reserved[0] || args->reserved[1]) ||
XE_IOCTL_ERR(xe, !args->num_binds) || XE_IOCTL_ERR(xe, !args->num_binds) ||
XE_IOCTL_ERR(xe, args->num_binds > MAX_BINDS)) XE_IOCTL_ERR(xe, args->num_binds > MAX_BINDS))
return -EINVAL; return -EINVAL;
...@@ -2998,13 +3036,6 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, ...@@ -2998,13 +3036,6 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe,
u32 region = (*bind_ops)[i].region; u32 region = (*bind_ops)[i].region;
bool is_null = op & XE_VM_BIND_FLAG_NULL; bool is_null = op & XE_VM_BIND_FLAG_NULL;
if (XE_IOCTL_ERR(xe, (*bind_ops)[i].pad) ||
XE_IOCTL_ERR(xe, (*bind_ops)[i].reserved[0] ||
(*bind_ops)[i].reserved[1])) {
err = -EINVAL;
goto free_bind_ops;
}
if (i == 0) { if (i == 0) {
*async = !!(op & XE_VM_BIND_FLAG_ASYNC); *async = !!(op & XE_VM_BIND_FLAG_ASYNC);
} else if (XE_IOCTL_ERR(xe, !*async) || } else if (XE_IOCTL_ERR(xe, !*async) ||
...@@ -3083,15 +3114,16 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file) ...@@ -3083,15 +3114,16 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
struct drm_xe_vm_bind *args = data; struct drm_xe_vm_bind *args = data;
struct drm_xe_sync __user *syncs_user; struct drm_xe_sync __user *syncs_user;
struct xe_bo **bos = NULL; struct xe_bo **bos = NULL;
struct xe_vma **vmas = NULL; struct drm_gpuva_ops **ops = NULL;
struct xe_vm *vm; struct xe_vm *vm;
struct xe_engine *e = NULL; struct xe_engine *e = NULL;
u32 num_syncs; u32 num_syncs;
struct xe_sync_entry *syncs = NULL; struct xe_sync_entry *syncs = NULL;
struct drm_xe_vm_bind_op *bind_ops; struct drm_xe_vm_bind_op *bind_ops;
LIST_HEAD(ops_list);
bool async; bool async;
int err; int err;
int i, j = 0; int i;
err = vm_bind_ioctl_check_args(xe, args, &bind_ops, &async); err = vm_bind_ioctl_check_args(xe, args, &bind_ops, &async);
if (err) if (err)
...@@ -3180,8 +3212,8 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file) ...@@ -3180,8 +3212,8 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
goto release_vm_lock; goto release_vm_lock;
} }
vmas = kzalloc(sizeof(*vmas) * args->num_binds, GFP_KERNEL); ops = kzalloc(sizeof(*ops) * args->num_binds, GFP_KERNEL);
if (!vmas) { if (!ops) {
err = -ENOMEM; err = -ENOMEM;
goto release_vm_lock; goto release_vm_lock;
} }
...@@ -3233,7 +3265,7 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file) ...@@ -3233,7 +3265,7 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
for (num_syncs = 0; num_syncs < args->num_syncs; num_syncs++) { for (num_syncs = 0; num_syncs < args->num_syncs; num_syncs++) {
err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs], err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs],
&syncs_user[num_syncs], false, &syncs_user[num_syncs], false,
xe_vm_in_fault_mode(vm)); xe_vm_no_dma_fences(vm));
if (err) if (err)
goto free_syncs; goto free_syncs;
} }
...@@ -3244,7 +3276,7 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file) ...@@ -3244,7 +3276,7 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
u64 addr = bind_ops[i].addr; u64 addr = bind_ops[i].addr;
u32 op = bind_ops[i].op; u32 op = bind_ops[i].op;
err = __vm_bind_ioctl_lookup_vma(vm, bos[i], addr, range, op); err = vm_bind_ioctl_lookup_vma(vm, bos[i], addr, range, op);
if (err) if (err)
goto free_syncs; goto free_syncs;
} }
...@@ -3257,126 +3289,43 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file) ...@@ -3257,126 +3289,43 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
u64 tile_mask = bind_ops[i].tile_mask; u64 tile_mask = bind_ops[i].tile_mask;
u32 region = bind_ops[i].region; u32 region = bind_ops[i].region;
vmas[i] = vm_bind_ioctl_lookup_vma(vm, bos[i], obj_offset, ops[i] = vm_bind_ioctl_ops_create(vm, bos[i], obj_offset,
addr, range, op, tile_mask, addr, range, op, tile_mask,
region); region);
if (IS_ERR(vmas[i])) { if (IS_ERR(ops[i])) {
err = PTR_ERR(vmas[i]); err = PTR_ERR(ops[i]);
vmas[i] = NULL; ops[i] = NULL;
goto destroy_vmas; goto unwind_ops;
}
}
for (j = 0; j < args->num_binds; ++j) {
struct xe_sync_entry *__syncs;
u32 __num_syncs = 0;
bool first_or_last = j == 0 || j == args->num_binds - 1;
if (args->num_binds == 1) {
__num_syncs = num_syncs;
__syncs = syncs;
} else if (first_or_last && num_syncs) {
bool first = j == 0;
__syncs = kmalloc(sizeof(*__syncs) * num_syncs,
GFP_KERNEL);
if (!__syncs) {
err = ENOMEM;
break;
}
/* in-syncs on first bind, out-syncs on last bind */
for (i = 0; i < num_syncs; ++i) {
bool signal = syncs[i].flags &
DRM_XE_SYNC_SIGNAL;
if ((first && !signal) || (!first && signal))
__syncs[__num_syncs++] = syncs[i];
}
} else {
__num_syncs = 0;
__syncs = NULL;
} }
if (async) {
bool last = j == args->num_binds - 1;
/*
* Each pass of async worker drops the ref, take a ref
* here, 1 set of refs taken above
*/
if (!last) {
if (e)
xe_engine_get(e);
xe_vm_get(vm);
} }
err = vm_bind_ioctl_async(vm, vmas[j], e, bos[j], err = vm_bind_ioctl_ops_parse(vm, e, ops, args->num_binds,
bind_ops + j, __syncs, syncs, num_syncs, &ops_list, async);
__num_syncs);
if (err && !last) {
if (e)
xe_engine_put(e);
xe_vm_put(vm);
}
if (err) if (err)
break; goto unwind_ops;
} else {
XE_BUG_ON(j != 0); /* Not supported */
err = vm_bind_ioctl(vm, vmas[j], e, bos[j],
bind_ops + j, __syncs,
__num_syncs, NULL);
break; /* Needed so cleanup loops work */
}
}
/* Most of cleanup owned by the async bind worker */ err = vm_bind_ioctl_ops_commit(vm, &ops_list, async);
if (async && !err) {
up_write(&vm->lock); up_write(&vm->lock);
if (args->num_binds > 1)
kfree(syncs);
goto free_objs;
}
destroy_vmas: for (i = 0; i < args->num_binds; ++i)
for (i = j; err && i < args->num_binds; ++i) { xe_bo_put(bos[i]);
u32 op = bind_ops[i].op;
struct xe_vma *vma, *next;
if (!vmas[i]) kfree(bos);
break; kfree(ops);
if (args->num_binds > 1)
kfree(bind_ops);
list_for_each_entry_safe(vma, next, &vmas[i]->unbind_link, return err;
unbind_link) {
list_del_init(&vma->unbind_link);
if (!vma->destroyed) {
prep_vma_destroy(vm, vma);
xe_vma_destroy_unlocked(vma);
}
}
switch (VM_BIND_OP(op)) { unwind_ops:
case XE_VM_BIND_OP_MAP: vm_bind_ioctl_ops_unwind(vm, ops, args->num_binds);
prep_vma_destroy(vm, vmas[i]);
xe_vma_destroy_unlocked(vmas[i]);
break;
case XE_VM_BIND_OP_MAP_USERPTR:
prep_vma_destroy(vm, vmas[i]);
xe_vma_destroy_unlocked(vmas[i]);
break;
}
}
free_syncs: free_syncs:
while (num_syncs--) { while (num_syncs--)
if (async && j &&
!(syncs[num_syncs].flags & DRM_XE_SYNC_SIGNAL))
continue; /* Still in async worker */
xe_sync_entry_cleanup(&syncs[num_syncs]); xe_sync_entry_cleanup(&syncs[num_syncs]);
}
kfree(syncs); kfree(syncs);
put_obj: put_obj:
for (i = j; i < args->num_binds; ++i) for (i = 0; i < args->num_binds; ++i)
xe_bo_put(bos[i]); xe_bo_put(bos[i]);
release_vm_lock: release_vm_lock:
up_write(&vm->lock); up_write(&vm->lock);
...@@ -3387,10 +3336,10 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file) ...@@ -3387,10 +3336,10 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
xe_engine_put(e); xe_engine_put(e);
free_objs: free_objs:
kfree(bos); kfree(bos);
kfree(vmas); kfree(ops);
if (args->num_binds > 1) if (args->num_binds > 1)
kfree(bind_ops); kfree(bind_ops);
return err; return err == -ENODATA ? 0 : err;
} }
/* /*
...@@ -3415,7 +3364,7 @@ int xe_vm_lock(struct xe_vm *vm, struct ww_acquire_ctx *ww, ...@@ -3415,7 +3364,7 @@ int xe_vm_lock(struct xe_vm *vm, struct ww_acquire_ctx *ww,
void xe_vm_unlock(struct xe_vm *vm, struct ww_acquire_ctx *ww) void xe_vm_unlock(struct xe_vm *vm, struct ww_acquire_ctx *ww)
{ {
dma_resv_unlock(&vm->resv); dma_resv_unlock(xe_vm_resv(vm));
ww_acquire_fini(ww); ww_acquire_fini(ww);
} }
...@@ -3448,7 +3397,7 @@ int xe_vm_invalidate_vma(struct xe_vma *vma) ...@@ -3448,7 +3397,7 @@ int xe_vm_invalidate_vma(struct xe_vma *vma)
WARN_ON_ONCE(!mmu_interval_check_retry WARN_ON_ONCE(!mmu_interval_check_retry
(&vma->userptr.notifier, (&vma->userptr.notifier,
vma->userptr.notifier_seq)); vma->userptr.notifier_seq));
WARN_ON_ONCE(!dma_resv_test_signaled(&xe_vma_vm(vma)->resv, WARN_ON_ONCE(!dma_resv_test_signaled(xe_vm_resv(xe_vma_vm(vma)),
DMA_RESV_USAGE_BOOKKEEP)); DMA_RESV_USAGE_BOOKKEEP));
} else { } else {
...@@ -3485,7 +3434,7 @@ int xe_vm_invalidate_vma(struct xe_vma *vma) ...@@ -3485,7 +3434,7 @@ int xe_vm_invalidate_vma(struct xe_vma *vma)
int xe_analyze_vm(struct drm_printer *p, struct xe_vm *vm, int gt_id) int xe_analyze_vm(struct drm_printer *p, struct xe_vm *vm, int gt_id)
{ {
struct rb_node *node; struct drm_gpuva *gpuva;
bool is_vram; bool is_vram;
uint64_t addr; uint64_t addr;
...@@ -3499,8 +3448,8 @@ int xe_analyze_vm(struct drm_printer *p, struct xe_vm *vm, int gt_id) ...@@ -3499,8 +3448,8 @@ int xe_analyze_vm(struct drm_printer *p, struct xe_vm *vm, int gt_id)
drm_printf(p, " VM root: A:0x%llx %s\n", addr, is_vram ? "VRAM" : "SYS"); drm_printf(p, " VM root: A:0x%llx %s\n", addr, is_vram ? "VRAM" : "SYS");
} }
for (node = rb_first(&vm->vmas); node; node = rb_next(node)) { drm_gpuvm_for_each_va(gpuva, &vm->gpuvm) {
struct xe_vma *vma = to_xe_vma(node); struct xe_vma *vma = gpuva_to_vma(gpuva);
bool is_userptr = xe_vma_is_userptr(vma); bool is_userptr = xe_vma_is_userptr(vma);
bool is_null = xe_vma_is_null(vma); bool is_null = xe_vma_is_null(vma);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#ifndef _XE_VM_H_ #ifndef _XE_VM_H_
#define _XE_VM_H_ #define _XE_VM_H_
#include "xe_bo_types.h"
#include "xe_macros.h" #include "xe_macros.h"
#include "xe_map.h" #include "xe_map.h"
#include "xe_vm_types.h" #include "xe_vm_types.h"
...@@ -22,20 +23,19 @@ struct xe_file; ...@@ -22,20 +23,19 @@ struct xe_file;
struct xe_sync_entry; struct xe_sync_entry;
struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags); struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags);
void xe_vm_free(struct kref *ref);
struct xe_vm *xe_vm_lookup(struct xe_file *xef, u32 id); struct xe_vm *xe_vm_lookup(struct xe_file *xef, u32 id);
int xe_vma_cmp_vma_cb(const void *key, const struct rb_node *node); int xe_vma_cmp_vma_cb(const void *key, const struct rb_node *node);
static inline struct xe_vm *xe_vm_get(struct xe_vm *vm) static inline struct xe_vm *xe_vm_get(struct xe_vm *vm)
{ {
kref_get(&vm->refcount); drm_gpuvm_get(&vm->gpuvm);
return vm; return vm;
} }
static inline void xe_vm_put(struct xe_vm *vm) static inline void xe_vm_put(struct xe_vm *vm)
{ {
kref_put(&vm->refcount, xe_vm_free); drm_gpuvm_put(&vm->gpuvm);
} }
int xe_vm_lock(struct xe_vm *vm, struct ww_acquire_ctx *ww, int xe_vm_lock(struct xe_vm *vm, struct ww_acquire_ctx *ww,
...@@ -61,7 +61,22 @@ static inline bool xe_vm_is_closed_or_banned(struct xe_vm *vm) ...@@ -61,7 +61,22 @@ static inline bool xe_vm_is_closed_or_banned(struct xe_vm *vm)
} }
struct xe_vma * struct xe_vma *
xe_vm_find_overlapping_vma(struct xe_vm *vm, struct xe_vma *vma); xe_vm_find_overlapping_vma(struct xe_vm *vm, u64 start, u64 range);
static inline struct xe_vm *gpuva_to_vm(struct drm_gpuva *gpuva)
{
return container_of(gpuva->vm, struct xe_vm, gpuvm);
}
static inline struct xe_vma *gpuva_to_vma(struct drm_gpuva *gpuva)
{
return container_of(gpuva, struct xe_vma, gpuva);
}
static inline struct xe_vma_op *gpuva_op_to_vma_op(struct drm_gpuva_op *op)
{
return container_of(op, struct xe_vma_op, base);
}
/** /**
* DOC: Provide accessors for vma members to facilitate easy change of * DOC: Provide accessors for vma members to facilitate easy change of
...@@ -69,12 +84,12 @@ xe_vm_find_overlapping_vma(struct xe_vm *vm, struct xe_vma *vma); ...@@ -69,12 +84,12 @@ xe_vm_find_overlapping_vma(struct xe_vm *vm, struct xe_vma *vma);
*/ */
static inline u64 xe_vma_start(struct xe_vma *vma) static inline u64 xe_vma_start(struct xe_vma *vma)
{ {
return vma->start; return vma->gpuva.va.addr;
} }
static inline u64 xe_vma_size(struct xe_vma *vma) static inline u64 xe_vma_size(struct xe_vma *vma)
{ {
return vma->end - vma->start + 1; return vma->gpuva.va.range;
} }
static inline u64 xe_vma_end(struct xe_vma *vma) static inline u64 xe_vma_end(struct xe_vma *vma)
...@@ -84,32 +99,33 @@ static inline u64 xe_vma_end(struct xe_vma *vma) ...@@ -84,32 +99,33 @@ static inline u64 xe_vma_end(struct xe_vma *vma)
static inline u64 xe_vma_bo_offset(struct xe_vma *vma) static inline u64 xe_vma_bo_offset(struct xe_vma *vma)
{ {
return vma->bo_offset; return vma->gpuva.gem.offset;
} }
static inline struct xe_bo *xe_vma_bo(struct xe_vma *vma) static inline struct xe_bo *xe_vma_bo(struct xe_vma *vma)
{ {
return vma->bo; return !vma->gpuva.gem.obj ? NULL :
container_of(vma->gpuva.gem.obj, struct xe_bo, ttm.base);
} }
static inline struct xe_vm *xe_vma_vm(struct xe_vma *vma) static inline struct xe_vm *xe_vma_vm(struct xe_vma *vma)
{ {
return vma->vm; return container_of(vma->gpuva.vm, struct xe_vm, gpuvm);
} }
static inline bool xe_vma_read_only(struct xe_vma *vma) static inline bool xe_vma_read_only(struct xe_vma *vma)
{ {
return vma->pte_flags & XE_PTE_FLAG_READ_ONLY; return vma->gpuva.flags & XE_VMA_READ_ONLY;
} }
static inline u64 xe_vma_userptr(struct xe_vma *vma) static inline u64 xe_vma_userptr(struct xe_vma *vma)
{ {
return vma->userptr.ptr; return vma->gpuva.gem.offset;
} }
static inline bool xe_vma_is_null(struct xe_vma *vma) static inline bool xe_vma_is_null(struct xe_vma *vma)
{ {
return vma->pte_flags & XE_PTE_FLAG_NULL; return vma->gpuva.flags & DRM_GPUVA_SPARSE;
} }
static inline bool xe_vma_has_no_bo(struct xe_vma *vma) static inline bool xe_vma_has_no_bo(struct xe_vma *vma)
...@@ -122,8 +138,6 @@ static inline bool xe_vma_is_userptr(struct xe_vma *vma) ...@@ -122,8 +138,6 @@ static inline bool xe_vma_is_userptr(struct xe_vma *vma)
return xe_vma_has_no_bo(vma) && !xe_vma_is_null(vma); return xe_vma_has_no_bo(vma) && !xe_vma_is_null(vma);
} }
#define xe_vm_assert_held(vm) dma_resv_assert_held(&(vm)->resv)
u64 xe_vm_pdp4_descriptor(struct xe_vm *vm, struct xe_tile *tile); u64 xe_vm_pdp4_descriptor(struct xe_vm *vm, struct xe_tile *tile);
int xe_vm_create_ioctl(struct drm_device *dev, void *data, int xe_vm_create_ioctl(struct drm_device *dev, void *data,
...@@ -218,6 +232,23 @@ void xe_vm_fence_all_extobjs(struct xe_vm *vm, struct dma_fence *fence, ...@@ -218,6 +232,23 @@ void xe_vm_fence_all_extobjs(struct xe_vm *vm, struct dma_fence *fence,
int xe_analyze_vm(struct drm_printer *p, struct xe_vm *vm, int gt_id); int xe_analyze_vm(struct drm_printer *p, struct xe_vm *vm, int gt_id);
/**
* xe_vm_resv() - Return's the vm's reservation object
* @vm: The vm
*
* Return: Pointer to the vm's reservation object.
*/
static inline struct dma_resv *xe_vm_resv(struct xe_vm *vm)
{
return drm_gpuvm_resv(&vm->gpuvm);
}
/**
* xe_vm_assert_held(vm) - Assert that the vm's reservation object is held.
* @vm: The vm
*/
#define xe_vm_assert_held(vm) dma_resv_assert_held(xe_vm_resv(vm))
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM) #if IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM)
#define vm_dbg drm_dbg #define vm_dbg drm_dbg
#else #else
......
...@@ -210,19 +210,12 @@ static const madvise_func madvise_funcs[] = { ...@@ -210,19 +210,12 @@ static const madvise_func madvise_funcs[] = {
[DRM_XE_VM_MADVISE_PIN] = madvise_pin, [DRM_XE_VM_MADVISE_PIN] = madvise_pin,
}; };
static struct xe_vma *node_to_vma(const struct rb_node *node)
{
BUILD_BUG_ON(offsetof(struct xe_vma, vm_node) != 0);
return (struct xe_vma *)node;
}
static struct xe_vma ** static struct xe_vma **
get_vmas(struct xe_vm *vm, int *num_vmas, u64 addr, u64 range) get_vmas(struct xe_vm *vm, int *num_vmas, u64 addr, u64 range)
{ {
struct xe_vma **vmas; struct xe_vma **vmas, **__vmas;
struct xe_vma *vma, *__vma, lookup; struct drm_gpuva *gpuva;
int max_vmas = 8; int max_vmas = 8;
struct rb_node *node;
lockdep_assert_held(&vm->lock); lockdep_assert_held(&vm->lock);
...@@ -230,62 +223,23 @@ get_vmas(struct xe_vm *vm, int *num_vmas, u64 addr, u64 range) ...@@ -230,62 +223,23 @@ get_vmas(struct xe_vm *vm, int *num_vmas, u64 addr, u64 range)
if (!vmas) if (!vmas)
return NULL; return NULL;
lookup.start = addr; drm_gpuvm_for_each_va_range(gpuva, &vm->gpuvm, addr, addr + range) {
lookup.end = addr + range - 1; struct xe_vma *vma = gpuva_to_vma(gpuva);
vma = xe_vm_find_overlapping_vma(vm, &lookup);
if (!vma)
return vmas;
if (!xe_vma_is_userptr(vma)) {
vmas[*num_vmas] = vma;
*num_vmas += 1;
}
node = &vma->vm_node; if (xe_vma_is_userptr(vma))
while ((node = rb_next(node))) {
if (!xe_vma_cmp_vma_cb(&lookup, node)) {
__vma = node_to_vma(node);
if (xe_vma_is_userptr(__vma))
continue; continue;
if (*num_vmas == max_vmas) { if (*num_vmas == max_vmas) {
struct xe_vma **__vmas = max_vmas <<= 1;
krealloc(vmas, max_vmas * sizeof(*vmas), __vmas = krealloc(vmas, max_vmas * sizeof(*vmas),
GFP_KERNEL); GFP_KERNEL);
if (!__vmas) if (!__vmas)
return NULL; return NULL;
vmas = __vmas; vmas = __vmas;
} }
vmas[*num_vmas] = __vma;
*num_vmas += 1;
} else {
break;
}
}
node = &vma->vm_node;
while ((node = rb_prev(node))) {
if (!xe_vma_cmp_vma_cb(&lookup, node)) {
__vma = node_to_vma(node);
if (xe_vma_is_userptr(__vma))
continue;
if (*num_vmas == max_vmas) {
struct xe_vma **__vmas =
krealloc(vmas, max_vmas * sizeof(*vmas),
GFP_KERNEL);
if (!__vmas) vmas[*num_vmas] = vma;
return NULL;
vmas = __vmas;
}
vmas[*num_vmas] = __vma;
*num_vmas += 1; *num_vmas += 1;
} else {
break;
}
} }
return vmas; return vmas;
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#ifndef _XE_VM_TYPES_H_ #ifndef _XE_VM_TYPES_H_
#define _XE_VM_TYPES_H_ #define _XE_VM_TYPES_H_
#include <drm/drm_gpuvm.h>
#include <linux/dma-resv.h> #include <linux/dma-resv.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/mmu_notifier.h> #include <linux/mmu_notifier.h>
...@@ -14,30 +16,23 @@ ...@@ -14,30 +16,23 @@
#include "xe_device_types.h" #include "xe_device_types.h"
#include "xe_pt_types.h" #include "xe_pt_types.h"
struct async_op_fence;
struct xe_bo; struct xe_bo;
struct xe_sync_entry;
struct xe_vm; struct xe_vm;
struct xe_vma { #define TEST_VM_ASYNC_OPS_ERROR
struct rb_node vm_node; #define FORCE_ASYNC_OP_ERROR BIT(31)
/** @vm: VM which this VMA belongs to */
struct xe_vm *vm;
/** #define XE_VMA_READ_ONLY DRM_GPUVA_USERBITS
* @start: start address of this VMA within its address domain, end - #define XE_VMA_DESTROYED (DRM_GPUVA_USERBITS << 1)
* start + 1 == VMA size #define XE_VMA_ATOMIC_PTE_BIT (DRM_GPUVA_USERBITS << 2)
*/ #define XE_VMA_FIRST_REBIND (DRM_GPUVA_USERBITS << 3)
u64 start; #define XE_VMA_LAST_REBIND (DRM_GPUVA_USERBITS << 4)
/** @end: end address of this VMA within its address domain */
u64 end; struct xe_vma {
/** @pte_flags: pte flags for this VMA */ /** @gpuva: Base GPUVA object */
#define XE_PTE_FLAG_READ_ONLY BIT(0) struct drm_gpuva gpuva;
#define XE_PTE_FLAG_NULL BIT(1)
u32 pte_flags;
/** @bo: BO if not a userptr, must be NULL is userptr */
struct xe_bo *bo;
/** @bo_offset: offset into BO if not a userptr, unused for userptr */
u64 bo_offset;
/** @tile_mask: Tile mask of where to create binding for this VMA */ /** @tile_mask: Tile mask of where to create binding for this VMA */
u64 tile_mask; u64 tile_mask;
...@@ -51,40 +46,8 @@ struct xe_vma { ...@@ -51,40 +46,8 @@ struct xe_vma {
*/ */
u64 tile_present; u64 tile_present;
/**
* @destroyed: VMA is destroyed, in the sense that it shouldn't be
* subject to rebind anymore. This field must be written under
* the vm lock in write mode and the userptr.notifier_lock in
* either mode. Read under the vm lock or the userptr.notifier_lock in
* write mode.
*/
bool destroyed;
/**
* @first_munmap_rebind: VMA is first in a sequence of ops that triggers
* a rebind (munmap style VM unbinds). This indicates the operation
* using this VMA must wait on all dma-resv slots (wait for pending jobs
* / trigger preempt fences).
*/
bool first_munmap_rebind;
/**
* @last_munmap_rebind: VMA is first in a sequence of ops that triggers
* a rebind (munmap style VM unbinds). This indicates the operation
* using this VMA must install itself into kernel dma-resv slot (blocks
* future jobs) and kick the rebind work in compute mode.
*/
bool last_munmap_rebind;
/** @use_atomic_access_pte_bit: Set atomic access bit in PTE */
bool use_atomic_access_pte_bit;
union {
/** @bo_link: link into BO if not a userptr */
struct list_head bo_link;
/** @userptr_link: link into VM repin list if userptr */ /** @userptr_link: link into VM repin list if userptr */
struct list_head userptr_link; struct list_head userptr_link;
};
/** /**
* @rebind_link: link into VM if this VMA needs rebinding, and * @rebind_link: link into VM if this VMA needs rebinding, and
...@@ -107,8 +70,6 @@ struct xe_vma { ...@@ -107,8 +70,6 @@ struct xe_vma {
/** @userptr: user pointer state */ /** @userptr: user pointer state */
struct { struct {
/** @ptr: user pointer */
uintptr_t ptr;
/** @invalidate_link: Link for the vm::userptr.invalidated list */ /** @invalidate_link: Link for the vm::userptr.invalidated list */
struct list_head invalidate_link; struct list_head invalidate_link;
/** /**
...@@ -153,24 +114,19 @@ struct xe_vma { ...@@ -153,24 +114,19 @@ struct xe_vma {
struct xe_device; struct xe_device;
#define xe_vm_assert_held(vm) dma_resv_assert_held(&(vm)->resv)
struct xe_vm { struct xe_vm {
struct xe_device *xe; /** @gpuvm: base GPUVM used to track VMAs */
struct drm_gpuvm gpuvm;
struct kref refcount; struct xe_device *xe;
/* engine used for (un)binding vma's */ /* engine used for (un)binding vma's */
struct xe_engine *eng[XE_MAX_TILES_PER_DEVICE]; struct xe_engine *eng[XE_MAX_TILES_PER_DEVICE];
/** Protects @rebind_list and the page-table structures */
struct dma_resv resv;
/** @lru_bulk_move: Bulk LRU move list for this VM's BOs */ /** @lru_bulk_move: Bulk LRU move list for this VM's BOs */
struct ttm_lru_bulk_move lru_bulk_move; struct ttm_lru_bulk_move lru_bulk_move;
u64 size; u64 size;
struct rb_root vmas;
struct xe_pt *pt_root[XE_MAX_TILES_PER_DEVICE]; struct xe_pt *pt_root[XE_MAX_TILES_PER_DEVICE];
struct xe_bo *scratch_bo[XE_MAX_TILES_PER_DEVICE]; struct xe_bo *scratch_bo[XE_MAX_TILES_PER_DEVICE];
...@@ -351,4 +307,99 @@ struct xe_vm { ...@@ -351,4 +307,99 @@ struct xe_vm {
bool batch_invalidate_tlb; bool batch_invalidate_tlb;
}; };
/** struct xe_vma_op_map - VMA map operation */
struct xe_vma_op_map {
/** @vma: VMA to map */
struct xe_vma *vma;
/** @immediate: Immediate bind */
bool immediate;
/** @read_only: Read only */
bool read_only;
/** @is_null: is NULL binding */
bool is_null;
};
/** struct xe_vma_op_unmap - VMA unmap operation */
struct xe_vma_op_unmap {
/** @start: start of the VMA unmap */
u64 start;
/** @range: range of the VMA unmap */
u64 range;
};
/** struct xe_vma_op_remap - VMA remap operation */
struct xe_vma_op_remap {
/** @prev: VMA preceding part of a split mapping */
struct xe_vma *prev;
/** @next: VMA subsequent part of a split mapping */
struct xe_vma *next;
/** @start: start of the VMA unmap */
u64 start;
/** @range: range of the VMA unmap */
u64 range;
/** @unmap_done: unmap operation in done */
bool unmap_done;
};
/** struct xe_vma_op_prefetch - VMA prefetch operation */
struct xe_vma_op_prefetch {
/** @region: memory region to prefetch to */
u32 region;
};
/** enum xe_vma_op_flags - flags for VMA operation */
enum xe_vma_op_flags {
/** @XE_VMA_OP_FIRST: first VMA operation for a set of syncs */
XE_VMA_OP_FIRST = (0x1 << 0),
/** @XE_VMA_OP_LAST: last VMA operation for a set of syncs */
XE_VMA_OP_LAST = (0x1 << 1),
/** @XE_VMA_OP_COMMITTED: VMA operation committed */
XE_VMA_OP_COMMITTED = (0x1 << 2),
};
/** struct xe_vma_op - VMA operation */
struct xe_vma_op {
/** @base: GPUVA base operation */
struct drm_gpuva_op base;
/**
* @ops: GPUVA ops, when set call drm_gpuva_ops_free after this
* operations is processed
*/
struct drm_gpuva_ops *ops;
/** @engine: engine for this operation */
struct xe_engine *engine;
/**
* @syncs: syncs for this operation, only used on first and last
* operation
*/
struct xe_sync_entry *syncs;
/** @num_syncs: number of syncs */
u32 num_syncs;
/** @link: async operation link */
struct list_head link;
/**
* @fence: async operation fence, signaled on last operation complete
*/
struct async_op_fence *fence;
/** @tile_mask: gt mask for this operation */
u64 tile_mask;
/** @flags: operation flags */
enum xe_vma_op_flags flags;
#ifdef TEST_VM_ASYNC_OPS_ERROR
/** @inject_error: inject error to test async op error handling */
bool inject_error;
#endif
union {
/** @map: VMA map operation specific data */
struct xe_vma_op_map map;
/** @unmap: VMA unmap operation specific data */
struct xe_vma_op_unmap unmap;
/** @remap: VMA remap operation specific data */
struct xe_vma_op_remap remap;
/** @prefetch: VMA prefetch operation specific data */
struct xe_vma_op_prefetch prefetch;
};
};
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment