Skip to content

Commit ff3d27a

Browse files
adam900710kdave
authored andcommitted
btrfs: qgroup: Finish rescan when hit the last leaf of extent tree
Under the following case, qgroup rescan can double account cowed tree blocks: In this case, extent tree only has one tree block. - | transid=5 last committed=4 | btrfs_qgroup_rescan_worker() | |- btrfs_start_transaction() | | transid = 5 | |- qgroup_rescan_leaf() | |- btrfs_search_slot_for_read() on extent tree | Get the only extent tree block from commit root (transid = 4). | Scan it, set qgroup_rescan_progress to the last | EXTENT/META_ITEM + 1 | now qgroup_rescan_progress = A + 1. | | fs tree get CoWed, new tree block is at A + 16K | transid 5 get committed - | transid=6 last committed=5 | btrfs_qgroup_rescan_worker() | btrfs_qgroup_rescan_worker() | |- btrfs_start_transaction() | | transid = 5 | |- qgroup_rescan_leaf() | |- btrfs_search_slot_for_read() on extent tree | Get the only extent tree block from commit root (transid = 5). | scan it using qgroup_rescan_progress (A + 1). | found new tree block beyong A, and it's fs tree block, | account it to increase qgroup numbers. - In above case, tree block A, and tree block A + 16K get accounted twice, while qgroup rescan should stop when it already reach the last leaf, other than continue using its qgroup_rescan_progress. Such case could happen by just looping btrfs/017 and with some possibility it can hit such double qgroup accounting problem. Fix it by checking the path to determine if we should finish qgroup rescan, other than relying on next loop to exit. Reported-by: Nikolay Borisov <[email protected]> Signed-off-by: Qu Wenruo <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent b6debf1 commit ff3d27a

File tree

1 file changed

+19
-0
lines changed

1 file changed

+19
-0
lines changed

fs/btrfs/qgroup.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2579,6 +2579,21 @@ void btrfs_qgroup_free_refroot(struct btrfs_fs_info *fs_info,
25792579
spin_unlock(&fs_info->qgroup_lock);
25802580
}
25812581

2582+
/*
2583+
* Check if the leaf is the last leaf. Which means all node pointers
2584+
* are at their last position.
2585+
*/
2586+
static bool is_last_leaf(struct btrfs_path *path)
2587+
{
2588+
int i;
2589+
2590+
for (i = 1; i < BTRFS_MAX_LEVEL && path->nodes[i]; i++) {
2591+
if (path->slots[i] != btrfs_header_nritems(path->nodes[i]) - 1)
2592+
return false;
2593+
}
2594+
return true;
2595+
}
2596+
25822597
/*
25832598
* returns < 0 on error, 0 when more leafs are to be scanned.
25842599
* returns 1 when done.
@@ -2591,6 +2606,7 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
25912606
struct extent_buffer *scratch_leaf = NULL;
25922607
struct ulist *roots = NULL;
25932608
u64 num_bytes;
2609+
bool done;
25942610
int slot;
25952611
int ret;
25962612

@@ -2619,6 +2635,7 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
26192635
mutex_unlock(&fs_info->qgroup_rescan_lock);
26202636
return ret;
26212637
}
2638+
done = is_last_leaf(path);
26222639

26232640
btrfs_item_key_to_cpu(path->nodes[0], &found,
26242641
btrfs_header_nritems(path->nodes[0]) - 1);
@@ -2663,6 +2680,8 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
26632680
free_extent_buffer(scratch_leaf);
26642681
}
26652682

2683+
if (done && !ret)
2684+
ret = 1;
26662685
return ret;
26672686
}
26682687

0 commit comments

Comments
 (0)