]> git.proxmox.com Git - mirror_zfs.git/blobdiff - module/zcommon/zfs_uio.c
deadlock between mm_sem and tx assign in zfs_write() and page fault
[mirror_zfs.git] / module / zcommon / zfs_uio.c
index 7b4175bbeeeb4e3631f470e18353a1cc4637dff9..8e969bbcc00647bd8387428e7ca86f6f868321cf 100644 (file)
@@ -50,6 +50,7 @@
 #include <sys/types.h>
 #include <sys/uio_impl.h>
 #include <linux/kmap_compat.h>
+#include <linux/uaccess.h>
 
 /*
  * Move "n" bytes at byte address "p"; "rw" indicates the direction
@@ -77,8 +78,24 @@ uiomove_iov(void *p, size_t n, enum uio_rw rw, struct uio *uio)
                                if (copy_to_user(iov->iov_base+skip, p, cnt))
                                        return (EFAULT);
                        } else {
-                               if (copy_from_user(p, iov->iov_base+skip, cnt))
-                                       return (EFAULT);
+                               if (uio->uio_fault_disable) {
+                                       if (!access_ok(VERIFY_READ,
+                                           (iov->iov_base + skip), cnt)) {
+                                               return (EFAULT);
+                                       }
+
+                                       pagefault_disable();
+                                       if (__copy_from_user_inatomic(p,
+                                           (iov->iov_base + skip), cnt)) {
+                                               pagefault_enable();
+                                               return (EFAULT);
+                                       }
+                                       pagefault_enable();
+                               } else {
+                                       if (copy_from_user(p,
+                                           (iov->iov_base + skip), cnt))
+                                               return (EFAULT);
+                               }
                        }
                        break;
                case UIO_SYSSPACE:
@@ -156,7 +173,7 @@ EXPORT_SYMBOL(uiomove);
  * error will terminate the process as this is only a best attempt to get
  * the pages resident.
  */
-void
+int
 uio_prefaultpages(ssize_t n, struct uio *uio)
 {
        const struct iovec *iov;
@@ -170,7 +187,7 @@ uio_prefaultpages(ssize_t n, struct uio *uio)
        switch (uio->uio_segflg) {
                case UIO_SYSSPACE:
                case UIO_BVEC:
-                       return;
+                       return (0);
                case UIO_USERSPACE:
                case UIO_USERISPACE:
                        break;
@@ -194,7 +211,7 @@ uio_prefaultpages(ssize_t n, struct uio *uio)
                p = iov->iov_base + skip;
                while (cnt) {
                        if (fuword8((uint8_t *)p, &tmp))
-                               return;
+                               return (EFAULT);
                        incr = MIN(cnt, PAGESIZE);
                        p += incr;
                        cnt -= incr;
@@ -204,8 +221,10 @@ uio_prefaultpages(ssize_t n, struct uio *uio)
                 */
                p--;
                if (fuword8((uint8_t *)p, &tmp))
-                       return;
+                       return (EFAULT);
        }
+
+       return (0);
 }
 EXPORT_SYMBOL(uio_prefaultpages);