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 @@ again:
|
|||
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,
|
||||
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);
|
||||
|
||||
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);
|
||||
int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
|
||||
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,
|
||||
struct btrfs_root *root);
|
||||
struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
|
||||
|
|
|
@ -3694,6 +3694,8 @@ again:
|
|||
goto again;
|
||||
}
|
||||
spin_unlock(&cur_trans->dirty_bgs_lock);
|
||||
} else if (ret < 0) {
|
||||
btrfs_cleanup_dirty_bgs(cur_trans, root);
|
||||
}
|
||||
|
||||
btrfs_free_path(path);
|
||||
|
|
Loading…
Reference in New Issue