summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2015-07-13 10:55:44 -0400
committerMauro Carvalho Chehab <mchehab@osg.samsung.com>2015-08-16 12:02:47 -0400
commit8025e5ddf9c1cac0e632dad49a63abf7848b78cb (patch)
treefb812c2d3cf5940b714d63af0b2bc26082b5855c
parent0f6e2825ce916eed882996bb6e9148c13ecebefd (diff)
[media] mm: Provide new get_vaddr_frames() helper
Provide new function get_vaddr_frames(). This function maps virtual addresses from given start and fills given array with page frame numbers of the corresponding pages. If given start belongs to a normal vma, the function grabs reference to each of the pages to pin them in memory. If start belongs to VM_IO | VM_PFNMAP vma, we don't touch page structures. Caller must make sure pfns aren't reused for anything else while he is using them. This function is created for various drivers to simplify handling of their buffers. Signed-off-by: Jan Kara <jack@suse.cz> Acked-by: Mel Gorman <mgorman@suse.de> Acked-by: Vlastimil Babka <vbabka@suse.cz> Acked-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
-rw-r--r--include/linux/mm.h44
-rw-r--r--mm/Kconfig3
-rw-r--r--mm/Makefile1
-rw-r--r--mm/frame_vector.c230
4 files changed, 278 insertions, 0 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 2e872f92dbac..79ad29a8a60a 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -20,6 +20,7 @@
20#include <linux/shrinker.h> 20#include <linux/shrinker.h>
21#include <linux/resource.h> 21#include <linux/resource.h>
22#include <linux/page_ext.h> 22#include <linux/page_ext.h>
23#include <linux/err.h>
23 24
24struct mempolicy; 25struct mempolicy;
25struct anon_vma; 26struct anon_vma;
@@ -1198,6 +1199,49 @@ long get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
1198 int write, int force, struct page **pages); 1199 int write, int force, struct page **pages);
1199int get_user_pages_fast(unsigned long start, int nr_pages, int write, 1200int get_user_pages_fast(unsigned long start, int nr_pages, int write,
1200 struct page **pages); 1201 struct page **pages);
1202
1203/* Container for pinned pfns / pages */
1204struct frame_vector {
1205 unsigned int nr_allocated; /* Number of frames we have space for */
1206 unsigned int nr_frames; /* Number of frames stored in ptrs array */
1207 bool got_ref; /* Did we pin pages by getting page ref? */
1208 bool is_pfns; /* Does array contain pages or pfns? */
1209 void *ptrs[0]; /* Array of pinned pfns / pages. Use
1210 * pfns_vector_pages() or pfns_vector_pfns()
1211 * for access */
1212};
1213
1214struct frame_vector *frame_vector_create(unsigned int nr_frames);
1215void frame_vector_destroy(struct frame_vector *vec);
1216int get_vaddr_frames(unsigned long start, unsigned int nr_pfns,
1217 bool write, bool force, struct frame_vector *vec);
1218void put_vaddr_frames(struct frame_vector *vec);
1219int frame_vector_to_pages(struct frame_vector *vec);
1220void frame_vector_to_pfns(struct frame_vector *vec);
1221
1222static inline unsigned int frame_vector_count(struct frame_vector *vec)
1223{
1224 return vec->nr_frames;
1225}
1226
1227static inline struct page **frame_vector_pages(struct frame_vector *vec)
1228{
1229 if (vec->is_pfns) {
1230 int err = frame_vector_to_pages(vec);
1231
1232 if (err)
1233 return ERR_PTR(err);
1234 }
1235 return (struct page **)(vec->ptrs);
1236}
1237
1238static inline unsigned long *frame_vector_pfns(struct frame_vector *vec)
1239{
1240 if (!vec->is_pfns)
1241 frame_vector_to_pfns(vec);
1242 return (unsigned long *)(vec->ptrs);
1243}
1244
1201struct kvec; 1245struct kvec;
1202int get_kernel_pages(const struct kvec *iov, int nr_pages, int write, 1246int get_kernel_pages(const struct kvec *iov, int nr_pages, int write,
1203 struct page **pages); 1247 struct page **pages);
diff --git a/mm/Kconfig b/mm/Kconfig
index e79de2bd12cd..7f146dd32fc5 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -654,3 +654,6 @@ config DEFERRED_STRUCT_PAGE_INIT
654 when kswapd starts. This has a potential performance impact on 654 when kswapd starts. This has a potential performance impact on
655 processes running early in the lifetime of the systemm until kswapd 655 processes running early in the lifetime of the systemm until kswapd
656 finishes the initialisation. 656 finishes the initialisation.
657
658config FRAME_VECTOR
659 bool
diff --git a/mm/Makefile b/mm/Makefile
index 98c4eaeabdcb..be5d5c866305 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_CMA) += cma.o
78obj-$(CONFIG_MEMORY_BALLOON) += balloon_compaction.o 78obj-$(CONFIG_MEMORY_BALLOON) += balloon_compaction.o
79obj-$(CONFIG_PAGE_EXTENSION) += page_ext.o 79obj-$(CONFIG_PAGE_EXTENSION) += page_ext.o
80obj-$(CONFIG_CMA_DEBUGFS) += cma_debug.o 80obj-$(CONFIG_CMA_DEBUGFS) += cma_debug.o
81obj-$(CONFIG_FRAME_VECTOR) += frame_vector.o
diff --git a/mm/frame_vector.c b/mm/frame_vector.c
new file mode 100644
index 000000000000..cdabcb93c6a6
--- /dev/null
+++ b/mm/frame_vector.c
@@ -0,0 +1,230 @@
1#include <linux/kernel.h>
2#include <linux/errno.h>
3#include <linux/err.h>
4#include <linux/mm.h>
5#include <linux/slab.h>
6#include <linux/vmalloc.h>
7#include <linux/pagemap.h>
8#include <linux/sched.h>
9
10/*
11 * get_vaddr_frames() - map virtual addresses to pfns
12 * @start: starting user address
13 * @nr_frames: number of pages / pfns from start to map
14 * @write: whether pages will be written to by the caller
15 * @force: whether to force write access even if user mapping is
16 * readonly. See description of the same argument of
17 get_user_pages().
18 * @vec: structure which receives pages / pfns of the addresses mapped.
19 * It should have space for at least nr_frames entries.
20 *
21 * This function maps virtual addresses from @start and fills @vec structure
22 * with page frame numbers or page pointers to corresponding pages (choice
23 * depends on the type of the vma underlying the virtual address). If @start
24 * belongs to a normal vma, the function grabs reference to each of the pages
25 * to pin them in memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't
26 * touch page structures and the caller must make sure pfns aren't reused for
27 * anything else while he is using them.
28 *
29 * The function returns number of pages mapped which may be less than
30 * @nr_frames. In particular we stop mapping if there are more vmas of
31 * different type underlying the specified range of virtual addresses.
32 * When the function isn't able to map a single page, it returns error.
33 *
34 * This function takes care of grabbing mmap_sem as necessary.
35 */
36int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
37 bool write, bool force, struct frame_vector *vec)
38{
39 struct mm_struct *mm = current->mm;
40 struct vm_area_struct *vma;
41 int ret = 0;
42 int err;
43 int locked;
44
45 if (nr_frames == 0)
46 return 0;
47
48 if (WARN_ON_ONCE(nr_frames > vec->nr_allocated))
49 nr_frames = vec->nr_allocated;
50
51 down_read(&mm->mmap_sem);
52 locked = 1;
53 vma = find_vma_intersection(mm, start, start + 1);
54 if (!vma) {
55 ret = -EFAULT;
56 goto out;
57 }
58 if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
59 vec->got_ref = true;
60 vec->is_pfns = false;
61 ret = get_user_pages_locked(current, mm, start, nr_frames,
62 write, force, (struct page **)(vec->ptrs), &locked);
63 goto out;
64 }
65
66 vec->got_ref = false;
67 vec->is_pfns = true;
68 do {
69 unsigned long *nums = frame_vector_pfns(vec);
70
71 while (ret < nr_frames && start + PAGE_SIZE <= vma->vm_end) {
72 err = follow_pfn(vma, start, &nums[ret]);
73 if (err) {
74 if (ret == 0)
75 ret = err;
76 goto out;
77 }
78 start += PAGE_SIZE;
79 ret++;
80 }
81 /*
82 * We stop if we have enough pages or if VMA doesn't completely
83 * cover the tail page.
84 */
85 if (ret >= nr_frames || start < vma->vm_end)
86 break;
87 vma = find_vma_intersection(mm, start, start + 1);
88 } while (vma && vma->vm_flags & (VM_IO | VM_PFNMAP));
89out:
90 if (locked)
91 up_read(&mm->mmap_sem);
92 if (!ret)
93 ret = -EFAULT;
94 if (ret > 0)
95 vec->nr_frames = ret;
96 return ret;
97}
98EXPORT_SYMBOL(get_vaddr_frames);
99
100/**
101 * put_vaddr_frames() - drop references to pages if get_vaddr_frames() acquired
102 * them
103 * @vec: frame vector to put
104 *
105 * Drop references to pages if get_vaddr_frames() acquired them. We also
106 * invalidate the frame vector so that it is prepared for the next call into
107 * get_vaddr_frames().
108 */
109void put_vaddr_frames(struct frame_vector *vec)
110{
111 int i;
112 struct page **pages;
113
114 if (!vec->got_ref)
115 goto out;
116 pages = frame_vector_pages(vec);
117 /*
118 * frame_vector_pages() might needed to do a conversion when
119 * get_vaddr_frames() got pages but vec was later converted to pfns.
120 * But it shouldn't really fail to convert pfns back...
121 */
122 if (WARN_ON(IS_ERR(pages)))
123 goto out;
124 for (i = 0; i < vec->nr_frames; i++)
125 put_page(pages[i]);
126 vec->got_ref = false;
127out:
128 vec->nr_frames = 0;
129}
130EXPORT_SYMBOL(put_vaddr_frames);
131
132/**
133 * frame_vector_to_pages - convert frame vector to contain page pointers
134 * @vec: frame vector to convert
135 *
136 * Convert @vec to contain array of page pointers. If the conversion is
137 * successful, return 0. Otherwise return an error. Note that we do not grab
138 * page references for the page structures.
139 */
140int frame_vector_to_pages(struct frame_vector *vec)
141{
142 int i;
143 unsigned long *nums;
144 struct page **pages;
145
146 if (!vec->is_pfns)
147 return 0;
148 nums = frame_vector_pfns(vec);
149 for (i = 0; i < vec->nr_frames; i++)
150 if (!pfn_valid(nums[i]))
151 return -EINVAL;
152 pages = (struct page **)nums;
153 for (i = 0; i < vec->nr_frames; i++)
154 pages[i] = pfn_to_page(nums[i]);
155 vec->is_pfns = false;
156 return 0;
157}
158EXPORT_SYMBOL(frame_vector_to_pages);
159
160/**
161 * frame_vector_to_pfns - convert frame vector to contain pfns
162 * @vec: frame vector to convert
163 *
164 * Convert @vec to contain array of pfns.
165 */
166void frame_vector_to_pfns(struct frame_vector *vec)
167{
168 int i;
169 unsigned long *nums;
170 struct page **pages;
171
172 if (vec->is_pfns)
173 return;
174 pages = (struct page **)(vec->ptrs);
175 nums = (unsigned long *)pages;
176 for (i = 0; i < vec->nr_frames; i++)
177 nums[i] = page_to_pfn(pages[i]);
178 vec->is_pfns = true;
179}
180EXPORT_SYMBOL(frame_vector_to_pfns);
181
182/**
183 * frame_vector_create() - allocate & initialize structure for pinned pfns
184 * @nr_frames: number of pfns slots we should reserve
185 *
186 * Allocate and initialize struct pinned_pfns to be able to hold @nr_pfns
187 * pfns.
188 */
189struct frame_vector *frame_vector_create(unsigned int nr_frames)
190{
191 struct frame_vector *vec;
192 int size = sizeof(struct frame_vector) + sizeof(void *) * nr_frames;
193
194 if (WARN_ON_ONCE(nr_frames == 0))
195 return NULL;
196 /*
197 * This is absurdly high. It's here just to avoid strange effects when
198 * arithmetics overflows.
199 */
200 if (WARN_ON_ONCE(nr_frames > INT_MAX / sizeof(void *) / 2))
201 return NULL;
202 /*
203 * Avoid higher order allocations, use vmalloc instead. It should
204 * be rare anyway.
205 */
206 if (size <= PAGE_SIZE)
207 vec = kmalloc(size, GFP_KERNEL);
208 else
209 vec = vmalloc(size);
210 if (!vec)
211 return NULL;
212 vec->nr_allocated = nr_frames;
213 vec->nr_frames = 0;
214 return vec;
215}
216EXPORT_SYMBOL(frame_vector_create);
217
218/**
219 * frame_vector_destroy() - free memory allocated to carry frame vector
220 * @vec: Frame vector to free
221 *
222 * Free structure allocated by frame_vector_create() to carry frames.
223 */
224void frame_vector_destroy(struct frame_vector *vec)
225{
226 /* Make sure put_vaddr_frames() got called properly... */
227 VM_BUG_ON(vec->nr_frames > 0);
228 kvfree(vec);
229}
230EXPORT_SYMBOL(frame_vector_destroy);