mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-28 05:12:49 +00:00
ext4: using nofail preallocation in ext4_es_remove_extent()
[ Upstream commite9fe2b882b
] If __es_remove_extent() returns an error it means that when splitting extent, allocating an extent that must be kept failed, where returning an error directly would cause the extent tree to be inconsistent. So we use GFP_NOFAIL to pre-allocate an extent_status and pass it to __es_remove_extent() to avoid this problem. In addition, since the allocated memory is outside the i_es_lock, the extent_status tree may change and the pre-allocated extent_status is no longer needed, so we release the pre-allocated extent_status when es->es_len is not initialized. Suggested-by: Jan Kara <jack@suse.cz> Signed-off-by: Baokun Li <libaokun1@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://lore.kernel.org/r/20230424033846.4732-7-libaokun1@huawei.com Signed-off-by: Theodore Ts'o <tytso@mit.edu> Stable-dep-of:8e387c89e9
("ext4: make sure allocate pending entry not fail") Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
66bc78a295
commit
580b9dd6ab
1 changed files with 11 additions and 2 deletions
|
@ -1457,6 +1457,7 @@ int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
|
|||
ext4_lblk_t end;
|
||||
int err = 0;
|
||||
int reserved = 0;
|
||||
struct extent_status *es = NULL;
|
||||
|
||||
if (EXT4_SB(inode->i_sb)->s_mount_state & EXT4_FC_REPLAY)
|
||||
return 0;
|
||||
|
@ -1471,17 +1472,25 @@ int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
|
|||
end = lblk + len - 1;
|
||||
BUG_ON(end < lblk);
|
||||
|
||||
retry:
|
||||
if (err && !es)
|
||||
es = __es_alloc_extent(true);
|
||||
/*
|
||||
* ext4_clear_inode() depends on us taking i_es_lock unconditionally
|
||||
* so that we are sure __es_shrink() is done with the inode before it
|
||||
* is reclaimed.
|
||||
*/
|
||||
write_lock(&EXT4_I(inode)->i_es_lock);
|
||||
err = __es_remove_extent(inode, lblk, end, &reserved, NULL);
|
||||
err = __es_remove_extent(inode, lblk, end, &reserved, es);
|
||||
if (es && !es->es_len)
|
||||
__es_free_extent(es);
|
||||
write_unlock(&EXT4_I(inode)->i_es_lock);
|
||||
if (err)
|
||||
goto retry;
|
||||
|
||||
ext4_es_print_tree(inode);
|
||||
ext4_da_release_space(inode, reserved);
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __es_shrink(struct ext4_sb_info *sbi, int nr_to_scan,
|
||||
|
|
Loading…
Reference in a new issue