0001 #include <fcntl.h>
0002 #include <errno.h>
0003 #include <stdio.h>
0004 #include <stdlib.h>
0005 #include <unistd.h>
0006 #include <dirent.h>
0007 #include <sys/ioctl.h>
0008 #include <sys/mman.h>
0009 #include <sys/stat.h>
0010 #include <sys/types.h>
0011 #include <pthread.h>
0012 #include <assert.h>
0013 #include "../../../../mm/gup_test.h"
0014 #include "../kselftest.h"
0015
0016 #include "util.h"
0017
0018 #define MB (1UL << 20)
0019
0020
0021 #define FOLL_WRITE 0x01
0022 #define FOLL_TOUCH 0x02
0023
0024 #define GUP_TEST_FILE "/sys/kernel/debug/gup_test"
0025
0026 static unsigned long cmd = GUP_FAST_BENCHMARK;
0027 static int gup_fd, repeats = 1;
0028 static unsigned long size = 128 * MB;
0029
0030 static pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;
0031
0032 static char *cmd_to_str(unsigned long cmd)
0033 {
0034 switch (cmd) {
0035 case GUP_FAST_BENCHMARK:
0036 return "GUP_FAST_BENCHMARK";
0037 case PIN_FAST_BENCHMARK:
0038 return "PIN_FAST_BENCHMARK";
0039 case PIN_LONGTERM_BENCHMARK:
0040 return "PIN_LONGTERM_BENCHMARK";
0041 case GUP_BASIC_TEST:
0042 return "GUP_BASIC_TEST";
0043 case PIN_BASIC_TEST:
0044 return "PIN_BASIC_TEST";
0045 case DUMP_USER_PAGES_TEST:
0046 return "DUMP_USER_PAGES_TEST";
0047 }
0048 return "Unknown command";
0049 }
0050
0051 void *gup_thread(void *data)
0052 {
0053 struct gup_test gup = *(struct gup_test *)data;
0054 int i;
0055
0056
0057 if ((cmd == PIN_FAST_BENCHMARK) || (cmd == GUP_FAST_BENCHMARK) ||
0058 (cmd == PIN_LONGTERM_BENCHMARK)) {
0059 for (i = 0; i < repeats; i++) {
0060 gup.size = size;
0061 if (ioctl(gup_fd, cmd, &gup))
0062 perror("ioctl"), exit(1);
0063
0064 pthread_mutex_lock(&print_mutex);
0065 printf("%s: Time: get:%lld put:%lld us",
0066 cmd_to_str(cmd), gup.get_delta_usec,
0067 gup.put_delta_usec);
0068 if (gup.size != size)
0069 printf(", truncated (size: %lld)", gup.size);
0070 printf("\n");
0071 pthread_mutex_unlock(&print_mutex);
0072 }
0073 } else {
0074 gup.size = size;
0075 if (ioctl(gup_fd, cmd, &gup)) {
0076 perror("ioctl");
0077 exit(1);
0078 }
0079
0080 pthread_mutex_lock(&print_mutex);
0081 printf("%s: done\n", cmd_to_str(cmd));
0082 if (gup.size != size)
0083 printf("Truncated (size: %lld)\n", gup.size);
0084 pthread_mutex_unlock(&print_mutex);
0085 }
0086
0087 return NULL;
0088 }
0089
0090 int main(int argc, char **argv)
0091 {
0092 struct gup_test gup = { 0 };
0093 int filed, i, opt, nr_pages = 1, thp = -1, write = 1, nthreads = 1, ret;
0094 int flags = MAP_PRIVATE, touch = 0;
0095 char *file = "/dev/zero";
0096 pthread_t *tid;
0097 char *p;
0098
0099 while ((opt = getopt(argc, argv, "m:r:n:F:f:abcj:tTLUuwWSHpz")) != -1) {
0100 switch (opt) {
0101 case 'a':
0102 cmd = PIN_FAST_BENCHMARK;
0103 break;
0104 case 'b':
0105 cmd = PIN_BASIC_TEST;
0106 break;
0107 case 'L':
0108 cmd = PIN_LONGTERM_BENCHMARK;
0109 break;
0110 case 'c':
0111 cmd = DUMP_USER_PAGES_TEST;
0112
0113
0114
0115
0116
0117
0118
0119 gup.which_pages[0] = 1;
0120 break;
0121 case 'p':
0122
0123 gup.test_flags |= GUP_TEST_FLAG_DUMP_PAGES_USE_PIN;
0124 break;
0125 case 'F':
0126
0127 gup.gup_flags = strtol(optarg, 0, 0);
0128 break;
0129 case 'j':
0130 nthreads = atoi(optarg);
0131 break;
0132 case 'm':
0133 size = atoi(optarg) * MB;
0134 break;
0135 case 'r':
0136 repeats = atoi(optarg);
0137 break;
0138 case 'n':
0139 nr_pages = atoi(optarg);
0140 break;
0141 case 't':
0142 thp = 1;
0143 break;
0144 case 'T':
0145 thp = 0;
0146 break;
0147 case 'U':
0148 cmd = GUP_BASIC_TEST;
0149 break;
0150 case 'u':
0151 cmd = GUP_FAST_BENCHMARK;
0152 break;
0153 case 'w':
0154 write = 1;
0155 break;
0156 case 'W':
0157 write = 0;
0158 break;
0159 case 'f':
0160 file = optarg;
0161 break;
0162 case 'S':
0163 flags &= ~MAP_PRIVATE;
0164 flags |= MAP_SHARED;
0165 break;
0166 case 'H':
0167 flags |= (MAP_HUGETLB | MAP_ANONYMOUS);
0168 break;
0169 case 'z':
0170
0171 touch = 1;
0172 break;
0173 default:
0174 return -1;
0175 }
0176 }
0177
0178 if (optind < argc) {
0179 int extra_arg_count = 0;
0180
0181
0182
0183
0184
0185
0186
0187
0188 while ((optind < argc) &&
0189 (extra_arg_count < GUP_TEST_MAX_PAGES_TO_DUMP)) {
0190
0191
0192
0193
0194 long page_index = strtol(argv[optind], 0, 0) + 1;
0195
0196 gup.which_pages[extra_arg_count] = page_index;
0197 extra_arg_count++;
0198 optind++;
0199 }
0200 }
0201
0202 filed = open(file, O_RDWR|O_CREAT);
0203 if (filed < 0) {
0204 perror("open");
0205 exit(filed);
0206 }
0207
0208 gup.nr_pages_per_call = nr_pages;
0209 if (write)
0210 gup.gup_flags |= FOLL_WRITE;
0211
0212 gup_fd = open(GUP_TEST_FILE, O_RDWR);
0213 if (gup_fd == -1) {
0214 switch (errno) {
0215 case EACCES:
0216 if (getuid())
0217 printf("Please run this test as root\n");
0218 break;
0219 case ENOENT:
0220 if (opendir("/sys/kernel/debug") == NULL) {
0221 printf("mount debugfs at /sys/kernel/debug\n");
0222 break;
0223 }
0224 printf("check if CONFIG_GUP_TEST is enabled in kernel config\n");
0225 break;
0226 default:
0227 perror("failed to open " GUP_TEST_FILE);
0228 break;
0229 }
0230 exit(KSFT_SKIP);
0231 }
0232
0233 p = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, filed, 0);
0234 if (p == MAP_FAILED) {
0235 perror("mmap");
0236 exit(1);
0237 }
0238 gup.addr = (unsigned long)p;
0239
0240 if (thp == 1)
0241 madvise(p, size, MADV_HUGEPAGE);
0242 else if (thp == 0)
0243 madvise(p, size, MADV_NOHUGEPAGE);
0244
0245
0246
0247
0248
0249
0250
0251 if (touch) {
0252 gup.gup_flags |= FOLL_TOUCH;
0253 } else {
0254 for (; (unsigned long)p < gup.addr + size; p += PAGE_SIZE)
0255 p[0] = 0;
0256 }
0257
0258 tid = malloc(sizeof(pthread_t) * nthreads);
0259 assert(tid);
0260 for (i = 0; i < nthreads; i++) {
0261 ret = pthread_create(&tid[i], NULL, gup_thread, &gup);
0262 assert(ret == 0);
0263 }
0264 for (i = 0; i < nthreads; i++) {
0265 ret = pthread_join(tid[i], NULL);
0266 assert(ret == 0);
0267 }
0268 free(tid);
0269
0270 return 0;
0271 }