Commit d5c12070 authored by Miao Xie's avatar Miao Xie Committed by Josef Bacik

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: default avatarMiao Xie <miaox@cn.fujitsu.com>
Signed-off-by: default avatarJosef Bacik <jbacik@fusionio.com>
parent e9662f70
...@@ -3106,8 +3106,13 @@ void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans, ...@@ -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) ...@@ -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) * btrfs_subvolume_reserve_metadata() - reserve space for subvolume operation
* 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().
*/
int btrfs_subvolume_reserve_metadata(struct btrfs_root *root,
struct btrfs_block_rsv *rsv,
int items,
u64 *qgroup_reserved)
{ {
struct btrfs_root *root = pending->root; u64 num_bytes;
struct btrfs_block_rsv *src_rsv = get_block_rsv(trans, root); int ret;
struct btrfs_block_rsv *dst_rsv = &pending->block_rsv;
/* if (root->fs_info->quota_enabled) {
* two for root back/forward refs, two for directory entries, /* One for parent inode, two for dir entries */
* one for root of the snapshot and one for parent inode. num_bytes = 3 * root->leafsize;
*/ ret = btrfs_qgroup_reserve(root, num_bytes);
u64 num_bytes = btrfs_calc_trans_metadata_size(root, 6); if (ret)
dst_rsv->space_info = src_rsv->space_info; return ret;
return block_rsv_migrate_bytes(src_rsv, dst_rsv, num_bytes); } 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) ...@@ -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, ...@@ -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, ...@@ -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 @@ static noinline int create_subvol(struct btrfs_root *root, ...@@ -526,7 +535,8 @@ static noinline int create_subvol(struct btrfs_root *root,
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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, ...@@ -1279,8 +1279,6 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
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 { ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment