aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLiu Bo <bo.li.liu@oracle.com>2016-07-20 20:44:12 -0400
committerDavid Sterba <dsterba@suse.com>2016-09-26 11:59:49 -0400
commitc79a175175d5a908a7c4d923d44f36808f56536e (patch)
treeb1eac23b00d931a0d6f5ebd72ba330096ddd6921 /fs
parent08895a8b6b06ed2323cd97a36ee40a116b3db8ed (diff)
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>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/disk-io.c71
-rw-r--r--fs/btrfs/disk-io.h2
-rw-r--r--fs/btrfs/extent-tree.c2
3 files changed, 75 insertions, 0 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 54bc8c7c6bcd..754de24f17af 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -4475,9 +4475,80 @@ again:
4475 return 0; 4475 return 0;
4476} 4476}
4477 4477
4478static void btrfs_cleanup_bg_io(struct btrfs_block_group_cache *cache)
4479{
4480 struct inode *inode;
4481
4482 inode = cache->io_ctl.inode;
4483 if (inode) {
4484 invalidate_inode_pages2(inode->i_mapping);
4485 BTRFS_I(inode)->generation = 0;
4486 cache->io_ctl.inode = NULL;
4487 iput(inode);
4488 }
4489 btrfs_put_block_group(cache);
4490}
4491
4492void btrfs_cleanup_dirty_bgs(struct btrfs_transaction *cur_trans,
4493 struct btrfs_root *root)
4494{
4495 struct btrfs_block_group_cache *cache;
4496
4497 spin_lock(&cur_trans->dirty_bgs_lock);
4498 while (!list_empty(&cur_trans->dirty_bgs)) {
4499 cache = list_first_entry(&cur_trans->dirty_bgs,
4500 struct btrfs_block_group_cache,
4501 dirty_list);
4502 if (!cache) {
4503 btrfs_err(root->fs_info,
4504 "orphan block group dirty_bgs list");
4505 spin_unlock(&cur_trans->dirty_bgs_lock);
4506 return;
4507 }
4508
4509 if (!list_empty(&cache->io_list)) {
4510 spin_unlock(&cur_trans->dirty_bgs_lock);
4511 list_del_init(&cache->io_list);
4512 btrfs_cleanup_bg_io(cache);
4513 spin_lock(&cur_trans->dirty_bgs_lock);
4514 }
4515
4516 list_del_init(&cache->dirty_list);
4517 spin_lock(&cache->lock);
4518 cache->disk_cache_state = BTRFS_DC_ERROR;
4519 spin_unlock(&cache->lock);
4520
4521 spin_unlock(&cur_trans->dirty_bgs_lock);
4522 btrfs_put_block_group(cache);
4523 spin_lock(&cur_trans->dirty_bgs_lock);
4524 }
4525 spin_unlock(&cur_trans->dirty_bgs_lock);
4526
4527 while (!list_empty(&cur_trans->io_bgs)) {
4528 cache = list_first_entry(&cur_trans->io_bgs,
4529 struct btrfs_block_group_cache,
4530 io_list);
4531 if (!cache) {
4532 btrfs_err(root->fs_info,
4533 "orphan block group on io_bgs list");
4534 return;
4535 }
4536
4537 list_del_init(&cache->io_list);
4538 spin_lock(&cache->lock);
4539 cache->disk_cache_state = BTRFS_DC_ERROR;
4540 spin_unlock(&cache->lock);
4541 btrfs_cleanup_bg_io(cache);
4542 }
4543}
4544
4478void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans, 4545void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
4479 struct btrfs_root *root) 4546 struct btrfs_root *root)
4480{ 4547{
4548 btrfs_cleanup_dirty_bgs(cur_trans, root);
4549 ASSERT(list_empty(&cur_trans->dirty_bgs));
4550 ASSERT(list_empty(&cur_trans->io_bgs));
4551
4481 btrfs_destroy_delayed_refs(cur_trans, root); 4552 btrfs_destroy_delayed_refs(cur_trans, root);
4482 4553
4483 cur_trans->state = TRANS_STATE_COMMIT_START; 4554 cur_trans->state = TRANS_STATE_COMMIT_START;
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index f19a982f5a4f..1a3237e5700f 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -136,6 +136,8 @@ int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans,
136 struct btrfs_fs_info *fs_info); 136 struct btrfs_fs_info *fs_info);
137int btrfs_add_log_tree(struct btrfs_trans_handle *trans, 137int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
138 struct btrfs_root *root); 138 struct btrfs_root *root);
139void btrfs_cleanup_dirty_bgs(struct btrfs_transaction *trans,
140 struct btrfs_root *root);
139void btrfs_cleanup_one_transaction(struct btrfs_transaction *trans, 141void btrfs_cleanup_one_transaction(struct btrfs_transaction *trans,
140 struct btrfs_root *root); 142 struct btrfs_root *root);
141struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans, 143struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 665da8f66ff1..616c45580c23 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -3694,6 +3694,8 @@ again:
3694 goto again; 3694 goto again;
3695 } 3695 }
3696 spin_unlock(&cur_trans->dirty_bgs_lock); 3696 spin_unlock(&cur_trans->dirty_bgs_lock);
3697 } else if (ret < 0) {
3698 btrfs_cleanup_dirty_bgs(cur_trans, root);
3697 } 3699 }
3698 3700
3699 btrfs_free_path(path); 3701 btrfs_free_path(path);