Commit b223555d authored by Ralph Campbell's avatar Ralph Campbell Committed by Jason Gunthorpe
Browse files

nouveau/hmm: support mapping large sysmem pages

Nouveau currently only supports mapping PAGE_SIZE sized pages of system
memory when shared virtual memory (SVM) is enabled. Use the new
hmm_pfn_to_map_order() function to support mapping system memory pages
that are PMD_SIZE.

Link: https://lore.kernel.org/r/20200701225352.9649-5-rcampbell@nvidia.com


Signed-off-by: default avatarRalph Campbell <rcampbell@nvidia.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
parent 4725c6b8
Loading
Loading
Loading
Loading
+40 −13
Original line number Diff line number Diff line
@@ -514,38 +514,57 @@ static const struct mmu_interval_notifier_ops nouveau_svm_mni_ops = {
};

static void nouveau_hmm_convert_pfn(struct nouveau_drm *drm,
				    struct hmm_range *range, u64 *ioctl_addr)
				    struct hmm_range *range,
				    struct nouveau_pfnmap_args *args)
{
	struct page *page;

	/*
	 * The ioctl_addr prepared here is passed through nvif_object_ioctl()
	 * The address prepared here is passed through nvif_object_ioctl()
	 * to an eventual DMA map in something like gp100_vmm_pgt_pfn()
	 *
	 * This is all just encoding the internal hmm representation into a
	 * different nouveau internal representation.
	 */
	if (!(range->hmm_pfns[0] & HMM_PFN_VALID)) {
		ioctl_addr[0] = 0;
		args->p.phys[0] = 0;
		return;
	}

	page = hmm_pfn_to_page(range->hmm_pfns[0]);
	/*
	 * Only map compound pages to the GPU if the CPU is also mapping the
	 * page as a compound page. Otherwise, the PTE protections might not be
	 * consistent (e.g., CPU only maps part of a compound page).
	 * Note that the underlying page might still be larger than the
	 * CPU mapping (e.g., a PUD sized compound page partially mapped with
	 * a PMD sized page table entry).
	 */
	if (hmm_pfn_to_map_order(range->hmm_pfns[0])) {
		unsigned long addr = args->p.addr;

		args->p.page = hmm_pfn_to_map_order(range->hmm_pfns[0]) +
				PAGE_SHIFT;
		args->p.size = 1UL << args->p.page;
		args->p.addr &= ~(args->p.size - 1);
		page -= (addr - args->p.addr) >> PAGE_SHIFT;
	}
	if (is_device_private_page(page))
		ioctl_addr[0] = nouveau_dmem_page_addr(page) |
		args->p.phys[0] = nouveau_dmem_page_addr(page) |
				NVIF_VMM_PFNMAP_V0_V |
				NVIF_VMM_PFNMAP_V0_VRAM;
	else
		ioctl_addr[0] = page_to_phys(page) |
		args->p.phys[0] = page_to_phys(page) |
				NVIF_VMM_PFNMAP_V0_V |
				NVIF_VMM_PFNMAP_V0_HOST;
	if (range->hmm_pfns[0] & HMM_PFN_WRITE)
		ioctl_addr[0] |= NVIF_VMM_PFNMAP_V0_W;
		args->p.phys[0] |= NVIF_VMM_PFNMAP_V0_W;
}

static int nouveau_range_fault(struct nouveau_svmm *svmm,
			       struct nouveau_drm *drm, void *data, u32 size,
			       u64 *ioctl_addr, unsigned long hmm_flags,
			       struct nouveau_drm *drm,
			       struct nouveau_pfnmap_args *args, u32 size,
			       unsigned long hmm_flags,
			       struct svm_notifier *notifier)
{
	unsigned long timeout =
@@ -585,10 +604,10 @@ static int nouveau_range_fault(struct nouveau_svmm *svmm,
		break;
	}

	nouveau_hmm_convert_pfn(drm, &range, ioctl_addr);
	nouveau_hmm_convert_pfn(drm, &range, args);

	svmm->vmm->vmm.object.client->super = true;
	ret = nvif_object_ioctl(&svmm->vmm->vmm.object, data, size, NULL);
	ret = nvif_object_ioctl(&svmm->vmm->vmm.object, args, size, NULL);
	svmm->vmm->vmm.object.client->super = false;
	mutex_unlock(&svmm->mutex);

@@ -717,12 +736,13 @@ nouveau_svm_fault(struct nvif_notify *notify)
						   args.i.p.addr, args.i.p.size,
						   &nouveau_svm_mni_ops);
		if (!ret) {
			ret = nouveau_range_fault(svmm, svm->drm, &args,
				sizeof(args), args.phys, hmm_flags, &notifier);
			ret = nouveau_range_fault(svmm, svm->drm, &args.i,
				sizeof(args), hmm_flags, &notifier);
			mmu_interval_notifier_remove(&notifier.notifier);
		}
		mmput(mm);

		limit = args.i.p.addr + args.i.p.size;
		for (fn = fi; ++fn < buffer->fault_nr; ) {
			/* It's okay to skip over duplicate addresses from the
			 * same SVMM as faults are ordered by access type such
@@ -730,9 +750,16 @@ nouveau_svm_fault(struct nvif_notify *notify)
			 *
			 * ie. WRITE faults appear first, thus any handling of
			 * pending READ faults will already be satisfied.
			 * But if a large page is mapped, make sure subsequent
			 * fault addresses have sufficient access permission.
			 */
			if (buffer->fault[fn]->svmm != svmm ||
			    buffer->fault[fn]->addr >= limit)
			    buffer->fault[fn]->addr >= limit ||
			    (buffer->fault[fi]->access == 0 /* READ. */ &&
			     !(args.phys[0] & NVIF_VMM_PFNMAP_V0_V)) ||
			    (buffer->fault[fi]->access != 0 /* READ. */ &&
			     buffer->fault[fi]->access != 3 /* PREFETCH. */ &&
			     !(args.phys[0] & NVIF_VMM_PFNMAP_V0_W)))
				break;
		}