We used to set disk extent refs' generation with transid,
but now with sub transid, it will cause a generation mismatch between
a tree block and its corresponding disk extent ref, and this will lead
to balance and scrub's crash.

Since these disk extent refs are delayed and stored in a rbtree,
the udpate will be easy.  When we update a block's generation, we
also update the related ref's generation in the rbtree.

Signed-off-by: Liu Bo <liubo2...@cn.fujitsu.com>
---
 fs/btrfs/ctree.c       |   32 ++++++++++++--------
 fs/btrfs/ctree.h       |    6 +++-
 fs/btrfs/delayed-ref.c |    7 ++++-
 fs/btrfs/delayed-ref.h |    2 +
 fs/btrfs/disk-io.c     |    3 +-
 fs/btrfs/extent-tree.c |   72 ++++++++++++++++++++++++++++++++++++++++--------
 fs/btrfs/ioctl.c       |    2 +-
 7 files changed, 95 insertions(+), 29 deletions(-)

diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 6cc9529..4ec35d9 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -227,6 +227,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
        int ret = 0;
        int level;
        struct btrfs_disk_key disk_key;
+       u64 transid;
 
        WARN_ON(root->ref_cows && trans->transaction->transid !=
                root->fs_info->running_transaction->transid);
@@ -240,13 +241,13 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
 
        cow = btrfs_alloc_free_block(trans, root, buf->len, 0,
                                     new_root_objectid, &disk_key, level,
-                                    buf->start, 0);
+                                    buf->start, 0, &transid);
        if (IS_ERR(cow))
                return PTR_ERR(cow);
 
        copy_extent_buffer(cow, buf, 0, 0, cow->len);
        btrfs_set_header_bytenr(cow, cow->start);
-       btrfs_set_header_generation(cow, trans->transaction->sub_transid);
+       btrfs_set_header_generation(cow, transid);
        btrfs_set_header_backref_rev(cow, BTRFS_MIXED_BACKREF_REV);
        btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN |
                                     BTRFS_HEADER_FLAG_RELOC);
