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.new --- a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c +++ b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c.new @@ -6240,9 +6240,26 @@ struct vnode *outvp = ap->a_outvp; struct mount *mp; struct uio io; - int error; + int error, error2; uint64_t len = *ap->a_lenp; + zfsvfs_t *outzfsvfs; + objset_t *outos; + bool done_startwrite; + error2 = 0; + mp = NULL; + error = vn_start_write(outvp, &mp, V_WAIT); + done_startwrite = true; + if (error == 0 && mp == outvp->v_mount) { + outzfsvfs = (zfsvfs_t *)mp->mnt_data; + outos = outzfsvfs->z_os; + if (!spa_feature_is_enabled(dmu_ojset_spa(outos), + SPA_FEATURE_BLOCK_CLONING)) { + vn_finished_write(mp); + goto do_generic; + } + } + /* * TODO: If offset/length is not aligned to recordsize, use * vn_generic_copy_file_range() on this fragment. @@ -6252,8 +6269,10 @@ /* Lock both vnodes, avoiding risk of deadlock. */ do { - mp = NULL; - error = vn_start_write(outvp, &mp, V_WAIT); + if (!done_startwrite) { + mp = NULL; + error = vn_start_write(outvp, &mp, V_WAIT); + } if (error == 0) { error = vn_lock(outvp, LK_EXCLUSIVE); if (error == 0) { @@ -6273,6 +6292,7 @@ } if (mp != NULL) vn_finished_write(mp); + done_startwrite = false; } while (error == 0); if (error != 0) return (error); @@ -6289,7 +6309,7 @@ if (error != 0) goto unlock; - error = zfs_clone_range(VTOZ(invp), ap->a_inoffp, VTOZ(outvp), + error = error2 = zfs_clone_range(VTOZ(invp), ap->a_inoffp, VTOZ(outvp), ap->a_outoffp, &len, ap->a_outcred); *ap->a_lenp = (size_t)len; @@ -6299,6 +6319,17 @@ VOP_UNLOCK(outvp); if (mp != NULL) vn_finished_write(mp); + + /* + * Fall back if block_cloning is disabled + * or other EXDEV failures from zfs_vnops.c + */ + if (error2 == EXDEV) { +do_generic: + 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); }