]> git.ipfire.org Git - thirdparty/linux.git/commit
btrfs: fix race when refilling delayed refs block reserve
authorFilipe Manana <fdmanana@suse.com>
Fri, 8 Sep 2023 17:20:18 +0000 (18:20 +0100)
committerDavid Sterba <dsterba@suse.com>
Wed, 20 Sep 2023 18:42:08 +0000 (20:42 +0200)
commit2ed45c0f1879079b30248568c515cf60fc668d8a
tree8449e675a896f3a18c8765acc3a454e9f982077e
parent8e7f82deb0c0386a03b62e30082574347f8b57d5
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>
fs/btrfs/delayed-ref.c