diff --git a/kmod/Makefile b/kmod/Makefile index 58963acf..9a210509 100644 --- a/kmod/Makefile +++ b/kmod/Makefile @@ -31,12 +31,12 @@ TARFILE = scoutfs-kmod-$(RPM_VERSION).tar all: module module: - make $(SCOUTFS_ARGS) - $(SP) make C=2 CF="-D__CHECK_ENDIAN__" $(SCOUTFS_ARGS) + $(MAKE) $(SCOUTFS_ARGS) + $(SP) $(MAKE) C=2 CF="-D__CHECK_ENDIAN__" $(SCOUTFS_ARGS) modules_install: - make $(SCOUTFS_ARGS) modules_install + $(MAKE) $(SCOUTFS_ARGS) modules_install %.spec: %.spec.in .FORCE @@ -50,4 +50,4 @@ dist: scoutfs-kmod.spec @ tar rf $(TARFILE) --transform="s@\(.*\)@scoutfs-kmod-$(RPM_VERSION)/\1@" scoutfs-kmod.spec clean: - make $(SCOUTFS_ARGS) clean + $(MAKE) $(SCOUTFS_ARGS) clean diff --git a/kmod/scoutfs-kmod.spec.in b/kmod/scoutfs-kmod.spec.in index ee1acf54..e9cfc2dc 100644 --- a/kmod/scoutfs-kmod.spec.in +++ b/kmod/scoutfs-kmod.spec.in @@ -3,16 +3,28 @@ %define kmod_git_hash @@GITHASH@@ %define pkg_date %(date +%%Y%%m%%d) +# Disable the building of the debug package(s). +%define debug_package %{nil} + # take kernel version or default to uname -r %{!?kversion: %global kversion %(uname -r)} %global kernel_version %{kversion} +%if 0%{?el7} %global kernel_source() /usr/src/kernels/%{kernel_version}.$(arch) -%global kernel_release() %{kversion} +%endif +%if 0%{?el8} +%global kernel_source() /usr/src/kernels/%{kernel_version} +%endif %{!?_release: %global _release 0.%{pkg_date}git%{kmod_git_hash}} +%if 0%{?el7} Name: %{kmod_name} +%endif +%if 0%{?el8} +Name: kmod-%{kmod_name} +%endif Summary: %{kmod_name} kernel module Version: %{kmod_version} Release: %{_release}%{?dist} @@ -20,24 +32,30 @@ License: GPLv2 Group: System/Kernel URL: http://scoutfs.org/ +%if 0%{?el7} BuildRequires: %{kernel_module_package_buildreqs} -BuildRequires: git +%endif +%if 0%{?el8} +BuildRequires: elfutils-libelf-devel +%endif BuildRequires: kernel-devel-uname-r = %{kernel_version} +BuildRequires: git BuildRequires: module-init-tools ExclusiveArch: x86_64 Source: %{kmod_name}-kmod-%{kmod_version}.tar +%if 0%{?el7} # Build only for standard kernel variant(s); for debug packages, append "debug" # after "default" (separated by space) %kernel_module_package default +%endif -# Disable the building of the debug package(s). -%define debug_package %{nil} - -%global install_mod_dir extra/%{name} - +%global install_mod_dir extra/%{kmod_name} +%if 0%{?el8} +%global flavors_to_build x86_64 +%endif %description %{kmod_name} - kernel module @@ -66,7 +84,7 @@ export INSTALL_MOD_DIR=%{install_mod_dir} mkdir -p %{install_mod_dir} for flavor in %{flavors_to_build}; do export KSRC=%{kernel_source $flavor} - export KVERSION=%{kernel_release $KSRC} + export KVERSION=%{kversion} install -d $INSTALL_MOD_PATH/lib/modules/$KVERSION/%{install_mod_dir} cp $PWD/obj/$flavor/src/scoutfs.ko $INSTALL_MOD_PATH/lib/modules/$KVERSION/%{install_mod_dir}/ done @@ -74,6 +92,14 @@ done # mark modules executable so that strip-to-file can strip them find %{buildroot} -type f -name \*.ko -exec %{__chmod} u+x \{\} \; +%if 0%{?el8} +%files +/lib/modules + +%post +weak-modules --add-kernel --no-initramfs +depmod -a +%endif %clean rm -rf %{buildroot} diff --git a/kmod/src/Makefile b/kmod/src/Makefile index d96c4967..2666e6df 100644 --- a/kmod/src/Makefile +++ b/kmod/src/Makefile @@ -25,6 +25,7 @@ scoutfs-y += \ inode.o \ ioctl.o \ item.o \ + kernelcompat.o \ lock.o \ lock_server.o \ msg.o \ diff --git a/kmod/src/Makefile.kernelcompat b/kmod/src/Makefile.kernelcompat index d7995cc5..352f2671 100644 --- a/kmod/src/Makefile.kernelcompat +++ b/kmod/src/Makefile.kernelcompat @@ -26,6 +26,16 @@ ifneq (,$(shell grep 'dir_emit_dots' include/linux/fs.h)) ccflags-y += -DKC_DIR_EMIT_DOTS endif +# +# v3.18-rc2-19-gb5ae6b15bd73 +# +# Folds d_materialise_unique into d_splice_alias. Note reversal +# of arguments (Also note Documentation/filesystems/porting.rst) +# +ifneq (,$(shell grep 'd_materialise_unique' include/linux/dcache.h)) +ccflags-y += -DKC_D_MATERIALISE_UNIQUE=1 +endif + # # RHEL extended the fop struct so to use it we have to set # a flag to indicate that the struct is large enough and @@ -40,6 +50,211 @@ endif # # Added user_ns argument to posix_acl_valid # -ifneq (,$(shell grep 'posix_acl_valid.*user_ns,' include/linux/posix_acl.h)) +ifneq (,$(shell grep 'posix_acl_valid.*user_namespace' include/linux/posix_acl.h)) ccflags-y += -DKC_POSIX_ACL_VALID_USER_NS endif + +# +# v5.3-12296-g6d2052d188d9 +# +# The RBCOMPUTE function is now passed an extra flag, and should return a bool +# to indicate whether the propagated callback should stop or not. +# +ifneq (,$(shell grep 'static inline bool RBNAME.*_compute_max' include/linux/rbtree_augmented.h)) +ccflags-y += -DKC_RB_TREE_AUGMENTED_COMPUTE_MAX +endif + +# +# v3.13-25-g37bc15392a23 +# +# Renames posix_acl_create to __posix_acl_create and provide some +# new interfaces for creating ACLs +# +ifneq (,$(shell grep '__posix_acl_create' include/linux/posix_acl.h)) +ccflags-y += -DKC___POSIX_ACL_CREATE +endif + +# +# v4.8-rc1-29-g31051c85b5e2 +# +# inode_change_ok() removed - replace with setattr_prepare() +# +ifneq (,$(shell grep 'extern int setattr_prepare' include/linux/fs.h)) +ccflags-y += -DKC_SETATTR_PREPARE +endif + +# +# v4.15-rc3-4-gae5e165d855d +# +# linux/iversion.h needs to manually be included for code that +# manipulates this field. +# +ifneq (,$(shell grep -s 'define _LINUX_IVERSION_H' include/linux/iversion.h)) +ccflags-y += -DKC_NEED_LINUX_IVERSION_H=1 +endif + +# v4.11-12447-g104b4e5139fe +# +# Renamed __percpu_counter_add to percpu_counter_add_batch to clarify +# that the __ wasn't less safe, just took an extra parameter. +# +ifneq (,$(shell grep 'percpu_counter_add_batch' include/linux/percpu_counter.h)) +ccflags-y += -DKC_PERCPU_COUNTER_ADD_BATCH +endif + +# +# v4.11-4550-g7dea19f9ee63 +# +# Introduced memalloc_nofs_{save,restore} preferred instead of _noio_. +# +ifneq (,$(shell grep 'memalloc_nofs_save' include/linux/sched/mm.h)) +ccflags-y += -DKC_MEMALLOC_NOFS_SAVE +endif + +# +# v4.7-12414-g1eff9d322a44 +# +# Renamed bi_rw to bi_opf to force old code to catch up. We use it as a +# single switch between old and new bio structures. +# +ifneq (,$(shell grep 'bi_opf' include/linux/blk_types.h)) +ccflags-y += -DKC_BIO_BI_OPF +endif + +# +# v4.12-rc2-201-g4e4cbee93d56 +# +# Moves to bi_status BLK_STS_ API instead of having a mix of error +# end_io args or bi_error. +# +ifneq (,$(shell grep 'bi_status' include/linux/blk_types.h)) +ccflags-y += -DKC_BIO_BI_STATUS +endif + +# +# v3.11-8765-ga0b02131c5fc +# +# Remove the old ->shrink() API, ->{scan,count}_objects is preferred. +# +ifneq (,$(shell grep '(*shrink)' include/linux/shrinker.h)) +ccflags-y += -DKC_SHRINKER_SHRINK +endif + +# +# v3.19-4777-g6bec00352861 +# +# backing_dev_info is removed from address_space. Instead we need to use +# inode_to_bdi() inline from . +# +ifneq (,$(shell grep 'struct backing_dev_info.*backing_dev_info' include/linux/fs.h)) +ccflags-y += -DKC_LINUX_BACKING_DEV_INFO=1 +endif + +# +# v4.3-9290-ge409de992e3e +# +# xattr handlers are now passed a struct that contains `flags` +# +ifneq (,$(shell grep 'int...get..const struct xattr_handler.*struct dentry.*dentry,' include/linux/xattr.h)) +ccflags-y += -DKC_XATTR_STRUCT_XATTR_HANDLER=1 +endif + +# +# v4.16-rc1-1-g9b2c45d479d0 +# +# kernel_getsockname() and kernel_getpeername dropped addrlen arg +# +ifneq (,$(shell grep 'kernel_getsockname.*,$$' include/linux/net.h)) +ccflags-y += -DKC_KERNEL_GETSOCKNAME_ADDRLEN=1 +endif + +# +# v4.1-rc1-410-geeb1bd5c40ed +# +# Adds a struct net parameter to sock_create_kern +# +ifneq (,$(shell grep 'sock_create_kern.*struct net' include/linux/net.h)) +ccflags-y += -DKC_SOCK_CREATE_KERN_NET=1 +endif + +# +# v3.18-rc6-1619-gc0371da6047a +# +# iov_iter is now part of struct msghdr +# +ifneq (,$(shell grep 'struct iov_iter.*msg_iter' include/linux/socket.h)) +ccflags-y += -DKC_MSGHDR_STRUCT_IOV_ITER=1 +endif + +# +# v4.17-rc6-7-g95582b008388 +# +# Kernel has current_time(inode) to uniformly retreive timespec in the right unit +# +ifneq (,$(shell grep 'extern struct timespec64 current_time' include/linux/fs.h)) +ccflags-y += -DKC_CURRENT_TIME_INODE=1 +endif + +# +# v4.9-12228-g530e9b76ae8f +# +# register_cpu_notifier and family were all removed and to be +# replaced with cpuhp_* API calls. +# +ifneq (,$(shell grep 'define register_hotcpu_notifier' include/linux/cpu.h)) +ccflags-y += -DKC_CPU_NOTIFIER +endif + +# +# v3.14-rc8-130-gccad2365668f +# +# generic_file_buffered_write is removed, backport it +# +ifneq (,$(shell grep 'extern ssize_t generic_file_buffered_write' include/linux/fs.h)) +ccflags-y += -DKC_GENERIC_FILE_BUFFERED_WRITE=1 +endif + +# +# v5.7-438-g8151b4c8bee4 +# +# struct address_space_operations switches away from .readpages to .readahead +# +# RHEL has backported this feature all the way to RHEL8, as part of RHEL_KABI, +# which means we need to detect this very precisely +# +ifneq (,$(shell grep 'readahead.*struct readahead_control' include/linux/fs.h)) +ccflags-y += -DKC_FILE_AOPS_READAHEAD +endif + +# +# v4.0-rc7-1743-g8436318205b9 +# +# .aio_read and .aio_write no longer exist. All reads and writes now use the +# .read_iter and .write_iter methods, or must implement .read and .write (which +# we don't). +# +ifneq (,$(shell grep 'ssize_t.*aio_read' include/linux/fs.h)) +ccflags-y += -DKC_LINUX_HAVE_FOP_AIO_READ=1 +endif + +# +# rhel7 has a custom inode_operations_wrapper struct that is discarded +# entirely in favor of upstream structure since rhel8. +# +ifneq (,$(shell grep 'void.*follow_link.*struct dentry' include/linux/fs.h)) +ccflags-y += -DKC_LINUX_HAVE_RHEL_IOPS_WRAPPER=1 +endif + +ifneq (,$(shell grep 'size_t.*ki_left;' include/linux/aio.h)) +ccflags-y += -DKC_LINUX_AIO_KI_LEFT=1 +endif + +# +# v4.4-rc4-4-g98e9cb5711c6 +# +# Introduces a new xattr_handler .name member that can be used to match the +# entire field, instead of just a prefix. For these kernels, we must use +# the new .name field instead. +ifneq (,$(shell grep 'static inline const char .xattr_prefix' include/linux/xattr.h)) +ccflags-y += -DKC_XATTR_HANDLER_NAME=1 +endif diff --git a/kmod/src/acl.c b/kmod/src/acl.c index 11a83266..bfe81bc5 100644 --- a/kmod/src/acl.c +++ b/kmod/src/acl.c @@ -69,12 +69,14 @@ struct posix_acl *scoutfs_get_acl_locked(struct inode *inode, int type, struct s char *name; int ret; +#ifndef KC___POSIX_ACL_CREATE if (!IS_POSIXACL(inode)) return NULL; acl = get_cached_acl(inode, type); if (acl != ACL_NOT_CACHED) return acl; +#endif ret = acl_xattr_name_len(type, &name, NULL); if (ret < 0) @@ -96,9 +98,11 @@ struct posix_acl *scoutfs_get_acl_locked(struct inode *inode, int type, struct s acl = ERR_PTR(ret); } +#ifndef KC___POSIX_ACL_CREATE /* can set null negative cache */ if (!IS_ERR(acl)) set_cached_acl(inode, type, acl); +#endif kfree(value); @@ -112,8 +116,10 @@ struct posix_acl *scoutfs_get_acl(struct inode *inode, int type) struct posix_acl *acl; int ret; +#ifndef KC___POSIX_ACL_CREATE if (!IS_POSIXACL(inode)) return NULL; +#endif ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, 0, inode, &lock); if (ret < 0) { @@ -183,13 +189,15 @@ int scoutfs_set_acl_locked(struct inode *inode, struct posix_acl *acl, int type, if (!value) { /* can be setting an acl that only affects mode, didn't need xattr */ inode_inc_iversion(inode); - inode->i_ctime = CURRENT_TIME; + inode->i_ctime = current_time(inode); } } out: +#ifndef KC___POSIX_ACL_CREATE if (!ret) set_cached_acl(inode, type, acl); +#endif kfree(value); @@ -218,10 +226,17 @@ int scoutfs_set_acl(struct inode *inode, struct posix_acl *acl, int type) scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE); return ret; } - +#ifdef KC_XATTR_STRUCT_XATTR_HANDLER +int scoutfs_acl_get_xattr(const struct xattr_handler *handler, struct dentry *dentry, + struct inode *inode, const char *name, void *value, + size_t size) +{ + int type = handler->flags; +#else int scoutfs_acl_get_xattr(struct dentry *dentry, const char *name, void *value, size_t size, int type) { +#endif struct posix_acl *acl; int ret = 0; @@ -240,9 +255,17 @@ int scoutfs_acl_get_xattr(struct dentry *dentry, const char *name, void *value, return ret; } +#ifdef KC_XATTR_STRUCT_XATTR_HANDLER +int scoutfs_acl_set_xattr(const struct xattr_handler *handler, struct dentry *dentry, + struct inode *inode, const char *name, const void *value, + size_t size, int flags) +{ + int type = handler->flags; +#else int scoutfs_acl_set_xattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags, int type) { +#endif struct posix_acl *acl = NULL; int ret; @@ -301,7 +324,7 @@ int scoutfs_init_acl_locked(struct inode *inode, struct inode *dir, if (ret) goto out; } - ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); + ret = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); if (ret < 0) return ret; if (ret > 0) @@ -345,7 +368,7 @@ int scoutfs_acl_chmod_locked(struct inode *inode, struct iattr *attr, if (IS_ERR_OR_NULL(acl)) return PTR_ERR(acl); - ret = posix_acl_chmod(&acl, GFP_KERNEL, attr->ia_mode); + ret = __posix_acl_chmod(&acl, GFP_KERNEL, attr->ia_mode); if (ret) return ret; diff --git a/kmod/src/acl.h b/kmod/src/acl.h index a9235eb5..1712b7e8 100644 --- a/kmod/src/acl.h +++ b/kmod/src/acl.h @@ -6,10 +6,19 @@ struct posix_acl *scoutfs_get_acl_locked(struct inode *inode, int type, struct s int scoutfs_set_acl(struct inode *inode, struct posix_acl *acl, int type); int scoutfs_set_acl_locked(struct inode *inode, struct posix_acl *acl, int type, struct scoutfs_lock *lock, struct list_head *ind_locks); +#ifdef KC_XATTR_STRUCT_XATTR_HANDLER +int scoutfs_acl_get_xattr(const struct xattr_handler *, struct dentry *dentry, + struct inode *inode, const char *name, void *value, + size_t size); +int scoutfs_acl_set_xattr(const struct xattr_handler *, struct dentry *dentry, + struct inode *inode, const char *name, const void *value, + size_t size, int flags); +#else int scoutfs_acl_get_xattr(struct dentry *dentry, const char *name, void *value, size_t size, int type); int scoutfs_acl_set_xattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags, int type); +#endif int scoutfs_acl_chmod_locked(struct inode *inode, struct iattr *attr, struct scoutfs_lock *lock, struct list_head *ind_locks); int scoutfs_init_acl_locked(struct inode *inode, struct inode *dir, diff --git a/kmod/src/block.c b/kmod/src/block.c index 89cda9c0..c23764d3 100644 --- a/kmod/src/block.c +++ b/kmod/src/block.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "format.h" #include "super.h" @@ -30,6 +31,7 @@ #include "scoutfs_trace.h" #include "alloc.h" #include "triggers.h" +#include "util.h" /* * The scoutfs block cache manages metadata blocks that can be larger @@ -57,7 +59,7 @@ struct block_info { atomic64_t access_counter; struct rhashtable ht; wait_queue_head_t waitq; - struct shrinker shrinker; + KC_DEFINE_SHRINKER(shrinker); struct work_struct free_work; struct llist_head free_llist; }; @@ -128,7 +130,7 @@ static __le32 block_calc_crc(struct scoutfs_block_header *hdr, u32 size) static struct block_private *block_alloc(struct super_block *sb, u64 blkno) { struct block_private *bp; - unsigned int noio_flags; + unsigned int nofs_flags; /* * If we had multiple blocks per page we'd need to be a little @@ -156,9 +158,9 @@ static struct block_private *block_alloc(struct super_block *sb, u64 blkno) * spurious reclaim-on dependencies and warnings. */ lockdep_off(); - noio_flags = memalloc_noio_save(); + nofs_flags = memalloc_nofs_save(); bp->virt = __vmalloc(SCOUTFS_BLOCK_LG_SIZE, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL); - memalloc_noio_restore(noio_flags); + memalloc_nofs_restore(nofs_flags); lockdep_on(); if (!bp->virt) { @@ -436,11 +438,10 @@ static void block_remove_all(struct super_block *sb) * possible. Final freeing, verifying checksums, and unlinking errored * blocks are all done by future users of the blocks. */ -static void block_end_io(struct super_block *sb, int rw, +static void block_end_io(struct super_block *sb, unsigned int opf, struct block_private *bp, int err) { DECLARE_BLOCK_INFO(sb, binf); - bool is_read = !(rw & WRITE); if (err) { scoutfs_inc_counter(sb, block_cache_end_io_error); @@ -450,7 +451,7 @@ static void block_end_io(struct super_block *sb, int rw, if (!atomic_dec_and_test(&bp->io_count)) return; - if (is_read && !test_bit(BLOCK_BIT_ERROR, &bp->bits)) + if (!op_is_write(opf) && !test_bit(BLOCK_BIT_ERROR, &bp->bits)) set_bit(BLOCK_BIT_UPTODATE, &bp->bits); clear_bit(BLOCK_BIT_IO_BUSY, &bp->bits); @@ -463,13 +464,13 @@ static void block_end_io(struct super_block *sb, int rw, wake_up(&binf->waitq); } -static void block_bio_end_io(struct bio *bio, int err) +static void KC_DECLARE_BIO_END_IO(block_bio_end_io, struct bio *bio) { struct block_private *bp = bio->bi_private; struct super_block *sb = bp->sb; TRACE_BLOCK(end_io, bp); - block_end_io(sb, bio->bi_rw, bp, err); + block_end_io(sb, kc_bio_get_opf(bio), bp, kc_bio_get_errno(bio)); bio_put(bio); } @@ -477,7 +478,7 @@ static void block_bio_end_io(struct bio *bio, int err) * Kick off IO for a single block. */ static int block_submit_bio(struct super_block *sb, struct block_private *bp, - int rw) + unsigned int opf) { struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb); struct bio *bio = NULL; @@ -510,8 +511,9 @@ static int block_submit_bio(struct super_block *sb, struct block_private *bp, break; } - bio->bi_sector = sector + (off >> 9); - bio->bi_bdev = sbi->meta_bdev; + kc_bio_set_opf(bio, opf); + kc_bio_set_sector(bio, sector + (off >> 9)); + bio_set_dev(bio, sbi->meta_bdev); bio->bi_end_io = block_bio_end_io; bio->bi_private = bp; @@ -528,18 +530,18 @@ static int block_submit_bio(struct super_block *sb, struct block_private *bp, BUG(); if (!bio_add_page(bio, page, PAGE_SIZE, 0)) { - submit_bio(rw, bio); + kc_submit_bio(bio); bio = NULL; } } if (bio) - submit_bio(rw, bio); + kc_submit_bio(bio); blk_finish_plug(&plug); /* let racing end_io know we're done */ - block_end_io(sb, rw, bp, ret); + block_end_io(sb, opf, bp, ret); return ret; } @@ -640,7 +642,7 @@ static struct block_private *block_read(struct super_block *sb, u64 blkno) if (!test_bit(BLOCK_BIT_UPTODATE, &bp->bits) && test_and_clear_bit(BLOCK_BIT_NEW, &bp->bits)) { - ret = block_submit_bio(sb, bp, READ); + ret = block_submit_bio(sb, bp, REQ_OP_READ); if (ret < 0) goto out; } @@ -969,7 +971,7 @@ int scoutfs_block_writer_write(struct super_block *sb, /* retry previous write errors */ clear_bit(BLOCK_BIT_ERROR, &bp->bits); - ret = block_submit_bio(sb, bp, WRITE); + ret = block_submit_bio(sb, bp, REQ_OP_WRITE); if (ret < 0) break; } @@ -1069,6 +1071,16 @@ u64 scoutfs_block_writer_dirty_bytes(struct super_block *sb, return wri->nr_dirty_blocks * SCOUTFS_BLOCK_LG_SIZE; } +static unsigned long block_count_objects(struct shrinker *shrink, struct shrink_control *sc) +{ + struct block_info *binf = KC_SHRINKER_CONTAINER_OF(shrink, struct block_info); + struct super_block *sb = binf->sb; + + scoutfs_inc_counter(sb, block_cache_count_objects); + + return shrinker_min_long(atomic_read(&binf->total_inserted)); +} + /* * Remove a number of cached blocks that haven't been used recently. * @@ -1089,24 +1101,18 @@ u64 scoutfs_block_writer_dirty_bytes(struct super_block *sb, * atomically remove blocks when the only references are ours and the * hash table. */ -static int block_shrink(struct shrinker *shrink, struct shrink_control *sc) +static unsigned long block_scan_objects(struct shrinker *shrink, struct shrink_control *sc) { - struct block_info *binf = container_of(shrink, struct block_info, - shrinker); + struct block_info *binf = KC_SHRINKER_CONTAINER_OF(shrink, struct block_info); struct super_block *sb = binf->sb; struct rhashtable_iter iter; struct block_private *bp; bool stop = false; - unsigned long nr; + unsigned long freed = 0; + unsigned long nr = sc->nr_to_scan; u64 recently; - nr = sc->nr_to_scan; - if (nr == 0) - goto out; - - scoutfs_inc_counter(sb, block_cache_shrink); - - nr = DIV_ROUND_UP(nr, SCOUTFS_BLOCK_LG_PAGES_PER); + scoutfs_inc_counter(sb, block_cache_scan_objects); recently = accessed_recently(binf); rhashtable_walk_enter(&binf->ht, &iter); @@ -1151,6 +1157,7 @@ static int block_shrink(struct shrinker *shrink, struct shrink_control *sc) if (block_remove_solo(sb, bp)) { scoutfs_inc_counter(sb, block_cache_shrink_remove); TRACE_BLOCK(shrink, bp); + freed++; nr--; } block_put(sb, bp); @@ -1159,12 +1166,11 @@ static int block_shrink(struct shrinker *shrink, struct shrink_control *sc) rhashtable_walk_stop(&iter); rhashtable_walk_exit(&iter); -out: + if (stop) - return -1; + return SHRINK_STOP; else - return min_t(u64, INT_MAX, - (u64)atomic_read(&binf->total_inserted) * SCOUTFS_BLOCK_LG_PAGES_PER); + return freed; } struct sm_block_completion { @@ -1172,11 +1178,11 @@ struct sm_block_completion { int err; }; -static void sm_block_bio_end_io(struct bio *bio, int err) +static void KC_DECLARE_BIO_END_IO(sm_block_bio_end_io, struct bio *bio) { struct sm_block_completion *sbc = bio->bi_private; - sbc->err = err; + sbc->err = kc_bio_get_errno(bio); complete(&sbc->comp); bio_put(bio); } @@ -1191,9 +1197,8 @@ static void sm_block_bio_end_io(struct bio *bio, int err) * only layer that sees the full block buffer so we pass the calculated * crc to the caller for them to check in their context. */ -static int sm_block_io(struct super_block *sb, struct block_device *bdev, int rw, u64 blkno, - struct scoutfs_block_header *hdr, size_t len, - __le32 *blk_crc) +static int sm_block_io(struct super_block *sb, struct block_device *bdev, unsigned int opf, + u64 blkno, struct scoutfs_block_header *hdr, size_t len, __le32 *blk_crc) { struct scoutfs_block_header *pg_hdr; struct sm_block_completion sbc; @@ -1207,7 +1212,7 @@ static int sm_block_io(struct super_block *sb, struct block_device *bdev, int rw return -EIO; if (WARN_ON_ONCE(len > SCOUTFS_BLOCK_SM_SIZE) || - WARN_ON_ONCE(!(rw & WRITE) && !blk_crc)) + WARN_ON_ONCE(!op_is_write(opf) && !blk_crc)) return -EINVAL; page = alloc_page(GFP_NOFS); @@ -1216,7 +1221,7 @@ static int sm_block_io(struct super_block *sb, struct block_device *bdev, int rw pg_hdr = page_address(page); - if (rw & WRITE) { + if (op_is_write(opf)) { memcpy(pg_hdr, hdr, len); if (len < SCOUTFS_BLOCK_SM_SIZE) memset((char *)pg_hdr + len, 0, @@ -1230,8 +1235,9 @@ static int sm_block_io(struct super_block *sb, struct block_device *bdev, int rw goto out; } - bio->bi_sector = blkno << (SCOUTFS_BLOCK_SM_SHIFT - 9); - bio->bi_bdev = bdev; + kc_bio_set_opf(bio, opf | REQ_SYNC); + kc_bio_set_sector(bio, blkno << (SCOUTFS_BLOCK_SM_SHIFT - 9)); + bio_set_dev(bio, bdev); bio->bi_end_io = sm_block_bio_end_io; bio->bi_private = &sbc; bio_add_page(bio, page, SCOUTFS_BLOCK_SM_SIZE, 0); @@ -1239,12 +1245,12 @@ static int sm_block_io(struct super_block *sb, struct block_device *bdev, int rw init_completion(&sbc.comp); sbc.err = 0; - submit_bio((rw & WRITE) ? WRITE_SYNC : READ_SYNC, bio); + kc_submit_bio(bio); wait_for_completion(&sbc.comp); ret = sbc.err; - if (ret == 0 && !(rw & WRITE)) { + if (ret == 0 && !op_is_write(opf)) { memcpy(hdr, pg_hdr, len); *blk_crc = block_calc_crc(pg_hdr, SCOUTFS_BLOCK_SM_SIZE); } @@ -1258,14 +1264,14 @@ int scoutfs_block_read_sm(struct super_block *sb, struct scoutfs_block_header *hdr, size_t len, __le32 *blk_crc) { - return sm_block_io(sb, bdev, READ, blkno, hdr, len, blk_crc); + return sm_block_io(sb, bdev, REQ_OP_READ, blkno, hdr, len, blk_crc); } int scoutfs_block_write_sm(struct super_block *sb, struct block_device *bdev, u64 blkno, struct scoutfs_block_header *hdr, size_t len) { - return sm_block_io(sb, bdev, WRITE, blkno, hdr, len, NULL); + return sm_block_io(sb, bdev, REQ_OP_WRITE, blkno, hdr, len, NULL); } int scoutfs_block_setup(struct super_block *sb) @@ -1290,9 +1296,9 @@ int scoutfs_block_setup(struct super_block *sb) atomic_set(&binf->total_inserted, 0); atomic64_set(&binf->access_counter, 0); init_waitqueue_head(&binf->waitq); - binf->shrinker.shrink = block_shrink; - binf->shrinker.seeks = DEFAULT_SEEKS; - register_shrinker(&binf->shrinker); + KC_INIT_SHRINKER_FUNCS(&binf->shrinker, block_count_objects, + block_scan_objects); + KC_REGISTER_SHRINKER(&binf->shrinker); INIT_WORK(&binf->free_work, block_free_work); init_llist_head(&binf->free_llist); @@ -1312,7 +1318,7 @@ void scoutfs_block_destroy(struct super_block *sb) struct block_info *binf = SCOUTFS_SB(sb)->block_info; if (binf) { - unregister_shrinker(&binf->shrinker); + KC_UNREGISTER_SHRINKER(&binf->shrinker); block_remove_all(sb); flush_work(&binf->free_work); rhashtable_destroy(&binf->ht); diff --git a/kmod/src/counters.h b/kmod/src/counters.h index e681e07a..dd291816 100644 --- a/kmod/src/counters.h +++ b/kmod/src/counters.h @@ -30,6 +30,8 @@ EXPAND_COUNTER(block_cache_free) \ EXPAND_COUNTER(block_cache_free_work) \ EXPAND_COUNTER(block_cache_remove_stale) \ + EXPAND_COUNTER(block_cache_count_objects) \ + EXPAND_COUNTER(block_cache_scan_objects) \ EXPAND_COUNTER(block_cache_shrink) \ EXPAND_COUNTER(block_cache_shrink_next) \ EXPAND_COUNTER(block_cache_shrink_recent) \ @@ -88,6 +90,8 @@ EXPAND_COUNTER(forest_read_items) \ EXPAND_COUNTER(forest_roots_next_hint) \ EXPAND_COUNTER(forest_set_bloom_bits) \ + EXPAND_COUNTER(item_cache_count_objects) \ + EXPAND_COUNTER(item_cache_scan_objects) \ EXPAND_COUNTER(item_clear_dirty) \ EXPAND_COUNTER(item_create) \ EXPAND_COUNTER(item_delete) \ @@ -121,6 +125,7 @@ EXPAND_COUNTER(item_update) \ EXPAND_COUNTER(item_write_dirty) \ EXPAND_COUNTER(lock_alloc) \ + EXPAND_COUNTER(lock_count_objects) \ EXPAND_COUNTER(lock_free) \ EXPAND_COUNTER(lock_grant_request) \ EXPAND_COUNTER(lock_grant_response) \ @@ -134,6 +139,7 @@ EXPAND_COUNTER(lock_lock_error) \ EXPAND_COUNTER(lock_nonblock_eagain) \ EXPAND_COUNTER(lock_recover_request) \ + EXPAND_COUNTER(lock_scan_objects) \ EXPAND_COUNTER(lock_shrink_attempted) \ EXPAND_COUNTER(lock_shrink_aborted) \ EXPAND_COUNTER(lock_shrink_work) \ @@ -232,12 +238,12 @@ struct scoutfs_counters { #define SCOUTFS_PCPU_COUNTER_BATCH (1 << 30) #define scoutfs_inc_counter(sb, which) \ - __percpu_counter_add(&SCOUTFS_SB(sb)->counters->which, 1, \ - SCOUTFS_PCPU_COUNTER_BATCH) + percpu_counter_add_batch(&SCOUTFS_SB(sb)->counters->which, 1, \ + SCOUTFS_PCPU_COUNTER_BATCH) #define scoutfs_add_counter(sb, which, cnt) \ - __percpu_counter_add(&SCOUTFS_SB(sb)->counters->which, cnt, \ - SCOUTFS_PCPU_COUNTER_BATCH) + percpu_counter_add_batch(&SCOUTFS_SB(sb)->counters->which, cnt, \ + SCOUTFS_PCPU_COUNTER_BATCH) void __init scoutfs_init_counters(void); int scoutfs_setup_counters(struct super_block *sb); diff --git a/kmod/src/data.c b/kmod/src/data.c index 48aae57a..78dd74ad 100644 --- a/kmod/src/data.c +++ b/kmod/src/data.c @@ -307,7 +307,7 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode, LIST_HEAD(ind_locks); s64 ret = 0; - WARN_ON_ONCE(inode && !mutex_is_locked(&inode->i_mutex)); + WARN_ON_ONCE(inode && !inode_is_locked(inode)); /* clamp last to the last possible block? */ if (last > SCOUTFS_BLOCK_SM_MAX) @@ -558,7 +558,7 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock, u64 offset; int ret; - WARN_ON_ONCE(create && !mutex_is_locked(&inode->i_mutex)); + WARN_ON_ONCE(create && !inode_is_locked(inode)); /* make sure caller holds a cluster lock */ lock = scoutfs_per_task_get(&si->pt_data_lock); @@ -704,7 +704,7 @@ static int scoutfs_readpage(struct file *file, struct page *page) if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, inode_lock)) { ret = scoutfs_data_wait_check(inode, page_offset(page), - PAGE_CACHE_SIZE, SEF_OFFLINE, + PAGE_SIZE, SEF_OFFLINE, SCOUTFS_IOC_DWO_READ, &dw, inode_lock); if (ret != 0) { @@ -729,6 +729,7 @@ static int scoutfs_readpage(struct file *file, struct page *page) return ret; } +#ifndef KC_FILE_AOPS_READAHEAD /* * This is used for opportunistic read-ahead which can throw the pages * away if it needs to. If the caller didn't deal with offline extents @@ -754,14 +755,14 @@ static int scoutfs_readpages(struct file *file, struct address_space *mapping, list_for_each_entry_safe(page, tmp, pages, lru) { ret = scoutfs_data_wait_check(inode, page_offset(page), - PAGE_CACHE_SIZE, SEF_OFFLINE, + PAGE_SIZE, SEF_OFFLINE, SCOUTFS_IOC_DWO_READ, NULL, inode_lock); if (ret < 0) goto out; if (ret > 0) { list_del(&page->lru); - page_cache_release(page); + put_page(page); if (--nr_pages == 0) { ret = 0; goto out; @@ -775,6 +776,29 @@ out: BUG_ON(!list_empty(pages)); return ret; } +#else +static void scoutfs_readahead(struct readahead_control *rac) +{ + struct inode *inode = rac->file->f_inode; + struct super_block *sb = inode->i_sb; + struct scoutfs_lock *inode_lock = NULL; + int ret; + + ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, + SCOUTFS_LKF_REFRESH_INODE, inode, &inode_lock); + if (ret) + return; + + ret = scoutfs_data_wait_check(inode, readahead_pos(rac), + readahead_length(rac), SEF_OFFLINE, + SCOUTFS_IOC_DWO_READ, NULL, + inode_lock); + if (ret == 0) + mpage_readahead(rac, scoutfs_get_block_read); + + scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ); +} +#endif static int scoutfs_writepage(struct page *page, struct writeback_control *wbc) { @@ -1057,7 +1081,7 @@ long scoutfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) goto out; } - mutex_lock(&inode->i_mutex); + inode_lock(inode); ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE, SCOUTFS_LKF_REFRESH_INODE, inode, &lock); @@ -1118,7 +1142,7 @@ out_extent: up_write(&si->extent_sem); out_mutex: scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE); - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); out: trace_scoutfs_data_fallocate(sb, ino, mode, offset, len, ret); @@ -1221,7 +1245,7 @@ int scoutfs_data_move_blocks(struct inode *from, u64 from_off, struct data_ext_args from_args; struct data_ext_args to_args; struct scoutfs_extent ext; - struct timespec cur_time; + struct kc_timespec cur_time; LIST_HEAD(locks); bool done = false; loff_t from_size; @@ -1442,7 +1466,7 @@ int scoutfs_data_move_blocks(struct inode *from, u64 from_off, up_write(&from_si->extent_sem); up_write(&to_si->extent_sem); - cur_time = CURRENT_TIME; + cur_time = current_time(from); if (!is_stage) { to->i_ctime = to->i_mtime = cur_time; inode_inc_iversion(to); @@ -1529,7 +1553,7 @@ int scoutfs_data_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, if (ret) goto out; - mutex_lock(&inode->i_mutex); + inode_lock(inode); down_read(&si->extent_sem); ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, 0, inode, &lock); @@ -1583,7 +1607,7 @@ int scoutfs_data_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, unlock: scoutfs_unlock(sb, lock, SCOUTFS_LOCK_READ); up_read(&si->extent_sem); - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); out: if (ret == 1) @@ -1783,6 +1807,37 @@ int scoutfs_data_wait_check_iov(struct inode *inode, const struct iovec *iov, return ret; } +int scoutfs_data_wait_check_iter(struct inode *inode, loff_t pos, struct iov_iter *iter, + u8 sef, u8 op, struct scoutfs_data_wait *dw, + struct scoutfs_lock *lock) +{ + size_t count = iov_iter_count(iter); + size_t off = iter->iov_offset; + const struct iovec *iov; + size_t len; + int ret = 0; + + for (iov = iter->iov; count > 0; iov++) { + len = iov->iov_len - off; + if (len == 0) + continue; + + /* aren't we waiting on too much data here ? */ + ret = scoutfs_data_wait_check(inode, pos, len, + sef, op, dw, lock); + + if (ret != 0) + break; + + + pos += len; + count -= len; + off = 0; + } + + return ret; +} + int scoutfs_data_wait(struct inode *inode, struct scoutfs_data_wait *dw) { DECLARE_DATA_WAIT_ROOT(inode->i_sb, rt); @@ -1873,7 +1928,11 @@ int scoutfs_data_waiting(struct super_block *sb, u64 ino, u64 iblock, const struct address_space_operations scoutfs_file_aops = { .readpage = scoutfs_readpage, +#ifndef KC_FILE_AOPS_READAHEAD .readpages = scoutfs_readpages, +#else + .readahead = scoutfs_readahead, +#endif .writepage = scoutfs_writepage, .writepages = scoutfs_writepages, .write_begin = scoutfs_write_begin, @@ -1881,10 +1940,15 @@ const struct address_space_operations scoutfs_file_aops = { }; const struct file_operations scoutfs_file_fops = { +#ifdef KC_LINUX_HAVE_FOP_AIO_READ .read = do_sync_read, .write = do_sync_write, .aio_read = scoutfs_file_aio_read, .aio_write = scoutfs_file_aio_write, +#else + .read_iter = scoutfs_file_read_iter, + .write_iter = scoutfs_file_write_iter, +#endif .unlocked_ioctl = scoutfs_ioctl, .fsync = scoutfs_file_fsync, .llseek = scoutfs_file_llseek, diff --git a/kmod/src/data.h b/kmod/src/data.h index a34854eb..1fbfce9b 100644 --- a/kmod/src/data.h +++ b/kmod/src/data.h @@ -65,6 +65,9 @@ int scoutfs_data_wait_check_iov(struct inode *inode, const struct iovec *iov, unsigned long nr_segs, loff_t pos, u8 sef, u8 op, struct scoutfs_data_wait *ow, struct scoutfs_lock *lock); +int scoutfs_data_wait_check_iter(struct inode *inode, loff_t pos, struct iov_iter *iter, + u8 sef, u8 op, struct scoutfs_data_wait *ow, + struct scoutfs_lock *lock); bool scoutfs_data_wait_found(struct scoutfs_data_wait *ow); int scoutfs_data_wait(struct inode *inode, struct scoutfs_data_wait *ow); diff --git a/kmod/src/dir.c b/kmod/src/dir.c index d3a6e031..357d33c6 100644 --- a/kmod/src/dir.c +++ b/kmod/src/dir.c @@ -272,7 +272,7 @@ static void set_dentry_fsdata(struct dentry *dentry, struct scoutfs_lock *lock) static bool test_dentry_fsdata(struct dentry *dentry, u64 refresh) { - u64 fsd = (unsigned long)ACCESS_ONCE(dentry->d_fsdata); + u64 fsd = (unsigned long)READ_ONCE(dentry->d_fsdata); return fsd == refresh; } @@ -735,7 +735,7 @@ static int scoutfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, set_dentry_fsdata(dentry, dir_lock); i_size_write(dir, i_size_read(dir) + dentry->d_name.len); - dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_mtime = dir->i_ctime = current_time(inode); inode->i_mtime = inode->i_atime = inode->i_ctime = dir->i_mtime; si->crtime = inode->i_mtime; inode_inc_iversion(dir); @@ -859,7 +859,7 @@ retry: set_dentry_fsdata(dentry, dir_lock); i_size_write(dir, dir_size); - dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_mtime = dir->i_ctime = current_time(inode); inode->i_ctime = dir->i_mtime; inc_nlink(inode); inode_inc_iversion(dir); @@ -900,7 +900,7 @@ static int scoutfs_unlink(struct inode *dir, struct dentry *dentry) { struct super_block *sb = dir->i_sb; struct inode *inode = dentry->d_inode; - struct timespec ts = current_kernel_time(); + struct kc_timespec ts = current_time(inode); struct scoutfs_lock *inode_lock = NULL; struct scoutfs_lock *orph_lock = NULL; struct scoutfs_lock *dir_lock = NULL; @@ -1059,14 +1059,14 @@ static int symlink_item_ops(struct super_block *sb, enum symlink_ops op, u64 ino } /* - * Full a buffer with the null terminated symlink, point nd at it, and - * return it so put_link can free it once the vfs is done. + * Fill a buffer with the null terminated symlink, and return it + * so callers can free it once the vfs is done. * * We chose to pay the runtime cost of per-call allocation and copy * overhead instead of wiring up symlinks to the page cache, storing * each small link in a full page, and later having to reclaim them. */ -static void *scoutfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static void *scoutfs_get_link_target(struct dentry *dentry) { struct inode *inode = dentry->d_inode; struct super_block *sb = inode->i_sb; @@ -1125,32 +1125,41 @@ out: if (ret < 0) { kfree(path); path = ERR_PTR(ret); - } else { - nd_set_link(nd, path); } + scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ); return path; } +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER +static void *scoutfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *path; + + path = scoutfs_get_link_target(dentry); + if (!IS_ERR_OR_NULL(path)) + nd_set_link(nd, path); + return path; +} + static void scoutfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) { if (!IS_ERR_OR_NULL(cookie)) kfree(cookie); } +#else +static const char *scoutfs_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) +{ + char *path; -const struct inode_operations scoutfs_symlink_iops = { - .readlink = generic_readlink, - .follow_link = scoutfs_follow_link, - .put_link = scoutfs_put_link, - .getattr = scoutfs_getattr, - .setattr = scoutfs_setattr, - .setxattr = generic_setxattr, - .getxattr = generic_getxattr, - .listxattr = scoutfs_listxattr, - .removexattr = generic_removexattr, - .get_acl = scoutfs_get_acl, -}; + path = scoutfs_get_link_target(dentry); + if (!IS_ERR_OR_NULL(path)) + set_delayed_call(done, kfree_link, path); + + return path; +} +#endif /* * Symlink target paths can be annoyingly large. We store relatively @@ -1204,7 +1213,7 @@ static int scoutfs_symlink(struct inode *dir, struct dentry *dentry, set_dentry_fsdata(dentry, dir_lock); i_size_write(dir, i_size_read(dir) + dentry->d_name.len); - dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_mtime = dir->i_ctime = current_time(inode); inode_inc_iversion(dir); inode->i_ctime = dir->i_mtime; @@ -1558,7 +1567,7 @@ static int scoutfs_rename_common(struct inode *old_dir, struct scoutfs_lock *orph_lock = NULL; struct scoutfs_dirent new_dent; struct scoutfs_dirent old_dent; - struct timespec now; + struct kc_timespec now; bool ins_new = false; bool del_new = false; bool ins_old = false; @@ -1724,7 +1733,7 @@ retry: inc_nlink(new_dir); } - now = CURRENT_TIME; + now = current_time(old_inode); old_dir->i_ctime = now; old_dir->i_mtime = now; if (new_dir != old_dir) { @@ -1811,12 +1820,14 @@ out_unlock: return ret; } +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER static int scoutfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { return scoutfs_rename_common(old_dir, old_dentry, new_dir, new_dentry, 0); } +#endif static int scoutfs_rename2(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, @@ -1861,7 +1872,7 @@ static int scoutfs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mod if (ret < 0) goto out; /* XXX returning error but items created */ - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); si->crtime = inode->i_mtime; insert_inode_hash(inode); ihold(inode); /* need to update inode modifications in d_tmpfile */ @@ -1886,6 +1897,37 @@ out: return ret; } +const struct inode_operations scoutfs_symlink_iops = { +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER + .readlink = generic_readlink, + .follow_link = scoutfs_follow_link, + .put_link = scoutfs_put_link, +#else + .get_link = scoutfs_get_link, +#endif + .getattr = scoutfs_getattr, + .setattr = scoutfs_setattr, +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, +#endif + .listxattr = scoutfs_listxattr, +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER + .removexattr = generic_removexattr, +#endif + .get_acl = scoutfs_get_acl, +#ifndef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER + .tmpfile = scoutfs_tmpfile, + .rename = scoutfs_rename_common, + .symlink = scoutfs_symlink, + .unlink = scoutfs_unlink, + .link = scoutfs_link, + .mkdir = scoutfs_mkdir, + .create = scoutfs_create, + .lookup = scoutfs_lookup, +#endif +}; + const struct file_operations scoutfs_dir_fops = { .KC_FOP_READDIR = scoutfs_readdir, #ifdef KC_FMODE_KABI_ITERATE @@ -1897,9 +1939,12 @@ const struct file_operations scoutfs_dir_fops = { }; - +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER const struct inode_operations_wrapper scoutfs_dir_iops = { .ops = { +#else +const struct inode_operations scoutfs_dir_iops = { +#endif .lookup = scoutfs_lookup, .mknod = scoutfs_mknod, .create = scoutfs_create, @@ -1907,17 +1952,25 @@ const struct inode_operations_wrapper scoutfs_dir_iops = { .link = scoutfs_link, .unlink = scoutfs_unlink, .rmdir = scoutfs_unlink, - .rename = scoutfs_rename, .getattr = scoutfs_getattr, .setattr = scoutfs_setattr, +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER + .rename = scoutfs_rename, .setxattr = generic_setxattr, .getxattr = generic_getxattr, - .listxattr = scoutfs_listxattr, .removexattr = generic_removexattr, +#endif + .listxattr = scoutfs_listxattr, .get_acl = scoutfs_get_acl, .symlink = scoutfs_symlink, .permission = scoutfs_permission, +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER }, +#endif .tmpfile = scoutfs_tmpfile, +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER .rename2 = scoutfs_rename2, +#else + .rename = scoutfs_rename2, +#endif }; diff --git a/kmod/src/dir.h b/kmod/src/dir.h index 9bd1f193..9985b7c3 100644 --- a/kmod/src/dir.h +++ b/kmod/src/dir.h @@ -5,7 +5,11 @@ #include "lock.h" extern const struct file_operations scoutfs_dir_fops; +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER extern const struct inode_operations_wrapper scoutfs_dir_iops; +#else +extern const struct inode_operations scoutfs_dir_iops; +#endif extern const struct inode_operations scoutfs_symlink_iops; extern const struct dentry_operations scoutfs_dentry_ops; diff --git a/kmod/src/file.c b/kmod/src/file.c index 586d77fd..fd31a9d0 100644 --- a/kmod/src/file.c +++ b/kmod/src/file.c @@ -29,6 +29,7 @@ #include "per_task.h" #include "omap.h" +#ifdef KC_LINUX_HAVE_FOP_AIO_READ /* * Start a high level file read. We check for offline extents in the * read region here so that we only check the extents once. We use the @@ -42,27 +43,27 @@ ssize_t scoutfs_file_aio_read(struct kiocb *iocb, const struct iovec *iov, struct inode *inode = file_inode(file); struct scoutfs_inode_info *si = SCOUTFS_I(inode); struct super_block *sb = inode->i_sb; - struct scoutfs_lock *inode_lock = NULL; + struct scoutfs_lock *scoutfs_inode_lock = NULL; SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent); DECLARE_DATA_WAIT(dw); int ret; retry: /* protect checked extents from release */ - mutex_lock(&inode->i_mutex); + inode_lock(inode); atomic_inc(&inode->i_dio_count); - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, - SCOUTFS_LKF_REFRESH_INODE, inode, &inode_lock); + SCOUTFS_LKF_REFRESH_INODE, inode, &scoutfs_inode_lock); if (ret) goto out; - if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, inode_lock)) { + if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, scoutfs_inode_lock)) { ret = scoutfs_data_wait_check_iov(inode, iov, nr_segs, pos, SEF_OFFLINE, SCOUTFS_IOC_DWO_READ, - &dw, inode_lock); + &dw, scoutfs_inode_lock); if (ret != 0) goto out; } else { @@ -74,7 +75,7 @@ retry: out: inode_dio_done(inode); scoutfs_per_task_del(&si->pt_data_lock, &pt_ent); - scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ); + scoutfs_unlock(sb, scoutfs_inode_lock, SCOUTFS_LOCK_READ); if (scoutfs_data_wait_found(&dw)) { ret = scoutfs_data_wait(inode, &dw); @@ -92,7 +93,7 @@ ssize_t scoutfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, struct inode *inode = file_inode(file); struct scoutfs_inode_info *si = SCOUTFS_I(inode); struct super_block *sb = inode->i_sb; - struct scoutfs_lock *inode_lock = NULL; + struct scoutfs_lock *scoutfs_inode_lock = NULL; SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent); DECLARE_DATA_WAIT(dw); int ret; @@ -101,22 +102,22 @@ ssize_t scoutfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, return 0; retry: - mutex_lock(&inode->i_mutex); + inode_lock(inode); ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE, - SCOUTFS_LKF_REFRESH_INODE, inode, &inode_lock); + SCOUTFS_LKF_REFRESH_INODE, inode, &scoutfs_inode_lock); if (ret) goto out; - ret = scoutfs_complete_truncate(inode, inode_lock); + ret = scoutfs_complete_truncate(inode, scoutfs_inode_lock); if (ret) goto out; - if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, inode_lock)) { + if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, scoutfs_inode_lock)) { /* data_version is per inode, whole file must be online */ ret = scoutfs_data_wait_check(inode, 0, i_size_read(inode), SEF_OFFLINE, SCOUTFS_IOC_DWO_WRITE, - &dw, inode_lock); + &dw, scoutfs_inode_lock); if (ret != 0) goto out; } @@ -127,8 +128,8 @@ retry: out: scoutfs_per_task_del(&si->pt_data_lock, &pt_ent); - scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_WRITE); - mutex_unlock(&inode->i_mutex); + scoutfs_unlock(sb, scoutfs_inode_lock, SCOUTFS_LOCK_WRITE); + inode_unlock(inode); if (scoutfs_data_wait_found(&dw)) { ret = scoutfs_data_wait(inode, &dw); @@ -146,6 +147,113 @@ out: return ret; } +#else +ssize_t scoutfs_file_read_iter(struct kiocb *iocb, struct iov_iter *to) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct scoutfs_inode_info *si = SCOUTFS_I(inode); + struct super_block *sb = inode->i_sb; + struct scoutfs_lock *scoutfs_inode_lock = NULL; + SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent); + DECLARE_DATA_WAIT(dw); + int ret; + +retry: + /* protect checked extents from release */ + inode_lock(inode); + atomic_inc(&inode->i_dio_count); + inode_unlock(inode); + + ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, + SCOUTFS_LKF_REFRESH_INODE, inode, &scoutfs_inode_lock); + if (ret) + goto out; + + if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, scoutfs_inode_lock)) { + ret = scoutfs_data_wait_check_iter(inode, iocb->ki_pos, to, + SEF_OFFLINE, + SCOUTFS_IOC_DWO_READ, + &dw, scoutfs_inode_lock); + if (ret != 0) + goto out; + } else { + WARN_ON_ONCE(true); + } + + ret = generic_file_read_iter(iocb, to); + +out: + inode_dio_end(inode); + scoutfs_per_task_del(&si->pt_data_lock, &pt_ent); + scoutfs_unlock(sb, scoutfs_inode_lock, SCOUTFS_LOCK_READ); + + if (scoutfs_data_wait_found(&dw)) { + ret = scoutfs_data_wait(inode, &dw); + if (ret == 0) + goto retry; + } + return ret; +} + +ssize_t scoutfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct scoutfs_inode_info *si = SCOUTFS_I(inode); + struct super_block *sb = inode->i_sb; + struct scoutfs_lock *scoutfs_inode_lock = NULL; + SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent); + DECLARE_DATA_WAIT(dw); + int ret; + int written; + +retry: + inode_lock(inode); + ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE, + SCOUTFS_LKF_REFRESH_INODE, inode, &scoutfs_inode_lock); + if (ret) + goto out; + + ret = generic_write_checks(iocb, from); + if (ret <= 0) + goto out; + + ret = scoutfs_complete_truncate(inode, scoutfs_inode_lock); + if (ret) + goto out; + + if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, scoutfs_inode_lock)) { + /* data_version is per inode, whole file must be online */ + ret = scoutfs_data_wait_check_iter(inode, iocb->ki_pos, from, + SEF_OFFLINE, + SCOUTFS_IOC_DWO_WRITE, + &dw, scoutfs_inode_lock); + if (ret != 0) + goto out; + } + + /* XXX: remove SUID bit */ + + written = __generic_file_write_iter(iocb, from); + +out: + scoutfs_per_task_del(&si->pt_data_lock, &pt_ent); + scoutfs_unlock(sb, scoutfs_inode_lock, SCOUTFS_LOCK_WRITE); + inode_unlock(inode); + + if (scoutfs_data_wait_found(&dw)) { + ret = scoutfs_data_wait(inode, &dw); + if (ret == 0) + goto retry; + } + + if (ret > 0 || ret == -EIOCBQUEUED) + ret = generic_write_sync(iocb, written); + + return written ? written : ret; +} +#endif int scoutfs_permission(struct inode *inode, int mask) { diff --git a/kmod/src/file.h b/kmod/src/file.h index 82d86618..82829ef5 100644 --- a/kmod/src/file.h +++ b/kmod/src/file.h @@ -1,10 +1,15 @@ #ifndef _SCOUTFS_FILE_H_ #define _SCOUTFS_FILE_H_ +#ifdef KC_LINUX_HAVE_FOP_AIO_READ ssize_t scoutfs_file_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos); ssize_t scoutfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos); +#else +ssize_t scoutfs_file_read_iter(struct kiocb *, struct iov_iter *); +ssize_t scoutfs_file_write_iter(struct kiocb *, struct iov_iter *); +#endif int scoutfs_permission(struct inode *inode, int mask); loff_t scoutfs_file_llseek(struct file *file, loff_t offset, int whence); diff --git a/kmod/src/inode.c b/kmod/src/inode.c index 6741bda7..540004cc 100644 --- a/kmod/src/inode.c +++ b/kmod/src/inode.c @@ -143,10 +143,12 @@ void scoutfs_destroy_inode(struct inode *inode) static const struct inode_operations scoutfs_file_iops = { .getattr = scoutfs_getattr, .setattr = scoutfs_setattr, +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER .setxattr = generic_setxattr, .getxattr = generic_getxattr, - .listxattr = scoutfs_listxattr, .removexattr = generic_removexattr, +#endif + .listxattr = scoutfs_listxattr, .get_acl = scoutfs_get_acl, .fiemap = scoutfs_data_fiemap, }; @@ -154,10 +156,12 @@ static const struct inode_operations scoutfs_file_iops = { static const struct inode_operations scoutfs_special_iops = { .getattr = scoutfs_getattr, .setattr = scoutfs_setattr, +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER .setxattr = generic_setxattr, .getxattr = generic_getxattr, - .listxattr = scoutfs_listxattr, .removexattr = generic_removexattr, +#endif + .listxattr = scoutfs_listxattr, .get_acl = scoutfs_get_acl, }; @@ -174,8 +178,12 @@ static void set_inode_ops(struct inode *inode) inode->i_fop = &scoutfs_file_fops; break; case S_IFDIR: +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER inode->i_op = &scoutfs_dir_iops.ops; inode->i_flags |= S_IOPS_WRAPPER; +#else + inode->i_op = &scoutfs_dir_iops; +#endif inode->i_fop = &scoutfs_dir_fops; break; case S_IFLNK: @@ -247,7 +255,7 @@ static void load_inode(struct inode *inode, struct scoutfs_inode *cinode) struct scoutfs_inode_info *si = SCOUTFS_I(inode); i_size_write(inode, le64_to_cpu(cinode->size)); - inode->i_version = le64_to_cpu(cinode->version); + inode_set_iversion_queried(inode, le64_to_cpu(cinode->version)); set_nlink(inode, le32_to_cpu(cinode->nlink)); i_uid_write(inode, le32_to_cpu(cinode->uid)); i_gid_write(inode, le32_to_cpu(cinode->gid)); @@ -340,10 +348,17 @@ int scoutfs_inode_refresh(struct inode *inode, struct scoutfs_lock *lock) return ret; } +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER int scoutfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { struct inode *inode = dentry->d_inode; +#else +int scoutfs_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int query_flags) +{ + struct inode *inode = d_inode(path->dentry); +#endif struct super_block *sb = inode->i_sb; struct scoutfs_lock *lock = NULL; int ret; @@ -384,7 +399,7 @@ static int set_inode_size(struct inode *inode, struct scoutfs_lock *lock, scoutfs_inode_inc_data_version(inode); truncate_setsize(inode, new_size); - inode->i_ctime = inode->i_mtime = CURRENT_TIME; + inode->i_ctime = inode->i_mtime = current_time(inode); if (truncate) si->flags |= SCOUTFS_INO_FLAG_TRUNCATE; scoutfs_inode_set_data_seq(inode); @@ -467,8 +482,7 @@ retry: SCOUTFS_LKF_REFRESH_INODE, inode, &lock); if (ret) return ret; - - ret = inode_change_ok(inode, attr); + ret = setattr_prepare(dentry, attr); if (ret) goto out; @@ -496,9 +510,9 @@ retry: scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE); /* XXX callee locks instead? */ - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); ret = scoutfs_data_wait(inode, &dw); - mutex_lock(&inode->i_mutex); + inode_lock(inode); if (ret == 0) goto retry; @@ -750,7 +764,7 @@ struct inode *scoutfs_iget(struct super_block *sb, u64 ino, int lkf, int igf) /* XXX ensure refresh, instead clear in drop_inode? */ si = SCOUTFS_I(inode); atomic64_set(&si->last_refreshed, 0); - inode->i_version = 0; + inode_set_iversion_queried(inode, 0); } ret = scoutfs_inode_refresh(inode, lock); @@ -798,7 +812,7 @@ static void store_inode(struct scoutfs_inode *cinode, struct inode *inode) scoutfs_inode_get_onoff(inode, &online_blocks, &offline_blocks); cinode->size = cpu_to_le64(i_size_read(inode)); - cinode->version = cpu_to_le64(inode->i_version); + cinode->version = cpu_to_le64(inode_peek_iversion(inode)); cinode->nlink = cpu_to_le32(inode->i_nlink); cinode->uid = cpu_to_le32(i_uid_read(inode)); cinode->gid = cpu_to_le32(i_gid_read(inode)); @@ -1475,7 +1489,7 @@ int scoutfs_new_inode(struct super_block *sb, struct inode *dir, umode_t mode, d inode->i_ino = ino; /* XXX overflow */ inode_init_owner(inode, dir, mode); inode_set_bytes(inode, 0); - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); inode->i_rdev = rdev; set_inode_ops(inode); diff --git a/kmod/src/inode.h b/kmod/src/inode.h index 79eacde0..607ac9f8 100644 --- a/kmod/src/inode.h +++ b/kmod/src/inode.h @@ -22,7 +22,7 @@ struct scoutfs_inode_info { u64 online_blocks; u64 offline_blocks; u32 flags; - struct timespec crtime; + struct kc_timespec crtime; /* * Protects per-inode extent items, most particularly readers @@ -123,8 +123,13 @@ void scoutfs_inode_get_onoff(struct inode *inode, s64 *on, s64 *off); int scoutfs_complete_truncate(struct inode *inode, struct scoutfs_lock *lock); int scoutfs_inode_refresh(struct inode *inode, struct scoutfs_lock *lock); +#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER int scoutfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); +#else +int scoutfs_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int query_flags); +#endif int scoutfs_setattr(struct dentry *dentry, struct iattr *attr); int scoutfs_inode_orphan_create(struct super_block *sb, u64 ino, struct scoutfs_lock *lock, diff --git a/kmod/src/ioctl.c b/kmod/src/ioctl.c index 3bc9c546..6bf70403 100644 --- a/kmod/src/ioctl.c +++ b/kmod/src/ioctl.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "format.h" #include "key.h" @@ -302,7 +303,7 @@ static long scoutfs_ioc_release(struct file *file, unsigned long arg) if (ret) return ret; - mutex_lock(&inode->i_mutex); + inode_lock(inode); ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE, SCOUTFS_LKF_REFRESH_INODE, inode, &lock); @@ -351,7 +352,7 @@ static long scoutfs_ioc_release(struct file *file, unsigned long arg) out: scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE); - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); mnt_drop_write_file(file); trace_scoutfs_ioc_release_ret(sb, scoutfs_ino(inode), ret); @@ -393,7 +394,7 @@ static long scoutfs_ioc_data_wait_err(struct file *file, unsigned long arg) goto out; } - mutex_lock(&inode->i_mutex); + inode_lock(inode); ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, SCOUTFS_LKF_REFRESH_INODE, inode, &lock); @@ -411,7 +412,7 @@ static long scoutfs_ioc_data_wait_err(struct file *file, unsigned long arg) scoutfs_unlock(sb, lock, SCOUTFS_LOCK_READ); unlock: - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); iput(inode); out: return ret; @@ -448,7 +449,6 @@ static long scoutfs_ioc_stage(struct file *file, unsigned long arg) { struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; - struct address_space *mapping = inode->i_mapping; struct scoutfs_inode_info *si = SCOUTFS_I(inode); SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent); struct scoutfs_ioctl_stage args; @@ -480,8 +480,10 @@ static long scoutfs_ioc_stage(struct file *file, unsigned long arg) /* the iocb is really only used for the file pointer :P */ init_sync_kiocb(&kiocb, file); kiocb.ki_pos = args.offset; +#ifdef KC_LINUX_AIO_KI_LEFT kiocb.ki_left = args.length; kiocb.ki_nbytes = args.length; +#endif iov.iov_base = (void __user *)(unsigned long)args.buf_ptr; iov.iov_len = args.length; @@ -489,7 +491,7 @@ static long scoutfs_ioc_stage(struct file *file, unsigned long arg) if (ret) return ret; - mutex_lock(&inode->i_mutex); + inode_lock(inode); ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE, SCOUTFS_LKF_REFRESH_INODE, inode, &lock); @@ -516,7 +518,7 @@ static long scoutfs_ioc_stage(struct file *file, unsigned long arg) } si->staging = true; - current->backing_dev_info = mapping->backing_dev_info; + current->backing_dev_info = inode_to_bdi(inode); pos = args.offset; written = 0; @@ -533,7 +535,7 @@ static long scoutfs_ioc_stage(struct file *file, unsigned long arg) out: scoutfs_per_task_del(&si->pt_data_lock, &pt_ent); scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE); - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); mnt_drop_write_file(file); trace_scoutfs_ioc_stage_ret(sb, scoutfs_ino(inode), ret); @@ -652,7 +654,7 @@ static long scoutfs_ioc_setattr_more(struct file *file, unsigned long arg) if (ret) goto out; - mutex_lock(&inode->i_mutex); + inode_lock(inode); ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE, SCOUTFS_LKF_REFRESH_INODE, inode, &lock); @@ -696,7 +698,7 @@ static long scoutfs_ioc_setattr_more(struct file *file, unsigned long arg) unlock: scoutfs_inode_index_unlock(sb, &ind_locks); scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE); - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); mnt_drop_write_file(file); out: diff --git a/kmod/src/item.c b/kmod/src/item.c index 8c7900d5..673797ef 100644 --- a/kmod/src/item.c +++ b/kmod/src/item.c @@ -27,6 +27,7 @@ #include "trans.h" #include "counters.h" #include "scoutfs_trace.h" +#include "util.h" /* * The item cache maintains a consistent view of items that are read @@ -76,8 +77,10 @@ struct item_cache_info { /* almost always read, barely written */ struct super_block *sb; struct item_percpu_pages __percpu *pcpu_pages; - struct shrinker shrinker; + KC_DEFINE_SHRINKER(shrinker); +#ifdef KC_CPU_NOTIFIER struct notifier_block notifier; +#endif /* often walked, but per-cpu refs are fast path */ rwlock_t rwlock; @@ -2277,7 +2280,7 @@ int scoutfs_item_write_dirty(struct super_block *sb) ret = -ENOMEM; goto out; } - list_add(&page->list, &pages); + list_add(&page->lru, &pages); first = NULL; prev = &first; @@ -2290,7 +2293,7 @@ int scoutfs_item_write_dirty(struct super_block *sb) ret = -ENOMEM; goto out; } - list_add(&second->list, &pages); + list_add(&second->lru, &pages); } /* read lock next sorted page, we're only dirty_list user */ @@ -2347,8 +2350,8 @@ int scoutfs_item_write_dirty(struct super_block *sb) /* write all the dirty items into log btree blocks */ ret = scoutfs_forest_insert_list(sb, first); out: - list_for_each_entry_safe(page, second, &pages, list) { - list_del_init(&page->list); + list_for_each_entry_safe(page, second, &pages, lru) { + list_del_init(&page->lru); __free_page(page); } @@ -2530,27 +2533,35 @@ retry: put_pg(sb, right); } +static unsigned long item_cache_count_objects(struct shrinker *shrink, + struct shrink_control *sc) +{ + struct item_cache_info *cinf = KC_SHRINKER_CONTAINER_OF(shrink, struct item_cache_info); + struct super_block *sb = cinf->sb; + + scoutfs_inc_counter(sb, item_cache_count_objects); + + return shrinker_min_long(cinf->lru_pages); +} + /* * Shrink the size the item cache. We're operating against the fast * path lock ordering and we skip pages if we can't acquire locks. We * can run into dirty pages or pages with items that weren't visible to * the earliest active reader which must be skipped. */ -static int item_lru_shrink(struct shrinker *shrink, - struct shrink_control *sc) +static unsigned long item_cache_scan_objects(struct shrinker *shrink, + struct shrink_control *sc) { - struct item_cache_info *cinf = container_of(shrink, - struct item_cache_info, - shrinker); + struct item_cache_info *cinf = KC_SHRINKER_CONTAINER_OF(shrink, struct item_cache_info); struct super_block *sb = cinf->sb; struct cached_page *tmp; struct cached_page *pg; + unsigned long freed = 0; u64 first_reader_seq; - int nr; + int nr = sc->nr_to_scan; - if (sc->nr_to_scan == 0) - goto out; - nr = sc->nr_to_scan; + scoutfs_inc_counter(sb, item_cache_scan_objects); /* can't invalidate pages with items that weren't visible to first reader */ first_reader_seq = first_active_reader_seq(cinf); @@ -2582,6 +2593,7 @@ static int item_lru_shrink(struct shrinker *shrink, rbtree_erase(&pg->node, &cinf->pg_root); invalidate_pcpu_page(pg); write_unlock(&pg->rwlock); + freed++; put_pg(sb, pg); @@ -2591,10 +2603,11 @@ static int item_lru_shrink(struct shrinker *shrink, write_unlock(&cinf->rwlock); spin_unlock(&cinf->lru_lock); -out: - return min_t(unsigned long, cinf->lru_pages, INT_MAX); + + return freed; } +#ifdef KC_CPU_NOTIFIER static int item_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { @@ -2609,6 +2622,7 @@ static int item_cpu_callback(struct notifier_block *nfb, return NOTIFY_OK; } +#endif int scoutfs_item_setup(struct super_block *sb) { @@ -2638,11 +2652,13 @@ int scoutfs_item_setup(struct super_block *sb) for_each_possible_cpu(cpu) init_pcpu_pages(cinf, cpu); - cinf->shrinker.shrink = item_lru_shrink; - cinf->shrinker.seeks = DEFAULT_SEEKS; - register_shrinker(&cinf->shrinker); + KC_INIT_SHRINKER_FUNCS(&cinf->shrinker, item_cache_count_objects, + item_cache_scan_objects); + KC_REGISTER_SHRINKER(&cinf->shrinker); +#ifdef KC_CPU_NOTIFIER cinf->notifier.notifier_call = item_cpu_callback; register_hotcpu_notifier(&cinf->notifier); +#endif sbi->item_cache_info = cinf; return 0; @@ -2662,8 +2678,10 @@ void scoutfs_item_destroy(struct super_block *sb) if (cinf) { BUG_ON(!list_empty(&cinf->active_list)); +#ifdef KC_CPU_NOTIFIER unregister_hotcpu_notifier(&cinf->notifier); - unregister_shrinker(&cinf->shrinker); +#endif + KC_UNREGISTER_SHRINKER(&cinf->shrinker); for_each_possible_cpu(cpu) drop_pcpu_pages(sb, cinf, cpu); diff --git a/kmod/src/kernelcompat.c b/kmod/src/kernelcompat.c new file mode 100644 index 00000000..7f8da413 --- /dev/null +++ b/kmod/src/kernelcompat.c @@ -0,0 +1,84 @@ + +#include + +#include "kernelcompat.h" + +#ifdef KC_SHRINKER_SHRINK +#include +/* + * If a target doesn't have that .{count,scan}_objects() interface then + * we have a .shrink() helper that performs the shrink work in terms of + * count/scan. + */ +int kc_shrink_wrapper_fn(struct shrinker *shrink, struct shrink_control *sc) +{ + struct kc_shrinker_wrapper *wrapper = container_of(shrink, struct kc_shrinker_wrapper, shrink); + unsigned long nr; + unsigned long rc; + + if (sc->nr_to_scan != 0) { + rc = wrapper->scan_objects(shrink, sc); + /* translate magic values to the equivalent for older kernels */ + if (rc == SHRINK_STOP) + return -1; + else if (rc == SHRINK_EMPTY) + return 0; + } + + nr = wrapper->count_objects(shrink, sc); + + return min_t(unsigned long, nr, INT_MAX); +} +#endif + +#ifndef KC_CURRENT_TIME_INODE +struct timespec64 kc_current_time(struct inode *inode) +{ + struct timespec64 now; + unsigned gran; + + getnstimeofday64(&now); + + if (unlikely(!inode->i_sb)) { + WARN(1, "current_time() called with uninitialized super_block in the inode"); + return now; + } + + gran = inode->i_sb->s_time_gran; + + /* Avoid division in the common cases 1 ns and 1 s. */ + if (gran == 1) { + /* nothing */ + } else if (gran == NSEC_PER_SEC) { + now.tv_nsec = 0; + } else if (gran > 1 && gran < NSEC_PER_SEC) { + now.tv_nsec -= now.tv_nsec % gran; + } else { + WARN(1, "illegal file time granularity: %u", gran); + } + + return now; +} +#endif + +#ifndef KC_GENERIC_FILE_BUFFERED_WRITE +ssize_t +kc_generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos, loff_t *ppos, + size_t count, ssize_t written) +{ + struct file *file = iocb->ki_filp; + ssize_t status; + struct iov_iter i; + + iov_iter_init(&i, WRITE, iov, nr_segs, count); + status = generic_perform_write(file, &i, pos); + + if (likely(status >= 0)) { + written += status; + *ppos = pos + status; + } + + return written ? written : status; +} +#endif diff --git a/kmod/src/kernelcompat.h b/kmod/src/kernelcompat.h index 12e16a10..32124829 100644 --- a/kmod/src/kernelcompat.h +++ b/kmod/src/kernelcompat.h @@ -1,8 +1,35 @@ #ifndef _SCOUTFS_KERNELCOMPAT_H_ #define _SCOUTFS_KERNELCOMPAT_H_ -#ifndef KC_ITERATE_DIR_CONTEXT +#include #include + +/* + * v4.15-rc3-4-gae5e165d855d + * + * new API for handling inode->i_version. This forces us to + * include this API where we need. We include it here for + * convenience instead of where it's needed. + */ +#ifdef KC_NEED_LINUX_IVERSION_H +#include +#else +/* + * Kernels before above version will need to fall back to + * manipulating inode->i_version as previous with degraded + * methods. + */ +#define inode_set_iversion_queried(inode, val) \ +do { \ + (inode)->i_version = val; \ +} while (0) +#define inode_peek_iversion(inode) \ +({ \ + (inode)->i_version; \ +}) +#endif + +#ifndef KC_ITERATE_DIR_CONTEXT typedef filldir_t kc_readdir_ctx_t; #define KC_DECLARE_READDIR(name, file, dirent, ctx) name(file, dirent, ctx) #define KC_FOP_READDIR readdir @@ -52,4 +79,198 @@ static inline int dir_emit_dots(struct file *file, void *dirent, #define kc_posix_acl_valid(user_ns, acl) posix_acl_valid(acl) #endif +/* + * v3.6-rc1-24-gdbf2576e37da + * + * All workqueues are now non-reentrant, and the bit flag is removed + * shortly after its uses were removed. + */ +#ifndef WQ_NON_REENTRANT +#define WQ_NON_REENTRANT 0 +#endif + +/* + * v3.18-rc2-19-gb5ae6b15bd73 + * + * Folds d_materialise_unique into d_splice_alias. Note reversal + * of arguments (Also note Documentation/filesystems/porting.rst) + */ +#ifndef KC_D_MATERIALISE_UNIQUE +#define d_materialise_unique(dentry, inode) d_splice_alias(inode, dentry) +#endif + +/* + * v4.8-rc1-29-g31051c85b5e2 + * + * fall back to inode_change_ok() if setattr_prepare() isn't available + */ +#ifndef KC_SETATTR_PREPARE +#define setattr_prepare(dentry, attr) inode_change_ok(d_inode(dentry), attr) +#endif + +#ifndef KC___POSIX_ACL_CREATE +#define __posix_acl_create posix_acl_create +#define __posix_acl_chmod posix_acl_chmod +#endif + +#ifndef KC_PERCPU_COUNTER_ADD_BATCH +#define percpu_counter_add_batch __percpu_counter_add +#endif + +#ifndef KC_MEMALLOC_NOFS_SAVE +#define memalloc_nofs_save memalloc_noio_save +#define memalloc_nofs_restore memalloc_noio_restore +#endif + +#ifdef KC_BIO_BI_OPF +#define kc_bio_get_opf(bio) \ +({ \ + (bio)->bi_opf; \ +}) +#define kc_bio_set_opf(bio, opf) \ +do { \ + (bio)->bi_opf = opf; \ +} while (0) +#define kc_bio_set_sector(bio, sect) \ +do { \ + (bio)->bi_iter.bi_sector = sect;\ +} while (0) +#define kc_submit_bio(bio) submit_bio(bio) +#else +#define kc_bio_get_opf(bio) \ +({ \ + (bio)->bi_rw; \ +}) +#define kc_bio_set_opf(bio, opf) \ +do { \ + (bio)->bi_rw = opf; \ +} while (0) +#define kc_bio_set_sector(bio, sect) \ +do { \ + (bio)->bi_sector = sect; \ +} while (0) +#define kc_submit_bio(bio) \ +do { \ + submit_bio((bio)->bi_rw, bio); \ +} while (0) +#define bio_set_dev(bio, bdev) \ +do { \ + (bio)->bi_bdev = (bdev); \ +} while (0) +#endif + +#ifdef KC_BIO_BI_STATUS +#define KC_DECLARE_BIO_END_IO(name, bio) name(bio) +#define kc_bio_get_errno(bio) ({ blk_status_to_errno((bio)->bi_status); }) +#else +#define KC_DECLARE_BIO_END_IO(name, bio) name(bio, int _error_arg) +#define kc_bio_get_errno(bio) ({ (int)((void)(bio), _error_arg); }) +#endif + +/* + * v4.13-rc1-6-ge462ec50cb5f + * + * MS_* (mount) flags from should not be used in the kernel + * anymore from 4.x onwards. Instead, we need to use the SB_* (superblock) flags + */ +#ifndef SB_POSIXACL +#define SB_POSIXACL MS_POSIXACL +#define SB_I_VERSION MS_I_VERSION +#endif + +#ifndef KC_CURRENT_TIME_INODE +struct timespec64 kc_current_time(struct inode *inode); +#define current_time kc_current_time +#define kc_timespec timespec +#else +#define kc_timespec timespec64 +#endif + +#ifndef KC_SHRINKER_SHRINK + +#define KC_DEFINE_SHRINKER(name) struct shrinker name +#define KC_INIT_SHRINKER_FUNCS(name, countfn, scanfn) do { \ + __typeof__(name) _shrink = (name); \ + _shrink->count_objects = (countfn); \ + _shrink->scan_objects = (scanfn); \ + _shrink->seeks = DEFAULT_SEEKS; \ +} while (0) + +#define KC_SHRINKER_CONTAINER_OF(ptr, type) container_of(ptr, type, shrinker) +#define KC_REGISTER_SHRINKER(ptr) (register_shrinker(ptr)) +#define KC_UNREGISTER_SHRINKER(ptr) (unregister_shrinker(ptr)) +#define KC_SHRINKER_FN(ptr) (ptr) +#else + +#include +#ifndef SHRINK_STOP +#define SHRINK_STOP (~0UL) +#define SHRINK_EMPTY (~0UL - 1) +#endif + +int kc_shrink_wrapper_fn(struct shrinker *shrink, struct shrink_control *sc); +struct kc_shrinker_wrapper { + unsigned long (*count_objects)(struct shrinker *, struct shrink_control *sc); + unsigned long (*scan_objects)(struct shrinker *, struct shrink_control *sc); + struct shrinker shrink; +}; + +#define KC_DEFINE_SHRINKER(name) struct kc_shrinker_wrapper name; +#define KC_INIT_SHRINKER_FUNCS(name, countfn, scanfn) do { \ + struct kc_shrinker_wrapper *_wrap = (name); \ + _wrap->count_objects = (countfn); \ + _wrap->scan_objects = (scanfn); \ + _wrap->shrink.shrink = kc_shrink_wrapper_fn; \ + _wrap->shrink.seeks = DEFAULT_SEEKS; \ +} while (0) +#define KC_SHRINKER_CONTAINER_OF(ptr, type) container_of(container_of(ptr, struct kc_shrinker_wrapper, shrink), type, shrinker) +#define KC_REGISTER_SHRINKER(ptr) (register_shrinker(ptr.shrink)) +#define KC_UNREGISTER_SHRINKER(ptr) (unregister_shrinker(ptr.shrink)) +#define KC_SHRINKER_FN(ptr) (ptr.shrink) + +#endif /* KC_SHRINKER_SHRINK */ + +#ifdef KC_KERNEL_GETSOCKNAME_ADDRLEN +#include +#include +static inline int kc_kernel_getsockname(struct socket *sock, struct sockaddr *addr) +{ + int addrlen = sizeof(struct sockaddr_in); + int ret = kernel_getsockname(sock, addr, &addrlen); + if (ret == 0 && addrlen != sizeof(struct sockaddr_in)) + return -EAFNOSUPPORT; + else if (ret < 0) + return ret; + + return sizeof(struct sockaddr_in); +} +static inline int kc_kernel_getpeername(struct socket *sock, struct sockaddr *addr) +{ + int addrlen = sizeof(struct sockaddr_in); + int ret = kernel_getpeername(sock, addr, &addrlen); + if (ret == 0 && addrlen != sizeof(struct sockaddr_in)) + return -EAFNOSUPPORT; + else if (ret < 0) + return ret; + + return sizeof(struct sockaddr_in); +} +#else +#define kc_kernel_getsockname(sock, addr) kernel_getsockname(sock, addr) +#define kc_kernel_getpeername(sock, addr) kernel_getpeername(sock, addr) +#endif + +#ifdef KC_SOCK_CREATE_KERN_NET +#define kc_sock_create_kern(family, type, proto, res) sock_create_kern(&init_net, family, type, proto, res) +#else +#define kc_sock_create_kern sock_create_kern +#endif + +#ifndef KC_GENERIC_FILE_BUFFERED_WRITE +ssize_t kc_generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos, loff_t *ppos, + size_t count, ssize_t written); +#define generic_file_buffered_write kc_generic_file_buffered_write +#endif + #endif diff --git a/kmod/src/lock.c b/kmod/src/lock.c index bd726cec..db4c384f 100644 --- a/kmod/src/lock.c +++ b/kmod/src/lock.c @@ -12,7 +12,6 @@ */ #include #include -#include /* a rhel shed.h needed preempt_offset? */ #include #include #include @@ -36,6 +35,7 @@ #include "xattr.h" #include "item.h" #include "omap.h" +#include "util.h" /* * scoutfs uses a lock service to manage item cache consistency between @@ -77,7 +77,7 @@ struct lock_info { bool unmounting; struct rb_root lock_tree; struct rb_root lock_range_tree; - struct shrinker shrinker; + KC_DEFINE_SHRINKER(shrinker); struct list_head lru_list; unsigned long long lru_nr; struct workqueue_struct *workq; @@ -1346,7 +1346,7 @@ void scoutfs_lock_del_coverage(struct super_block *sb, bool scoutfs_lock_protected(struct scoutfs_lock *lock, struct scoutfs_key *key, enum scoutfs_lock_mode mode) { - signed char lock_mode = ACCESS_ONCE(lock->mode); + signed char lock_mode = READ_ONCE(lock->mode); return lock_modes_match(lock_mode, mode) && scoutfs_key_compare_ranges(key, key, @@ -1401,6 +1401,17 @@ static void lock_shrink_worker(struct work_struct *work) } } +static unsigned long lock_count_objects(struct shrinker *shrink, + struct shrink_control *sc) +{ + struct lock_info *linfo = KC_SHRINKER_CONTAINER_OF(shrink, struct lock_info); + struct super_block *sb = linfo->sb; + + scoutfs_inc_counter(sb, lock_count_objects); + + return shrinker_min_long(linfo->lru_nr); +} + /* * Start the shrinking process for locks on the lru. If a lock is on * the lru then it can't have any active users. We don't want to block @@ -1413,21 +1424,18 @@ static void lock_shrink_worker(struct work_struct *work) * mode which will prevent the lock from being freed when the null * response arrives. */ -static int scoutfs_lock_shrink(struct shrinker *shrink, - struct shrink_control *sc) +static unsigned long lock_scan_objects(struct shrinker *shrink, + struct shrink_control *sc) { - struct lock_info *linfo = container_of(shrink, struct lock_info, - shrinker); + struct lock_info *linfo = KC_SHRINKER_CONTAINER_OF(shrink, struct lock_info); struct super_block *sb = linfo->sb; struct scoutfs_lock *lock; struct scoutfs_lock *tmp; - unsigned long nr; + unsigned long freed = 0; + unsigned long nr = sc->nr_to_scan; bool added = false; - int ret; - nr = sc->nr_to_scan; - if (nr == 0) - goto out; + scoutfs_inc_counter(sb, lock_scan_objects); spin_lock(&linfo->lock); @@ -1445,6 +1453,7 @@ restart: lock->request_pending = 1; list_add_tail(&lock->shrink_head, &linfo->shrink_list); added = true; + freed++; scoutfs_inc_counter(sb, lock_shrink_attempted); trace_scoutfs_lock_shrink(sb, lock); @@ -1459,10 +1468,8 @@ restart: if (added) queue_work(linfo->workq, &linfo->shrink_work); -out: - ret = min_t(unsigned long, linfo->lru_nr, INT_MAX); - trace_scoutfs_lock_shrink_exit(sb, sc->nr_to_scan, ret); - return ret; + trace_scoutfs_lock_shrink_exit(sb, sc->nr_to_scan, freed); + return freed; } void scoutfs_free_unused_locks(struct super_block *sb) @@ -1473,7 +1480,7 @@ void scoutfs_free_unused_locks(struct super_block *sb) .nr_to_scan = INT_MAX, }; - linfo->shrinker.shrink(&linfo->shrinker, &sc); + lock_scan_objects(KC_SHRINKER_FN(&linfo->shrinker), &sc); } static void lock_tseq_show(struct seq_file *m, struct scoutfs_tseq_entry *ent) @@ -1580,7 +1587,7 @@ void scoutfs_lock_shutdown(struct super_block *sb) trace_scoutfs_lock_shutdown(sb, linfo); /* stop the shrinker from queueing work */ - unregister_shrinker(&linfo->shrinker); + KC_UNREGISTER_SHRINKER(&linfo->shrinker); flush_work(&linfo->shrink_work); /* cause current and future lock calls to return errors */ @@ -1699,9 +1706,9 @@ int scoutfs_lock_setup(struct super_block *sb) spin_lock_init(&linfo->lock); linfo->lock_tree = RB_ROOT; linfo->lock_range_tree = RB_ROOT; - linfo->shrinker.shrink = scoutfs_lock_shrink; - linfo->shrinker.seeks = DEFAULT_SEEKS; - register_shrinker(&linfo->shrinker); + KC_INIT_SHRINKER_FUNCS(&linfo->shrinker, lock_count_objects, + lock_scan_objects); + KC_REGISTER_SHRINKER(&linfo->shrinker); INIT_LIST_HEAD(&linfo->lru_list); INIT_WORK(&linfo->inv_work, lock_invalidate_worker); INIT_LIST_HEAD(&linfo->inv_list); diff --git a/kmod/src/net.c b/kmod/src/net.c index eab190da..bbcff7db 100644 --- a/kmod/src/net.c +++ b/kmod/src/net.c @@ -549,12 +549,16 @@ static int recvmsg_full(struct socket *sock, void *buf, unsigned len) while (len) { memset(&msg, 0, sizeof(msg)); - msg.msg_iov = (struct iovec *)&kv; - msg.msg_iovlen = 1; msg.msg_flags = MSG_NOSIGNAL; kv.iov_base = buf; kv.iov_len = len; +#ifndef KC_MSGHDR_STRUCT_IOV_ITER + msg.msg_iov = (struct iovec *)&kv; + msg.msg_iovlen = 1; +#else + iov_iter_init(&msg.msg_iter, READ, (struct iovec *)&kv, len, 1); +#endif ret = kernel_recvmsg(sock, &msg, &kv, 1, len, msg.msg_flags); if (ret <= 0) return -ECONNABORTED; @@ -707,12 +711,16 @@ static int sendmsg_full(struct socket *sock, void *buf, unsigned len) while (len) { memset(&msg, 0, sizeof(msg)); - msg.msg_iov = (struct iovec *)&kv; - msg.msg_iovlen = 1; msg.msg_flags = MSG_NOSIGNAL; kv.iov_base = buf; kv.iov_len = len; +#ifndef KC_MSGHDR_STRUCT_IOV_ITER + msg.msg_iov = (struct iovec *)&kv; + msg.msg_iovlen = 1; +#else + iov_iter_init(&msg.msg_iter, WRITE, (struct iovec *)&kv, len, 1); +#endif ret = kernel_sendmsg(sock, &msg, &kv, 1, len); if (ret <= 0) return -ECONNABORTED; @@ -897,7 +905,6 @@ static int sock_opts_and_names(struct scoutfs_net_connection *conn, struct socket *sock) { struct timeval tv; - int addrlen; int optval; int ret; @@ -947,23 +954,18 @@ static int sock_opts_and_names(struct scoutfs_net_connection *conn, if (ret) goto out; - addrlen = sizeof(struct sockaddr_in); - ret = kernel_getsockname(sock, (struct sockaddr *)&conn->sockname, - &addrlen); - if (ret == 0 && addrlen != sizeof(struct sockaddr_in)) - ret = -EAFNOSUPPORT; - if (ret) + ret = kc_kernel_getsockname(sock, (struct sockaddr *)&conn->sockname); + if (ret < 0) goto out; - addrlen = sizeof(struct sockaddr_in); - ret = kernel_getpeername(sock, (struct sockaddr *)&conn->peername, - &addrlen); - if (ret == 0 && addrlen != sizeof(struct sockaddr_in)) - ret = -EAFNOSUPPORT; - if (ret) + ret = kc_kernel_getpeername(sock, (struct sockaddr *)&conn->peername); + if (ret < 0) goto out; + ret = 0; + conn->last_peername = conn->peername; + out: return ret; } @@ -1052,7 +1054,7 @@ static void scoutfs_net_connect_worker(struct work_struct *work) trace_scoutfs_net_connect_work_enter(sb, 0, 0); - ret = sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); + ret = kc_sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); if (ret) goto out; @@ -1453,7 +1455,7 @@ int scoutfs_net_bind(struct super_block *sb, if (WARN_ON_ONCE(conn->sock)) return -EINVAL; - ret = sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); + ret = kc_sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); if (ret) goto out; @@ -1471,20 +1473,18 @@ int scoutfs_net_bind(struct super_block *sb, goto out; ret = kernel_listen(sock, 255); - if (ret) + if (ret < 0) goto out; - addrlen = sizeof(struct sockaddr_in); - ret = kernel_getsockname(sock, (struct sockaddr *)&conn->sockname, - &addrlen); - if (ret == 0 && addrlen != sizeof(struct sockaddr_in)) - ret = -EAFNOSUPPORT; - if (ret) + ret = kc_kernel_getsockname(sock, (struct sockaddr *)&conn->sockname); + if (ret < 0) goto out; + ret = 0; + conn->sock = sock; *sin = conn->sockname; - ret = 0; + out: if (ret < 0 && sock) sock_release(sock); diff --git a/kmod/src/options.c b/kmod/src/options.c index 2a19a9e5..b7a1148b 100644 --- a/kmod/src/options.c +++ b/kmod/src/options.c @@ -169,7 +169,7 @@ static int parse_options(struct super_block *sb, char *options, struct scoutfs_m switch (token) { case Opt_acl: - sb->s_flags |= MS_POSIXACL; + sb->s_flags |= SB_POSIXACL; break; case Opt_data_prealloc_blocks: @@ -203,7 +203,7 @@ static int parse_options(struct super_block *sb, char *options, struct scoutfs_m break; case Opt_noacl: - sb->s_flags &= ~MS_POSIXACL; + sb->s_flags &= ~SB_POSIXACL; break; case Opt_orphan_scan_delay_ms: @@ -327,7 +327,7 @@ int scoutfs_options_show(struct seq_file *seq, struct dentry *root) { struct super_block *sb = root->d_sb; struct scoutfs_mount_options opts; - const bool is_acl = !!(sb->s_flags & MS_POSIXACL); + const bool is_acl = !!(sb->s_flags & SB_POSIXACL); scoutfs_options_read(sb, &opts); diff --git a/kmod/src/quorum.c b/kmod/src/quorum.c index de260b3a..88e5c07a 100644 --- a/kmod/src/quorum.c +++ b/kmod/src/quorum.c @@ -183,7 +183,7 @@ static int create_socket(struct super_block *sb) int addrlen; int ret; - ret = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock); + ret = kc_sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock); if (ret) { scoutfs_err(sb, "quorum couldn't create udp socket: %d", ret); goto out; @@ -243,8 +243,10 @@ static int send_msg_members(struct super_block *sb, int type, u64 term, int only }; struct sockaddr_in sin; struct msghdr mh = { +#ifndef KC_MSGHDR_STRUCT_IOV_ITER .msg_iov = (struct iovec *)&kv, .msg_iovlen = 1, +#endif .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL, .msg_name = &sin, .msg_namelen = sizeof(sin), @@ -266,6 +268,9 @@ static int send_msg_members(struct super_block *sb, int type, u64 term, int only scoutfs_quorum_slot_sin(&qinf->qconf, i, &sin); now = ktime_get(); +#ifdef KC_MSGHDR_STRUCT_IOV_ITER + iov_iter_init(&mh.msg_iter, WRITE, (struct iovec *)&kv, sizeof(qmes), 1); +#endif ret = kernel_sendmsg(qinf->sock, &mh, &kv, 1, kv.iov_len); if (ret != kv.iov_len) failed++; @@ -308,8 +313,10 @@ static int recv_msg(struct super_block *sb, struct quorum_host_msg *msg, .iov_len = sizeof(struct scoutfs_quorum_message), }; struct msghdr mh = { +#ifndef KC_MSGHDR_STRUCT_IOV_ITER .msg_iov = (struct iovec *)&kv, .msg_iovlen = 1, +#endif .msg_flags = MSG_NOSIGNAL, }; @@ -331,6 +338,9 @@ static int recv_msg(struct super_block *sb, struct quorum_host_msg *msg, return ret; } +#ifdef KC_MSGHDR_STRUCT_IOV_ITER + iov_iter_init(&mh.msg_iter, READ, (struct iovec *)&kv, sizeof(struct scoutfs_quorum_message), 1); +#endif ret = kernel_recvmsg(qinf->sock, &mh, &kv, 1, kv.iov_len, mh.msg_flags); if (ret < 0) return ret; @@ -719,11 +729,13 @@ static void scoutfs_quorum_worker(struct work_struct *work) struct sockaddr_in unused; struct quorum_host_msg msg; struct quorum_status qst = {0,}; - struct hb_recording hbr = {{0,},}; + struct hb_recording hbr; bool record_hb; int ret; int err; + memset(&hbr, 0, sizeof(struct hb_recording)); + /* recording votes from slots as native single word bitmap */ BUILD_BUG_ON(SCOUTFS_QUORUM_MAX_SLOTS > BITS_PER_LONG); @@ -771,8 +783,7 @@ static void scoutfs_quorum_worker(struct work_struct *work) msg.type = SCOUTFS_QUORUM_MSG_INVALID; trace_scoutfs_quorum_loop(sb, qst.role, qst.term, qst.vote_for, - qst.vote_bits, - ktime_to_timespec64(qst.timeout)); + qst.vote_bits, ktime_to_ns(qst.timeout)); /* receiving greater terms resets term, becomes follower */ if (msg.type != SCOUTFS_QUORUM_MSG_INVALID && diff --git a/kmod/src/scoutfs_trace.h b/kmod/src/scoutfs_trace.h index 3eb735d5..96feeedd 100644 --- a/kmod/src/scoutfs_trace.h +++ b/kmod/src/scoutfs_trace.h @@ -2024,9 +2024,9 @@ DEFINE_EVENT(scoutfs_quorum_message_class, scoutfs_quorum_recv_message, TRACE_EVENT(scoutfs_quorum_loop, TP_PROTO(struct super_block *sb, int role, u64 term, int vote_for, - unsigned long vote_bits, struct timespec64 timeout), + unsigned long vote_bits, unsigned long long nsecs), - TP_ARGS(sb, role, term, vote_for, vote_bits, timeout), + TP_ARGS(sb, role, term, vote_for, vote_bits, nsecs), TP_STRUCT__entry( SCSB_TRACE_FIELDS @@ -2035,8 +2035,7 @@ TRACE_EVENT(scoutfs_quorum_loop, __field(int, vote_for) __field(unsigned long, vote_bits) __field(unsigned long, vote_count) - __field(unsigned long long, timeout_sec) - __field(int, timeout_nsec) + __field(unsigned long long, nsecs) ), TP_fast_assign( @@ -2046,14 +2045,13 @@ TRACE_EVENT(scoutfs_quorum_loop, __entry->vote_for = vote_for; __entry->vote_bits = vote_bits; __entry->vote_count = hweight_long(vote_bits); - __entry->timeout_sec = timeout.tv_sec; - __entry->timeout_nsec = timeout.tv_nsec; + __entry->nsecs = nsecs; ), - TP_printk(SCSBF" term %llu role %d vote_for %d vote_bits 0x%lx vote_count %lu timeout %llu.%u", + TP_printk(SCSBF" term %llu role %d vote_for %d vote_bits 0x%lx vote_count %lu timeout %llu", SCSB_TRACE_ARGS, __entry->term, __entry->role, __entry->vote_for, __entry->vote_bits, __entry->vote_count, - __entry->timeout_sec, __entry->timeout_nsec) + __entry->nsecs) ); TRACE_EVENT(scoutfs_trans_seq_last, diff --git a/kmod/src/server.c b/kmod/src/server.c index e00d1bca..c8f9be8d 100644 --- a/kmod/src/server.c +++ b/kmod/src/server.c @@ -4464,7 +4464,7 @@ void scoutfs_server_stop_wait(struct super_block *sb) DECLARE_SERVER_INFO(sb, server); stop_server(server); - flush_work_sync(&server->work); + flush_work(&server->work); } int scoutfs_server_setup(struct super_block *sb) diff --git a/kmod/src/srch.c b/kmod/src/srch.c index ebf113e6..36385ad9 100644 --- a/kmod/src/srch.c +++ b/kmod/src/srch.c @@ -1747,7 +1747,7 @@ static int compact_logs(struct super_block *sb, goto out; } page->private = 0; - list_add_tail(&page->list, &pages); + list_add_tail(&page->lru, &pages); nr_pages++; scoutfs_inc_counter(sb, srch_compact_log_page); } @@ -1800,7 +1800,7 @@ static int compact_logs(struct super_block *sb, /* sort page entries and reset private for _next */ i = 0; - list_for_each_entry(page, &pages, list) { + list_for_each_entry(page, &pages, lru) { args[i++] = page; if (atomic_read(&srinf->shutdown)) { @@ -1821,7 +1821,7 @@ static int compact_logs(struct super_block *sb, goto out; /* make sure we finished all the pages */ - list_for_each_entry(page, &pages, list) { + list_for_each_entry(page, &pages, lru) { sre = page_priv_sre(page); if (page->private < SRES_PER_PAGE && sre->ino != 0) { ret = -ENOSPC; @@ -1834,8 +1834,8 @@ static int compact_logs(struct super_block *sb, out: scoutfs_block_put(sb, bl); vfree(args); - list_for_each_entry_safe(page, tmp, &pages, list) { - list_del(&page->list); + list_for_each_entry_safe(page, tmp, &pages, lru) { + list_del(&page->lru); __free_page(page); } diff --git a/kmod/src/super.c b/kmod/src/super.c index 10016a74..434aace6 100644 --- a/kmod/src/super.c +++ b/kmod/src/super.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -178,7 +179,7 @@ static void scoutfs_put_super(struct super_block *sb) /* * Wait for invalidation and iput to finish with any lingering * inode references that escaped the evict_inodes in - * generic_shutdown_super. MS_ACTIVE is clear so final iput + * generic_shutdown_super. SB_ACTIVE is clear so final iput * will always evict. */ scoutfs_lock_flush_invalidate(sb); @@ -485,7 +486,7 @@ static int scoutfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_d_op = &scoutfs_dentry_ops; sb->s_export_op = &scoutfs_export_ops; sb->s_xattr = scoutfs_xattr_handlers; - sb->s_flags |= MS_I_VERSION | MS_POSIXACL; + sb->s_flags |= SB_I_VERSION | SB_POSIXACL; sb->s_time_gran = 1; /* btree blocks use long lived bh->b_data refs */ @@ -674,14 +675,14 @@ out: teardown_module(); return ret; } -module_init(scoutfs_module_init) +module_init(scoutfs_module_init); static void __exit scoutfs_module_exit(void) { unregister_filesystem(&scoutfs_fs_type); teardown_module(); } -module_exit(scoutfs_module_exit) +module_exit(scoutfs_module_exit); MODULE_AUTHOR("Zach Brown "); MODULE_LICENSE("GPL"); diff --git a/kmod/src/tseq.c b/kmod/src/tseq.c index 781d00f3..b4b07f34 100644 --- a/kmod/src/tseq.c +++ b/kmod/src/tseq.c @@ -46,6 +46,23 @@ static struct scoutfs_tseq_entry *tseq_rb_next(struct scoutfs_tseq_entry *ent) return rb_entry(node, struct scoutfs_tseq_entry, node); } +#ifdef KC_RB_TREE_AUGMENTED_COMPUTE_MAX +static bool tseq_compute_total(struct scoutfs_tseq_entry *ent, bool exit) +{ + loff_t total = 1 + tseq_node_total(ent->node.rb_left) + + tseq_node_total(ent->node.rb_right); + + if (exit && ent->total == total) + return true; + + ent->total = total; + return false; +} + +RB_DECLARE_CALLBACKS(static, tseq_rb_callbacks, struct scoutfs_tseq_entry, + node, total, tseq_compute_total); +#else + static loff_t tseq_compute_total(struct scoutfs_tseq_entry *ent) { return 1 + tseq_node_total(ent->node.rb_left) + @@ -53,7 +70,8 @@ static loff_t tseq_compute_total(struct scoutfs_tseq_entry *ent) } RB_DECLARE_CALLBACKS(static, tseq_rb_callbacks, struct scoutfs_tseq_entry, - node, loff_t, total, tseq_compute_total) + node, loff_t, total, tseq_compute_total); +#endif void scoutfs_tseq_tree_init(struct scoutfs_tseq_tree *tree, scoutfs_tseq_show_t show) diff --git a/kmod/src/util.h b/kmod/src/util.h index 20d9db79..7ca9d3eb 100644 --- a/kmod/src/util.h +++ b/kmod/src/util.h @@ -17,4 +17,15 @@ static inline void down_write_two(struct rw_semaphore *a, down_write_nested(b, SINGLE_DEPTH_NESTING); } +/* + * When returning shrinker counts from scan_objects, we should steer + * clear of the magic SHRINK_STOP and SHRINK_EMPTY values, which are near + * ~0UL values. Hence, we cap count to ~0L, which is arbitarily high + * enough to avoid it. + */ +static inline long shrinker_min_long(long count) +{ + return min(count, LONG_MAX); +} + #endif diff --git a/kmod/src/xattr.c b/kmod/src/xattr.c index abdf8e0b..fa418b8c 100644 --- a/kmod/src/xattr.c +++ b/kmod/src/xattr.c @@ -773,7 +773,7 @@ int scoutfs_xattr_set_locked(struct inode *inode, const char *name, size_t name_ /* XXX do these want i_mutex or anything? */ inode_inc_iversion(inode); - inode->i_ctime = CURRENT_TIME; + inode->i_ctime = current_time(inode); ret = 0; out: @@ -850,6 +850,7 @@ unlock: return ret; } +#ifndef KC_XATTR_STRUCT_XATTR_HANDLER /* * Future kernels have this amazing hack to rewind the name to get the * skipped prefix. We're back in the stone ages without the handler @@ -857,22 +858,41 @@ unlock: * compat hook to either call the kernel's xattr_full_name(handler), or * our hack to use the flags as the prefix length. */ -static const char *full_name_hack(void *handler, const char *name, int len) +static const char *full_name_hack(const char *name, int len) { return name - len; } +#endif -static int scoutfs_xattr_get_handler(struct dentry *dentry, const char *name, - void *value, size_t size, int handler_flags) +static int scoutfs_xattr_get_handler +#ifdef KC_XATTR_STRUCT_XATTR_HANDLER + (const struct xattr_handler *handler, struct dentry *dentry, + struct inode *inode, const char *name, void *value, + size_t size) { - name = full_name_hack(NULL, name, handler_flags); + name = xattr_full_name(handler, name); +#else + (struct dentry *dentry, const char *name, + void *value, size_t size, int handler_flags) +{ + name = full_name_hack(name, handler_flags); +#endif return scoutfs_xattr_get(dentry, name, value, size); } -static int scoutfs_xattr_set_handler(struct dentry *dentry, const char *name, - const void *value, size_t size, int flags, int handler_flags) +static int scoutfs_xattr_set_handler +#ifdef KC_XATTR_STRUCT_XATTR_HANDLER + (const struct xattr_handler *handler, struct dentry *dentry, + struct inode *inode, const char *name, const void *value, + size_t size, int flags) { - name = full_name_hack(NULL, name, handler_flags); + name = xattr_full_name(handler, name); +#else + (struct dentry *dentry, const char *name, + const void *value, size_t size, int flags, int handler_flags) +{ + name = full_name_hack(name, handler_flags); +#endif return scoutfs_xattr_set(dentry, name, value, size, flags); } @@ -905,14 +925,22 @@ static const struct xattr_handler scoutfs_xattr_security_handler = { }; static const struct xattr_handler scoutfs_xattr_acl_access_handler = { +#ifdef KC_XATTR_HANDLER_NAME + .name = XATTR_NAME_POSIX_ACL_ACCESS, +#else .prefix = XATTR_NAME_POSIX_ACL_ACCESS, +#endif .flags = ACL_TYPE_ACCESS, .get = scoutfs_acl_get_xattr, .set = scoutfs_acl_set_xattr, }; static const struct xattr_handler scoutfs_xattr_acl_default_handler = { +#ifdef KC_XATTR_HANDLER_NAME + .name = XATTR_NAME_POSIX_ACL_DEFAULT, +#else .prefix = XATTR_NAME_POSIX_ACL_DEFAULT, +#endif .flags = ACL_TYPE_DEFAULT, .get = scoutfs_acl_get_xattr, .set = scoutfs_acl_set_xattr, diff --git a/tests/funcs/filter.sh b/tests/funcs/filter.sh index 766ab562..2ff9d286 100644 --- a/tests/funcs/filter.sh +++ b/tests/funcs/filter.sh @@ -85,5 +85,8 @@ t_filter_dmesg() re="$re|scoutfs .* error.*server failed to bind to.*" re="$re|scoutfs .* critical transaction commit failure.*" + # change-devices causes loop device resizing + re="$re|loop[0-9].* detected capacity change from.*" + egrep -v "($re)" } diff --git a/tests/golden/basic-posix-consistency b/tests/golden/basic-posix-consistency index 2c5fb6ab..3bf10ae1 100644 --- a/tests/golden/basic-posix-consistency +++ b/tests/golden/basic-posix-consistency @@ -47,7 +47,7 @@ four --- dir within dir --- overwrite file --- can't overwrite non-empty dir -mv: cannot move ‘/mnt/test/test/basic-posix-consistency/dir/c/clobber’ to ‘/mnt/test/test/basic-posix-consistency/dir/a/dir’: Directory not empty +mv: cannot move '/mnt/test/test/basic-posix-consistency/dir/c/clobber' to '/mnt/test/test/basic-posix-consistency/dir/a/dir': Directory not empty --- can overwrite empty dir --- can rename into root == path resoluion diff --git a/tests/golden/inode-deletion b/tests/golden/inode-deletion index 1e36ff37..f586d563 100644 --- a/tests/golden/inode-deletion +++ b/tests/golden/inode-deletion @@ -17,7 +17,7 @@ ino not found in dseq index mount 0 contents after mount 1 rm: contents ino found in dseq index ino found in dseq index -stat: cannot stat ‘/mnt/test/test/inode-deletion/file’: No such file or directory +stat: cannot stat '/mnt/test/test/inode-deletion/file': No such file or directory ino not found in dseq index ino not found in dseq index == lots of deletions use one open map diff --git a/tests/golden/offline-extent-waiting b/tests/golden/offline-extent-waiting index 5b4af2d2..9ea47d4f 100644 --- a/tests/golden/offline-extent-waiting +++ b/tests/golden/offline-extent-waiting @@ -20,10 +20,10 @@ offline waiting should now have two known entries: data_wait_err found 2 waiters. offline waiting should now have 0 known entries: 0 -dd: error reading ‘/mnt/test/test/offline-extent-waiting/dir/file’: Input/output error +dd: error reading '/mnt/test/test/offline-extent-waiting/dir/file': Input/output error 0+0 records in 0+0 records out -dd: error reading ‘/mnt/test/test/offline-extent-waiting/dir/file’: Input/output error +dd: error reading '/mnt/test/test/offline-extent-waiting/dir/file': Input/output error 0+0 records in 0+0 records out offline waiting should be empty again: diff --git a/tests/golden/xfstests b/tests/golden/xfstests index cc382847..16824a05 100644 --- a/tests/golden/xfstests +++ b/tests/golden/xfstests @@ -241,7 +241,6 @@ generic/312 generic/314 generic/316 generic/317 -generic/318 generic/324 generic/326 generic/327 diff --git a/tests/run-tests.sh b/tests/run-tests.sh index a9613687..1202cada 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -1,5 +1,8 @@ #!/usr/bin/bash +# Force system tools to use ASCII quotes +export LC_ALL=C + # # XXX # - could have helper functions for waiting for pids diff --git a/tests/src/handle_fsetxattr.c b/tests/src/handle_fsetxattr.c index 5d063552..b39d7db5 100644 --- a/tests/src/handle_fsetxattr.c +++ b/tests/src/handle_fsetxattr.c @@ -48,7 +48,7 @@ struct our_handle { static void exit_usage(void) { printf(" -h/-? output this usage message and exit\n" - " -e keep trying on enoent, consider success an error\n" + " -e keep trying on enoent and estale, consider success an error\n" " -i 64bit inode number for handle open, can be multiple\n" " -m scoutfs mount path string for ioctl fd\n" " -n optional xattr name string, defaults to \""DEFAULT_NAME"\"\n" @@ -149,7 +149,7 @@ int main(int argc, char **argv) fd = open_by_handle_at(mntfd, &handle.handle, O_RDWR); if (fd == -1) { - if (!enoent_success_err || errno != ENOENT) { + if (!enoent_success_err || ( errno != ENOENT && errno != ESTALE )) { perror("open_by_handle_at"); return 1; } diff --git a/tests/tests/data-prealloc.sh b/tests/tests/data-prealloc.sh index fdc09f8e..6c798246 100644 --- a/tests/tests/data-prealloc.sh +++ b/tests/tests/data-prealloc.sh @@ -95,7 +95,7 @@ print_logical_extents() } print $2, $6, flags } - ' + ' | sed 's/last,eof/eof/' } t_save_all_sysfs_mount_options data_prealloc_blocks diff --git a/tests/tests/inode-deletion.sh b/tests/tests/inode-deletion.sh index 5af97354..9324d035 100644 --- a/tests/tests/inode-deletion.sh +++ b/tests/tests/inode-deletion.sh @@ -72,7 +72,7 @@ check_ino_index "$ino" "$dseq" "$T_M0" check_ino_index "$ino" "$dseq" "$T_M1" exec {FD}>&- # close # we know that revalidating will unhash the remote dentry -stat "$T_D0/file" 2>&1 | t_filter_fs +stat "$T_D0/file" 2>&1 | sed 's/cannot statx/cannot stat/' | t_filter_fs check_ino_index "$ino" "$dseq" "$T_M0" check_ino_index "$ino" "$dseq" "$T_M1" diff --git a/tests/tests/setattr_more.sh b/tests/tests/setattr_more.sh index af05b89f..c15a507a 100644 --- a/tests/tests/setattr_more.sh +++ b/tests/tests/setattr_more.sh @@ -55,10 +55,17 @@ scoutfs setattr -t 67305985.999999999 -V 1 -s 1 "$FILE" 2>&1 | t_filter_fs TZ=GMT stat -c "%z" "$FILE" rm "$FILE" +# +# With e2fsprogs-v1.42.10-10-g29758d2f, the output of filefrag 'flags' changes +# significantly. First, the _LAST flag is now output. Second, the 'unknown' +# flag is now printed out as 'unknown_loc'. To compensate for this, we check +# and replace the "correct" output for new versions here with the expected +# value. +# echo "== large offline extents are created" touch "$FILE" scoutfs setattr -V 1 -o -s $((10007 * 4096)) "$FILE" 2>&1 | t_filter_fs -filefrag -v -b4096 "$FILE" 2>&1 | t_filter_fs +filefrag -v -b4096 "$FILE" 2>&1 | sed 's/last,unknown_loc,eof$/unknown,eof/' | t_filter_fs rm "$FILE" # had a bug where we were creating extents that were too long diff --git a/tests/tests/simple-xattr-unit.sh b/tests/tests/simple-xattr-unit.sh index 31cc5878..b761f708 100644 --- a/tests/tests/simple-xattr-unit.sh +++ b/tests/tests/simple-xattr-unit.sh @@ -27,15 +27,9 @@ test_xattr_lengths() { echo "key len $name_len val len $val_len" >> "$T_TMP.log" setfattr -n $name -v \"$val\" "$FILE" - # grep has trouble with enormous args? so we dump the - # name=value to a file and compare with a known good file - getfattr -d --absolute-names "$FILE" | grep "$name" > "$T_TMP.got" + getfattr -d --only-values --absolute-names "$FILE" -n "$name" > "$T_TMP.got" + echo -n "$val" > "$T_TMP.good" - if [ $val_len == 0 ]; then - echo "$name" > "$T_TMP.good" - else - echo "$name=\"$val\"" > "$T_TMP.good" - fi cmp "$T_TMP.good" "$T_TMP.got" || \ t_fail "cmp failed name len $name_len val len $val_len" diff --git a/tests/tests/xfstests.sh b/tests/tests/xfstests.sh index 13349368..25e66bbe 100644 --- a/tests/tests/xfstests.sh +++ b/tests/tests/xfstests.sh @@ -75,6 +75,7 @@ generic/215 # mmap missing generic/246 # mmap missing generic/247 # mmap missing generic/248 # mmap missing +generic/318 # can't support user namespaces until v5.11 generic/321 # requires selinux enabled for '+' in ls? generic/325 # mmap missing generic/338 # BUG_ON update inode error handling diff --git a/utils/scoutfs-utils.spec.in b/utils/scoutfs-utils.spec.in index 4568b73f..fb24b812 100644 --- a/utils/scoutfs-utils.spec.in +++ b/utils/scoutfs-utils.spec.in @@ -61,7 +61,7 @@ install -m 644 -D fenced/scoutfs-fenced.conf.example $RPM_BUILD_ROOT%{_sysconfdi %files %defattr(644,root,root,755) %{_mandir}/man*/scoutfs*.gz -%{_unitdir}/scoutfs-fenced.service +/%{_unitdir}/scoutfs-fenced.service %{_sysconfdir}/scoutfs %defattr(755,root,root,755) %{_sbindir}/scoutfs