Skip to content

Commit 3cce39a

Browse files
jc2870kdave
authored andcommitted
btrfs: qgroup: use xarray to track dirty extents in transaction
Use xarray to track dirty extents to reduce the size of the struct btrfs_qgroup_extent_record from 64 bytes to 40 bytes. The xarray is more cache line friendly, it also reduces the complexity of insertion and search code compared to rb tree. Another change introduced is about error handling. Before this patch, the result of btrfs_qgroup_trace_extent_nolock() is always a success. In this patch, because of this function calls the function xa_store() which has the possibility to fail, so mark qgroup as inconsistent if error happened and then free preallocated memory. Also we preallocate memory before spin_lock(), if memory preallcation failed, error handling is the same the existing code. Suggested-by: Qu Wenruo <[email protected]> Signed-off-by: Junchao Sun <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 14ed830 commit 3cce39a

File tree

5 files changed

+50
-43
lines changed

5 files changed

+50
-43
lines changed

fs/btrfs/delayed-ref.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -855,11 +855,17 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans,
855855

856856
/* Record qgroup extent info if provided */
857857
if (qrecord) {
858-
if (btrfs_qgroup_trace_extent_nolock(trans->fs_info,
859-
delayed_refs, qrecord))
858+
int ret;
859+
860+
ret = btrfs_qgroup_trace_extent_nolock(trans->fs_info,
861+
delayed_refs, qrecord);
862+
if (ret) {
863+
/* Clean up if insertion fails or item exists. */
864+
xa_release(&delayed_refs->dirty_extents, qrecord->bytenr);
860865
kfree(qrecord);
861-
else
866+
} else {
862867
qrecord_inserted = true;
868+
}
863869
}
864870

865871
trace_add_delayed_ref_head(trans->fs_info, head_ref, action);
@@ -1012,6 +1018,9 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans,
10121018
record = kzalloc(sizeof(*record), GFP_NOFS);
10131019
if (!record)
10141020
goto free_head_ref;
1021+
if (xa_reserve(&trans->transaction->delayed_refs.dirty_extents,
1022+
generic_ref->bytenr, GFP_NOFS))
1023+
goto free_record;
10151024
}
10161025

10171026
init_delayed_ref_common(fs_info, node, generic_ref);
@@ -1048,6 +1057,8 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans,
10481057
return btrfs_qgroup_trace_extent_post(trans, record);
10491058
return 0;
10501059

1060+
free_record:
1061+
kfree(record);
10511062
free_head_ref:
10521063
kmem_cache_free(btrfs_delayed_ref_head_cachep, head_ref);
10531064
free_node:

