diff options
Diffstat (limited to 'mm/gup.c')
| -rw-r--r-- | mm/gup.c | 66 | 
1 files changed, 49 insertions, 17 deletions
@@ -124,8 +124,8 @@ static inline struct page *try_get_compound_head(struct page *page, int refs)   * considered failure, and furthermore, a likely bug in the caller, so a warning   * is also emitted.   */ -struct page *try_grab_compound_head(struct page *page, -				    int refs, unsigned int flags) +__maybe_unused struct page *try_grab_compound_head(struct page *page, +						   int refs, unsigned int flags)  {  	if (flags & FOLL_GET)  		return try_get_compound_head(page, refs); @@ -208,10 +208,35 @@ static void put_compound_head(struct page *page, int refs, unsigned int flags)   */  bool __must_check try_grab_page(struct page *page, unsigned int flags)  { -	if (!(flags & (FOLL_GET | FOLL_PIN))) -		return true; +	WARN_ON_ONCE((flags & (FOLL_GET | FOLL_PIN)) == (FOLL_GET | FOLL_PIN)); -	return try_grab_compound_head(page, 1, flags); +	if (flags & FOLL_GET) +		return try_get_page(page); +	else if (flags & FOLL_PIN) { +		int refs = 1; + +		page = compound_head(page); + +		if (WARN_ON_ONCE(page_ref_count(page) <= 0)) +			return false; + +		if (hpage_pincount_available(page)) +			hpage_pincount_add(page, 1); +		else +			refs = GUP_PIN_COUNTING_BIAS; + +		/* +		 * Similar to try_grab_compound_head(): even if using the +		 * hpage_pincount_add/_sub() routines, be sure to +		 * *also* increment the normal page refcount field at least +		 * once, so that the page really is pinned. +		 */ +		page_ref_add(page, refs); + +		mod_node_page_state(page_pgdat(page), NR_FOLL_PIN_ACQUIRED, 1); +	} + +	return true;  }  /** @@ -642,12 +667,17 @@ static struct page *follow_pmd_mask(struct vm_area_struct *vma,  	}  retry:  	if (!pmd_present(pmdval)) { +		/* +		 * Should never reach here, if thp migration is not supported; +		 * Otherwise, it must be a thp migration entry. +		 */ +		VM_BUG_ON(!thp_migration_supported() || +				  !is_pmd_migration_entry(pmdval)); +  		if (likely(!(flags & FOLL_MIGRATION)))  			return no_page_table(vma, flags); -		VM_BUG_ON(thp_migration_supported() && -				  !is_pmd_migration_entry(pmdval)); -		if (is_pmd_migration_entry(pmdval)) -			pmd_migration_entry_wait(mm, pmd); + +		pmd_migration_entry_wait(mm, pmd);  		pmdval = READ_ONCE(*pmd);  		/*  		 * MADV_DONTNEED may convert the pmd to null because @@ -1672,21 +1702,22 @@ size_t fault_in_writeable(char __user *uaddr, size_t size)  	if (unlikely(size == 0))  		return 0; +	if (!user_write_access_begin(uaddr, size)) +		return size;  	if (!PAGE_ALIGNED(uaddr)) { -		if (unlikely(__put_user(0, uaddr) != 0)) -			return size; +		unsafe_put_user(0, uaddr, out);  		uaddr = (char __user *)PAGE_ALIGN((unsigned long)uaddr);  	}  	end = (char __user *)PAGE_ALIGN((unsigned long)start + size);  	if (unlikely(end < start))  		end = NULL;  	while (uaddr != end) { -		if (unlikely(__put_user(0, uaddr) != 0)) -			goto out; +		unsafe_put_user(0, uaddr, out);  		uaddr += PAGE_SIZE;  	}  out: +	user_write_access_end();  	if (size > uaddr - start)  		return size - (uaddr - start);  	return 0; @@ -1771,21 +1802,22 @@ size_t fault_in_readable(const char __user *uaddr, size_t size)  	if (unlikely(size == 0))  		return 0; +	if (!user_read_access_begin(uaddr, size)) +		return size;  	if (!PAGE_ALIGNED(uaddr)) { -		if (unlikely(__get_user(c, uaddr) != 0)) -			return size; +		unsafe_get_user(c, uaddr, out);  		uaddr = (const char __user *)PAGE_ALIGN((unsigned long)uaddr);  	}  	end = (const char __user *)PAGE_ALIGN((unsigned long)start + size);  	if (unlikely(end < start))  		end = NULL;  	while (uaddr != end) { -		if (unlikely(__get_user(c, uaddr) != 0)) -			goto out; +		unsafe_get_user(c, uaddr, out);  		uaddr += PAGE_SIZE;  	}  out: +	user_read_access_end();  	(void)c;  	if (size > uaddr - start)  		return size - (uaddr - start);  |