xref: /linux/drivers/media/common/videobuf2/frame_vector.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1eb83b8e3SDaniel Vetter // SPDX-License-Identifier: GPL-2.0
2eb83b8e3SDaniel Vetter #include <linux/kernel.h>
3eb83b8e3SDaniel Vetter #include <linux/errno.h>
4eb83b8e3SDaniel Vetter #include <linux/err.h>
5eb83b8e3SDaniel Vetter #include <linux/mm.h>
6eb83b8e3SDaniel Vetter #include <linux/slab.h>
7eb83b8e3SDaniel Vetter #include <linux/vmalloc.h>
8eb83b8e3SDaniel Vetter #include <linux/pagemap.h>
9eb83b8e3SDaniel Vetter #include <linux/sched.h>
10eb83b8e3SDaniel Vetter 
11eb83b8e3SDaniel Vetter #include <media/frame_vector.h>
12eb83b8e3SDaniel Vetter 
13eb83b8e3SDaniel Vetter /**
14eb83b8e3SDaniel Vetter  * get_vaddr_frames() - map virtual addresses to pfns
15eb83b8e3SDaniel Vetter  * @start:	starting user address
16eb83b8e3SDaniel Vetter  * @nr_frames:	number of pages / pfns from start to map
17e2fc6eddSHans Verkuil  * @write:	the mapped address has write permission
18eb83b8e3SDaniel Vetter  * @vec:	structure which receives pages / pfns of the addresses mapped.
19eb83b8e3SDaniel Vetter  *		It should have space for at least nr_frames entries.
20eb83b8e3SDaniel Vetter  *
21eb83b8e3SDaniel Vetter  * This function maps virtual addresses from @start and fills @vec structure
22eb83b8e3SDaniel Vetter  * with page frame numbers or page pointers to corresponding pages (choice
23eb83b8e3SDaniel Vetter  * depends on the type of the vma underlying the virtual address). If @start
24eb83b8e3SDaniel Vetter  * belongs to a normal vma, the function grabs reference to each of the pages
25eb83b8e3SDaniel Vetter  * to pin them in memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't
26eb83b8e3SDaniel Vetter  * touch page structures and the caller must make sure pfns aren't reused for
27eb83b8e3SDaniel Vetter  * anything else while he is using them.
28eb83b8e3SDaniel Vetter  *
29eb83b8e3SDaniel Vetter  * The function returns number of pages mapped which may be less than
30eb83b8e3SDaniel Vetter  * @nr_frames. In particular we stop mapping if there are more vmas of
31eb83b8e3SDaniel Vetter  * different type underlying the specified range of virtual addresses.
32eb83b8e3SDaniel Vetter  * When the function isn't able to map a single page, it returns error.
33eb83b8e3SDaniel Vetter  *
34735de5caSHans Verkuil  * Note that get_vaddr_frames() cannot follow VM_IO mappings. It used
35735de5caSHans Verkuil  * to be able to do that, but that could (racily) return non-refcounted
36735de5caSHans Verkuil  * pfns.
37735de5caSHans Verkuil  *
38eb83b8e3SDaniel Vetter  * This function takes care of grabbing mmap_lock as necessary.
39eb83b8e3SDaniel Vetter  */
get_vaddr_frames(unsigned long start,unsigned int nr_frames,bool write,struct frame_vector * vec)40e2fc6eddSHans Verkuil int get_vaddr_frames(unsigned long start, unsigned int nr_frames, bool write,
41eb83b8e3SDaniel Vetter 		     struct frame_vector *vec)
42eb83b8e3SDaniel Vetter {
436647e76aSLinus Torvalds 	int ret;
44e2ca6ba6SLinus Torvalds 	unsigned int gup_flags = FOLL_LONGTERM;
45eb83b8e3SDaniel Vetter 
46eb83b8e3SDaniel Vetter 	if (nr_frames == 0)
47eb83b8e3SDaniel Vetter 		return 0;
48eb83b8e3SDaniel Vetter 
49eb83b8e3SDaniel Vetter 	if (WARN_ON_ONCE(nr_frames > vec->nr_allocated))
50eb83b8e3SDaniel Vetter 		nr_frames = vec->nr_allocated;
51eb83b8e3SDaniel Vetter 
52eb83b8e3SDaniel Vetter 	start = untagged_addr(start);
53eb83b8e3SDaniel Vetter 
54e2fc6eddSHans Verkuil 	if (write)
55e2fc6eddSHans Verkuil 		gup_flags |= FOLL_WRITE;
56e2fc6eddSHans Verkuil 
57e2fc6eddSHans Verkuil 	ret = pin_user_pages_fast(start, nr_frames, gup_flags,
58eb83b8e3SDaniel Vetter 				  (struct page **)(vec->ptrs));
59eb83b8e3SDaniel Vetter 	vec->got_ref = true;
60eb83b8e3SDaniel Vetter 	vec->is_pfns = false;
61eb83b8e3SDaniel Vetter 	vec->nr_frames = ret;
626647e76aSLinus Torvalds 
636647e76aSLinus Torvalds 	if (likely(ret > 0))
64eb83b8e3SDaniel Vetter 		return ret;
656647e76aSLinus Torvalds 
666647e76aSLinus Torvalds 	vec->nr_frames = 0;
676647e76aSLinus Torvalds 	return ret ? ret : -EFAULT;
68eb83b8e3SDaniel Vetter }
69eb83b8e3SDaniel Vetter EXPORT_SYMBOL(get_vaddr_frames);
70eb83b8e3SDaniel Vetter 
71eb83b8e3SDaniel Vetter /**
72eb83b8e3SDaniel Vetter  * put_vaddr_frames() - drop references to pages if get_vaddr_frames() acquired
73eb83b8e3SDaniel Vetter  *			them
74eb83b8e3SDaniel Vetter  * @vec:	frame vector to put
75eb83b8e3SDaniel Vetter  *
76eb83b8e3SDaniel Vetter  * Drop references to pages if get_vaddr_frames() acquired them. We also
77eb83b8e3SDaniel Vetter  * invalidate the frame vector so that it is prepared for the next call into
78eb83b8e3SDaniel Vetter  * get_vaddr_frames().
79eb83b8e3SDaniel Vetter  */
put_vaddr_frames(struct frame_vector * vec)80eb83b8e3SDaniel Vetter void put_vaddr_frames(struct frame_vector *vec)
81eb83b8e3SDaniel Vetter {
82eb83b8e3SDaniel Vetter 	struct page **pages;
83eb83b8e3SDaniel Vetter 
84eb83b8e3SDaniel Vetter 	if (!vec->got_ref)
85eb83b8e3SDaniel Vetter 		goto out;
86eb83b8e3SDaniel Vetter 	pages = frame_vector_pages(vec);
87eb83b8e3SDaniel Vetter 	/*
88eb83b8e3SDaniel Vetter 	 * frame_vector_pages() might needed to do a conversion when
89eb83b8e3SDaniel Vetter 	 * get_vaddr_frames() got pages but vec was later converted to pfns.
90eb83b8e3SDaniel Vetter 	 * But it shouldn't really fail to convert pfns back...
91eb83b8e3SDaniel Vetter 	 */
92eb83b8e3SDaniel Vetter 	if (WARN_ON(IS_ERR(pages)))
93eb83b8e3SDaniel Vetter 		goto out;
94eb83b8e3SDaniel Vetter 
95eb83b8e3SDaniel Vetter 	unpin_user_pages(pages, vec->nr_frames);
96eb83b8e3SDaniel Vetter 	vec->got_ref = false;
97eb83b8e3SDaniel Vetter out:
98eb83b8e3SDaniel Vetter 	vec->nr_frames = 0;
99eb83b8e3SDaniel Vetter }
100eb83b8e3SDaniel Vetter EXPORT_SYMBOL(put_vaddr_frames);
101eb83b8e3SDaniel Vetter 
102eb83b8e3SDaniel Vetter /**
103eb83b8e3SDaniel Vetter  * frame_vector_to_pages - convert frame vector to contain page pointers
104eb83b8e3SDaniel Vetter  * @vec:	frame vector to convert
105eb83b8e3SDaniel Vetter  *
106eb83b8e3SDaniel Vetter  * Convert @vec to contain array of page pointers.  If the conversion is
107eb83b8e3SDaniel Vetter  * successful, return 0. Otherwise return an error. Note that we do not grab
108eb83b8e3SDaniel Vetter  * page references for the page structures.
109eb83b8e3SDaniel Vetter  */
frame_vector_to_pages(struct frame_vector * vec)110eb83b8e3SDaniel Vetter int frame_vector_to_pages(struct frame_vector *vec)
111eb83b8e3SDaniel Vetter {
112eb83b8e3SDaniel Vetter 	int i;
113eb83b8e3SDaniel Vetter 	unsigned long *nums;
114eb83b8e3SDaniel Vetter 	struct page **pages;
115eb83b8e3SDaniel Vetter 
116eb83b8e3SDaniel Vetter 	if (!vec->is_pfns)
117eb83b8e3SDaniel Vetter 		return 0;
118eb83b8e3SDaniel Vetter 	nums = frame_vector_pfns(vec);
119eb83b8e3SDaniel Vetter 	for (i = 0; i < vec->nr_frames; i++)
120eb83b8e3SDaniel Vetter 		if (!pfn_valid(nums[i]))
121eb83b8e3SDaniel Vetter 			return -EINVAL;
122eb83b8e3SDaniel Vetter 	pages = (struct page **)nums;
123eb83b8e3SDaniel Vetter 	for (i = 0; i < vec->nr_frames; i++)
124eb83b8e3SDaniel Vetter 		pages[i] = pfn_to_page(nums[i]);
125eb83b8e3SDaniel Vetter 	vec->is_pfns = false;
126eb83b8e3SDaniel Vetter 	return 0;
127eb83b8e3SDaniel Vetter }
128eb83b8e3SDaniel Vetter EXPORT_SYMBOL(frame_vector_to_pages);
129eb83b8e3SDaniel Vetter 
130eb83b8e3SDaniel Vetter /**
131eb83b8e3SDaniel Vetter  * frame_vector_to_pfns - convert frame vector to contain pfns
132eb83b8e3SDaniel Vetter  * @vec:	frame vector to convert
133eb83b8e3SDaniel Vetter  *
134eb83b8e3SDaniel Vetter  * Convert @vec to contain array of pfns.
135eb83b8e3SDaniel Vetter  */
frame_vector_to_pfns(struct frame_vector * vec)136eb83b8e3SDaniel Vetter void frame_vector_to_pfns(struct frame_vector *vec)
137eb83b8e3SDaniel Vetter {
138eb83b8e3SDaniel Vetter 	int i;
139eb83b8e3SDaniel Vetter 	unsigned long *nums;
140eb83b8e3SDaniel Vetter 	struct page **pages;
141eb83b8e3SDaniel Vetter 
142eb83b8e3SDaniel Vetter 	if (vec->is_pfns)
143eb83b8e3SDaniel Vetter 		return;
144eb83b8e3SDaniel Vetter 	pages = (struct page **)(vec->ptrs);
145eb83b8e3SDaniel Vetter 	nums = (unsigned long *)pages;
146eb83b8e3SDaniel Vetter 	for (i = 0; i < vec->nr_frames; i++)
147eb83b8e3SDaniel Vetter 		nums[i] = page_to_pfn(pages[i]);
148eb83b8e3SDaniel Vetter 	vec->is_pfns = true;
149eb83b8e3SDaniel Vetter }
150eb83b8e3SDaniel Vetter EXPORT_SYMBOL(frame_vector_to_pfns);
151eb83b8e3SDaniel Vetter 
152eb83b8e3SDaniel Vetter /**
153eb83b8e3SDaniel Vetter  * frame_vector_create() - allocate & initialize structure for pinned pfns
154eb83b8e3SDaniel Vetter  * @nr_frames:	number of pfns slots we should reserve
155eb83b8e3SDaniel Vetter  *
156eb83b8e3SDaniel Vetter  * Allocate and initialize struct pinned_pfns to be able to hold @nr_pfns
157eb83b8e3SDaniel Vetter  * pfns.
158eb83b8e3SDaniel Vetter  */
frame_vector_create(unsigned int nr_frames)159eb83b8e3SDaniel Vetter struct frame_vector *frame_vector_create(unsigned int nr_frames)
160eb83b8e3SDaniel Vetter {
161eb83b8e3SDaniel Vetter 	struct frame_vector *vec;
162*ba85aea8SYu Liao 	int size = struct_size(vec, ptrs, nr_frames);
163eb83b8e3SDaniel Vetter 
164eb83b8e3SDaniel Vetter 	if (WARN_ON_ONCE(nr_frames == 0))
165eb83b8e3SDaniel Vetter 		return NULL;
166eb83b8e3SDaniel Vetter 	/*
167eb83b8e3SDaniel Vetter 	 * This is absurdly high. It's here just to avoid strange effects when
168eb83b8e3SDaniel Vetter 	 * arithmetics overflows.
169eb83b8e3SDaniel Vetter 	 */
170eb83b8e3SDaniel Vetter 	if (WARN_ON_ONCE(nr_frames > INT_MAX / sizeof(void *) / 2))
171eb83b8e3SDaniel Vetter 		return NULL;
172eb83b8e3SDaniel Vetter 	/*
173eb83b8e3SDaniel Vetter 	 * Avoid higher order allocations, use vmalloc instead. It should
174eb83b8e3SDaniel Vetter 	 * be rare anyway.
175eb83b8e3SDaniel Vetter 	 */
176eb83b8e3SDaniel Vetter 	vec = kvmalloc(size, GFP_KERNEL);
177eb83b8e3SDaniel Vetter 	if (!vec)
178eb83b8e3SDaniel Vetter 		return NULL;
179eb83b8e3SDaniel Vetter 	vec->nr_allocated = nr_frames;
180eb83b8e3SDaniel Vetter 	vec->nr_frames = 0;
181eb83b8e3SDaniel Vetter 	return vec;
182eb83b8e3SDaniel Vetter }
183eb83b8e3SDaniel Vetter EXPORT_SYMBOL(frame_vector_create);
184eb83b8e3SDaniel Vetter 
185eb83b8e3SDaniel Vetter /**
186eb83b8e3SDaniel Vetter  * frame_vector_destroy() - free memory allocated to carry frame vector
187eb83b8e3SDaniel Vetter  * @vec:	Frame vector to free
188eb83b8e3SDaniel Vetter  *
189eb83b8e3SDaniel Vetter  * Free structure allocated by frame_vector_create() to carry frames.
190eb83b8e3SDaniel Vetter  */
frame_vector_destroy(struct frame_vector * vec)191eb83b8e3SDaniel Vetter void frame_vector_destroy(struct frame_vector *vec)
192eb83b8e3SDaniel Vetter {
193eb83b8e3SDaniel Vetter 	/* Make sure put_vaddr_frames() got called properly... */
194eb83b8e3SDaniel Vetter 	VM_BUG_ON(vec->nr_frames > 0);
195eb83b8e3SDaniel Vetter 	kvfree(vec);
196eb83b8e3SDaniel Vetter }
197eb83b8e3SDaniel Vetter EXPORT_SYMBOL(frame_vector_destroy);
198