Skip to content

Commit f169c53

Browse files
committed
Linux 5.10 compat: restore custom uio_prefaultpages()
As part of commit 1c2358c 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 <[email protected]> Closes openzfs#11463 Closes openzfs#11484
1 parent acb39fc commit f169c53

File tree

1 file changed

+44
-11
lines changed

1 file changed

+44
-11
lines changed

module/os/linux/zfs/zfs_uio.c

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -209,25 +209,58 @@ uiomove(void *p, size_t n, enum uio_rw rw, struct uio *uio)
209209
}
210210
EXPORT_SYMBOL(uiomove);
211211

212+
/*
213+
* Fault in the pages of the first n bytes specified by the uio structure.
214+
* 1 byte in each page is touched and the uio struct is unmodified. Any
215+
* error will terminate the process as this is only a best attempt to get
216+
* the pages resident.
217+
*/
212218
int
213219
uio_prefaultpages(ssize_t n, struct uio *uio)
214220
{
215-
struct iov_iter iter, *iterp = NULL;
216-
217-
#if defined(HAVE_IOV_ITER_FAULT_IN_READABLE)
218-
if (uio->uio_segflg == UIO_USERSPACE) {
219-
iterp = &iter;
220-
iov_iter_init_compat(iterp, READ, uio->uio_iov,
221-
uio->uio_iovcnt, uio->uio_resid);
221+
if (uio->uio_segflg == UIO_SYSSPACE || uio->uio_segflg == UIO_BVEC) {
222+
/* There's never a need to fault in kernel pages */
223+
return (0);
222224
#if defined(HAVE_VFS_IOV_ITER)
223225
} else if (uio->uio_segflg == UIO_ITER) {
224-
iterp = uio->uio_iter;
226+
/*
227+
* At least a Linux 4.9 kernel, iov_iter_fault_in_readable()
228+
* can be relied on to fault in user pages when referenced.
229+
*/
230+
if (iov_iter_fault_in_readable(uio->uio_iter, n))
231+
return (EFAULT);
225232
#endif
233+
} else {
234+
/* Fault in all user pages */
235+
ASSERT3S(uio->uio_segflg, ==, UIO_USERSPACE);
236+
const struct iovec *iov = uio->uio_iov;
237+
int iovcnt = uio->uio_iovcnt;
238+
size_t skip = uio->uio_skip;
239+
uint8_t tmp;
240+
caddr_t p;
241+
242+
for (; n > 0 && iovcnt > 0; iov++, iovcnt--, skip = 0) {
243+
ulong_t cnt = MIN(iov->iov_len - skip, n);
244+
/* empty iov */
245+
if (cnt == 0)
246+
continue;
247+
n -= cnt;
248+
/* touch each page in this segment. */
249+
p = iov->iov_base + skip;
250+
while (cnt) {
251+
if (get_user(tmp, (uint8_t *)p))
252+
return (EFAULT);
253+
ulong_t incr = MIN(cnt, PAGESIZE);
254+
p += incr;
255+
cnt -= incr;
256+
}
257+
/* touch the last byte in case it straddles a page. */
258+
p--;
259+
if (get_user(tmp, (uint8_t *)p))
260+
return (EFAULT);
261+
}
226262
}
227263

228-
if (iterp && iov_iter_fault_in_readable(iterp, n))
229-
return (EFAULT);
230-
#endif
231264
return (0);
232265
}
233266
EXPORT_SYMBOL(uio_prefaultpages);

0 commit comments

Comments
 (0)