gup_test.c 6.2 KB

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