Skip to content

Commit ea9448b

Browse files
committed
Merge tag 'drm-next-2020-04-03-1' of git://anongit.freedesktop.org/drm/drm
Pull drm hugepage support from Dave Airlie: "This adds support for hugepages to TTM and has been tested with the vmwgfx drivers, though I expect other drivers to start using it" * tag 'drm-next-2020-04-03-1' of git://anongit.freedesktop.org/drm/drm: drm/vmwgfx: Hook up the helpers to align buffer objects drm/vmwgfx: Introduce a huge page aligning TTM range manager drm: Add a drm_get_unmapped_area() helper drm/vmwgfx: Support huge page faults drm/ttm, drm/vmwgfx: Support huge TTM pagefaults mm: Add vmf_insert_pfn_xxx_prot() for huge page-table entries mm: Split huge pages on write-notify or COW mm: Introduce vma_is_special_huge fs: Constify vma argument to vma_is_dax
2 parents 83eb69f + 0e7e619 commit ea9448b

File tree

16 files changed

+692
-28
lines changed

16 files changed

+692
-28
lines changed

drivers/gpu/drm/drm_file.c

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@
4848
#include "drm_internal.h"
4949
#include "drm_legacy.h"
5050

51+
#if defined(CONFIG_MMU) && defined(CONFIG_TRANSPARENT_HUGEPAGE)
52+
#include <uapi/asm/mman.h>
53+
#include <drm/drm_vma_manager.h>
54+
#endif
55+
5156
/* from BKL pushdown */
5257
DEFINE_MUTEX(drm_global_mutex);
5358

@@ -872,3 +877,139 @@ struct file *mock_drm_getfile(struct drm_minor *minor, unsigned int flags)
872877
return file;
873878
}
874879
EXPORT_SYMBOL_FOR_TESTS_ONLY(mock_drm_getfile);
880+
881+
#ifdef CONFIG_MMU
882+
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
883+
/*
884+
* drm_addr_inflate() attempts to construct an aligned area by inflating
885+
* the area size and skipping the unaligned start of the area.
886+
* adapted from shmem_get_unmapped_area()
887+
*/
888+
static unsigned long drm_addr_inflate(unsigned long addr,
889+
unsigned long len,
890+
unsigned long pgoff,
891+
unsigned long flags,
892+
unsigned long huge_size)
893+
{
894+
unsigned long offset, inflated_len;
895+
unsigned long inflated_addr;
896+
unsigned long inflated_offset;
897+
898+
offset = (pgoff << PAGE_SHIFT) & (huge_size - 1);
899+
if (offset && offset + len < 2 * huge_size)
900+
return addr;
901+
if ((addr & (huge_size - 1)) == offset)
902+
return addr;
903+
904+
inflated_len = len + huge_size - PAGE_SIZE;
905+
if (inflated_len > TASK_SIZE)
906+
return addr;
907+
if (inflated_len < len)
908+
return addr;
909+
910+
inflated_addr = current->mm->get_unmapped_area(NULL, 0, inflated_len,
911+
0, flags);
912+
if (IS_ERR_VALUE(inflated_addr))
913+
return addr;
914+
if (inflated_addr & ~PAGE_MASK)
915+
return addr;
916+
917+
inflated_offset = inflated_addr & (huge_size - 1);
918+
inflated_addr += offset - inflated_offset;
919+
if (inflated_offset > offset)
920+
inflated_addr += huge_size;
921+
922+
if (inflated_addr > TASK_SIZE - len)
923+
return addr;
924+
925+
return inflated_addr;
926+
}
927+
928+
/**
929+
* drm_get_unmapped_area() - Get an unused user-space virtual memory area
930+
* suitable for huge page table entries.
931+
* @file: The struct file representing the address space being mmap()'d.
932+
* @uaddr: Start address suggested by user-space.
933+
* @len: Length of the area.
934+
* @pgoff: The page offset into the address space.
935+
* @flags: mmap flags
936+
* @mgr: The address space manager used by the drm driver. This argument can
937+
* probably be removed at some point when all drivers use the same
938+
* address space manager.
939+
*
940+
* This function attempts to find an unused user-space virtual memory area
941+
* that can accommodate the size we want to map, and that is properly
942+
* aligned to facilitate huge page table entries matching actual
943+
* huge pages or huge page aligned memory in buffer objects. Buffer objects
944+
* are assumed to start at huge page boundary pfns (io memory) or be
945+
* populated by huge pages aligned to the start of the buffer object
946+
* (system- or coherent memory). Adapted from shmem_get_unmapped_area.
947+
*
948+
* Return: aligned user-space address.
949+
*/
950+
unsigned long drm_get_unmapped_area(struct file *file,
951+
unsigned long uaddr, unsigned long len,
952+
unsigned long pgoff, unsigned long flags,
953+
struct drm_vma_offset_manager *mgr)
954+
{
955+
unsigned long addr;
956+
unsigned long inflated_addr;
957+
struct drm_vma_offset_node *node;
958+
959+
if (len > TASK_SIZE)
960+
return -ENOMEM;
961+
962+
/*
963+
* @pgoff is the file page-offset the huge page boundaries of
964+
* which typically aligns to physical address huge page boundaries.
965+
* That's not true for DRM, however, where physical address huge
966+
* page boundaries instead are aligned with the offset from
967+
* buffer object start. So adjust @pgoff to be the offset from
968+
* buffer object start.
969+
*/
970+
drm_vma_offset_lock_lookup(mgr);
971+
node = drm_vma_offset_lookup_locked(mgr, pgoff, 1);
972+
if (node)
973+
pgoff -= node->vm_node.start;
974+
drm_vma_offset_unlock_lookup(mgr);
975+
976+
addr = current->mm->get_unmapped_area(file, uaddr, len, pgoff, flags);
977+
if (IS_ERR_VALUE(addr))
978+
return addr;
979+
if (addr & ~PAGE_MASK)
980+
return addr;
981+
if (addr > TASK_SIZE - len)
982+
return addr;
983+
984+
if (len < HPAGE_PMD_SIZE)
985+
return addr;
986+
if (flags & MAP_FIXED)
987+
return addr;
988+
/*
989+
* Our priority is to support MAP_SHARED mapped hugely;
990+
* and support MAP_PRIVATE mapped hugely too, until it is COWed.
991+
* But if caller specified an address hint, respect that as before.
992+
*/
993+
if (uaddr)
994+
return addr;
995+
996+
inflated_addr = drm_addr_inflate(addr, len, pgoff, flags,
997+
HPAGE_PMD_SIZE);
998+
999+
if (IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
1000+
len >= HPAGE_PUD_SIZE)
1001+
inflated_addr = drm_addr_inflate(inflated_addr, len, pgoff,
1002+
flags, HPAGE_PUD_SIZE);
1003+
return inflated_addr;
1004+
}
1005+
#else /* CONFIG_TRANSPARENT_HUGEPAGE */
1006+
unsigned long drm_get_unmapped_area(struct file *file,
1007+
unsigned long uaddr, unsigned long len,
1008+
unsigned long pgoff, unsigned long flags,
1009+
struct drm_vma_offset_manager *mgr)
1010+
{
1011+
return current->mm->get_unmapped_area(file, uaddr, len, pgoff, flags);
1012+
}
1013+
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
1014+
EXPORT_SYMBOL_GPL(drm_get_unmapped_area);
1015+
#endif /* CONFIG_MMU */

drivers/gpu/drm/ttm/ttm_bo_vm.c

Lines changed: 158 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,97 @@ vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo,
162162
}
163163
EXPORT_SYMBOL(ttm_bo_vm_reserve);
164164

