Skip to contentSkip to conversation

mm: faster get user pages

PATCH Superseded Keith Busch 3 participants 11 messages Sep 20, 2018 – Sep 23, 2018

Subsystems (7)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover mm: faster get user pages

Changes since v1:

Fixed shift size for following huge PMD on powerpc

Updated change log with addition justification details

Keith Busch (7):
mm/gup_benchmark: Time put_page mm/gup_benchmark: Add additional pinning methods tools/gup_benchmark: Fix 'write' flag usage tools/gup_benchmark: Allow user specified file tools/gup_benchmark: Add parameter for hugetlb mm/gup: Combine parameters into struct mm/gup: Cache dev_pagemap while pinning pages

 include/linux/huge_mm.h                    |  12 +-
 include/linux/hugetlb.h                    |   2 +-
 include/linux/mm.h                         |  27 ++-
 mm/gup.c                                   | 279 ++++++++++++++---------------
 mm/gup_benchmark.c                         |  36 +++-
 mm/huge_memory.c                           |  67 ++++---
 mm/nommu.c                                 |   6 +-
 tools/testing/selftests/vm/gup_benchmark.c |  40 ++++-
 8 files changed, 262 insertions(+), 207 deletions(-)
Signature · 1 line
2.14.4
#1 lore raw

PATCH v2 1/7 mm/gup_benchmark: Time put_page

We'd like to measure time to unpin user pages, so this adds a second benchmark timer on put_page, separate from get_page.

Adding the field will breaks this ioctl ABI, but should be okay since this an in-tree kernel selftest.

Cc Kirill Shutemov <[email protected]>
Cc Dave Hansen <[email protected]>
Cc Dan Williams <[email protected]>
---
 mm/gup_benchmark.c                         | 8 ++++++--
 tools/testing/selftests/vm/gup_benchmark.c | 6 ++++--
 2 files changed, 10 insertions(+), 4 deletions(-)
