0001
0002 #include <linux/kernel.h>
0003 #include <linux/errno.h>
0004 #include <linux/err.h>
0005 #include <linux/mm.h>
0006 #include <linux/slab.h>
0007 #include <linux/vmalloc.h>
0008 #include <linux/pagemap.h>
0009 #include <linux/sched.h>
0010
0011 #include <media/frame_vector.h>
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035 int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
0036 struct frame_vector *vec)
0037 {
0038 struct mm_struct *mm = current->mm;
0039 struct vm_area_struct *vma;
0040 int ret_pin_user_pages_fast = 0;
0041 int ret = 0;
0042 int err;
0043
0044 if (nr_frames == 0)
0045 return 0;
0046
0047 if (WARN_ON_ONCE(nr_frames > vec->nr_allocated))
0048 nr_frames = vec->nr_allocated;
0049
0050 start = untagged_addr(start);
0051
0052 ret = pin_user_pages_fast(start, nr_frames,
0053 FOLL_FORCE | FOLL_WRITE | FOLL_LONGTERM,
0054 (struct page **)(vec->ptrs));
0055 if (ret > 0) {
0056 vec->got_ref = true;
0057 vec->is_pfns = false;
0058 goto out_unlocked;
0059 }
0060 ret_pin_user_pages_fast = ret;
0061
0062 mmap_read_lock(mm);
0063 vec->got_ref = false;
0064 vec->is_pfns = true;
0065 ret = 0;
0066 do {
0067 unsigned long *nums = frame_vector_pfns(vec);
0068
0069 vma = vma_lookup(mm, start);
0070 if (!vma)
0071 break;
0072
0073 while (ret < nr_frames && start + PAGE_SIZE <= vma->vm_end) {
0074 err = follow_pfn(vma, start, &nums[ret]);
0075 if (err) {
0076 if (ret)
0077 goto out;
0078
0079
0080
0081
0082
0083
0084
0085 if (err == -EINVAL && ret_pin_user_pages_fast)
0086 ret = ret_pin_user_pages_fast;
0087 else
0088 ret = err;
0089 goto out;
0090 }
0091 start += PAGE_SIZE;
0092 ret++;
0093 }
0094
0095 if (start < vma->vm_end)
0096 break;
0097 } while (ret < nr_frames);
0098 out:
0099 mmap_read_unlock(mm);
0100 out_unlocked:
0101 if (!ret)
0102 ret = -EFAULT;
0103 if (ret > 0)
0104 vec->nr_frames = ret;
0105 return ret;
0106 }
0107 EXPORT_SYMBOL(get_vaddr_frames);
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118 void put_vaddr_frames(struct frame_vector *vec)
0119 {
0120 struct page **pages;
0121
0122 if (!vec->got_ref)
0123 goto out;
0124 pages = frame_vector_pages(vec);
0125
0126
0127
0128
0129
0130 if (WARN_ON(IS_ERR(pages)))
0131 goto out;
0132
0133 unpin_user_pages(pages, vec->nr_frames);
0134 vec->got_ref = false;
0135 out:
0136 vec->nr_frames = 0;
0137 }
0138 EXPORT_SYMBOL(put_vaddr_frames);
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148 int frame_vector_to_pages(struct frame_vector *vec)
0149 {
0150 int i;
0151 unsigned long *nums;
0152 struct page **pages;
0153
0154 if (!vec->is_pfns)
0155 return 0;
0156 nums = frame_vector_pfns(vec);
0157 for (i = 0; i < vec->nr_frames; i++)
0158 if (!pfn_valid(nums[i]))
0159 return -EINVAL;
0160 pages = (struct page **)nums;
0161 for (i = 0; i < vec->nr_frames; i++)
0162 pages[i] = pfn_to_page(nums[i]);
0163 vec->is_pfns = false;
0164 return 0;
0165 }
0166 EXPORT_SYMBOL(frame_vector_to_pages);
0167
0168
0169
0170
0171
0172
0173
0174 void frame_vector_to_pfns(struct frame_vector *vec)
0175 {
0176 int i;
0177 unsigned long *nums;
0178 struct page **pages;
0179
0180 if (vec->is_pfns)
0181 return;
0182 pages = (struct page **)(vec->ptrs);
0183 nums = (unsigned long *)pages;
0184 for (i = 0; i < vec->nr_frames; i++)
0185 nums[i] = page_to_pfn(pages[i]);
0186 vec->is_pfns = true;
0187 }
0188 EXPORT_SYMBOL(frame_vector_to_pfns);
0189
0190
0191
0192
0193
0194
0195
0196
0197 struct frame_vector *frame_vector_create(unsigned int nr_frames)
0198 {
0199 struct frame_vector *vec;
0200 int size = sizeof(struct frame_vector) + sizeof(void *) * nr_frames;
0201
0202 if (WARN_ON_ONCE(nr_frames == 0))
0203 return NULL;
0204
0205
0206
0207
0208 if (WARN_ON_ONCE(nr_frames > INT_MAX / sizeof(void *) / 2))
0209 return NULL;
0210
0211
0212
0213
0214 vec = kvmalloc(size, GFP_KERNEL);
0215 if (!vec)
0216 return NULL;
0217 vec->nr_allocated = nr_frames;
0218 vec->nr_frames = 0;
0219 return vec;
0220 }
0221 EXPORT_SYMBOL(frame_vector_create);
0222
0223
0224
0225
0226
0227
0228
0229 void frame_vector_destroy(struct frame_vector *vec)
0230 {
0231
0232 VM_BUG_ON(vec->nr_frames > 0);
0233 kvfree(vec);
0234 }
0235 EXPORT_SYMBOL(frame_vector_destroy);