btrfs: qgroup: Add new qgroup calculation function
btrfs_qgroup_account_extents(). The new btrfs_qgroup_account_extents() function should be called in btrfs_commit_transaction() and it will update all the qgroup according to delayed_ref_root->dirty_extent_root. The new function can handle both normal operation during commit_transaction() or in rescan in a unified method with clearer logic. Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com> Signed-off-by: Chris Mason <clm@fb.com>
This commit is contained in:
parent
21633fc603
commit
550d7a2ed5
0
fs/btrfs/extent-tree.h
Normal file
0
fs/btrfs/extent-tree.h
Normal file
@ -2455,6 +2455,122 @@ static int btrfs_qgroup_account(struct btrfs_trans_handle *trans,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_fs_info *fs_info,
|
||||||
|
u64 bytenr, u64 num_bytes,
|
||||||
|
struct ulist *old_roots, struct ulist *new_roots)
|
||||||
|
{
|
||||||
|
struct ulist *qgroups = NULL;
|
||||||
|
struct ulist *tmp = NULL;
|
||||||
|
u64 seq;
|
||||||
|
u64 nr_new_roots = 0;
|
||||||
|
u64 nr_old_roots = 0;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (new_roots)
|
||||||
|
nr_new_roots = new_roots->nnodes;
|
||||||
|
if (old_roots)
|
||||||
|
nr_old_roots = old_roots->nnodes;
|
||||||
|
|
||||||
|
if (!fs_info->quota_enabled)
|
||||||
|
goto out_free;
|
||||||
|
BUG_ON(!fs_info->quota_root);
|
||||||
|
|
||||||
|
qgroups = ulist_alloc(GFP_NOFS);
|
||||||
|
if (!qgroups) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
tmp = ulist_alloc(GFP_NOFS);
|
||||||
|
if (!tmp) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&fs_info->qgroup_rescan_lock);
|
||||||
|
if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
|
||||||
|
if (fs_info->qgroup_rescan_progress.objectid <= bytenr) {
|
||||||
|
mutex_unlock(&fs_info->qgroup_rescan_lock);
|
||||||
|
ret = 0;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&fs_info->qgroup_rescan_lock);
|
||||||
|
|
||||||
|
spin_lock(&fs_info->qgroup_lock);
|
||||||
|
seq = fs_info->qgroup_seq;
|
||||||
|
|
||||||
|
/* Update old refcnts using old_roots */
|
||||||
|
ret = qgroup_update_refcnt(fs_info, old_roots, tmp, qgroups, seq,
|
||||||
|
UPDATE_OLD);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* Update new refcnts using new_roots */
|
||||||
|
ret = qgroup_update_refcnt(fs_info, new_roots, tmp, qgroups, seq,
|
||||||
|
UPDATE_NEW);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
qgroup_update_counters(fs_info, qgroups, nr_old_roots, nr_new_roots,
|
||||||
|
num_bytes, seq);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bump qgroup_seq to avoid seq overlap
|
||||||
|
*/
|
||||||
|
fs_info->qgroup_seq += max(nr_old_roots, nr_new_roots) + 1;
|
||||||
|
out:
|
||||||
|
spin_unlock(&fs_info->qgroup_lock);
|
||||||
|
out_free:
|
||||||
|
ulist_free(tmp);
|
||||||
|
ulist_free(qgroups);
|
||||||
|
ulist_free(old_roots);
|
||||||
|
ulist_free(new_roots);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_fs_info *fs_info)
|
||||||
|
{
|
||||||
|
struct btrfs_qgroup_extent_record *record;
|
||||||
|
struct btrfs_delayed_ref_root *delayed_refs;
|
||||||
|
struct ulist *new_roots = NULL;
|
||||||
|
struct rb_node *node;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
delayed_refs = &trans->transaction->delayed_refs;
|
||||||
|
while ((node = rb_first(&delayed_refs->dirty_extent_root))) {
|
||||||
|
record = rb_entry(node, struct btrfs_qgroup_extent_record,
|
||||||
|
node);
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
/*
|
||||||
|
* Use (u64)-1 as time_seq to do special search, which
|
||||||
|
* doesn't lock tree or delayed_refs and search current
|
||||||
|
* root. It's safe inside commit_transaction().
|
||||||
|
*/
|
||||||
|
ret = btrfs_find_all_roots(trans, fs_info,
|
||||||
|
record->bytenr, (u64)-1, &new_roots);
|
||||||
|
if (ret < 0)
|
||||||
|
goto cleanup;
|
||||||
|
ret = btrfs_qgroup_account_extent(trans, fs_info,
|
||||||
|
record->bytenr, record->num_bytes,
|
||||||
|
record->old_roots, new_roots);
|
||||||
|
record->old_roots = NULL;
|
||||||
|
new_roots = NULL;
|
||||||
|
}
|
||||||
|
cleanup:
|
||||||
|
ulist_free(record->old_roots);
|
||||||
|
ulist_free(new_roots);
|
||||||
|
new_roots = NULL;
|
||||||
|
rb_erase(node, &delayed_refs->dirty_extent_root);
|
||||||
|
kfree(record);
|
||||||
|
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Needs to be called everytime we run delayed refs, even if there is an error
|
* Needs to be called everytime we run delayed refs, even if there is an error
|
||||||
* in order to cleanup outstanding operations.
|
* in order to cleanup outstanding operations.
|
||||||
|
@ -103,6 +103,8 @@ int btrfs_qgroup_prepare_account_extents(struct btrfs_trans_handle *trans,
|
|||||||
struct btrfs_qgroup_extent_record
|
struct btrfs_qgroup_extent_record
|
||||||
*btrfs_qgroup_insert_dirty_extent(struct btrfs_delayed_ref_root *delayed_refs,
|
*btrfs_qgroup_insert_dirty_extent(struct btrfs_delayed_ref_root *delayed_refs,
|
||||||
struct btrfs_qgroup_extent_record *record);
|
struct btrfs_qgroup_extent_record *record);
|
||||||
|
int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_fs_info *fs_info);
|
||||||
int btrfs_delayed_qgroup_accounting(struct btrfs_trans_handle *trans,
|
int btrfs_delayed_qgroup_accounting(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_fs_info *fs_info);
|
struct btrfs_fs_info *fs_info);
|
||||||
void btrfs_remove_qgroup_operation(struct btrfs_trans_handle *trans,
|
void btrfs_remove_qgroup_operation(struct btrfs_trans_handle *trans,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user