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 <zab@versity.com>
This commit is contained in:
Zach Brown
2021-08-25 10:14:38 -07:00
parent 0c3026a2b7
commit a4f5293e78
5 changed files with 37 additions and 4 deletions
+13 -1
View File
@@ -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;
+2 -1
View File
@@ -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
+8
View File
@@ -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.
+1
View File
@@ -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);
+13 -2
View File
@@ -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);
}