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