18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/kernel.h> 38c2ecf20Sopenharmony_ci#include <linux/errno.h> 48c2ecf20Sopenharmony_ci#include <linux/err.h> 58c2ecf20Sopenharmony_ci#include <linux/mm.h> 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 88c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 98c2ecf20Sopenharmony_ci#include <linux/sched.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/** 128c2ecf20Sopenharmony_ci * get_vaddr_frames() - map virtual addresses to pfns 138c2ecf20Sopenharmony_ci * @start: starting user address 148c2ecf20Sopenharmony_ci * @nr_frames: number of pages / pfns from start to map 158c2ecf20Sopenharmony_ci * @gup_flags: flags modifying lookup behaviour 168c2ecf20Sopenharmony_ci * @vec: structure which receives pages / pfns of the addresses mapped. 178c2ecf20Sopenharmony_ci * It should have space for at least nr_frames entries. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * This function maps virtual addresses from @start and fills @vec structure 208c2ecf20Sopenharmony_ci * with page frame numbers or page pointers to corresponding pages (choice 218c2ecf20Sopenharmony_ci * depends on the type of the vma underlying the virtual address). If @start 228c2ecf20Sopenharmony_ci * belongs to a normal vma, the function grabs reference to each of the pages 238c2ecf20Sopenharmony_ci * to pin them in memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't 248c2ecf20Sopenharmony_ci * touch page structures and the caller must make sure pfns aren't reused for 258c2ecf20Sopenharmony_ci * anything else while he is using them. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * The function returns number of pages mapped which may be less than 288c2ecf20Sopenharmony_ci * @nr_frames. In particular we stop mapping if there are more vmas of 298c2ecf20Sopenharmony_ci * different type underlying the specified range of virtual addresses. 308c2ecf20Sopenharmony_ci * When the function isn't able to map a single page, it returns error. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * Note that get_vaddr_frames() cannot follow VM_IO mappings. It used 338c2ecf20Sopenharmony_ci * to be able to do that, but that could (racily) return non-refcounted 348c2ecf20Sopenharmony_ci * pfns. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * This function takes care of grabbing mmap_lock as necessary. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ciint get_vaddr_frames(unsigned long start, unsigned int nr_frames, 398c2ecf20Sopenharmony_ci unsigned int gup_flags, struct frame_vector *vec) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct mm_struct *mm = current->mm; 428c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 438c2ecf20Sopenharmony_ci int ret = 0; 448c2ecf20Sopenharmony_ci int locked; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (nr_frames == 0) 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(nr_frames > vec->nr_allocated)) 508c2ecf20Sopenharmony_ci nr_frames = vec->nr_allocated; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci start = untagged_addr(start); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci mmap_read_lock(mm); 558c2ecf20Sopenharmony_ci locked = 1; 568c2ecf20Sopenharmony_ci vma = find_vma_intersection(mm, start, start + 1); 578c2ecf20Sopenharmony_ci if (!vma) { 588c2ecf20Sopenharmony_ci ret = -EFAULT; 598c2ecf20Sopenharmony_ci goto out; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* 638c2ecf20Sopenharmony_ci * While get_vaddr_frames() could be used for transient (kernel 648c2ecf20Sopenharmony_ci * controlled lifetime) pinning of memory pages all current 658c2ecf20Sopenharmony_ci * users establish long term (userspace controlled lifetime) 668c2ecf20Sopenharmony_ci * page pinning. Treat get_vaddr_frames() like 678c2ecf20Sopenharmony_ci * get_user_pages_longterm() and disallow it for filesystem-dax 688c2ecf20Sopenharmony_ci * mappings. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci if (vma_is_fsdax(vma)) { 718c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 728c2ecf20Sopenharmony_ci goto out; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) { 768c2ecf20Sopenharmony_ci vec->got_ref = true; 778c2ecf20Sopenharmony_ci vec->is_pfns = false; 788c2ecf20Sopenharmony_ci ret = pin_user_pages_locked(start, nr_frames, 798c2ecf20Sopenharmony_ci gup_flags, (struct page **)(vec->ptrs), &locked); 808c2ecf20Sopenharmony_ci if (likely(ret > 0)) 818c2ecf20Sopenharmony_ci goto out; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci vec->nr_frames = 0; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ciout: 878c2ecf20Sopenharmony_ci if (locked) 888c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 898c2ecf20Sopenharmony_ci if (!ret) 908c2ecf20Sopenharmony_ci ret = -EFAULT; 918c2ecf20Sopenharmony_ci if (ret > 0) 928c2ecf20Sopenharmony_ci vec->nr_frames = ret; 938c2ecf20Sopenharmony_ci return ret; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_vaddr_frames); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/** 988c2ecf20Sopenharmony_ci * put_vaddr_frames() - drop references to pages if get_vaddr_frames() acquired 998c2ecf20Sopenharmony_ci * them 1008c2ecf20Sopenharmony_ci * @vec: frame vector to put 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * Drop references to pages if get_vaddr_frames() acquired them. We also 1038c2ecf20Sopenharmony_ci * invalidate the frame vector so that it is prepared for the next call into 1048c2ecf20Sopenharmony_ci * get_vaddr_frames(). 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_civoid put_vaddr_frames(struct frame_vector *vec) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct page **pages; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!vec->got_ref) 1118c2ecf20Sopenharmony_ci goto out; 1128c2ecf20Sopenharmony_ci pages = frame_vector_pages(vec); 1138c2ecf20Sopenharmony_ci /* 1148c2ecf20Sopenharmony_ci * frame_vector_pages() might needed to do a conversion when 1158c2ecf20Sopenharmony_ci * get_vaddr_frames() got pages but vec was later converted to pfns. 1168c2ecf20Sopenharmony_ci * But it shouldn't really fail to convert pfns back... 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci if (WARN_ON(IS_ERR(pages))) 1198c2ecf20Sopenharmony_ci goto out; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci unpin_user_pages(pages, vec->nr_frames); 1228c2ecf20Sopenharmony_ci vec->got_ref = false; 1238c2ecf20Sopenharmony_ciout: 1248c2ecf20Sopenharmony_ci vec->nr_frames = 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(put_vaddr_frames); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/** 1298c2ecf20Sopenharmony_ci * frame_vector_to_pages - convert frame vector to contain page pointers 1308c2ecf20Sopenharmony_ci * @vec: frame vector to convert 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * Convert @vec to contain array of page pointers. If the conversion is 1338c2ecf20Sopenharmony_ci * successful, return 0. Otherwise return an error. Note that we do not grab 1348c2ecf20Sopenharmony_ci * page references for the page structures. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ciint frame_vector_to_pages(struct frame_vector *vec) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci int i; 1398c2ecf20Sopenharmony_ci unsigned long *nums; 1408c2ecf20Sopenharmony_ci struct page **pages; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (!vec->is_pfns) 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci nums = frame_vector_pfns(vec); 1458c2ecf20Sopenharmony_ci for (i = 0; i < vec->nr_frames; i++) 1468c2ecf20Sopenharmony_ci if (!pfn_valid(nums[i])) 1478c2ecf20Sopenharmony_ci return -EINVAL; 1488c2ecf20Sopenharmony_ci pages = (struct page **)nums; 1498c2ecf20Sopenharmony_ci for (i = 0; i < vec->nr_frames; i++) 1508c2ecf20Sopenharmony_ci pages[i] = pfn_to_page(nums[i]); 1518c2ecf20Sopenharmony_ci vec->is_pfns = false; 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(frame_vector_to_pages); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/** 1578c2ecf20Sopenharmony_ci * frame_vector_to_pfns - convert frame vector to contain pfns 1588c2ecf20Sopenharmony_ci * @vec: frame vector to convert 1598c2ecf20Sopenharmony_ci * 1608c2ecf20Sopenharmony_ci * Convert @vec to contain array of pfns. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_civoid frame_vector_to_pfns(struct frame_vector *vec) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci int i; 1658c2ecf20Sopenharmony_ci unsigned long *nums; 1668c2ecf20Sopenharmony_ci struct page **pages; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (vec->is_pfns) 1698c2ecf20Sopenharmony_ci return; 1708c2ecf20Sopenharmony_ci pages = (struct page **)(vec->ptrs); 1718c2ecf20Sopenharmony_ci nums = (unsigned long *)pages; 1728c2ecf20Sopenharmony_ci for (i = 0; i < vec->nr_frames; i++) 1738c2ecf20Sopenharmony_ci nums[i] = page_to_pfn(pages[i]); 1748c2ecf20Sopenharmony_ci vec->is_pfns = true; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(frame_vector_to_pfns); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/** 1798c2ecf20Sopenharmony_ci * frame_vector_create() - allocate & initialize structure for pinned pfns 1808c2ecf20Sopenharmony_ci * @nr_frames: number of pfns slots we should reserve 1818c2ecf20Sopenharmony_ci * 1828c2ecf20Sopenharmony_ci * Allocate and initialize struct pinned_pfns to be able to hold @nr_pfns 1838c2ecf20Sopenharmony_ci * pfns. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistruct frame_vector *frame_vector_create(unsigned int nr_frames) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct frame_vector *vec; 1888c2ecf20Sopenharmony_ci int size = sizeof(struct frame_vector) + sizeof(void *) * nr_frames; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(nr_frames == 0)) 1918c2ecf20Sopenharmony_ci return NULL; 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * This is absurdly high. It's here just to avoid strange effects when 1948c2ecf20Sopenharmony_ci * arithmetics overflows. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(nr_frames > INT_MAX / sizeof(void *) / 2)) 1978c2ecf20Sopenharmony_ci return NULL; 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * Avoid higher order allocations, use vmalloc instead. It should 2008c2ecf20Sopenharmony_ci * be rare anyway. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci vec = kvmalloc(size, GFP_KERNEL); 2038c2ecf20Sopenharmony_ci if (!vec) 2048c2ecf20Sopenharmony_ci return NULL; 2058c2ecf20Sopenharmony_ci vec->nr_allocated = nr_frames; 2068c2ecf20Sopenharmony_ci vec->nr_frames = 0; 2078c2ecf20Sopenharmony_ci return vec; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(frame_vector_create); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/** 2128c2ecf20Sopenharmony_ci * frame_vector_destroy() - free memory allocated to carry frame vector 2138c2ecf20Sopenharmony_ci * @vec: Frame vector to free 2148c2ecf20Sopenharmony_ci * 2158c2ecf20Sopenharmony_ci * Free structure allocated by frame_vector_create() to carry frames. 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_civoid frame_vector_destroy(struct frame_vector *vec) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci /* Make sure put_vaddr_frames() got called properly... */ 2208c2ecf20Sopenharmony_ci VM_BUG_ON(vec->nr_frames > 0); 2218c2ecf20Sopenharmony_ci kvfree(vec); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(frame_vector_destroy); 224