btrfs: introduce BTRFS_NESTING_COW for cow'ing blocks
authorJosef Bacik <josef@toxicpanda.com>
Thu, 20 Aug 2020 15:46:03 +0000 (11:46 -0400)
committerDavid Sterba <dsterba@suse.com>
Wed, 7 Oct 2020 10:12:16 +0000 (12:12 +0200)
When we COW a block we are holding a lock on the original block, and
then we lock the new COW block.  Because our lockdep maps are based on
root + level, this will make lockdep complain.  We need a way to
indicate a subclass for locking the COW'ed block, so plumb through our
btrfs_lock_nesting from btrfs_cow_block down to the btrfs_init_buffer,
and then introduce BTRFS_NESTING_COW to be used for cow'ing blocks.

The reason I've added all this extra infrastructure is because there
will be need of different nesting classes in follow up patches.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c
fs/btrfs/ioctl.c
fs/btrfs/locking.h
fs/btrfs/relocation.c
fs/btrfs/transaction.c

index 71e3fa30082c888677162c4c2fc7a30ec7eb4944..48178fd8740f8c45019032631bfda66fba6892e3 100644 (file)
@@ -198,7 +198,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
                btrfs_node_key(buf, &disk_key, 0);
 
        cow = btrfs_alloc_tree_block(trans, root, 0, new_root_objectid,
-                       &disk_key, level, buf->start, 0);
+                       &disk_key, level, buf->start, 0, BTRFS_NESTING_NORMAL);
        if (IS_ERR(cow))
                return PTR_ERR(cow);
 
@@ -957,7 +957,8 @@ static struct extent_buffer *alloc_tree_block_no_bg_flush(
                                          const struct btrfs_disk_key *disk_key,
                                          int level,
                                          u64 hint,
-                                         u64 empty_size)
+                                         u64 empty_size,
+                                         enum btrfs_lock_nesting nest)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
        struct extent_buffer *ret;
@@ -986,7 +987,7 @@ static struct extent_buffer *alloc_tree_block_no_bg_flush(
 
        ret = btrfs_alloc_tree_block(trans, root, parent_start,
                                     root->root_key.objectid, disk_key, level,
-                                    hint, empty_size);
+                                    hint, empty_size, nest);
        trans->can_flush_pending_bgs = true;
 
        return ret;
@@ -1009,7 +1010,8 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
                             struct extent_buffer *buf,
                             struct extent_buffer *parent, int parent_slot,
                             struct extent_buffer **cow_ret,
-                            u64 search_start, u64 empty_size)
+                            u64 search_start, u64 empty_size,
+                            enum btrfs_lock_nesting nest)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
        struct btrfs_disk_key disk_key;
@@ -1040,7 +1042,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
                parent_start = parent->start;
 
        cow = alloc_tree_block_no_bg_flush(trans, root, parent_start, &disk_key,
-                                          level, search_start, empty_size);
+                                          level, search_start, empty_size, nest);
        if (IS_ERR(cow))
                return PTR_ERR(cow);
 
@@ -1446,7 +1448,8 @@ static inline int should_cow_block(struct btrfs_trans_handle *trans,
 noinline int btrfs_cow_block(struct btrfs_trans_handle *trans,
                    struct btrfs_root *root, struct extent_buffer *buf,
                    struct extent_buffer *parent, int parent_slot,
-                   struct extent_buffer **cow_ret)
+                   struct extent_buffer **cow_ret,
+                   enum btrfs_lock_nesting nest)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
        u64 search_start;
@@ -1485,7 +1488,7 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans,
         */
        btrfs_qgroup_trace_subtree_after_cow(trans, root, buf);
        ret = __btrfs_cow_block(trans, root, buf, parent,
-                                parent_slot, cow_ret, search_start, 0);
+                                parent_slot, cow_ret, search_start, 0, nest);
 
        trace_btrfs_cow_block(root, buf, *cow_ret);
 
