From 3fc92adc409a36de229c78c7ca3d4689e9386bd3 Mon Sep 17 00:00:00 2001 From: Brian Behlendorf Date: Tue, 14 Feb 2023 11:04:34 -0800 Subject: [PATCH] Linux: use filemap_range_has_page() As of the 4.13 kernel filemap_range_has_page() can be used to check if there is a page mapped in a given file range. When available this interface should be used which eliminates the need for the zp->z_is_mapped boolean. Reviewed-by: Brian Atkinson Signed-off-by: Brian Behlendorf Closes #14493 --- config/kernel-filemap.m4 | 26 +++++++++++++++++++++ config/kernel.m4 | 2 ++ include/os/freebsd/zfs/sys/zfs_znode_impl.h | 3 ++- include/os/linux/zfs/sys/trace_acl.h | 9 +++---- include/os/linux/zfs/sys/zfs_znode_impl.h | 16 ++++++++++++- include/sys/zfs_znode.h | 1 - module/os/linux/zfs/zfs_ctldir.c | 2 ++ module/os/linux/zfs/zfs_vnops_os.c | 5 ++-- module/os/linux/zfs/zfs_znode.c | 4 +++- module/os/linux/zfs/zpl_file.c | 6 +++-- module/zfs/zfs_vnops.c | 8 ++++--- 11 files changed, 65 insertions(+), 17 deletions(-) create mode 100644 config/kernel-filemap.m4 diff --git a/config/kernel-filemap.m4 b/config/kernel-filemap.m4 new file mode 100644 index 000000000000..745928168f92 --- /dev/null +++ b/config/kernel-filemap.m4 @@ -0,0 +1,26 @@ +dnl # +dnl # filemap_range_has_page was not available till 4.13 +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_FILEMAP], [ + ZFS_LINUX_TEST_SRC([filemap_range_has_page], [ + #include + ],[ + struct address_space *mapping = NULL; + loff_t lstart = 0; + loff_t lend = 0; + bool ret __attribute__ ((unused)); + + ret = filemap_range_has_page(mapping, lstart, lend); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_FILEMAP], [ + AC_MSG_CHECKING([whether filemap_range_has_page() is available]) + ZFS_LINUX_TEST_RESULT([filemap_range_has_page], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_FILEMAP_RANGE_HAS_PAGE, 1, + [filemap_range_has_page() is available]) + ],[ + AC_MSG_RESULT(no) + ]) +]) diff --git a/config/kernel.m4 b/config/kernel.m4 index 353988e9c867..121d73ef641a 100644 --- a/config/kernel.m4 +++ b/config/kernel.m4 @@ -150,6 +150,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [ ZFS_AC_KERNEL_SRC_USER_NS_COMMON_INUM ZFS_AC_KERNEL_SRC_IDMAP_MNT_API ZFS_AC_KERNEL_SRC_IATTR_VFSID + ZFS_AC_KERNEL_SRC_FILEMAP AC_MSG_CHECKING([for available kernel interfaces]) ZFS_LINUX_TEST_COMPILE_ALL([kabi]) @@ -273,6 +274,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [ ZFS_AC_KERNEL_USER_NS_COMMON_INUM ZFS_AC_KERNEL_IDMAP_MNT_API ZFS_AC_KERNEL_IATTR_VFSID + ZFS_AC_KERNEL_FILEMAP ]) dnl # diff --git a/include/os/freebsd/zfs/sys/zfs_znode_impl.h b/include/os/freebsd/zfs/sys/zfs_znode_impl.h index 41a5bb218c12..8cde33dbcbbb 100644 --- a/include/os/freebsd/zfs/sys/zfs_znode_impl.h +++ b/include/os/freebsd/zfs/sys/zfs_znode_impl.h @@ -116,7 +116,8 @@ typedef struct zfs_soft_state { #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, start, end) \ + 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/trace_acl.h b/include/os/linux/zfs/sys/trace_acl.h index 2c734322267a..15dc77edafac 100644 --- a/include/os/linux/zfs/sys/trace_acl.h +++ b/include/os/linux/zfs/sys/trace_acl.h @@ -62,7 +62,6 @@ DECLARE_EVENT_CLASS(zfs_ace_class, __field(uint32_t, z_async_writes_cnt) __field(mode_t, z_mode) __field(boolean_t, z_is_sa) - __field(boolean_t, z_is_mapped) __field(boolean_t, z_is_ctldir) __field(uint32_t, i_uid) @@ -96,7 +95,6 @@ DECLARE_EVENT_CLASS(zfs_ace_class, __entry->z_async_writes_cnt = zn->z_async_writes_cnt; __entry->z_mode = zn->z_mode; __entry->z_is_sa = zn->z_is_sa; - __entry->z_is_mapped = zn->z_is_mapped; __entry->z_is_ctldir = zn->z_is_ctldir; __entry->i_uid = KUID_TO_SUID(ZTOI(zn)->i_uid); @@ -119,7 +117,7 @@ DECLARE_EVENT_CLASS(zfs_ace_class, "zn_prefetch %u blksz %u seq %u " "mapcnt %llu size %llu pflags %llu " "sync_cnt %u sync_writes_cnt %u async_writes_cnt %u " - "mode 0x%x is_sa %d is_mapped %d is_ctldir %d " + "mode 0x%x is_sa %d is_ctldir %d " "inode { uid %u gid %u ino %lu nlink %u size %lli " "blkbits %u bytes %u mode 0x%x generation %x } } " "ace { type %u flags %u access_mask %u } mask_matched %u", @@ -128,9 +126,8 @@ DECLARE_EVENT_CLASS(zfs_ace_class, __entry->z_seq, __entry->z_mapcnt, __entry->z_size, __entry->z_pflags, __entry->z_sync_cnt, __entry->z_sync_writes_cnt, __entry->z_async_writes_cnt, - __entry->z_mode, __entry->z_is_sa, __entry->z_is_mapped, - __entry->z_is_ctldir, __entry->i_uid, - __entry->i_gid, __entry->i_ino, __entry->i_nlink, + __entry->z_mode, __entry->z_is_sa, __entry->z_is_ctldir, + __entry->i_uid, __entry->i_gid, __entry->i_ino, __entry->i_nlink, __entry->i_size, __entry->i_blkbits, __entry->i_bytes, __entry->i_mode, __entry->i_generation, __entry->z_type, __entry->z_flags, __entry->z_access_mask, diff --git a/include/os/linux/zfs/sys/zfs_znode_impl.h b/include/os/linux/zfs/sys/zfs_znode_impl.h index 52568781011f..81607ef2a25e 100644 --- a/include/os/linux/zfs/sys/zfs_znode_impl.h +++ b/include/os/linux/zfs/sys/zfs_znode_impl.h @@ -47,9 +47,16 @@ extern "C" { #endif +#if defined(HAVE_FILEMAP_RANGE_HAS_PAGE) #define ZNODE_OS_FIELDS \ inode_timespec_t z_btime; /* creation/birth time (cached) */ \ struct inode z_inode; +#else +#define ZNODE_OS_FIELDS \ + inode_timespec_t z_btime; /* creation/birth time (cached) */ \ + struct inode z_inode; \ + boolean_t z_is_mapped; /* we are mmap'ed */ +#endif /* * Convert between znode pointers and inode pointers @@ -70,7 +77,14 @@ extern "C" { #define Z_ISDEV(type) (S_ISCHR(type) || S_ISBLK(type) || S_ISFIFO(type)) #define Z_ISDIR(type) S_ISDIR(type) -#define zn_has_cached_data(zp) ((zp)->z_is_mapped) +#if defined(HAVE_FILEMAP_RANGE_HAS_PAGE) +#define zn_has_cached_data(zp, start, end) \ + filemap_range_has_page(ZTOI(zp)->i_mapping, start, end) +#else +#define zn_has_cached_data(zp, start, end) \ + ((zp)->z_is_mapped) +#endif + #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/zfs_znode.h b/include/sys/zfs_znode.h index de38f56dc32d..fcee55b0199d 100644 --- a/include/sys/zfs_znode.h +++ b/include/sys/zfs_znode.h @@ -188,7 +188,6 @@ typedef struct znode { boolean_t z_atime_dirty; /* atime needs to be synced */ boolean_t z_zn_prefetch; /* Prefetch znodes? */ boolean_t z_is_sa; /* are we native sa? */ - boolean_t z_is_mapped; /* are we mmap'ed */ boolean_t z_is_ctldir; /* are we .zfs entry */ boolean_t z_suspended; /* extra ref from a suspend? */ uint_t z_blksz; /* block size in bytes */ diff --git a/module/os/linux/zfs/zfs_ctldir.c b/module/os/linux/zfs/zfs_ctldir.c index cc4bc9702f8e..dca48e1e4010 100644 --- a/module/os/linux/zfs/zfs_ctldir.c +++ b/module/os/linux/zfs/zfs_ctldir.c @@ -498,7 +498,9 @@ zfsctl_inode_alloc(zfsvfs_t *zfsvfs, uint64_t id, zp->z_atime_dirty = B_FALSE; zp->z_zn_prefetch = B_FALSE; zp->z_is_sa = B_FALSE; +#if !defined(HAVE_FILEMAP_RANGE_HAS_PAGE) zp->z_is_mapped = B_FALSE; +#endif zp->z_is_ctldir = B_TRUE; zp->z_sa_hdl = NULL; zp->z_blksz = 0; diff --git a/module/os/linux/zfs/zfs_vnops_os.c b/module/os/linux/zfs/zfs_vnops_os.c index 47f132a38abe..302a88c2d353 100644 --- a/module/os/linux/zfs/zfs_vnops_os.c +++ b/module/os/linux/zfs/zfs_vnops_os.c @@ -987,7 +987,7 @@ zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags) mutex_enter(&zp->z_lock); may_delete_now = atomic_read(&ZTOI(zp)->i_count) == 1 && - !(zp->z_is_mapped); + !zn_has_cached_data(zp, 0, LLONG_MAX); mutex_exit(&zp->z_lock); /* @@ -1075,7 +1075,8 @@ zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags) &xattr_obj_unlinked, sizeof (xattr_obj_unlinked)); delete_now = may_delete_now && !toobig && atomic_read(&ZTOI(zp)->i_count) == 1 && - !(zp->z_is_mapped) && xattr_obj == xattr_obj_unlinked && + !zn_has_cached_data(zp, 0, LLONG_MAX) && + xattr_obj == xattr_obj_unlinked && zfs_external_acl(zp) == acl_obj; } diff --git a/module/os/linux/zfs/zfs_znode.c b/module/os/linux/zfs/zfs_znode.c index 1faf25d93cc7..7b802a9bace0 100644 --- a/module/os/linux/zfs/zfs_znode.c +++ b/module/os/linux/zfs/zfs_znode.c @@ -551,7 +551,9 @@ zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, int blksz, ASSERT3P(zp->z_xattr_cached, ==, NULL); zp->z_unlinked = B_FALSE; zp->z_atime_dirty = B_FALSE; +#if !defined(HAVE_FILEMAP_RANGE_HAS_PAGE) zp->z_is_mapped = B_FALSE; +#endif zp->z_is_ctldir = B_FALSE; zp->z_suspended = B_FALSE; zp->z_sa_hdl = NULL; @@ -1641,7 +1643,7 @@ zfs_free_range(znode_t *zp, uint64_t off, uint64_t len) * Zero partial page cache entries. This must be done under a * range lock in order to keep the ARC and page cache in sync. */ - if (zp->z_is_mapped) { + if (zn_has_cached_data(zp, off, off + len - 1)) { loff_t first_page, last_page, page_len; loff_t first_page_offset, last_page_offset; diff --git a/module/os/linux/zfs/zpl_file.c b/module/os/linux/zfs/zpl_file.c index c56e3691e70a..e42b15042a3c 100644 --- a/module/os/linux/zfs/zpl_file.c +++ b/module/os/linux/zfs/zpl_file.c @@ -625,7 +625,6 @@ static int zpl_mmap(struct file *filp, struct vm_area_struct *vma) { struct inode *ip = filp->f_mapping->host; - znode_t *zp = ITOZ(ip); int error; fstrans_cookie_t cookie; @@ -640,9 +639,12 @@ zpl_mmap(struct file *filp, struct vm_area_struct *vma) if (error) return (error); +#if !defined(HAVE_FILEMAP_RANGE_HAS_PAGE) + znode_t *zp = ITOZ(ip); mutex_enter(&zp->z_lock); zp->z_is_mapped = B_TRUE; mutex_exit(&zp->z_lock); +#endif return (error); } @@ -937,7 +939,7 @@ zpl_fadvise(struct file *filp, loff_t offset, loff_t len, int advice) case POSIX_FADV_SEQUENTIAL: case POSIX_FADV_WILLNEED: #ifdef HAVE_GENERIC_FADVISE - if (zn_has_cached_data(zp)) + if (zn_has_cached_data(zp, offset, offset + len - 1)) error = generic_fadvise(filp, offset, len, advice); #endif /* diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c index 0c392b9da0fb..10677d8d9947 100644 --- a/module/zfs/zfs_vnops.c +++ b/module/zfs/zfs_vnops.c @@ -106,7 +106,7 @@ zfs_holey_common(znode_t *zp, ulong_t cmd, loff_t *off) hole = B_FALSE; /* Flush any mmap()'d data to disk */ - if (zn_has_cached_data(zp)) + if (zn_has_cached_data(zp, 0, file_sz - 1)) zn_flush_cached_data(zp, B_FALSE); lr = zfs_rangelock_enter(&zp->z_rangelock, 0, file_sz, RL_READER); @@ -288,7 +288,8 @@ zfs_read(struct znode *zp, zfs_uio_t *uio, int ioflag, cred_t *cr) error = mappedread_sf(zp, nbytes, uio); else #endif - if (zn_has_cached_data(zp) && !(ioflag & O_DIRECT)) { + if (zn_has_cached_data(zp, zfs_uio_offset(uio), + zfs_uio_offset(uio) + nbytes - 1) && !(ioflag & O_DIRECT)) { error = mappedread(zp, nbytes, uio); } else { error = dmu_read_uio_dbuf(sa_get_db(zp->z_sa_hdl), @@ -696,7 +697,8 @@ zfs_write(znode_t *zp, zfs_uio_t *uio, int ioflag, cred_t *cr) zfs_uioskip(uio, nbytes); tx_bytes = nbytes; } - if (tx_bytes && zn_has_cached_data(zp) && + if (tx_bytes && + zn_has_cached_data(zp, woff, woff + tx_bytes - 1) && !(ioflag & O_DIRECT)) { update_pages(zp, woff, tx_bytes, zfsvfs->z_os); }