btrfs: qgroup: Fix root item corruption when multiple same source snapshots are creat...
authorQu Wenruo <wqu@suse.com>
Tue, 19 Dec 2017 07:44:54 +0000 (15:44 +0800)
committerDavid Sterba <dsterba@suse.com>
Fri, 30 Mar 2018 23:26:51 +0000 (01:26 +0200)
When multiple pending snapshots referring to the same source subvolume
are executed, enabled quota will cause root item corruption, where root
items are using old bytenr (no backref in extent tree).

This can be triggered by fstests btrfs/152.

The cause is when source subvolume is still dirty, extra commit
(simplied transaction commit) of qgroup_account_snapshot() can skip
dirty roots not recorded in current transaction, making root item of
source subvolume not updated.

Fix it by forcing recording source subvolume in current transaction
before qgroup sub-transaction commit.

Reported-by: Justin Maggard <jmaggard@netgear.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/transaction.c

index 665438542b96549cf28489bb422b9ec7432c3bbe..17978fd731f65f8403107d09b7cef94aa170a4dc 100644 (file)
@@ -319,7 +319,7 @@ static int record_root_in_trans(struct btrfs_trans_handle *trans,
        if ((test_bit(BTRFS_ROOT_REF_COWS, &root->state) &&
            root->last_trans < trans->transid) || force) {
                WARN_ON(root == fs_info->extent_root);
        if ((test_bit(BTRFS_ROOT_REF_COWS, &root->state) &&
            root->last_trans < trans->transid) || force) {
                WARN_ON(root == fs_info->extent_root);
-               WARN_ON(root->commit_root != root->node);
+               WARN_ON(!force && root->commit_root != root->node);
 
                /*
                 * see below for IN_TRANS_SETUP usage rules
 
                /*
                 * see below for IN_TRANS_SETUP usage rules
@@ -1371,6 +1371,14 @@ static int qgroup_account_snapshot(struct btrfs_trans_handle *trans,
        if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
                return 0;
 
        if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
                return 0;
 
+       /*
+        * Ensure dirty @src will be commited.  Or, after comming
+        * commit_fs_roots() and switch_commit_roots(), any dirty but not
+        * recorded root will never be updated again, causing an outdated root
+        * item.
+        */
+       record_root_in_trans(trans, src, 1);
+
        /*
         * We are going to commit transaction, see btrfs_commit_transaction()
         * comment for reason locking tree_log_mutex
        /*
         * We are going to commit transaction, see btrfs_commit_transaction()
         * comment for reason locking tree_log_mutex