@@ -419,6 +420,7 @@ static noinline int __btrfs_cow_block(struct 
btrfs_trans_handle *trans,
        int last_ref = 0;
        int unlock_orig = 0;
        u64 parent_start;
+       u64 transid;
 
        if (*cow_ret == buf)
                unlock_orig = 1;
@@ -446,7 +448,7 @@ static noinline int __btrfs_cow_block(struct 
btrfs_trans_handle *trans,
 
        cow = btrfs_alloc_free_block(trans, root, buf->len, parent_start,
                                     root->root_key.objectid, &disk_key,
-                                    level, search_start, empty_size);
+                                    level, search_start, empty_size, &transid);
        if (IS_ERR(cow))
                return PTR_ERR(cow);
 
@@ -459,8 +461,7 @@ static noinline int __btrfs_cow_block(struct 
btrfs_trans_handle *trans,
            buf == root->node) {
                btrfs_set_header_generation(cow, trans->transaction->transid);
        } else {
-               btrfs_set_header_generation(cow,
-                                           trans->transaction->sub_transid);
+               btrfs_set_header_generation(cow, transid);
        }
 
        btrfs_set_header_backref_rev(cow, BTRFS_MIXED_BACKREF_REV);
@@ -556,6 +557,9 @@ static inline void update_block_generation(struct 
btrfs_trans_handle *trans,
                return;
 
        btrfs_set_header_generation(buf, transid);
+       if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID)
+               btrfs_set_disk_extent_gen(trans, root, buf->start, buf->len,
+                                         transid, 0);
 
        if (buf == root->node) {
                btrfs_mark_buffer_dirty(buf);
@@ -2154,6 +2158,7 @@ static noinline int insert_new_root(struct 
btrfs_trans_handle *trans,
        struct extent_buffer *c;
        struct extent_buffer *old;
        struct btrfs_disk_key lower_key;
+       u64 transid;
 
        BUG_ON(path->nodes[level]);
        BUG_ON(path->nodes[level-1] != root->node);
@@ -2166,7 +2171,7 @@ static noinline int insert_new_root(struct 
btrfs_trans_handle *trans,
 
        c = btrfs_alloc_free_block(trans, root, root->nodesize, 0,
                                   root->root_key.objectid, &lower_key,
-                                  level, root->node->start, 0);
+                                  level, root->node->start, 0, &transid);
        if (IS_ERR(c))
                return PTR_ERR(c);
 
@@ -2188,8 +2193,7 @@ static noinline int insert_new_root(struct 
btrfs_trans_handle *trans,
                                            trans->transaction->sub_transid);
                btrfs_mark_buffer_dirty(lower);
        } else {
-               btrfs_set_header_generation(c,
-                                           trans->transaction->sub_transid);
+               btrfs_set_header_generation(c, transid);
        }
 
        btrfs_set_header_backref_rev(c, BTRFS_MIXED_BACKREF_REV);
@@ -2284,6 +2288,7 @@ static noinline int split_node(struct btrfs_trans_handle 
*trans,
        int ret;
        int wret;
        u32 c_nritems;
+       u64 transid;
 
        c = path->nodes[level];
        WARN_ON(btrfs_header_generation(c) < trans->transaction->transid);
@@ -2307,8 +2312,8 @@ static noinline int split_node(struct btrfs_trans_handle 
*trans,
        btrfs_node_key(c, &disk_key, mid);
 
        split = btrfs_alloc_free_block(trans, root, root->nodesize, 0,
-                                       root->root_key.objectid,
-                                       &disk_key, level, c->start, 0);
+                                       root->root_key.objectid, &disk_key,
+                                       level, c->start, 0, &transid);
        if (IS_ERR(split))
                return PTR_ERR(split);
 
@@ -2317,7 +2322,7 @@ static noinline int split_node(struct btrfs_trans_handle 
*trans,
        memset_extent_buffer(split, 0, 0, sizeof(struct btrfs_header));
        btrfs_set_header_level(split, btrfs_header_level(c));
        btrfs_set_header_bytenr(split, split->start);
-       btrfs_set_header_generation(split, trans->transaction->sub_transid);
+       btrfs_set_header_generation(split, transid);
        btrfs_set_header_backref_rev(split, BTRFS_MIXED_BACKREF_REV);
        btrfs_set_header_owner(split, root->root_key.objectid);
        write_extent_buffer(split, root->fs_info->fsid,
@@ -2981,6 +2986,7 @@ static noinline int split_leaf(struct btrfs_trans_handle 
*trans,
        int split;
        int num_doubles = 0;
        int tried_avoid_double = 0;
+       u64 transid;
 
        l = path->nodes[0];
        slot = path->slots[0];
@@ -3063,7 +3069,7 @@ again:
 
        right = btrfs_alloc_free_block(trans, root, root->leafsize, 0,
                                        root->root_key.objectid,
-                                       &disk_key, 0, l->start, 0);
+                                       &disk_key, 0, l->start, 0, &transid);
        if (IS_ERR(right))
                return PTR_ERR(right);
 
@@ -3071,7 +3077,7 @@ again:
 
        memset_extent_buffer(right, 0, 0, sizeof(struct btrfs_header));
        btrfs_set_header_bytenr(right, right->start);
-       btrfs_set_header_generation(right, trans->transaction->sub_transid);
+       btrfs_set_header_generation(right, transid);
        btrfs_set_header_backref_rev(right, BTRFS_MIXED_BACKREF_REV);
        btrfs_set_header_owner(right, root->root_key.objectid);
        btrfs_set_header_level(right, 0);
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index a635f1c..7fd10eb 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2276,7 +2276,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct 
btrfs_trans_handle *trans,
                                        struct btrfs_root *root, u32 blocksize,
                                        u64 parent, u64 root_objectid,
                                        struct btrfs_disk_key *key, int level,
-                                       u64 hint, u64 empty_size);
+                                       u64 hint, u64 empty_size, u64 *gen_ret);
 void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root,
                           struct extent_buffer *buf,
@@ -2307,6 +2307,10 @@ int btrfs_set_disk_extent_flags(struct 
btrfs_trans_handle *trans,
                                struct btrfs_root *root,
                                u64 bytenr, u64 num_bytes, u64 flags,
                                int is_data);
+int btrfs_set_disk_extent_gen(struct btrfs_trans_handle *trans,
+                             struct btrfs_root *root,
+                             u64 bytenr, u64 num_bytes, u64 gen,
+                             int is_data);
 int btrfs_free_extent(struct btrfs_trans_handle *trans,
                      struct btrfs_root *root,
                      u64 bytenr, u64 num_bytes, u64 parent,
diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c
index 125cf76..c5fb298 100644
--- a/fs/btrfs/delayed-ref.c
+++ b/fs/btrfs/delayed-ref.c
@@ -376,6 +376,11 @@ update_existing_head_ref(struct btrfs_delayed_ref_node 
*existing,
                                        ref->extent_op->flags_to_set;
                                existing_ref->extent_op->update_flags = 1;
                        }
+                       if (ref->extent_op->update_gen) {
+                               existing_ref->extent_op->generation =
+                                       ref->extent_op->generation;
+                               existing_ref->extent_op->update_gen = 1;
+                       }
                        kfree(ref->extent_op);
                }
        }
@@ -674,7 +679,7 @@ int btrfs_add_delayed_extent_op(struct btrfs_trans_handle 
*trans,
        struct btrfs_delayed_ref_root *delayed_refs;
        int ret;
 
-       head_ref = kmalloc(sizeof(*head_ref), GFP_NOFS);
+       head_ref = kmalloc(sizeof(*head_ref), GFP_ATOMIC);
        if (!head_ref)
                return -ENOMEM;
 
diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h
index e287e3b..098e4c2 100644
--- a/fs/btrfs/delayed-ref.h
+++ b/fs/btrfs/delayed-ref.h
@@ -57,8 +57,10 @@ struct btrfs_delayed_ref_node {
 struct btrfs_delayed_extent_op {
        struct btrfs_disk_key key;
        u64 flags_to_set;
+       u64 generation;
        unsigned int update_key:1;
        unsigned int update_flags:1;
+       unsigned int update_gen:1;
        unsigned int is_data:1;
 };
 
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 6e1a660..8a72f0d 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1243,7 +1243,8 @@ static struct btrfs_root *alloc_log_tree(struct 
btrfs_trans_handle *trans,
        root->ref_cows = 0;
 
        leaf = btrfs_alloc_free_block(trans, root, root->leafsize, 0,
-                                     BTRFS_TREE_LOG_OBJECTID, NULL, 0, 0, 0);
+                                     BTRFS_TREE_LOG_OBJECTID, NULL, 0, 0, 0,
+                                     NULL);
        if (IS_ERR(leaf)) {
                kfree(root);
                return ERR_CAST(leaf);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 1311beb..2279386 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -89,7 +89,7 @@ static int alloc_reserved_tree_block(struct 
btrfs_trans_handle *trans,
                                     struct btrfs_root *root,
                                     u64 parent, u64 root_objectid,
                                     u64 flags, struct btrfs_disk_key *key,
-                                    int level, struct btrfs_key *ins);
+                                    u64 gen, int level, struct btrfs_key *ins);
 static int do_chunk_alloc(struct btrfs_trans_handle *trans,
                          struct btrfs_root *extent_root, u64 alloc_bytes,
                          u64 flags, int force);
@@ -1971,6 +1971,8 @@ static void __run_delayed_extent_op(struct 
btrfs_delayed_extent_op *extent_op,
                bi = (struct btrfs_tree_block_info *)(ei + 1);
                btrfs_set_tree_block_key(leaf, bi, &extent_op->key);
        }
+       if (extent_op->update_gen)
+               btrfs_set_extent_generation(leaf, ei, extent_op->generation);
 }
 
 static int run_delayed_extent_op(struct btrfs_trans_handle *trans,
@@ -2056,11 +2058,12 @@ static int run_delayed_tree_ref(struct 
btrfs_trans_handle *trans,
        BUG_ON(node->ref_mod != 1);
        if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) {
                BUG_ON(!extent_op || !extent_op->update_flags ||
-                      !extent_op->update_key);
+                      !extent_op->update_key || !extent_op->update_gen);
                ret = alloc_reserved_tree_block(trans, root,
                                                parent, ref_root,
                                                extent_op->flags_to_set,
                                                &extent_op->key,
+                                               extent_op->generation,
                                                ref->level, &ins);
        } else if (node->action == BTRFS_ADD_DELAYED_REF) {
                ret = __btrfs_inc_extent_ref(trans, root, node->bytenr,
@@ -2347,29 +2350,67 @@ out:
        return 0;
 }
 
-int btrfs_set_disk_extent_flags(struct btrfs_trans_handle *trans,
-                               struct btrfs_root *root,
-                               u64 bytenr, u64 num_bytes, u64 flags,
-                               int is_data)
+static int __btrfs_update_disk_extent_attr(struct btrfs_trans_handle *trans,
+                                          struct btrfs_root *root,
+                                          u64 bytenr, u64 num_bytes,
+                                          u64 flags, u64 gen,
+                                          int update_flags, int update_gen,
+                                          int is_data)
 {
        struct btrfs_delayed_extent_op *extent_op;
        int ret;
 
-       extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS);
+       extent_op = kmalloc(sizeof(*extent_op), GFP_ATOMIC);
        if (!extent_op)
                return -ENOMEM;
 
-       extent_op->flags_to_set = flags;
-       extent_op->update_flags = 1;
+       extent_op->update_flags = 0;
        extent_op->update_key = 0;
+       extent_op->update_gen = 0;
        extent_op->is_data = is_data ? 1 : 0;
 
+       if (update_flags) {
+               extent_op->flags_to_set = flags;
+               extent_op->update_flags = 1;
+       }
+
+       if (update_gen) {
+               extent_op->generation = gen;
+               extent_op->update_gen = 1;
+       }
+
        ret = btrfs_add_delayed_extent_op(trans, bytenr, num_bytes, extent_op);
        if (ret)
                kfree(extent_op);
        return ret;
 }
 
+int btrfs_set_disk_extent_flags(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *root,
+                               u64 bytenr, u64 num_bytes, u64 flags,
+                               int is_data)
+{
+       int ret;
+
+       ret = __btrfs_update_disk_extent_attr(trans, root, bytenr, num_bytes,
+                                             flags, 0, 1, 0, is_data);
+       BUG_ON(ret);
+       return ret;
+}
+
+int btrfs_set_disk_extent_gen(struct btrfs_trans_handle *trans,
+                             struct btrfs_root *root,
+                             u64 bytenr, u64 num_bytes, u64 gen,
+                             int is_data)
+{
+       int ret;
+
+       ret = __btrfs_update_disk_extent_attr(trans, root, bytenr, num_bytes,
+                                             0, gen, 0, 1, is_data);
+       BUG_ON(ret);
+       return ret;
+}
+
 static noinline int check_delayed_ref(struct btrfs_trans_handle *trans,
                                      struct btrfs_root *root,
                                      struct btrfs_path *path,
@@ -5697,7 +5738,7 @@ static int alloc_reserved_tree_block(struct 
btrfs_trans_handle *trans,
                                     struct btrfs_root *root,
                                     u64 parent, u64 root_objectid,
                                     u64 flags, struct btrfs_disk_key *key,
-                                    int level, struct btrfs_key *ins)
+                                    u64 gen, int level, struct btrfs_key *ins)
 {
        int ret;
        struct btrfs_fs_info *fs_info = root->fs_info;
@@ -5721,7 +5762,7 @@ static int alloc_reserved_tree_block(struct 
btrfs_trans_handle *trans,
        extent_item = btrfs_item_ptr(leaf, path->slots[0],
                                     struct btrfs_extent_item);
        btrfs_set_extent_refs(leaf, extent_item, 1);
-       btrfs_set_extent_generation(leaf, extent_item, trans->transid);
+       btrfs_set_extent_generation(leaf, extent_item, gen);
        btrfs_set_extent_flags(leaf, extent_item,
                               flags | BTRFS_EXTENT_FLAG_TREE_BLOCK);
        block_info = (struct btrfs_tree_block_info *)(extent_item + 1);
@@ -5935,12 +5976,13 @@ struct extent_buffer *btrfs_alloc_free_block(struct 
btrfs_trans_handle *trans,
                                        struct btrfs_root *root, u32 blocksize,
                                        u64 parent, u64 root_objectid,
                                        struct btrfs_disk_key *key, int level,
-                                       u64 hint, u64 empty_size)
+                                       u64 hint, u64 empty_size, u64 *gen_ret)
 {
        struct btrfs_key ins;
        struct btrfs_block_rsv *block_rsv;
        struct extent_buffer *buf;
        u64 flags = 0;
+       u64 gen;
        int ret;
 
 
@@ -5966,6 +6008,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct 
btrfs_trans_handle *trans,
        } else
                BUG_ON(parent > 0);
 
+       gen = trans->transaction->sub_transid;
        if (root_objectid != BTRFS_TREE_LOG_OBJECTID) {
                struct btrfs_delayed_extent_op *extent_op;
                extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS);
@@ -5975,8 +6018,10 @@ struct extent_buffer *btrfs_alloc_free_block(struct 
btrfs_trans_handle *trans,
                else
                        memset(&extent_op->key, 0, sizeof(extent_op->key));
                extent_op->flags_to_set = flags;
+               extent_op->generation = gen;
                extent_op->update_key = 1;
                extent_op->update_flags = 1;
+               extent_op->update_gen = 1;
                extent_op->is_data = 0;
 
                ret = btrfs_add_delayed_tree_ref(trans, ins.objectid,
@@ -5985,6 +6030,9 @@ struct extent_buffer *btrfs_alloc_free_block(struct 
btrfs_trans_handle *trans,
                                        extent_op);
                BUG_ON(ret);
        }
+
+       if (gen_ret)
+               *gen_ret = gen;
        return buf;
 }
 
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index c7c81e6..fe8876c 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -358,7 +358,7 @@ static noinline int create_subvol(struct btrfs_root *root,
                return PTR_ERR(trans);
 
        leaf = btrfs_alloc_free_block(trans, root, root->leafsize,
-                                     0, objectid, NULL, 0, 0, 0);
+                                     0, objectid, NULL, 0, 0, 0, NULL);
        if (IS_ERR(leaf)) {
                ret = PTR_ERR(leaf);
                goto fail;
-- 
1.6.5.2

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to