19c84f229SJohn Hubbard #include <linux/kernel.h>
29c84f229SJohn Hubbard #include <linux/mm.h>
39c84f229SJohn Hubbard #include <linux/slab.h>
49c84f229SJohn Hubbard #include <linux/uaccess.h>
59c84f229SJohn Hubbard #include <linux/ktime.h>
69c84f229SJohn Hubbard #include <linux/debugfs.h>
7a0ac9b35SDavid Hildenbrand #include <linux/highmem.h>
8b9dcfdffSJohn Hubbard #include "gup_test.h"
99c84f229SJohn Hubbard
put_back_pages(unsigned int cmd,struct page ** pages,unsigned long nr_pages,unsigned int gup_test_flags)109c84f229SJohn Hubbard static void put_back_pages(unsigned int cmd, struct page **pages,
11f4f9bda4SJohn Hubbard unsigned long nr_pages, unsigned int gup_test_flags)
129c84f229SJohn Hubbard {
139c84f229SJohn Hubbard unsigned long i;
149c84f229SJohn Hubbard
159c84f229SJohn Hubbard switch (cmd) {
169c84f229SJohn Hubbard case GUP_FAST_BENCHMARK:
17a9bed1e1SJohn Hubbard case GUP_BASIC_TEST:
189c84f229SJohn Hubbard for (i = 0; i < nr_pages; i++)
199c84f229SJohn Hubbard put_page(pages[i]);
209c84f229SJohn Hubbard break;
219c84f229SJohn Hubbard
229c84f229SJohn Hubbard case PIN_FAST_BENCHMARK:
23a9bed1e1SJohn Hubbard case PIN_BASIC_TEST:
249c84f229SJohn Hubbard case PIN_LONGTERM_BENCHMARK:
259c84f229SJohn Hubbard unpin_user_pages(pages, nr_pages);
269c84f229SJohn Hubbard break;
27f4f9bda4SJohn Hubbard case DUMP_USER_PAGES_TEST:
28f4f9bda4SJohn Hubbard if (gup_test_flags & GUP_TEST_FLAG_DUMP_PAGES_USE_PIN) {
29f4f9bda4SJohn Hubbard unpin_user_pages(pages, nr_pages);
30f4f9bda4SJohn Hubbard } else {
31f4f9bda4SJohn Hubbard for (i = 0; i < nr_pages; i++)
32f4f9bda4SJohn Hubbard put_page(pages[i]);
33f4f9bda4SJohn Hubbard
34f4f9bda4SJohn Hubbard }
35f4f9bda4SJohn Hubbard break;
369c84f229SJohn Hubbard }
379c84f229SJohn Hubbard }
389c84f229SJohn Hubbard
verify_dma_pinned(unsigned int cmd,struct page ** pages,unsigned long nr_pages)399c84f229SJohn Hubbard static void verify_dma_pinned(unsigned int cmd, struct page **pages,
409c84f229SJohn Hubbard unsigned long nr_pages)
419c84f229SJohn Hubbard {
429c84f229SJohn Hubbard unsigned long i;
43*c9223a4aSVishal Moola (Oracle) struct folio *folio;
449c84f229SJohn Hubbard
459c84f229SJohn Hubbard switch (cmd) {
469c84f229SJohn Hubbard case PIN_FAST_BENCHMARK:
47a9bed1e1SJohn Hubbard case PIN_BASIC_TEST:
489c84f229SJohn Hubbard case PIN_LONGTERM_BENCHMARK:
499c84f229SJohn Hubbard for (i = 0; i < nr_pages; i++) {
50*c9223a4aSVishal Moola (Oracle) folio = page_folio(pages[i]);
51*c9223a4aSVishal Moola (Oracle)
52*c9223a4aSVishal Moola (Oracle) if (WARN(!folio_maybe_dma_pinned(folio),
539c84f229SJohn Hubbard "pages[%lu] is NOT dma-pinned\n", i)) {
549c84f229SJohn Hubbard
55*c9223a4aSVishal Moola (Oracle) dump_page(&folio->page, "gup_test failure");
569c84f229SJohn Hubbard break;
57e44605a8SPavel Tatashin } else if (cmd == PIN_LONGTERM_BENCHMARK &&
58*c9223a4aSVishal Moola (Oracle) WARN(!folio_is_longterm_pinnable(folio),
59e44605a8SPavel Tatashin "pages[%lu] is NOT pinnable but pinned\n",
60e44605a8SPavel Tatashin i)) {
61*c9223a4aSVishal Moola (Oracle) dump_page(&folio->page, "gup_test failure");
62e44605a8SPavel Tatashin break;
639c84f229SJohn Hubbard }
649c84f229SJohn Hubbard }
659c84f229SJohn Hubbard break;
669c84f229SJohn Hubbard }
679c84f229SJohn Hubbard }
689c84f229SJohn Hubbard
dump_pages_test(struct gup_test * gup,struct page ** pages,unsigned long nr_pages)69f4f9bda4SJohn Hubbard static void dump_pages_test(struct gup_test *gup, struct page **pages,
70f4f9bda4SJohn Hubbard unsigned long nr_pages)
71f4f9bda4SJohn Hubbard {
72f4f9bda4SJohn Hubbard unsigned int index_to_dump;
73f4f9bda4SJohn Hubbard unsigned int i;
74f4f9bda4SJohn Hubbard
75f4f9bda4SJohn Hubbard /*
76f4f9bda4SJohn Hubbard * Zero out any user-supplied page index that is out of range. Remember:
77f4f9bda4SJohn Hubbard * .which_pages[] contains a 1-based set of page indices.
78f4f9bda4SJohn Hubbard */
79f4f9bda4SJohn Hubbard for (i = 0; i < GUP_TEST_MAX_PAGES_TO_DUMP; i++) {
80f4f9bda4SJohn Hubbard if (gup->which_pages[i] > nr_pages) {
81f4f9bda4SJohn Hubbard pr_warn("ZEROING due to out of range: .which_pages[%u]: %u\n",
82f4f9bda4SJohn Hubbard i, gup->which_pages[i]);
83f4f9bda4SJohn Hubbard gup->which_pages[i] = 0;
84f4f9bda4SJohn Hubbard }
85f4f9bda4SJohn Hubbard }
86f4f9bda4SJohn Hubbard
87f4f9bda4SJohn Hubbard for (i = 0; i < GUP_TEST_MAX_PAGES_TO_DUMP; i++) {
88f4f9bda4SJohn Hubbard index_to_dump = gup->which_pages[i];
89f4f9bda4SJohn Hubbard
90f4f9bda4SJohn Hubbard if (index_to_dump) {
91f4f9bda4SJohn Hubbard index_to_dump--; // Decode from 1-based, to 0-based
92f4f9bda4SJohn Hubbard pr_info("---- page #%u, starting from user virt addr: 0x%llx\n",
93f4f9bda4SJohn Hubbard index_to_dump, gup->addr);
94f4f9bda4SJohn Hubbard dump_page(pages[index_to_dump],
95f4f9bda4SJohn Hubbard "gup_test: dump_pages() test");
96f4f9bda4SJohn Hubbard }
97f4f9bda4SJohn Hubbard }
98f4f9bda4SJohn Hubbard }
99f4f9bda4SJohn Hubbard
__gup_test_ioctl(unsigned int cmd,struct gup_test * gup)1009c84f229SJohn Hubbard static int __gup_test_ioctl(unsigned int cmd,
1019c84f229SJohn Hubbard struct gup_test *gup)
1029c84f229SJohn Hubbard {
1039c84f229SJohn Hubbard ktime_t start_time, end_time;
1049c84f229SJohn Hubbard unsigned long i, nr_pages, addr, next;
10579dbf135SPavel Tatashin long nr;
1069c84f229SJohn Hubbard struct page **pages;
1079c84f229SJohn Hubbard int ret = 0;
1089c84f229SJohn Hubbard bool needs_mmap_lock =
1099c84f229SJohn Hubbard cmd != GUP_FAST_BENCHMARK && cmd != PIN_FAST_BENCHMARK;
1109c84f229SJohn Hubbard
1119c84f229SJohn Hubbard if (gup->size > ULONG_MAX)
1129c84f229SJohn Hubbard return -EINVAL;
1139c84f229SJohn Hubbard
1149c84f229SJohn Hubbard nr_pages = gup->size / PAGE_SIZE;
1159c84f229SJohn Hubbard pages = kvcalloc(nr_pages, sizeof(void *), GFP_KERNEL);
1169c84f229SJohn Hubbard if (!pages)
1179c84f229SJohn Hubbard return -ENOMEM;
1189c84f229SJohn Hubbard
1199c84f229SJohn Hubbard if (needs_mmap_lock && mmap_read_lock_killable(current->mm)) {
1209c84f229SJohn Hubbard ret = -EINTR;
1219c84f229SJohn Hubbard goto free_pages;
1229c84f229SJohn Hubbard }
1239c84f229SJohn Hubbard
1249c84f229SJohn Hubbard i = 0;
1259c84f229SJohn Hubbard nr = gup->nr_pages_per_call;
1269c84f229SJohn Hubbard start_time = ktime_get();
1279c84f229SJohn Hubbard for (addr = gup->addr; addr < gup->addr + gup->size; addr = next) {
1289c84f229SJohn Hubbard if (nr != gup->nr_pages_per_call)
1299c84f229SJohn Hubbard break;
1309c84f229SJohn Hubbard
1319c84f229SJohn Hubbard next = addr + nr * PAGE_SIZE;
1329c84f229SJohn Hubbard if (next > gup->addr + gup->size) {
1339c84f229SJohn Hubbard next = gup->addr + gup->size;
1349c84f229SJohn Hubbard nr = (next - addr) / PAGE_SIZE;
1359c84f229SJohn Hubbard }
1369c84f229SJohn Hubbard
1379c84f229SJohn Hubbard switch (cmd) {
1389c84f229SJohn Hubbard case GUP_FAST_BENCHMARK:
13979dbf135SPavel Tatashin nr = get_user_pages_fast(addr, nr, gup->gup_flags,
1409c84f229SJohn Hubbard pages + i);
1419c84f229SJohn Hubbard break;
142a9bed1e1SJohn Hubbard case GUP_BASIC_TEST:
14354d02069SLorenzo Stoakes nr = get_user_pages(addr, nr, gup->gup_flags, pages + i);
1449c84f229SJohn Hubbard break;
1459c84f229SJohn Hubbard case PIN_FAST_BENCHMARK:
14679dbf135SPavel Tatashin nr = pin_user_pages_fast(addr, nr, gup->gup_flags,
1479c84f229SJohn Hubbard pages + i);
1489c84f229SJohn Hubbard break;
149a9bed1e1SJohn Hubbard case PIN_BASIC_TEST:
1504c630f30SLorenzo Stoakes nr = pin_user_pages(addr, nr, gup->gup_flags, pages + i);
1519c84f229SJohn Hubbard break;
1529c84f229SJohn Hubbard case PIN_LONGTERM_BENCHMARK:
1539c84f229SJohn Hubbard nr = pin_user_pages(addr, nr,
15479dbf135SPavel Tatashin gup->gup_flags | FOLL_LONGTERM,
1554c630f30SLorenzo Stoakes pages + i);
1569c84f229SJohn Hubbard break;
157f4f9bda4SJohn Hubbard case DUMP_USER_PAGES_TEST:
15879dbf135SPavel Tatashin if (gup->test_flags & GUP_TEST_FLAG_DUMP_PAGES_USE_PIN)
15979dbf135SPavel Tatashin nr = pin_user_pages(addr, nr, gup->gup_flags,
1604c630f30SLorenzo Stoakes pages + i);
161f4f9bda4SJohn Hubbard else
16279dbf135SPavel Tatashin nr = get_user_pages(addr, nr, gup->gup_flags,
16354d02069SLorenzo Stoakes pages + i);
164f4f9bda4SJohn Hubbard break;
1659c84f229SJohn Hubbard default:
1669c84f229SJohn Hubbard ret = -EINVAL;
1679c84f229SJohn Hubbard goto unlock;
1689c84f229SJohn Hubbard }
1699c84f229SJohn Hubbard
1709c84f229SJohn Hubbard if (nr <= 0)
1719c84f229SJohn Hubbard break;
1729c84f229SJohn Hubbard i += nr;
1739c84f229SJohn Hubbard }
1749c84f229SJohn Hubbard end_time = ktime_get();
1759c84f229SJohn Hubbard
1769c84f229SJohn Hubbard /* Shifting the meaning of nr_pages: now it is actual number pinned: */
1779c84f229SJohn Hubbard nr_pages = i;
1789c84f229SJohn Hubbard
1799c84f229SJohn Hubbard gup->get_delta_usec = ktime_us_delta(end_time, start_time);
1809c84f229SJohn Hubbard gup->size = addr - gup->addr;
1819c84f229SJohn Hubbard
1829c84f229SJohn Hubbard /*
1839c84f229SJohn Hubbard * Take an un-benchmark-timed moment to verify DMA pinned
1849c84f229SJohn Hubbard * state: print a warning if any non-dma-pinned pages are found:
1859c84f229SJohn Hubbard */
1869c84f229SJohn Hubbard verify_dma_pinned(cmd, pages, nr_pages);
1879c84f229SJohn Hubbard
188f4f9bda4SJohn Hubbard if (cmd == DUMP_USER_PAGES_TEST)
189f4f9bda4SJohn Hubbard dump_pages_test(gup, pages, nr_pages);
190f4f9bda4SJohn Hubbard
1919c84f229SJohn Hubbard start_time = ktime_get();
1929c84f229SJohn Hubbard
19379dbf135SPavel Tatashin put_back_pages(cmd, pages, nr_pages, gup->test_flags);
1949c84f229SJohn Hubbard
1959c84f229SJohn Hubbard end_time = ktime_get();
1969c84f229SJohn Hubbard gup->put_delta_usec = ktime_us_delta(end_time, start_time);
1979c84f229SJohn Hubbard
1989c84f229SJohn Hubbard unlock:
1999c84f229SJohn Hubbard if (needs_mmap_lock)
2009c84f229SJohn Hubbard mmap_read_unlock(current->mm);
2019c84f229SJohn Hubbard free_pages:
2029c84f229SJohn Hubbard kvfree(pages);
2039c84f229SJohn Hubbard return ret;
2049c84f229SJohn Hubbard }
2059c84f229SJohn Hubbard
206c77369b4SDavid Hildenbrand static DEFINE_MUTEX(pin_longterm_test_mutex);
207c77369b4SDavid Hildenbrand static struct page **pin_longterm_test_pages;
208c77369b4SDavid Hildenbrand static unsigned long pin_longterm_test_nr_pages;
209c77369b4SDavid Hildenbrand
pin_longterm_test_stop(void)210c77369b4SDavid Hildenbrand static inline void pin_longterm_test_stop(void)
211c77369b4SDavid Hildenbrand {
212c77369b4SDavid Hildenbrand if (pin_longterm_test_pages) {
213c77369b4SDavid Hildenbrand if (pin_longterm_test_nr_pages)
214c77369b4SDavid Hildenbrand unpin_user_pages(pin_longterm_test_pages,
215c77369b4SDavid Hildenbrand pin_longterm_test_nr_pages);
21661b963b5SDavid Hildenbrand kvfree(pin_longterm_test_pages);
217c77369b4SDavid Hildenbrand pin_longterm_test_pages = NULL;
218c77369b4SDavid Hildenbrand pin_longterm_test_nr_pages = 0;
219c77369b4SDavid Hildenbrand }
220c77369b4SDavid Hildenbrand }
221c77369b4SDavid Hildenbrand
pin_longterm_test_start(unsigned long arg)222c77369b4SDavid Hildenbrand static inline int pin_longterm_test_start(unsigned long arg)
223c77369b4SDavid Hildenbrand {
224c77369b4SDavid Hildenbrand long nr_pages, cur_pages, addr, remaining_pages;
225c77369b4SDavid Hildenbrand int gup_flags = FOLL_LONGTERM;
226c77369b4SDavid Hildenbrand struct pin_longterm_test args;
227c77369b4SDavid Hildenbrand struct page **pages;
228c77369b4SDavid Hildenbrand int ret = 0;
229c77369b4SDavid Hildenbrand bool fast;
230c77369b4SDavid Hildenbrand
231c77369b4SDavid Hildenbrand if (pin_longterm_test_pages)
232c77369b4SDavid Hildenbrand return -EINVAL;
233c77369b4SDavid Hildenbrand
234c77369b4SDavid Hildenbrand if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
235c77369b4SDavid Hildenbrand return -EFAULT;
236c77369b4SDavid Hildenbrand
237c77369b4SDavid Hildenbrand if (args.flags &
238c77369b4SDavid Hildenbrand ~(PIN_LONGTERM_TEST_FLAG_USE_WRITE|PIN_LONGTERM_TEST_FLAG_USE_FAST))
239c77369b4SDavid Hildenbrand return -EINVAL;
240c77369b4SDavid Hildenbrand if (!IS_ALIGNED(args.addr | args.size, PAGE_SIZE))
241c77369b4SDavid Hildenbrand return -EINVAL;
242c77369b4SDavid Hildenbrand if (args.size > LONG_MAX)
243c77369b4SDavid Hildenbrand return -EINVAL;
244c77369b4SDavid Hildenbrand nr_pages = args.size / PAGE_SIZE;
245c77369b4SDavid Hildenbrand if (!nr_pages)
246c77369b4SDavid Hildenbrand return -EINVAL;
247c77369b4SDavid Hildenbrand
248c77369b4SDavid Hildenbrand pages = kvcalloc(nr_pages, sizeof(void *), GFP_KERNEL);
249c77369b4SDavid Hildenbrand if (!pages)
250c77369b4SDavid Hildenbrand return -ENOMEM;
251c77369b4SDavid Hildenbrand
252c77369b4SDavid Hildenbrand if (args.flags & PIN_LONGTERM_TEST_FLAG_USE_WRITE)
253c77369b4SDavid Hildenbrand gup_flags |= FOLL_WRITE;
254c77369b4SDavid Hildenbrand fast = !!(args.flags & PIN_LONGTERM_TEST_FLAG_USE_FAST);
255c77369b4SDavid Hildenbrand
256c77369b4SDavid Hildenbrand if (!fast && mmap_read_lock_killable(current->mm)) {
25761b963b5SDavid Hildenbrand kvfree(pages);
258c77369b4SDavid Hildenbrand return -EINTR;
259c77369b4SDavid Hildenbrand }
260c77369b4SDavid Hildenbrand
261c77369b4SDavid Hildenbrand pin_longterm_test_pages = pages;
262c77369b4SDavid Hildenbrand pin_longterm_test_nr_pages = 0;
263c77369b4SDavid Hildenbrand
264c77369b4SDavid Hildenbrand while (nr_pages - pin_longterm_test_nr_pages) {
265c77369b4SDavid Hildenbrand remaining_pages = nr_pages - pin_longterm_test_nr_pages;
266c77369b4SDavid Hildenbrand addr = args.addr + pin_longterm_test_nr_pages * PAGE_SIZE;
267c77369b4SDavid Hildenbrand
268c77369b4SDavid Hildenbrand if (fast)
269c77369b4SDavid Hildenbrand cur_pages = pin_user_pages_fast(addr, remaining_pages,
270c77369b4SDavid Hildenbrand gup_flags, pages);
271c77369b4SDavid Hildenbrand else
272c77369b4SDavid Hildenbrand cur_pages = pin_user_pages(addr, remaining_pages,
2734c630f30SLorenzo Stoakes gup_flags, pages);
274c77369b4SDavid Hildenbrand if (cur_pages < 0) {
275c77369b4SDavid Hildenbrand pin_longterm_test_stop();
276c77369b4SDavid Hildenbrand ret = cur_pages;
277c77369b4SDavid Hildenbrand break;
278c77369b4SDavid Hildenbrand }
279c77369b4SDavid Hildenbrand pin_longterm_test_nr_pages += cur_pages;
280c77369b4SDavid Hildenbrand pages += cur_pages;
281c77369b4SDavid Hildenbrand }
282c77369b4SDavid Hildenbrand
283c77369b4SDavid Hildenbrand if (!fast)
284c77369b4SDavid Hildenbrand mmap_read_unlock(current->mm);
285c77369b4SDavid Hildenbrand return ret;
286c77369b4SDavid Hildenbrand }
287c77369b4SDavid Hildenbrand
pin_longterm_test_read(unsigned long arg)288c77369b4SDavid Hildenbrand static inline int pin_longterm_test_read(unsigned long arg)
289c77369b4SDavid Hildenbrand {
290c77369b4SDavid Hildenbrand __u64 user_addr;
291c77369b4SDavid Hildenbrand unsigned long i;
292c77369b4SDavid Hildenbrand
293c77369b4SDavid Hildenbrand if (!pin_longterm_test_pages)
294c77369b4SDavid Hildenbrand return -EINVAL;
295c77369b4SDavid Hildenbrand
296c77369b4SDavid Hildenbrand if (copy_from_user(&user_addr, (void __user *)arg, sizeof(user_addr)))
297c77369b4SDavid Hildenbrand return -EFAULT;
298c77369b4SDavid Hildenbrand
299c77369b4SDavid Hildenbrand for (i = 0; i < pin_longterm_test_nr_pages; i++) {
300a0ac9b35SDavid Hildenbrand void *addr = kmap_local_page(pin_longterm_test_pages[i]);
301a0ac9b35SDavid Hildenbrand unsigned long ret;
302c77369b4SDavid Hildenbrand
303a0ac9b35SDavid Hildenbrand ret = copy_to_user((void __user *)(unsigned long)user_addr, addr,
304a0ac9b35SDavid Hildenbrand PAGE_SIZE);
305a0ac9b35SDavid Hildenbrand kunmap_local(addr);
306a0ac9b35SDavid Hildenbrand if (ret)
307c77369b4SDavid Hildenbrand return -EFAULT;
308c77369b4SDavid Hildenbrand user_addr += PAGE_SIZE;
309c77369b4SDavid Hildenbrand }
310c77369b4SDavid Hildenbrand return 0;
311c77369b4SDavid Hildenbrand }
312c77369b4SDavid Hildenbrand
pin_longterm_test_ioctl(struct file * filep,unsigned int cmd,unsigned long arg)313c77369b4SDavid Hildenbrand static long pin_longterm_test_ioctl(struct file *filep, unsigned int cmd,
314c77369b4SDavid Hildenbrand unsigned long arg)
315c77369b4SDavid Hildenbrand {
316c77369b4SDavid Hildenbrand int ret = -EINVAL;
317c77369b4SDavid Hildenbrand
318c77369b4SDavid Hildenbrand if (mutex_lock_killable(&pin_longterm_test_mutex))
319c77369b4SDavid Hildenbrand return -EINTR;
320c77369b4SDavid Hildenbrand
321c77369b4SDavid Hildenbrand switch (cmd) {
322c77369b4SDavid Hildenbrand case PIN_LONGTERM_TEST_START:
323c77369b4SDavid Hildenbrand ret = pin_longterm_test_start(arg);
324c77369b4SDavid Hildenbrand break;
325c77369b4SDavid Hildenbrand case PIN_LONGTERM_TEST_STOP:
326c77369b4SDavid Hildenbrand pin_longterm_test_stop();
327c77369b4SDavid Hildenbrand ret = 0;
328c77369b4SDavid Hildenbrand break;
329c77369b4SDavid Hildenbrand case PIN_LONGTERM_TEST_READ:
330c77369b4SDavid Hildenbrand ret = pin_longterm_test_read(arg);
331c77369b4SDavid Hildenbrand break;
332c77369b4SDavid Hildenbrand }
333c77369b4SDavid Hildenbrand
334c77369b4SDavid Hildenbrand mutex_unlock(&pin_longterm_test_mutex);
335c77369b4SDavid Hildenbrand return ret;
336c77369b4SDavid Hildenbrand }
337c77369b4SDavid Hildenbrand
gup_test_ioctl(struct file * filep,unsigned int cmd,unsigned long arg)3389c84f229SJohn Hubbard static long gup_test_ioctl(struct file *filep, unsigned int cmd,
3399c84f229SJohn Hubbard unsigned long arg)
3409c84f229SJohn Hubbard {
3419c84f229SJohn Hubbard struct gup_test gup;
3429c84f229SJohn Hubbard int ret;
3439c84f229SJohn Hubbard
3449c84f229SJohn Hubbard switch (cmd) {
3459c84f229SJohn Hubbard case GUP_FAST_BENCHMARK:
3469c84f229SJohn Hubbard case PIN_FAST_BENCHMARK:
3479c84f229SJohn Hubbard case PIN_LONGTERM_BENCHMARK:
348a9bed1e1SJohn Hubbard case GUP_BASIC_TEST:
349a9bed1e1SJohn Hubbard case PIN_BASIC_TEST:
350f4f9bda4SJohn Hubbard case DUMP_USER_PAGES_TEST:
3519c84f229SJohn Hubbard break;
352c77369b4SDavid Hildenbrand case PIN_LONGTERM_TEST_START:
353c77369b4SDavid Hildenbrand case PIN_LONGTERM_TEST_STOP:
354c77369b4SDavid Hildenbrand case PIN_LONGTERM_TEST_READ:
355c77369b4SDavid Hildenbrand return pin_longterm_test_ioctl(filep, cmd, arg);
3569c84f229SJohn Hubbard default:
3579c84f229SJohn Hubbard return -EINVAL;
3589c84f229SJohn Hubbard }
3599c84f229SJohn Hubbard
3609c84f229SJohn Hubbard if (copy_from_user(&gup, (void __user *)arg, sizeof(gup)))
3619c84f229SJohn Hubbard return -EFAULT;
3629c84f229SJohn Hubbard
3639c84f229SJohn Hubbard ret = __gup_test_ioctl(cmd, &gup);
3649c84f229SJohn Hubbard if (ret)
3659c84f229SJohn Hubbard return ret;
3669c84f229SJohn Hubbard
3679c84f229SJohn Hubbard if (copy_to_user((void __user *)arg, &gup, sizeof(gup)))
3689c84f229SJohn Hubbard return -EFAULT;
3699c84f229SJohn Hubbard
3709c84f229SJohn Hubbard return 0;
3719c84f229SJohn Hubbard }
3729c84f229SJohn Hubbard
gup_test_release(struct inode * inode,struct file * file)373c77369b4SDavid Hildenbrand static int gup_test_release(struct inode *inode, struct file *file)
374c77369b4SDavid Hildenbrand {
375c77369b4SDavid Hildenbrand pin_longterm_test_stop();
376c77369b4SDavid Hildenbrand
377c77369b4SDavid Hildenbrand return 0;
378c77369b4SDavid Hildenbrand }
379c77369b4SDavid Hildenbrand
3809c84f229SJohn Hubbard static const struct file_operations gup_test_fops = {
3819c84f229SJohn Hubbard .open = nonseekable_open,
3829c84f229SJohn Hubbard .unlocked_ioctl = gup_test_ioctl,
3834f572f00SHaibo Li .compat_ioctl = compat_ptr_ioctl,
384c77369b4SDavid Hildenbrand .release = gup_test_release,
3859c84f229SJohn Hubbard };
3869c84f229SJohn Hubbard
gup_test_init(void)387afaa7888SBarry Song static int __init gup_test_init(void)
3889c84f229SJohn Hubbard {
3899c84f229SJohn Hubbard debugfs_create_file_unsafe("gup_test", 0600, NULL, NULL,
3909c84f229SJohn Hubbard &gup_test_fops);
3919c84f229SJohn Hubbard
3929c84f229SJohn Hubbard return 0;
3939c84f229SJohn Hubbard }
3949c84f229SJohn Hubbard
3959c84f229SJohn Hubbard late_initcall(gup_test_init);
396