Btrfs: fix wrong reserved space in qgroup during snap/subv creation
There are two problems in the space reservation of the snapshot/ subvolume creation. - don't reserve the space for the root item insertion - the space which is reserved in the qgroup is different with the free space reservation. we need reserve free space for 7 items, but in qgroup reservation, we need reserve space only for 3 items. So we implement new metadata reservation functions for the snapshot/subvolume creation. Signed-off-by: Miao Xie <miaox@cn.fujitsu.com> Signed-off-by: Josef Bacik <jbacik@fusionio.com>
This commit is contained in:
parent
e9662f701c
commit
d5c1207017
|
@ -3106,8 +3106,13 @@ void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans,
|
||||||
int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans,
|
int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans,
|
||||||
struct inode *inode);
|
struct inode *inode);
|
||||||
void btrfs_orphan_release_metadata(struct inode *inode);
|
void btrfs_orphan_release_metadata(struct inode *inode);
|
||||||
int btrfs_snap_reserve_metadata(struct btrfs_trans_handle *trans,
|
int btrfs_subvolume_reserve_metadata(struct btrfs_root *root,
|
||||||
struct btrfs_pending_snapshot *pending);
|
struct btrfs_block_rsv *rsv,
|
||||||
|
int nitems,
|
||||||
|
u64 *qgroup_reserved);
|
||||||
|
void btrfs_subvolume_release_metadata(struct btrfs_root *root,
|
||||||
|
struct btrfs_block_rsv *rsv,
|
||||||
|
u64 qgroup_reserved);
|
||||||
int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes);
|
int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes);
|
||||||
void btrfs_delalloc_release_metadata(struct inode *inode, u64 num_bytes);
|
void btrfs_delalloc_release_metadata(struct inode *inode, u64 num_bytes);
|
||||||
int btrfs_delalloc_reserve_space(struct inode *inode, u64 num_bytes);
|
int btrfs_delalloc_reserve_space(struct inode *inode, u64 num_bytes);
|
||||||
|
|
|
@ -4560,19 +4560,60 @@ void btrfs_orphan_release_metadata(struct inode *inode)
|
||||||
btrfs_block_rsv_release(root, root->orphan_block_rsv, num_bytes);
|
btrfs_block_rsv_release(root, root->orphan_block_rsv, num_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
int btrfs_snap_reserve_metadata(struct btrfs_trans_handle *trans,
|
|
||||||
struct btrfs_pending_snapshot *pending)
|
|
||||||
{
|
|
||||||
struct btrfs_root *root = pending->root;
|
|
||||||
struct btrfs_block_rsv *src_rsv = get_block_rsv(trans, root);
|
|
||||||
struct btrfs_block_rsv *dst_rsv = &pending->block_rsv;
|
|
||||||
/*
|
/*
|
||||||
* two for root back/forward refs, two for directory entries,
|
* btrfs_subvolume_reserve_metadata() - reserve space for subvolume operation
|
||||||
* one for root of the snapshot and one for parent inode.
|
* root: the root of the parent directory
|
||||||
|
* rsv: block reservation
|
||||||
|
* items: the number of items that we need do reservation
|
||||||
|
* qgroup_reserved: used to return the reserved size in qgroup
|
||||||
|
*
|
||||||
|
* This function is used to reserve the space for snapshot/subvolume
|
||||||
|
* creation and deletion. Those operations are different with the
|
||||||
|
* common file/directory operations, they change two fs/file trees
|
||||||
|
* and root tree, the number of items that the qgroup reserves is
|
||||||
|
* different with the free space reservation. So we can not use
|
||||||
|
* the space reseravtion mechanism in start_transaction().
|
||||||
*/
|
*/
|
||||||
u64 num_bytes = btrfs_calc_trans_metadata_size(root, 6);
|
int btrfs_subvolume_reserve_metadata(struct btrfs_root *root,
|
||||||
dst_rsv->space_info = src_rsv->space_info;
|
struct btrfs_block_rsv *rsv,
|
||||||
return block_rsv_migrate_bytes(src_rsv, dst_rsv, num_bytes);
|
int items,
|
||||||
|
u64 *qgroup_reserved)
|
||||||
|
{
|
||||||
|
u64 num_bytes;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (root->fs_info->quota_enabled) {
|
||||||
|
/* One for parent inode, two for dir entries */
|
||||||
|
num_bytes = 3 * root->leafsize;
|
||||||
|
ret = btrfs_qgroup_reserve(root, num_bytes);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
num_bytes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*qgroup_reserved = num_bytes;
|
||||||
|
|
||||||
|
num_bytes = btrfs_calc_trans_metadata_size(root, items);
|
||||||
|
rsv->space_info = __find_space_info(root->fs_info,
|
||||||
|
BTRFS_BLOCK_GROUP_METADATA);
|
||||||
|
ret = btrfs_block_rsv_add(root, rsv, num_bytes,
|
||||||
|
BTRFS_RESERVE_FLUSH_ALL);
|
||||||
|
if (ret) {
|
||||||
|
if (*qgroup_reserved)
|
||||||
|
btrfs_qgroup_free(root, *qgroup_reserved);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void btrfs_subvolume_release_metadata(struct btrfs_root *root,
|
||||||
|
struct btrfs_block_rsv *rsv,
|
||||||
|
u64 qgroup_reserved)
|
||||||
|
{
|
||||||
|
btrfs_block_rsv_release(root, rsv, (u64)-1);
|
||||||
|
if (qgroup_reserved)
|
||||||
|
btrfs_qgroup_free(root, qgroup_reserved);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -363,7 +363,7 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static noinline int create_subvol(struct btrfs_root *root,
|
static noinline int create_subvol(struct inode *dir,
|
||||||
struct dentry *dentry,
|
struct dentry *dentry,
|
||||||
char *name, int namelen,
|
char *name, int namelen,
|
||||||
u64 *async_transid,
|
u64 *async_transid,
|
||||||
|
@ -374,32 +374,39 @@ static noinline int create_subvol(struct btrfs_root *root,
|
||||||
struct btrfs_root_item root_item;
|
struct btrfs_root_item root_item;
|
||||||
struct btrfs_inode_item *inode_item;
|
struct btrfs_inode_item *inode_item;
|
||||||
struct extent_buffer *leaf;
|
struct extent_buffer *leaf;
|
||||||
|
struct btrfs_root *root = BTRFS_I(dir)->root;
|
||||||
struct btrfs_root *new_root;
|
struct btrfs_root *new_root;
|
||||||
struct dentry *parent = dentry->d_parent;
|
struct btrfs_block_rsv block_rsv;
|
||||||
struct inode *dir;
|
|
||||||
struct timespec cur_time = CURRENT_TIME;
|
struct timespec cur_time = CURRENT_TIME;
|
||||||
int ret;
|
int ret;
|
||||||
int err;
|
int err;
|
||||||
u64 objectid;
|
u64 objectid;
|
||||||
u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;
|
u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;
|
||||||
u64 index = 0;
|
u64 index = 0;
|
||||||
|
u64 qgroup_reserved;
|
||||||
uuid_le new_uuid;
|
uuid_le new_uuid;
|
||||||
|
|
||||||
ret = btrfs_find_free_objectid(root->fs_info->tree_root, &objectid);
|
ret = btrfs_find_free_objectid(root->fs_info->tree_root, &objectid);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
dir = parent->d_inode;
|
btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 1 - inode item
|
* The same as the snapshot creation, please see the comment
|
||||||
* 2 - refs
|
* of create_snapshot().
|
||||||
* 1 - root item
|
|
||||||
* 2 - dir items
|
|
||||||
*/
|
*/
|
||||||
trans = btrfs_start_transaction(root, 6);
|
ret = btrfs_subvolume_reserve_metadata(root, &block_rsv,
|
||||||
if (IS_ERR(trans))
|
7, &qgroup_reserved);
|
||||||
return PTR_ERR(trans);
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
trans = btrfs_start_transaction(root, 0);
|
||||||
|
if (IS_ERR(trans)) {
|
||||||
|
ret = PTR_ERR(trans);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
trans->block_rsv = &block_rsv;
|
||||||
|
trans->bytes_reserved = block_rsv.size;
|
||||||
|
|
||||||
ret = btrfs_qgroup_inherit(trans, root->fs_info, 0, objectid, inherit);
|
ret = btrfs_qgroup_inherit(trans, root->fs_info, 0, objectid, inherit);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -515,6 +522,8 @@ static noinline int create_subvol(struct btrfs_root *root,
|
||||||
BUG_ON(ret);
|
BUG_ON(ret);
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
trans->block_rsv = NULL;
|
||||||
|
trans->bytes_reserved = 0;
|
||||||
if (async_transid) {
|
if (async_transid) {
|
||||||
*async_transid = trans->transid;
|
*async_transid = trans->transid;
|
||||||
err = btrfs_commit_transaction_async(trans, root, 1);
|
err = btrfs_commit_transaction_async(trans, root, 1);
|
||||||
|
@ -526,7 +535,8 @@ fail:
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry));
|
d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry));
|
||||||
|
out:
|
||||||
|
btrfs_subvolume_release_metadata(root, &block_rsv, qgroup_reserved);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,21 +559,31 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
|
||||||
|
|
||||||
btrfs_init_block_rsv(&pending_snapshot->block_rsv,
|
btrfs_init_block_rsv(&pending_snapshot->block_rsv,
|
||||||
BTRFS_BLOCK_RSV_TEMP);
|
BTRFS_BLOCK_RSV_TEMP);
|
||||||
|
/*
|
||||||
|
* 1 - parent dir inode
|
||||||
|
* 2 - dir entries
|
||||||
|
* 1 - root item
|
||||||
|
* 2 - root ref/backref
|
||||||
|
* 1 - root of snapshot
|
||||||
|
*/
|
||||||
|
ret = btrfs_subvolume_reserve_metadata(BTRFS_I(dir)->root,
|
||||||
|
&pending_snapshot->block_rsv, 7,
|
||||||
|
&pending_snapshot->qgroup_reserved);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
pending_snapshot->dentry = dentry;
|
pending_snapshot->dentry = dentry;
|
||||||
pending_snapshot->root = root;
|
pending_snapshot->root = root;
|
||||||
pending_snapshot->readonly = readonly;
|
pending_snapshot->readonly = readonly;
|
||||||
pending_snapshot->dir = dir;
|
pending_snapshot->dir = dir;
|
||||||
pending_snapshot->inherit = inherit;
|
pending_snapshot->inherit = inherit;
|
||||||
|
|
||||||
trans = btrfs_start_transaction(root->fs_info->extent_root, 6);
|
trans = btrfs_start_transaction(root, 0);
|
||||||
if (IS_ERR(trans)) {
|
if (IS_ERR(trans)) {
|
||||||
ret = PTR_ERR(trans);
|
ret = PTR_ERR(trans);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = btrfs_snap_reserve_metadata(trans, pending_snapshot);
|
|
||||||
BUG_ON(ret);
|
|
||||||
|
|
||||||
spin_lock(&root->fs_info->trans_lock);
|
spin_lock(&root->fs_info->trans_lock);
|
||||||
list_add(&pending_snapshot->list,
|
list_add(&pending_snapshot->list,
|
||||||
&trans->transaction->pending_snapshots);
|
&trans->transaction->pending_snapshots);
|
||||||
|
@ -600,6 +620,10 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
|
||||||
d_instantiate(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
fail:
|
fail:
|
||||||
|
btrfs_subvolume_release_metadata(BTRFS_I(dir)->root,
|
||||||
|
&pending_snapshot->block_rsv,
|
||||||
|
pending_snapshot->qgroup_reserved);
|
||||||
|
out:
|
||||||
kfree(pending_snapshot);
|
kfree(pending_snapshot);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -733,8 +757,8 @@ static noinline int btrfs_mksubvol(struct path *parent,
|
||||||
error = create_snapshot(snap_src, dir, dentry, name, namelen,
|
error = create_snapshot(snap_src, dir, dentry, name, namelen,
|
||||||
async_transid, readonly, inherit);
|
async_transid, readonly, inherit);
|
||||||
} else {
|
} else {
|
||||||
error = create_subvol(BTRFS_I(dir)->root, dentry,
|
error = create_subvol(dir, dentry, name, namelen,
|
||||||
name, namelen, async_transid, inherit);
|
async_transid, inherit);
|
||||||
}
|
}
|
||||||
if (!error)
|
if (!error)
|
||||||
fsnotify_mkdir(dir, dentry);
|
fsnotify_mkdir(dir, dentry);
|
||||||
|
|
|
@ -1082,7 +1082,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
||||||
path = btrfs_alloc_path();
|
path = btrfs_alloc_path();
|
||||||
if (!path) {
|
if (!path) {
|
||||||
ret = pending->error = -ENOMEM;
|
ret = pending->error = -ENOMEM;
|
||||||
goto path_alloc_fail;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS);
|
new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS);
|
||||||
|
@ -1279,8 +1279,6 @@ no_free_objectid:
|
||||||
kfree(new_root_item);
|
kfree(new_root_item);
|
||||||
root_item_alloc_fail:
|
root_item_alloc_fail:
|
||||||
btrfs_free_path(path);
|
btrfs_free_path(path);
|
||||||
path_alloc_fail:
|
|
||||||
btrfs_block_rsv_release(root, &pending->block_rsv, (u64)-1);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,7 @@ struct btrfs_pending_snapshot {
|
||||||
struct btrfs_qgroup_inherit *inherit;
|
struct btrfs_qgroup_inherit *inherit;
|
||||||
/* block reservation for the operation */
|
/* block reservation for the operation */
|
||||||
struct btrfs_block_rsv block_rsv;
|
struct btrfs_block_rsv block_rsv;
|
||||||
|
u64 qgroup_reserved;
|
||||||
/* extra metadata reseration for relocation */
|
/* extra metadata reseration for relocation */
|
||||||
int error;
|
int error;
|
||||||
bool readonly;
|
bool readonly;
|
||||||
|
|
Loading…
Reference in New Issue