| #include <fcntl.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #include <sys/ioctl.h> | |
| #include <sys/mman.h> | |
| #include <sys/stat.h> | |
| #include <sys/types.h> | |
| #include "../../../../mm/gup_test.h" | |
| #define MB (1UL << 20) | |
| #define PAGE_SIZE sysconf(_SC_PAGESIZE) | |
| /* Just the flags we need, copied from mm.h: */ | |
| #define FOLL_WRITE 0x01 /* check pte is writable */ | |
| static char *cmd_to_str(unsigned long cmd) | |
| { | |
| switch (cmd) { | |
| case GUP_FAST_BENCHMARK: | |
| return "GUP_FAST_BENCHMARK"; | |
| case PIN_FAST_BENCHMARK: | |
| return "PIN_FAST_BENCHMARK"; | |
| case PIN_LONGTERM_BENCHMARK: | |
| return "PIN_LONGTERM_BENCHMARK"; | |
| case GUP_BASIC_TEST: | |
| return "GUP_BASIC_TEST"; | |
| case PIN_BASIC_TEST: | |
| return "PIN_BASIC_TEST"; | |
| case DUMP_USER_PAGES_TEST: | |
| return "DUMP_USER_PAGES_TEST"; | |
| } | |
| return "Unknown command"; | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| struct gup_test gup = { 0 }; | |
| unsigned long size = 128 * MB; | |
| int i, fd, filed, opt, nr_pages = 1, thp = -1, repeats = 1, write = 0; | |
| unsigned long cmd = GUP_FAST_BENCHMARK; | |
| int flags = MAP_PRIVATE; | |
| char *file = "/dev/zero"; | |
| char *p; | |
| while ((opt = getopt(argc, argv, "m:r:n:F:f:abctTLUuwSH")) != -1) { | |
| switch (opt) { | |
| case 'a': | |
| cmd = PIN_FAST_BENCHMARK; | |
| break; | |
| case 'b': | |
| cmd = PIN_BASIC_TEST; | |
| break; | |
| case 'L': | |
| cmd = PIN_LONGTERM_BENCHMARK; | |
| break; | |
| case 'c': | |
| cmd = DUMP_USER_PAGES_TEST; | |
| /* | |
| * Dump page 0 (index 1). May be overridden later, by | |
| * user's non-option arguments. | |
| * | |
| * .which_pages is zero-based, so that zero can mean "do | |
| * nothing". | |
| */ | |
| gup.which_pages[0] = 1; | |
| break; | |
| case 'F': | |
| /* strtol, so you can pass flags in hex form */ | |
| gup.flags = strtol(optarg, 0, 0); | |
| break; | |
| case 'm': | |
| size = atoi(optarg) * MB; | |
| break; | |
| case 'r': | |
| repeats = atoi(optarg); | |
| break; | |
| case 'n': | |
| nr_pages = atoi(optarg); | |
| break; | |
| case 't': | |
| thp = 1; | |
| break; | |
| case 'T': | |
| thp = 0; | |
| break; | |
| case 'U': | |
| cmd = GUP_BASIC_TEST; | |
| break; | |
| case 'u': | |
| cmd = GUP_FAST_BENCHMARK; | |
| break; | |
| case 'w': | |
| write = 1; | |
| break; | |
| case 'f': | |
| file = optarg; | |
| break; | |
| case 'S': | |
| flags &= ~MAP_PRIVATE; | |
| flags |= MAP_SHARED; | |
| break; | |
| case 'H': | |
| flags |= (MAP_HUGETLB | MAP_ANONYMOUS); | |
| break; | |
| default: | |
| return -1; | |
| } | |
| } | |
| if (optind < argc) { | |
| int extra_arg_count = 0; | |
| /* | |
| * For example: | |
| * | |
| * ./gup_test -c 0 1 0x1001 | |
| * | |
| * ...to dump pages 0, 1, and 4097 | |
| */ | |
| while ((optind < argc) && | |
| (extra_arg_count < GUP_TEST_MAX_PAGES_TO_DUMP)) { | |
| /* | |
| * Do the 1-based indexing here, so that the user can | |
| * use normal 0-based indexing on the command line. | |
| */ | |
| long page_index = strtol(argv[optind], 0, 0) + 1; | |
| gup.which_pages[extra_arg_count] = page_index; | |
| extra_arg_count++; | |
| optind++; | |
| } | |
| } | |
| filed = open(file, O_RDWR|O_CREAT); | |
| if (filed < 0) { | |
| perror("open"); | |
| exit(filed); | |
| } | |
| gup.nr_pages_per_call = nr_pages; | |
| if (write) | |
| gup.flags |= FOLL_WRITE; | |
| fd = open("/sys/kernel/debug/gup_test", O_RDWR); | |
| if (fd == -1) { | |
| perror("open"); | |
| exit(1); | |
| } | |
| p = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, filed, 0); | |
| if (p == MAP_FAILED) { | |
| perror("mmap"); | |
| exit(1); | |
| } | |
| gup.addr = (unsigned long)p; | |
| if (thp == 1) | |
| madvise(p, size, MADV_HUGEPAGE); | |
| else if (thp == 0) | |
| madvise(p, size, MADV_NOHUGEPAGE); | |
| for (; (unsigned long)p < gup.addr + size; p += PAGE_SIZE) | |
| p[0] = 0; | |
| /* Only report timing information on the *_BENCHMARK commands: */ | |
| if ((cmd == PIN_FAST_BENCHMARK) || (cmd == GUP_FAST_BENCHMARK) || | |
| (cmd == PIN_LONGTERM_BENCHMARK)) { | |
| for (i = 0; i < repeats; i++) { | |
| gup.size = size; | |
| if (ioctl(fd, cmd, &gup)) | |
| perror("ioctl"), exit(1); | |
| printf("%s: Time: get:%lld put:%lld us", | |
| cmd_to_str(cmd), gup.get_delta_usec, | |
| gup.put_delta_usec); | |
| if (gup.size != size) | |
| printf(", truncated (size: %lld)", gup.size); | |
| printf("\n"); | |
| } | |
| } else { | |
| gup.size = size; | |
| if (ioctl(fd, cmd, &gup)) { | |
| perror("ioctl"); | |
| exit(1); | |
| } | |
| printf("%s: done\n", cmd_to_str(cmd)); | |
| if (gup.size != size) | |
| printf("Truncated (size: %lld)\n", gup.size); | |
| } | |
| return 0; | |
| } |