gup_test.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. #include <linux/kernel.h>
  2. #include <linux/mm.h>
  3. #include <linux/slab.h>
  4. #include <linux/uaccess.h>
  5. #include <linux/ktime.h>
  6. #include <linux/debugfs.h>
  7. #include <linux/highmem.h>
  8. #include "gup_test.h"
  9. static void put_back_pages(unsigned int cmd, struct page **pages,
  10. unsigned long nr_pages, unsigned int gup_test_flags)
  11. {
  12. unsigned long i;
  13. switch (cmd) {
  14. case GUP_FAST_BENCHMARK:
  15. case GUP_BASIC_TEST:
  16. for (i = 0; i < nr_pages; i++)
  17. put_page(pages[i]);
  18. break;
  19. case PIN_FAST_BENCHMARK:
  20. case PIN_BASIC_TEST:
  21. case PIN_LONGTERM_BENCHMARK:
  22. unpin_user_pages(pages, nr_pages);
  23. break;
  24. case DUMP_USER_PAGES_TEST:
  25. if (gup_test_flags & GUP_TEST_FLAG_DUMP_PAGES_USE_PIN) {
  26. unpin_user_pages(pages, nr_pages);
  27. } else {
  28. for (i = 0; i < nr_pages; i++)
  29. put_page(pages[i]);
  30. }
  31. break;
  32. }
  33. }
  34. static void verify_dma_pinned(unsigned int cmd, struct page **pages,
  35. unsigned long nr_pages)
  36. {
  37. unsigned long i;
  38. struct folio *folio;
  39. switch (cmd) {
  40. case PIN_FAST_BENCHMARK:
  41. case PIN_BASIC_TEST:
  42. case PIN_LONGTERM_BENCHMARK:
  43. for (i = 0; i < nr_pages; i++) {
  44. folio = page_folio(pages[i]);
  45. if (WARN(!folio_maybe_dma_pinned(folio),
  46. "pages[%lu] is NOT dma-pinned\n", i)) {
  47. dump_page(&folio->page, "gup_test failure");
  48. break;
  49. } else if (cmd == PIN_LONGTERM_BENCHMARK &&
  50. WARN(!folio_is_longterm_pinnable(folio),
  51. "pages[%lu] is NOT pinnable but pinned\n",
  52. i)) {
  53. dump_page(&folio->page, "gup_test failure");
  54. break;
  55. }
  56. }
  57. break;
  58. }
  59. }
  60. static void dump_pages_test(struct gup_test *gup, struct page **pages,
  61. unsigned long nr_pages)
  62. {
  63. unsigned int index_to_dump;
  64. unsigned int i;
  65. /*
  66. * Zero out any user-supplied page index that is out of range. Remember:
  67. * .which_pages[] contains a 1-based set of page indices.
  68. */
  69. for (i = 0; i < GUP_TEST_MAX_PAGES_TO_DUMP; i++) {
  70. if (gup->which_pages[i] > nr_pages) {
  71. pr_warn("ZEROING due to out of range: .which_pages[%u]: %u\n",
  72. i, gup->which_pages[i]);
  73. gup->which_pages[i] = 0;
  74. }
  75. }
  76. for (i = 0; i < GUP_TEST_MAX_PAGES_TO_DUMP; i++) {
  77. index_to_dump = gup->which_pages[i];
  78. if (index_to_dump) {
  79. index_to_dump--; // Decode from 1-based, to 0-based
  80. pr_info("---- page #%u, starting from user virt addr: 0x%llx\n",
  81. index_to_dump, gup->addr);
  82. dump_page(pages[index_to_dump],
  83. "gup_test: dump_pages() test");
  84. }
  85. }
  86. }
  87. static int __gup_test_ioctl(unsigned int cmd,
  88. struct gup_test *gup)
  89. {
  90. ktime_t start_time, end_time;
  91. unsigned long i, nr_pages, addr, next;
  92. long nr;
  93. struct page **pages;
  94. int ret = 0;
  95. bool needs_mmap_lock =
  96. cmd != GUP_FAST_BENCHMARK && cmd != PIN_FAST_BENCHMARK;
  97. if (gup->size > ULONG_MAX)
  98. return -EINVAL;
  99. nr_pages = gup->size / PAGE_SIZE;
  100. pages = kvcalloc(nr_pages, sizeof(void *), GFP_KERNEL);
  101. if (!pages)
  102. return -ENOMEM;
  103. if (needs_mmap_lock && mmap_read_lock_killable(current->mm)) {
  104. ret = -EINTR;
  105. goto free_pages;
  106. }
  107. i = 0;
  108. nr = gup->nr_pages_per_call;
  109. start_time = ktime_get();
  110. for (addr = gup->addr; addr < gup->addr + gup->size; addr = next) {
  111. if (nr != gup->nr_pages_per_call)
  112. break;
  113. next = addr + nr * PAGE_SIZE;
  114. if (next > gup->addr + gup->size) {
  115. next = gup->addr + gup->size;
  116. nr = (next - addr) / PAGE_SIZE;
  117. }
  118. switch (cmd) {
  119. case GUP_FAST_BENCHMARK:
  120. nr = get_user_pages_fast(addr, nr, gup->gup_flags,
  121. pages + i);
  122. break;
  123. case GUP_BASIC_TEST:
  124. nr = get_user_pages(addr, nr, gup->gup_flags, pages + i);
  125. break;
  126. case PIN_FAST_BENCHMARK:
  127. nr = pin_user_pages_fast(addr, nr, gup->gup_flags,
  128. pages + i);
  129. break;
  130. case PIN_BASIC_TEST:
  131. nr = pin_user_pages(addr, nr, gup->gup_flags, pages + i);
  132. break;
  133. case PIN_LONGTERM_BENCHMARK:
  134. nr = pin_user_pages(addr, nr,
  135. gup->gup_flags | FOLL_LONGTERM,
  136. pages + i);
  137. break;
  138. case DUMP_USER_PAGES_TEST:
  139. if (gup->test_flags & GUP_TEST_FLAG_DUMP_PAGES_USE_PIN)
  140. nr = pin_user_pages(addr, nr, gup->gup_flags,
  141. pages + i);
  142. else
  143. nr = get_user_pages(addr, nr, gup->gup_flags,
  144. pages + i);
  145. break;
  146. default:
  147. ret = -EINVAL;
  148. goto unlock;
  149. }
  150. if (nr <= 0)
  151. break;
  152. i += nr;
  153. }
  154. end_time = ktime_get();
  155. /* Shifting the meaning of nr_pages: now it is actual number pinned: */
  156. nr_pages = i;
  157. gup->get_delta_usec = ktime_us_delta(end_time, start_time);
  158. gup->size = addr - gup->addr;
  159. /*
  160. * Take an un-benchmark-timed moment to verify DMA pinned
  161. * state: print a warning if any non-dma-pinned pages are found:
  162. */
  163. verify_dma_pinned(cmd, pages, nr_pages);
  164. if (cmd == DUMP_USER_PAGES_TEST)
  165. dump_pages_test(gup, pages, nr_pages);
  166. start_time = ktime_get();
  167. put_back_pages(cmd, pages, nr_pages, gup->test_flags);
  168. end_time = ktime_get();
  169. gup->put_delta_usec = ktime_us_delta(end_time, start_time);
  170. unlock:
  171. if (needs_mmap_lock)
  172. mmap_read_unlock(current->mm);
  173. free_pages:
  174. kvfree(pages);
  175. return ret;
  176. }
  177. static DEFINE_MUTEX(pin_longterm_test_mutex);
  178. static struct page **pin_longterm_test_pages;
  179. static unsigned long pin_longterm_test_nr_pages;
  180. static inline void pin_longterm_test_stop(void)
  181. {
  182. if (pin_longterm_test_pages) {
  183. if (pin_longterm_test_nr_pages)
  184. unpin_user_pages(pin_longterm_test_pages,
  185. pin_longterm_test_nr_pages);
  186. kvfree(pin_longterm_test_pages);
  187. pin_longterm_test_pages = NULL;
  188. pin_longterm_test_nr_pages = 0;
  189. }
  190. }
  191. static inline int pin_longterm_test_start(unsigned long arg)
  192. {
  193. long nr_pages, cur_pages, addr, remaining_pages;
  194. int gup_flags = FOLL_LONGTERM;
  195. struct pin_longterm_test args;
  196. struct page **pages;
  197. int ret = 0;
  198. bool fast;
  199. if (pin_longterm_test_pages)
  200. return -EINVAL;
  201. if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
  202. return -EFAULT;
  203. if (args.flags &
  204. ~(PIN_LONGTERM_TEST_FLAG_USE_WRITE|PIN_LONGTERM_TEST_FLAG_USE_FAST))
  205. return -EINVAL;
  206. if (!IS_ALIGNED(args.addr | args.size, PAGE_SIZE))
  207. return -EINVAL;
  208. if (args.size > LONG_MAX)
  209. return -EINVAL;
  210. nr_pages = args.size / PAGE_SIZE;
  211. if (!nr_pages)
  212. return -EINVAL;
  213. pages = kvcalloc(nr_pages, sizeof(void *), GFP_KERNEL);
  214. if (!pages)
  215. return -ENOMEM;
  216. if (args.flags & PIN_LONGTERM_TEST_FLAG_USE_WRITE)
  217. gup_flags |= FOLL_WRITE;
  218. fast = !!(args.flags & PIN_LONGTERM_TEST_FLAG_USE_FAST);
  219. if (!fast && mmap_read_lock_killable(current->mm)) {
  220. kvfree(pages);
  221. return -EINTR;
  222. }
  223. pin_longterm_test_pages = pages;
  224. pin_longterm_test_nr_pages = 0;
  225. while (nr_pages - pin_longterm_test_nr_pages) {
  226. remaining_pages = nr_pages - pin_longterm_test_nr_pages;
  227. addr = args.addr + pin_longterm_test_nr_pages * PAGE_SIZE;
  228. if (fast)
  229. cur_pages = pin_user_pages_fast(addr, remaining_pages,
  230. gup_flags, pages);
  231. else
  232. cur_pages = pin_user_pages(addr, remaining_pages,
  233. gup_flags, pages);
  234. if (cur_pages < 0) {
  235. pin_longterm_test_stop();
  236. ret = cur_pages;
  237. break;
  238. }
  239. pin_longterm_test_nr_pages += cur_pages;
  240. pages += cur_pages;
  241. }
  242. if (!fast)
  243. mmap_read_unlock(current->mm);
  244. return ret;
  245. }
  246. static inline int pin_longterm_test_read(unsigned long arg)
  247. {
  248. __u64 user_addr;
  249. unsigned long i;
  250. if (!pin_longterm_test_pages)
  251. return -EINVAL;
  252. if (copy_from_user(&user_addr, (void __user *)arg, sizeof(user_addr)))
  253. return -EFAULT;
  254. for (i = 0; i < pin_longterm_test_nr_pages; i++) {
  255. void *addr = kmap_local_page(pin_longterm_test_pages[i]);
  256. unsigned long ret;
  257. ret = copy_to_user((void __user *)(unsigned long)user_addr, addr,
  258. PAGE_SIZE);
  259. kunmap_local(addr);
  260. if (ret)
  261. return -EFAULT;
  262. user_addr += PAGE_SIZE;
  263. }
  264. return 0;
  265. }
  266. static long pin_longterm_test_ioctl(struct file *filep, unsigned int cmd,
  267. unsigned long arg)
  268. {
  269. int ret = -EINVAL;
  270. if (mutex_lock_killable(&pin_longterm_test_mutex))
  271. return -EINTR;
  272. switch (cmd) {
  273. case PIN_LONGTERM_TEST_START:
  274. ret = pin_longterm_test_start(arg);
  275. break;
  276. case PIN_LONGTERM_TEST_STOP:
  277. pin_longterm_test_stop();
  278. ret = 0;
  279. break;
  280. case PIN_LONGTERM_TEST_READ:
  281. ret = pin_longterm_test_read(arg);
  282. break;
  283. }
  284. mutex_unlock(&pin_longterm_test_mutex);
  285. return ret;
  286. }
  287. static long gup_test_ioctl(struct file *filep, unsigned int cmd,
  288. unsigned long arg)
  289. {
  290. struct gup_test gup;
  291. int ret;
  292. switch (cmd) {
  293. case GUP_FAST_BENCHMARK:
  294. case PIN_FAST_BENCHMARK:
  295. case PIN_LONGTERM_BENCHMARK:
  296. case GUP_BASIC_TEST:
  297. case PIN_BASIC_TEST:
  298. case DUMP_USER_PAGES_TEST:
  299. break;
  300. case PIN_LONGTERM_TEST_START:
  301. case PIN_LONGTERM_TEST_STOP:
  302. case PIN_LONGTERM_TEST_READ:
  303. return pin_longterm_test_ioctl(filep, cmd, arg);
  304. default:
  305. return -EINVAL;
  306. }
  307. if (copy_from_user(&gup, (void __user *)arg, sizeof(gup)))
  308. return -EFAULT;
  309. ret = __gup_test_ioctl(cmd, &gup);
  310. if (ret)
  311. return ret;
  312. if (copy_to_user((void __user *)arg, &gup, sizeof(gup)))
  313. return -EFAULT;
  314. return 0;
  315. }
  316. static int gup_test_release(struct inode *inode, struct file *file)
  317. {
  318. pin_longterm_test_stop();
  319. return 0;
  320. }
  321. static const struct file_operations gup_test_fops = {
  322. .open = nonseekable_open,
  323. .unlocked_ioctl = gup_test_ioctl,
  324. .compat_ioctl = compat_ptr_ioctl,
  325. .release = gup_test_release,
  326. };
  327. static int __init gup_test_init(void)
  328. {
  329. debugfs_create_file_unsafe("gup_test", 0600, NULL, NULL,
  330. &gup_test_fops);
  331. return 0;
  332. }
  333. late_initcall(gup_test_init);