mm/gup: fix comments to pin_user_pages_*()
[linux-block.git] / tools / testing / selftests / vm / gup_test.c
CommitLineData
64c349f4 1#include <fcntl.h>
62e80f2b 2#include <errno.h>
64c349f4
KS
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
62e80f2b 6#include <dirent.h>
64c349f4
KS
7#include <sys/ioctl.h>
8#include <sys/mman.h>
64c349f4
KS
9#include <sys/stat.h>
10#include <sys/types.h>
f39bd853
PX
11#include <pthread.h>
12#include <assert.h>
b9dcfdff 13#include "../../../../mm/gup_test.h"
62e80f2b 14#include "../kselftest.h"
64c349f4 15
6f6a841f
MR
16#include "util.h"
17
64c349f4 18#define MB (1UL << 20)
64c349f4 19
bdffe23e
JH
20/* Just the flags we need, copied from mm.h: */
21#define FOLL_WRITE 0x01 /* check pte is writable */
e44605a8 22#define FOLL_TOUCH 0x02 /* mark page accessed */
bdffe23e 23
f39bd853
PX
24static unsigned long cmd = GUP_FAST_BENCHMARK;
25static int gup_fd, repeats = 1;
26static unsigned long size = 128 * MB;
27/* Serialize prints */
28static pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;
29
a9bed1e1
JH
30static char *cmd_to_str(unsigned long cmd)
31{
32 switch (cmd) {
33 case GUP_FAST_BENCHMARK:
34 return "GUP_FAST_BENCHMARK";
35 case PIN_FAST_BENCHMARK:
36 return "PIN_FAST_BENCHMARK";
37 case PIN_LONGTERM_BENCHMARK:
38 return "PIN_LONGTERM_BENCHMARK";
39 case GUP_BASIC_TEST:
40 return "GUP_BASIC_TEST";
41 case PIN_BASIC_TEST:
42 return "PIN_BASIC_TEST";
f4f9bda4
JH
43 case DUMP_USER_PAGES_TEST:
44 return "DUMP_USER_PAGES_TEST";
a9bed1e1
JH
45 }
46 return "Unknown command";
47}
48
f39bd853
PX
49void *gup_thread(void *data)
50{
51 struct gup_test gup = *(struct gup_test *)data;
52 int i;
53
54 /* Only report timing information on the *_BENCHMARK commands: */
55 if ((cmd == PIN_FAST_BENCHMARK) || (cmd == GUP_FAST_BENCHMARK) ||
56 (cmd == PIN_LONGTERM_BENCHMARK)) {
57 for (i = 0; i < repeats; i++) {
58 gup.size = size;
59 if (ioctl(gup_fd, cmd, &gup))
60 perror("ioctl"), exit(1);
61
62 pthread_mutex_lock(&print_mutex);
63 printf("%s: Time: get:%lld put:%lld us",
64 cmd_to_str(cmd), gup.get_delta_usec,
65 gup.put_delta_usec);
66 if (gup.size != size)
67 printf(", truncated (size: %lld)", gup.size);
68 printf("\n");
69 pthread_mutex_unlock(&print_mutex);
70 }
71 } else {
72 gup.size = size;
73 if (ioctl(gup_fd, cmd, &gup)) {
74 perror("ioctl");
75 exit(1);
76 }
77
78 pthread_mutex_lock(&print_mutex);
79 printf("%s: done\n", cmd_to_str(cmd));
80 if (gup.size != size)
81 printf("Truncated (size: %lld)\n", gup.size);
82 pthread_mutex_unlock(&print_mutex);
83 }
84
85 return NULL;
86}
87
64c349f4
KS
88int main(int argc, char **argv)
89{
f4f9bda4 90 struct gup_test gup = { 0 };
f39bd853 91 int filed, i, opt, nr_pages = 1, thp = -1, write = 1, nthreads = 1, ret;
e44605a8 92 int flags = MAP_PRIVATE, touch = 0;
aeb85ed4 93 char *file = "/dev/zero";
f39bd853 94 pthread_t *tid;
64c349f4
KS
95 char *p;
96
f39bd853 97 while ((opt = getopt(argc, argv, "m:r:n:F:f:abcj:tTLUuwWSHpz")) != -1) {
64c349f4 98 switch (opt) {
41c45d37
JH
99 case 'a':
100 cmd = PIN_FAST_BENCHMARK;
101 break;
102 case 'b':
a9bed1e1 103 cmd = PIN_BASIC_TEST;
41c45d37 104 break;
657d4f79
BS
105 case 'L':
106 cmd = PIN_LONGTERM_BENCHMARK;
107 break;
f4f9bda4
JH
108 case 'c':
109 cmd = DUMP_USER_PAGES_TEST;
110 /*
111 * Dump page 0 (index 1). May be overridden later, by
112 * user's non-option arguments.
113 *
114 * .which_pages is zero-based, so that zero can mean "do
115 * nothing".
116 */
117 gup.which_pages[0] = 1;
118 break;
79dbf135
PT
119 case 'p':
120 /* works only with DUMP_USER_PAGES_TEST */
121 gup.test_flags |= GUP_TEST_FLAG_DUMP_PAGES_USE_PIN;
122 break;
f4f9bda4
JH
123 case 'F':
124 /* strtol, so you can pass flags in hex form */
79dbf135 125 gup.gup_flags = strtol(optarg, 0, 0);
f4f9bda4 126 break;
f39bd853
PX
127 case 'j':
128 nthreads = atoi(optarg);
129 break;
64c349f4
KS
130 case 'm':
131 size = atoi(optarg) * MB;
132 break;
133 case 'r':
134 repeats = atoi(optarg);
135 break;
136 case 'n':
137 nr_pages = atoi(optarg);
138 break;
139 case 't':
140 thp = 1;
141 break;
142 case 'T':
143 thp = 0;
144 break;
714a3a1e 145 case 'U':
a9bed1e1 146 cmd = GUP_BASIC_TEST;
714a3a1e 147 break;
41c45d37
JH
148 case 'u':
149 cmd = GUP_FAST_BENCHMARK;
150 break;
64c349f4
KS
151 case 'w':
152 write = 1;
319e0bec 153 break;
79dbf135
PT
154 case 'W':
155 write = 0;
156 break;
aeb85ed4
KB
157 case 'f':
158 file = optarg;
159 break;
0dd8666a
KB
160 case 'S':
161 flags &= ~MAP_PRIVATE;
162 flags |= MAP_SHARED;
163 break;
3821b76c 164 case 'H':
64801d19 165 flags |= (MAP_HUGETLB | MAP_ANONYMOUS);
3821b76c 166 break;
e44605a8
PT
167 case 'z':
168 /* fault pages in gup, do not fault in userland */
169 touch = 1;
170 break;
64c349f4
KS
171 default:
172 return -1;
173 }
174 }
175
f4f9bda4
JH
176 if (optind < argc) {
177 int extra_arg_count = 0;
178 /*
179 * For example:
180 *
181 * ./gup_test -c 0 1 0x1001
182 *
183 * ...to dump pages 0, 1, and 4097
184 */
185
186 while ((optind < argc) &&
187 (extra_arg_count < GUP_TEST_MAX_PAGES_TO_DUMP)) {
188 /*
189 * Do the 1-based indexing here, so that the user can
190 * use normal 0-based indexing on the command line.
191 */
192 long page_index = strtol(argv[optind], 0, 0) + 1;
193
194 gup.which_pages[extra_arg_count] = page_index;
195 extra_arg_count++;
196 optind++;
197 }
198 }
199
aeb85ed4
KB
200 filed = open(file, O_RDWR|O_CREAT);
201 if (filed < 0) {
202 perror("open");
203 exit(filed);
204 }
205
64c349f4 206 gup.nr_pages_per_call = nr_pages;
bdffe23e 207 if (write)
79dbf135 208 gup.gup_flags |= FOLL_WRITE;
64c349f4 209
f39bd853
PX
210 gup_fd = open("/sys/kernel/debug/gup_test", O_RDWR);
211 if (gup_fd == -1) {
62e80f2b
SK
212 switch (errno) {
213 case EACCES:
214 if (getuid())
215 printf("Please run this test as root\n");
216 break;
217 case ENOENT:
218 if (opendir("/sys/kernel/debug") == NULL) {
219 printf("mount debugfs at /sys/kernel/debug\n");
220 break;
221 }
222 printf("check if CONFIG_GUP_TEST is enabled in kernel config\n");
223 break;
224 default:
225 perror("failed to open /sys/kernel/debug/gup_test");
226 break;
227 }
228 exit(KSFT_SKIP);
aa803771 229 }
64c349f4 230
0dd8666a 231 p = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, filed, 0);
aa803771
JP
232 if (p == MAP_FAILED) {
233 perror("mmap");
234 exit(1);
235 }
64c349f4
KS
236 gup.addr = (unsigned long)p;
237
238 if (thp == 1)
239 madvise(p, size, MADV_HUGEPAGE);
240 else if (thp == 0)
241 madvise(p, size, MADV_NOHUGEPAGE);
242
e44605a8
PT
243 /*
244 * FOLL_TOUCH, in gup_test, is used as an either/or case: either
245 * fault pages in from the kernel via FOLL_TOUCH, or fault them
246 * in here, from user space. This allows comparison of performance
247 * between those two cases.
248 */
249 if (touch) {
250 gup.gup_flags |= FOLL_TOUCH;
251 } else {
252 for (; (unsigned long)p < gup.addr + size; p += PAGE_SIZE)
253 p[0] = 0;
254 }
64c349f4 255
f39bd853
PX
256 tid = malloc(sizeof(pthread_t) * nthreads);
257 assert(tid);
258 for (i = 0; i < nthreads; i++) {
259 ret = pthread_create(&tid[i], NULL, gup_thread, &gup);
260 assert(ret == 0);
261 }
262 for (i = 0; i < nthreads; i++) {
263 ret = pthread_join(tid[i], NULL);
264 assert(ret == 0);
64c349f4 265 }
f39bd853 266 free(tid);
64c349f4
KS
267
268 return 0;
269}