btrfs: qgroup: convert PREALLOC to PERTRANS after record_root_in_trans
authorBoris Burkov <boris@bur.io>
Thu, 21 Mar 2024 17:18:39 +0000 (10:18 -0700)
committerDavid Sterba <dsterba@suse.com>
Tue, 2 Apr 2024 17:18:47 +0000 (19:18 +0200)
The transaction is only able to free PERTRANS reservations for a root
once that root has been recorded with the TRANS tag on the roots radix
tree. Therefore, until we are sure that this root will get tagged, it
isn't safe to convert. Generally, this is not an issue as *some*
transaction will likely tag the root before long and this reservation
will get freed in that transaction, but technically it could stick
around until unmount and result in a warning about leaked metadata
reservation space.

This path is most exercised by running the generic/269 fstest with
CONFIG_BTRFS_DEBUG.

Fixes: a6496849671a ("btrfs: fix start transaction qgroup rsv double free")
CC: stable@vger.kernel.org # 6.6+
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Boris Burkov <boris@bur.io>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/transaction.c

index 46e8426adf4f15768507303430b38c9e6be56c7d..1f10082d35d928d5a6d3952d6eb0a24f45327e0b 100644 (file)
@@ -745,14 +745,6 @@ again:
                h->reloc_reserved = reloc_reserved;
        }
 
-       /*
-        * Now that we have found a transaction to be a part of, convert the
-        * qgroup reservation from prealloc to pertrans. A different transaction
-        * can't race in and free our pertrans out from under us.
-        */
-       if (qgroup_reserved)
-               btrfs_qgroup_convert_reserved_meta(root, qgroup_reserved);
-
 got_it:
        if (!current->journal_info)
                current->journal_info = h;
@@ -786,8 +778,15 @@ got_it:
                 * not just freed.
                 */
                btrfs_end_transaction(h);
-               return ERR_PTR(ret);
+               goto reserve_fail;
        }
+       /*
+        * Now that we have found a transaction to be a part of, convert the
+        * qgroup reservation from prealloc to pertrans. A different transaction
+        * can't race in and free our pertrans out from under us.
+        */
+       if (qgroup_reserved)
+               btrfs_qgroup_convert_reserved_meta(root, qgroup_reserved);
 
        return h;