Commit 0d979509 authored by Jason Gunthorpe's avatar Jason Gunthorpe Committed by Daniel Vetter
Browse files

drm/ttm: remove ttm_bo_vm_insert_huge()



The huge page functionality in TTM does not work safely because PUD and
PMD entries do not have a special bit.

get_user_pages_fast() considers any page that passed pmd_huge() as
usable:

	if (unlikely(pmd_trans_huge(pmd) || pmd_huge(pmd) ||
		     pmd_devmap(pmd))) {

And vmf_insert_pfn_pmd_prot() unconditionally sets

	entry = pmd_mkhuge(pfn_t_pmd(pfn, prot));

eg on x86 the page will be _PAGE_PRESENT | PAGE_PSE.

As such gup_huge_pmd() will try to deref a struct page:

	head = try_grab_compound_head(pmd_page(orig), refs, flags);

and thus crash.

Thomas further notices that the drivers are not expecting the struct page
to be used by anything - in particular the refcount incr above will cause
them to malfunction.

Thus everything about this is not able to fully work correctly considering
GUP_fast. Delete it entirely. It can return someday along with a proper
PMD/PUD_SPECIAL bit in the page table itself to gate GUP_fast.

Fixes: 314b6580 ("drm/ttm, drm/vmwgfx: Support huge TTM pagefaults")
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Reviewed-by: default avatarThomas Hellström <thomas.helllstrom@linux.intel.com>
Reviewed-by: default avatarChristian König <christian.koenig@amd.com>
[danvet: Update subject per Thomas' &Christian's review]
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/0-v2-a44694790652+4ac-ttm_pmd_jgg@nvidia.com
parent ff2d2384
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -61,7 +61,7 @@ static vm_fault_t amdgpu_gem_fault(struct vm_fault *vmf)
		}

		 ret = ttm_bo_vm_fault_reserved(vmf, vmf->vma->vm_page_prot,
						TTM_BO_VM_NUM_PREFAULT, 1);
						TTM_BO_VM_NUM_PREFAULT);

		 drm_dev_exit(idx);
	} else {
+1 −1
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ static vm_fault_t nouveau_ttm_fault(struct vm_fault *vmf)

	nouveau_bo_del_io_reserve_lru(bo);
	prot = vm_get_page_prot(vma->vm_flags);
	ret = ttm_bo_vm_fault_reserved(vmf, prot, TTM_BO_VM_NUM_PREFAULT, 1);
	ret = ttm_bo_vm_fault_reserved(vmf, prot, TTM_BO_VM_NUM_PREFAULT);
	nouveau_bo_add_io_reserve_lru(bo);
	if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT))
		return ret;
+1 −1
Original line number Diff line number Diff line
@@ -61,7 +61,7 @@ static vm_fault_t radeon_gem_fault(struct vm_fault *vmf)
		goto unlock_resv;

	ret = ttm_bo_vm_fault_reserved(vmf, vmf->vma->vm_page_prot,
				       TTM_BO_VM_NUM_PREFAULT, 1);
				       TTM_BO_VM_NUM_PREFAULT);
	if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT))
		goto unlock_mclk;

+2 −92
Original line number Diff line number Diff line
@@ -173,89 +173,6 @@ vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo,
}
EXPORT_SYMBOL(ttm_bo_vm_reserve);

#ifdef CONFIG_TRANSPARENT_HUGEPAGE
/**
 * ttm_bo_vm_insert_huge - Insert a pfn for PUD or PMD faults
 * @vmf: Fault data
 * @bo: The buffer object
 * @page_offset: Page offset from bo start
 * @fault_page_size: The size of the fault in pages.
 * @pgprot: The page protections.
 * Does additional checking whether it's possible to insert a PUD or PMD
 * pfn and performs the insertion.
 *
 * Return: VM_FAULT_NOPAGE on successful insertion, VM_FAULT_FALLBACK if
 * a huge fault was not possible, or on insertion error.
 */
