Skip to content

Commit 0cafc62

Browse files
Ralph Campbelljgunthorpe
authored andcommitted
nouveau/hmm: fault one page at a time
The SVM page fault handler groups faults into a range of contiguous virtual addresses and requests hmm_range_fault() to populate and return the page frame number of system memory mapped by the CPU. In preparation for supporting large pages to be mapped by the GPU, process faults one page at a time. In addition, use the hmm_range default_flags to fix a corner case where the input hmm_pfns array is not reinitialized after hmm_range_fault() returns -EBUSY and must be called again. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Ralph Campbell <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent e478425 commit 0cafc62

File tree

1 file changed

+66
-133
lines changed

1 file changed

+66
-133
lines changed

drivers/gpu/drm/nouveau/nouveau_svm.c

Lines changed: 66 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ static const struct mmu_interval_notifier_ops nouveau_svm_mni_ops = {
516516
static void nouveau_hmm_convert_pfn(struct nouveau_drm *drm,
517517
struct hmm_range *range, u64 *ioctl_addr)
518518
{
519-
unsigned long i, npages;
519+
struct page *page;
520520

521521
/*
522522
* The ioctl_addr prepared here is passed through nvif_object_ioctl()
@@ -525,42 +525,38 @@ static void nouveau_hmm_convert_pfn(struct nouveau_drm *drm,
525525
* This is all just encoding the internal hmm representation into a
526526
* different nouveau internal representation.
527527
*/
528-
npages = (range->end - range->start) >> PAGE_SHIFT;
529-
for (i = 0; i < npages; ++i) {
530-
struct page *page;
531-
532-
if (!(range->hmm_pfns[i] & HMM_PFN_VALID)) {
533-
ioctl_addr[i] = 0;
534-
continue;
535-
}
536-
537-
page = hmm_pfn_to_page(range->hmm_pfns[i]);
538-
if (is_device_private_page(page))
539-
ioctl_addr[i] = nouveau_dmem_page_addr(page) |
540-
NVIF_VMM_PFNMAP_V0_V |
541-
NVIF_VMM_PFNMAP_V0_VRAM;
542-
else
543-
ioctl_addr[i] = page_to_phys(page) |
544-
NVIF_VMM_PFNMAP_V0_V |
545-
NVIF_VMM_PFNMAP_V0_HOST;
546-
if (range->hmm_pfns[i] & HMM_PFN_WRITE)
547-
ioctl_addr[i] |= NVIF_VMM_PFNMAP_V0_W;
528+
if (!(range->hmm_pfns[0] & HMM_PFN_VALID)) {
529+
ioctl_addr[0] = 0;
530+
return;
548531
}
532+
533+
page = hmm_pfn_to_page(range->hmm_pfns[0]);
534+
if (is_device_private_page(page))
535+
ioctl_addr[0] = nouveau_dmem_page_addr(page) |
536+
NVIF_VMM_PFNMAP_V0_V |
537+
NVIF_VMM_PFNMAP_V0_VRAM;
538+
else
539+
ioctl_addr[0] = page_to_phys(page) |
540+
NVIF_VMM_PFNMAP_V0_V |
541+
NVIF_VMM_PFNMAP_V0_HOST;
542+
if (range->hmm_pfns[0] & HMM_PFN_WRITE)
543+
ioctl_addr[0] |= NVIF_VMM_PFNMAP_V0_W;
549544
}
550545

551546
static int nouveau_range_fault(struct nouveau_svmm *svmm,
552547
struct nouveau_drm *drm, void *data, u32 size,
553-
unsigned long hmm_pfns[], u64 *ioctl_addr,
548+
u64 *ioctl_addr, unsigned long hmm_flags,
554549
struct svm_notifier *notifier)
555550
{
556551
unsigned long timeout =
557552
jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
558553
/* Have HMM fault pages within the fault window to the GPU. */
554+
unsigned long hmm_pfns[1];
559555
struct hmm_range range = {
560556
.notifier = &notifier->notifier,
561557
.start = notifier->notifier.interval_tree.start,
562558
.end = notifier->notifier.interval_tree.last + 1,
563-
.pfn_flags_mask = HMM_PFN_REQ_FAULT | HMM_PFN_REQ_WRITE,
559+
.default_flags = hmm_flags,
564560
.hmm_pfns = hmm_pfns,
565561
};
566562
struct mm_struct *mm = notifier->notifier.mm;
@@ -575,11 +571,6 @@ static int nouveau_range_fault(struct nouveau_svmm *svmm,
575571
ret = hmm_range_fault(&range);
576572
mmap_read_unlock(mm);
577573
if (ret) {
578-
/*
579-
* FIXME: the input PFN_REQ flags are destroyed on
580-
* -EBUSY, we need to regenerate them, also for the
581-
* other continue below
582-
*/
583574
if (ret == -EBUSY)
584575
continue;
585576
return ret;
@@ -614,17 +605,12 @@ nouveau_svm_fault(struct nvif_notify *notify)
614605
struct nvif_object *device = &svm->drm->client.device.object;
615606
struct nouveau_svmm *svmm;
616607
struct {
617-
struct {
618-
struct nvif_ioctl_v0 i;
619-
struct nvif_ioctl_mthd_v0 m;
620-
struct nvif_vmm_pfnmap_v0 p;
621-
} i;
622-
u64 phys[16];
608+
struct nouveau_pfnmap_args i;
609+
u64 phys[1];
623610
} args;
624-
unsigned long hmm_pfns[ARRAY_SIZE(args.phys)];
625-
struct vm_area_struct *vma;
611+
unsigned long hmm_flags;
626612
u64 inst, start, limit;
627-
int fi, fn, pi, fill;
613+
int fi, fn;
628614
int replay = 0, ret;
629615

630616
/* Parse available fault buffer entries into a cache, and update
@@ -691,128 +677,75 @@ nouveau_svm_fault(struct nvif_notify *notify)
691677
* window into a single update.
692678
*/
693679
start = buffer->fault[fi]->addr;
694-
limit = start + (ARRAY_SIZE(args.phys) << PAGE_SHIFT);
680+
limit = start + PAGE_SIZE;
695681
if (start < svmm->unmanaged.limit)
696682
limit = min_t(u64, limit, svmm->unmanaged.start);
697-
SVMM_DBG(svmm, "wndw %016llx-%016llx", start, limit);
698683

699-
mm = svmm->notifier.mm;
700-
if (!mmget_not_zero(mm)) {
701-
nouveau_svm_fault_cancel_fault(svm, buffer->fault[fi]);
702-
continue;
703-
}
704-
705-
/* Intersect fault window with the CPU VMA, cancelling
706-
* the fault if the address is invalid.
684+
/*
685+
* Prepare the GPU-side update of all pages within the
686+
* fault window, determining required pages and access
687+
* permissions based on pending faults.
707688
*/
708-
mmap_read_lock(mm);
709-
vma = find_vma_intersection(mm, start, limit);
710-
if (!vma) {
711-
SVMM_ERR(svmm, "wndw %016llx-%016llx", start, limit);
712-
mmap_read_unlock(mm);
713-
mmput(mm);
714-
nouveau_svm_fault_cancel_fault(svm, buffer->fault[fi]);
715-
continue;
689+
args.i.p.addr = start;
690+
args.i.p.page = PAGE_SHIFT;
691+
args.i.p.size = PAGE_SIZE;
692+
/*
693+
* Determine required permissions based on GPU fault
694+
* access flags.
695+
* XXX: atomic?
696+
*/
697+
switch (buffer->fault[fi]->access) {
698+
case 0: /* READ. */
699+
hmm_flags = HMM_PFN_REQ_FAULT;
700+
break;
701+
case 3: /* PREFETCH. */
702+
hmm_flags = 0;
703+
break;
704+
default:
705+
hmm_flags = HMM_PFN_REQ_FAULT | HMM_PFN_REQ_WRITE;
706+
break;
716707
}
717-
start = max_t(u64, start, vma->vm_start);
718-
limit = min_t(u64, limit, vma->vm_end);
719-
mmap_read_unlock(mm);
720-
SVMM_DBG(svmm, "wndw %016llx-%016llx", start, limit);
721708

722-
if (buffer->fault[fi]->addr != start) {
723-
SVMM_ERR(svmm, "addr %016llx", buffer->fault[fi]->addr);
724-
mmput(mm);
709+
mm = svmm->notifier.mm;
710+
if (!mmget_not_zero(mm)) {
725711
nouveau_svm_fault_cancel_fault(svm, buffer->fault[fi]);
726712
continue;
727713
}
728714

729-
/* Prepare the GPU-side update of all pages within the
730-
* fault window, determining required pages and access
731-
* permissions based on pending faults.
732-
*/
733-
args.i.p.page = PAGE_SHIFT;
734-
args.i.p.addr = start;
735-
for (fn = fi, pi = 0;;) {
736-
/* Determine required permissions based on GPU fault
737-
* access flags.
738-
*XXX: atomic?
739-
*/
740-
switch (buffer->fault[fn]->access) {
741-
case 0: /* READ. */
742-
hmm_pfns[pi++] = HMM_PFN_REQ_FAULT;
743-
break;
744-
case 3: /* PREFETCH. */
745-
hmm_pfns[pi++] = 0;
746-
break;
747-
default:
748-
hmm_pfns[pi++] = HMM_PFN_REQ_FAULT |
749-
HMM_PFN_REQ_WRITE;
750-
break;
751-
}
752-
args.i.p.size = pi << PAGE_SHIFT;
715+
notifier.svmm = svmm;
716+
ret = mmu_interval_notifier_insert(&notifier.notifier, mm,
717+
args.i.p.addr, args.i.p.size,
718+
&nouveau_svm_mni_ops);
719+
if (!ret) {
720+
ret = nouveau_range_fault(svmm, svm->drm, &args,
721+
sizeof(args), args.phys, hmm_flags, &notifier);
722+
mmu_interval_notifier_remove(&notifier.notifier);
723+
}
724+
mmput(mm);
753725

726+
for (fn = fi; ++fn < buffer->fault_nr; ) {
754727
/* It's okay to skip over duplicate addresses from the
755728
* same SVMM as faults are ordered by access type such
756729
* that only the first one needs to be handled.
757730
*
758731
* ie. WRITE faults appear first, thus any handling of
759732
* pending READ faults will already be satisfied.
760733
*/
761-
while (++fn < buffer->fault_nr &&
762-
buffer->fault[fn]->svmm == svmm &&
763-
buffer->fault[fn ]->addr ==
764-
buffer->fault[fn - 1]->addr);
765-
766-
/* If the next fault is outside the window, or all GPU
767-
* faults have been dealt with, we're done here.
768-
*/
769-
if (fn >= buffer->fault_nr ||
770-
buffer->fault[fn]->svmm != svmm ||
734+
if (buffer->fault[fn]->svmm != svmm ||
771735
buffer->fault[fn]->addr >= limit)
772736
break;
773-
774-
/* Fill in the gap between this fault and the next. */
775-
fill = (buffer->fault[fn ]->addr -
776-
buffer->fault[fn - 1]->addr) >> PAGE_SHIFT;
777-
while (--fill)
778-
hmm_pfns[pi++] = 0;
779737
}
780738

781-
SVMM_DBG(svmm, "wndw %016llx-%016llx covering %d fault(s)",
782-
args.i.p.addr,
783-
args.i.p.addr + args.i.p.size, fn - fi);
784-
785-
notifier.svmm = svmm;
786-
ret = mmu_interval_notifier_insert(&notifier.notifier,
787-
svmm->notifier.mm,
788-
args.i.p.addr, args.i.p.size,
789-
&nouveau_svm_mni_ops);
790-
if (!ret) {
791-
ret = nouveau_range_fault(
792-
svmm, svm->drm, &args,
793-
sizeof(args.i) + pi * sizeof(args.phys[0]),
794-
hmm_pfns, args.phys, &notifier);
795-
mmu_interval_notifier_remove(&notifier.notifier);
796-
}
797-
mmput(mm);
739+
/* If handling failed completely, cancel all faults. */
740+
if (ret) {
741+
while (fi < fn) {
742+
struct nouveau_svm_fault *fault =
743+
buffer->fault[fi++];
798744

799-
/* Cancel any faults in the window whose pages didn't manage
800-
* to keep their valid bit, or stay writeable when required.
801-
*
802-
* If handling failed completely, cancel all faults.
803-
*/
804-
while (fi < fn) {
805-
struct nouveau_svm_fault *fault = buffer->fault[fi++];
806-
pi = (fault->addr - args.i.p.addr) >> PAGE_SHIFT;
807-
if (ret ||
808-
!(args.phys[pi] & NVIF_VMM_PFNMAP_V0_V) ||
809-
(!(args.phys[pi] & NVIF_VMM_PFNMAP_V0_W) &&
810-
fault->access != 0 && fault->access != 3)) {
811745
nouveau_svm_fault_cancel_fault(svm, fault);
812-
continue;
813746
}
747+
} else
814748
replay++;
815-
}
816749
}
817750

818751
/* Issue fault replay to the GPU. */

0 commit comments

Comments
 (0)