mm/gup_benchmark.c +6 -2
index 6a473709e9b6..76cd35e477af 100644
@@ -8,7 +8,8 @@
 #define GUP_FAST_BENCHMARK	_IOWR('g', 1, struct gup_benchmark)
 
 struct gup_benchmark {
-	__u64 delta_usec;
+	__u64 get_delta_usec;
+	__u64 put_delta_usec;
 	__u64 addr;
 	__u64 size;
 	__u32 nr_pages_per_call;
@@ -47,14 +48,17 @@ static int __gup_benchmark_ioctl(unsigned int cmd,
 	}
 	end_time = ktime_get();
 
-	gup->delta_usec = ktime_us_delta(end_time, start_time);
+	gup->get_delta_usec = ktime_us_delta(end_time, start_time);
 	gup->size = addr - gup->addr;
 
+	start_time = ktime_get();
 	for (i = 0; i < nr_pages; i++) {
 		if (!pages[i])
 			break;
 		put_page(pages[i]);
 	}
+	end_time = ktime_get();
+	gup->put_delta_usec = ktime_us_delta(end_time, start_time);
 
 	kvfree(pages);
 	return 0;
tools/testing/selftests/vm/gup_benchmark.c +4 -2
index 36df55132036..bdcb97acd0ac 100644
@@ -17,7 +17,8 @@
 #define GUP_FAST_BENCHMARK	_IOWR('g', 1, struct gup_benchmark)
 
 struct gup_benchmark {
-	__u64 delta_usec;
+	__u64 get_delta_usec;
+	__u64 put_delta_usec;
 	__u64 addr;
 	__u64 size;
 	__u32 nr_pages_per_call;
@@ -81,7 +82,8 @@ int main(int argc, char **argv)
 		if (ioctl(fd, GUP_FAST_BENCHMARK, &gup))
 			perror("ioctl"), exit(1);
 
-		printf("Time: %lld us", gup.delta_usec);
+		printf("Time: get:%lld put:%lld us", gup.get_delta_usec,
+			gup.put_delta_usec);
 		if (gup.size != size)
 			printf(", truncated (size: %lld)", gup.size);
 		printf("\n");
Signature · 1 line
2.14.4
#2 lore raw

PATCH v2 2/7 mm/gup_benchmark: Add additional pinning methods

In reply to Keith Busch

This patch provides new gup benchmark ioctl commands to run different user page pinning methods, get_user_pages_longterm and get_user_pages, in addition to the existing get_user_pages_fast.

Cc Kirill Shutemov <[email protected]>
Cc Dave Hansen <[email protected]>
Cc Dan Williams <[email protected]>
---
 mm/gup_benchmark.c                         | 28 ++++++++++++++++++++++++++--
 tools/testing/selftests/vm/gup_benchmark.c | 13 +++++++++++--
 2 files changed, 37 insertions(+), 4 deletions(-)
mm/gup_benchmark.c +26 -2
index 76cd35e477af..e6d9ce001ffa 100644
@@ -6,6 +6,8 @@
 #include <linux/debugfs.h>
 
 #define GUP_FAST_BENCHMARK	_IOWR('g', 1, struct gup_benchmark)
+#define GUP_LONGTERM_BENCHMARK	_IOWR('g', 2, struct gup_benchmark)
+#define GUP_BENCHMARK		_IOWR('g', 3, struct gup_benchmark)
 
 struct gup_benchmark {
 	__u64 get_delta_usec;
@@ -41,7 +43,23 @@ static int __gup_benchmark_ioctl(unsigned int cmd,
 			nr = (next - addr) / PAGE_SIZE;
 		}
 
-		nr = get_user_pages_fast(addr, nr, gup->flags & 1, pages + i);
+		switch (cmd) {
+		case GUP_FAST_BENCHMARK:
+			nr = get_user_pages_fast(addr, nr, gup->flags & 1,
+						 pages + i);
+			break;
+		case GUP_LONGTERM_BENCHMARK:
+			nr = get_user_pages_longterm(addr, nr, gup->flags & 1,
+						     pages + i, NULL);
+			break;
+		case GUP_BENCHMARK:
+			nr = get_user_pages(addr, nr, gup->flags & 1, pages + i,
+					    NULL);
+			break;
+		default:
+			return -1;
+		}
+
 		if (nr <= 0)
 			break;
 		i += nr;
@@ -70,8 +88,14 @@ static long gup_benchmark_ioctl(struct file *filep, unsigned int cmd,
 	struct gup_benchmark gup;
 	int ret;
 
-	if (cmd != GUP_FAST_BENCHMARK)
+	switch (cmd) {
+	case GUP_FAST_BENCHMARK:
+	case GUP_LONGTERM_BENCHMARK:
+	case GUP_BENCHMARK:
+		break;
+	default:
 		return -EINVAL;
+	}
 
 	if (copy_from_user(&gup, (void __user *)arg, sizeof(gup)))
 		return -EFAULT;
tools/testing/selftests/vm/gup_benchmark.c +11 -2
index bdcb97acd0ac..c2f785ded9b9 100644
@@ -15,6 +15,8 @@
 #define PAGE_SIZE sysconf(_SC_PAGESIZE)
 
 #define GUP_FAST_BENCHMARK	_IOWR('g', 1, struct gup_benchmark)
+#define GUP_LONGTERM_BENCHMARK	_IOWR('g', 2, struct gup_benchmark)
+#define GUP_BENCHMARK		_IOWR('g', 3, struct gup_benchmark)
 
 struct gup_benchmark {
 	__u64 get_delta_usec;
@@ -30,9 +32,10 @@ int main(int argc, char **argv)
 	struct gup_benchmark gup;
 	unsigned long size = 128 * MB;
 	int i, fd, opt, nr_pages = 1, thp = -1, repeats = 1, write = 0;
+	int cmd = GUP_FAST_BENCHMARK;
 	char *p;
 
-	while ((opt = getopt(argc, argv, "m:r:n:tT")) != -1) {
+	while ((opt = getopt(argc, argv, "m:r:n:tTLU")) != -1) {
 		switch (opt) {
 		case 'm':
 			size = atoi(optarg) * MB;
@@ -49,6 +52,12 @@ int main(int argc, char **argv)
 		case 'T':
 			thp = 0;
 			break;
+		case 'L':
+			cmd = GUP_LONGTERM_BENCHMARK;
+			break;
+		case 'U':
+			cmd = GUP_BENCHMARK;
+			break;
 		case 'w':
 			write = 1;
 		default:
@@ -79,7 +88,7 @@ int main(int argc, char **argv)
 
 	for (i = 0; i < repeats; i++) {
 		gup.size = size;
-		if (ioctl(fd, GUP_FAST_BENCHMARK, &gup))
+		if (ioctl(fd, cmd, &gup))
 			perror("ioctl"), exit(1);
 
 		printf("Time: get:%lld put:%lld us", gup.get_delta_usec,
Signature · 1 line
2.14.4
#3 lore raw

PATCH v2 3/7 tools/gup_benchmark: Fix 'write' flag usage

In reply to Keith Busch

If the '-w' parameter was provided, the benchmark would exit due to a mssing 'break'.

Cc Kirill Shutemov <[email protected]>
Cc Dave Hansen <[email protected]>
Cc Dan Williams <[email protected]>
---
 tools/testing/selftests/vm/gup_benchmark.c | 1 +
 1 file changed, 1 insertion(+)
tools/testing/selftests/vm/gup_benchmark.c +1 -0
index c2f785ded9b9..b2082df8beb4 100644
@@ -60,6 +60,7 @@ int main(int argc, char **argv)
 			break;
 		case 'w':
 			write = 1;
+			break;
 		default:
 			return -1;
 		}
Signature · 1 line
2.14.4
#4 lore raw

PATCH v2 4/7 tools/gup_benchmark: Allow user specified file

In reply to Keith Busch

The gup benchmark by default maps anonymous memory. This patch allows a user to specify a file to map, providing a means to test various file backings, like device and filesystem DAX.

Cc Kirill Shutemov <[email protected]>
Cc Dave Hansen <[email protected]>
Cc Dan Williams <[email protected]>
---
 tools/testing/selftests/vm/gup_benchmark.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)
tools/testing/selftests/vm/gup_benchmark.c +16 -3
index b2082df8beb4..f2c99e2436f8 100644
@@ -33,9 +33,12 @@ int main(int argc, char **argv)
 	unsigned long size = 128 * MB;
 	int i, fd, opt, nr_pages = 1, thp = -1, repeats = 1, write = 0;
 	int cmd = GUP_FAST_BENCHMARK;
+	int file_map = -1;
+	int flags = MAP_ANONYMOUS | MAP_PRIVATE;
+	char *file = NULL;
 	char *p;
 
-	while ((opt = getopt(argc, argv, "m:r:n:tTLU")) != -1) {
+	while ((opt = getopt(argc, argv, "m:r:n:f:tTLU")) != -1) {
 		switch (opt) {
 		case 'm':
 			size = atoi(optarg) * MB;
@@ -61,11 +64,22 @@ int main(int argc, char **argv)
 		case 'w':
 			write = 1;
 			break;
+		case 'f':
+			file = optarg;
+			flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
+			flags |= MAP_SHARED;
+			break;
 		default:
 			return -1;
 		}
 	}
 
+	if (file) {
+		file_map = open(file, O_RDWR|O_CREAT);
+		if (file_map < 0)
+			perror("open"), exit(file_map);
+	}
+
 	gup.nr_pages_per_call = nr_pages;
 	gup.flags = write;
 
@@ -73,8 +87,7 @@ int main(int argc, char **argv)
 	if (fd == -1)
 		perror("open"), exit(1);
 
-	p = mmap(NULL, size, PROT_READ | PROT_WRITE,
-			MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+	p = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, file_map, 0);
 	if (p == MAP_FAILED)
 		perror("mmap"), exit(1);
 	gup.addr = (unsigned long)p;
Signature · 1 line
2.14.4
#5 lore raw

PATCH v2 5/7 tools/gup_benchmark: Add parameter for hugetlb

In reply to Keith Busch
Cc Kirill Shutemov <[email protected]>
Cc Dave Hansen <[email protected]>
Cc Dan Williams <[email protected]>
---
 tools/testing/selftests/vm/gup_benchmark.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
tools/testing/selftests/vm/gup_benchmark.c +4 -1
index f2c99e2436f8..5d96e2b3d2f1 100644
@@ -38,7 +38,7 @@ int main(int argc, char **argv)
 	char *file = NULL;
 	char *p;
 
-	while ((opt = getopt(argc, argv, "m:r:n:f:tTLU")) != -1) {
+	while ((opt = getopt(argc, argv, "m:r:n:f:tTLUH")) != -1) {
 		switch (opt) {
 		case 'm':
 			size = atoi(optarg) * MB;
@@ -64,6 +64,9 @@ int main(int argc, char **argv)
 		case 'w':
 			write = 1;
 			break;
+		case 'H':
+			flags |= MAP_HUGETLB;
+			break;
 		case 'f':
 			file = optarg;
 			flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
Signature · 1 line
2.14.4
#6 lore raw

PATCH v2 6/7 mm/gup: Combine parameters into struct

In reply to Keith Busch

This will make it easier to add new parameters that we may wish to thread through these function calls.

Cc Kirill Shutemov <[email protected]>
Cc Dave Hansen <[email protected]>
Cc Dan Williams <[email protected]>
---
 include/linux/huge_mm.h |  12 +--
 include/linux/hugetlb.h |   2 +-
 include/linux/mm.h      |  21 ++++-
 mm/gup.c                | 238 +++++++++++++++++++++++-------------------------
 mm/huge_memory.c        |  32 +++----
 mm/nommu.c              |   6 +-
 6 files changed, 151 insertions(+), 160 deletions(-)
include/linux/huge_mm.h +4 -8
index 99c19b06d9a4..7d22e2c7f154 100644
@@ -212,10 +212,8 @@ static inline int hpage_nr_pages(struct page *page)
 	return 1;
 }
 
-struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
-		pmd_t *pmd, int flags);
-struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr,
-		pud_t *pud, int flags);
+struct page *follow_devmap_pmd(struct follow_page_context *ctx, pmd_t *pmd);
+struct page *follow_devmap_pud(struct follow_page_context *ctx, pud_t *pud);
 
 extern vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf, pmd_t orig_pmd);
 
@@ -343,14 +341,12 @@ static inline void mm_put_huge_zero_page(struct mm_struct *mm)
 	return;
 }
 
-static inline struct page *follow_devmap_pmd(struct vm_area_struct *vma,
-		unsigned long addr, pmd_t *pmd, int flags)
+static inline struct page *follow_devmap_pmd(struct gup_context *ctx, pmd_t *pmd)
 {
 	return NULL;
 }
 
-static inline struct page *follow_devmap_pud(struct vm_area_struct *vma,
-		unsigned long addr, pud_t *pud, int flags)
+static inline struct page *follow_devmap_pud(struct gup_context *ctx, pud_t *pud)
 {
 	return NULL;
 }
include/linux/hugetlb.h +1 -1
index 6b68e345f0ca..64b675863793 100644
@@ -180,7 +180,7 @@ static inline void hugetlb_report_meminfo(struct seq_file *m)
 static inline void hugetlb_show_meminfo(void)
 {
 }
-#define follow_huge_pd(vma, addr, hpd, flags, pdshift) NULL
+#define follow_huge_pd(ctx, hpd, pdshift)	NULL
 #define follow_huge_pmd(mm, addr, pmd, flags)	NULL
 #define follow_huge_pud(mm, addr, pud, flags)	NULL
 #define follow_huge_pgd(mm, addr, pgd, flags)	NULL
include/linux/mm.h +16 -5
index a61ebe8ad4ca..f1fd241c9071 100644
@@ -378,6 +378,13 @@ struct vm_fault {
 					 */
 };
 
+struct follow_page_context {
+	struct vm_area_struct *vma;
+	unsigned long address;
+	unsigned int page_mask;
+	unsigned int flags;
+};
+
 /* page entry size for vm->huge_fault() */
 enum page_entry_size {
 	PE_SIZE_PTE = 0,
@@ -2534,15 +2541,19 @@ static inline vm_fault_t vmf_error(int err)
 	return VM_FAULT_SIGBUS;
 }
 
-struct page *follow_page_mask(struct vm_area_struct *vma,
-			      unsigned long address, unsigned int foll_flags,
-			      unsigned int *page_mask);
+struct page *follow_page_mask(struct follow_page_context *ctx);
 
 static inline struct page *follow_page(struct vm_area_struct *vma,
 		unsigned long address, unsigned int foll_flags)
 {
-	unsigned int unused_page_mask;
-	return follow_page_mask(vma, address, foll_flags, &unused_page_mask);
+	struct follow_page_context ctx = {
+		.vma = vma,
+		.address = address,
+		.page_mask = 0,
+		.flags = foll_flags,
+	};
+
+	return follow_page_mask(&ctx);
 }
 
 #define FOLL_WRITE	0x01	/* check pte is writable */
mm/gup.c +113 -125

545 lines not on this page · raw message

mm/huge_memory.c +15 -17
index 533f9b00147d..abd36e6afe2c 100644
@@ -851,11 +851,10 @@ static void touch_pmd(struct vm_area_struct *vma, unsigned long addr,
 		update_mmu_cache_pmd(vma, addr, pmd);
 }
 
-struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
-		pmd_t *pmd, int flags)
+struct page *follow_devmap_pmd(struct follow_page_context *ctx, pmd_t *pmd)
 {
 	unsigned long pfn = pmd_pfn(*pmd);
-	struct mm_struct *mm = vma->vm_mm;
+	struct mm_struct *mm = ctx->vma->vm_mm;
 	struct dev_pagemap *pgmap;
 	struct page *page;
 
@@ -865,9 +864,9 @@ struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
 	 * When we COW a devmap PMD entry, we split it into PTEs, so we should
 	 * not be in this function with `flags & FOLL_COW` set.
 	 */
-	WARN_ONCE(flags & FOLL_COW, "mm: In follow_devmap_pmd with FOLL_COW set");
+	WARN_ONCE(ctx->flags & FOLL_COW, "mm: In follow_devmap_pmd with FOLL_COW set");
 
-	if (flags & FOLL_WRITE && !pmd_write(*pmd))
+	if (ctx->flags & FOLL_WRITE && !pmd_write(*pmd))
 		return NULL;
 
 	if (pmd_present(*pmd) && pmd_devmap(*pmd))
@@ -875,17 +874,17 @@ struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
 	else
 		return NULL;
 
-	if (flags & FOLL_TOUCH)
-		touch_pmd(vma, addr, pmd, flags);
+	if (ctx->flags & FOLL_TOUCH)
+		touch_pmd(ctx->vma, ctx->address, pmd, ctx->flags);
 
 	/*
 	 * device mapped pages can only be returned if the
 	 * caller will manage the page reference count.
 	 */
-	if (!(flags & FOLL_GET))
+	if (!(ctx->flags & FOLL_GET))
 		return ERR_PTR(-EEXIST);
 
-	pfn += (addr & ~PMD_MASK) >> PAGE_SHIFT;
+	pfn += (ctx->address & ~PMD_MASK) >> PAGE_SHIFT;
 	pgmap = get_dev_pagemap(pfn, NULL);
 	if (!pgmap)
 		return ERR_PTR(-EFAULT);
@@ -999,17 +998,16 @@ static void touch_pud(struct vm_area_struct *vma, unsigned long addr,
 		update_mmu_cache_pud(vma, addr, pud);
 }
 
-struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr,
-		pud_t *pud, int flags)
+struct page *follow_devmap_pud(struct follow_page_context *ctx, pud_t *pud)
 {
 	unsigned long pfn = pud_pfn(*pud);
-	struct mm_struct *mm = vma->vm_mm;
+	struct mm_struct *mm = ctx->vma->vm_mm;
 	struct dev_pagemap *pgmap;
 	struct page *page;
 
 	assert_spin_locked(pud_lockptr(mm, pud));
 
-	if (flags & FOLL_WRITE && !pud_write(*pud))
+	if (ctx->flags & FOLL_WRITE && !pud_write(*pud))
 		return NULL;
 
 	if (pud_present(*pud) && pud_devmap(*pud))
@@ -1017,17 +1015,17 @@ struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr,
 	else
 		return NULL;
 
-	if (flags & FOLL_TOUCH)
-		touch_pud(vma, addr, pud, flags);
+	if (ctx->flags & FOLL_TOUCH)
+		touch_pud(ctx->vma, ctx->address, pud, ctx->flags);
 
 	/*
 	 * device mapped pages can only be returned if the
 	 * caller will manage the page reference count.
 	 */
-	if (!(flags & FOLL_GET))
+	if (!(ctx->flags & FOLL_GET))
 		return ERR_PTR(-EEXIST);
 
-	pfn += (addr & ~PUD_MASK) >> PAGE_SHIFT;
+	pfn += (ctx->address & ~PUD_MASK) >> PAGE_SHIFT;
 	pgmap = get_dev_pagemap(pfn, NULL);
 	if (!pgmap)
 		return ERR_PTR(-EFAULT);
mm/nommu.c +2 -4
index e4aac33216ae..59db9f5dbb4e 100644
@@ -1709,11 +1709,9 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
 	return ret;
 }
 
-struct page *follow_page_mask(struct vm_area_struct *vma,
-			      unsigned long address, unsigned int flags,
-			      unsigned int *page_mask)
+struct page *follow_page_mask(struct follow_page_context *ctx)
 {
-	*page_mask = 0;
+	ctx->page_mask = 0;
 	return NULL;
 }
 
Signature · 1 line
2.14.4
#7 lore raw

Re: [PATCHv2 6/7] mm/gup: Combine parameters into struct

Quoted 69 lines · Keith Busch

This will make it easier to add new parameters that we may wish to thread through these function calls.

Cc Kirill Shutemov <[email protected]>
Cc Dave Hansen <[email protected]>
Cc Dan Williams <[email protected]>
---
 include/linux/huge_mm.h |  12 +--
 include/linux/hugetlb.h |   2 +-
 include/linux/mm.h      |  21 ++++-
 mm/gup.c                | 238 +++++++++++++++++++++++-------------------------
 mm/huge_memory.c        |  32 +++----
 mm/nommu.c              |   6 +-
 6 files changed, 151 insertions(+), 160 deletions(-)
include/linux/huge_mm.h +4 -8
index 99c19b06d9a4..7d22e2c7f154 100644
@@ -212,10 +212,8 @@ static inline int hpage_nr_pages(struct page *page)
        return 1;
 }
 
-struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
-               pmd_t *pmd, int flags);
-struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr,
-               pud_t *pud, int flags);
+struct page *follow_devmap_pmd(struct follow_page_context *ctx, pmd_t *pmd);
+struct page *follow_devmap_pud(struct follow_page_context *ctx, pud_t *pud);
 
 extern vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf, pmd_t orig_pmd);
 
@@ -343,14 +341,12 @@ static inline void mm_put_huge_zero_page(struct mm_struct *mm)
        return;
 }
 
-static inline struct page *follow_devmap_pmd(struct vm_area_struct *vma,
-               unsigned long addr, pmd_t *pmd, int flags)
+static inline struct page *follow_devmap_pmd(struct gup_context *ctx, pmd_t *pmd)
 {
        return NULL;
 }
 
-static inline struct page *follow_devmap_pud(struct vm_area_struct *vma,
-               unsigned long addr, pud_t *pud, int flags)
+static inline struct page *follow_devmap_pud(struct gup_context *ctx, pud_t *pud)
 {
        return NULL;
 }
include/linux/hugetlb.h +1 -1
index 6b68e345f0ca..64b675863793 100644
@@ -180,7 +180,7 @@ static inline void hugetlb_report_meminfo(struct seq_file *m)
 static inline void hugetlb_show_meminfo(void)
 {
 }
-#define follow_huge_pd(vma, addr, hpd, flags, pdshift) NULL
+#define follow_huge_pd(ctx, hpd, pdshift)      NULL
 #define follow_huge_pmd(mm, addr, pmd, flags)  NULL
 #define follow_huge_pud(mm, addr, pud, flags)  NULL
 #define follow_huge_pgd(mm, addr, pgd, flags)  NULL
include/linux/mm.h +0 -0
index a61ebe8ad4ca..f1fd241c9071 100644

include/linux/mm.h struct vm_fault { · line 386

Quoted 9 lines
include/linux/mm.h +6 -0
@@ -378,6 +378,13 @@ struct vm_fault {
                                         */
 };
 
+struct follow_page_context {
+       struct vm_area_struct *vma;
+       unsigned long address;
+       unsigned int page_mask;
+       unsigned int flags;
+};

I was thinking that the context would only contain the in/out parameters of page_mask and pgmap. I don't think we plan to have more parameters beyond this one addition and from a code readability standpoint the less "ctx->" the better.

#8 lore raw

Re: [PATCHv2 6/7] mm/gup: Combine parameters into struct

Quoted 74 lines · Dan Williams
Quoted 72 lines · Keith Busch

This will make it easier to add new parameters that we may wish to thread through these function calls.

Cc Kirill Shutemov <[email protected]>
Cc Dave Hansen <[email protected]>
Cc Dan Williams <[email protected]>
---
 include/linux/huge_mm.h |  12 +--
 include/linux/hugetlb.h |   2 +-
 include/linux/mm.h      |  21 ++++-
 mm/gup.c                | 238 +++++++++++++++++++++++-------------------------
 mm/huge_memory.c        |  32 +++----
 mm/nommu.c              |   6 +-
 6 files changed, 151 insertions(+), 160 deletions(-)
include/linux/huge_mm.h +4 -8
index 99c19b06d9a4..7d22e2c7f154 100644
@@ -212,10 +212,8 @@ static inline int hpage_nr_pages(struct page *page)
        return 1;
 }
 
-struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
-               pmd_t *pmd, int flags);
-struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr,
-               pud_t *pud, int flags);
+struct page *follow_devmap_pmd(struct follow_page_context *ctx, pmd_t *pmd);
+struct page *follow_devmap_pud(struct follow_page_context *ctx, pud_t *pud);
 
 extern vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf, pmd_t orig_pmd);
 