static vm_fault_t ttm_bo_vm_insert_huge(struct vm_fault *vmf,
					struct ttm_buffer_object *bo,
					pgoff_t page_offset,
					pgoff_t fault_page_size,
					pgprot_t pgprot)
{
	pgoff_t i;
	vm_fault_t ret;
	unsigned long pfn;
	pfn_t pfnt;
	struct ttm_tt *ttm = bo->ttm;
	bool write = vmf->flags & FAULT_FLAG_WRITE;

	/* Fault should not cross bo boundary. */
	page_offset &= ~(fault_page_size - 1);
	if (page_offset + fault_page_size > bo->resource->num_pages)
		goto out_fallback;

	if (bo->resource->bus.is_iomem)
		pfn = ttm_bo_io_mem_pfn(bo, page_offset);
	else
		pfn = page_to_pfn(ttm->pages[page_offset]);

	/* pfn must be fault_page_size aligned. */
	if ((pfn & (fault_page_size - 1)) != 0)
		goto out_fallback;

	/* Check that memory is contiguous. */
	if (!bo->resource->bus.is_iomem) {
		for (i = 1; i < fault_page_size; ++i) {
			if (page_to_pfn(ttm->pages[page_offset + i]) != pfn + i)
				goto out_fallback;
		}
	} else if (bo->bdev->funcs->io_mem_pfn) {
		for (i = 1; i < fault_page_size; ++i) {
			if (ttm_bo_io_mem_pfn(bo, page_offset + i) != pfn + i)
				goto out_fallback;
		}
	}

	pfnt = __pfn_to_pfn_t(pfn, PFN_DEV);
	if (fault_page_size == (HPAGE_PMD_SIZE >> PAGE_SHIFT))
		ret = vmf_insert_pfn_pmd_prot(vmf, pfnt, pgprot, write);
#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
	else if (fault_page_size == (HPAGE_PUD_SIZE >> PAGE_SHIFT))
		ret = vmf_insert_pfn_pud_prot(vmf, pfnt, pgprot, write);
#endif
	else
		WARN_ON_ONCE(ret = VM_FAULT_FALLBACK);

	if (ret != VM_FAULT_NOPAGE)
		goto out_fallback;

	return VM_FAULT_NOPAGE;
out_fallback:
	count_vm_event(THP_FAULT_FALLBACK);
	return VM_FAULT_FALLBACK;
}
#else
static vm_fault_t ttm_bo_vm_insert_huge(struct vm_fault *vmf,
					struct ttm_buffer_object *bo,
					pgoff_t page_offset,
					pgoff_t fault_page_size,
					pgprot_t pgprot)
{
	return VM_FAULT_FALLBACK;
}
#endif

/**
 * ttm_bo_vm_fault_reserved - TTM fault helper
 * @vmf: The struct vm_fault given as argument to the fault callback
@@ -263,7 +180,6 @@ static vm_fault_t ttm_bo_vm_insert_huge(struct vm_fault *vmf,
 * @num_prefault: Maximum number of prefault pages. The caller may want to
 * specify this based on madvice settings and the size of the GPU object
 * backed by the memory.
 * @fault_page_size: The size of the fault in pages.
 *
 * This function inserts one or more page table entries pointing to the
 * memory backing the buffer object, and then returns a return code
@@ -277,8 +193,7 @@ static vm_fault_t ttm_bo_vm_insert_huge(struct vm_fault *vmf,
 */
vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf,
				    pgprot_t prot,
				    pgoff_t num_prefault,
				    pgoff_t fault_page_size)
				    pgoff_t num_prefault)
{
	struct vm_area_struct *vma = vmf->vma;
	struct ttm_buffer_object *bo = vma->vm_private_data;
@@ -329,11 +244,6 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf,
		prot = pgprot_decrypted(prot);
	}

	/* We don't prefault on huge faults. Yet. */
	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && fault_page_size != 1)
		return ttm_bo_vm_insert_huge(vmf, bo, page_offset,
					     fault_page_size, prot);

	/*
	 * Speculatively prefault a number of pages. Only error on
	 * first page.
@@ -429,7 +339,7 @@ vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf)

	prot = vma->vm_page_prot;
	if (drm_dev_enter(ddev, &idx)) {
		ret = ttm_bo_vm_fault_reserved(vmf, prot, TTM_BO_VM_NUM_PREFAULT, 1);
		ret = ttm_bo_vm_fault_reserved(vmf, prot, TTM_BO_VM_NUM_PREFAULT);
		drm_dev_exit(idx);
	} else {
		ret = ttm_bo_vm_dummy_page(vmf, prot);
+0 −4
Original line number Diff line number Diff line
@@ -1550,10 +1550,6 @@ void vmw_bo_dirty_unmap(struct vmw_buffer_object *vbo,
			pgoff_t start, pgoff_t end);
vm_fault_t vmw_bo_vm_fault(struct vm_fault *vmf);
vm_fault_t vmw_bo_vm_mkwrite(struct vm_fault *vmf);
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
vm_fault_t vmw_bo_vm_huge_fault(struct vm_fault *vmf,
				enum page_entry_size pe_size);
#endif

/* Transparent hugepage support - vmwgfx_thp.c */
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
Loading