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....@oracle.com>
---
v2: - remove the 'return' when dirty_bgs is empty because there
      might be block group in list io_bgs.
    - remove unnecessary '\n' in btrfs_err().
    - more commit log.

 fs/btrfs/disk-io.c     | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/btrfs/disk-io.h     |  2 ++
 fs/btrfs/extent-tree.c |  2 ++
 3 files changed, 75 insertions(+)

diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index db53eb8..4c110de 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -4434,9 +4434,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;
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index acba821..6201663 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -133,6 +133,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,
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 50bd683..7a35c9d 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -3698,6 +3698,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);
-- 
2.5.5

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to