@@ -343,14 +341,12 @@ static inline void mm_put_huge_zero_page(struct mm_struct *mm)
        return;
 }
 
-static inline struct page *follow_devmap_pmd(struct vm_area_struct *vma,
-               unsigned long addr, pmd_t *pmd, int flags)
+static inline struct page *follow_devmap_pmd(struct gup_context *ctx, pmd_t *pmd)
 {
        return NULL;
 }
 
-static inline struct page *follow_devmap_pud(struct vm_area_struct *vma,
-               unsigned long addr, pud_t *pud, int flags)
+static inline struct page *follow_devmap_pud(struct gup_context *ctx, pud_t *pud)
 {
        return NULL;
 }
include/linux/hugetlb.h +1 -1
index 6b68e345f0ca..64b675863793 100644
@@ -180,7 +180,7 @@ static inline void hugetlb_report_meminfo(struct seq_file *m)
 static inline void hugetlb_show_meminfo(void)
 {
 }
-#define follow_huge_pd(vma, addr, hpd, flags, pdshift) NULL
+#define follow_huge_pd(ctx, hpd, pdshift)      NULL
 #define follow_huge_pmd(mm, addr, pmd, flags)  NULL
 #define follow_huge_pud(mm, addr, pud, flags)  NULL
 #define follow_huge_pgd(mm, addr, pgd, flags)  NULL
