Skip to content

Commit 2ed45c0

Browse files
fdmananakdave
authored andcommitted
btrfs: fix race when refilling delayed refs block reserve
If we have two (or more) tasks attempting to refill the delayed refs block reserve we can end up with the delayed block reserve being over reserved, that is, with a reserved space greater than its size. If this happens, we are holding to more reserved space than necessary for a while. The race happens like this: 1) The delayed refs block reserve has a size of 8M and a reserved space of 6M for example; 2) Task A calls btrfs_delayed_refs_rsv_refill(); 3) Task B also calls btrfs_delayed_refs_rsv_refill(); 4) Task A sees there's a 2M difference between the size and the reserved space of the delayed refs rsv, so it will reserve 2M of space by calling btrfs_reserve_metadata_bytes(); 5) Task B also sees that 2M difference, and like task A, it reserves another 2M of metadata space; 6) Both task A and task B increase the reserved space of block reserve by 2M, by calling btrfs_block_rsv_add_bytes(), so the block reserve ends up with a size of 8M and a reserved space of 10M; 7) The extra, over reserved space will eventually be freed by some task calling btrfs_delayed_refs_rsv_release() -> btrfs_block_rsv_release() -> block_rsv_release_bytes(), as there we will detect the over reserve and release that space. So fix this by checking if we still need to add space to the delayed refs block reserve after reserving the metadata space, and if we don't, just release that space immediately. Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 8e7f82d commit 2ed45c0

1 file changed

Lines changed: 34 additions & 3 deletions

File tree

fs/btrfs/delayed-ref.c

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ int btrfs_delayed_refs_rsv_refill(struct btrfs_fs_info *fs_info,
163163
struct btrfs_block_rsv *block_rsv = &fs_info->delayed_refs_rsv;
164164
u64 limit = btrfs_calc_delayed_ref_bytes(fs_info, 1);
165165
u64 num_bytes = 0;
166+
u64 refilled_bytes;
167+
u64 to_free;
166168
int ret = -ENOSPC;
167169

168170
spin_lock(&block_rsv->lock);
@@ -178,9 +180,38 @@ int btrfs_delayed_refs_rsv_refill(struct btrfs_fs_info *fs_info,
178180
ret = btrfs_reserve_metadata_bytes(fs_info, block_rsv, num_bytes, flush);
179181
if (ret)
180182
return ret;
181-
btrfs_block_rsv_add_bytes(block_rsv, num_bytes, false);
182-
trace_btrfs_space_reservation(fs_info, "delayed_refs_rsv",
183-
0, num_bytes, 1);
183+
184+
/*
185+
* We may have raced with someone else, so check again if we the block
186+
* reserve is still not full and release any excess space.
187+
*/
188+
spin_lock(&block_rsv->lock);
189+
if (block_rsv->reserved < block_rsv->size) {
190+
u64 needed = block_rsv->size - block_rsv->reserved;
191+
192+
if (num_bytes >= needed) {
193+
block_rsv->reserved += needed;
194+
block_rsv->full = true;
195+
to_free = num_bytes - needed;
196+
refilled_bytes = needed;
197+
} else {
198+
block_rsv->reserved += num_bytes;
199+
to_free = 0;
200+
refilled_bytes = num_bytes;
201+
}
202+
} else {
203+
to_free = num_bytes;
204+
refilled_bytes = 0;
205+
}
206+
spin_unlock(&block_rsv->lock);
207+
208+
if (to_free > 0)
209+
btrfs_space_info_free_bytes_may_use(fs_info, block_rsv->space_info,
210+
to_free);
211+
212+
if (refilled_bytes > 0)
213+
trace_btrfs_space_reservation(fs_info, "delayed_refs_rsv", 0,
214+
refilled_bytes, 1);
184215
return 0;
185216
}
186217

0 commit comments

Comments
 (0)