diff options
author | Andreas Gruenbacher <agruenba@redhat.com> | 2023-01-26 22:23:40 +0300 |
---|---|---|
committer | Andreas Gruenbacher <agruenba@redhat.com> | 2023-02-01 00:40:24 +0300 |
commit | b88beb9a246f7506778f8680ee9627cd85262ba4 (patch) | |
tree | 8b93e29818c8200eed3aa82a4a8203118c9b8e48 /fs/gfs2 | |
parent | 6b388abc33998330c6fe55a712d61be888fd7b67 (diff) | |
download | linux-b88beb9a246f7506778f8680ee9627cd85262ba4.tar.xz |
gfs2: Evict inodes cooperatively
Add a gfs2_evict_inodes() helper that evicts inodes cooperatively across
the cluster. This avoids running into timeouts during unmount
unnecessarily.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Diffstat (limited to 'fs/gfs2')
-rw-r--r-- | fs/gfs2/incore.h | 1 | ||||
-rw-r--r-- | fs/gfs2/ops_fstype.c | 51 | ||||
-rw-r--r-- | fs/gfs2/super.c | 7 |
3 files changed, 59 insertions, 0 deletions
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index d35bb49d1d3f..79485329118b 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -607,6 +607,7 @@ enum { SDF_WITHDRAW_RECOVERY = 14, /* Wait for journal recovery when we are withdrawing */ SDF_DEACTIVATING = 15, + SDF_EVICTING = 16, }; enum gfs2_freeze_state { diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 9db376950014..6de901c3b89b 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1728,6 +1728,55 @@ static int gfs2_meta_init_fs_context(struct fs_context *fc) return 0; } +/** + * gfs2_evict_inodes - evict inodes cooperatively + * @sb: the superblock + * + * When evicting an inode with a zero link count, we are trying to upgrade the + * inode's iopen glock from SH to EX mode in order to determine if we can + * delete the inode. The other nodes are supposed to evict the inode from + * their caches if they can, and to poke the inode's inode glock if they cannot + * do so. Either behavior allows gfs2_upgrade_iopen_glock() to proceed + * quickly, but if the other nodes are not cooperating, the lock upgrading + * attempt will time out. Since inodes are evicted sequentially, this can add + * up quickly. + * + * Function evict_inodes() tries to keep the s_inode_list_lock list locked over + * a long time, which prevents other inodes from being evicted concurrently. + * This precludes the cooperative behavior we are looking for. This special + * version of evict_inodes() avoids that. + * + * Modeled after drop_pagecache_sb(). + */ +static void gfs2_evict_inodes(struct super_block *sb) +{ + struct inode *inode, *toput_inode = NULL; + struct gfs2_sbd *sdp = sb->s_fs_info; + + set_bit(SDF_EVICTING, &sdp->sd_flags); + + spin_lock(&sb->s_inode_list_lock); + list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { + spin_lock(&inode->i_lock); + if ((inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) && + !need_resched()) { + spin_unlock(&inode->i_lock); + continue; + } + atomic_inc(&inode->i_count); + spin_unlock(&inode->i_lock); + spin_unlock(&sb->s_inode_list_lock); + + iput(toput_inode); + toput_inode = inode; + + cond_resched(); + spin_lock(&sb->s_inode_list_lock); + } + spin_unlock(&sb->s_inode_list_lock); + iput(toput_inode); +} + static void gfs2_kill_sb(struct super_block *sb) { struct gfs2_sbd *sdp = sb->s_fs_info; @@ -1744,6 +1793,8 @@ static void gfs2_kill_sb(struct super_block *sb) sdp->sd_master_dir = NULL; shrink_dcache_sb(sb); + gfs2_evict_inodes(sb); + /* * Flush and then drain the delete workqueue here (via * destroy_workqueue()) to ensure that any delete work that diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 63512bb9d6f0..52c3502de58c 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -935,6 +935,7 @@ static int gfs2_statfs(struct dentry *dentry, struct kstatfs *buf) static int gfs2_drop_inode(struct inode *inode) { struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); if (inode->i_nlink && gfs2_holder_initialized(&ip->i_iopen_gh)) { @@ -959,6 +960,12 @@ static int gfs2_drop_inode(struct inode *inode) return 0; } + /* + * No longer cache inodes when trying to evict them all. + */ + if (test_bit(SDF_EVICTING, &sdp->sd_flags)) + return 1; + return generic_drop_inode(inode); } |