xref: /linux/mm/gup_test.c (revision 61b963b52f59524e27692bc1c14bfb2459e32eb3)
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 
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 
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;
439c84f229SJohn Hubbard 	struct page *page;
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++) {
509c84f229SJohn Hubbard 			page = pages[i];
519c84f229SJohn Hubbard 			if (WARN(!page_maybe_dma_pinned(page),
529c84f229SJohn Hubbard 				 "pages[%lu] is NOT dma-pinned\n", i)) {
539c84f229SJohn Hubbard 
549c84f229SJohn Hubbard 				dump_page(page, "gup_test failure");
559c84f229SJohn Hubbard 				break;
56e44605a8SPavel Tatashin 			} else if (cmd == PIN_LONGTERM_BENCHMARK &&
576077c943SAlex Sierra 				WARN(!is_longterm_pinnable_page(page),
58e44605a8SPavel Tatashin 				     "pages[%lu] is NOT pinnable but pinned\n",
59e44605a8SPavel Tatashin 				     i)) {
60e44605a8SPavel Tatashin 				dump_page(page, "gup_test failure");
61e44605a8SPavel Tatashin 				break;
629c84f229SJohn Hubbard 			}
639c84f229SJohn Hubbard 		}
649c84f229SJohn Hubbard 		break;
659c84f229SJohn Hubbard 	}
669c84f229SJohn Hubbard }
679c84f229SJohn Hubbard 
68f4f9bda4SJohn Hubbard static void dump_pages_test(struct gup_test *gup, struct page **pages,
69f4f9bda4SJohn Hubbard 			    unsigned long nr_pages)
70f4f9bda4SJohn Hubbard {
71f4f9bda4SJohn Hubbard 	unsigned int index_to_dump;
72f4f9bda4SJohn Hubbard 	unsigned int i;
73f4f9bda4SJohn Hubbard 
74f4f9bda4SJohn Hubbard 	/*
75f4f9bda4SJohn Hubbard 	 * Zero out any user-supplied page index that is out of range. Remember:
76f4f9bda4SJohn Hubbard 	 * .which_pages[] contains a 1-based set of page indices.
77f4f9bda4SJohn Hubbard 	 */
78f4f9bda4SJohn Hubbard 	for (i = 0; i < GUP_TEST_MAX_PAGES_TO_DUMP; i++) {
79f4f9bda4SJohn Hubbard 		if (gup->which_pages[i] > nr_pages) {
80f4f9bda4SJohn Hubbard 			pr_warn("ZEROING due to out of range: .which_pages[%u]: %u\n",
81f4f9bda4SJohn Hubbard 				i, gup->which_pages[i]);
82f4f9bda4SJohn Hubbard 			gup->which_pages[i] = 0;
83f4f9bda4SJohn Hubbard 		}
84f4f9bda4SJohn Hubbard 	}
85f4f9bda4SJohn Hubbard 
86f4f9bda4SJohn Hubbard 	for (i = 0; i < GUP_TEST_MAX_PAGES_TO_DUMP; i++) {
87f4f9bda4SJohn Hubbard 		index_to_dump = gup->which_pages[i];
88f4f9bda4SJohn Hubbard 
89f4f9bda4SJohn Hubbard 		if (index_to_dump) {
90f4f9bda4SJohn Hubbard 			index_to_dump--; // Decode from 1-based, to 0-based
91f4f9bda4SJohn Hubbard 			pr_info("---- page #%u, starting from user virt addr: 0x%llx\n",
92f4f9bda4SJohn Hubbard 				index_to_dump, gup->addr);
93f4f9bda4SJohn Hubbard 			dump_page(pages[index_to_dump],
94f4f9bda4SJohn Hubbard 				  "gup_test: dump_pages() test");
95f4f9bda4SJohn Hubbard 		}
96f4f9bda4SJohn Hubbard 	}
97f4f9bda4SJohn Hubbard }
98f4f9bda4SJohn Hubbard 
999c84f229SJohn Hubbard static int __gup_test_ioctl(unsigned int cmd,
1009c84f229SJohn Hubbard 		struct gup_test *gup)
1019c84f229SJohn Hubbard {
1029c84f229SJohn Hubbard 	ktime_t start_time, end_time;
1039c84f229SJohn Hubbard 	unsigned long i, nr_pages, addr, next;
10479dbf135SPavel Tatashin 	long nr;
1059c84f229SJohn Hubbard 	struct page **pages;
1069c84f229SJohn Hubbard 	int ret = 0;
1079c84f229SJohn Hubbard 	bool needs_mmap_lock =
1089c84f229SJohn Hubbard 		cmd != GUP_FAST_BENCHMARK && cmd != PIN_FAST_BENCHMARK;
1099c84f229SJohn Hubbard 
1109c84f229SJohn Hubbard 	if (gup->size > ULONG_MAX)
1119c84f229SJohn Hubbard 		return -EINVAL;
1129c84f229SJohn Hubbard 
1139c84f229SJohn Hubbard 	nr_pages = gup->size / PAGE_SIZE;
1149c84f229SJohn Hubbard 	pages = kvcalloc(nr_pages, sizeof(void *), GFP_KERNEL);
1159c84f229SJohn Hubbard 	if (!pages)
1169c84f229SJohn Hubbard 		return -ENOMEM;
1179c84f229SJohn Hubbard 
1189c84f229SJohn Hubbard 	if (needs_mmap_lock && mmap_read_lock_killable(current->mm)) {
1199c84f229SJohn Hubbard 		ret = -EINTR;
1209c84f229SJohn Hubbard 		goto free_pages;
1219c84f229SJohn Hubbard 	}
1229c84f229SJohn Hubbard 
1239c84f229SJohn Hubbard 	i = 0;
1249c84f229SJohn Hubbard 	nr = gup->nr_pages_per_call;
1259c84f229SJohn Hubbard 	start_time = ktime_get();
1269c84f229SJohn Hubbard 	for (addr = gup->addr; addr < gup->addr + gup->size; addr = next) {
1279c84f229SJohn Hubbard 		if (nr != gup->nr_pages_per_call)
1289c84f229SJohn Hubbard 			break;
1299c84f229SJohn Hubbard 
1309c84f229SJohn Hubbard 		next = addr + nr * PAGE_SIZE;
1319c84f229SJohn Hubbard 		if (next > gup->addr + gup->size) {
1329c84f229SJohn Hubbard 			next = gup->addr + gup->size;
1339c84f229SJohn Hubbard 			nr = (next - addr) / PAGE_SIZE;
1349c84f229SJohn Hubbard 		}
1359c84f229SJohn Hubbard 
1369c84f229SJohn Hubbard 		switch (cmd) {
1379c84f229SJohn Hubbard 		case GUP_FAST_BENCHMARK:
13879dbf135SPavel Tatashin 			nr = get_user_pages_fast(addr, nr, gup->gup_flags,
1399c84f229SJohn Hubbard 						 pages + i);
1409c84f229SJohn Hubbard 			break;
141a9bed1e1SJohn Hubbard 		case GUP_BASIC_TEST:
14279dbf135SPavel Tatashin 			nr = get_user_pages(addr, nr, gup->gup_flags, pages + i,
1439c84f229SJohn Hubbard 					    NULL);
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:
15079dbf135SPavel Tatashin 			nr = pin_user_pages(addr, nr, gup->gup_flags, pages + i,
1519c84f229SJohn Hubbard 					    NULL);
1529c84f229SJohn Hubbard 			break;
1539c84f229SJohn Hubbard 		case PIN_LONGTERM_BENCHMARK:
1549c84f229SJohn Hubbard 			nr = pin_user_pages(addr, nr,
15579dbf135SPavel Tatashin 					    gup->gup_flags | FOLL_LONGTERM,
1569c84f229SJohn Hubbard 					    pages + i, NULL);
1579c84f229SJohn Hubbard 			break;
158f4f9bda4SJohn Hubbard 		case DUMP_USER_PAGES_TEST:
15979dbf135SPavel Tatashin 			if (gup->test_flags & GUP_TEST_FLAG_DUMP_PAGES_USE_PIN)
16079dbf135SPavel Tatashin 				nr = pin_user_pages(addr, nr, gup->gup_flags,
161f4f9bda4SJohn Hubbard 						    pages + i, NULL);
162f4f9bda4SJohn Hubbard 			else
16379dbf135SPavel Tatashin 				nr = get_user_pages(addr, nr, gup->gup_flags,
164f4f9bda4SJohn Hubbard 						    pages + i, NULL);
165f4f9bda4SJohn Hubbard 			break;
1669c84f229SJohn Hubbard 		default:
1679c84f229SJohn Hubbard 			ret = -EINVAL;
1689c84f229SJohn Hubbard 			goto unlock;
1699c84f229SJohn Hubbard 		}
1709c84f229SJohn Hubbard 
1719c84f229SJohn Hubbard 		if (nr <= 0)
1729c84f229SJohn Hubbard 			break;
1739c84f229SJohn Hubbard 		i += nr;
1749c84f229SJohn Hubbard 	}
1759c84f229SJohn Hubbard 	end_time = ktime_get();
1769c84f229SJohn Hubbard 
1779c84f229SJohn Hubbard 	/* Shifting the meaning of nr_pages: now it is actual number pinned: */
1789c84f229SJohn Hubbard 	nr_pages = i;
1799c84f229SJohn Hubbard 
1809c84f229SJohn Hubbard 	gup->get_delta_usec = ktime_us_delta(end_time, start_time);
1819c84f229SJohn Hubbard 	gup->size = addr - gup->addr;
1829c84f229SJohn Hubbard 
1839c84f229SJohn Hubbard 	/*
1849c84f229SJohn Hubbard 	 * Take an un-benchmark-timed moment to verify DMA pinned
1859c84f229SJohn Hubbard 	 * state: print a warning if any non-dma-pinned pages are found:
1869c84f229SJohn Hubbard 	 */
1879c84f229SJohn Hubbard 	verify_dma_pinned(cmd, pages, nr_pages);
1889c84f229SJohn Hubbard 
189f4f9bda4SJohn Hubbard 	if (cmd == DUMP_USER_PAGES_TEST)
190f4f9bda4SJohn Hubbard 		dump_pages_test(gup, pages, nr_pages);
191f4f9bda4SJohn Hubbard 
1929c84f229SJohn Hubbard 	start_time = ktime_get();
1939c84f229SJohn Hubbard 
19479dbf135SPavel Tatashin 	put_back_pages(cmd, pages, nr_pages, gup->test_flags);
1959c84f229SJohn Hubbard 
1969c84f229SJohn Hubbard 	end_time = ktime_get();
1979c84f229SJohn Hubbard 	gup->put_delta_usec = ktime_us_delta(end_time, start_time);
1989c84f229SJohn Hubbard 
1999c84f229SJohn Hubbard unlock:
2009c84f229SJohn Hubbard 	if (needs_mmap_lock)
2019c84f229SJohn Hubbard 		mmap_read_unlock(current->mm);
2029c84f229SJohn Hubbard free_pages:
2039c84f229SJohn Hubbard 	kvfree(pages);
2049c84f229SJohn Hubbard 	return ret;
2059c84f229SJohn Hubbard }
2069c84f229SJohn Hubbard 
207c77369b4SDavid Hildenbrand static DEFINE_MUTEX(pin_longterm_test_mutex);
208c77369b4SDavid Hildenbrand static struct page **pin_longterm_test_pages;
209c77369b4SDavid Hildenbrand static unsigned long pin_longterm_test_nr_pages;
210c77369b4SDavid Hildenbrand 
211c77369b4SDavid Hildenbrand static inline void pin_longterm_test_stop(void)
212c77369b4SDavid Hildenbrand {
213c77369b4SDavid Hildenbrand 	if (pin_longterm_test_pages) {
214c77369b4SDavid Hildenbrand 		if (pin_longterm_test_nr_pages)
215c77369b4SDavid Hildenbrand 			unpin_user_pages(pin_longterm_test_pages,
216c77369b4SDavid Hildenbrand 					 pin_longterm_test_nr_pages);
217*61b963b5SDavid Hildenbrand 		kvfree(pin_longterm_test_pages);
218c77369b4SDavid Hildenbrand 		pin_longterm_test_pages = NULL;
219c77369b4SDavid Hildenbrand 		pin_longterm_test_nr_pages = 0;
220c77369b4SDavid Hildenbrand 	}
221c77369b4SDavid Hildenbrand }
222c77369b4SDavid Hildenbrand 
223c77369b4SDavid Hildenbrand static inline int pin_longterm_test_start(unsigned long arg)
224c77369b4SDavid Hildenbrand {
225c77369b4SDavid Hildenbrand 	long nr_pages, cur_pages, addr, remaining_pages;
226c77369b4SDavid Hildenbrand 	int gup_flags = FOLL_LONGTERM;
227c77369b4SDavid Hildenbrand 	struct pin_longterm_test args;
228c77369b4SDavid Hildenbrand 	struct page **pages;
229c77369b4SDavid Hildenbrand 	int ret = 0;
230c77369b4SDavid Hildenbrand 	bool fast;
231c77369b4SDavid Hildenbrand 
232c77369b4SDavid Hildenbrand 	if (pin_longterm_test_pages)
233c77369b4SDavid Hildenbrand 		return -EINVAL;
234c77369b4SDavid Hildenbrand 
235c77369b4SDavid Hildenbrand 	if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
236c77369b4SDavid Hildenbrand 		return -EFAULT;
237c77369b4SDavid Hildenbrand 
238c77369b4SDavid Hildenbrand 	if (args.flags &
239c77369b4SDavid Hildenbrand 	    ~(PIN_LONGTERM_TEST_FLAG_USE_WRITE|PIN_LONGTERM_TEST_FLAG_USE_FAST))
240c77369b4SDavid Hildenbrand 		return -EINVAL;
241c77369b4SDavid Hildenbrand 	if (!IS_ALIGNED(args.addr | args.size, PAGE_SIZE))
242c77369b4SDavid Hildenbrand 		return -EINVAL;
243c77369b4SDavid Hildenbrand 	if (args.size > LONG_MAX)
244c77369b4SDavid Hildenbrand 		return -EINVAL;
245c77369b4SDavid Hildenbrand 	nr_pages = args.size / PAGE_SIZE;
246c77369b4SDavid Hildenbrand 	if (!nr_pages)
247c77369b4SDavid Hildenbrand 		return -EINVAL;
248c77369b4SDavid Hildenbrand 
249c77369b4SDavid Hildenbrand 	pages = kvcalloc(nr_pages, sizeof(void *), GFP_KERNEL);
250c77369b4SDavid Hildenbrand 	if (!pages)
251c77369b4SDavid Hildenbrand 		return -ENOMEM;
252c77369b4SDavid Hildenbrand 
253c77369b4SDavid Hildenbrand 	if (args.flags & PIN_LONGTERM_TEST_FLAG_USE_WRITE)
254c77369b4SDavid Hildenbrand 		gup_flags |= FOLL_WRITE;
255c77369b4SDavid Hildenbrand 	fast = !!(args.flags & PIN_LONGTERM_TEST_FLAG_USE_FAST);
256c77369b4SDavid Hildenbrand 
257c77369b4SDavid Hildenbrand 	if (!fast && mmap_read_lock_killable(current->mm)) {
258*61b963b5SDavid Hildenbrand 		kvfree(pages);
259c77369b4SDavid Hildenbrand 		return -EINTR;
260c77369b4SDavid Hildenbrand 	}
261c77369b4SDavid Hildenbrand 
262c77369b4SDavid Hildenbrand 	pin_longterm_test_pages = pages;
263c77369b4SDavid Hildenbrand 	pin_longterm_test_nr_pages = 0;
264c77369b4SDavid Hildenbrand 
265c77369b4SDavid Hildenbrand 	while (nr_pages - pin_longterm_test_nr_pages) {
266c77369b4SDavid Hildenbrand 		remaining_pages = nr_pages - pin_longterm_test_nr_pages;
267c77369b4SDavid Hildenbrand 		addr = args.addr + pin_longterm_test_nr_pages * PAGE_SIZE;
268c77369b4SDavid Hildenbrand 
269c77369b4SDavid Hildenbrand 		if (fast)
270c77369b4SDavid Hildenbrand 			cur_pages = pin_user_pages_fast(addr, remaining_pages,
271c77369b4SDavid Hildenbrand 							gup_flags, pages);
272c77369b4SDavid Hildenbrand 		else
273c77369b4SDavid Hildenbrand 			cur_pages = pin_user_pages(addr, remaining_pages,
274c77369b4SDavid Hildenbrand 						   gup_flags, pages, NULL);
275c77369b4SDavid Hildenbrand 		if (cur_pages < 0) {
276c77369b4SDavid Hildenbrand 			pin_longterm_test_stop();
277c77369b4SDavid Hildenbrand 			ret = cur_pages;
278c77369b4SDavid Hildenbrand 			break;
279c77369b4SDavid Hildenbrand 		}
280c77369b4SDavid Hildenbrand 		pin_longterm_test_nr_pages += cur_pages;
281c77369b4SDavid Hildenbrand 		pages += cur_pages;
282c77369b4SDavid Hildenbrand 	}
283c77369b4SDavid Hildenbrand 
284c77369b4SDavid Hildenbrand 	if (!fast)
285c77369b4SDavid Hildenbrand 		mmap_read_unlock(current->mm);
286c77369b4SDavid Hildenbrand 	return ret;
287c77369b4SDavid Hildenbrand }
288c77369b4SDavid Hildenbrand 
289c77369b4SDavid Hildenbrand static inline int pin_longterm_test_read(unsigned long arg)
290c77369b4SDavid Hildenbrand {
291c77369b4SDavid Hildenbrand 	__u64 user_addr;
292c77369b4SDavid Hildenbrand 	unsigned long i;
293c77369b4SDavid Hildenbrand 
294c77369b4SDavid Hildenbrand 	if (!pin_longterm_test_pages)
295c77369b4SDavid Hildenbrand 		return -EINVAL;
296c77369b4SDavid Hildenbrand 
297c77369b4SDavid Hildenbrand 	if (copy_from_user(&user_addr, (void __user *)arg, sizeof(user_addr)))
298c77369b4SDavid Hildenbrand 		return -EFAULT;
299c77369b4SDavid Hildenbrand 
300c77369b4SDavid Hildenbrand 	for (i = 0; i < pin_longterm_test_nr_pages; i++) {
301a0ac9b35SDavid Hildenbrand 		void *addr = kmap_local_page(pin_longterm_test_pages[i]);
302a0ac9b35SDavid Hildenbrand 		unsigned long ret;
303c77369b4SDavid Hildenbrand 
304a0ac9b35SDavid Hildenbrand 		ret = copy_to_user((void __user *)(unsigned long)user_addr, addr,
305a0ac9b35SDavid Hildenbrand 				   PAGE_SIZE);
306a0ac9b35SDavid Hildenbrand 		kunmap_local(addr);
307a0ac9b35SDavid Hildenbrand 		if (ret)
308c77369b4SDavid Hildenbrand 			return -EFAULT;
309c77369b4SDavid Hildenbrand 		user_addr += PAGE_SIZE;
310c77369b4SDavid Hildenbrand 	}
311c77369b4SDavid Hildenbrand 	return 0;
312c77369b4SDavid Hildenbrand }
313c77369b4SDavid Hildenbrand 
314c77369b4SDavid Hildenbrand static long pin_longterm_test_ioctl(struct file *filep, unsigned int cmd,
315c77369b4SDavid Hildenbrand 				    unsigned long arg)
316c77369b4SDavid Hildenbrand {
317c77369b4SDavid Hildenbrand 	int ret = -EINVAL;
318c77369b4SDavid Hildenbrand 
319c77369b4SDavid Hildenbrand 	if (mutex_lock_killable(&pin_longterm_test_mutex))
320c77369b4SDavid Hildenbrand 		return -EINTR;
321c77369b4SDavid Hildenbrand 
322c77369b4SDavid Hildenbrand 	switch (cmd) {
323c77369b4SDavid Hildenbrand 	case PIN_LONGTERM_TEST_START:
324c77369b4SDavid Hildenbrand 		ret = pin_longterm_test_start(arg);
325c77369b4SDavid Hildenbrand 		break;
326c77369b4SDavid Hildenbrand 	case PIN_LONGTERM_TEST_STOP:
327c77369b4SDavid Hildenbrand 		pin_longterm_test_stop();
328c77369b4SDavid Hildenbrand 		ret = 0;
329c77369b4SDavid Hildenbrand 		break;
330c77369b4SDavid Hildenbrand 	case PIN_LONGTERM_TEST_READ:
331c77369b4SDavid Hildenbrand 		ret = pin_longterm_test_read(arg);
332c77369b4SDavid Hildenbrand 		break;
333c77369b4SDavid Hildenbrand 	}
334c77369b4SDavid Hildenbrand 
335c77369b4SDavid Hildenbrand 	mutex_unlock(&pin_longterm_test_mutex);
336c77369b4SDavid Hildenbrand 	return ret;
337c77369b4SDavid Hildenbrand }
338c77369b4SDavid Hildenbrand 
3399c84f229SJohn Hubbard static long gup_test_ioctl(struct file *filep, unsigned int cmd,
3409c84f229SJohn Hubbard 		unsigned long arg)
3419c84f229SJohn Hubbard {
3429c84f229SJohn Hubbard 	struct gup_test gup;
3439c84f229SJohn Hubbard 	int ret;
3449c84f229SJohn Hubbard 
3459c84f229SJohn Hubbard 	switch (cmd) {
3469c84f229SJohn Hubbard 	case GUP_FAST_BENCHMARK:
3479c84f229SJohn Hubbard 	case PIN_FAST_BENCHMARK:
3489c84f229SJohn Hubbard 	case PIN_LONGTERM_BENCHMARK:
349a9bed1e1SJohn Hubbard 	case GUP_BASIC_TEST:
350a9bed1e1SJohn Hubbard 	case PIN_BASIC_TEST:
351f4f9bda4SJohn Hubbard 	case DUMP_USER_PAGES_TEST:
3529c84f229SJohn Hubbard 		break;
353c77369b4SDavid Hildenbrand 	case PIN_LONGTERM_TEST_START:
354c77369b4SDavid Hildenbrand 	case PIN_LONGTERM_TEST_STOP:
355c77369b4SDavid Hildenbrand 	case PIN_LONGTERM_TEST_READ:
356c77369b4SDavid Hildenbrand 		return pin_longterm_test_ioctl(filep, cmd, arg);
3579c84f229SJohn Hubbard 	default:
3589c84f229SJohn Hubbard 		return -EINVAL;
3599c84f229SJohn Hubbard 	}
3609c84f229SJohn Hubbard 
3619c84f229SJohn Hubbard 	if (copy_from_user(&gup, (void __user *)arg, sizeof(gup)))
3629c84f229SJohn Hubbard 		return -EFAULT;
3639c84f229SJohn Hubbard 
3649c84f229SJohn Hubbard 	ret = __gup_test_ioctl(cmd, &gup);
3659c84f229SJohn Hubbard 	if (ret)
3669c84f229SJohn Hubbard 		return ret;
3679c84f229SJohn Hubbard 
3689c84f229SJohn Hubbard 	if (copy_to_user((void __user *)arg, &gup, sizeof(gup)))
3699c84f229SJohn Hubbard 		return -EFAULT;
3709c84f229SJohn Hubbard 
3719c84f229SJohn Hubbard 	return 0;
3729c84f229SJohn Hubbard }
3739c84f229SJohn Hubbard 
374c77369b4SDavid Hildenbrand static int gup_test_release(struct inode *inode, struct file *file)
375c77369b4SDavid Hildenbrand {
376c77369b4SDavid Hildenbrand 	pin_longterm_test_stop();
377c77369b4SDavid Hildenbrand 
378c77369b4SDavid Hildenbrand 	return 0;
379c77369b4SDavid Hildenbrand }
380c77369b4SDavid Hildenbrand 
3819c84f229SJohn Hubbard static const struct file_operations gup_test_fops = {
3829c84f229SJohn Hubbard 	.open = nonseekable_open,
3839c84f229SJohn Hubbard 	.unlocked_ioctl = gup_test_ioctl,
384c77369b4SDavid Hildenbrand 	.release = gup_test_release,
3859c84f229SJohn Hubbard };
3869c84f229SJohn Hubbard 
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