Btrfs: fix memory leak in reading btree blocks
[deliverable/linux.git] / fs / btrfs / disk-io.c
index 54bc8c7c6bcd387ef48ffc9b69e9771de6e2ca2c..754de24f17afe0ba2bc373bce72c4b72fdc3d71c 100644 (file)
@@ -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;
This page took 0.026349 seconds and 5 git commands to generate.