@@ -1657,7 +1660,8 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
                err = __btrfs_cow_block(trans, root, cur, parent, i,
                                        &cur, search_start,
                                        min(16 * blocksize,
-                                           (end_slot - i) * blocksize));
+                                           (end_slot - i) * blocksize),
+                                       BTRFS_NESTING_COW);
                if (err) {
                        btrfs_tree_unlock(cur);
                        free_extent_buffer(cur);
@@ -1855,7 +1859,8 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
 
                btrfs_tree_lock(child);
                btrfs_set_lock_blocking_write(child);
-               ret = btrfs_cow_block(trans, root, child, mid, 0, &child);
+               ret = btrfs_cow_block(trans, root, child, mid, 0, &child,
+                                     BTRFS_NESTING_COW);
                if (ret) {
                        btrfs_tree_unlock(child);
                        free_extent_buffer(child);
@@ -1894,7 +1899,8 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
                btrfs_tree_lock(left);
                btrfs_set_lock_blocking_write(left);
                wret = btrfs_cow_block(trans, root, left,
-                                      parent, pslot - 1, &left);
+                                      parent, pslot - 1, &left,
+                                      BTRFS_NESTING_COW);
                if (wret) {
                        ret = wret;
                        goto enospc;
@@ -1909,7 +1915,8 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
                btrfs_tree_lock(right);
                btrfs_set_lock_blocking_write(right);
                wret = btrfs_cow_block(trans, root, right,
-                                      parent, pslot + 1, &right);
+                                      parent, pslot + 1, &right,
+                                      BTRFS_NESTING_COW);
                if (wret) {
                        ret = wret;
                        goto enospc;
@@ -2077,7 +2084,8 @@ static noinline int push_nodes_for_insert(struct btrfs_trans_handle *trans,
                        wret = 1;
                } else {
                        ret = btrfs_cow_block(trans, root, left, parent,
-                                             pslot - 1, &left);
+                                             pslot - 1, &left,
+                                             BTRFS_NESTING_COW);
                        if (ret)
                                wret = 1;
                        else {
@@ -2132,7 +2140,7 @@ static noinline int push_nodes_for_insert(struct btrfs_trans_handle *trans,
                } else {
                        ret = btrfs_cow_block(trans, root, right,
                                              parent, pslot + 1,
-                                             &right);
+                                             &right, BTRFS_NESTING_COW);
                        if (ret)
                                wret = 1;
                        else {
@@ -2740,11 +2748,13 @@ again:
                        btrfs_set_path_blocking(p);
                        if (last_level)
                                err = btrfs_cow_block(trans, root, b, NULL, 0,
-                                                     &b);
+                                                     &b,
+                                                     BTRFS_NESTING_COW);
                        else
                                err = btrfs_cow_block(trans, root, b,
                                                      p->nodes[level + 1],
-                                                     p->slots[level + 1], &b);
+                                                     p->slots[level + 1], &b,
+                                                     BTRFS_NESTING_COW);
                        if (err) {
                                ret = err;
                                goto done;
@@ -3396,7 +3406,8 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
                btrfs_node_key(lower, &lower_key, 0);
 
        c = alloc_tree_block_no_bg_flush(trans, root, 0, &lower_key, level,
-                                        root->node->start, 0);
+                                        root->node->start, 0,
+                                        BTRFS_NESTING_NORMAL);
        if (IS_ERR(c))
                return PTR_ERR(c);
 
@@ -3526,7 +3537,7 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
        btrfs_node_key(c, &disk_key, mid);
 
        split = alloc_tree_block_no_bg_flush(trans, root, 0, &disk_key, level,
-                                            c->start, 0);
+                                            c->start, 0, BTRFS_NESTING_NORMAL);
        if (IS_ERR(split))
                return PTR_ERR(split);
 
@@ -3804,7 +3815,7 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root
 
        /* cow and double check */
        ret = btrfs_cow_block(trans, root, right, upper,
-                             slot + 1, &right);
+                             slot + 1, &right, BTRFS_NESTING_COW);
        if (ret)
                goto out_unlock;
 
@@ -4045,7 +4056,8 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
 
        /* cow and double check */
        ret = btrfs_cow_block(trans, root, left,
-                             path->nodes[1], slot - 1, &left);
+                             path->nodes[1], slot - 1, &left,
+                             BTRFS_NESTING_COW);
        if (ret) {
                /* we hit -ENOSPC, but it isn't fatal here */
                if (ret == -ENOSPC)
@@ -4312,7 +4324,7 @@ again:
                btrfs_item_key(l, &disk_key, mid);
 
        right = alloc_tree_block_no_bg_flush(trans, root, 0, &disk_key, 0,
-                                            l->start, 0);
+                                            l->start, 0, BTRFS_NESTING_NORMAL);
        if (IS_ERR(right))
                return PTR_ERR(right);
 
