Merge tag 'for-5.10-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[sfrench/cifs-2.6.git] / fs / btrfs / disk-io.c
index 8e3438672a82d2f53c78922fa43572e1fc130eb8..af97ddcc6b3e8710cf9a55a4064192b3b1027b81 100644 (file)
@@ -1281,32 +1281,26 @@ int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
        return 0;
 }
 
-struct btrfs_root *btrfs_read_tree_root(struct btrfs_root *tree_root,
-                                       struct btrfs_key *key)
+static struct btrfs_root *read_tree_root_path(struct btrfs_root *tree_root,
+                                             struct btrfs_path *path,
+                                             struct btrfs_key *key)
 {
        struct btrfs_root *root;
        struct btrfs_fs_info *fs_info = tree_root->fs_info;
-       struct btrfs_path *path;
        u64 generation;
        int ret;
        int level;
 
-       path = btrfs_alloc_path();
-       if (!path)
-               return ERR_PTR(-ENOMEM);
-
        root = btrfs_alloc_root(fs_info, key->objectid, GFP_NOFS);
-       if (!root) {
-               ret = -ENOMEM;
-               goto alloc_fail;
-       }
+       if (!root)
+               return ERR_PTR(-ENOMEM);
 
        ret = btrfs_find_root(tree_root, key, path,
                              &root->root_item, &root->root_key);
        if (ret) {
                if (ret > 0)
                        ret = -ENOENT;
-               goto find_fail;
+               goto fail;
        }
 
        generation = btrfs_root_generation(&root->root_item);
@@ -1317,21 +1311,31 @@ struct btrfs_root *btrfs_read_tree_root(struct btrfs_root *tree_root,
        if (IS_ERR(root->node)) {
                ret = PTR_ERR(root->node);
                root->node = NULL;
-               goto find_fail;
+               goto fail;
        } else if (!btrfs_buffer_uptodate(root->node, generation, 0)) {
                ret = -EIO;
-               goto find_fail;
+               goto fail;
        }
        root->commit_root = btrfs_root_node(root);
-out:
-       btrfs_free_path(path);
        return root;
-
-find_fail:
+fail:
        btrfs_put_root(root);
-alloc_fail:
-       root = ERR_PTR(ret);
-       goto out;
+       return ERR_PTR(ret);
+}
+
+struct btrfs_root *btrfs_read_tree_root(struct btrfs_root *tree_root,
+                                       struct btrfs_key *key)
+{
+       struct btrfs_root *root;
+       struct btrfs_path *path;
+
+       path = btrfs_alloc_path();
+       if (!path)
+               return ERR_PTR(-ENOMEM);
+       root = read_tree_root_path(tree_root, path, key);
+       btrfs_free_path(path);
+
+       return root;
 }
 
 /*
@@ -1419,6 +1423,31 @@ static struct btrfs_root *btrfs_lookup_fs_root(struct btrfs_fs_info *fs_info,
        return root;
 }
 
+static struct btrfs_root *btrfs_get_global_root(struct btrfs_fs_info *fs_info,
+                                               u64 objectid)
+{
+       if (objectid == BTRFS_ROOT_TREE_OBJECTID)
+               return btrfs_grab_root(fs_info->tree_root);
+       if (objectid == BTRFS_EXTENT_TREE_OBJECTID)
+               return btrfs_grab_root(fs_info->extent_root);
+       if (objectid == BTRFS_CHUNK_TREE_OBJECTID)
+               return btrfs_grab_root(fs_info->chunk_root);
+       if (objectid == BTRFS_DEV_TREE_OBJECTID)
+               return btrfs_grab_root(fs_info->dev_root);
+       if (objectid == BTRFS_CSUM_TREE_OBJECTID)
+               return btrfs_grab_root(fs_info->csum_root);
+       if (objectid == BTRFS_QUOTA_TREE_OBJECTID)
+               return btrfs_grab_root(fs_info->quota_root) ?
+                       fs_info->quota_root : ERR_PTR(-ENOENT);
+       if (objectid == BTRFS_UUID_TREE_OBJECTID)
+               return btrfs_grab_root(fs_info->uuid_root) ?
+                       fs_info->uuid_root : ERR_PTR(-ENOENT);
+       if (objectid == BTRFS_FREE_SPACE_TREE_OBJECTID)
+               return btrfs_grab_root(fs_info->free_space_root) ?
+                       fs_info->free_space_root : ERR_PTR(-ENOENT);
+       return NULL;
+}
+
 int btrfs_insert_fs_root(struct btrfs_fs_info *fs_info,
                         struct btrfs_root *root)
 {
@@ -1518,25 +1547,9 @@ static struct btrfs_root *btrfs_get_root_ref(struct btrfs_fs_info *fs_info,
        struct btrfs_key key;
        int ret;
 
-       if (objectid == BTRFS_ROOT_TREE_OBJECTID)
-               return btrfs_grab_root(fs_info->tree_root);
-       if (objectid == BTRFS_EXTENT_TREE_OBJECTID)
-               return btrfs_grab_root(fs_info->extent_root);
-       if (objectid == BTRFS_CHUNK_TREE_OBJECTID)
-               return btrfs_grab_root(fs_info->chunk_root);
-       if (objectid == BTRFS_DEV_TREE_OBJECTID)
-               return btrfs_grab_root(fs_info->dev_root);
-       if (objectid == BTRFS_CSUM_TREE_OBJECTID)
-               return btrfs_grab_root(fs_info->csum_root);
-       if (objectid == BTRFS_QUOTA_TREE_OBJECTID)
-               return btrfs_grab_root(fs_info->quota_root) ?
-                       fs_info->quota_root : ERR_PTR(-ENOENT);
-       if (objectid == BTRFS_UUID_TREE_OBJECTID)
-               return btrfs_grab_root(fs_info->uuid_root) ?
-                       fs_info->uuid_root : ERR_PTR(-ENOENT);
-       if (objectid == BTRFS_FREE_SPACE_TREE_OBJECTID)
-               return btrfs_grab_root(fs_info->free_space_root) ?
-                       fs_info->free_space_root : ERR_PTR(-ENOENT);
+       root = btrfs_get_global_root(fs_info, objectid);
+       if (root)
+               return root;
 again:
        root = btrfs_lookup_fs_root(fs_info, objectid);
        if (root) {
@@ -1621,6 +1634,52 @@ struct btrfs_root *btrfs_get_new_fs_root(struct btrfs_fs_info *fs_info,
        return btrfs_get_root_ref(fs_info, objectid, anon_dev, true);
 }
 
+/*
+ * btrfs_get_fs_root_commit_root - return a root for the given objectid
+ * @fs_info:   the fs_info
+ * @objectid:  the objectid we need to lookup
+ *
+ * This is exclusively used for backref walking, and exists specifically because
+ * of how qgroups does lookups.  Qgroups will do a backref lookup at delayed ref
+ * creation time, which means we may have to read the tree_root in order to look
+ * up a fs root that is not in memory.  If the root is not in memory we will
+ * read the tree root commit root and look up the fs root from there.  This is a
+ * temporary root, it will not be inserted into the radix tree as it doesn't
+ * have the most uptodate information, it'll simply be discarded once the
+ * backref code is finished using the root.
+ */
+struct btrfs_root *btrfs_get_fs_root_commit_root(struct btrfs_fs_info *fs_info,
+                                                struct btrfs_path *path,
+                                                u64 objectid)
+{
+       struct btrfs_root *root;
+       struct btrfs_key key;
+
+       ASSERT(path->search_commit_root && path->skip_locking);
+
+       /*
+        * This can return -ENOENT if we ask for a root that doesn't exist, but
+        * since this is called via the backref walking code we won't be looking
+        * up a root that doesn't exist, unless there's corruption.  So if root
+        * != NULL just return it.
+        */
+       root = btrfs_get_global_root(fs_info, objectid);
+       if (root)
+               return root;
+
+       root = btrfs_lookup_fs_root(fs_info, objectid);
+       if (root)
+               return root;
+
+       key.objectid = objectid;
+       key.type = BTRFS_ROOT_ITEM_KEY;
+       key.offset = (u64)-1;
+       root = read_tree_root_path(fs_info->tree_root, path, &key);
+       btrfs_release_path(path);
+
+       return root;
+}
+
 /*
  * called by the kthread helper functions to finally call the bio end_io
  * functions.  This is where read checksum verification actually happens