Patch "ext4: check the extent status again before inserting delalloc block" has been added to the 6.6-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

    ext4: check the extent status again before inserting delalloc block

to the 6.6-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:
     ext4-check-the-extent-status-again-before-inserting-.patch
and it can be found in the queue-6.6 subdirectory.

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



commit 23136c890b3cbe78363afb1ff1b7ad4a10b684dd
Author: Zhang Yi <yi.zhang@xxxxxxxxxx>
Date:   Fri May 17 20:39:57 2024 +0800

    ext4: check the extent status again before inserting delalloc block
    
    [ Upstream commit 0ea6560abb3bac1ffcfa4bf6b2c4d344fdc27b3c ]
    
    ext4_da_map_blocks looks up for any extent entry in the extent status
    tree (w/o i_data_sem) and then the looks up for any ondisk extent
    mapping (with i_data_sem in read mode).
    
    If it finds a hole in the extent status tree or if it couldn't find any
    entry at all, it then takes the i_data_sem in write mode to add a da
    entry into the extent status tree. This can actually race with page
    mkwrite & fallocate path.
    
    Note that this is ok between
    1. ext4 buffered-write path v/s ext4_page_mkwrite(), because of the
       folio lock
    2. ext4 buffered write path v/s ext4 fallocate because of the inode
       lock.
    
    But this can race between ext4_page_mkwrite() & ext4 fallocate path
    
    ext4_page_mkwrite()             ext4_fallocate()
     block_page_mkwrite()
      ext4_da_map_blocks()
       //find hole in extent status tree
                                     ext4_alloc_file_blocks()
                                      ext4_map_blocks()
                                       //allocate block and unwritten extent
       ext4_insert_delayed_block()
        ext4_da_reserve_space()
         //reserve one more block
        ext4_es_insert_delayed_block()
         //drop unwritten extent and add delayed extent by mistake
    
    Then, the delalloc extent is wrong until writeback and the extra
    reserved block can't be released any more and it triggers below warning:
    
     EXT4-fs (pmem2): Inode 13 (00000000bbbd4d23): i_reserved_data_blocks(1) not cleared!
    
    Fix the problem by looking up extent status tree again while the
    i_data_sem is held in write mode. If it still can't find any entry, then
    we insert a new da entry into the extent status tree.
    
    Cc: stable@xxxxxxxxxxxxxxx
    Signed-off-by: Zhang Yi <yi.zhang@xxxxxxxxxx>
    Reviewed-by: Jan Kara <jack@xxxxxxx>
    Link: https://patch.msgid.link/20240517124005.347221-3-yi.zhang@xxxxxxxxxxxxxxx
    Signed-off-by: Theodore Ts'o <tytso@xxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index b8b2f731f1d0e..cef119a2476bb 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1737,6 +1737,7 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
 		if (ext4_es_is_hole(&es))
 			goto add_delayed;
 
+found:
 		/*
 		 * Delayed extent could be allocated by fallocate.
 		 * So we need to check it.
@@ -1781,6 +1782,26 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
 
 add_delayed:
 	down_write(&EXT4_I(inode)->i_data_sem);
+	/*
+	 * Page fault path (ext4_page_mkwrite does not take i_rwsem)
+	 * and fallocate path (no folio lock) can race. Make sure we
+	 * lookup the extent status tree here again while i_data_sem
+	 * is held in write mode, before inserting a new da entry in
+	 * the extent status tree.
+	 */
+	if (ext4_es_lookup_extent(inode, iblock, NULL, &es)) {
+		if (!ext4_es_is_hole(&es)) {
+			up_write(&EXT4_I(inode)->i_data_sem);
+			goto found;
+		}
+	} else if (!ext4_has_inline_data(inode)) {
+		retval = ext4_map_query_blocks(NULL, inode, map);
+		if (retval) {
+			up_write(&EXT4_I(inode)->i_data_sem);
+			return retval;
+		}
+	}
+
 	retval = ext4_insert_delayed_block(inode, map->m_lblk);
 	up_write(&EXT4_I(inode)->i_data_sem);
 	if (retval)




[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