/** * xe_vma_userptr_check_repin() - Advisory check for repin needed * @uvma: The userptr vma * * Check if the userptr vma has been invalidated since last successful * repin. The check is advisory only and can the function can be called * without the vm->userptr.notifier_lock held. There is no guarantee that the * vma userptr will remain valid after a lockless check, so typically * the call needs to be followed by a proper check under the notifier_lock. * * Return: 0 if userptr vma is valid, -EAGAIN otherwise; repin recommended.
*/ int xe_vma_userptr_check_repin(struct xe_userptr_vma *uvma)
{ return mmu_interval_check_retry(&uvma->userptr.notifier,
uvma->userptr.notifier_seq) ?
-EAGAIN : 0;
}
/* * Check to see if a preemption on VM is in flight or userptr * invalidation, if so trigger this preempt fence to sync state with * other preempt fences on the VM.
*/
wait = __xe_vm_userptr_needs_repin(vm) || preempt_fences_waiting(vm); if (wait)
dma_fence_enable_sw_signaling(pfence);
/** * xe_vm_remove_compute_exec_queue() - Remove compute exec queue from VM * @vm: The VM. * @q: The exec_queue * * Note that this function might be called multiple times on the same queue.
*/ void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
{ if (!xe_vm_in_preempt_fence_mode(vm)) return;
down_write(&vm->lock); if (!list_empty(&q->lr.link)) {
list_del_init(&q->lr.link);
--vm->preempt.num_exec_queues;
} if (q->lr.pfence) {
dma_fence_enable_sw_signaling(q->lr.pfence);
dma_fence_put(q->lr.pfence);
q->lr.pfence = NULL;
}
up_write(&vm->lock);
}
/** * __xe_vm_userptr_needs_repin() - Check whether the VM does have userptrs * that need repinning. * @vm: The VM. * * This function checks for whether the VM has userptrs that need repinning, * and provides a release-type barrier on the userptr.notifier_lock after * checking. * * Return: 0 if there are no userptrs needing repinning, -EAGAIN if there are.
*/ int __xe_vm_userptr_needs_repin(struct xe_vm *vm)
{
lockdep_assert_held_read(&vm->userptr.notifier_lock);
/** * xe_vm_kill() - VM Kill * @vm: The VM. * @unlocked: Flag indicates the VM's dma-resv is not held * * Kill the VM by setting banned flag indicated VM is no longer available for * use. If in preempt fence mode, also kill all exec queue attached to the VM.
*/ void xe_vm_kill(struct xe_vm *vm, bool unlocked)
{ struct xe_exec_queue *q;
/** * xe_vm_validate_should_retry() - Whether to retry after a validate error. * @exec: The drm_exec object used for locking before validation. * @err: The error returned from ttm_bo_validate(). * @end: A ktime_t cookie that should be set to 0 before first use and * that should be reused on subsequent calls. * * With multiple active VMs, under memory pressure, it is possible that * ttm_bo_validate() run into -EDEADLK and in such case returns -ENOMEM. * Until ttm properly handles locking in such scenarios, best thing the * driver can do is retry with a timeout. Check if that is necessary, and * if so unlock the drm_exec's objects while keeping the ticket to prepare * for a rerun. * * Return: true if a retry after drm_exec_init() is recommended; * false otherwise.
*/ bool xe_vm_validate_should_retry(struct drm_exec *exec, int err, ktime_t *end)
{
ktime_t cur;
if (err != -ENOMEM) returnfalse;
cur = ktime_get();
*end = *end ? : ktime_add_ms(cur, XE_VM_REBIND_RETRY_TIMEOUT_MS); if (!ktime_before(cur, *end)) returnfalse;
if (!try_wait_for_completion(&vm->xe->pm_block)) return -EAGAIN;
ret = xe_bo_validate(gem_to_xe_bo(vm_bo->obj), vm, false); if (ret) return ret;
vm_bo->evicted = false; return 0;
}
/** * xe_vm_validate_rebind() - Validate buffer objects and rebind vmas * @vm: The vm for which we are rebinding. * @exec: The struct drm_exec with the locked GEM objects. * @num_fences: The number of fences to reserve for the operation, not * including rebinds and validations. * * Validates all evicted gem objects and rebinds their vmas. Note that * rebindings may cause evictions and hence the validation-rebind * sequence is rerun until there are no more objects to validate. * * Return: 0 on success, negative error code on error. In particular, * may return -EINTR or -ERESTARTSYS if interrupted, and -EDEADLK if * the drm_exec transaction needs to be restarted.
*/ int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec, unsignedint num_fences)
{ struct drm_gem_object *obj; unsignedlong index; int ret;
do {
ret = drm_gpuvm_validate(&vm->gpuvm, exec); if (ret) return ret;
ret = xe_vm_rebind(vm, false); if (ret) return ret;
} while (!list_empty(&vm->gpuvm.evict.list));
drm_exec_for_each_locked_object(exec, index, obj) {
ret = dma_resv_reserve_fences(obj->resv, num_fences); if (ret) return ret;
}
if (!preempt_fences_waiting(vm)) {
*done = true; return 0;
}
err = drm_gpuvm_prepare_objects(&vm->gpuvm, exec, 0); if (err) return err;
err = wait_for_existing_preempt_fences(vm); if (err) return err;
/* * Add validation and rebinding to the locking loop since both can * cause evictions which may require blocing dma_resv locks. * The fence reservation here is intended for the new preempt fences * we attach at the end of the rebind work.
*/ return xe_vm_validate_rebind(vm, exec, vm->preempt.num_exec_queues);
}
mutex_lock(&xe->rebind_resume_lock); if (!try_wait_for_completion(&vm->xe->pm_block)) {
ret = true;
list_move_tail(&vm->preempt.pm_activate_link, &xe->rebind_resume_list);
}
mutex_unlock(&xe->rebind_resume_lock);
return ret;
}
/** * xe_vm_resume_rebind_worker() - Resume the rebind worker. * @vm: The vm whose preempt worker to resume. * * Resume a preempt worker that was previously suspended by * vm_suspend_rebind_worker().
*/ void xe_vm_resume_rebind_worker(struct xe_vm *vm)
{
queue_work(vm->xe->ordered_wq, &vm->preempt.rebind_work);
}
staticvoid preempt_rebind_work_func(struct work_struct *w)
{ struct xe_vm *vm = container_of(w, struct xe_vm, preempt.rebind_work); struct drm_exec exec; unsignedint fence_count = 0;
LIST_HEAD(preempt_fences);
ktime_t end = 0; int err = 0; long wait; int __maybe_unused tries = 0;
/* Point of no return. */
arm_preempt_fences(vm, &preempt_fences);
resume_and_reinstall_preempt_fences(vm, &exec);
up_read(&vm->userptr.notifier_lock);
/* * Tell exec and rebind worker they need to repin and rebind this * userptr.
*/ if (!xe_vm_in_fault_mode(vm) &&
!(vma->gpuva.flags & XE_VMA_DESTROYED)) {
spin_lock(&vm->userptr.invalidated_lock);
list_move_tail(&userptr->invalidate_link,
&vm->userptr.invalidated);
spin_unlock(&vm->userptr.invalidated_lock);
}
/* * Preempt fences turn into schedule disables, pipeline these. * Note that even in fault mode, we need to wait for binds and * unbinds to complete, and those are attached as BOOKMARK fences * to the vm.
*/
dma_resv_iter_begin(&cursor, xe_vm_resv(vm),
DMA_RESV_USAGE_BOOKKEEP);
dma_resv_for_each_fence_unlocked(&cursor, fence)
dma_fence_enable_sw_signaling(fence);
dma_resv_iter_end(&cursor);
#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) /** * xe_vma_userptr_force_invalidate() - force invalidate a userptr * @uvma: The userptr vma to invalidate * * Perform a forced userptr invalidation for testing purposes.
*/ void xe_vma_userptr_force_invalidate(struct xe_userptr_vma *uvma)
{ struct xe_vm *vm = xe_vma_vm(&uvma->vma);
/* Protect against concurrent userptr pinning */
lockdep_assert_held(&vm->lock); /* Protect against concurrent notifiers */
lockdep_assert_held(&vm->userptr.notifier_lock); /* * Protect against concurrent instances of this function and * the critical exec sections
*/
xe_vm_assert_held(vm);
if (!mmu_interval_read_retry(&uvma->userptr.notifier,
uvma->userptr.notifier_seq))
uvma->userptr.notifier_seq -= 2;
__vma_userptr_invalidate(vm, uvma);
} #endif
int xe_vm_userptr_pin(struct xe_vm *vm)
{ struct xe_userptr_vma *uvma, *next; int err = 0;
/* Pin and move to bind list */
list_for_each_entry_safe(uvma, next, &vm->userptr.repin_list,
userptr.repin_link) {
err = xe_vma_userptr_pin_pages(uvma); if (err == -EFAULT) {
list_del_init(&uvma->userptr.repin_link); /* * We might have already done the pin once already, but * then had to retry before the re-bind happened, due * some other condition in the caller, but in the * meantime the userptr got dinged by the notifier such * that we need to revalidate here, but this time we hit * the EFAULT. In such a case make sure we remove * ourselves from the rebind list to avoid going down in * flames.
*/ if (!list_empty(&uvma->vma.combined_links.rebind))
list_del_init(&uvma->vma.combined_links.rebind);
/** * xe_vm_userptr_check_repin() - Check whether the VM might have userptrs * that need repinning. * @vm: The VM. * * This function does an advisory check for whether the VM has userptrs that * need repinning. * * Return: 0 if there are no indications of userptrs needing repinning, * -EAGAIN if there are.
*/ int xe_vm_userptr_check_repin(struct xe_vm *vm)
{ return (list_empty_careful(&vm->userptr.repin_list) &&
list_empty_careful(&vm->userptr.invalidated)) ? 0 : -EAGAIN;
}
staticint xe_vma_ops_alloc(struct xe_vma_ops *vops, bool array_of_binds)
{ int i;
for (i = 0; i < XE_MAX_TILES_PER_DEVICE; ++i) { if (!vops->pt_update_ops[i].num_ops) continue;
/** * xe_vm_range_rebind() - VM range (re)bind * @vm: The VM which the range belongs to. * @vma: The VMA which the range belongs to. * @range: SVM range to rebind. * @tile_mask: Tile mask to bind the range to. * * (re)bind SVM range setting up GPU page tables for the range. * * Return: dma fence for rebind to signal completion on succees, ERR_PTR on * failure
*/ struct dma_fence *xe_vm_range_rebind(struct xe_vm *vm, struct xe_vma *vma, struct xe_svm_range *range,
u8 tile_mask)
{ struct dma_fence *fence = NULL; struct xe_vma_ops vops; struct xe_vma_op *op, *next_op; struct xe_tile *tile;
u8 id; int err;
/** * xe_vm_range_unbind() - VM range unbind * @vm: The VM which the range belongs to. * @range: SVM range to rebind. * * Unbind SVM range removing the GPU page tables for the range. * * Return: dma fence for unbind to signal completion on succees, ERR_PTR on * failure
*/ struct dma_fence *xe_vm_range_unbind(struct xe_vm *vm, struct xe_svm_range *range)
{ struct dma_fence *fence = NULL; struct xe_vma_ops vops; struct xe_vma_op *op, *next_op; struct xe_tile *tile;
u8 id; int err;
xe_assert(vm->xe, start < end);
xe_assert(vm->xe, end < vm->size);
/* * Allocate and ensure that the xe_vma_is_userptr() return * matches what was allocated.
*/ if (!bo && !is_null && !is_cpu_addr_mirror) { struct xe_userptr_vma *uvma = kzalloc(sizeof(*uvma), GFP_KERNEL);
/* * Since userptr pages are not pinned, we can't remove * the notifier until we're sure the GPU is not accessing * them anymore
*/
mmu_interval_notifier_remove(&userptr->notifier);
mutex_destroy(&userptr->unmap_mutex);
xe_vm_put(vm);
} elseif (xe_vma_is_null(vma) || xe_vma_is_cpu_addr_mirror(vma)) {
xe_vm_put(vm);
} else {
xe_bo_put(xe_vma_bo(vma));
}
/** * xe_vm_lock_vma() - drm_exec utility to lock a vma * @exec: The drm_exec object we're currently locking for. * @vma: The vma for witch we want to lock the vm resv and any attached * object's resv. * * Return: 0 on success, negative error code on error. In particular * may return -EDEADLK on WW transaction contention and -EINTR if * an interruptible wait is terminated by a signal.
*/ int xe_vm_lock_vma(struct drm_exec *exec, struct xe_vma *vma)
{ struct xe_vm *vm = xe_vma_vm(vma); struct xe_bo *bo = xe_vma_bo(vma); int err;
XE_WARN_ON(!vm);
err = drm_exec_lock_obj(exec, xe_vm_obj(vm)); if (!err && bo && !bo->vm)
err = drm_exec_lock_obj(exec, &bo->ttm.base);
return err;
}
staticvoid xe_vma_destroy_unlocked(struct xe_vma *vma)
{ struct drm_exec exec; int err;
/* * We only have two bits to encode the PAT index in non-leaf nodes, but * these only point to other paging structures so we only need a minimal * selection of options. The user PAT index is only for encoding leaf * nodes, where we have use of more bits to do the encoding. The * non-leaf nodes are instead under driver control so the chosen index * here should be distict from the user PAT index. Also the * corresponding coherency of the PAT index should be tied to the * allocation type of the page table (or at least we should pick * something which is always safe).
*/ if (!xe_bo_is_vram(bo) && bo->ttm.ttm->caching == ttm_cached)
pat_index = xe->pat.idx[XE_CACHE_WB]; else
pat_index = xe->pat.idx[XE_CACHE_NONE];
/** * xe_vm_create_scratch() - Setup a scratch memory pagetable tree for the * given tile and vm. * @xe: xe device. * @tile: tile to set up for. * @vm: vm to set up for. * * Sets up a pagetable tree with one page-table per level and a single * leaf PTE. All pagetable entries point to the single page-table or, * for MAX_HUGEPTE_LEVEL, a NULL huge PTE returning 0 on read and * writes become NOPs. * * Return: 0 on success, negative error code on error.
*/ staticint xe_vm_create_scratch(struct xe_device *xe, struct xe_tile *tile, struct xe_vm *vm)
{
u8 id = tile->id; int i;
for (i = MAX_HUGEPTE_LEVEL; i < vm->pt_root[id]->level; i++) {
vm->scratch_pt[id][i] = xe_pt_create(vm, tile, i); if (IS_ERR(vm->scratch_pt[id][i])) { int err = PTR_ERR(vm->scratch_pt[id][i]);
/* * Since the GSCCS is not user-accessible, we don't expect a GSC VM to * ever be in faulting mode.
*/
xe_assert(xe, !((flags & XE_VM_FLAG_GSC) && (flags & XE_VM_FLAG_FAULT_MODE)));
vm = kzalloc(sizeof(*vm), GFP_KERNEL); if (!vm) return ERR_PTR(-ENOMEM);
if (xef)
vm->xef = xe_file_get(xef); /** * GSC VMs are kernel-owned, only used for PXP ops and can sometimes be * manipulated under the PXP mutex. However, the PXP mutex can be taken * under a user-VM lock when the PXP session is started at exec_queue * creation time. Those are different VMs and therefore there is no risk * of deadlock, but we need to tell lockdep that this is the case or it * will print a warning.
*/ if (flags & XE_VM_FLAG_GSC) { staticstruct lock_class_key gsc_vm_key;
/* * Long-running workloads are not protected by the scheduler references. * By design, run_job for long-running workloads returns NULL and the * scheduler drops all the references of it, hence protecting the VM * for this case is necessary.
*/ if (flags & XE_VM_FLAG_LR_MODE) {
INIT_WORK(&vm->preempt.rebind_work, preempt_rebind_work_func);
xe_pm_runtime_get_noresume(xe);
INIT_LIST_HEAD(&vm->preempt.pm_activate_link);
}
if (flags & XE_VM_FLAG_FAULT_MODE) {
err = xe_svm_init(vm); if (err) goto err_no_resv;
}
/* * All vm operations will add shared fences to resv. * The only exception is eviction for a shared object, * but even so, the unbind when evicted would still * install a fence to resv. Hence it's safe to * destroy the pagetables immediately.
*/
xe_vm_free_scratch(vm);
/* * 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 * the members safely without locking.
*/
list_for_each_entry_safe(vma, next_vma, &contested,
combined_links.destroy) {
list_del_init(&vma->combined_links.destroy);
xe_vma_destroy_unlocked(vma);
}
if (xe_vm_in_fault_mode(vm))
xe_svm_fini(vm);
up_write(&vm->lock);
down_write(&xe->usm.lock); if (vm->usm.asid) { void *lookup;
if (args->flags & DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE)
flags |= XE_VM_FLAG_SCRATCH_PAGE; if (args->flags & DRM_XE_VM_CREATE_FLAG_LR_MODE)
flags |= XE_VM_FLAG_LR_MODE; if (args->flags & DRM_XE_VM_CREATE_FLAG_FAULT_MODE)
flags |= XE_VM_FLAG_FAULT_MODE;
vm = xe_vm_create(xe, flags, xef); if (IS_ERR(vm)) return PTR_ERR(vm);
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_MEM) /* Warning: Security issue - never enable by default */
args->reserved[0] = xe_bo_main_addr(vm->pt_root[0]->bo, XE_PAGE_SIZE); #endif
/* user id alloc must always be last in ioctl to prevent UAF */
err = xa_alloc(&xef->vm.xa, &id, vm, xa_limit_32b, GFP_KERNEL); if (err) goto err_close_and_put;
/** * xe_vm_find_vma_by_addr() - Find a VMA by its address * * @vm: the xe_vm the vma belongs to * @page_addr: address to look up
*/ struct xe_vma *xe_vm_find_vma_by_addr(struct xe_vm *vm, u64 page_addr)
{ struct xe_vma *vma = NULL;
if (vm->usm.last_fault_vma) { /* Fast lookup */ if (vma_matches(vm->usm.last_fault_vma, page_addr))
vma = vm->usm.last_fault_vma;
} if (!vma)
vma = xe_vm_find_overlapping_vma(vm, page_addr, SZ_4K);
if (PTR_ERR(svm_range) == -ENOENT) {
u64 ret = xe_svm_find_vma_start(vm, addr, range_end, vma);
addr = ret == ULONG_MAX ? 0 : ret; if (addr) goto alloc_next_range; else goto print_op_label;
}
if (IS_ERR(svm_range)) {
err = PTR_ERR(svm_range); goto unwind_prefetch_ops;
}
if (xe_svm_range_validate(vm, svm_range, tile_mask, !!prefetch_region)) {
xe_svm_range_debug(svm_range, "PREFETCH - RANGE IS VALID"); goto check_next_range;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.