165+
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
166+
/**
167+
* ttm_bo_vm_insert_huge - Insert a pfn for PUD or PMD faults
168+
* @vmf: Fault data
169+
* @bo: The buffer object
170+
* @page_offset: Page offset from bo start
171+
* @fault_page_size: The size of the fault in pages.
172+
* @pgprot: The page protections.
173+
* Does additional checking whether it's possible to insert a PUD or PMD
174+
* pfn and performs the insertion.
175+
*
176+
* Return: VM_FAULT_NOPAGE on successful insertion, VM_FAULT_FALLBACK if
177+
* a huge fault was not possible, or on insertion error.
178+
*/
179+
static vm_fault_t ttm_bo_vm_insert_huge(struct vm_fault *vmf,
180+
struct ttm_buffer_object *bo,
181+
pgoff_t page_offset,
182+
pgoff_t fault_page_size,
183+
pgprot_t pgprot)
184+
{
185+
pgoff_t i;
186+
vm_fault_t ret;
187+
unsigned long pfn;
188+
pfn_t pfnt;
189+
struct ttm_tt *ttm = bo->ttm;
190+
bool write = vmf->flags & FAULT_FLAG_WRITE;
191+
192+
/* Fault should not cross bo boundary. */
193+
page_offset &= ~(fault_page_size - 1);
194+
if (page_offset + fault_page_size > bo->num_pages)
195+
goto out_fallback;
196+
197+
if (bo->mem.bus.is_iomem)
198+
pfn = ttm_bo_io_mem_pfn(bo, page_offset);
199+
else
200+
pfn = page_to_pfn(ttm->pages[page_offset]);
201+
202+
/* pfn must be fault_page_size aligned. */
203+
if ((pfn & (fault_page_size - 1)) != 0)
204+
goto out_fallback;
205+
206+
/* Check that memory is contiguous. */
207+
if (!bo->mem.bus.is_iomem) {
208+
for (i = 1; i < fault_page_size; ++i) {
209+
if (page_to_pfn(ttm->pages[page_offset + i]) != pfn + i)
210+
goto out_fallback;
211+
}
212+
} else if (bo->bdev->driver->io_mem_pfn) {
213+
for (i = 1; i < fault_page_size; ++i) {
214+
if (ttm_bo_io_mem_pfn(bo, page_offset + i) != pfn + i)
215+
goto out_fallback;
216+
}
217+
}
218+
219+
pfnt = __pfn_to_pfn_t(pfn, PFN_DEV);
220+
if (fault_page_size == (HPAGE_PMD_SIZE >> PAGE_SHIFT))
221+
ret = vmf_insert_pfn_pmd_prot(vmf, pfnt, pgprot, write);
222+
#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
223+
else if (fault_page_size == (HPAGE_PUD_SIZE >> PAGE_SHIFT))
224+
ret = vmf_insert_pfn_pud_prot(vmf, pfnt, pgprot, write);
225+
#endif
226+
else
227+
WARN_ON_ONCE(ret = VM_FAULT_FALLBACK);
228+
229+
if (ret != VM_FAULT_NOPAGE)
230+
goto out_fallback;
231+
232+
return VM_FAULT_NOPAGE;
233+
out_fallback:
234+
count_vm_event(THP_FAULT_FALLBACK);
235+
return VM_FAULT_FALLBACK;
236+
}
237+
#else
238+
static vm_fault_t ttm_bo_vm_insert_huge(struct vm_fault *vmf,
239+
struct ttm_buffer_object *bo,
240+
pgoff_t page_offset,
241+
pgoff_t fault_page_size,
242+
pgprot_t pgprot)
243+
{
244+
return VM_FAULT_FALLBACK;
245+
}
246+
#endif
247+
165248
/**
166249
* ttm_bo_vm_fault_reserved - TTM fault helper
167250
* @vmf: The struct vm_fault given as argument to the fault callback
168251
* @prot: The page protection to be used for this memory area.
169252
* @num_prefault: Maximum number of prefault pages. The caller may want to
170253
* specify this based on madvice settings and the size of the GPU object
171254
* backed by the memory.
255+
* @fault_page_size: The size of the fault in pages.
172256
*
173257
* This function inserts one or more page table entries pointing to the
174258
* memory backing the buffer object, and then returns a return code
@@ -182,7 +266,8 @@ EXPORT_SYMBOL(ttm_bo_vm_reserve);
182266
*/
183267
vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf,
184268
pgprot_t prot,
185-
pgoff_t num_prefault)
269+
pgoff_t num_prefault,
270+
pgoff_t fault_page_size)
186271
{
187272
struct vm_area_struct *vma = vmf->vma;
188273
struct ttm_buffer_object *bo = vma->vm_private_data;
@@ -274,6 +359,13 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf,
274359
prot = pgprot_decrypted(prot);
275360
}
276361

