@@ -1506,12 +1506,42 @@ struct btrfs_root *btrfs_read_tree_root(struct btrfs_root *tree_root,
return root;
}
+struct btrfs_new_fs_root_args *btrfs_alloc_new_fs_root_args(void)
+{
+ int err;
+ struct btrfs_new_fs_root_args *args;
+
+ args = kzalloc(sizeof(*args), GFP_KERNEL);
+ if (!args)
+ return ERR_PTR(-ENOMEM);
+
+ err = get_anon_bdev(&args->anon_dev);
+ if (err)
+ goto error;
+
+ return args;
+
+error:
+ btrfs_free_new_fs_root_args(args);
+ return ERR_PTR(err);
+}
+
+void btrfs_free_new_fs_root_args(struct btrfs_new_fs_root_args *args)
+{
+ if (!args)
+ return;
+ if (args->anon_dev)
+ free_anon_bdev(args->anon_dev);
+ kfree(args);
+}
+
/*
* Initialize subvolume root in-memory structure
*
* @anon_dev: anonymous device to attach to the root, if zero, allocate new
*/
-static int btrfs_init_fs_root(struct btrfs_root *root, dev_t anon_dev)
+static int btrfs_init_fs_root(struct btrfs_root *root,
+ struct btrfs_new_fs_root_args *new_fs_root_args)
{
int ret;
unsigned int nofs_flag;
@@ -1538,12 +1568,13 @@ static int btrfs_init_fs_root(struct btrfs_root *root, dev_t anon_dev)
*/
if (is_fstree(root->root_key.objectid) &&
btrfs_root_refs(&root->root_item) > 0) {
- if (!anon_dev) {
+ if (!new_fs_root_args || !new_fs_root_args->anon_dev) {
ret = get_anon_bdev(&root->anon_dev);
if (ret)
goto fail;
} else {
- root->anon_dev = anon_dev;
+ root->anon_dev = new_fs_root_args->anon_dev;
+ new_fs_root_args->anon_dev = 0;
}
}
@@ -1717,7 +1748,8 @@ void btrfs_free_fs_info(struct btrfs_fs_info *fs_info)
* for orphan roots
*/
static struct btrfs_root *btrfs_get_root_ref(struct btrfs_fs_info *fs_info,
- u64 objectid, dev_t anon_dev,
+ u64 objectid,
+ struct btrfs_new_fs_root_args *new_fs_root_args,
bool check_ref)
{
struct btrfs_root *root;
@@ -1731,8 +1763,8 @@ static struct btrfs_root *btrfs_get_root_ref(struct btrfs_fs_info *fs_info,
again:
root = btrfs_lookup_fs_root(fs_info, objectid);
if (root) {
- /* Shouldn't get preallocated anon_dev for cached roots */
- ASSERT(!anon_dev);
+ /* Shouldn't get new_fs_root_args for cached roots */
+ ASSERT(!new_fs_root_args);
if (check_ref && btrfs_root_refs(&root->root_item) == 0) {
btrfs_put_root(root);
return ERR_PTR(-ENOENT);
@@ -1752,7 +1784,7 @@ static struct btrfs_root *btrfs_get_root_ref(struct btrfs_fs_info *fs_info,
goto fail;
}
- ret = btrfs_init_fs_root(root, anon_dev);
+ ret = btrfs_init_fs_root(root, new_fs_root_args);
if (ret)
goto fail;
@@ -1782,14 +1814,6 @@ static struct btrfs_root *btrfs_get_root_ref(struct btrfs_fs_info *fs_info,
}
return root;
fail:
- /*
- * If our caller provided us an anonymous device, then it's his
- * responsibility to free it in case we fail. So we have to set our
- * root's anon_dev to 0 to avoid a double free, once by btrfs_put_root()
- * and once again by our caller.
- */
- if (anon_dev)
- root->anon_dev = 0;
btrfs_put_root(root);
return ERR_PTR(ret);
}
@@ -1804,7 +1828,7 @@ static struct btrfs_root *btrfs_get_root_ref(struct btrfs_fs_info *fs_info,
struct btrfs_root *btrfs_get_fs_root(struct btrfs_fs_info *fs_info,
u64 objectid, bool check_ref)
{
- return btrfs_get_root_ref(fs_info, objectid, 0, check_ref);
+ return btrfs_get_root_ref(fs_info, objectid, NULL, check_ref);
}
/*
@@ -1816,9 +1840,10 @@ struct btrfs_root *btrfs_get_fs_root(struct btrfs_fs_info *fs_info,
* parameter value
*/
struct btrfs_root *btrfs_get_new_fs_root(struct btrfs_fs_info *fs_info,
- u64 objectid, dev_t anon_dev)
+ u64 objectid,
+ struct btrfs_new_fs_root_args *new_fs_root_args)
{
- return btrfs_get_root_ref(fs_info, objectid, anon_dev, true);
+ return btrfs_get_root_ref(fs_info, objectid, new_fs_root_args, true);
}
/*
@@ -25,6 +25,11 @@ static inline u64 btrfs_sb_offset(int mirror)
return BTRFS_SUPER_INFO_OFFSET;
}
+struct btrfs_new_fs_root_args {
+ /* Preallocated anonymous block device number */
+ dev_t anon_dev;
+};
+
struct btrfs_device;
struct btrfs_fs_devices;
struct btrfs_tree_parent_check;
@@ -58,6 +63,8 @@ struct btrfs_super_block *btrfs_read_dev_one_super(struct block_device *bdev,
int btrfs_commit_super(struct btrfs_fs_info *fs_info);
struct btrfs_root *btrfs_read_tree_root(struct btrfs_root *tree_root,
struct btrfs_key *key);
+struct btrfs_new_fs_root_args *btrfs_alloc_new_fs_root_args(void);
+void btrfs_free_new_fs_root_args(struct btrfs_new_fs_root_args *args);
int btrfs_insert_fs_root(struct btrfs_fs_info *fs_info,
struct btrfs_root *root);
void btrfs_free_fs_roots(struct btrfs_fs_info *fs_info);
@@ -65,7 +72,8 @@ void btrfs_free_fs_roots(struct btrfs_fs_info *fs_info);
struct btrfs_root *btrfs_get_fs_root(struct btrfs_fs_info *fs_info,
u64 objectid, bool check_ref);
struct btrfs_root *btrfs_get_new_fs_root(struct btrfs_fs_info *fs_info,
- u64 objectid, dev_t anon_dev);
+ u64 objectid,
+ struct btrfs_new_fs_root_args *new_fs_root_args);
struct btrfs_root *btrfs_get_fs_root_commit_root(struct btrfs_fs_info *fs_info,
struct btrfs_path *path,
u64 objectid);
@@ -599,8 +599,8 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
};
unsigned int trans_num_items;
int ret;
- dev_t anon_dev;
u64 objectid;
+ struct btrfs_new_fs_root_args *new_fs_root_args = NULL;
root_item = kzalloc(sizeof(*root_item), GFP_KERNEL);
if (!root_item)
@@ -619,14 +619,17 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
goto out_root_item;
}
- ret = get_anon_bdev(&anon_dev);
- if (ret < 0)
+ new_fs_root_args = btrfs_alloc_new_fs_root_args();
+ if (IS_ERR(new_fs_root_args)) {
+ ret = PTR_ERR(new_fs_root_args);
+ new_fs_root_args = NULL;
goto out_root_item;
+ }
new_inode_args.inode = btrfs_new_subvol_inode(mnt_userns, dir);
if (!new_inode_args.inode) {
ret = -ENOMEM;
- goto out_anon_dev;
+ goto out_root_item;
}
ret = btrfs_new_inode_prepare(&new_inode_args, &trans_num_items);
if (ret)
@@ -717,14 +720,12 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
free_extent_buffer(leaf);
leaf = NULL;
- new_root = btrfs_get_new_fs_root(fs_info, objectid, anon_dev);
+ new_root = btrfs_get_new_fs_root(fs_info, objectid, new_fs_root_args);
if (IS_ERR(new_root)) {
ret = PTR_ERR(new_root);
btrfs_abort_transaction(trans, ret);
goto out;
}
- /* anon_dev is owned by new_root now. */
- anon_dev = 0;
BTRFS_I(new_inode_args.inode)->root = new_root;
/* ... and new_root is owned by new_inode_args.inode now. */
@@ -763,10 +764,8 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
btrfs_new_inode_args_destroy(&new_inode_args);
out_inode:
iput(new_inode_args.inode);
-out_anon_dev:
- if (anon_dev)
- free_anon_bdev(anon_dev);
out_root_item:
+ btrfs_free_new_fs_root_args(new_fs_root_args);
kfree(root_item);
return ret;
}
@@ -802,9 +801,13 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
if (!pending_snapshot)
return -ENOMEM;
- ret = get_anon_bdev(&pending_snapshot->anon_dev);
- if (ret < 0)
+ pending_snapshot->new_fs_root_args = btrfs_alloc_new_fs_root_args();
+ if (IS_ERR(pending_snapshot->new_fs_root_args)) {
+ ret = PTR_ERR(pending_snapshot->new_fs_root_args);
+ pending_snapshot->new_fs_root_args = NULL;
goto free_pending;
+ }
+
pending_snapshot->root_item = kzalloc(sizeof(struct btrfs_root_item),
GFP_KERNEL);
pending_snapshot->path = btrfs_alloc_path();
@@ -861,16 +864,11 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
d_instantiate(dentry, inode);
ret = 0;
- pending_snapshot->anon_dev = 0;
fail:
- /* Prevent double freeing of anon_dev */
- if (ret && pending_snapshot->snap)
- pending_snapshot->snap->anon_dev = 0;
btrfs_put_root(pending_snapshot->snap);
btrfs_subvolume_release_metadata(root, &pending_snapshot->block_rsv);
free_pending:
- if (pending_snapshot->anon_dev)
- free_anon_bdev(pending_snapshot->anon_dev);
+ btrfs_free_new_fs_root_args(pending_snapshot->new_fs_root_args);
kfree(pending_snapshot->root_item);
btrfs_free_path(pending_snapshot->path);
kfree(pending_snapshot);
@@ -1801,7 +1801,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
}
key.offset = (u64)-1;
- pending->snap = btrfs_get_new_fs_root(fs_info, objectid, pending->anon_dev);
+ pending->snap = btrfs_get_new_fs_root(fs_info, objectid, pending->new_fs_root_args);
if (IS_ERR(pending->snap)) {
ret = PTR_ERR(pending->snap);
pending->snap = NULL;
@@ -11,6 +11,7 @@
#include "delayed-ref.h"
#include "ctree.h"
#include "misc.h"
+#include "disk-io.h"
enum btrfs_trans_state {
TRANS_STATE_RUNNING,
@@ -163,8 +164,8 @@ struct btrfs_pending_snapshot {
struct btrfs_block_rsv block_rsv;
/* extra metadata reservation for relocation */
int error;
- /* Preallocated anonymous block device number */
- dev_t anon_dev;
+ /* Preallocated new_fs_root_args */
+ struct btrfs_new_fs_root_args *new_fs_root_args;
bool readonly;
struct list_head list;
};