diff --git a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c --- a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c +++ b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c @@ -6242,6 +6242,30 @@ struct uio io; int error; uint64_t len = *ap->a_lenp; + zfsvfs_t *outzfsvfs; + objset_t *outos; + bool done_outvp; + + mp = NULL; + error = vn_start_write(outvp, &mp, V_WAIT); + if (error == 0) + error = vn_lock(outvp, LK_EXCLUSIVE); + done_outvp = true; + if (error == 0) { + outzfsvfs = ZTOZSB(VTOZ(outvp)); + outos = outzfsvfs->z_os; + if (!spa_feature_is_enabled(dmu_objset_spa(outos), + SPA_FEATURE_BLOCK_CLONING)) { + VOP_UNLOCK(outvp); + if (mp != NULL) + vn_finished_write(mp); + error = vn_generic_copy_file_range(ap->a_invp, + ap->a_inoffp, ap->a_outvp, ap->a_outoffp, + ap->a_lenp, ap->a_flags, ap->a_incred, + ap->a_outcred, ap->a_fsizetd); + return (error); + } + } /* * TODO: If offset/length is not aligned to recordsize, use @@ -6252,27 +6276,29 @@ /* Lock both vnodes, avoiding risk of deadlock. */ do { - mp = NULL; - error = vn_start_write(outvp, &mp, V_WAIT); + if (!done_outvp) { + mp = NULL; + error = vn_start_write(outvp, &mp, V_WAIT); + if (error == 0) + error = vn_lock(outvp, LK_EXCLUSIVE); + } if (error == 0) { - error = vn_lock(outvp, LK_EXCLUSIVE); - if (error == 0) { - if (invp == outvp) - break; - error = vn_lock(invp, LK_SHARED | LK_NOWAIT); - if (error == 0) - break; - VOP_UNLOCK(outvp); - if (mp != NULL) - vn_finished_write(mp); - mp = NULL; - error = vn_lock(invp, LK_SHARED); - if (error == 0) - VOP_UNLOCK(invp); - } + if (invp == outvp) + break; + error = vn_lock(invp, LK_SHARED | LK_NOWAIT); + if (error == 0) + break; + VOP_UNLOCK(outvp); + if (mp != NULL) + vn_finished_write(mp); + mp = NULL; + error = vn_lock(invp, LK_SHARED); + if (error == 0) + VOP_UNLOCK(invp); } if (mp != NULL) vn_finished_write(mp); + done_outvp = false; } while (error == 0); if (error != 0) return (error); @@ -6291,6 +6317,11 @@ error = zfs_clone_range(VTOZ(invp), ap->a_inoffp, VTOZ(outvp), ap->a_outoffp, &len, ap->a_outcred); + if (error == EXDEV) + error = vn_generic_copy_file_range(ap->a_invp, + ap->a_inoffp, ap->a_outvp, ap->a_outoffp, + ap->a_lenp, ap->a_flags, ap->a_incred, + ap->a_outcred, ap->a_fsizetd); *ap->a_lenp = (size_t)len; unlock: