|
@@ -5735,10 +5735,13 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
|
|
|
unpin = &fs_info->freed_extents[0];
|
|
|
|
|
|
while (1) {
|
|
|
+ mutex_lock(&fs_info->unused_bg_unpin_mutex);
|
|
|
ret = find_first_extent_bit(unpin, 0, &start, &end,
|
|
|
EXTENT_DIRTY, NULL);
|
|
|
- if (ret)
|
|
|
+ if (ret) {
|
|
|
+ mutex_unlock(&fs_info->unused_bg_unpin_mutex);
|
|
|
break;
|
|
|
+ }
|
|
|
|
|
|
if (btrfs_test_opt(root, DISCARD))
|
|
|
ret = btrfs_discard_extent(root, start,
|
|
@@ -5746,6 +5749,7 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
|
|
|
|
|
|
clear_extent_dirty(unpin, start, end, GFP_NOFS);
|
|
|
unpin_extent_range(root, start, end, true);
|
|
|
+ mutex_unlock(&fs_info->unused_bg_unpin_mutex);
|
|
|
cond_resched();
|
|
|
}
|
|
|
|
|
@@ -9561,18 +9565,33 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
|
|
|
*/
|
|
|
start = block_group->key.objectid;
|
|
|
end = start + block_group->key.offset - 1;
|
|
|
+ /*
|
|
|
+ * Hold the unused_bg_unpin_mutex lock to avoid racing with
|
|
|
+ * btrfs_finish_extent_commit(). If we are at transaction N,
|
|
|
+ * another task might be running finish_extent_commit() for the
|
|
|
+ * previous transaction N - 1, and have seen a range belonging
|
|
|
+ * to the block group in freed_extents[] before we were able to
|
|
|
+ * clear the whole block group range from freed_extents[]. This
|
|
|
+ * means that task can lookup for the block group after we
|
|
|
+ * unpinned it from freed_extents[] and removed it, leading to
|
|
|
+ * a BUG_ON() at btrfs_unpin_extent_range().
|
|
|
+ */
|
|
|
+ mutex_lock(&fs_info->unused_bg_unpin_mutex);
|
|
|
ret = clear_extent_bits(&fs_info->freed_extents[0], start, end,
|
|
|
EXTENT_DIRTY, GFP_NOFS);
|
|
|
if (ret) {
|
|
|
+ mutex_unlock(&fs_info->unused_bg_unpin_mutex);
|
|
|
btrfs_set_block_group_rw(root, block_group);
|
|
|
goto end_trans;
|
|
|
}
|
|
|
ret = clear_extent_bits(&fs_info->freed_extents[1], start, end,
|
|
|
EXTENT_DIRTY, GFP_NOFS);
|
|
|
if (ret) {
|
|
|
+ mutex_unlock(&fs_info->unused_bg_unpin_mutex);
|
|
|
btrfs_set_block_group_rw(root, block_group);
|
|
|
goto end_trans;
|
|
|
}
|
|
|
+ mutex_unlock(&fs_info->unused_bg_unpin_mutex);
|
|
|
|
|
|
/* Reset pinned so btrfs_put_block_group doesn't complain */
|
|
|
block_group->pinned = 0;
|