Btrfs: fix memory leaks after transaction is aborted
authorFilipe Manana <fdmanana@suse.com>
Fri, 27 Nov 2015 16:12:00 +0000 (16:12 +0000)
committerFilipe Manana <fdmanana@suse.com>
Thu, 17 Dec 2015 10:59:48 +0000 (10:59 +0000)
When a transaction is aborted, or its commit fails before writing the new
superblock and calling btrfs_finish_extent_commit(), we leak reference
counts on the block groups attached to the transaction's delete_bgs list,
because btrfs_finish_extent_commit() is never called for those two cases.
Fix this by dropping their references at btrfs_put_transaction(), which
is called when transactions are aborted (by making the transaction kthread
commit the transaction) or if their commits fail.

Signed-off-by: Filipe Manana <fdmanana@suse.com>
fs/btrfs/transaction.c

index be8eae80ff6572608a478610f9c2b16f3bcb1871..f85ccf634ca195dff70003f99ac4f2b0d82ae73d 100644 (file)
@@ -75,6 +75,23 @@ void btrfs_put_transaction(struct btrfs_transaction *transaction)
                        list_del_init(&em->list);
                        free_extent_map(em);
                }
+               /*
+                * If any block groups are found in ->deleted_bgs then it's
+                * because the transaction was aborted and a commit did not
+                * happen (things failed before writing the new superblock
+                * and calling btrfs_finish_extent_commit()), so we can not
+                * discard the physical locations of the block groups.
+                */
+               while (!list_empty(&transaction->deleted_bgs)) {
+                       struct btrfs_block_group_cache *cache;
+
+                       cache = list_first_entry(&transaction->deleted_bgs,
+                                                struct btrfs_block_group_cache,
+                                                bg_list);
+                       list_del_init(&cache->bg_list);
+                       btrfs_put_block_group_trimming(cache);
+                       btrfs_put_block_group(cache);
+               }
                kmem_cache_free(btrfs_transaction_cachep, transaction);
        }
 }