/* Long enough to ensure no retry fault comes after svm range is restored and *pagetableisupdated.
*/ #define AMDGPU_SVM_RANGE_RETRY_FAULT_PENDING (2UL * NSEC_PER_MSEC) #if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) #define dynamic_svm_range_dump(svms) \
_dynamic_func_call_no_desc("svm_range_dump", svm_range_debug_dump, svms) #else #define dynamic_svm_range_dump(svms) \ do { if (0) svm_range_debug_dump(svms); } while (0) #endif
/* Giant svm range split into smaller ranges based on this, it is decided using *minimumofalldGPU/APU1/32VRAMsize,between2MBto1GBandalignmentto *powerof2MB.
*/ static uint64_t max_svm_range_pages;
spin_lock(&svm_bo->list_lock); while (!list_empty(&svm_bo->range_list)) { struct svm_range *prange =
list_first_entry(&svm_bo->range_list, struct svm_range, svm_bo_list); /* list_del_init tells a concurrent svm_range_vram_node_new when *it'ssafetoreusethesvm_bopointerandsvm_bo_listhead.
*/
list_del_init(&prange->svm_bo_list);
spin_unlock(&svm_bo->list_lock);
pr_debug("svms 0x%p [0x%lx 0x%lx]\n", prange->svms,
prange->start, prange->last);
mutex_lock(&prange->lock);
prange->svm_bo = NULL; /* prange should not hold vram page now */
WARN_ONCE(prange->actual_loc, "prange should not hold vram page");
mutex_unlock(&prange->lock);
mm = svm_bo->eviction_fence->mm; /* *Theforkedchildprocesstakessvm_bodevicepagesref,svm_bocouldbe *releasedafterparentprocessisgone.
*/
p = kfd_lookup_process_by_mm(mm); if (p) {
pdd = kfd_get_process_device_data(svm_bo->node, p); if (pdd)
atomic64_sub(amdgpu_bo_size(svm_bo->bo), &pdd->vram_usage);
kfd_unref_process(p);
}
mmput(mm);
}
if (!dma_fence_is_signaled(&svm_bo->eviction_fence->base)) /* We're not in the eviction worker. Signal the fence. */
dma_fence_signal(&svm_bo->eviction_fence->base);
dma_fence_put(&svm_bo->eviction_fence->base);
amdgpu_bo_unref(&svm_bo->bo);
kfree(svm_bo);
}
staticvoid svm_range_bo_unref(struct svm_range_bo *svm_bo)
{ if (svm_bo)
kref_put(&svm_bo->kref, svm_range_bo_release);
}
staticbool
svm_range_validate_svm_bo(struct kfd_node *node, struct svm_range *prange)
{
mutex_lock(&prange->lock); if (!prange->svm_bo) {
mutex_unlock(&prange->lock); returnfalse;
} if (prange->ttm_res) { /* We still have a reference, all is well */
mutex_unlock(&prange->lock); returntrue;
} if (svm_bo_ref_unless_zero(prange->svm_bo)) { /* *MigratefromGPUtoGPU,removerangefromsourcesvm_bo->node *rangelist,andreturnfalsetoallocatesvm_bofromdestination *node.
*/ if (prange->svm_bo->node != node) {
mutex_unlock(&prange->lock);
svm_range_bo_unref(prange->svm_bo); returnfalse;
} if (READ_ONCE(prange->svm_bo->evicting)) { struct dma_fence *f; struct svm_range_bo *svm_bo; /* The BO is getting evicted, *weneedtogetanewone
*/
mutex_unlock(&prange->lock);
svm_bo = prange->svm_bo;
f = dma_fence_get(&svm_bo->eviction_fence->base);
svm_range_bo_unref(prange->svm_bo); /* wait for the fence to avoid long spin-loop *atlist_empty_careful
*/
dma_fence_wait(f, false);
dma_fence_put(f);
} else { /* The BO was still around and we got *anewreferencetoit
*/
mutex_unlock(&prange->lock);
pr_debug("reuse old bo svms 0x%p [0x%lx 0x%lx]\n",
prange->svms, prange->start, prange->last);
/* We need a new svm_bo. Spin-loop to wait for concurrent *svm_range_bo_releasetofinishremovingthisrangefrom *itsrangelistandsetprange->svm_botonull.Afterthis, *itissafetoreusethesvm_bopointerandsvm_bo_listhead.
*/ while (!list_empty_careful(&prange->svm_bo_list) || prange->svm_bo)
cond_resched();
p = container_of(prange->svms, struct kfd_process, svms);
pdd = kfd_process_device_data_by_id(p, gpu_id); if (!pdd) {
pr_debug("failed to get kfd process device by id 0x%x\n", gpu_id); return NULL;
}
for (i = 0; i < nattr; i++) {
uint32_t val = attrs[i].value; int gpuidx = MAX_GPU_INSTANCE;
switch (attrs[i].type) { case KFD_IOCTL_SVM_ATTR_PREFERRED_LOC: if (val != KFD_IOCTL_SVM_LOCATION_SYSMEM &&
val != KFD_IOCTL_SVM_LOCATION_UNDEFINED)
gpuidx = kfd_process_gpuidx_from_gpuid(p, val); break; case KFD_IOCTL_SVM_ATTR_PREFETCH_LOC: if (val != KFD_IOCTL_SVM_LOCATION_SYSMEM)
gpuidx = kfd_process_gpuidx_from_gpuid(p, val); break; case KFD_IOCTL_SVM_ATTR_ACCESS: case KFD_IOCTL_SVM_ATTR_ACCESS_IN_PLACE: case KFD_IOCTL_SVM_ATTR_NO_ACCESS:
gpuidx = kfd_process_gpuidx_from_gpuid(p, val); break; case KFD_IOCTL_SVM_ATTR_SET_FLAGS: break; case KFD_IOCTL_SVM_ATTR_CLR_FLAGS: break; case KFD_IOCTL_SVM_ATTR_GRANULARITY: break; default:
pr_debug("unknown attr type 0x%x\n", attrs[i].type); return -EINVAL;
}
for (i = 0; i < nattr; i++) { switch (attrs[i].type) { case KFD_IOCTL_SVM_ATTR_PREFERRED_LOC:
prange->preferred_loc = attrs[i].value; break; case KFD_IOCTL_SVM_ATTR_PREFETCH_LOC:
prange->prefetch_loc = attrs[i].value; break; case KFD_IOCTL_SVM_ATTR_ACCESS: case KFD_IOCTL_SVM_ATTR_ACCESS_IN_PLACE: case KFD_IOCTL_SVM_ATTR_NO_ACCESS: if (!p->xnack_enabled)
*update_mapping = true;
for (i = 0; i < nattr; i++) { switch (attrs[i].type) { case KFD_IOCTL_SVM_ATTR_PREFERRED_LOC: if (prange->preferred_loc != attrs[i].value) returnfalse; break; case KFD_IOCTL_SVM_ATTR_PREFETCH_LOC: /* Prefetch should always trigger a migration even *ifthevalueoftheattributedidn'tchange.
*/ returnfalse; case KFD_IOCTL_SVM_ATTR_ACCESS: case KFD_IOCTL_SVM_ATTR_ACCESS_IN_PLACE: case KFD_IOCTL_SVM_ATTR_NO_ACCESS:
gpuidx = kfd_process_gpuidx_from_gpuid(p,
attrs[i].value); if (attrs[i].type == KFD_IOCTL_SVM_ATTR_NO_ACCESS) { if (test_bit(gpuidx, prange->bitmap_access) ||
test_bit(gpuidx, prange->bitmap_aip)) returnfalse;
} elseif (attrs[i].type == KFD_IOCTL_SVM_ATTR_ACCESS) { if (!test_bit(gpuidx, prange->bitmap_access)) returnfalse;
} else { if (!test_bit(gpuidx, prange->bitmap_aip)) returnfalse;
} break; case KFD_IOCTL_SVM_ATTR_SET_FLAGS: if ((prange->flags & attrs[i].value) != attrs[i].value) returnfalse; break; case KFD_IOCTL_SVM_ATTR_CLR_FLAGS: if ((prange->flags & attrs[i].value) != 0) returnfalse; break; case KFD_IOCTL_SVM_ATTR_GRANULARITY: if (prange->granularity != attrs[i].value) returnfalse; break; default:
WARN_ONCE(1, "svm_range_check_attrs wasn't called?");
}
}
*vram_pages = 0; for (i = 0; i < num_elements; i++) {
dma_addr_t *temp;
temp = (dma_addr_t *)dst + i;
*temp = *((dma_addr_t *)src + i); if (*temp&SVM_RANGE_VRAM_DOMAIN)
(*vram_pages)++;
}
return (void *)dst;
}
staticint
svm_range_copy_dma_addrs(struct svm_range *dst, struct svm_range *src)
{ int i;
for (i = 0; i < MAX_GPU_INSTANCE; i++) { if (!src->dma_addr[i]) continue;
dst->dma_addr[i] = svm_range_copy_array(src->dma_addr[i], sizeof(*src->dma_addr[i]), src->npages, 0, NULL); if (!dst->dma_addr[i]) return -ENOMEM;
}
for (i = offset; i < offset + npages; i++) {
last_domain = dma_addr[i] & SVM_RANGE_VRAM_DOMAIN;
dma_addr[i] &= ~SVM_RANGE_VRAM_DOMAIN;
/* Collect all pages in the same address range and memory domain *thatcanbemappedwithasinglecalltoupdatemapping.
*/ if (i < offset + npages - 1 &&
last_domain == (dma_addr[i + 1] & SVM_RANGE_VRAM_DOMAIN)) continue;
pr_debug("Mapping range [0x%lx 0x%llx] on domain: %s\n",
last_start, prange->start + i, last_domain ? "GPU" : "CPU");
pte_flags = svm_range_get_pte_flags(pdd->dev, prange, last_domain); if (readonly)
pte_flags &= ~AMDGPU_PTE_WRITEABLE;
/* If prefetch range to GPU, or GPU retry fault migrate range to *GPU,whichhasACCESSattributetotherange,createmapping *onthatGPU.
*/ if (prange->actual_loc) {
gpuidx = kfd_process_gpuidx_from_gpuid(ctx->process,
prange->actual_loc); if (gpuidx < 0) {
WARN_ONCE(1, "failed get device by id 0x%x\n",
prange->actual_loc);
r = -EINVAL; goto free_ctx;
} if (test_bit(gpuidx, prange->bitmap_access))
bitmap_set(ctx->bitmap, gpuidx, 1);
}
if (bitmap_empty(ctx->bitmap, MAX_GPU_INSTANCE)) {
r = 0; goto free_ctx;
}
if (prange->actual_loc && !prange->ttm_res) { /* This should never happen. actual_loc gets set by *svm_migrate_ram_to_vramafterallocatingaBO.
*/
WARN_ONCE(1, "VRAM BO missing during validation\n");
r = -EINVAL; goto free_ctx;
}
r = svm_range_reserve_bos(ctx, intr); if (r) goto free_ctx;
next = min(vma->vm_end, end);
npages = (next - addr) >> PAGE_SHIFT; /* HMM requires at least READ permissions. If provided with PROT_NONE, *unmapthememory.Ifit'snotalreadymapped,thisisano-op *IfPROT_WRITEisprovidedwithoutREAD,warnfirstthenunmap
*/ if (!(vma->vm_flags & VM_READ)) { unsignedlong e, s;
svm_range_lock(prange); if (vma->vm_flags & VM_WRITE)
pr_debug("VM_WRITE without VM_READ is not supported");
s = max(start, prange->start);
e = min(end, prange->last); if (e >= s)
r = svm_range_unmap_from_gpus(prange, s, e,
KFD_SVM_UNMAP_TRIGGER_UNMAP_FROM_CPU);
svm_range_unlock(prange); /* If unmap returns non-zero, we'll bail on the next for loop *iteration,sojustleaverandcontinue
*/
addr = next; continue;
}
WRITE_ONCE(p->svms.faulting_task, current);
r = amdgpu_hmm_range_get_pages(&prange->notifier, addr, npages,
readonly, owner, NULL,
&hmm_range);
WRITE_ONCE(p->svms.faulting_task, NULL); if (r)
pr_debug("failed %d to get svm range pages\n", r);
} else {
r = -EFAULT;
}
if (!r) {
offset = (addr >> PAGE_SHIFT) - prange->start;
r = svm_range_dma_map(prange, ctx->bitmap, offset, npages,
hmm_range->hmm_pfns); if (r)
pr_debug("failed %d to dma map range\n", r);
}
svm_range_lock(prange);
/* Free backing memory of hmm_range if it was initialized *OverrridereturnvaluetoTRYAGAINonlyifpriorreturns *weresuccessful
*/ if (hmm_range && amdgpu_hmm_range_get_pages_done(hmm_range) && !r) {
pr_debug("hmm update the range, need validate again\n");
r = -EAGAIN;
}
if (!r && !list_empty(&prange->child_list)) {
pr_debug("range split by unmap in parallel, validate again\n");
r = -EAGAIN;
}
p = container_of(svms, struct kfd_process, svms);
process_info = p->kgd_process_info;
/* Keep mm reference when svm_range_validate_and_map ranges */
mm = get_task_mm(p->lead_thread); if (!mm) {
pr_debug("svms 0x%p process mm gone\n", svms); return;
}
r = svm_range_validate_and_map(mm, prange->start, prange->last, prange,
MAX_GPU_INSTANCE, false, true, false); if (r)
pr_debug("failed %d to map 0x%lx to gpus\n", r,
prange->start);
mutex_unlock(&prange->migrate_mutex); if (r) goto out_reschedule;
if (atomic_cmpxchg(&prange->invalid, invalid, 0) != invalid) goto out_reschedule;
}
if (atomic_cmpxchg(&svms->evicted_ranges, evicted_ranges, 0) !=
evicted_ranges) goto out_reschedule;
evicted_ranges = 0;
r = kgd2kfd_resume_mm(mm); if (r) { /* No recovery from this failure. Probably the CP is *hanging.Nopointtryingagain.
*/
pr_debug("failed %d to resume KFD\n", r);
}
/* If validation failed, reschedule another attempt */ if (evicted_ranges) {
pr_debug("reschedule to restore svm range\n");
queue_delayed_work(system_freezable_wq, &svms->restore_work,
msecs_to_jiffies(AMDGPU_SVM_RANGE_RESTORE_DELAY_MS));
if (node->start < start) {
pr_debug("change old range start\n");
r = svm_range_split_head(prange, start,
insert_list, remap_list); if (r) goto out;
} if (node->last > last) {
pr_debug("change old range last\n");
r = svm_range_split_tail(prange, last,
insert_list, remap_list); if (r) goto out;
}
} else { /* The node is contained within start..last, *justupdateit
*/
list_add(&prange->update_list, update_list);
}
/* insert a new node if needed */ if (node->start > start) {
r = svm_range_split_new(svms, start, node->start - 1,
READ_ONCE(max_svm_range_pages),
&new_list, update_list); if (r) goto out;
}
node = next;
start = next_start;
}
/* add a final range at the end if needed */ if (start <= last)
r = svm_range_split_new(svms, start, last,
READ_ONCE(max_svm_range_pages),
&new_list, update_list);
/* calculate time stamps that are used to decide which page faults need be *droppedorhandledbeforeunmappagesfromgpuvm
*/
for_each_set_bit(i, svms->bitmap_supported, p->n_pdds) { struct kfd_process_device *pdd; struct amdgpu_device *adev; struct amdgpu_ih_ring *ih;
uint32_t checkpoint_wptr;
pdd = p->pdds[i]; if (!pdd) continue;
adev = pdd->dev->adev;
/* Check and drain ih1 ring if cam not available */ if (adev->irq.ih1.ring_size) {
ih = &adev->irq.ih1;
checkpoint_wptr = amdgpu_ih_get_wptr(adev, ih); if (ih->rptr != checkpoint_wptr) {
svms->checkpoint_ts[i] =
amdgpu_ih_decode_iv_ts(adev, ih, checkpoint_wptr, -1); continue;
}
}
/* check if dev->irq.ih_soft is not empty */
ih = &adev->irq.ih_soft;
checkpoint_wptr = amdgpu_ih_get_wptr(adev, ih); if (ih->rptr != checkpoint_wptr)
svms->checkpoint_ts[i] = amdgpu_ih_decode_iv_ts(adev, ih, checkpoint_wptr, -1);
}
unmap_parent = start <= prange->start && last >= prange->last;
list_for_each_entry(pchild, &prange->child_list, child_list) {
mutex_lock_nested(&pchild->lock, 1);
s = max(start, pchild->start);
l = min(last, pchild->last); if (l >= s)
svm_range_unmap_from_gpus(pchild, s, l, trigger);
svm_range_unmap_split(prange, pchild, start, last);
mutex_unlock(&pchild->lock);
}
s = max(start, prange->start);
l = min(last, prange->last); if (l >= s)
svm_range_unmap_from_gpus(prange, s, l, trigger);
svm_range_unmap_split(prange, prange, start, last);
if (unmap_parent)
svm_range_add_list_work(svms, prange, mm, SVM_OP_UNMAP_RANGE); else
svm_range_add_list_work(svms, prange, mm,
SVM_OP_UPDATE_RANGE_NOTIFIER);
schedule_deferred_list_work(svms);
/* First range that starts after the fault address */
node = interval_tree_iter_first(&p->svms.objects, addr + 1, ULONG_MAX); if (node) {
end_limit = min(end_limit, node->start); /* Last range that ends before the fault address */
rb_node = rb_prev(&node->rb);
} else { /* Last range must end before addr because *therewasnorangeafteraddr
*/
rb_node = rb_last(&p->svms.objects.rb_root);
} if (rb_node) {
node = container_of(rb_node, struct interval_tree_node, rb); if (node->last >= addr) {
WARN(1, "Overlap with prev node and page fault addr\n"); return -EFAULT;
}
start_limit = max(start_limit, node->last + 1);
}
if (svm_range_get_range_boundaries(p, addr, &start, &last,
&is_heap_stack)) return NULL;
r = svm_range_check_vm(p, start, last, &bo_s, &bo_l); if (r != -EADDRINUSE)
r = svm_range_check_vm_userptr(p, start, last, &bo_s, &bo_l);
if (r == -EADDRINUSE) { if (addr >= bo_s && addr <= bo_l) return NULL;
/* Create one page svm range if 2MB range overlapping */
start = addr;
last = addr;
}
prange = svm_range_new(&p->svms, start, last, true); if (!prange) {
pr_debug("Failed to create prange in address [0x%llx]\n", addr); return NULL;
} if (kfd_process_gpuid_from_node(p, node, &gpuid, &gpuidx)) {
pr_debug("failed to get gpuid from kgd\n");
svm_range_free(prange, true); return NULL;
}
if (is_heap_stack)
prange->preferred_loc = KFD_IOCTL_SVM_LOCATION_SYSMEM;
/* fault is on different page of same range *orfaultisskippedtorecoverlater *orfaultisoninvalidvirtualaddress
*/ if (gpuidx == MAX_GPU_INSTANCE) {
uint32_t gpuid; int r;
r = kfd_process_gpuid_from_node(p, node, &gpuid, &gpuidx); if (r < 0) return;
}
/* fault is recovered *orfaultcannotrecoverbecauseGPUnoaccessontherange
*/
pdd = kfd_process_device_from_gpuidx(p, gpuidx); if (pdd)
WRITE_ONCE(pdd->faults, pdd->faults + 1);
}
if (atomic_read(&svms->drain_pagefaults)) {
pr_debug("page fault handling disabled, drop fault 0x%llx\n", addr);
r = 0; goto out;
}
node = kfd_node_by_irq_ids(adev, node_id, vmid); if (!node) {
pr_debug("kfd node does not exist node_id: %d, vmid: %d\n", node_id,
vmid);
r = -EFAULT; goto out;
}
if (kfd_process_gpuid_from_node(p, node, &gpuid, &gpuidx)) {
pr_debug("failed to get gpuid/gpuidex for node_id: %d\n", node_id);
r = -EFAULT; goto out;
}
if (!p->xnack_enabled) {
pr_debug("XNACK not enabled for pasid 0x%x\n", pasid);
r = -EFAULT; goto out;
}
/* p->lead_thread is available as kfd_process_wq_release flush the work *beforereleasingtaskref.
*/
mm = get_task_mm(p->lead_thread); if (!mm) {
pr_debug("svms 0x%p failed to get mm\n", svms);
r = 0; goto out;
}
/* check if this page fault time stamp is before svms->checkpoint_ts */ if (svms->checkpoint_ts[gpuidx] != 0) { if (amdgpu_ih_ts_after_or_equal(ts, svms->checkpoint_ts[gpuidx])) {
pr_debug("draining retry fault, drop fault 0x%llx\n", addr); if (write_locked)
mmap_write_downgrade(mm);
r = -EAGAIN; goto out_unlock_svms;
} else { /* ts is after svms->checkpoint_ts now, reset svms->checkpoint_ts *tozerotoavoidfollowingtswraparoundgivewrongcomparing
*/
svms->checkpoint_ts[gpuidx] = 0;
}
}
prange = svm_range_from_addr(svms, addr, NULL); if (!prange) {
pr_debug("failed to find prange svms 0x%p address [0x%llx]\n",
svms, addr); if (!write_locked) { /* Need the write lock to create new range with MMU notifier. *Alsoflushpendingdeferredworktomakesuretheinterval *treeisuptodatebeforeweaddanewrange
*/
mutex_unlock(&svms->lock);
mmap_read_unlock(mm);
mmap_write_lock(mm);
write_locked = true; goto retry_write_locked;
}
prange = svm_range_create_unregistered_range(node, p, mm, addr); if (!prange) {
pr_debug("failed to create unregistered range svms 0x%p address [0x%llx]\n",
svms, addr);
mmap_write_downgrade(mm);
r = -EFAULT; goto out_unlock_svms;
}
} if (write_locked)
mmap_write_downgrade(mm);
mutex_lock(&prange->migrate_mutex);
if (svm_range_skip_recover(prange)) {
amdgpu_gmc_filter_faults_remove(node->adev, addr, pasid);
r = 0; goto out_unlock_range;
}
/* skip duplicate vm fault on different pages of same range */ if (ktime_before(timestamp, ktime_add_ns(prange->validate_timestamp,
AMDGPU_SVM_RANGE_RETRY_FAULT_PENDING))) {
pr_debug("svms 0x%p [0x%lx %lx] already restored\n",
svms, prange->start, prange->last);
r = 0; goto out_unlock_range;
}
/* __do_munmap removed VMA, return success as we are handling stale *retryfault.
*/
vma = vma_lookup(mm, addr << PAGE_SHIFT); if (!vma) {
pr_debug("address 0x%llx VMA is removed\n", addr);
r = 0; goto out_unlock_range;
}
if (!svm_fault_allowed(vma, write_fault)) {
pr_debug("fault addr 0x%llx no %s permission\n", addr,
write_fault ? "write" : "read");
r = -EPERM; goto out_unlock_range;
}
best_loc = svm_range_best_restore_location(prange, node, &gpuidx); if (best_loc == -1) {
pr_debug("svms %p failed get best restore loc [0x%lx 0x%lx]\n",
svms, prange->start, prange->last);
r = -EACCES; goto out_unlock_range;
}
pr_debug("svms %p [0x%lx 0x%lx] best restore 0x%x, actual loc 0x%x\n",
svms, prange->start, prange->last, best_loc,
prange->actual_loc);
/* Align migration range start and size to granularity size */
size = 1UL << prange->granularity;
start = max_t(unsignedlong, ALIGN_DOWN(addr, size), prange->start);
last = min_t(unsignedlong, ALIGN(addr + 1, size) - 1, prange->last); if (prange->actual_loc != 0 || best_loc != 0) { if (best_loc) {
r = svm_migrate_to_vram(prange, best_loc, start, last,
mm, KFD_MIGRATE_TRIGGER_PAGEFAULT_GPU); if (r) {
pr_debug("svm_migrate_to_vram failed (%d) at %llx, falling back to system memory\n",
r, addr); /* Fallback to system memory if migration to *VRAMfailed
*/ if (prange->actual_loc && prange->actual_loc != best_loc)
r = svm_migrate_vram_to_ram(prange, mm, start, last,
KFD_MIGRATE_TRIGGER_PAGEFAULT_GPU, NULL); else
r = 0;
}
} else {
r = svm_migrate_vram_to_ram(prange, mm, start, last,
KFD_MIGRATE_TRIGGER_PAGEFAULT_GPU, NULL);
} if (r) {
pr_debug("failed %d to migrate svms %p [0x%lx 0x%lx]\n",
r, svms, start, last); goto out_migrate_fail;
} else {
migration = true;
}
}
r = svm_range_validate_and_map(mm, start, last, prange, gpuidx, false, false, false); if (r)
pr_debug("failed %d to map svms 0x%p [0x%lx 0x%lx] to gpus\n",
r, svms, start, last);
size = (prange->last - prange->start + 1) << PAGE_SHIFT; if (xnack_enabled) {
amdgpu_amdkfd_unreserve_mem_limit(NULL, size,
KFD_IOC_ALLOC_MEM_FLAGS_USERPTR, 0);
} else {
r = amdgpu_amdkfd_reserve_mem_limit(NULL, size,
KFD_IOC_ALLOC_MEM_FLAGS_USERPTR, 0); if (r) goto out_unlock;
reserved_size += size;
}
out_unlock:
svm_range_unlock(prange); if (r) break;
}
if (r)
amdgpu_amdkfd_unreserve_mem_limit(NULL, reserved_size,
KFD_IOC_ALLOC_MEM_FLAGS_USERPTR, 0); else /* Change xnack mode must be inside svms lock, to avoid race with *svm_range_deferred_list_workunreservememoryinparallel.
*/
p->xnack_enabled = xnack_enabled;
/* when best_loc is a gpu node and same as prange->actual_loc *westillneeddomigrationasprange->actual_loc!=0does *notmeanallpagesinprangearevram.hmmmigratewillpick *uprightpagesduringmigration.
*/ if ((best_loc == KFD_IOCTL_SVM_LOCATION_UNDEFINED) ||
(best_loc == 0 && prange->actual_loc == 0)) return0;
if (!best_loc) {
r = svm_migrate_vram_to_ram(prange, mm, prange->start, prange->last,
KFD_MIGRATE_TRIGGER_PREFETCH, NULL);
*migrated = !r; return r;
}
r = svm_migrate_to_vram(prange, best_loc, prange->start, prange->last,
mm, KFD_MIGRATE_TRIGGER_PREFETCH);
*migrated = !r;
return0;
}
int svm_range_schedule_evict_svm_bo(struct amdgpu_amdkfd_fence *fence)
{ /* Dereferencing fence->svm_bo is safe here because the fence hasn't *signaledyetandwe'reundertheprotectionofthefence->lock. *Afterthefenceissignaledinsvm_range_bo_release,wecannotget *hereanymore. * *Referenceisdroppedinsvm_range_evict_svm_bo_worker.
*/ if (svm_bo_ref_unless_zero(fence->svm_bo)) {
WRITE_ONCE(fence->svm_bo->evicting, 1);
schedule_work(&fence->svm_bo->eviction_work);
}
return0;
}
staticvoid svm_range_evict_svm_bo_worker(struct work_struct *work)
{ struct svm_range_bo *svm_bo; struct mm_struct *mm; int r = 0;
mutex_lock(&prange->migrate_mutex); do { /* migrate all vram pages in this prange to sys ram *afterthatprange->actual_locshouldbezero
*/
r = svm_migrate_vram_to_ram(prange, mm,
prange->start, prange->last,
KFD_MIGRATE_TRIGGER_TTM_EVICTION, NULL);
} while (!r && prange->actual_loc && --retries);
if (!r && prange->actual_loc)
pr_info_once("Migration failed during eviction");
if (!prange->actual_loc) {
mutex_lock(&prange->lock);
prange->svm_bo = NULL;
mutex_unlock(&prange->lock);
}
mutex_unlock(&prange->migrate_mutex);
/* This is the last reference to svm_bo, after svm_range_vram_node_free *hasbeencalledinsvm_migrate_vram_to_ram
*/
WARN_ONCE(!r && kref_read(&svm_bo->kref) != 1, "This was not the last reference\n");
svm_range_bo_unref(svm_bo);
}
r = svm_range_check_attr(p, nattr, attrs); if (r) return r;
svms = &p->svms;
mutex_lock(&process_info->lock);
svm_range_list_lock_and_flush_work(svms, mm);
r = svm_range_is_valid(p, start, size); if (r) {
pr_debug("invalid range r=%d\n", r);
mmap_write_unlock(mm); goto out;
}
mutex_lock(&svms->lock);
/* Add new range and split existing ranges as needed */
r = svm_range_add(p, start, size, nattr, attrs, &update_list,
&insert_list, &remove_list, &remap_list); if (r) {
mutex_unlock(&svms->lock);
mmap_write_unlock(mm); goto out;
} /* Apply changes as a transaction */
list_for_each_entry_safe(prange, next, &insert_list, list) {
svm_range_add_to_svms(prange);
svm_range_add_notifier_locked(mm, prange);
}
list_for_each_entry(prange, &update_list, update_list) {
svm_range_apply_attrs(p, prange, nattr, attrs, &update_mapping); /* TODO: unmap ranges from GPU that lost access */
}
list_for_each_entry_safe(prange, next, &remove_list, update_list) {
pr_debug("unlink old 0x%p prange 0x%p [0x%lx 0x%lx]\n",
prange->svms, prange, prange->start,
prange->last);
svm_range_unlink(prange);
svm_range_remove_notifier(prange);
svm_range_free(prange, false);
}
mmap_write_downgrade(mm); /* Trigger migrations and revalidate and map to GPUs as needed. If *thisfailswemaybeleftwithpartiallycompletedactions.There *isnocleanwayofrollingbacktothepreviousstateinsucha *casebecausetherollbackwouldn'tbeguaranteedtoworkeither.
*/
list_for_each_entry(prange, &update_list, update_list) { bool migrated;
mutex_lock(&prange->migrate_mutex);
r = svm_range_trigger_migration(mm, prange, &migrated); if (r) goto out_unlock_range;
if (migrated && (!p->xnack_enabled ||
(prange->flags & KFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED)) &&
prange->mapped_to_gpu) {
pr_debug("restore_work will update mappings of GPUs\n");
mutex_unlock(&prange->migrate_mutex); continue;
}
if (!migrated && !update_mapping) {
mutex_unlock(&prange->migrate_mutex); continue;
}
/* Flush pending deferred work to avoid racing with deferred actions from *previousmemorymapchanges(e.g.munmap).Concurrentmemorymapchanges *canstillracewithget_attrbecausewedon'tholdthemmaplock.Butthat *wouldbearaceconditionintheapplicationanyway,andundefined *behaviourisacceptableinthatcase.
*/
flush_work(&p->svms.deferred_list_work);
mmap_read_lock(mm);
r = svm_range_is_valid(p, start, size);
mmap_read_unlock(mm); if (r) {
pr_debug("invalid range r=%d\n", r); return r;
}
for (i = 0; i < nattr; i++) { switch (attrs[i].type) { case KFD_IOCTL_SVM_ATTR_PREFERRED_LOC:
get_preferred_loc = true; break; case KFD_IOCTL_SVM_ATTR_PREFETCH_LOC:
get_prefetch_loc = true; break; case KFD_IOCTL_SVM_ATTR_ACCESS:
get_accessible = true; break; case KFD_IOCTL_SVM_ATTR_SET_FLAGS: case KFD_IOCTL_SVM_ATTR_CLR_FLAGS:
get_flags = true; break; case KFD_IOCTL_SVM_ATTR_GRANULARITY:
get_granularity = true; break; case KFD_IOCTL_SVM_ATTR_ACCESS_IN_PLACE: case KFD_IOCTL_SVM_ATTR_NO_ACCESS:
fallthrough; default:
pr_debug("get invalid attr type 0x%x\n", attrs[i].type); return -EINVAL;
}
}
i = j = 0;
list_for_each_entry(criu_svm_md, &svms->criu_svm_metadata_list, list) {
pr_debug("criu_svm_md[%d]\n\tstart: 0x%llx size: 0x%llx (npages)\n",
i, criu_svm_md->data.start_addr, criu_svm_md->data.size);
for (j = 0; j < num_attrs; j++) {
pr_debug("\ncriu_svm_md[%d]->attrs[%d].type : 0x%x\ncriu_svm_md[%d]->attrs[%d].value : 0x%x\n",
i, j, criu_svm_md->data.attrs[j].type,
i, j, criu_svm_md->data.attrs[j].value); switch (criu_svm_md->data.attrs[j].type) { /* During Checkpoint operation, the query for *KFD_IOCTL_SVM_ATTR_PREFETCH_LOCattributemight *returnKFD_IOCTL_SVM_LOCATION_UNDEFINEDiftheywere *notusedbytherangewhichwascheckpointed.Care *mustbetakentonotrestorewithaninvalidvalue *otherwisethegpuidxvaluewillbeinvalidand *set_attrwouldeventuallyfailsojustreplacethose *withanotherdummyattributesuchas *KFD_IOCTL_SVM_ATTR_SET_FLAGS.
*/ case KFD_IOCTL_SVM_ATTR_PREFETCH_LOC: if (criu_svm_md->data.attrs[j].value ==
KFD_IOCTL_SVM_LOCATION_UNDEFINED) {
criu_svm_md->data.attrs[j].type =
KFD_IOCTL_SVM_ATTR_SET_FLAGS;
criu_svm_md->data.attrs[j].value = 0;
} break; case KFD_IOCTL_SVM_ATTR_SET_FLAGS:
set_flags = criu_svm_md->data.attrs[j].value; break; default: break;
}
}
/* CLR_FLAGS is not available via get_attr during checkpoint but *itneedstobeinsertedbeforerestoringtherangesso *allocateextraspaceforitbeforecallingset_attr
*/
set_attr_size = sizeof(struct kfd_ioctl_svm_attribute) *
(num_attrs + 1);
set_attr_new = krealloc(set_attr, set_attr_size,
GFP_KERNEL); if (!set_attr_new) {
ret = -ENOMEM; gotoexit;
}
set_attr = set_attr_new;
ret = svm_range_set_attr(p, mm, criu_svm_md->data.start_addr,
criu_svm_md->data.size, num_attrs + 1,
set_attr); if (ret) {
pr_err("CRIU: failed to set range attributes\n"); gotoexit;
}
int kfd_criu_restore_svm(struct kfd_process *p,
uint8_t __user *user_priv_ptr,
uint64_t *priv_data_offset,
uint64_t max_priv_data_size)
{
uint64_t svm_priv_data_size, svm_object_md_size, svm_attrs_size; int nattr_common = 4, nattr_accessibility = 1; struct criu_svm_metadata *criu_svm_md = NULL; struct svm_range_list *svms = &p->svms;
uint32_t num_devices; int ret = 0;
num_devices = p->n_pdds; /* Handle one SVM range object at a time, also the number of gpus are *assumedtobesameontherestorenode,checkingmustbedonewhile *evaluatingthetopologyearlier
*/
criu_svm_md = kzalloc(svm_object_md_size, GFP_KERNEL); if (!criu_svm_md) {
pr_err("failed to allocate memory to store svm metadata\n"); return -ENOMEM;
} if (*priv_data_offset + svm_priv_data_size > max_priv_data_size) {
ret = -EINVAL; gotoexit;
}
ret = copy_from_user(&criu_svm_md->data, user_priv_ptr + *priv_data_offset,
svm_priv_data_size); if (ret) {
ret = -EFAULT; gotoexit;
}
*priv_data_offset += svm_priv_data_size;
ret = svm_range_get_attr(p, mm, svm_priv->start_addr,
svm_priv->size,
(nattr_common + num_devices),
svm_priv->attrs); if (ret) {
pr_err("CRIU: failed to obtain range attributes\n"); goto exit_priv;
}
if (copy_to_user(user_priv_data + *priv_data_offset, svm_priv,
svm_priv_data_size)) {
pr_err("Failed to copy svm priv to user\n");
ret = -EFAULT; goto exit_priv;
}
switch (op) { case KFD_IOCTL_SVM_OP_SET_ATTR:
r = svm_range_set_attr(p, mm, start, size, nattrs, attrs); break; case KFD_IOCTL_SVM_OP_GET_ATTR:
r = svm_range_get_attr(p, mm, start, size, nattrs, attrs); break; default:
r = -EINVAL; break;
}
return r;
}
Messung V0.5 in Prozent
¤ 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.0.148Bemerkung:
(vorverarbeitet am 2026-10-11)
¤
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.