btrfs: introduce btrfs_path::recurse
authorJosef Bacik <josef@toxicpanda.com>
Thu, 20 Aug 2020 15:46:01 +0000 (11:46 -0400)
committerDavid Sterba <dsterba@suse.com>
Wed, 7 Oct 2020 10:12:16 +0000 (12:12 +0200)
Our current tree locking stuff allows us to recurse with read locks if
we're already holding the write lock.  This is necessary for the space
cache inode, as we could be holding a lock on the root_tree root when we
need to cache a block group, and thus need to be able to read down the
root_tree to read in the inode cache.

We can get away with this in our current locking, but we won't be able
to with a rwsem.  Handle this by purposefully annotating the places
where we require recursion, so that in the future we can maybe come up
with a way to avoid the recursion.  In the case of the free space inode,
this will be superseded by the free space tree.

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/inode.c
fs/btrfs/locking.c
fs/btrfs/locking.h

index be7d01055118963602df040d73aab25e5b61165b..7c99bf112960f01291366084ff4f85283f9fe27f 100644 (file)
@@ -2601,7 +2601,7 @@ static struct extent_buffer *btrfs_search_slot_get_root(struct btrfs_root *root,
                 * We don't know the level of the root node until we actually
                 * have it read locked
                 */
-               b = btrfs_read_lock_root_node(root);
+               b = __btrfs_read_lock_root_node(root, p->recurse);
                level = btrfs_header_level(b);
                if (level > write_lock_level)
                        goto out;
@@ -2875,7 +2875,7 @@ cow_done:
                        } else {
                                if (!btrfs_tree_read_lock_atomic(b)) {
                                        btrfs_set_path_blocking(p);
-                                       btrfs_tree_read_lock(b);
+                                       __btrfs_tree_read_lock(b, p->recurse);
                                }
                                p->locks[level] = BTRFS_READ_LOCK;
                        }
@@ -5453,7 +5453,7 @@ again:
                        }
                        if (!ret) {
                                btrfs_set_path_blocking(path);
-                               btrfs_tree_read_lock(next);
+                               __btrfs_tree_read_lock(next, path->recurse);
                        }
                        next_rw_lock = BTRFS_READ_LOCK;
                }
@@ -5488,7 +5488,7 @@ again:
                        ret = btrfs_try_tree_read_lock(next);
                        if (!ret) {
                                btrfs_set_path_blocking(path);
-                               btrfs_tree_read_lock(next);
+                               __btrfs_tree_read_lock(next, path->recurse);
                        }
                        next_rw_lock = BTRFS_READ_LOCK;
                }
index f9d4e0958e2e5394c172e80ff7297653201c94d7..1339e390a7571e918d8d52388dec4245e6c4a1e3 100644 (file)
@@ -374,6 +374,7 @@ struct btrfs_path {
        unsigned int search_commit_root:1;
        unsigned int need_commit_sem:1;
        unsigned int skip_release_on_error:1;
+       unsigned int recurse:1;
 };
 #define BTRFS_MAX_EXTENT_ITEM_SIZE(r) ((BTRFS_LEAF_DATA_SIZE(r->fs_info) >> 4) - \
                                        sizeof(struct btrfs_item))
@@ -2654,8 +2655,6 @@ void btrfs_set_item_key_safe(struct btrfs_fs_info *fs_info,
                             struct btrfs_path *path,
                             const struct btrfs_key *new_key);
 struct extent_buffer *btrfs_root_node(struct btrfs_root *root);
-struct extent_buffer *btrfs_lock_root_node(struct btrfs_root *root);
-struct extent_buffer *btrfs_read_lock_root_node(struct btrfs_root *root);
 int btrfs_find_next_key(struct btrfs_root *root, struct btrfs_path *path,
                        struct btrfs_key *key, int lowest_level,
                        u64 min_trans);
