]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - mm/util.c
Merge tags 'cris-for-4.16' and 'cris-for-4.16-urgent' of git://git.kernel.org/pub...
[linux.git] / mm / util.c
index 34e57fae959decc1edf0805ca5bf40b1ef40d8de..c1250501364fbdb319420d2ba3cd1fc8b63d8c37 100644 (file)
--- 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