Patch "ext4: convert to exclusive lock while inserting delalloc extents" has been added to the 6.1-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: convert to exclusive lock while inserting delalloc extents

to the 6.1-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-convert-to-exclusive-lock-while-inserting-delal.patch
and it can be found in the queue-6.1 subdirectory.

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



commit f55733e3df97e5c9a292b773ac397145d03c3eac
Author: Zhang Yi <yi.zhang@xxxxxxxxxx>
Date:   Sat Jan 27 09:58:01 2024 +0800

    ext4: convert to exclusive lock while inserting delalloc extents
    
    [ Upstream commit acf795dc161f3cf481db20f05db4250714e375e5 ]
    
    ext4_da_map_blocks() only hold i_data_sem in shared mode and i_rwsem
    when inserting delalloc extents, it could be raced by another querying
    path of ext4_map_blocks() without i_rwsem, .e.g buffered read path.
    Suppose we buffered read a file containing just a hole, and without any
    cached extents tree, then it is raced by another delayed buffered write
    to the same area or the near area belongs to the same hole, and the new
    delalloc extent could be overwritten to a hole extent.
    
     pread()                           pwrite()
      filemap_read_folio()
       ext4_mpage_readpages()
        ext4_map_blocks()
         down_read(i_data_sem)
         ext4_ext_determine_hole()
         //find hole
         ext4_ext_put_gap_in_cache()
          ext4_es_find_extent_range()
          //no delalloc extent
                                        ext4_da_map_blocks()
                                         down_read(i_data_sem)
                                         ext4_insert_delayed_block()
                                         //insert delalloc extent
          ext4_es_insert_extent()
          //overwrite delalloc extent to hole
    
    This race could lead to inconsistent delalloc extents tree and
    incorrect reserved space counter. Fix this by converting to hold
    i_data_sem in exclusive mode when adding a new delalloc extent in
    ext4_da_map_blocks().
    
    Cc: stable@xxxxxxxxxxxxxxx
    Signed-off-by: Zhang Yi <yi.zhang@xxxxxxxxxx>
    Suggested-by: Jan Kara <jack@xxxxxxx>
    Reviewed-by: Jan Kara <jack@xxxxxxx>
    Link: https://lore.kernel.org/r/20240127015825.1608160-3-yi.zhang@xxxxxxxxxxxxxxx
    Signed-off-by: Theodore Ts'o <tytso@xxxxxxx>
    Stable-dep-of: 0ea6560abb3b ("ext4: check the extent status again before inserting delalloc block")
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index eab9aefe96ce6..d6f7525a796c0 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1740,10 +1740,8 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
 
 	/* Lookup extent status tree firstly */
 	if (ext4_es_lookup_extent(inode, iblock, NULL, &es)) {
-		if (ext4_es_is_hole(&es)) {
-			down_read(&EXT4_I(inode)->i_data_sem);
+		if (ext4_es_is_hole(&es))
 			goto add_delayed;
-		}
 
 		/*
 		 * Delayed extent could be allocated by fallocate.
@@ -1785,8 +1783,10 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
 		retval = ext4_ext_map_blocks(NULL, inode, map, 0);
 	else
 		retval = ext4_ind_map_blocks(NULL, inode, map, 0);
-	if (retval < 0)
-		goto out_unlock;
+	if (retval < 0) {
+		up_read(&EXT4_I(inode)->i_data_sem);
+		return retval;
+	}
 	if (retval > 0) {
 		unsigned int status;
 
@@ -1802,24 +1802,21 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
 				EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
 		ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
 				      map->m_pblk, status);
-		goto out_unlock;
+		up_read(&EXT4_I(inode)->i_data_sem);
+		return retval;
 	}
+	up_read(&EXT4_I(inode)->i_data_sem);
 
 add_delayed:
-	/*
-	 * XXX: __block_prepare_write() unmaps passed block,
-	 * is it OK?
-	 */
+	down_write(&EXT4_I(inode)->i_data_sem);
 	retval = ext4_insert_delayed_block(inode, map->m_lblk);
+	up_write(&EXT4_I(inode)->i_data_sem);
 	if (retval)
-		goto out_unlock;
+		return retval;
 
 	map_bh(bh, inode->i_sb, invalid_block);
 	set_buffer_new(bh);
 	set_buffer_delay(bh);
-
-out_unlock:
-	up_read((&EXT4_I(inode)->i_data_sem));
 	return 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