gup_test.c 5.8 KB

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