Btrfs: Add a skip_locking parameter to struct path, and make various funcs honor it
authorChris Mason <chris.mason@oracle.com>
Wed, 25 Jun 2008 20:01:30 +0000 (16:01 -0400)
committerChris Mason <chris.mason@oracle.com>
Thu, 25 Sep 2008 15:04:03 +0000 (11:04 -0400)
Allocations may need to read in block groups from the extent allocation tree,
which will require a tree search and take locks on the extent allocation
tree.  But, those locks might already be held in other places, leading
to deadlocks.

Since the alloc_mutex serializes everything right now, it is safe to
skip the btree locking while caching block groups.  A better fix will be
to either create a recursive lock or find a way to back off existing
locks while caching block groups.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/extent-tree.c

index b8f7aecf68d72de145ed8ce710b11dd5e1862855..fd68601b60b1dedf5343776495a9a579d36fdea0 100644 (file)
@@ -64,6 +64,7 @@ void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p)
 {
        int i;
        int keep = p->keep_locks;
+       int skip = p->skip_locking;
 
        for (i = 0; i < BTRFS_MAX_LEVEL; i++) {
                if (!p->nodes[i])
@@ -76,6 +77,7 @@ void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p)
        }
        memset(p, 0, sizeof(*p));
        p->keep_locks = keep;
+       p->skip_locking = skip;
 }
 
 struct extent_buffer *btrfs_root_node(struct btrfs_root *root)
@@ -1262,7 +1264,10 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
        if (ins_len < 0)
                lowest_unlock = 2;
 again:
-       b = btrfs_lock_root_node(root);
+       if (p->skip_locking)
+               b = btrfs_root_node(root);
+       else
+               b = btrfs_lock_root_node(root);
 
        while (b) {
                level = btrfs_header_level(b);
@@ -1282,7 +1287,8 @@ again:
                        WARN_ON(1);
                level = btrfs_header_level(b);
                p->nodes[level] = b;
-               p->locks[level] = 1;
+               if (!p->skip_locking)
+                       p->locks[level] = 1;
                ret = check_block(root, p, level);
                if (ret)
                        return -1;
@@ -1349,7 +1355,8 @@ again:
                                        b = read_node_slot(root, b, slot);
                                }
                        }
-                       btrfs_tree_lock(b);
+                       if (!p->skip_locking)
+                               btrfs_tree_lock(b);
                        unlock_up(p, level, lowest_unlock);
                } else {
                        p->slots[level] = slot;
@@ -1392,13 +1399,6 @@ static int fixup_low_keys(struct btrfs_trans_handle *trans,
                        break;
                t = path->nodes[i];
                btrfs_set_node_key(t, key, tslot);
-               if (!btrfs_tree_locked(path->nodes[i])) {
-                       int ii;
-printk("fixup without lock on level %d\n", btrfs_header_level(path->nodes[i]));
-                       for (ii = 0; ii < BTRFS_MAX_LEVEL; ii++) {
-printk("level %d slot %d\n", ii, path->slots[ii]);
-                       }
-               }
                btrfs_mark_buffer_dirty(path->nodes[i]);
                if (tslot != 0)
                        break;
@@ -3033,8 +3033,10 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path)
                        reada_for_search(root, path, level, slot, 0);
 
                next = read_node_slot(root, c, slot);
-               WARN_ON(!btrfs_tree_locked(c));
-               btrfs_tree_lock(next);
+               if (!path->skip_locking) {
+                       WARN_ON(!btrfs_tree_locked(c));
+                       btrfs_tree_lock(next);
+               }
                break;
        }
        path->slots[level] = slot;
@@ -3052,8 +3054,10 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path)
                if (level == 1 && path->locks[1] && path->reada)
                        reada_for_search(root, path, level, slot, 0);
                next = read_node_slot(root, next, 0);
-               WARN_ON(!btrfs_tree_locked(path->nodes[level]));
-               btrfs_tree_lock(next);
+               if (!path->skip_locking) {
+                       WARN_ON(!btrfs_tree_locked(path->nodes[level]));
+                       btrfs_tree_lock(next);
+               }
        }
 done:
        unlock_up(path, 0, 1);
index 9ea12d42741c19a6be38279505490bd32a3d3758..e9bbb53eda63b6f19753f18fe80e20fa03ed53dd 100644 (file)
@@ -335,6 +335,7 @@ struct btrfs_path {
        int reada;
        /* keep some upper locks as we walk down */
        int keep_locks;
+       int skip_locking;
        int lowest_level;
 };
 
index 0905653dd3fc7eb6892a284b49f5b6feb34bc0bb..544fc3f2fe6c08fbfe4b9b8550f7a19efac9c4d4 100644 (file)
@@ -88,6 +88,12 @@ static int cache_block_group(struct btrfs_root *root,
                return -ENOMEM;
 
        path->reada = 2;
+       /*
+        * we get into deadlocks with paths held by callers of this function.
+        * since the alloc_mutex is protecting things right now, just
+        * skip the locking here
+        */
+       path->skip_locking = 1;
        first_free = block_group->key.objectid;
        key.objectid = block_group->key.objectid;
        key.offset = 0;