Skip to content

Commit

Permalink
Btrfs: fix memory leak of block group cache
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Liu Bo authored and David Sterba committed Sep 26, 2016
1 parent 08895a8 commit c79a175
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 0 deletions.
71 changes: 71 additions & 0 deletions fs/btrfs/disk-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -4475,9 +4475,80 @@ static int btrfs_destroy_pinned_extent(struct btrfs_root *root,
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;
Expand Down
2 changes: 2 additions & 0 deletions fs/btrfs/disk-io.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions fs/btrfs/extent-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -3694,6 +3694,8 @@ int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans,
goto again;
}
spin_unlock(&cur_trans->dirty_bgs_lock);
} else if (ret < 0) {
btrfs_cleanup_dirty_bgs(cur_trans, root);
}

btrfs_free_path(path);
Expand Down

0 comments on commit c79a175

Please sign in to comment.