btrfs: catch cow on deleting snapshots
authorJosef Bacik <jbacik@fb.com>
Fri, 30 Nov 2018 16:52:13 +0000 (11:52 -0500)
committerDavid Sterba <dsterba@suse.com>
Mon, 17 Dec 2018 13:51:48 +0000 (14:51 +0100)
When debugging some weird extent reference bug I suspected that we were
changing a snapshot while we were deleting it, which could explain my
bug.  This was indeed what was happening, and this patch helped me
verify my theory.  It is never correct to modify the snapshot once it's
being deleted, so mark the root when we are deleting it and make sure we
complain about it when it happens.

Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/extent-tree.c

index 5912a97b07a6f877877d9b4c1a8f40155f3ecc24..72745235896fb286b4ed2bf62baa27cae0c8833c 100644 (file)
@@ -1440,6 +1440,10 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans,
        u64 search_start;
        int ret;
 
+       if (test_bit(BTRFS_ROOT_DELETING, &root->state))
+               btrfs_err(fs_info,
+                       "COW'ing blocks on a fs root that's being dropped");
+
        if (trans->transaction != fs_info->running_transaction)
                WARN(1, KERN_CRIT "trans %llu running %llu\n",
                       trans->transid,
index 6520e8e70b09e8e2f65cf45872a1db1160c3a561..f031a447a047cc3375ffc033464f10186b32e2c5 100644 (file)
@@ -1197,6 +1197,7 @@ enum {
        BTRFS_ROOT_FORCE_COW,
        BTRFS_ROOT_MULTI_LOG_TASKS,
        BTRFS_ROOT_DIRTY,
+       BTRFS_ROOT_DELETING,
 };
 
 /*
index 306e33d4960281b7ec973f396114fd8c8df406a9..de21e0c93eb62778e219ec065b78dca7dc4329a9 100644 (file)
@@ -9275,6 +9275,15 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
        if (block_rsv)
                trans->block_rsv = block_rsv;
 
+       /*
+        * This will help us catch people modifying the fs tree while we're
+        * dropping it.  It is unsafe to mess with the fs tree while it's being
+        * dropped as we unlock the root node and parent nodes as we walk down
+        * the tree, assuming nothing will change.  If something does change
+        * then we'll have stale information and drop references to blocks we've
+        * already dropped.
+        */
+       set_bit(BTRFS_ROOT_DELETING, &root->state);
        if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
                level = btrfs_header_level(root->node);
                path->nodes[level] = btrfs_lock_root_node(root);