index 123521aa5595261c8a129a7c9238a6bac17d0fd1..1fbc7d9e5103ba62746ced1eff7cd2e9b2fb947f 100644 (file)
@@ -6585,6 +6585,8 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
         */
        path->leave_spinning = 1;
 
+       path->recurse = btrfs_is_free_space_inode(inode);
+
        ret = btrfs_lookup_file_extent(NULL, root, path, objectid, start, 0);
        if (ret < 0) {
                goto out;
index 14ceb2ce33ac057d0d397588aa77ea8bb06e9a16..740a0cde57314e8104caf82e8142a56982bf223f 100644 (file)
@@ -244,7 +244,7 @@ void btrfs_set_lock_blocking_write(struct extent_buffer *eb)
  *
  * The rwlock is held upon exit.
  */
-void btrfs_tree_read_lock(struct extent_buffer *eb)
+void __btrfs_tree_read_lock(struct extent_buffer *eb, bool recurse)
 {
        u64 start_ns = 0;
 
@@ -263,6 +263,7 @@ again:
                         * depends on this as it may be called on a partly
                         * (write-)locked tree.
                         */
+                       WARN_ON(!recurse);
                        BUG_ON(eb->lock_recursed);
                        eb->lock_recursed = true;
                        read_unlock(&eb->lock);
@@ -279,6 +280,11 @@ again:
        trace_btrfs_tree_read_lock(eb, start_ns);
 }
 
+void btrfs_tree_read_lock(struct extent_buffer *eb)
+{
+       __btrfs_tree_read_lock(eb, false);
+}
+
 /*
  * Lock extent buffer for read, optimistically expecting that there are no
  * contending blocking writers. If there are, don't wait.
@@ -552,13 +558,14 @@ struct extent_buffer *btrfs_lock_root_node(struct btrfs_root *root)
  *
  * Return: root extent buffer with read lock held
  */
-struct extent_buffer *btrfs_read_lock_root_node(struct btrfs_root *root)
+struct extent_buffer *__btrfs_read_lock_root_node(struct btrfs_root *root,
+                                                 bool recurse)
 {
        struct extent_buffer *eb;
 
        while (1) {
                eb = btrfs_root_node(root);
-               btrfs_tree_read_lock(eb);
+               __btrfs_tree_read_lock(eb, recurse);
                if (eb == root->node)
                        break;
                btrfs_tree_read_unlock(eb);
index d715846c10b8d3b40c00d4c74febed9196c95d30..31f6d6405c1de104fb656b0e9678f0185befc601 100644 (file)
@@ -21,6 +21,7 @@ struct btrfs_path;
 void btrfs_tree_lock(struct extent_buffer *eb);
 void btrfs_tree_unlock(struct extent_buffer *eb);
 
+void __btrfs_tree_read_lock(struct extent_buffer *eb, bool recurse);
 void btrfs_tree_read_lock(struct extent_buffer *eb);
 void btrfs_tree_read_unlock(struct extent_buffer *eb);
 void btrfs_tree_read_unlock_blocking(struct extent_buffer *eb);
@@ -29,6 +30,14 @@ void btrfs_set_lock_blocking_write(struct extent_buffer *eb);
 int btrfs_try_tree_read_lock(struct extent_buffer *eb);
 int btrfs_try_tree_write_lock(struct extent_buffer *eb);
 int btrfs_tree_read_lock_atomic(struct extent_buffer *eb);
+struct extent_buffer *btrfs_lock_root_node(struct btrfs_root *root);
+struct extent_buffer *__btrfs_read_lock_root_node(struct btrfs_root *root,
+                                                 bool recurse);
+
+static inline struct extent_buffer *btrfs_read_lock_root_node(struct btrfs_root *root)
+{
+       return __btrfs_read_lock_root_node(root, false);
+}
 
 #ifdef CONFIG_BTRFS_DEBUG
 static inline void btrfs_assert_tree_locked(struct extent_buffer *eb) {