mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 18:40:43 +03:00
deadlock between mm_sem and tx assign in zfs_write() and page fault
The bug time sequence: 1. thread #1, `zfs_write` assign a txg "n". 2. In a same process, thread #2, mmap page fault (which means the `mm_sem` is hold) occurred, `zfs_dirty_inode` open a txg failed, and wait previous txg "n" completed. 3. thread #1 call `uiomove` to write, however page fault is occurred in `uiomove`, which means it need `mm_sem`, but `mm_sem` is hold by thread #2, so it stuck and can't complete, then txg "n" will not complete. So thread #1 and thread #2 are deadlocked. Reviewed-by: Chunwei Chen <tuxoko@gmail.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Matthew Ahrens <mahrens@delphix.com> Signed-off-by: Grady Wong <grady.w@xtaotech.com> Closes #7939
This commit is contained in:
@@ -52,6 +52,7 @@
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/strings.h>
|
||||
#include <linux/kmap_compat.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
/*
|
||||
* Move "n" bytes at byte address "p"; "rw" indicates the direction
|
||||
@@ -79,8 +80,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:
|
||||
@@ -158,7 +175,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;
|
||||
@@ -172,7 +189,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;
|
||||
@@ -196,7 +213,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;
|
||||
@@ -206,8 +223,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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user