mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 05:20:53 +07:00
Btrfs: fix memory leak of block group cache
While processing delayed refs, we may update block group's statistics and attach it to cur_trans->dirty_bgs, and later writing dirty block groups will process the list, which happens during btrfs_commit_transaction(). For whatever reason, the transaction is aborted and dirty_bgs is not processed in cleanup_transaction(), we end up with memory leak of these dirty block group cache. Since btrfs_start_dirty_block_groups() doesn't make it go to the commit critical section, this also adds the cleanup work inside it. Signed-off-by: Liu Bo <bo.li.liu@oracle.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
08895a8b6b
commit
c79a175175
@ -4475,9 +4475,80 @@ static int btrfs_destroy_pinned_extent(struct btrfs_root *root,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void btrfs_cleanup_bg_io(struct btrfs_block_group_cache *cache)
|
||||||
|
{
|
||||||
|
struct inode *inode;
|
||||||
|
|
||||||
|
inode = cache->io_ctl.inode;
|
||||||
|
if (inode) {
|
||||||
|
invalidate_inode_pages2(inode->i_mapping);
|
||||||
|
BTRFS_I(inode)->generation = 0;
|
||||||
|
cache->io_ctl.inode = NULL;
|
||||||
|
iput(inode);
|
||||||
|
}
|
||||||
|
btrfs_put_block_group(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
void btrfs_cleanup_dirty_bgs(struct btrfs_transaction *cur_trans,
|
||||||
|
struct btrfs_root *root)
|
||||||
|
{
|
||||||
|
struct btrfs_block_group_cache *cache;
|
||||||
|
|
||||||
|
spin_lock(&cur_trans->dirty_bgs_lock);
|
||||||
|
while (!list_empty(&cur_trans->dirty_bgs)) {
|
||||||
|
cache = list_first_entry(&cur_trans->dirty_bgs,
|
||||||
|
struct btrfs_block_group_cache,
|
||||||
|
dirty_list);
|
||||||
|
if (!cache) {
|
||||||
|
btrfs_err(root->fs_info,
|
||||||
|
"orphan block group dirty_bgs list");
|
||||||
|
spin_unlock(&cur_trans->dirty_bgs_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!list_empty(&cache->io_list)) {
|
||||||
|
spin_unlock(&cur_trans->dirty_bgs_lock);
|
||||||
|
list_del_init(&cache->io_list);
|
||||||
|
btrfs_cleanup_bg_io(cache);
|
||||||
|
spin_lock(&cur_trans->dirty_bgs_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
list_del_init(&cache->dirty_list);
|
||||||
|
spin_lock(&cache->lock);
|
||||||
|
cache->disk_cache_state = BTRFS_DC_ERROR;
|
||||||
|
spin_unlock(&cache->lock);
|
||||||
|
|
||||||
|
spin_unlock(&cur_trans->dirty_bgs_lock);
|
||||||
|
btrfs_put_block_group(cache);
|
||||||
|
spin_lock(&cur_trans->dirty_bgs_lock);
|
||||||
|
}
|
||||||
|
spin_unlock(&cur_trans->dirty_bgs_lock);
|
||||||
|
|
||||||
|
while (!list_empty(&cur_trans->io_bgs)) {
|
||||||
|
cache = list_first_entry(&cur_trans->io_bgs,
|
||||||
|
struct btrfs_block_group_cache,
|
||||||
|
io_list);
|
||||||
|
if (!cache) {
|
||||||
|
btrfs_err(root->fs_info,
|
||||||
|
"orphan block group on io_bgs list");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_del_init(&cache->io_list);
|
||||||
|
spin_lock(&cache->lock);
|
||||||
|
cache->disk_cache_state = BTRFS_DC_ERROR;
|
||||||
|
spin_unlock(&cache->lock);
|
||||||
|
btrfs_cleanup_bg_io(cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
|
void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
|
||||||
struct btrfs_root *root)
|
struct btrfs_root *root)
|
||||||
{
|
{
|
||||||
|
btrfs_cleanup_dirty_bgs(cur_trans, root);
|
||||||
|
ASSERT(list_empty(&cur_trans->dirty_bgs));
|
||||||
|
ASSERT(list_empty(&cur_trans->io_bgs));
|
||||||
|
|
||||||
btrfs_destroy_delayed_refs(cur_trans, root);
|
btrfs_destroy_delayed_refs(cur_trans, root);
|
||||||
|
|
||||||
cur_trans->state = TRANS_STATE_COMMIT_START;
|
cur_trans->state = TRANS_STATE_COMMIT_START;
|
||||||
|
@ -136,6 +136,8 @@ int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans,
|
|||||||
struct btrfs_fs_info *fs_info);
|
struct btrfs_fs_info *fs_info);
|
||||||
int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
|
int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root);
|
struct btrfs_root *root);
|
||||||
|
void btrfs_cleanup_dirty_bgs(struct btrfs_transaction *trans,
|
||||||
|
struct btrfs_root *root);
|
||||||
void btrfs_cleanup_one_transaction(struct btrfs_transaction *trans,
|
void btrfs_cleanup_one_transaction(struct btrfs_transaction *trans,
|
||||||
struct btrfs_root *root);
|
struct btrfs_root *root);
|
||||||
struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
|
struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
|
||||||
|
@ -3694,6 +3694,8 @@ int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans,
|
|||||||
goto again;
|
goto again;
|
||||||
}
|
}
|
||||||
spin_unlock(&cur_trans->dirty_bgs_lock);
|
spin_unlock(&cur_trans->dirty_bgs_lock);
|
||||||
|
} else if (ret < 0) {
|
||||||
|
btrfs_cleanup_dirty_bgs(cur_trans, root);
|
||||||
}
|
}
|
||||||
|
|
||||||
btrfs_free_path(path);
|
btrfs_free_path(path);
|
||||||
|
Loading…
Reference in New Issue
Block a user