diff options
author | Brian Behlendorf <[email protected]> | 2021-11-07 13:27:44 -0800 |
---|---|---|
committer | Tony Hutter <[email protected]> | 2021-11-05 08:08:55 -0700 |
commit | 664d487a5dbd758216ac613934a4080fcc1de347 (patch) | |
tree | ecd0b7b292537ca1525155439e4fc3433a199a0e /include | |
parent | 5bf81fea2f899520f2015d2982072f64b7428f71 (diff) |
Fix lseek(SEEK_DATA/SEEK_HOLE) mmap consistency
When using lseek(2) to report data/holes memory mapped regions of
the file were ignored. This could result in incorrect results.
To handle this zfs_holey_common() was updated to asynchronously
writeback any dirty mmap(2) regions prior to reporting holes.
Additionally, while not strictly required, the dn_struct_rwlock is
now held over the dirty check to prevent the dnode structure from
changing. This ensures that a clean dnode can't be dirtied before
the data/hole is located. The range lock is now also taken to
ensure the call cannot race with zfs_write().
Furthermore, the code was refactored to provide a dnode_is_dirty()
helper function which checks the dnode for any dirty records to
determine its dirtiness.
Reviewed-by: Matthew Ahrens <[email protected]>
Reviewed-by: Tony Hutter <[email protected]>
Reviewed-by: Rich Ercolani <[email protected]>
Signed-off-by: Brian Behlendorf <[email protected]>
Issue #11900
Closes #12724
Diffstat (limited to 'include')
-rw-r--r-- | include/os/freebsd/spl/sys/vnode.h | 18 | ||||
-rw-r--r-- | include/os/freebsd/zfs/sys/zfs_znode_impl.h | 3 | ||||
-rw-r--r-- | include/os/linux/zfs/sys/zfs_znode_impl.h | 1 | ||||
-rw-r--r-- | include/sys/dnode.h | 1 |
4 files changed, 22 insertions, 1 deletions
diff --git a/include/os/freebsd/spl/sys/vnode.h b/include/os/freebsd/spl/sys/vnode.h index 3670712a0..3bc8a18ee 100644 --- a/include/os/freebsd/spl/sys/vnode.h +++ b/include/os/freebsd/spl/sys/vnode.h @@ -59,6 +59,8 @@ enum symfollow { NO_FOLLOW = NOFOLLOW }; #include <sys/file.h> #include <sys/filedesc.h> #include <sys/syscallsubr.h> +#include <sys/vm.h> +#include <vm/vm_object.h> typedef struct vop_vector vnodeops_t; #define VOP_FID VOP_VPTOFH @@ -83,6 +85,22 @@ vn_is_readonly(vnode_t *vp) #define vn_has_cached_data(vp) \ ((vp)->v_object != NULL && \ (vp)->v_object->resident_page_count > 0) + +static __inline void +vn_flush_cached_data(vnode_t *vp, boolean_t sync) +{ +#if __FreeBSD_version > 1300054 + if (vm_object_mightbedirty(vp->v_object)) { +#else + if (vp->v_object->flags & OBJ_MIGHTBEDIRTY) { +#endif + int flags = sync ? OBJPC_SYNC : 0; + zfs_vmobject_wlock(vp->v_object); + vm_object_page_clean(vp->v_object, 0, 0, flags); + zfs_vmobject_wunlock(vp->v_object); + } +} + #define vn_exists(vp) do { } while (0) #define vn_invalid(vp) do { } while (0) #define vn_renamepath(tdvp, svp, tnm, lentnm) do { } while (0) diff --git a/include/os/freebsd/zfs/sys/zfs_znode_impl.h b/include/os/freebsd/zfs/sys/zfs_znode_impl.h index e90008c70..edb28d041 100644 --- a/include/os/freebsd/zfs/sys/zfs_znode_impl.h +++ b/include/os/freebsd/zfs/sys/zfs_znode_impl.h @@ -118,7 +118,8 @@ extern minor_t zfsdev_minor_alloc(void); #define Z_ISLNK(type) ((type) == VLNK) #define Z_ISDIR(type) ((type) == VDIR) -#define zn_has_cached_data(zp) vn_has_cached_data(ZTOV(zp)) +#define zn_has_cached_data(zp) vn_has_cached_data(ZTOV(zp)) +#define zn_flush_cached_data(zp, sync) vn_flush_cached_data(ZTOV(zp), sync) #define zn_rlimit_fsize(zp, uio) \ vn_rlimit_fsize(ZTOV(zp), GET_UIO_STRUCT(uio), zfs_uio_td(uio)) diff --git a/include/os/linux/zfs/sys/zfs_znode_impl.h b/include/os/linux/zfs/sys/zfs_znode_impl.h index 0a6273442..de46fc8f2 100644 --- a/include/os/linux/zfs/sys/zfs_znode_impl.h +++ b/include/os/linux/zfs/sys/zfs_znode_impl.h @@ -71,6 +71,7 @@ extern "C" { #define Z_ISDIR(type) S_ISDIR(type) #define zn_has_cached_data(zp) ((zp)->z_is_mapped) +#define zn_flush_cached_data(zp, sync) write_inode_now(ZTOI(zp), sync) #define zn_rlimit_fsize(zp, uio) (0) /* diff --git a/include/sys/dnode.h b/include/sys/dnode.h index 2cdc5b879..af8775b9e 100644 --- a/include/sys/dnode.h +++ b/include/sys/dnode.h @@ -425,6 +425,7 @@ boolean_t dnode_add_ref(dnode_t *dn, void *ref); void dnode_rele(dnode_t *dn, void *ref); void dnode_rele_and_unlock(dnode_t *dn, void *tag, boolean_t evicting); int dnode_try_claim(objset_t *os, uint64_t object, int slots); +boolean_t dnode_is_dirty(dnode_t *dn); void dnode_setdirty(dnode_t *dn, dmu_tx_t *tx); void dnode_set_dirtyctx(dnode_t *dn, dmu_tx_t *tx, void *tag); void dnode_sync(dnode_t *dn, dmu_tx_t *tx); |