Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : #include <linux/kernel.h> 3 : : #include <linux/errno.h> 4 : : #include <linux/err.h> 5 : : #include <linux/mm.h> 6 : : #include <linux/slab.h> 7 : : #include <linux/vmalloc.h> 8 : : #include <linux/pagemap.h> 9 : : #include <linux/sched.h> 10 : : 11 : : /** 12 : : * get_vaddr_frames() - map virtual addresses to pfns 13 : : * @start: starting user address 14 : : * @nr_frames: number of pages / pfns from start to map 15 : : * @gup_flags: flags modifying lookup behaviour 16 : : * @vec: structure which receives pages / pfns of the addresses mapped. 17 : : * It should have space for at least nr_frames entries. 18 : : * 19 : : * This function maps virtual addresses from @start and fills @vec structure 20 : : * with page frame numbers or page pointers to corresponding pages (choice 21 : : * depends on the type of the vma underlying the virtual address). If @start 22 : : * belongs to a normal vma, the function grabs reference to each of the pages 23 : : * to pin them in memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't 24 : : * touch page structures and the caller must make sure pfns aren't reused for 25 : : * anything else while he is using them. 26 : : * 27 : : * The function returns number of pages mapped which may be less than 28 : : * @nr_frames. In particular we stop mapping if there are more vmas of 29 : : * different type underlying the specified range of virtual addresses. 30 : : * When the function isn't able to map a single page, it returns error. 31 : : * 32 : : * This function takes care of grabbing mmap_sem as necessary. 33 : : */ 34 : 0 : int get_vaddr_frames(unsigned long start, unsigned int nr_frames, 35 : : unsigned int gup_flags, struct frame_vector *vec) 36 : : { 37 : 0 : struct mm_struct *mm = current->mm; 38 : : struct vm_area_struct *vma; 39 : : int ret = 0; 40 : : int err; 41 : : int locked; 42 : : 43 : 0 : if (nr_frames == 0) 44 : : return 0; 45 : : 46 : 0 : if (WARN_ON_ONCE(nr_frames > vec->nr_allocated)) 47 : 0 : nr_frames = vec->nr_allocated; 48 : : 49 : : start = untagged_addr(start); 50 : : 51 : 0 : down_read(&mm->mmap_sem); 52 : 0 : locked = 1; 53 : 0 : vma = find_vma_intersection(mm, start, start + 1); 54 : 0 : if (!vma) { 55 : : ret = -EFAULT; 56 : : goto out; 57 : : } 58 : : 59 : : /* 60 : : * While get_vaddr_frames() could be used for transient (kernel 61 : : * controlled lifetime) pinning of memory pages all current 62 : : * users establish long term (userspace controlled lifetime) 63 : : * page pinning. Treat get_vaddr_frames() like 64 : : * get_user_pages_longterm() and disallow it for filesystem-dax 65 : : * mappings. 66 : : */ 67 : : if (vma_is_fsdax(vma)) { 68 : : ret = -EOPNOTSUPP; 69 : : goto out; 70 : : } 71 : : 72 : 0 : if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) { 73 : 0 : vec->got_ref = true; 74 : 0 : vec->is_pfns = false; 75 : 0 : ret = get_user_pages_locked(start, nr_frames, 76 : 0 : gup_flags, (struct page **)(vec->ptrs), &locked); 77 : 0 : goto out; 78 : : } 79 : : 80 : 0 : vec->got_ref = false; 81 : 0 : vec->is_pfns = true; 82 : : do { 83 : : unsigned long *nums = frame_vector_pfns(vec); 84 : : 85 : 0 : while (ret < nr_frames && start + PAGE_SIZE <= vma->vm_end) { 86 : 0 : err = follow_pfn(vma, start, &nums[ret]); 87 : 0 : if (err) { 88 : 0 : if (ret == 0) 89 : 0 : ret = err; 90 : : goto out; 91 : : } 92 : : start += PAGE_SIZE; 93 : 0 : ret++; 94 : : } 95 : : /* 96 : : * We stop if we have enough pages or if VMA doesn't completely 97 : : * cover the tail page. 98 : : */ 99 : 0 : if (ret >= nr_frames || start < vma->vm_end) 100 : : break; 101 : 0 : vma = find_vma_intersection(mm, start, start + 1); 102 : 0 : } while (vma && vma->vm_flags & (VM_IO | VM_PFNMAP)); 103 : : out: 104 : 0 : if (locked) 105 : 0 : up_read(&mm->mmap_sem); 106 : 0 : if (!ret) 107 : : ret = -EFAULT; 108 : 0 : if (ret > 0) 109 : 0 : vec->nr_frames = ret; 110 : 0 : return ret; 111 : : } 112 : : EXPORT_SYMBOL(get_vaddr_frames); 113 : : 114 : : /** 115 : : * put_vaddr_frames() - drop references to pages if get_vaddr_frames() acquired 116 : : * them 117 : : * @vec: frame vector to put 118 : : * 119 : : * Drop references to pages if get_vaddr_frames() acquired them. We also 120 : : * invalidate the frame vector so that it is prepared for the next call into 121 : : * get_vaddr_frames(). 122 : : */ 123 : 0 : void put_vaddr_frames(struct frame_vector *vec) 124 : : { 125 : : int i; 126 : : struct page **pages; 127 : : 128 : 0 : if (!vec->got_ref) 129 : : goto out; 130 : : pages = frame_vector_pages(vec); 131 : : /* 132 : : * frame_vector_pages() might needed to do a conversion when 133 : : * get_vaddr_frames() got pages but vec was later converted to pfns. 134 : : * But it shouldn't really fail to convert pfns back... 135 : : */ 136 : 0 : if (WARN_ON(IS_ERR(pages))) 137 : : goto out; 138 : 0 : for (i = 0; i < vec->nr_frames; i++) 139 : 0 : put_page(pages[i]); 140 : 0 : vec->got_ref = false; 141 : : out: 142 : 0 : vec->nr_frames = 0; 143 : 0 : } 144 : : EXPORT_SYMBOL(put_vaddr_frames); 145 : : 146 : : /** 147 : : * frame_vector_to_pages - convert frame vector to contain page pointers 148 : : * @vec: frame vector to convert 149 : : * 150 : : * Convert @vec to contain array of page pointers. If the conversion is 151 : : * successful, return 0. Otherwise return an error. Note that we do not grab 152 : : * page references for the page structures. 153 : : */ 154 : 0 : int frame_vector_to_pages(struct frame_vector *vec) 155 : : { 156 : : int i; 157 : : unsigned long *nums; 158 : : struct page **pages; 159 : : 160 : 0 : if (!vec->is_pfns) 161 : : return 0; 162 : : nums = frame_vector_pfns(vec); 163 : 0 : for (i = 0; i < vec->nr_frames; i++) 164 : 0 : if (!pfn_valid(nums[i])) 165 : : return -EINVAL; 166 : : pages = (struct page **)nums; 167 : 0 : for (i = 0; i < vec->nr_frames; i++) 168 : 0 : pages[i] = pfn_to_page(nums[i]); 169 : 0 : vec->is_pfns = false; 170 : 0 : return 0; 171 : : } 172 : : EXPORT_SYMBOL(frame_vector_to_pages); 173 : : 174 : : /** 175 : : * frame_vector_to_pfns - convert frame vector to contain pfns 176 : : * @vec: frame vector to convert 177 : : * 178 : : * Convert @vec to contain array of pfns. 179 : : */ 180 : 0 : void frame_vector_to_pfns(struct frame_vector *vec) 181 : : { 182 : : int i; 183 : : unsigned long *nums; 184 : : struct page **pages; 185 : : 186 : 0 : if (vec->is_pfns) 187 : 0 : return; 188 : 0 : pages = (struct page **)(vec->ptrs); 189 : : nums = (unsigned long *)pages; 190 : 0 : for (i = 0; i < vec->nr_frames; i++) 191 : 0 : nums[i] = page_to_pfn(pages[i]); 192 : 0 : vec->is_pfns = true; 193 : : } 194 : : EXPORT_SYMBOL(frame_vector_to_pfns); 195 : : 196 : : /** 197 : : * frame_vector_create() - allocate & initialize structure for pinned pfns 198 : : * @nr_frames: number of pfns slots we should reserve 199 : : * 200 : : * Allocate and initialize struct pinned_pfns to be able to hold @nr_pfns 201 : : * pfns. 202 : : */ 203 : 0 : struct frame_vector *frame_vector_create(unsigned int nr_frames) 204 : : { 205 : : struct frame_vector *vec; 206 : 0 : int size = sizeof(struct frame_vector) + sizeof(void *) * nr_frames; 207 : : 208 : 0 : if (WARN_ON_ONCE(nr_frames == 0)) 209 : : return NULL; 210 : : /* 211 : : * This is absurdly high. It's here just to avoid strange effects when 212 : : * arithmetics overflows. 213 : : */ 214 : 0 : if (WARN_ON_ONCE(nr_frames > INT_MAX / sizeof(void *) / 2)) 215 : : return NULL; 216 : : /* 217 : : * Avoid higher order allocations, use vmalloc instead. It should 218 : : * be rare anyway. 219 : : */ 220 : : vec = kvmalloc(size, GFP_KERNEL); 221 : 0 : if (!vec) 222 : : return NULL; 223 : 0 : vec->nr_allocated = nr_frames; 224 : 0 : vec->nr_frames = 0; 225 : 0 : return vec; 226 : : } 227 : : EXPORT_SYMBOL(frame_vector_create); 228 : : 229 : : /** 230 : : * frame_vector_destroy() - free memory allocated to carry frame vector 231 : : * @vec: Frame vector to free 232 : : * 233 : : * Free structure allocated by frame_vector_create() to carry frames. 234 : : */ 235 : 0 : void frame_vector_destroy(struct frame_vector *vec) 236 : : { 237 : : /* Make sure put_vaddr_frames() got called properly... */ 238 : : VM_BUG_ON(vec->nr_frames > 0); 239 : 0 : kvfree(vec); 240 : 0 : } 241 : : EXPORT_SYMBOL(frame_vector_destroy);