mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	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
							
								
									acb39fc9a4
								
							
						
					
					
						commit
						dd487640b2
					
				@ -209,25 +209,58 @@ uiomove(void *p, size_t n, enum uio_rw rw, struct uio *uio)
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(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
 | 
			
		||||
uio_prefaultpages(ssize_t n, struct uio *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(uio_prefaultpages);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user