Patch "btrfs: fix data race when accessing the inode's disk_i_size at btrfs_drop_extents()" has been added to the 6.13-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 data race when accessing the inode's disk_i_size at btrfs_drop_extents()

to the 6.13-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-data-race-when-accessing-the-inode-s-disk_.patch
and it can be found in the queue-6.13 subdirectory.

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



commit 42695268098bba8afa518d1f776abfd94abc32fb
Author: Hao-ran Zheng <zhenghaoran154@xxxxxxxxx>
Date:   Tue Dec 3 15:56:51 2024 +0800

    btrfs: fix data race when accessing the inode's disk_i_size at btrfs_drop_extents()
    
    [ Upstream commit 5324c4e10e9c2ce307a037e904c0d9671d7137d9 ]
    
    A data race occurs when the function `insert_ordered_extent_file_extent()`
    and the function `btrfs_inode_safe_disk_i_size_write()` are executed
    concurrently. The function `insert_ordered_extent_file_extent()` is not
    locked when reading inode->disk_i_size, causing
    `btrfs_inode_safe_disk_i_size_write()` to cause data competition when
    writing inode->disk_i_size, thus affecting the value of `modify_tree`.
    
    The specific call stack that appears during testing is as follows:
    
      ============DATA_RACE============
       btrfs_drop_extents+0x89a/0xa060 [btrfs]
       insert_reserved_file_extent+0xb54/0x2960 [btrfs]
       insert_ordered_extent_file_extent+0xff5/0x1760 [btrfs]
       btrfs_finish_one_ordered+0x1b85/0x36a0 [btrfs]
       btrfs_finish_ordered_io+0x37/0x60 [btrfs]
       finish_ordered_fn+0x3e/0x50 [btrfs]
       btrfs_work_helper+0x9c9/0x27a0 [btrfs]
       process_scheduled_works+0x716/0xf10
       worker_thread+0xb6a/0x1190
       kthread+0x292/0x330
       ret_from_fork+0x4d/0x80
       ret_from_fork_asm+0x1a/0x30
      ============OTHER_INFO============
       btrfs_inode_safe_disk_i_size_write+0x4ec/0x600 [btrfs]
       btrfs_finish_one_ordered+0x24c7/0x36a0 [btrfs]
       btrfs_finish_ordered_io+0x37/0x60 [btrfs]
       finish_ordered_fn+0x3e/0x50 [btrfs]
       btrfs_work_helper+0x9c9/0x27a0 [btrfs]
       process_scheduled_works+0x716/0xf10
       worker_thread+0xb6a/0x1190
       kthread+0x292/0x330
       ret_from_fork+0x4d/0x80
       ret_from_fork_asm+0x1a/0x30
      =================================
    
    The main purpose of the check of the inode's disk_i_size is to avoid
    taking write locks on a btree path when we have a write at or beyond
    EOF, since in these cases we don't expect to find extent items in the
    root to drop. However if we end up taking write locks due to a data
    race on disk_i_size, everything is still correct, we only add extra
    lock contention on the tree in case there's concurrency from other tasks.
    If the race causes us to not take write locks when we actually need them,
    then everything is functionally correct as well, since if we find out we
    have extent items to drop and we took read locks (modify_tree set to 0),
    we release the path and retry again with write locks.
    
    Since this data race does not affect the correctness of the function,
    it is a harmless data race, use data_race() to check inode->disk_i_size.
    
    Reviewed-by: Filipe Manana <fdmanana@xxxxxxxx>
    Signed-off-by: Hao-ran Zheng <zhenghaoran154@xxxxxxxxx>
    Signed-off-by: Filipe Manana <fdmanana@xxxxxxxx>
    Reviewed-by: David Sterba <dsterba@xxxxxxxx>
    Signed-off-by: David Sterba <dsterba@xxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 14e27473c5bce..4d7c7a296d2d1 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -224,7 +224,7 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
 	if (args->drop_cache)
 		btrfs_drop_extent_map_range(inode, args->start, args->end - 1, false);
 
-	if (args->start >= inode->disk_i_size && !args->replace_extent)
+	if (data_race(args->start >= inode->disk_i_size) && !args->replace_extent)
 		modify_tree = 0;
 
 	update_refs = (btrfs_root_id(root) != BTRFS_TREE_LOG_OBJECTID);




[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