Jan Kara 449ab7
From 6fed83957f21eff11c8496e9f24253b03d2bc1dc Mon Sep 17 00:00:00 2001
Jan Kara 449ab7
From: Jeffle Xu <jefflexu@linux.alibaba.com>
Jan Kara 449ab7
Date: Mon, 23 Aug 2021 14:13:58 +0800
Jan Kara 449ab7
Subject: [PATCH] ext4: fix reserved space counter leakage
Jan Kara 449ab7
Git-commit: 6fed83957f21eff11c8496e9f24253b03d2bc1dc
Jan Kara 449ab7
Patch-mainline: v5.15-rc4
Jan Kara 449ab7
References: bsc#1191450
Jan Kara 449ab7
Jan Kara 449ab7
When ext4_insert_delayed block receives and recovers from an error from
Jan Kara 449ab7
ext4_es_insert_delayed_block(), e.g., ENOMEM, it does not release the
Jan Kara 449ab7
space it has reserved for that block insertion as it should. One effect
Jan Kara 449ab7
of this bug is that s_dirtyclusters_counter is not decremented and
Jan Kara 449ab7
remains incorrectly elevated until the file system has been unmounted.
Jan Kara 449ab7
This can result in premature ENOSPC returns and apparent loss of free
Jan Kara 449ab7
space.
Jan Kara 449ab7
Jan Kara 449ab7
Another effect of this bug is that
Jan Kara 449ab7
/sys/fs/ext4/<dev>/delayed_allocation_blocks can remain non-zero even
Jan Kara 449ab7
after syncfs has been executed on the filesystem.
Jan Kara 449ab7
Jan Kara 449ab7
Besides, add check for s_dirtyclusters_counter when inode is going to be
Jan Kara 449ab7
evicted and freed. s_dirtyclusters_counter can still keep non-zero until
Jan Kara 449ab7
inode is written back in .evict_inode(), and thus the check is delayed
Jan Kara 449ab7
to .destroy_inode().
Jan Kara 449ab7
Jan Kara 449ab7
Fixes: 51865fda28e5 ("ext4: let ext4 maintain extent status tree")
Jan Kara 449ab7
Cc: stable@kernel.org
Jan Kara 449ab7
Suggested-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Jan Kara 449ab7
Signed-off-by: Jeffle Xu <jefflexu@linux.alibaba.com>
Jan Kara 449ab7
Reviewed-by: Eric Whitney <enwlinux@gmail.com>
Jan Kara 449ab7
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Jan Kara 449ab7
Link: https://lore.kernel.org/r/20210823061358.84473-1-jefflexu@linux.alibaba.com
Jan Kara 449ab7
Acked-by: Jan Kara <jack@suse.cz>
Jan Kara 449ab7
Jan Kara 449ab7
---
Jan Kara 449ab7
 fs/ext4/inode.c | 5 +++++
Jan Kara 449ab7
 fs/ext4/super.c | 6 ++++++
Jan Kara 449ab7
 2 files changed, 11 insertions(+)
Jan Kara 449ab7
Jan Kara 449ab7
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
Jan Kara 449ab7
index 2a076d236ba1..9df1ab070fa5 100644
Jan Kara 449ab7
--- a/fs/ext4/inode.c
Jan Kara 449ab7
+++ b/fs/ext4/inode.c
Jan Kara 449ab7
@@ -1628,6 +1628,7 @@ static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
Jan Kara 449ab7
 	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
Jan Kara 449ab7
 	int ret;
Jan Kara 449ab7
 	bool allocated = false;
Jan Kara 449ab7
+	bool reserved = false;
Jan Kara 449ab7
 
Jan Kara 449ab7
 	/*
Jan Kara 449ab7
 	 * If the cluster containing lblk is shared with a delayed,
Jan Kara 449ab7
@@ -1644,6 +1645,7 @@ static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
Jan Kara 449ab7
 		ret = ext4_da_reserve_space(inode);
Jan Kara 449ab7
 		if (ret != 0)   /* ENOSPC */
Jan Kara 449ab7
 			goto errout;
Jan Kara 449ab7
+		reserved = true;
Jan Kara 449ab7
 	} else {   /* bigalloc */
Jan Kara 449ab7
 		if (!ext4_es_scan_clu(inode, &ext4_es_is_delonly, lblk)) {
Jan Kara 449ab7
 			if (!ext4_es_scan_clu(inode,
Jan Kara 449ab7
@@ -1656,6 +1658,7 @@ static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
Jan Kara 449ab7
 					ret = ext4_da_reserve_space(inode);
Jan Kara 449ab7
 					if (ret != 0)   /* ENOSPC */
Jan Kara 449ab7
 						goto errout;
Jan Kara 449ab7
+					reserved = true;
Jan Kara 449ab7
 				} else {
Jan Kara 449ab7
 					allocated = true;
Jan Kara 449ab7
 				}
Jan Kara 449ab7
@@ -1666,6 +1669,8 @@ static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
Jan Kara 449ab7
 	}
Jan Kara 449ab7
 
Jan Kara 449ab7
 	ret = ext4_es_insert_delayed_block(inode, lblk, allocated);
Jan Kara 449ab7
+	if (ret && reserved)
Jan Kara 449ab7
+		ext4_da_release_space(inode, 1);
Jan Kara 449ab7
 
Jan Kara 449ab7
 errout:
Jan Kara 449ab7
 	return ret;
Jan Kara 449ab7
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
Jan Kara 449ab7
index feca816b6bf3..a52f1572daa5 100644
Jan Kara 449ab7
--- a/fs/ext4/super.c
Jan Kara 449ab7
+++ b/fs/ext4/super.c
Jan Kara 449ab7
@@ -1352,6 +1352,12 @@ static void ext4_destroy_inode(struct inode *inode)
Jan Kara 449ab7
 				true);
Jan Kara 449ab7
 		dump_stack();
Jan Kara 449ab7
 	}
Jan Kara 449ab7
+
Jan Kara 449ab7
+	if (EXT4_I(inode)->i_reserved_data_blocks)
Jan Kara 449ab7
+		ext4_msg(inode->i_sb, KERN_ERR,
Jan Kara 449ab7
+			 "Inode %lu (%p): i_reserved_data_blocks (%u) not cleared!",
Jan Kara 449ab7
+			 inode->i_ino, EXT4_I(inode),
Jan Kara 449ab7
+			 EXT4_I(inode)->i_reserved_data_blocks);
Jan Kara 449ab7
 }
Jan Kara 449ab7
 
Jan Kara 449ab7
 static void init_once(void *foo)
Jan Kara 449ab7
-- 
Jan Kara 449ab7
2.26.2
Jan Kara 449ab7