Patch "btrfs: fix race when refilling delayed refs block reserve" has been added to the 6.5-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    btrfs: fix race when refilling delayed refs block reserve

to the 6.5-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     btrfs-fix-race-when-refilling-delayed-refs-block-res.patch
and it can be found in the queue-6.5 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 178be444b4e1f87dc6190ec8c9b121ca211bb3e7
Author: Filipe Manana <fdmanana@xxxxxxxx>
Date:   Fri Sep 8 18:20:18 2023 +0100

    btrfs: fix race when refilling delayed refs block reserve
    
    [ Upstream commit 2ed45c0f1879079b30248568c515cf60fc668d8a ]
    
    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@xxxxxxxx>
    Signed-off-by: David Sterba <dsterba@xxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c
index 6a13cf00218bc..1043f66cc130d 100644
--- a/fs/btrfs/delayed-ref.c
+++ b/fs/btrfs/delayed-ref.c
@@ -163,6 +163,8 @@ int btrfs_delayed_refs_rsv_refill(struct btrfs_fs_info *fs_info,
 	struct btrfs_block_rsv *block_rsv = &fs_info->delayed_refs_rsv;
 	u64 limit = btrfs_calc_delayed_ref_bytes(fs_info, 1);
 	u64 num_bytes = 0;
+	u64 refilled_bytes;
+	u64 to_free;
 	int ret = -ENOSPC;
 
 	spin_lock(&block_rsv->lock);
@@ -178,9 +180,38 @@ int btrfs_delayed_refs_rsv_refill(struct btrfs_fs_info *fs_info,
 	ret = btrfs_reserve_metadata_bytes(fs_info, block_rsv, num_bytes, flush);
 	if (ret)
 		return ret;
-	btrfs_block_rsv_add_bytes(block_rsv, num_bytes, false);
-	trace_btrfs_space_reservation(fs_info, "delayed_refs_rsv",
-				      0, num_bytes, 1);
+
+	/*
+	 * We may have raced with someone else, so check again if we the block
+	 * reserve is still not full and release any excess space.
+	 */
+	spin_lock(&block_rsv->lock);
+	if (block_rsv->reserved < block_rsv->size) {
+		u64 needed = block_rsv->size - block_rsv->reserved;
+
+		if (num_bytes >= needed) {
+			block_rsv->reserved += needed;
+			block_rsv->full = true;
+			to_free = num_bytes - needed;
+			refilled_bytes = needed;
+		} else {
+			block_rsv->reserved += num_bytes;
+			to_free = 0;
+			refilled_bytes = num_bytes;
+		}
+	} else {
+		to_free = num_bytes;
+		refilled_bytes = 0;
+	}
+	spin_unlock(&block_rsv->lock);
+
+	if (to_free > 0)
+		btrfs_space_info_free_bytes_may_use(fs_info, block_rsv->space_info,
+						    to_free);
+
+	if (refilled_bytes > 0)
+		trace_btrfs_space_reservation(fs_info, "delayed_refs_rsv", 0,
+					      refilled_bytes, 1);
 	return 0;
 }
 



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux