Back to home page

OSCL-LXR

 
 

    


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 /* Just the flags we need, copied from mm.h: */
0021 #define FOLL_WRITE  0x01    /* check pte is writable */
0022 #define FOLL_TOUCH  0x02    /* mark page accessed */
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 /* Serialize prints */
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     /* Only report timing information on the *_BENCHMARK commands: */
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              * Dump page 0 (index 1). May be overridden later, by
0114              * user's non-option arguments.
0115              *
0116              * .which_pages is zero-based, so that zero can mean "do
0117              * nothing".
0118              */
0119             gup.which_pages[0] = 1;
0120             break;
0121         case 'p':
0122             /* works only with DUMP_USER_PAGES_TEST */
0123             gup.test_flags |= GUP_TEST_FLAG_DUMP_PAGES_USE_PIN;
0124             break;
0125         case 'F':
0126             /* strtol, so you can pass flags in hex form */
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             /* fault pages in gup, do not fault in userland */
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          * For example:
0182          *
0183          *   ./gup_test -c 0 1 0x1001
0184          *
0185          * ...to dump pages 0, 1, and 4097
0186          */
0187 
0188         while ((optind < argc) &&
0189                (extra_arg_count < GUP_TEST_MAX_PAGES_TO_DUMP)) {
0190             /*
0191              * Do the 1-based indexing here, so that the user can
0192              * use normal 0-based indexing on the command line.
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      * FOLL_TOUCH, in gup_test, is used as an either/or case: either
0247      * fault pages in from the kernel via FOLL_TOUCH, or fault them
0248      * in here, from user space. This allows comparison of performance
0249      * between those two cases.
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 }