include/linux/mm.h +0 -0
index a61ebe8ad4ca..f1fd241c9071 100644
@@ -378,6 +378,13 @@ struct vm_fault {
                                         */
 };
 
Quoted 11 lines

include/linux/mm.h struct vm_fault { · line 386

Quoted 6 lines
include/linux/mm.h +6 -0
+struct follow_page_context {
+       struct vm_area_struct *vma;
+       unsigned long address;
+       unsigned int page_mask;
+       unsigned int flags;
+};

I was thinking that the context would only contain the in/out parameters of page_mask and pgmap. I don't think we plan to have more parameters beyond this one addition and from a code readability standpoint the less "ctx->" the better.

Also, the next version of this series should be copied to linux-mm.

#9 lore raw

Re: [PATCHv2 6/7] mm/gup: Combine parameters into struct

In reply to Keith Busch
I love your patch! Yet something to improve:

Hi Keith,

I love your patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.19-rc4 next-20180921]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Keith-Busch/mm-gup_benchmark-Time-put_page/20180921-223159
config: x86_64-kexec (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0 reproduce:

        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

mm/gup.c: In function 'follow_pmd_mask':

>> mm/gup.c:233:32: error: macro "follow_huge_pd" passed 5 arguments, but takes just 3
              ctx->flags, PMD_SHIFT);
                                   ^
