diff options
Diffstat (limited to 'mm/util.c')
| -rw-r--r-- | mm/util.c | 36 | 
1 files changed, 29 insertions, 7 deletions
diff --git a/mm/util.c b/mm/util.c index 34e57fae959d..c1250501364f 100644 --- a/mm/util.c +++ b/mm/util.c @@ -150,18 +150,14 @@ EXPORT_SYMBOL(kmemdup_nul);   * @src: source address in user space   * @len: number of bytes to copy   * - * Returns an ERR_PTR() on failure. + * Returns an ERR_PTR() on failure.  Result is physically + * contiguous, to be freed by kfree().   */  void *memdup_user(const void __user *src, size_t len)  {  	void *p; -	/* -	 * Always use GFP_KERNEL, since copy_from_user() can sleep and -	 * cause pagefault, which makes it pointless to use GFP_NOFS -	 * or GFP_ATOMIC. -	 */ -	p = kmalloc_track_caller(len, GFP_KERNEL); +	p = kmalloc_track_caller(len, GFP_USER);  	if (!p)  		return ERR_PTR(-ENOMEM); @@ -174,6 +170,32 @@ void *memdup_user(const void __user *src, size_t len)  }  EXPORT_SYMBOL(memdup_user); +/** + * vmemdup_user - duplicate memory region from user space + * + * @src: source address in user space + * @len: number of bytes to copy + * + * Returns an ERR_PTR() on failure.  Result may be not + * physically contiguous.  Use kvfree() to free. + */ +void *vmemdup_user(const void __user *src, size_t len) +{ +	void *p; + +	p = kvmalloc(len, GFP_USER); +	if (!p) +		return ERR_PTR(-ENOMEM); + +	if (copy_from_user(p, src, len)) { +		kvfree(p); +		return ERR_PTR(-EFAULT); +	} + +	return p; +} +EXPORT_SYMBOL(vmemdup_user); +  /*   * strndup_user - duplicate an existing string from user space   * @s: The string to duplicate  | 
