1 #define __SANE_USERSPACE_TYPES__ // Use ll64
11 #include <sys/types.h>
14 #include <mm/gup_test.h>
15 #include "../kselftest.h"
18 #define MB (1UL << 20)
20 /* Just the flags we need, copied from mm.h: */
21 #define FOLL_WRITE 0x01 /* check pte is writable */
22 #define FOLL_TOUCH 0x02 /* mark page accessed */
24 #define GUP_TEST_FILE "/sys/kernel/debug/gup_test"
26 static unsigned long cmd
= GUP_FAST_BENCHMARK
;
27 static int gup_fd
, repeats
= 1;
28 static unsigned long size
= 128 * MB
;
29 /* Serialize prints */
30 static pthread_mutex_t print_mutex
= PTHREAD_MUTEX_INITIALIZER
;
32 static char *cmd_to_str(unsigned long cmd
)
35 case GUP_FAST_BENCHMARK
:
36 return "GUP_FAST_BENCHMARK";
37 case PIN_FAST_BENCHMARK
:
38 return "PIN_FAST_BENCHMARK";
39 case PIN_LONGTERM_BENCHMARK
:
40 return "PIN_LONGTERM_BENCHMARK";
42 return "GUP_BASIC_TEST";
44 return "PIN_BASIC_TEST";
45 case DUMP_USER_PAGES_TEST
:
46 return "DUMP_USER_PAGES_TEST";
48 return "Unknown command";
51 void *gup_thread(void *data
)
53 struct gup_test gup
= *(struct gup_test
*)data
;
56 /* Only report timing information on the *_BENCHMARK commands: */
57 if ((cmd
== PIN_FAST_BENCHMARK
) || (cmd
== GUP_FAST_BENCHMARK
) ||
58 (cmd
== PIN_LONGTERM_BENCHMARK
)) {
59 for (i
= 0; i
< repeats
; i
++) {
61 status
= ioctl(gup_fd
, cmd
, &gup
);
65 pthread_mutex_lock(&print_mutex
);
66 ksft_print_msg("%s: Time: get:%lld put:%lld us",
67 cmd_to_str(cmd
), gup
.get_delta_usec
,
70 ksft_print_msg(", truncated (size: %lld)", gup
.size
);
72 pthread_mutex_unlock(&print_mutex
);
76 status
= ioctl(gup_fd
, cmd
, &gup
);
80 pthread_mutex_lock(&print_mutex
);
81 ksft_print_msg("%s: done\n", cmd_to_str(cmd
));
83 ksft_print_msg("Truncated (size: %lld)\n", gup
.size
);
84 pthread_mutex_unlock(&print_mutex
);
88 ksft_test_result(!status
, "ioctl status %d\n", status
);
92 int main(int argc
, char **argv
)
94 struct gup_test gup
= { 0 };
95 int filed
, i
, opt
, nr_pages
= 1, thp
= -1, write
= 1, nthreads
= 1, ret
;
96 int flags
= MAP_PRIVATE
, touch
= 0;
97 char *file
= "/dev/zero";
101 while ((opt
= getopt(argc
, argv
, "m:r:n:F:f:abcj:tTLUuwWSHpz")) != -1) {
104 cmd
= PIN_FAST_BENCHMARK
;
107 cmd
= PIN_BASIC_TEST
;
110 cmd
= PIN_LONGTERM_BENCHMARK
;
113 cmd
= DUMP_USER_PAGES_TEST
;
115 * Dump page 0 (index 1). May be overridden later, by
116 * user's non-option arguments.
118 * .which_pages is zero-based, so that zero can mean "do
121 gup
.which_pages
[0] = 1;
124 /* works only with DUMP_USER_PAGES_TEST */
125 gup
.test_flags
|= GUP_TEST_FLAG_DUMP_PAGES_USE_PIN
;
128 /* strtol, so you can pass flags in hex form */
129 gup
.gup_flags
= strtol(optarg
, 0, 0);
132 nthreads
= atoi(optarg
);
135 size
= atoi(optarg
) * MB
;
138 repeats
= atoi(optarg
);
141 nr_pages
= atoi(optarg
);
150 cmd
= GUP_BASIC_TEST
;
153 cmd
= GUP_FAST_BENCHMARK
;
165 flags
&= ~MAP_PRIVATE
;
169 flags
|= (MAP_HUGETLB
| MAP_ANONYMOUS
);
172 /* fault pages in gup, do not fault in userland */
176 ksft_exit_fail_msg("Wrong argument\n");
181 int extra_arg_count
= 0;
185 * ./gup_test -c 0 1 0x1001
187 * ...to dump pages 0, 1, and 4097
190 while ((optind
< argc
) &&
191 (extra_arg_count
< GUP_TEST_MAX_PAGES_TO_DUMP
)) {
193 * Do the 1-based indexing here, so that the user can
194 * use normal 0-based indexing on the command line.
196 long page_index
= strtol(argv
[optind
], 0, 0) + 1;
198 gup
.which_pages
[extra_arg_count
] = page_index
;
205 ksft_set_plan(nthreads
);
207 filed
= open(file
, O_RDWR
|O_CREAT
, 0664);
209 ksft_exit_fail_msg("Unable to open %s: %s\n", file
, strerror(errno
));
211 gup
.nr_pages_per_call
= nr_pages
;
213 gup
.gup_flags
|= FOLL_WRITE
;
215 gup_fd
= open(GUP_TEST_FILE
, O_RDWR
);
220 ksft_print_msg("Please run this test as root\n");
223 if (opendir("/sys/kernel/debug") == NULL
)
224 ksft_print_msg("mount debugfs at /sys/kernel/debug\n");
225 ksft_print_msg("check if CONFIG_GUP_TEST is enabled in kernel config\n");
228 ksft_print_msg("failed to open %s: %s\n", GUP_TEST_FILE
, strerror(errno
));
231 ksft_test_result_skip("Please run this test as root\n");
235 p
= mmap(NULL
, size
, PROT_READ
| PROT_WRITE
, flags
, filed
, 0);
237 ksft_exit_fail_msg("mmap: %s\n", strerror(errno
));
238 gup
.addr
= (unsigned long)p
;
241 madvise(p
, size
, MADV_HUGEPAGE
);
243 madvise(p
, size
, MADV_NOHUGEPAGE
);
246 * FOLL_TOUCH, in gup_test, is used as an either/or case: either
247 * fault pages in from the kernel via FOLL_TOUCH, or fault them
248 * in here, from user space. This allows comparison of performance
249 * between those two cases.
252 gup
.gup_flags
|= FOLL_TOUCH
;
254 for (; (unsigned long)p
< gup
.addr
+ size
; p
+= psize())
258 tid
= malloc(sizeof(pthread_t
) * nthreads
);
260 for (i
= 0; i
< nthreads
; i
++) {
261 ret
= pthread_create(&tid
[i
], NULL
, gup_thread
, &gup
);
264 for (i
= 0; i
< nthreads
; i
++) {
265 ret
= pthread_join(tid
[i
], NULL
);