git: 4f592683c356 - main - copy_file_range(2): improve copying of a large hole to EOF
Rick Macklem
rmacklem at FreeBSD.org
Sun May 2 23:08:50 UTC 2021
The branch main has been updated by rmacklem:
URL: https://cgit.FreeBSD.org/src/commit/?id=4f592683c356379c5bac56b52807ed4ad54ee647
commit 4f592683c356379c5bac56b52807ed4ad54ee647
Author: Rick Macklem <rmacklem at FreeBSD.org>
AuthorDate: 2021-05-02 23:04:27 +0000
Commit: Rick Macklem <rmacklem at FreeBSD.org>
CommitDate: 2021-05-02 23:04:27 +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
Tested by: asomers
Reviewed by: asomers
MFC after: 2 weeks
Differential Revision: https://reviews.freebsd.org/D30076
---
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;
More information about the dev-commits-src-all
mailing list