Date: Sun, 16 May 2021 23:52:15 GMT From: Rick Macklem <rmacklem@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Subject: git: cd192185662e - stable/13 - copy_file_range(2): improve copying of a large hole to EOF Message-ID: <202105162352.14GNqFGa043783@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by rmacklem: URL: https://cgit.FreeBSD.org/src/commit/?id=cd192185662ef90f52614ea17cb739f5047d5e0c commit cd192185662ef90f52614ea17cb739f5047d5e0c Author: Rick Macklem <rmacklem@FreeBSD.org> AuthorDate: 2021-05-02 23:04:27 +0000 Commit: Rick Macklem <rmacklem@FreeBSD.org> CommitDate: 2021-05-16 23:48:06 +0000 copy_file_range(2): improve copying of a large hole to EOF PR#255523 reported that a file copy for a file with a large hole to EOF on ZFS ran slowly over NFSv4.2. The problem was that vn_generic_copy_file_range() would loop around reading the hole's data and then see it is all 0s. It was coded this way since UFS always allocates a data block near the end of the file, such that a hole to EOF never exists. This patch modifies vn_generic_copy_file_range() to check for a ENXIO returned from VOP_IOCTL(..FIOSEEKDATA..) and handle that case as a hole to EOF. asomers@ confirms that it works for his ZFS test case. PR: 255523 (cherry picked from commit 4f592683c356379c5bac56b52807ed4ad54ee647) --- sys/kern/vfs_vnops.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index 832c717a33b7..d4396f67a67b 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -3099,13 +3099,13 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp, struct vnode *outvp, off_t *outoffp, size_t *lenp, unsigned int flags, struct ucred *incred, struct ucred *outcred, struct thread *fsize_td) { - struct vattr va; + struct vattr va, inva; struct mount *mp; struct uio io; off_t startoff, endoff, xfer, xfer2; u_long blksize; int error, interrupted; - bool cantseek, readzeros, eof, lastblock; + bool cantseek, readzeros, eof, lastblock, holetoeof; ssize_t aresid; size_t copylen, len, rem, savlen; char *dat; @@ -3122,7 +3122,11 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp, goto out; if (VOP_PATHCONF(invp, _PC_MIN_HOLE_SIZE, &holein) != 0) holein = 0; + if (holein > 0) + error = VOP_GETATTR(invp, &inva, incred); VOP_UNLOCK(invp); + if (error != 0) + goto out; mp = NULL; error = vn_start_write(outvp, &mp, V_WAIT); @@ -3203,7 +3207,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp, * Note that some file systems such as NFSv3, NFSv4.0 and NFSv4.1 may * support holes on the server, but do not support FIOSEEKHOLE. */ - eof = false; + holetoeof = eof = false; while (len > 0 && error == 0 && !eof && interrupted == 0) { endoff = 0; /* To shut up compilers. */ cantseek = true; @@ -3212,8 +3216,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp, /* * Find the next data area. If there is just a hole to EOF, - * FIOSEEKDATA should fail and then we drop down into the - * inner loop and create the hole on the outvp file. + * FIOSEEKDATA should fail with ENXIO. * (I do not know if any file system will report a hole to * EOF via FIOSEEKHOLE, but I am pretty sure FIOSEEKDATA * will fail for those file systems.) @@ -3222,10 +3225,16 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp, * the code just falls through to the inner copy loop. */ error = EINVAL; - if (holein > 0) + if (holein > 0) { error = VOP_IOCTL(invp, FIOSEEKDATA, &startoff, 0, incred, curthread); - if (error == 0) { + if (error == ENXIO) { + startoff = endoff = inva.va_size; + eof = holetoeof = true; + error = 0; + } + } + if (error == 0 && !holetoeof) { endoff = startoff; error = VOP_IOCTL(invp, FIOSEEKHOLE, &endoff, 0, incred, curthread); @@ -3256,11 +3265,12 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp, } if (error == 0 && *outoffp + xfer > - va.va_size && xfer == len) - /* Grow last block. */ + va.va_size && (xfer == len || holetoeof)) { + /* Grow output file (hole at end). */ error = vn_write_outvp(outvp, dat, *outoffp, xfer, blksize, true, false, outcred); + } if (error == 0) { *inoffp += xfer; *outoffp += xfer;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202105162352.14GNqFGa043783>