Btrfs: fix broken nocow after balance
authorMiao Xie <miaox@cn.fujitsu.com>
Thu, 6 Jun 2013 03:28:03 +0000 (03:28 +0000)
committerJosef Bacik <jbacik@fusionio.com>
Mon, 1 Jul 2013 12:52:25 +0000 (08:52 -0400)
Balance will create reloc_root for each fs root, and it's going to
record last_snapshot to filter shared blocks.  The side effect of
setting last_snapshot is to break nocow attributes of files.

Since the extents are not shared by the relocation tree after the balance,
we can recover the old last_snapshot safely if no one snapshoted the
source tree. We fix the above problem by this way.

Reported-by: Kyle Gates <kylegates@hotmail.com>
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
fs/btrfs/relocation.c

index aa559f1161df542b1ec3d27d2a37ddd2c481fc8a..4a404b44b0ad75eeaff463a3198f2934df7bce35 100644 (file)
@@ -1305,6 +1305,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
        struct extent_buffer *eb;
        struct btrfs_root_item *root_item;
        struct btrfs_key root_key;
+       u64 last_snap = 0;
        int ret;
 
        root_item = kmalloc(sizeof(*root_item), GFP_NOFS);
@@ -1320,6 +1321,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
                                      BTRFS_TREE_RELOC_OBJECTID);
                BUG_ON(ret);
 
+               last_snap = btrfs_root_last_snapshot(&root->root_item);
                btrfs_set_root_last_snapshot(&root->root_item,
                                             trans->transid - 1);
        } else {
@@ -1345,6 +1347,12 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
                memset(&root_item->drop_progress, 0,
                       sizeof(struct btrfs_disk_key));
                root_item->drop_level = 0;
+               /*
+                * abuse rtransid, it is safe because it is impossible to
+                * receive data into a relocation tree.
+                */
+               btrfs_set_root_rtransid(root_item, last_snap);
+               btrfs_set_root_otransid(root_item, trans->transid);
        }
 
        btrfs_tree_unlock(eb);
@@ -2272,8 +2280,12 @@ void free_reloc_roots(struct list_head *list)
 static noinline_for_stack
 int merge_reloc_roots(struct reloc_control *rc)
 {
+       struct btrfs_trans_handle *trans;
        struct btrfs_root *root;
        struct btrfs_root *reloc_root;
+       u64 last_snap;
+       u64 otransid;
+       u64 objectid;
        LIST_HEAD(reloc_roots);
        int found = 0;
        int ret = 0;
@@ -2307,12 +2319,44 @@ again:
                } else {
                        list_del_init(&reloc_root->root_list);
                }
+
+               /*
+                * we keep the old last snapshod transid in rtranid when we
+                * created the relocation tree.
+                */
+               last_snap = btrfs_root_rtransid(&reloc_root->root_item);
+               otransid = btrfs_root_otransid(&reloc_root->root_item);
+               objectid = reloc_root->root_key.offset;
+
                ret = btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0, 1);
                if (ret < 0) {
                        if (list_empty(&reloc_root->root_list))
                                list_add_tail(&reloc_root->root_list,
                                              &reloc_roots);
                        goto out;
+               } else if (!ret) {
+                       /*
+                        * recover the last snapshot tranid to avoid
+                        * the space balance break NOCOW.
+                        */
+                       root = read_fs_root(rc->extent_root->fs_info,
+                                           objectid);
+                       if (IS_ERR(root))
+                               continue;
+
+                       if (btrfs_root_refs(&root->root_item) == 0)
+                               continue;
+
+                       trans = btrfs_join_transaction(root);
+                       BUG_ON(IS_ERR(trans));
+
+                       /* Check if the fs/file tree was snapshoted or not. */
+                       if (btrfs_root_last_snapshot(&root->root_item) ==
+                           otransid - 1)
+                               btrfs_set_root_last_snapshot(&root->root_item,
+                                                            last_snap);
+                               
+                       btrfs_end_transaction(trans, root);
                }
        }