18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/kernel.h>
38c2ecf20Sopenharmony_ci#include <linux/errno.h>
48c2ecf20Sopenharmony_ci#include <linux/err.h>
58c2ecf20Sopenharmony_ci#include <linux/mm.h>
68c2ecf20Sopenharmony_ci#include <linux/slab.h>
78c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
88c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
98c2ecf20Sopenharmony_ci#include <linux/sched.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/**
128c2ecf20Sopenharmony_ci * get_vaddr_frames() - map virtual addresses to pfns
138c2ecf20Sopenharmony_ci * @start:	starting user address
148c2ecf20Sopenharmony_ci * @nr_frames:	number of pages / pfns from start to map
158c2ecf20Sopenharmony_ci * @gup_flags:	flags modifying lookup behaviour
168c2ecf20Sopenharmony_ci * @vec:	structure which receives pages / pfns of the addresses mapped.
178c2ecf20Sopenharmony_ci *		It should have space for at least nr_frames entries.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * This function maps virtual addresses from @start and fills @vec structure
208c2ecf20Sopenharmony_ci * with page frame numbers or page pointers to corresponding pages (choice
218c2ecf20Sopenharmony_ci * depends on the type of the vma underlying the virtual address). If @start
228c2ecf20Sopenharmony_ci * belongs to a normal vma, the function grabs reference to each of the pages
238c2ecf20Sopenharmony_ci * to pin them in memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't
248c2ecf20Sopenharmony_ci * touch page structures and the caller must make sure pfns aren't reused for
258c2ecf20Sopenharmony_ci * anything else while he is using them.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * The function returns number of pages mapped which may be less than
288c2ecf20Sopenharmony_ci * @nr_frames. In particular we stop mapping if there are more vmas of
298c2ecf20Sopenharmony_ci * different type underlying the specified range of virtual addresses.
308c2ecf20Sopenharmony_ci * When the function isn't able to map a single page, it returns error.
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * Note that get_vaddr_frames() cannot follow VM_IO mappings. It used
338c2ecf20Sopenharmony_ci * to be able to do that, but that could (racily) return non-refcounted
348c2ecf20Sopenharmony_ci * pfns.
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci * This function takes care of grabbing mmap_lock as necessary.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ciint get_vaddr_frames(unsigned long start, unsigned int nr_frames,
398c2ecf20Sopenharmony_ci		     unsigned int gup_flags, struct frame_vector *vec)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct mm_struct *mm = current->mm;
428c2ecf20Sopenharmony_ci	struct vm_area_struct *vma;
438c2ecf20Sopenharmony_ci	int ret = 0;
448c2ecf20Sopenharmony_ci	int locked;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (nr_frames == 0)
478c2ecf20Sopenharmony_ci		return 0;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(nr_frames > vec->nr_allocated))
508c2ecf20Sopenharmony_ci		nr_frames = vec->nr_allocated;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	start = untagged_addr(start);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	mmap_read_lock(mm);
558c2ecf20Sopenharmony_ci	locked = 1;
568c2ecf20Sopenharmony_ci	vma = find_vma_intersection(mm, start, start + 1);
578c2ecf20Sopenharmony_ci	if (!vma) {
588c2ecf20Sopenharmony_ci		ret = -EFAULT;
598c2ecf20Sopenharmony_ci		goto out;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/*
638c2ecf20Sopenharmony_ci	 * While get_vaddr_frames() could be used for transient (kernel
648c2ecf20Sopenharmony_ci	 * controlled lifetime) pinning of memory pages all current
658c2ecf20Sopenharmony_ci	 * users establish long term (userspace controlled lifetime)
668c2ecf20Sopenharmony_ci	 * page pinning. Treat get_vaddr_frames() like
678c2ecf20Sopenharmony_ci	 * get_user_pages_longterm() and disallow it for filesystem-dax
688c2ecf20Sopenharmony_ci	 * mappings.
698c2ecf20Sopenharmony_ci	 */
708c2ecf20Sopenharmony_ci	if (vma_is_fsdax(vma)) {
718c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
728c2ecf20Sopenharmony_ci		goto out;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
768c2ecf20Sopenharmony_ci		vec->got_ref = true;
778c2ecf20Sopenharmony_ci		vec->is_pfns = false;
788c2ecf20Sopenharmony_ci		ret = pin_user_pages_locked(start, nr_frames,
798c2ecf20Sopenharmony_ci			gup_flags, (struct page **)(vec->ptrs), &locked);
808c2ecf20Sopenharmony_ci		if (likely(ret > 0))
818c2ecf20Sopenharmony_ci			goto out;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	vec->nr_frames = 0;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ciout:
878c2ecf20Sopenharmony_ci	if (locked)
888c2ecf20Sopenharmony_ci		mmap_read_unlock(mm);
898c2ecf20Sopenharmony_ci	if (!ret)
908c2ecf20Sopenharmony_ci		ret = -EFAULT;
918c2ecf20Sopenharmony_ci	if (ret > 0)
928c2ecf20Sopenharmony_ci		vec->nr_frames = ret;
938c2ecf20Sopenharmony_ci	return ret;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_vaddr_frames);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/**
988c2ecf20Sopenharmony_ci * put_vaddr_frames() - drop references to pages if get_vaddr_frames() acquired
998c2ecf20Sopenharmony_ci *			them
1008c2ecf20Sopenharmony_ci * @vec:	frame vector to put
1018c2ecf20Sopenharmony_ci *
1028c2ecf20Sopenharmony_ci * Drop references to pages if get_vaddr_frames() acquired them. We also
1038c2ecf20Sopenharmony_ci * invalidate the frame vector so that it is prepared for the next call into
1048c2ecf20Sopenharmony_ci * get_vaddr_frames().
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_civoid put_vaddr_frames(struct frame_vector *vec)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct page **pages;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (!vec->got_ref)
1118c2ecf20Sopenharmony_ci		goto out;
1128c2ecf20Sopenharmony_ci	pages = frame_vector_pages(vec);
1138c2ecf20Sopenharmony_ci	/*
1148c2ecf20Sopenharmony_ci	 * frame_vector_pages() might needed to do a conversion when
1158c2ecf20Sopenharmony_ci	 * get_vaddr_frames() got pages but vec was later converted to pfns.
1168c2ecf20Sopenharmony_ci	 * But it shouldn't really fail to convert pfns back...
1178c2ecf20Sopenharmony_ci	 */
1188c2ecf20Sopenharmony_ci	if (WARN_ON(IS_ERR(pages)))
1198c2ecf20Sopenharmony_ci		goto out;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	unpin_user_pages(pages, vec->nr_frames);
1228c2ecf20Sopenharmony_ci	vec->got_ref = false;
1238c2ecf20Sopenharmony_ciout:
1248c2ecf20Sopenharmony_ci	vec->nr_frames = 0;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(put_vaddr_frames);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/**
1298c2ecf20Sopenharmony_ci * frame_vector_to_pages - convert frame vector to contain page pointers
1308c2ecf20Sopenharmony_ci * @vec:	frame vector to convert
1318c2ecf20Sopenharmony_ci *
1328c2ecf20Sopenharmony_ci * Convert @vec to contain array of page pointers.  If the conversion is
1338c2ecf20Sopenharmony_ci * successful, return 0. Otherwise return an error. Note that we do not grab
1348c2ecf20Sopenharmony_ci * page references for the page structures.
1358c2ecf20Sopenharmony_ci */
1368c2ecf20Sopenharmony_ciint frame_vector_to_pages(struct frame_vector *vec)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	int i;
1398c2ecf20Sopenharmony_ci	unsigned long *nums;
1408c2ecf20Sopenharmony_ci	struct page **pages;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (!vec->is_pfns)
1438c2ecf20Sopenharmony_ci		return 0;
1448c2ecf20Sopenharmony_ci	nums = frame_vector_pfns(vec);
1458c2ecf20Sopenharmony_ci	for (i = 0; i < vec->nr_frames; i++)
1468c2ecf20Sopenharmony_ci		if (!pfn_valid(nums[i]))
1478c2ecf20Sopenharmony_ci			return -EINVAL;
1488c2ecf20Sopenharmony_ci	pages = (struct page **)nums;
1498c2ecf20Sopenharmony_ci	for (i = 0; i < vec->nr_frames; i++)
1508c2ecf20Sopenharmony_ci		pages[i] = pfn_to_page(nums[i]);
1518c2ecf20Sopenharmony_ci	vec->is_pfns = false;
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(frame_vector_to_pages);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/**
1578c2ecf20Sopenharmony_ci * frame_vector_to_pfns - convert frame vector to contain pfns
1588c2ecf20Sopenharmony_ci * @vec:	frame vector to convert
1598c2ecf20Sopenharmony_ci *
1608c2ecf20Sopenharmony_ci * Convert @vec to contain array of pfns.
1618c2ecf20Sopenharmony_ci */
1628c2ecf20Sopenharmony_civoid frame_vector_to_pfns(struct frame_vector *vec)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	int i;
1658c2ecf20Sopenharmony_ci	unsigned long *nums;
1668c2ecf20Sopenharmony_ci	struct page **pages;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (vec->is_pfns)
1698c2ecf20Sopenharmony_ci		return;
1708c2ecf20Sopenharmony_ci	pages = (struct page **)(vec->ptrs);
1718c2ecf20Sopenharmony_ci	nums = (unsigned long *)pages;
1728c2ecf20Sopenharmony_ci	for (i = 0; i < vec->nr_frames; i++)
1738c2ecf20Sopenharmony_ci		nums[i] = page_to_pfn(pages[i]);
1748c2ecf20Sopenharmony_ci	vec->is_pfns = true;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(frame_vector_to_pfns);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/**
1798c2ecf20Sopenharmony_ci * frame_vector_create() - allocate & initialize structure for pinned pfns
1808c2ecf20Sopenharmony_ci * @nr_frames:	number of pfns slots we should reserve
1818c2ecf20Sopenharmony_ci *
1828c2ecf20Sopenharmony_ci * Allocate and initialize struct pinned_pfns to be able to hold @nr_pfns
1838c2ecf20Sopenharmony_ci * pfns.
1848c2ecf20Sopenharmony_ci */
1858c2ecf20Sopenharmony_cistruct frame_vector *frame_vector_create(unsigned int nr_frames)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct frame_vector *vec;
1888c2ecf20Sopenharmony_ci	int size = sizeof(struct frame_vector) + sizeof(void *) * nr_frames;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(nr_frames == 0))
1918c2ecf20Sopenharmony_ci		return NULL;
1928c2ecf20Sopenharmony_ci	/*
1938c2ecf20Sopenharmony_ci	 * This is absurdly high. It's here just to avoid strange effects when
1948c2ecf20Sopenharmony_ci	 * arithmetics overflows.
1958c2ecf20Sopenharmony_ci	 */
1968c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(nr_frames > INT_MAX / sizeof(void *) / 2))
1978c2ecf20Sopenharmony_ci		return NULL;
1988c2ecf20Sopenharmony_ci	/*
1998c2ecf20Sopenharmony_ci	 * Avoid higher order allocations, use vmalloc instead. It should
2008c2ecf20Sopenharmony_ci	 * be rare anyway.
2018c2ecf20Sopenharmony_ci	 */
2028c2ecf20Sopenharmony_ci	vec = kvmalloc(size, GFP_KERNEL);
2038c2ecf20Sopenharmony_ci	if (!vec)
2048c2ecf20Sopenharmony_ci		return NULL;
2058c2ecf20Sopenharmony_ci	vec->nr_allocated = nr_frames;
2068c2ecf20Sopenharmony_ci	vec->nr_frames = 0;
2078c2ecf20Sopenharmony_ci	return vec;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(frame_vector_create);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci/**
2128c2ecf20Sopenharmony_ci * frame_vector_destroy() - free memory allocated to carry frame vector
2138c2ecf20Sopenharmony_ci * @vec:	Frame vector to free
2148c2ecf20Sopenharmony_ci *
2158c2ecf20Sopenharmony_ci * Free structure allocated by frame_vector_create() to carry frames.
2168c2ecf20Sopenharmony_ci */
2178c2ecf20Sopenharmony_civoid frame_vector_destroy(struct frame_vector *vec)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	/* Make sure put_vaddr_frames() got called properly... */
2208c2ecf20Sopenharmony_ci	VM_BUG_ON(vec->nr_frames > 0);
2218c2ecf20Sopenharmony_ci	kvfree(vec);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(frame_vector_destroy);
224