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