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