362+
/* We don't prefault on huge faults. Yet. */
363+
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && fault_page_size != 1) {
364+
ret = ttm_bo_vm_insert_huge(vmf, bo, page_offset,
365+
fault_page_size, prot);
366+
goto out_io_unlock;
367+
}
368+
277369
/*
278370
* Speculatively prefault a number of pages. Only error on
279371
* first page.
@@ -340,7 +432,7 @@ vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf)
340432
return ret;
341433

342434
prot = vma->vm_page_prot;
343-
ret = ttm_bo_vm_fault_reserved(vmf, prot, TTM_BO_VM_NUM_PREFAULT);
435+
ret = ttm_bo_vm_fault_reserved(vmf, prot, TTM_BO_VM_NUM_PREFAULT, 1);
344436
if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT))
345437
return ret;
346438

@@ -350,6 +442,66 @@ vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf)
350442
}
351443
EXPORT_SYMBOL(ttm_bo_vm_fault);
352444

445+
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
446+
/**
447+
* ttm_pgprot_is_wrprotecting - Is a page protection value write-protecting?
448+
* @prot: The page protection value
449+
*
450+
* Return: true if @prot is write-protecting. false otherwise.
451+
*/
452+
static bool ttm_pgprot_is_wrprotecting(pgprot_t prot)
453+
{
454+
/*
455+
* This is meant to say "pgprot_wrprotect(prot) == prot" in a generic
456+
* way. Unfortunately there is no generic pgprot_wrprotect.
457+
*/
458+
return pte_val(pte_wrprotect(__pte(pgprot_val(prot)))) ==
459+
pgprot_val(prot);
460+
}
461+
462+
static vm_fault_t ttm_bo_vm_huge_fault(struct vm_fault *vmf,
463+
enum page_entry_size pe_size)
464+
{
465+
struct vm_area_struct *vma = vmf->vma;
466+
pgprot_t prot;
467+
struct ttm_buffer_object *bo = vma->vm_private_data;
468+
vm_fault_t ret;
469+
pgoff_t fault_page_size = 0;
470+
bool write = vmf->flags & FAULT_FLAG_WRITE;
471+
472+
switch (pe_size) {
473+
case PE_SIZE_PMD:
474+
fault_page_size = HPAGE_PMD_SIZE >> PAGE_SHIFT;
475+
break;
476+
#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
477+
case PE_SIZE_PUD:
478+
fault_page_size = HPAGE_PUD_SIZE >> PAGE_SHIFT;
479+
break;
480+
#endif
481+
default:
482+
WARN_ON_ONCE(1);
483+
return VM_FAULT_FALLBACK;
484+
}
485+
486+
/* Fallback on write dirty-tracking or COW */
487+
if (write && ttm_pgprot_is_wrprotecting(vma->vm_page_prot))
488+
return VM_FAULT_FALLBACK;
489+
490+
ret = ttm_bo_vm_reserve(bo, vmf);
491+
if (ret)
492+
return ret;
493+
494+
prot = vm_get_page_prot(vma->vm_flags);
495+
ret = ttm_bo_vm_fault_reserved(vmf, prot, 1, fault_page_size);
496+
if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT))
497+
return ret;
498+
499+
dma_resv_unlock(bo->base.resv);
500+
501+
return ret;
502+
}
503+
#endif
504+
353505
void ttm_bo_vm_open(struct vm_area_struct *vma)
354506
{
355507
struct ttm_buffer_object *bo = vma->vm_private_data;
@@ -451,7 +603,10 @@ static const struct vm_operations_struct ttm_bo_vm_ops = {
451603
.fault = ttm_bo_vm_fault,
452604
.open = ttm_bo_vm_open,
453605
.close = ttm_bo_vm_close,
454-
.access = ttm_bo_vm_access
606+
.access = ttm_bo_vm_access,
607+
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
608+
.huge_fault = ttm_bo_vm_huge_fault,
609+
#endif
455610
};
456611

457612
static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device *bdev,

drivers/gpu/drm/vmwgfx/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
1111
vmwgfx_validation.o vmwgfx_page_dirty.o vmwgfx_streamoutput.o \
1212
ttm_object.o ttm_lock.o
1313

14+
vmwgfx-$(CONFIG_TRANSPARENT_HUGEPAGE) += vmwgfx_thp.o
1415
obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o

drivers/gpu/drm/vmwgfx/vmwgfx_drv.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,18 @@ static void vmw_remove(struct pci_dev *pdev)
12471247
pci_disable_device(pdev);
12481248
}
12491249

1250+
static unsigned long
1251+
vmw_get_unmapped_area(struct file *file, unsigned long uaddr,
1252+
unsigned long len, unsigned long pgoff,
1253+
unsigned long flags)
1254+
{
1255+
struct drm_file *file_priv = file->private_data;
1256+
struct vmw_private *dev_priv = vmw_priv(file_priv->minor->dev);
1257+
1258+
return drm_get_unmapped_area(file, uaddr, len, pgoff, flags,
1259+
&dev_priv->vma_manager);
1260+
}
1261+
12501262
static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
12511263
void *ptr)
12521264
{
@@ -1418,6 +1430,7 @@ static const struct file_operations vmwgfx_driver_fops = {
14181430
.compat_ioctl = vmw_compat_ioctl,
14191431
#endif
14201432
.llseek = noop_llseek,
1433+
.get_unmapped_area = vmw_get_unmapped_area,
14211434
};
14221435

14231436
static struct drm_driver driver = {

0 commit comments

Comments
 (0)