The branch stable/13 has been updated by rmacklem:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=cd192185662ef90f52614ea17cb739f5047d5e0c

commit cd192185662ef90f52614ea17cb739f5047d5e0c
Author:     Rick Macklem <rmack...@freebsd.org>
AuthorDate: 2021-05-02 23:04:27 +0000
Commit:     Rick Macklem <rmack...@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;
_______________________________________________
dev-commits-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-all
To unsubscribe, send any mail to "dev-commits-src-all-unsubscr...@freebsd.org"

Reply via email to