aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>2017-11-17 18:31:22 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2017-11-17 19:10:04 -0500
commit64c349f4ae78723248c474531d0cbc524fc5ba77 (patch)
treeb8284ccb8224f9f4023d5b4c1caa6bc053a2c90f
parent15df03c87983660a4d1eedb4541778592bd97684 (diff)
mm: add infrastructure for get_user_pages_fast() benchmarking
Performance of get_user_pages_fast() is critical for some workloads, but it's tricky to test it directly. This patch provides /sys/kernel/debug/gup_benchmark that helps with testing performance of it. See tools/testing/selftests/vm/gup_benchmark.c for userspace counterpart. Link: http://lkml.kernel.org/r/20170908215603.9189-2-kirill.shutemov@linux.intel.com Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Cc: Shuah Khan <shuah@kernel.org> Cc: Ingo Molnar <mingo@kernel.org> Cc: Thorsten Leemhuis <regressions@leemhuis.info> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Huang Ying <ying.huang@intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--mm/Kconfig9
-rw-r--r--mm/Makefile1
-rw-r--r--mm/gup_benchmark.c100
-rw-r--r--tools/testing/selftests/vm/Makefile1
-rw-r--r--tools/testing/selftests/vm/gup_benchmark.c91
5 files changed, 202 insertions, 0 deletions
diff --git a/mm/Kconfig b/mm/Kconfig
index 9c4bdddd80c2..03ff7703d322 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -756,3 +756,12 @@ config PERCPU_STATS
756 This feature collects and exposes statistics via debugfs. The 756 This feature collects and exposes statistics via debugfs. The
757 information includes global and per chunk statistics, which can 757 information includes global and per chunk statistics, which can
758 be used to help understand percpu memory usage. 758 be used to help understand percpu memory usage.
759
760config GUP_BENCHMARK
761 bool "Enable infrastructure for get_user_pages_fast() benchmarking"
762 default n
763 help
764 Provides /sys/kernel/debug/gup_benchmark that helps with testing
765 performance of get_user_pages_fast().
766
767 See tools/testing/selftests/vm/gup_benchmark.c
diff --git a/mm/Makefile b/mm/Makefile
index e7ebd176fb93..e669f02c5a54 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -80,6 +80,7 @@ obj-$(CONFIG_PAGE_COUNTER) += page_counter.o
80obj-$(CONFIG_MEMCG) += memcontrol.o vmpressure.o 80obj-$(CONFIG_MEMCG) += memcontrol.o vmpressure.o
81obj-$(CONFIG_MEMCG_SWAP) += swap_cgroup.o 81obj-$(CONFIG_MEMCG_SWAP) += swap_cgroup.o
82obj-$(CONFIG_CGROUP_HUGETLB) += hugetlb_cgroup.o 82obj-$(CONFIG_CGROUP_HUGETLB) += hugetlb_cgroup.o
83obj-$(CONFIG_GUP_BENCHMARK) += gup_benchmark.o
83obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o 84obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o
84obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o 85obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o
85obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o 86obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
diff --git a/mm/gup_benchmark.c b/mm/gup_benchmark.c
new file mode 100644
index 000000000000..5c8e2abeaa15
--- /dev/null
+++ b/mm/gup_benchmark.c
@@ -0,0 +1,100 @@
1#include <linux/kernel.h>
2#include <linux/mm.h>
3#include <linux/slab.h>
4#include <linux/uaccess.h>
5#include <linux/ktime.h>
6#include <linux/debugfs.h>
7
8#define GUP_FAST_BENCHMARK _IOWR('g', 1, struct gup_benchmark)
9
10struct gup_benchmark {
11 __u64 delta_usec;
12 __u64 addr;
13 __u64 size;
14 __u32 nr_pages_per_call;
15 __u32 flags;
16};
17
18static int __gup_benchmark_ioctl(unsigned int cmd,
19 struct gup_benchmark *gup)
20{
21 ktime_t start_time, end_time;
22 unsigned long i, nr, nr_pages, addr, next;
23 struct page **pages;
24
25 nr_pages = gup->size / PAGE_SIZE;
26 pages = kvmalloc(sizeof(void *) * nr_pages, GFP_KERNEL);
27 if (!pages)
28 return -ENOMEM;
29
30 i = 0;
31 nr = gup->nr_pages_per_call;
32 start_time = ktime_get();
33 for (addr = gup->addr; addr < gup->addr + gup->size; addr = next) {
34 if (nr != gup->nr_pages_per_call)
35 break;
36
37 next = addr + nr * PAGE_SIZE;
38 if (next > gup->addr + gup->size) {
39 next = gup->addr + gup->size;
40 nr = (next - addr) / PAGE_SIZE;
41 }
42
43 nr = get_user_pages_fast(addr, nr, gup->flags & 1, pages + i);
44 i += nr;
45 }
46 end_time = ktime_get();
47
48 gup->delta_usec = ktime_us_delta(end_time, start_time);
49 gup->size = addr - gup->addr;
50
51 for (i = 0; i < nr_pages; i++) {
52 if (!pages[i])
53 break;
54 put_page(pages[i]);
55 }
56
57 kvfree(pages);
58 return 0;
59}
60
61static long gup_benchmark_ioctl(struct file *filep, unsigned int cmd,
62 unsigned long arg)
63{
64 struct gup_benchmark gup;
65 int ret;
66
67 if (cmd != GUP_FAST_BENCHMARK)
68 return -EINVAL;
69
70 if (copy_from_user(&gup, (void __user *)arg, sizeof(gup)))
71 return -EFAULT;
72
73 ret = __gup_benchmark_ioctl(cmd, &gup);
74 if (ret)
75 return ret;
76
77 if (copy_to_user((void __user *)arg, &gup, sizeof(gup)))
78 return -EFAULT;
79
80 return 0;
81}
82
83static const struct file_operations gup_benchmark_fops = {
84 .open = nonseekable_open,
85 .unlocked_ioctl = gup_benchmark_ioctl,
86};
87
88static int gup_benchmark_init(void)
89{
90 void *ret;
91
92 ret = debugfs_create_file_unsafe("gup_benchmark", 0600, NULL, NULL,
93 &gup_benchmark_fops);
94 if (!ret)
95 pr_warn("Failed to create gup_benchmark in debugfs");
96
97 return 0;
98}
99
100late_initcall(gup_benchmark_init);
diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile
index e49eca1915f8..7f45806bd863 100644
--- a/tools/testing/selftests/vm/Makefile
+++ b/tools/testing/selftests/vm/Makefile
@@ -18,6 +18,7 @@ TEST_GEN_FILES += transhuge-stress
18TEST_GEN_FILES += userfaultfd 18TEST_GEN_FILES += userfaultfd
19TEST_GEN_FILES += mlock-random-test 19TEST_GEN_FILES += mlock-random-test
20TEST_GEN_FILES += virtual_address_range 20TEST_GEN_FILES += virtual_address_range
21TEST_GEN_FILES += gup_benchmark
21 22
22TEST_PROGS := run_vmtests 23TEST_PROGS := run_vmtests
23 24
diff --git a/tools/testing/selftests/vm/gup_benchmark.c b/tools/testing/selftests/vm/gup_benchmark.c
new file mode 100644
index 000000000000..36df55132036
--- /dev/null
+++ b/tools/testing/selftests/vm/gup_benchmark.c
@@ -0,0 +1,91 @@
1#include <fcntl.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <unistd.h>
5
6#include <sys/ioctl.h>
7#include <sys/mman.h>
8#include <sys/prctl.h>
9#include <sys/stat.h>
10#include <sys/types.h>
11
12#include <linux/types.h>
13
14#define MB (1UL << 20)
15#define PAGE_SIZE sysconf(_SC_PAGESIZE)
16
17#define GUP_FAST_BENCHMARK _IOWR('g', 1, struct gup_benchmark)
18
19struct gup_benchmark {
20 __u64 delta_usec;
21 __u64 addr;
22 __u64 size;
23 __u32 nr_pages_per_call;
24 __u32 flags;
25};
26
27int main(int argc, char **argv)
28{
29 struct gup_benchmark gup;
30 unsigned long size = 128 * MB;
31 int i, fd, opt, nr_pages = 1, thp = -1, repeats = 1, write = 0;
32 char *p;
33
34 while ((opt = getopt(argc, argv, "m:r:n:tT")) != -1) {
35 switch (opt) {
36 case 'm':
37 size = atoi(optarg) * MB;
38 break;
39 case 'r':
40 repeats = atoi(optarg);
41 break;
42 case 'n':
43 nr_pages = atoi(optarg);
44 break;
45 case 't':
46 thp = 1;
47 break;
48 case 'T':
49 thp = 0;
50 break;
51 case 'w':
52 write = 1;
53 default:
54 return -1;
55 }
56 }
57
58 gup.nr_pages_per_call = nr_pages;
59 gup.flags = write;
60
61 fd = open("/sys/kernel/debug/gup_benchmark", O_RDWR);
62 if (fd == -1)
63 perror("open"), exit(1);
64