index 1339e390a7571e918d8d52388dec4245e6c4a1e3..b4d3a4d8205302da144abd7a5da8b76646c8e29e 100644 (file)
@@ -2526,7 +2526,8 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
                                             u64 parent, u64 root_objectid,
                                             const struct btrfs_disk_key *key,
                                             int level, u64 hint,
-                                            u64 empty_size);
+                                            u64 empty_size,
+                                            enum btrfs_lock_nesting nest);
 void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root,
                           struct extent_buffer *buf,
@@ -2667,7 +2668,8 @@ struct extent_buffer *btrfs_read_node_slot(struct extent_buffer *parent,
 int btrfs_cow_block(struct btrfs_trans_handle *trans,
                    struct btrfs_root *root, struct extent_buffer *buf,
                    struct extent_buffer *parent, int parent_slot,
-                   struct extent_buffer **cow_ret);
+                   struct extent_buffer **cow_ret,
+                   enum btrfs_lock_nesting nest);
 int btrfs_copy_root(struct btrfs_trans_handle *trans,
                      struct btrfs_root *root,
                      struct extent_buffer *buf,
index 4eb56f00ee81675183206035d74a6a3c33e11cd7..ed887242f348932637a31f626838dbd5ec05c7dd 100644 (file)
@@ -1205,7 +1205,8 @@ struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
        root->root_key.type = BTRFS_ROOT_ITEM_KEY;
        root->root_key.offset = 0;
 
-       leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0);
+       leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0,
+                                     BTRFS_NESTING_NORMAL);
        if (IS_ERR(leaf)) {
                ret = PTR_ERR(leaf);
                leaf = NULL;
@@ -1277,7 +1278,7 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans,
         */
 
        leaf = btrfs_alloc_tree_block(trans, root, 0, BTRFS_TREE_LOG_OBJECTID,
-                       NULL, 0, 0, 0);
+                       NULL, 0, 0, 0, BTRFS_NESTING_NORMAL);
        if (IS_ERR(leaf)) {
                btrfs_put_root(root);
                return ERR_CAST(leaf);
index 7b61b7b5122f2e1fad07a843312ee1e238335876..3b21fee13e77811e1d6cba72478ef8f805ef28a8 100644 (file)
@@ -4656,7 +4656,8 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
 
 static struct extent_buffer *
 btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root,
-                     u64 bytenr, int level, u64 owner)
+                     u64 bytenr, int level, u64 owner,
+                     enum btrfs_lock_nesting nest)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
        struct extent_buffer *buf;
@@ -4679,7 +4680,7 @@ btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root,
        }
 
        btrfs_set_buffer_lockdep_class(owner, buf, level);
-       btrfs_tree_lock(buf);
+       __btrfs_tree_lock(buf, nest);
        btrfs_clean_tree_block(buf);
        clear_bit(EXTENT_BUFFER_STALE, &buf->bflags);
 
@@ -4725,7 +4726,8 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
                                             u64 parent, u64 root_objectid,
                                             const struct btrfs_disk_key *key,
                                             int level, u64 hint,
-                                            u64 empty_size)
+                                            u64 empty_size,
+                                            enum btrfs_lock_nesting nest)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
        struct btrfs_key ins;
@@ -4741,7 +4743,7 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
 #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
        if (btrfs_is_testing(fs_info)) {
                buf = btrfs_init_new_buffer(trans, root, root->alloc_bytenr,
-                                           level, root_objectid);
+                                           level, root_objectid, nest);
                if (!IS_ERR(buf))
                        root->alloc_bytenr += blocksize;
                return buf;
