From a4f5293e78acb40e13de1f71388dd5de83ff6b2b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 18 Aug 2021 16:02:01 -0700 Subject: [PATCH] Flush invalidate and iput inode references We can be performing final deletion as inodes are evicted during unmount. We have to keep full locking, transactions, and networking up and running for the evict_inodes() call in generic_shutdown_super(). Unfortunately, this means that workers can be using inode references during evict_inodes() which prevents them from being evicted. Those workers can then remain running as we tear down the system, causing crashes and deadlocks as the final iputs try to use resources that have been destroyed. The fix is to first properly stop orphan scanning, which can instantiate new cached inodes, up before the call to kill_block_super ends up trying to evict all inodes. Then we just need to wait for any pending iput and invalidate work to finish and perform the final iput, which will always evict because generic_shutdown_super has cleared MS_ACTIVE. Signed-off-by: Zach Brown --- kmod/src/inode.c | 14 +++++++++++++- kmod/src/inode.h | 3 ++- kmod/src/lock.c | 8 ++++++++ kmod/src/lock.h | 1 + kmod/src/super.c | 15 +++++++++++++-- 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/kmod/src/inode.c b/kmod/src/inode.c index 881969a9..e395a36d 100644 --- a/kmod/src/inode.c +++ b/kmod/src/inode.c @@ -1972,7 +1972,11 @@ void scoutfs_inode_start(struct super_block *sb) schedule_orphan_dwork(inf); } -void scoutfs_inode_stop(struct super_block *sb) +/* + * Orphan scanning can instantiate inodes. We shut it down before + * calling into the vfs to tear down dentries and inodes during unmount. + */ +void scoutfs_inode_orphan_stop(struct super_block *sb) { DECLARE_INODE_SB_INFO(sb, inf); @@ -1982,6 +1986,14 @@ void scoutfs_inode_stop(struct super_block *sb) } } +void scoutfs_inode_flush_iput(struct super_block *sb) +{ + DECLARE_INODE_SB_INFO(sb, inf); + + if (inf) + flush_work(&inf->iput_work); +} + void scoutfs_inode_destroy(struct super_block *sb) { struct inode_sb_info *inf = SCOUTFS_SB(sb)->inode_sb_info; diff --git a/kmod/src/inode.h b/kmod/src/inode.h index 60e14f97..417a1825 100644 --- a/kmod/src/inode.h +++ b/kmod/src/inode.h @@ -133,7 +133,8 @@ int scoutfs_inode_init(void); int scoutfs_inode_setup(struct super_block *sb); void scoutfs_inode_start(struct super_block *sb); -void scoutfs_inode_stop(struct super_block *sb); +void scoutfs_inode_orphan_stop(struct super_block *sb); +void scoutfs_inode_flush_iput(struct super_block *sb); void scoutfs_inode_destroy(struct super_block *sb); #endif diff --git a/kmod/src/lock.c b/kmod/src/lock.c index 222f2461..e85fa7ff 100644 --- a/kmod/src/lock.c +++ b/kmod/src/lock.c @@ -1527,6 +1527,14 @@ void scoutfs_lock_unmount_begin(struct super_block *sb) } } +void scoutfs_lock_flush_invalidate(struct super_block *sb) +{ + DECLARE_LOCK_INFO(sb, linfo); + + if (linfo) + flush_work(&linfo->inv_work); +} + /* * The caller is going to be shutting down transactions and the client. * We need to make sure that locking won't call either after we return. diff --git a/kmod/src/lock.h b/kmod/src/lock.h index 9dc52441..c1848cf9 100644 --- a/kmod/src/lock.h +++ b/kmod/src/lock.h @@ -105,6 +105,7 @@ void scoutfs_free_unused_locks(struct super_block *sb); int scoutfs_lock_setup(struct super_block *sb); void scoutfs_lock_unmount_begin(struct super_block *sb); +void scoutfs_lock_flush_invalidate(struct super_block *sb); void scoutfs_lock_shutdown(struct super_block *sb); void scoutfs_lock_destroy(struct super_block *sb); diff --git a/kmod/src/super.c b/kmod/src/super.c index a54e8cf9..f9fcc133 100644 --- a/kmod/src/super.c +++ b/kmod/src/super.c @@ -255,7 +255,16 @@ static void scoutfs_put_super(struct super_block *sb) trace_scoutfs_put_super(sb); - scoutfs_inode_stop(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 + * will always evict. + */ + scoutfs_lock_flush_invalidate(sb); + scoutfs_inode_flush_iput(sb); + WARN_ON_ONCE(!list_empty(&sb->s_inodes)); + scoutfs_forest_stop(sb); scoutfs_srch_destroy(sb); @@ -668,8 +677,10 @@ static void scoutfs_kill_sb(struct super_block *sb) smp_wmb(); } - if (SCOUTFS_HAS_SBI(sb)) + if (SCOUTFS_HAS_SBI(sb)) { + scoutfs_inode_orphan_stop(sb); scoutfs_lock_unmount_begin(sb); + } kill_block_super(sb); }