gup_test.c 9.0 KB

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