diff options
Diffstat (limited to 'mm/gup.c')
| -rw-r--r-- | mm/gup.c | 66 | 
1 files changed, 65 insertions, 1 deletions
| @@ -66,7 +66,7 @@ static int follow_pfn_pte(struct vm_area_struct *vma, unsigned long address,   */  static inline bool can_follow_write_pte(pte_t pte, unsigned int flags)  { -	return pte_write(pte) || +	return pte_access_permitted(pte, WRITE) ||  		((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte));  } @@ -1095,6 +1095,70 @@ long get_user_pages(unsigned long start, unsigned long nr_pages,  }  EXPORT_SYMBOL(get_user_pages); +#ifdef CONFIG_FS_DAX +/* + * This is the same as get_user_pages() in that it assumes we are + * operating on the current task's mm, but it goes further to validate + * that the vmas associated with the address range are suitable for + * longterm elevated page reference counts. For example, filesystem-dax + * mappings are subject to the lifetime enforced by the filesystem and + * we need guarantees that longterm users like RDMA and V4L2 only + * establish mappings that have a kernel enforced revocation mechanism. + * + * "longterm" == userspace controlled elevated page count lifetime. + * Contrast this to iov_iter_get_pages() usages which are transient. + */ +long get_user_pages_longterm(unsigned long start, unsigned long nr_pages, +		unsigned int gup_flags, struct page **pages, +		struct vm_area_struct **vmas_arg) +{ +	struct vm_area_struct **vmas = vmas_arg; +	struct vm_area_struct *vma_prev = NULL; +	long rc, i; + +	if (!pages) +		return -EINVAL; + +	if (!vmas) { +		vmas = kcalloc(nr_pages, sizeof(struct vm_area_struct *), +			       GFP_KERNEL); +		if (!vmas) +			return -ENOMEM; +	} + +	rc = get_user_pages(start, nr_pages, gup_flags, pages, vmas); + +	for (i = 0; i < rc; i++) { +		struct vm_area_struct *vma = vmas[i]; + +		if (vma == vma_prev) +			continue; + +		vma_prev = vma; + +		if (vma_is_fsdax(vma)) +			break; +	} + +	/* +	 * Either get_user_pages() failed, or the vma validation +	 * succeeded, in either case we don't need to put_page() before +	 * returning. +	 */ +	if (i >= rc) +		goto out; + +	for (i = 0; i < rc; i++) +		put_page(pages[i]); +	rc = -EOPNOTSUPP; +out: +	if (vmas != vmas_arg) +		kfree(vmas); +	return rc; +} +EXPORT_SYMBOL(get_user_pages_longterm); +#endif /* CONFIG_FS_DAX */ +  /**   * populate_vma_page_range() -  populate a range of pages in the vma.   * @vma:   target vma | 

