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