Linux 5.10 compat: restore custom uio_prefaultpages()
As part of commit 1c2358c1
the custom uio_prefaultpages() code
was removed in favor of using the generic kernel provided
iov_iter_fault_in_readable() interface. Unfortunately, it
turns out that up until the Linux 4.7 kernel the function would
only ever fault in the first iovec of the iov_iter. The result
being uiomove_iov() may hang waiting for the page.
This commit effectively restores the custom uio_prefaultpages()
pages code for Linux 4.9 and earlier kernels which contain the
troublesome version of iov_iter_fault_in_readable().
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #11463
Closes #11484
This commit is contained in:
parent
d0cd9a5cc6
commit
83b91ae1a4
|
@ -209,25 +209,58 @@ zfs_uiomove(void *p, size_t n, zfs_uio_rw_t rw, zfs_uio_t *uio)
|
|||
}
|
||||
EXPORT_SYMBOL(zfs_uiomove);
|
||||
|
||||
/*
|
||||
* Fault in the pages of the first n bytes specified by the uio structure.
|
||||
* 1 byte in each page is touched and the uio struct is unmodified. Any
|
||||
* error will terminate the process as this is only a best attempt to get
|
||||
* the pages resident.
|
||||
*/
|
||||
int
|
||||
zfs_uio_prefaultpages(ssize_t n, zfs_uio_t *uio)
|
||||
{
|
||||
struct iov_iter iter, *iterp = NULL;
|
||||
|
||||
#if defined(HAVE_IOV_ITER_FAULT_IN_READABLE)
|
||||
if (uio->uio_segflg == UIO_USERSPACE) {
|
||||
iterp = &iter;
|
||||
iov_iter_init_compat(iterp, READ, uio->uio_iov,
|
||||
uio->uio_iovcnt, uio->uio_resid);
|
||||
if (uio->uio_segflg == UIO_SYSSPACE || uio->uio_segflg == UIO_BVEC) {
|
||||
/* There's never a need to fault in kernel pages */
|
||||
return (0);
|
||||
#if defined(HAVE_VFS_IOV_ITER)
|
||||
} else if (uio->uio_segflg == UIO_ITER) {
|
||||
iterp = uio->uio_iter;
|
||||
/*
|
||||
* At least a Linux 4.9 kernel, iov_iter_fault_in_readable()
|
||||
* can be relied on to fault in user pages when referenced.
|
||||
*/
|
||||
if (iov_iter_fault_in_readable(uio->uio_iter, n))
|
||||
return (EFAULT);
|
||||
#endif
|
||||
} else {
|
||||
/* Fault in all user pages */
|
||||
ASSERT3S(uio->uio_segflg, ==, UIO_USERSPACE);
|
||||
const struct iovec *iov = uio->uio_iov;
|
||||
int iovcnt = uio->uio_iovcnt;
|
||||
size_t skip = uio->uio_skip;
|
||||
uint8_t tmp;
|
||||
caddr_t p;
|
||||
|
||||
for (; n > 0 && iovcnt > 0; iov++, iovcnt--, skip = 0) {
|
||||
ulong_t cnt = MIN(iov->iov_len - skip, n);
|
||||
/* empty iov */
|
||||
if (cnt == 0)
|
||||
continue;
|
||||
n -= cnt;
|
||||
/* touch each page in this segment. */
|
||||
p = iov->iov_base + skip;
|
||||
while (cnt) {
|
||||
if (get_user(tmp, (uint8_t *)p))
|
||||
return (EFAULT);
|
||||
ulong_t incr = MIN(cnt, PAGESIZE);
|
||||
p += incr;
|
||||
cnt -= incr;
|
||||
}
|
||||
/* touch the last byte in case it straddles a page. */
|
||||
p--;
|
||||
if (get_user(tmp, (uint8_t *)p))
|
||||
return (EFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
if (iterp && iov_iter_fault_in_readable(iterp, n))
|
||||
return (EFAULT);
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
EXPORT_SYMBOL(zfs_uio_prefaultpages);
|
||||
|
|
Loading…
Reference in New Issue