>> mm/gup.c:231:10: error: 'follow_huge_pd' undeclared (first use in this function); did you mean 'follow_page_pte'?
      page = follow_huge_pd(ctx->vma, ctx->address,
             ^~~~~~~~~~~~~~
             follow_page_pte
   mm/gup.c:231:10: note: each undeclared identifier is reported only once for each function it appears in
   mm/gup.c: In function 'follow_pud_mask':
   mm/gup.c:333:32: error: macro "follow_huge_pd" passed 5 arguments, but takes just 3
              ctx->flags, PUD_SHIFT);
                                   ^
   mm/gup.c:331:10: error: 'follow_huge_pd' undeclared (first use in this function); did you mean 'follow_page_pte'?
      page = follow_huge_pd(ctx->vma, ctx->address,
             ^~~~~~~~~~~~~~
             follow_page_pte
   mm/gup.c: In function 'follow_p4d_mask':
   mm/gup.c:366:32: error: macro "follow_huge_pd" passed 5 arguments, but takes just 3
              ctx->flags, P4D_SHIFT);
                                   ^
   mm/gup.c:364:10: error: 'follow_huge_pd' undeclared (first use in this function); did you mean 'follow_page_pte'?
      page = follow_huge_pd(ctx->vma, ctx->address,
             ^~~~~~~~~~~~~~
             follow_page_pte
   mm/gup.c: In function 'follow_page_mask':
   mm/gup.c:414:34: error: macro "follow_huge_pd" passed 5 arguments, but takes just 3
              ctx->flags, PGDIR_SHIFT);
                                     ^
   mm/gup.c:412:10: error: 'follow_huge_pd' undeclared (first use in this function); did you mean 'follow_page_pte'?
      page = follow_huge_pd(ctx->vma, ctx->address,
             ^~~~~~~~~~~~~~
             follow_page_pte

vim +/follow_huge_pd +233 mm/gup.c

208
209 static struct page *follow_pmd_mask(struct follow_page_context *ctx, pud_t *pudp)
210 {
211 pmd_t *pmd, pmdval;
212 spinlock_t *ptl;
213 struct page *page;
214 struct mm_struct *mm = ctx->vma->vm_mm;
215
216 pmd = pmd_offset(pudp, ctx->address);
217 /*
218 * The READ_ONCE() will stabilize the pmdval in a register or
219 * on the stack so that it will stop changing under the code.
220 */
221 pmdval = READ_ONCE(*pmd);
222 if (pmd_none(pmdval))
223 return no_page_table(ctx);
224 if (pmd_huge(pmdval) && ctx->vma->vm_flags & VM_HUGETLB) {
225 page = follow_huge_pmd(mm, ctx->address, pmd, ctx->flags);
226 if (page)
227 return page;
228 return no_page_table(ctx);
229 }
230 if (is_hugepd(__hugepd(pmd_val(pmdval)))) {

 > 231			page = follow_huge_pd(ctx->vma, ctx->address,
   232					      __hugepd(pmd_val(pmdval)),
 > 233					      ctx->flags, PMD_SHIFT);

234 if (page)
235 return page;
236 return no_page_table(ctx);
237 }
238 retry:
239 if (!pmd_present(pmdval)) {
240 if (likely(!(ctx->flags & FOLL_MIGRATION)))
241 return no_page_table(ctx);
242 VM_BUG_ON(thp_migration_supported() &&
243 !is_pmd_migration_entry(pmdval));
244 if (is_pmd_migration_entry(pmdval))
245 pmd_migration_entry_wait(mm, pmd);
246 pmdval = READ_ONCE(*pmd);
247 /*
248 * MADV_DONTNEED may convert the pmd to null because
249 * mmap_sem is held in read mode
250 */
251 if (pmd_none(pmdval))
252 return no_page_table(ctx);
253 goto retry;
254 }
255 if (pmd_devmap(pmdval)) {
256 ptl = pmd_lock(mm, pmd);
257 page = follow_devmap_pmd(ctx, pmd);
258 spin_unlock(ptl);
259 if (page)
260 return page;
261 }
262 if (likely(!pmd_trans_huge(pmdval)))
263 return follow_page_pte(ctx, pmd);
264
265 if ((ctx->flags & FOLL_NUMA) && pmd_protnone(pmdval))
266 return no_page_table(ctx);
267
268 retry_locked:
269 ptl = pmd_lock(mm, pmd);
270 if (unlikely(pmd_none(*pmd))) {
271 spin_unlock(ptl);
272 return no_page_table(ctx);
273 }
274 if (unlikely(!pmd_present(*pmd))) {
275 spin_unlock(ptl);
276 if (likely(!(ctx->flags & FOLL_MIGRATION)))
277 return no_page_table(ctx);
278 pmd_migration_entry_wait(mm, pmd);
279 goto retry_locked;
280 }
281 if (unlikely(!pmd_trans_huge(*pmd))) {
282 spin_unlock(ptl);
283 return follow_page_pte(ctx, pmd);
284 }
285 if (ctx->flags & FOLL_SPLIT) {
286 int ret;
287 page = pmd_page(*pmd);
288 if (is_huge_zero_page(page)) {
289 spin_unlock(ptl);
290 ret = 0;
291 split_huge_pmd(ctx->vma, pmd, ctx->address);
292 if (pmd_trans_unstable(pmd))
293 ret = -EBUSY;
294 } else {
295 get_page(page);
296 spin_unlock(ptl);
297 lock_page(page);
298 ret = split_huge_page(page);
299 unlock_page(page);
300 put_page(page);
301 if (pmd_none(*pmd))
302 return no_page_table(ctx);
303 }
304
305 return ret ? ERR_PTR(ret) :
306 follow_page_pte(ctx, pmd);
307 }
308 page = follow_trans_huge_pmd(ctx->vma, ctx->address, pmd, ctx->flags);
309 spin_unlock(ptl);
310 ctx->page_mask = HPAGE_PMD_NR - 1;
311 return page;
312 }
313

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation

#10 lore raw

PATCH v2 7/7 mm/gup: Cache dev_pagemap while pinning pages

In reply to Keith Busch

Pinning pages from ZONE_DEVICE memory needs to check the backing device's live-ness, which is tracked in the device's dev_pagemap metadata. This metadata is stored in a radix tree and looking it up adds measurable software overhead.

This patch avoids repeating this relatively costly operation when dev_pagemap is used by caching the last dev_pagemap while getting user pages. The gup_benchmark reports this reduces the time to get user pages to as low as 1/3 of the previous time.

Cc Kirill Shutemov <[email protected]>
Cc Dave Hansen <[email protected]>
Cc Dan Williams <[email protected]>
---
 include/linux/mm.h |  8 +++++++-
 mm/gup.c           | 41 ++++++++++++++++++++++++-----------------
 mm/huge_memory.c   | 35 +++++++++++++++--------------------
 3 files changed, 46 insertions(+), 38 deletions(-)
include/linux/mm.h +7 -1
index f1fd241c9071..d688e18a19c4 100644
@@ -380,6 +380,7 @@ struct vm_fault {
 
 struct follow_page_context {
 	struct vm_area_struct *vma;
+	struct dev_pagemap *pgmap;
 	unsigned long address;
 	unsigned int page_mask;
 	unsigned int flags;
@@ -2546,14 +2547,19 @@ struct page *follow_page_mask(struct follow_page_context *ctx);
 static inline struct page *follow_page(struct vm_area_struct *vma,
 		unsigned long address, unsigned int foll_flags)
 {
+	struct page *page;
 	struct follow_page_context ctx = {
 		.vma = vma,
+		.pgmap = NULL,
 		.address = address,
 		.page_mask = 0,
 		.flags = foll_flags,
 	};
 
-	return follow_page_mask(&ctx);
+	page = follow_page_mask(&ctx);
+	if (ctx.pgmap)
+		put_dev_pagemap(ctx.pgmap);
+	return page;
 }
 
 #define FOLL_WRITE	0x01	/* check pte is writable */
mm/gup.c +24 -17

100 lines not on this page · raw message

mm/huge_memory.c +15 -20
index abd36e6afe2c..6787011385ce 100644
@@ -851,12 +851,23 @@ static void touch_pmd(struct vm_area_struct *vma, unsigned long addr,
 		update_mmu_cache_pmd(vma, addr, pmd);
 }
 
+static struct page *pagemap_page(struct follow_page_context *ctx,
+				 unsigned long pfn)
+{
+	struct page *page;
+
+	ctx->pgmap = get_dev_pagemap(pfn, ctx->pgmap);
+	if (!ctx->pgmap)
+		return ERR_PTR(-EFAULT);
+	page = pfn_to_page(pfn);
+	get_page(page);
+	return page;
+}
+
 struct page *follow_devmap_pmd(struct follow_page_context *ctx, pmd_t *pmd)
 {
 	unsigned long pfn = pmd_pfn(*pmd);
 	struct mm_struct *mm = ctx->vma->vm_mm;
-	struct dev_pagemap *pgmap;
-	struct page *page;
 
 	assert_spin_locked(pmd_lockptr(mm, pmd));
 
@@ -885,14 +896,7 @@ struct page *follow_devmap_pmd(struct follow_page_context *ctx, pmd_t *pmd)
 		return ERR_PTR(-EEXIST);
 
 	pfn += (ctx->address & ~PMD_MASK) >> PAGE_SHIFT;
-	pgmap = get_dev_pagemap(pfn, NULL);
-	if (!pgmap)
-		return ERR_PTR(-EFAULT);
-	page = pfn_to_page(pfn);
-	get_page(page);
-	put_dev_pagemap(pgmap);
-
-	return page;
+	return pagemap_page(ctx, pfn);
 }
 
 int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
@@ -1002,8 +1006,6 @@ struct page *follow_devmap_pud(struct follow_page_context *ctx, pud_t *pud)
 {
 	unsigned long pfn = pud_pfn(*pud);
 	struct mm_struct *mm = ctx->vma->vm_mm;
-	struct dev_pagemap *pgmap;
-	struct page *page;
 
 	assert_spin_locked(pud_lockptr(mm, pud));
 
@@ -1026,14 +1028,7 @@ struct page *follow_devmap_pud(struct follow_page_context *ctx, pud_t *pud)
 		return ERR_PTR(-EEXIST);
 
 	pfn += (ctx->address & ~PUD_MASK) >> PAGE_SHIFT;
-	pgmap = get_dev_pagemap(pfn, NULL);
-	if (!pgmap)
-		return ERR_PTR(-EFAULT);
-	page = pfn_to_page(pfn);
-	get_page(page);
-	put_dev_pagemap(pgmap);
-
-	return page;
+	return pagemap_page(ctx, pfn);
 }
 
 int copy_huge_pud(struct mm_struct *dst_mm, struct mm_struct *src_mm,
Signature · 1 line
2.14.4