@@ -4758,7 +4760,7 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
                goto out_unuse;
 
        buf = btrfs_init_new_buffer(trans, root, ins.objectid, level,
-                                   root_objectid);
+                                   root_objectid, nest);
        if (IS_ERR(buf)) {
                ret = PTR_ERR(buf);
                goto out_free_reserved;
index 3779a6c12184c4bd84a8db65ed2015cb0ebb0998..b91444e810a5d372626c1018f1f3f598208549a5 100644 (file)
@@ -628,7 +628,8 @@ static noinline int create_subvol(struct inode *dir,
        if (ret)
                goto fail;
 
-       leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0);
+       leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0,
+                                     BTRFS_NESTING_NORMAL);
        if (IS_ERR(leaf)) {
                ret = PTR_ERR(leaf);
                goto fail;
index 5fabda8f0a80f8ab766137f88715299ed0f32a41..8b47ba34fb037f9f4daefcbebb606ecef8681c9b 100644 (file)
 enum btrfs_lock_nesting {
        BTRFS_NESTING_NORMAL,
 
+       /*
+        * When we COW a block we are holding the lock on the original block,
+        * and since our lockdep maps are rootid+level, this confuses lockdep
+        * when we lock the newly allocated COW'd block.  Handle this by having
+        * a subclass for COW'ed blocks so that lockdep doesn't complain.
+        */
+       BTRFS_NESTING_COW,
+
        /*
         * We are limited to MAX_LOCKDEP_SUBLCLASSES number of subclasses, so
         * add this in here and add a static_assert to keep us from going over
index 4ba1ab9cc76db89935ed619d8886715d0eeaa6ad..3602806d71bd4fa2c3688ce45ba0b4b299f6e3bb 100644 (file)
@@ -1206,7 +1206,8 @@ again:
        }
 
        if (cow) {
-               ret = btrfs_cow_block(trans, dest, eb, NULL, 0, &eb);
+               ret = btrfs_cow_block(trans, dest, eb, NULL, 0, &eb,
+                                     BTRFS_NESTING_COW);
                BUG_ON(ret);
        }
        btrfs_set_lock_blocking_write(eb);
@@ -1274,7 +1275,8 @@ again:
                        btrfs_tree_lock(eb);
                        if (cow) {
                                ret = btrfs_cow_block(trans, dest, eb, parent,
-                                                     slot, &eb);
+                                                     slot, &eb,
+                                                     BTRFS_NESTING_COW);
                                BUG_ON(ret);
                        }
                        btrfs_set_lock_blocking_write(eb);
@@ -1781,7 +1783,8 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
         * relocated and the block is tree root.
         */
        leaf = btrfs_lock_root_node(root);
-       ret = btrfs_cow_block(trans, root, leaf, NULL, 0, &leaf);
+       ret = btrfs_cow_block(trans, root, leaf, NULL, 0, &leaf,
+                             BTRFS_NESTING_COW);
        btrfs_tree_unlock(leaf);
        free_extent_buffer(leaf);
        if (ret < 0)
@@ -2308,7 +2311,7 @@ static int do_relocation(struct btrfs_trans_handle *trans,
 
                if (!node->eb) {
                        ret = btrfs_cow_block(trans, root, eb, upper->eb,
-                                             slot, &eb);
+                                             slot, &eb, BTRFS_NESTING_COW);
                        btrfs_tree_unlock(eb);
                        free_extent_buffer(eb);
                        if (ret < 0) {
index f8265dbec9c38e6313de45d290669b6ea02d2b21..52ada47aff50d7670bda6189cce558e6605d008e 100644 (file)
@@ -1184,7 +1184,7 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans)
 
        eb = btrfs_lock_root_node(fs_info->tree_root);
        ret = btrfs_cow_block(trans, fs_info->tree_root, eb, NULL,
-                             0, &eb);
+                             0, &eb, BTRFS_NESTING_COW);
        btrfs_tree_unlock(eb);
        free_extent_buffer(eb);
 
@@ -1589,7 +1589,8 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
        btrfs_set_root_otransid(new_root_item, trans->transid);
 
        old = btrfs_lock_root_node(root);
-       ret = btrfs_cow_block(trans, root, old, NULL, 0, &old);
+       ret = btrfs_cow_block(trans, root, old, NULL, 0, &old,
+                             BTRFS_NESTING_COW);
        if (ret) {
                btrfs_tree_unlock(old);
                free_extent_buffer(old);