summaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorZhang Yi <yi.zhang@huawei.com>2021-06-10 19:24:37 +0800
committerTheodore Ts'o <tytso@mit.edu>2021-06-24 10:54:49 -0400
commit4ba3fcdde7e36af93610ceb3cc38365b14539865 (patch)
treecfe6b1077ca18cc5614a43446bd015e4f0c93afa /fs/ext4
parent214eb5a4d8a2032fb9f0711d1b202eb88ee02920 (diff)
downloadlinux-4ba3fcdde7e36af93610ceb3cc38365b14539865.tar.bz2
jbd2,ext4: add a shrinker to release checkpointed buffers
Current metadata buffer release logic in bdev_try_to_free_page() have a lot of use-after-free issues when umount filesystem concurrently, and it is difficult to fix directly because ext4 is the only user of s_op->bdev_try_to_free_page callback and we may have to add more special refcount or lock that is only used by ext4 into the common vfs layer, which is unacceptable. One better solution is remove the bdev_try_to_free_page callback, but the real problem is we cannot easily release journal_head on the checkpointed buffer, so try_to_free_buffers() cannot release buffers and page under memory pressure, which is more likely to trigger out-of-memory. So we cannot remove the callback directly before we find another way to release journal_head. This patch introduce a shrinker to free journal_head on the checkpointed transaction. After the journal_head got freed, try_to_free_buffers() could free buffer properly. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Suggested-by: Jan Kara <jack@suse.cz> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://lore.kernel.org/r/20210610112440.3438139-6-yi.zhang@huawei.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/super.c8
1 files changed, 8 insertions, 0 deletions
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index ad3919dbd49e..7ee2e21537e0 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1174,6 +1174,7 @@ static void ext4_put_super(struct super_block *sb)
ext4_unregister_sysfs(sb);
if (sbi->s_journal) {
+ jbd2_journal_unregister_shrinker(sbi->s_journal);
aborted = is_journal_aborted(sbi->s_journal);
err = jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL;
@@ -5186,6 +5187,7 @@ failed_mount_wq:
sbi->s_ea_block_cache = NULL;
if (sbi->s_journal) {
+ jbd2_journal_unregister_shrinker(sbi->s_journal);
jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL;
}
@@ -5511,6 +5513,12 @@ static int ext4_load_journal(struct super_block *sb,
ext4_commit_super(sb);
}
+ err = jbd2_journal_register_shrinker(journal);
+ if (err) {
+ EXT4_SB(sb)->s_journal = NULL;
+ goto err_out;
+ }
+
return 0;
err_out: