diff options
author | Liu Bo <bo.li.liu@oracle.com> | 2016-07-20 20:44:12 -0400 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2016-09-26 11:59:49 -0400 |
commit | c79a175175d5a908a7c4d923d44f36808f56536e (patch) | |
tree | b1eac23b00d931a0d6f5ebd72ba330096ddd6921 /fs | |
parent | 08895a8b6b06ed2323cd97a36ee40a116b3db8ed (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.c | 71 | ||||
-rw-r--r-- | fs/btrfs/disk-io.h | 2 | ||||
-rw-r--r-- | fs/btrfs/extent-tree.c | 2 |
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 | ||
4478 | static 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 | |||
4492 | void 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 | |||
4478 | void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans, | 4545 | void 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); |
137 | int btrfs_add_log_tree(struct btrfs_trans_handle *trans, | 137 | int btrfs_add_log_tree(struct btrfs_trans_handle *trans, |
138 | struct btrfs_root *root); | 138 | struct btrfs_root *root); |
139 | void btrfs_cleanup_dirty_bgs(struct btrfs_transaction *trans, | ||
140 | struct btrfs_root *root); | ||
139 | void btrfs_cleanup_one_transaction(struct btrfs_transaction *trans, | 141 | void btrfs_cleanup_one_transaction(struct btrfs_transaction *trans, |
140 | struct btrfs_root *root); | 142 | struct btrfs_root *root); |
141 | struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans, | 143 | struct 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); |