fs/btrfs/delayed-ref.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ struct btrfs_delayed_ref_root {
202202
/* head ref rbtree */
203203
struct rb_root_cached href_root;
204204

205-
/* dirty extent records */
206-
struct rb_root dirty_extent_root;
205+
/* Track dirty extent records. */
206+
struct xarray dirty_extents;
207207

208208
/* this spin lock protects the rbtree and the entries inside */
209209
spinlock_t lock;

fs/btrfs/qgroup.c

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1998,43 +1998,39 @@ int btrfs_limit_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid,
19981998
*
19991999
* Return 0 for success insert
20002000
* Return >0 for existing record, caller can free @record safely.
2001-
* Error is not possible
2001+
* Return <0 for insertion failure, caller can free @record safely.
20022002
*/
20032003
int btrfs_qgroup_trace_extent_nolock(struct btrfs_fs_info *fs_info,
20042004
struct btrfs_delayed_ref_root *delayed_refs,
20052005
struct btrfs_qgroup_extent_record *record)
20062006
{
2007-
struct rb_node **p = &delayed_refs->dirty_extent_root.rb_node;
2008-
struct rb_node *parent_node = NULL;
2009-
struct btrfs_qgroup_extent_record *entry;
2010-
u64 bytenr = record->bytenr;
2007+
struct btrfs_qgroup_extent_record *existing, *ret;
2008+
unsigned long bytenr = record->bytenr;
20112009

20122010
if (!btrfs_qgroup_full_accounting(fs_info))
20132011
return 1;
20142012

20152013
lockdep_assert_held(&delayed_refs->lock);
20162014
trace_btrfs_qgroup_trace_extent(fs_info, record);
20172015

2018-
while (*p) {
2019-
parent_node = *p;
2020-
entry = rb_entry(parent_node, struct btrfs_qgroup_extent_record,
2021-
node);
2022-
if (bytenr < entry->bytenr) {
2023-
p = &(*p)->rb_left;
2024-
} else if (bytenr > entry->bytenr) {
2025-
p = &(*p)->rb_right;
2026-
} else {
2027-
if (record->data_rsv && !entry->data_rsv) {
2028-
entry->data_rsv = record->data_rsv;
2029-
entry->data_rsv_refroot =
2030-
record->data_rsv_refroot;
2031-
}
2032-
return 1;
2016+
xa_lock(&delayed_refs->dirty_extents);
2017+
existing = xa_load(&delayed_refs->dirty_extents, bytenr);
2018+
if (existing) {
2019+
if (record->data_rsv && !existing->data_rsv) {
2020+
existing->data_rsv = record->data_rsv;
2021+
existing->data_rsv_refroot = record->data_rsv_refroot;
20332022
}
2023+
xa_unlock(&delayed_refs->dirty_extents);
2024+
return 1;
2025+
}
2026+
2027+
ret = __xa_store(&delayed_refs->dirty_extents, record->bytenr, record, GFP_ATOMIC);
2028+
xa_unlock(&delayed_refs->dirty_extents);
2029+
if (xa_is_err(ret)) {
2030+
qgroup_mark_inconsistent(fs_info);
2031+
return xa_err(ret);
20342032
}
20352033

2036-
rb_link_node(&record->node, parent_node, p);
2037-
rb_insert_color(&record->node, &delayed_refs->dirty_extent_root);
20382034
return 0;
20392035
}
20402036

@@ -2141,6 +2137,11 @@ int btrfs_qgroup_trace_extent(struct btrfs_trans_handle *trans, u64 bytenr,
21412137
if (!record)
21422138
return -ENOMEM;
21432139

2140+
if (xa_reserve(&trans->transaction->delayed_refs.dirty_extents, bytenr, GFP_NOFS)) {
2141+
kfree(record);
2142+
return -ENOMEM;
2143+
}
2144+
21442145
delayed_refs = &trans->transaction->delayed_refs;
21452146
record->bytenr = bytenr;
21462147
record->num_bytes = num_bytes;
@@ -2149,7 +2150,9 @@ int btrfs_qgroup_trace_extent(struct btrfs_trans_handle *trans, u64 bytenr,
21492150
spin_lock(&delayed_refs->lock);
21502151
ret = btrfs_qgroup_trace_extent_nolock(fs_info, delayed_refs, record);
21512152
spin_unlock(&delayed_refs->lock);
2152-
if (ret > 0) {
2153+
if (ret) {
2154+
/* Clean up if insertion fails or item exists. */
2155+
xa_release(&delayed_refs->dirty_extents, record->bytenr);
21532156
kfree(record);
21542157
return 0;
21552158
}
@@ -3018,7 +3021,7 @@ int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans)
30183021
struct btrfs_qgroup_extent_record *record;
30193022
struct btrfs_delayed_ref_root *delayed_refs;
30203023
struct ulist *new_roots = NULL;
3021-
struct rb_node *node;
3024+
unsigned long index;
30223025
u64 num_dirty_extents = 0;
30233026
u64 qgroup_to_skip;
30243027
int ret = 0;
@@ -3028,10 +3031,7 @@ int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans)
30283031

30293032
delayed_refs = &trans->transaction->delayed_refs;
30303033
qgroup_to_skip = delayed_refs->qgroup_to_skip;
3031-
while ((node = rb_first(&delayed_refs->dirty_extent_root))) {
3032-
record = rb_entry(node, struct btrfs_qgroup_extent_record,
3033-
node);
3034-
3034+
xa_for_each(&delayed_refs->dirty_extents, index, record) {
30353035
num_dirty_extents++;
30363036
trace_btrfs_qgroup_account_extents(fs_info, record);
30373037

@@ -3097,7 +3097,7 @@ int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans)
30973097
ulist_free(record->old_roots);
30983098
ulist_free(new_roots);
30993099
new_roots = NULL;
3100-
rb_erase(node, &delayed_refs->dirty_extent_root);
3100+
xa_erase(&delayed_refs->dirty_extents, index);
31013101
kfree(record);
31023102

31033103
}
@@ -4874,15 +4874,13 @@ int btrfs_qgroup_trace_subtree_after_cow(struct btrfs_trans_handle *trans,
48744874
void btrfs_qgroup_destroy_extent_records(struct btrfs_transaction *trans)
48754875
{
48764876
struct btrfs_qgroup_extent_record *entry;
4877-
struct btrfs_qgroup_extent_record *next;
4878-
struct rb_root *root;
4877+
unsigned long index;
48794878

4880-
root = &trans->delayed_refs.dirty_extent_root;
4881-
rbtree_postorder_for_each_entry_safe(entry, next, root, node) {
4879+
xa_for_each(&trans->delayed_refs.dirty_extents, index, entry) {
48824880
ulist_free(entry->old_roots);
48834881
kfree(entry);
48844882
}
4885-
*root = RB_ROOT;
4883+
xa_destroy(&trans->delayed_refs.dirty_extents);
48864884
}
48874885

48884886
void btrfs_free_squota_rsv(struct btrfs_fs_info *fs_info, u64 root, u64 rsv_bytes)

fs/btrfs/qgroup.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ struct btrfs_inode;
125125
* Record a dirty extent, and info qgroup to update quota on it
126126
*/
127127
struct btrfs_qgroup_extent_record {
128-
struct rb_node node;
129128
u64 bytenr;
130129
u64 num_bytes;
131130

fs/btrfs/transaction.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,7 @@ void btrfs_put_transaction(struct btrfs_transaction *transaction)
143143
BUG_ON(!list_empty(&transaction->list));
144144
WARN_ON(!RB_EMPTY_ROOT(
145145
&transaction->delayed_refs.href_root.rb_root));
146-
WARN_ON(!RB_EMPTY_ROOT(
147-
&transaction->delayed_refs.dirty_extent_root));
146+
WARN_ON(!xa_empty(&transaction->delayed_refs.dirty_extents));
148147
if (transaction->delayed_refs.pending_csums)
149148
btrfs_err(transaction->fs_info,
150149
"pending csums is %llu",
@@ -351,7 +350,7 @@ static noinline int join_transaction(struct btrfs_fs_info *fs_info,
351350
memset(&cur_trans->delayed_refs, 0, sizeof(cur_trans->delayed_refs));
352351

353352
cur_trans->delayed_refs.href_root = RB_ROOT_CACHED;
354-
cur_trans->delayed_refs.dirty_extent_root = RB_ROOT;
353+
xa_init(&cur_trans->delayed_refs.dirty_extents);
355354
atomic_set(&cur_trans->delayed_refs.num_entries, 0);
356355

357356
/*

0 commit comments

Comments
 (0)