Btrfs: hold the commit_root_sem when getting the commit root during send
We currently rely too heavily on roots being read-only to save us from just accessing root->commit_root. We can easily balance blocks out from underneath a read only root, so to save us from getting screwed make sure we only access root->commit_root under the commit root sem. Thanks, Signed-off-by: Josef Bacik <jbacik@fb.com> Signed-off-by: Chris Mason <clm@fb.com>
This commit is contained in:
parent
9e351cc862
commit
3f8a18cc53
|
@ -2769,9 +2769,13 @@ again:
|
||||||
* the commit roots are read only
|
* the commit roots are read only
|
||||||
* so we always do read locks
|
* so we always do read locks
|
||||||
*/
|
*/
|
||||||
|
if (p->need_commit_sem)
|
||||||
|
down_read(&root->fs_info->commit_root_sem);
|
||||||
b = root->commit_root;
|
b = root->commit_root;
|
||||||
extent_buffer_get(b);
|
extent_buffer_get(b);
|
||||||
level = btrfs_header_level(b);
|
level = btrfs_header_level(b);
|
||||||
|
if (p->need_commit_sem)
|
||||||
|
up_read(&root->fs_info->commit_root_sem);
|
||||||
if (!p->skip_locking)
|
if (!p->skip_locking)
|
||||||
btrfs_tree_read_lock(b);
|
btrfs_tree_read_lock(b);
|
||||||
} else {
|
} else {
|
||||||
|
@ -5436,6 +5440,7 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
|
||||||
* the right if possible or go up and right.
|
* the right if possible or go up and right.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
down_read(&left_root->fs_info->commit_root_sem);
|
||||||
left_level = btrfs_header_level(left_root->commit_root);
|
left_level = btrfs_header_level(left_root->commit_root);
|
||||||
left_root_level = left_level;
|
left_root_level = left_level;
|
||||||
left_path->nodes[left_level] = left_root->commit_root;
|
left_path->nodes[left_level] = left_root->commit_root;
|
||||||
|
@ -5445,6 +5450,7 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
|
||||||
right_root_level = right_level;
|
right_root_level = right_level;
|
||||||
right_path->nodes[right_level] = right_root->commit_root;
|
right_path->nodes[right_level] = right_root->commit_root;
|
||||||
extent_buffer_get(right_path->nodes[right_level]);
|
extent_buffer_get(right_path->nodes[right_level]);
|
||||||
|
up_read(&left_root->fs_info->commit_root_sem);
|
||||||
|
|
||||||
if (left_level == 0)
|
if (left_level == 0)
|
||||||
btrfs_item_key_to_cpu(left_path->nodes[left_level],
|
btrfs_item_key_to_cpu(left_path->nodes[left_level],
|
||||||
|
|
|
@ -609,6 +609,7 @@ struct btrfs_path {
|
||||||
unsigned int skip_locking:1;
|
unsigned int skip_locking:1;
|
||||||
unsigned int leave_spinning:1;
|
unsigned int leave_spinning:1;
|
||||||
unsigned int search_commit_root:1;
|
unsigned int search_commit_root:1;
|
||||||
|
unsigned int need_commit_sem:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -493,6 +493,7 @@ static struct btrfs_path *alloc_path_for_send(void)
|
||||||
return NULL;
|
return NULL;
|
||||||
path->search_commit_root = 1;
|
path->search_commit_root = 1;
|
||||||
path->skip_locking = 1;
|
path->skip_locking = 1;
|
||||||
|
path->need_commit_sem = 1;
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -771,29 +772,22 @@ out:
|
||||||
/*
|
/*
|
||||||
* Helper function to retrieve some fields from an inode item.
|
* Helper function to retrieve some fields from an inode item.
|
||||||
*/
|
*/
|
||||||
static int get_inode_info(struct btrfs_root *root,
|
static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
|
||||||
u64 ino, u64 *size, u64 *gen,
|
u64 ino, u64 *size, u64 *gen, u64 *mode, u64 *uid,
|
||||||
u64 *mode, u64 *uid, u64 *gid,
|
u64 *gid, u64 *rdev)
|
||||||
u64 *rdev)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct btrfs_inode_item *ii;
|
struct btrfs_inode_item *ii;
|
||||||
struct btrfs_key key;
|
struct btrfs_key key;
|
||||||
struct btrfs_path *path;
|
|
||||||
|
|
||||||
path = alloc_path_for_send();
|
|
||||||
if (!path)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
key.objectid = ino;
|
key.objectid = ino;
|
||||||
key.type = BTRFS_INODE_ITEM_KEY;
|
key.type = BTRFS_INODE_ITEM_KEY;
|
||||||
key.offset = 0;
|
key.offset = 0;
|
||||||
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
||||||
if (ret < 0)
|
|
||||||
goto out;
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ret = -ENOENT;
|
if (ret > 0)
|
||||||
goto out;
|
ret = -ENOENT;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ii = btrfs_item_ptr(path->nodes[0], path->slots[0],
|
ii = btrfs_item_ptr(path->nodes[0], path->slots[0],
|
||||||
|
@ -811,7 +805,22 @@ static int get_inode_info(struct btrfs_root *root,
|
||||||
if (rdev)
|
if (rdev)
|
||||||
*rdev = btrfs_inode_rdev(path->nodes[0], ii);
|
*rdev = btrfs_inode_rdev(path->nodes[0], ii);
|
||||||
|
|
||||||
out:
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_inode_info(struct btrfs_root *root,
|
||||||
|
u64 ino, u64 *size, u64 *gen,
|
||||||
|
u64 *mode, u64 *uid, u64 *gid,
|
||||||
|
u64 *rdev)
|
||||||
|
{
|
||||||
|
struct btrfs_path *path;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
path = alloc_path_for_send();
|
||||||
|
if (!path)
|
||||||
|
return -ENOMEM;
|
||||||
|
ret = __get_inode_info(root, path, ino, size, gen, mode, uid, gid,
|
||||||
|
rdev);
|
||||||
btrfs_free_path(path);
|
btrfs_free_path(path);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1085,6 +1094,7 @@ out:
|
||||||
struct backref_ctx {
|
struct backref_ctx {
|
||||||
struct send_ctx *sctx;
|
struct send_ctx *sctx;
|
||||||
|
|
||||||
|
struct btrfs_path *path;
|
||||||
/* number of total found references */
|
/* number of total found references */
|
||||||
u64 found;
|
u64 found;
|
||||||
|
|
||||||
|
@ -1155,8 +1165,9 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
|
||||||
* There are inodes that have extents that lie behind its i_size. Don't
|
* There are inodes that have extents that lie behind its i_size. Don't
|
||||||
* accept clones from these extents.
|
* accept clones from these extents.
|
||||||
*/
|
*/
|
||||||
ret = get_inode_info(found->root, ino, &i_size, NULL, NULL, NULL, NULL,
|
ret = __get_inode_info(found->root, bctx->path, ino, &i_size, NULL, NULL,
|
||||||
NULL);
|
NULL, NULL, NULL);
|
||||||
|
btrfs_release_path(bctx->path);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -1235,12 +1246,17 @@ static int find_extent_clone(struct send_ctx *sctx,
|
||||||
if (!tmp_path)
|
if (!tmp_path)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* We only use this path under the commit sem */
|
||||||
|
tmp_path->need_commit_sem = 0;
|
||||||
|
|
||||||
backref_ctx = kmalloc(sizeof(*backref_ctx), GFP_NOFS);
|
backref_ctx = kmalloc(sizeof(*backref_ctx), GFP_NOFS);
|
||||||
if (!backref_ctx) {
|
if (!backref_ctx) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backref_ctx->path = tmp_path;
|
||||||
|
|
||||||
if (data_offset >= ino_size) {
|
if (data_offset >= ino_size) {
|
||||||
/*
|
/*
|
||||||
* There may be extents that lie behind the file's size.
|
* There may be extents that lie behind the file's size.
|
||||||
|
|
Loading…
Reference in New Issue