xref: /linux/mm/gup_test.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
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